Skip to content
This repository was archived by the owner on Sep 30, 2024. It is now read-only.

Commit bd796dc

Browse files
authored
Code Insights: Support color for compute powered insight (#40038)
* Adjust compute-insight creation UI * Use only one colour for compute-powered insight on the dashboard page * Handle no data state for the compute insight * Refactor backend insight and chart card * Disable stylelint for the hack with self selector * Fix copy for compute powered insight card
1 parent 6fd138c commit bd796dc

File tree

13 files changed

+100
-132
lines changed

13 files changed

+100
-132
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ All notable changes to Sourcegraph are documented in this file.
2929
- **IMPORTANT: Search queries with patterns surrounded by** `/.../` **will now be interpreted as regular expressions.** Existing search links or code monitors are unaffected. In the rare event where older links rely on the literal meaning of `/.../`, the string will be automatically quoted it in a `content` filter, preserving the original meaning. If you happen to use an existing older link and want `/.../` to work as a regular expression, add `patterntype:standard` to the query. New queries and code monitors will interpret `/.../` as regular expressions. [#38141](https://github.com/sourcegraph/sourcegraph/pull/38141).
3030
- The password policy has been updated and is now part of the standard featureset configurable by site-admins. [#39213](https://github.com/sourcegraph/sourcegraph/pull/39213).
3131
- Replaced the `ALLOW_DECRYPT_MIGRATION` envvar with `ALLOW_DECRYPTION`. See [updated documentation](https://docs.sourcegraph.com/admin/config/encryption). [#39984](https://github.com/sourcegraph/sourcegraph/pull/39984)
32+
- Compute-powered insight now supports only one series custom colors for compute series bars [40038](https://github.com/sourcegraph/sourcegraph/pull/40038)
3233

3334
### Fixed
3435

client/web/src/enterprise/insights/components/creation-ui/form-series/FormSeries.tsx

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@ export interface FormSeriesProps {
2828
queryFieldDescription?: ReactNode
2929

3030
/**
31-
* This prop hides color picker from the series form. This field is needed for
32-
* compute powered insight creation UI, see https://github.com/sourcegraph/sourcegraph/issues/38832
33-
* for more details compute doesn't have series colors
31+
* For the compute-powered insight we do not support multi series, in order to enforce it
32+
* we need to hide this functionality by hiding add new series button.
33+
* More context in this issue https://github.com/sourcegraph/sourcegraph/issues/38832
3434
*/
35-
showColorPicker?: boolean
35+
hasAddNewSeriesButton?: boolean
3636
}
3737

3838
export const FormSeries: FC<FormSeriesProps> = props => {
@@ -41,7 +41,7 @@ export const FormSeries: FC<FormSeriesProps> = props => {
4141
showValidationErrorsOnMount,
4242
repositories,
4343
queryFieldDescription,
44-
showColorPicker = true,
44+
hasAddNewSeriesButton = true,
4545
} = props
4646

4747
const { licensed } = useUiFeatures()
@@ -60,7 +60,6 @@ export const FormSeries: FC<FormSeriesProps> = props => {
6060
autofocus={line.autofocus}
6161
repositories={repositories}
6262
queryFieldDescription={queryFieldDescription}
63-
showColorPicker={showColorPicker}
6463
className={classNames('p-3', styles.formSeriesItem)}
6564
onSubmit={editCommit}
6665
onCancel={() => cancelEdit(line.id)}
@@ -74,7 +73,6 @@ export const FormSeries: FC<FormSeriesProps> = props => {
7473
onEdit={() => editRequest(line.id)}
7574
onRemove={() => deleteSeries(line.id)}
7675
className={styles.formSeriesItem}
77-
showColor={showColorPicker}
7876
{...line}
7977
/>
8078
)
@@ -85,16 +83,18 @@ export const FormSeries: FC<FormSeriesProps> = props => {
8583
<LimitedAccessLabel message="Unlock Code Insights for unlimited data series" className="mx-auto my-3" />
8684
)}
8785

88-
<Button
89-
data-testid="add-series-button"
90-
type="button"
91-
onClick={() => editRequest()}
92-
variant="link"
93-
disabled={!licensed ? series.length >= 10 : false}
94-
className={classNames(styles.formSeriesItem, styles.formSeriesAddButton, 'p-3')}
95-
>
96-
+ Add another data series
97-
</Button>
86+
{hasAddNewSeriesButton && (
87+
<Button
88+
data-testid="add-series-button"
89+
type="button"
90+
onClick={() => editRequest()}
91+
variant="link"
92+
disabled={!licensed ? series.length >= 10 : false}
93+
className={classNames(styles.formSeriesItem, styles.formSeriesAddButton, 'p-3')}
94+
>
95+
+ Add another data series
96+
</Button>
97+
)}
9898
</ul>
9999
)
100100
}

client/web/src/enterprise/insights/components/creation-ui/form-series/components/form-series-input/FormSeriesInput.tsx

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,6 @@ interface FormSeriesInputProps {
3636
*/
3737
queryFieldDescription?: ReactNode
3838

39-
/**
40-
* This prop hides color picker from the series form. This field is needed for
41-
* compute powered insight creation UI, see https://github.com/sourcegraph/sourcegraph/issues/38832
42-
* for more details whe compute doesn't have series colors
43-
*/
44-
showColorPicker: boolean
45-
4639
/** Enable autofocus behavior of the first input element of series form. */
4740
autofocus?: boolean
4841

@@ -72,7 +65,6 @@ export const FormSeriesInput: FC<FormSeriesInputProps> = props => {
7265
autofocus = true,
7366
repositories,
7467
queryFieldDescription,
75-
showColorPicker,
7668
onCancel = noop,
7769
onSubmit = noop,
7870
onChange = noop,
@@ -156,15 +148,13 @@ export const FormSeriesInput: FC<FormSeriesInputProps> = props => {
156148
{...getDefaultInputProps(queryField)}
157149
/>
158150

159-
{showColorPicker && (
160-
<FormColorInput
161-
name={`color group of ${index} series`}
162-
title="Color"
163-
className="mt-4"
164-
value={colorField.input.value}
165-
onChange={colorField.input.onChange}
166-
/>
167-
)}
151+
<FormColorInput
152+
name={`color group of ${index} series`}
153+
title="Color"
154+
className="mt-4"
155+
value={colorField.input.value}
156+
onChange={colorField.input.onChange}
157+
/>
168158

169159
<div className="mt-4">
170160
<Button

client/web/src/enterprise/insights/components/creation-ui/form-series/components/series-card/SeriesCard.tsx

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,6 @@ interface SeriesCardProps {
1818
/** Color value of series. */
1919
stroke?: string
2020

21-
/**
22-
* This prop hides color picker from the series form. This field is needed for
23-
* compute powered insight creation UI, see https://github.com/sourcegraph/sourcegraph/issues/38832
24-
* for more details whe compute doesn't have series colors
25-
*/
26-
showColor?: boolean
27-
2821
/** Custom class name for root button element. */
2922
className?: string
3023
/** Edit handler. */
@@ -37,16 +30,7 @@ interface SeriesCardProps {
3730
* Renders series card component, visual list item of series (name, color, query)
3831
* */
3932
export function SeriesCard(props: SeriesCardProps): ReactElement {
40-
const {
41-
disabled,
42-
name,
43-
query,
44-
stroke: color = DEFAULT_DATA_SERIES_COLOR,
45-
showColor = true,
46-
className,
47-
onEdit,
48-
onRemove,
49-
} = props
33+
const { disabled, name, query, stroke: color = DEFAULT_DATA_SERIES_COLOR, className, onEdit, onRemove } = props
5034

5135
return (
5236
<Card
@@ -58,14 +42,12 @@ export function SeriesCard(props: SeriesCardProps): ReactElement {
5842
>
5943
<div className={styles.cardInfo}>
6044
<div className={classNames('mb-1 ', styles.cardTitle)}>
61-
{showColor && (
62-
<div
63-
data-testid="series-color-mark"
64-
/* eslint-disable-next-line react/forbid-dom-props */
65-
style={{ color: disabled ? 'var(--icon-muted)' : color }}
66-
className={styles.cardColorMark}
67-
/>
68-
)}
45+
<div
46+
data-testid="series-color-mark"
47+
/* eslint-disable-next-line react/forbid-dom-props */
48+
style={{ color: disabled ? 'var(--icon-muted)' : color }}
49+
className={styles.cardColorMark}
50+
/>
6951
<span
7052
data-testid="series-name"
7153
title={name}

client/web/src/enterprise/insights/components/insights-view-grid/components/backend-insight/BackendInsight.tsx

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ import {
3232
DrillDownFiltersPopover,
3333
DrillDownInsightCreationFormValues,
3434
BackendInsightChart,
35+
parseSeriesLimit,
3536
} from './components'
36-
import { parseSeriesLimit } from './components/drill-down-filters-panel/drill-down-filters/utils'
3737

3838
import styles from './BackendInsight.module.scss'
3939

@@ -67,14 +67,9 @@ export const BackendInsightView: React.FunctionComponent<React.PropsWithChildren
6767
const mergedInsightCardReference = useMergeRefs([insightCardReference, innerRef])
6868
const { wasEverVisible, isVisible } = useVisibility(insightCardReference)
6969

70-
// Use deep copy check in case if a setting subject has re-created copy of
71-
// the insight config with same structure and values. To avoid insight data
72-
// re-fetching.
73-
const cachedInsight = useDeepMemo(insight)
74-
7570
// Original insight filters values that are stored in setting subject with insight
7671
// configuration object, They are updated whenever the user clicks update/save button
77-
const [originalInsightFilters, setOriginalInsightFilters] = useState(cachedInsight.filters)
72+
const [originalInsightFilters, setOriginalInsightFilters] = useState(insight.filters)
7873

7974
// Live valid filters from filter form. They are updated whenever the user is changing
8075
// filter value in filters fields.
@@ -181,8 +176,6 @@ export const BackendInsightView: React.FunctionComponent<React.PropsWithChildren
181176
insightType: getTrackingTypeByInsightType(insight.type),
182177
})
183178

184-
const shareableUrl = `${window.location.origin}/insights/insight/${insight.id}`
185-
186179
return (
187180
<InsightCard
188181
{...otherProps}
@@ -194,7 +187,11 @@ export const BackendInsightView: React.FunctionComponent<React.PropsWithChildren
194187
>
195188
<InsightCardHeader
196189
title={
197-
<Link to={shareableUrl} target="_blank" rel="noopener noreferrer">
190+
<Link
191+
to={`${window.location.origin}/insights/insight/${insight.id}`}
192+
target="_blank"
193+
rel="noopener noreferrer"
194+
>
198195
{insight.title}
199196
</Link>
200197
}

client/web/src/enterprise/insights/components/insights-view-grid/components/backend-insight/components/backend-insight-chart/BackendInsightChart.module.scss

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,31 @@
77
grid-template-columns: auto minmax(10rem, 30%);
88
grid-template-rows: auto 1fr;
99
gap: 0.75rem;
10+
1011
grid-template-areas:
1112
'chart chart'
12-
'chart chart'
13-
'legend legend';
13+
'chart chart';
1414

15-
&--horizontal {
15+
// Hack for generating a proper scoped css nested class
16+
// see https://css-tricks.com/using-sass-control-scope-bem-naming/
17+
$self: &;
18+
19+
// stylelint-disable-next-line selector-class-pattern
20+
&--withLegend {
1621
grid-template-areas:
17-
'chart legend'
18-
'chart legend';
22+
'chart chart'
23+
'chart chart'
24+
'legend legend';
25+
26+
&#{ $self }--horizontal {
27+
grid-template-areas:
28+
'chart legend'
29+
'chart legend';
1930

20-
.legend-list {
21-
flex-wrap: nowrap;
22-
flex-direction: column;
31+
.legend-list {
32+
flex-wrap: nowrap;
33+
flex-direction: column;
34+
}
2335
}
2436
}
2537
}

client/web/src/enterprise/insights/components/insights-view-grid/components/backend-insight/components/backend-insight-chart/BackendInsightChart.tsx

Lines changed: 16 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,10 @@ import { ParentSize } from '@visx/responsive'
44
import classNames from 'classnames'
55
import useResizeObserver from 'use-resize-observer'
66

7-
import { useDebounce } from '@sourcegraph/wildcard'
8-
97
import { getLineColor, LegendItem, LegendList, ScrollBox, Series } from '../../../../../../../../charts'
108
import { BarChart } from '../../../../../../../../charts/components/bar-chart/BarChart'
119
import { UseSeriesToggleReturn } from '../../../../../../../../insights/utils/use-series-toggle'
12-
import { BackendInsightData, CategoricalChartContent, InsightContent } from '../../../../../../core'
10+
import { BackendInsightData, InsightContent } from '../../../../../../core'
1311
import { InsightContentType } from '../../../../../../core/types/insight/common'
1412
import { SeriesBasedChartTypes, SeriesChart } from '../../../../../views'
1513
import { BackendAlertOverlay } from '../backend-insight-alerts/BackendInsightAlerts'
@@ -51,17 +49,24 @@ interface BackendInsightChartProps<Datum> extends BackendInsightData {
5149

5250
export function BackendInsightChart<Datum>(props: BackendInsightChartProps<Datum>): React.ReactElement {
5351
const { locked, isFetchingHistoricalData, data, zeroYAxisMin, className, onDatumClick, seriesToggleState } = props
54-
const { ref, width = 0 } = useDebounce(useResizeObserver(), 100)
52+
const { ref, width = 0 } = useResizeObserver()
5553
const { setHoveredId } = seriesToggleState
5654

55+
const isEmptyDataset = useMemo(() => hasNoData(data), [data])
56+
5757
const hasViewManySeries = isManyKeysInsight(data)
5858
const hasEnoughXSpace = width >= MINIMAL_HORIZONTAL_LAYOUT_WIDTH
59-
6059
const isHorizontalMode = hasViewManySeries && hasEnoughXSpace
61-
const isEmptyDataset = useMemo(() => hasNoData(data), [data])
60+
const isSeriesLikeInsight = data.type === InsightContentType.Series
6261

6362
return (
64-
<div ref={ref} className={classNames(className, styles.root, { [styles.rootHorizontal]: isHorizontalMode })}>
63+
<div
64+
ref={ref}
65+
className={classNames(className, styles.root, {
66+
[styles.rootHorizontal]: isHorizontalMode,
67+
[styles.rootWithLegend]: isSeriesLikeInsight,
68+
})}
69+
>
6570
{width && (
6671
<>
6772
<ParentSize
@@ -96,13 +101,11 @@ export function BackendInsightChart<Datum>(props: BackendInsightChartProps<Datum
96101
)}
97102
</ParentSize>
98103

99-
<ScrollBox className={styles.legendListContainer} onMouseLeave={() => setHoveredId(undefined)}>
100-
{data.type === InsightContentType.Series ? (
104+
{isSeriesLikeInsight && (
105+
<ScrollBox className={styles.legendListContainer} onMouseLeave={() => setHoveredId(undefined)}>
101106
<SeriesLegends series={data.content.series} seriesToggleState={seriesToggleState} />
102-
) : (
103-
<CategoricalLegends data={data.content} />
104-
)}
105-
</ScrollBox>
107+
</ScrollBox>
108+
)}
106109
</>
107110
)}
108111
</div>
@@ -165,26 +168,3 @@ const SeriesLegends: FC<SeriesLegendsProps> = props => {
165168
</LegendList>
166169
)
167170
}
168-
169-
interface CategoricalLegendsProps {
170-
data: CategoricalChartContent<any>
171-
}
172-
173-
const CategoricalLegends: FC<CategoricalLegendsProps> = props => {
174-
const { data } = props
175-
176-
return (
177-
<LegendList className={styles.legendList}>
178-
{data.data.map(item => (
179-
<LegendItem
180-
key={item.id as string}
181-
color={data.getDatumColor(item) ?? 'gray'}
182-
name={data.getDatumName(item)}
183-
className={styles.legendListItem}
184-
// prevent accidental dragging events
185-
onMouseDown={event => event.stopPropagation()}
186-
/>
187-
))}
188-
</LegendList>
189-
)
190-
}

client/web/src/enterprise/insights/components/modals/ShareLinkModal/ShareLinkModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
isOrganizationOwner,
1818
isVirtualDashboard,
1919
} from '../../../core'
20-
import { useCopyURLHandler } from '../../../hooks/use-copy-url-handler'
20+
import { useCopyURLHandler } from '../../../hooks'
2121

2222
import { decodeDashboardIds, GET_SHARABLE_INSIGHT_INFO_GQL } from './get-sharable-insight-info'
2323

client/web/src/enterprise/insights/core/backend/gql-backend/methods/get-backend-insight-data/deserializators.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { InsightDataNode } from '../../../../../../../graphql-operations'
22
import { BackendInsight, isComputeInsight } from '../../../../types'
33
import { InsightContentType } from '../../../../types/insight/common'
44
import { BackendInsightData } from '../../../code-insights-backend-types'
5-
import { createCategoricalChart } from '../../../utils/create-categorical-content'
5+
import { createComputeCategoricalChart } from '../../../utils/create-categorical-content'
66
import { createLineChartContent } from '../../../utils/create-line-chart-content'
77

88
export const MAX_NUMBER_OF_SERIES = 20
@@ -21,7 +21,7 @@ export const createBackendInsightData = (insight: BackendInsight, response: Insi
2121
isFetchingHistoricalData: isFetchingHistoricalData || seriesData.some(series => !series.label),
2222
data: {
2323
type: InsightContentType.Categorical,
24-
content: createCategoricalChart(seriesData),
24+
content: createComputeCategoricalChart(insight, seriesData),
2525
},
2626
}
2727
}

0 commit comments

Comments
 (0)