@scality/core-ui 0.162.0 → 0.163.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/barchartv2/Barchart.component.d.ts +0 -2
- package/dist/components/barchartv2/Barchart.component.d.ts.map +1 -1
- package/dist/components/barchartv2/Barchart.component.js +11 -1
- package/dist/components/barchartv2/utils.d.ts +25 -2
- package/dist/components/barchartv2/utils.d.ts.map +1 -1
- package/dist/components/barchartv2/utils.js +35 -3
- package/dist/components/chartlegend/ChartLegend.d.ts +8 -0
- package/dist/components/chartlegend/ChartLegend.d.ts.map +1 -0
- package/dist/components/chartlegend/ChartLegend.js +65 -0
- package/dist/components/chartlegend/ChartLegendWrapper.d.ts +17 -0
- package/dist/components/chartlegend/ChartLegendWrapper.d.ts.map +1 -0
- package/dist/components/chartlegend/ChartLegendWrapper.js +50 -0
- package/dist/components/date/FormattedDateTime.d.ts +3 -1
- package/dist/components/date/FormattedDateTime.d.ts.map +1 -1
- package/dist/components/date/FormattedDateTime.js +19 -1
- package/dist/components/date/FormattedDateTime.spec.js +12 -0
- package/dist/components/icon/Icon.component.d.ts +5 -5
- package/dist/components/icon/Icon.component.d.ts.map +1 -1
- package/dist/components/icon/Icon.component.js +33 -31
- package/dist/components/linetimeseriechart/linetimeseriechart.component.d.ts +33 -0
- package/dist/components/linetimeseriechart/linetimeseriechart.component.d.ts.map +1 -0
- package/dist/components/linetimeseriechart/linetimeseriechart.component.js +249 -0
- package/dist/components/selectv2/Selectv2.component.d.ts.map +1 -1
- package/dist/components/selectv2/Selectv2.component.js +11 -6
- package/dist/components/steppers/Stepper.component.d.ts.map +1 -1
- package/dist/components/steppers/Stepper.component.js +9 -8
- package/dist/components/toast/ToastProvider.d.ts.map +1 -1
- package/dist/components/toast/ToastProvider.js +4 -5
- package/dist/components/vegachartv2/SyncedCursorCharts.d.ts.map +1 -1
- package/dist/components/vegachartv2/SyncedCursorCharts.js +3 -5
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/next.d.ts +1 -0
- package/dist/next.d.ts.map +1 -1
- package/dist/next.js +1 -0
- package/dist/style/theme.d.ts +1 -0
- package/dist/style/theme.d.ts.map +1 -1
- package/dist/style/theme.js +28 -0
- package/package.json +2 -2
- package/src/lib/components/accordion/Accordion.test.tsx +7 -15
- package/src/lib/components/barchartv2/Barchart.component.test.tsx +82 -101
- package/src/lib/components/barchartv2/Barchart.component.tsx +14 -2
- package/src/lib/components/barchartv2/utils.test.ts +117 -0
- package/src/lib/components/barchartv2/utils.ts +54 -6
- package/src/lib/components/chartlegend/ChartLegend.tsx +113 -0
- package/src/lib/components/chartlegend/ChartLegendWrapper.tsx +85 -0
- package/src/lib/components/date/FormattedDateTime.spec.tsx +24 -0
- package/src/lib/components/date/FormattedDateTime.tsx +36 -2
- package/src/lib/components/healthselectorv2/HealthSelector.component.test.tsx +3 -3
- package/src/lib/components/icon/Icon.component.tsx +48 -60
- package/src/lib/components/inlineinput/InlineInput.test.tsx +22 -19
- package/src/lib/components/inputlist/InputList.test.tsx +21 -19
- package/src/lib/components/linetimeseriechart/linetimeseriechart.component.tsx +502 -0
- package/src/lib/components/searchinput/SearchInput.test.tsx +3 -7
- package/src/lib/components/selectv2/Selectv2.component.tsx +13 -5
- package/src/lib/components/selectv2/selectv2.test.tsx +62 -57
- package/src/lib/components/steppers/Stepper.component.tsx +10 -8
- package/src/lib/components/tablev2/TableSync.test.tsx +8 -11
- package/src/lib/components/tablev2/Tablev2.test.tsx +36 -37
- package/src/lib/components/toast/ToastProvider.tsx +14 -6
- package/src/lib/components/vegachartv2/SyncedCursorCharts.tsx +5 -7
- package/src/lib/index.ts +1 -0
- package/src/lib/next.ts +1 -0
- package/src/lib/style/theme.ts +29 -0
- package/stories/BarChart/barchart.stories.tsx +292 -125
- package/stories/format.mdx +4 -2
- package/stories/linetimeseriechart.stories.tsx +485 -0
|
@@ -2,6 +2,7 @@ import { render, screen, waitFor } from '@testing-library/react';
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { getWrapper } from '../../testUtils';
|
|
4
4
|
import { Barchart } from './Barchart.component';
|
|
5
|
+
import { ChartLegendWrapper } from '../chartlegend/ChartLegendWrapper';
|
|
5
6
|
|
|
6
7
|
const ONE_DAY_IN_MILLISECONDS = 24 * 60 * 60 * 1000;
|
|
7
8
|
const ONE_HOUR_IN_MILLISECONDS = 60 * 60 * 1000;
|
|
@@ -48,19 +49,19 @@ const testTimeBars = [
|
|
|
48
49
|
},
|
|
49
50
|
] as const;
|
|
50
51
|
|
|
52
|
+
const testColorSet = {
|
|
53
|
+
Success: 'lineColor1',
|
|
54
|
+
};
|
|
55
|
+
|
|
51
56
|
describe('Barchart', () => {
|
|
52
57
|
describe('Basic rendering', () => {
|
|
53
58
|
it('should render the Barchart component with category data', async () => {
|
|
54
59
|
const { Wrapper } = getWrapper();
|
|
55
60
|
render(
|
|
56
61
|
<Wrapper>
|
|
57
|
-
<
|
|
58
|
-
type="category"
|
|
59
|
-
|
|
60
|
-
colorSet={{
|
|
61
|
-
Success: 'lineColor1',
|
|
62
|
-
}}
|
|
63
|
-
/>
|
|
62
|
+
<ChartLegendWrapper colorSet={testColorSet}>
|
|
63
|
+
<Barchart type="category" bars={testBars} />
|
|
64
|
+
</ChartLegendWrapper>
|
|
64
65
|
</Wrapper>,
|
|
65
66
|
);
|
|
66
67
|
|
|
@@ -72,20 +73,19 @@ describe('Barchart', () => {
|
|
|
72
73
|
const { Wrapper } = getWrapper();
|
|
73
74
|
render(
|
|
74
75
|
<Wrapper>
|
|
75
|
-
<
|
|
76
|
-
|
|
77
|
-
type
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
/>
|
|
76
|
+
<ChartLegendWrapper colorSet={testColorSet}>
|
|
77
|
+
<Barchart
|
|
78
|
+
type={{
|
|
79
|
+
type: 'time',
|
|
80
|
+
timeRange: {
|
|
81
|
+
startDate: new Date('2024-07-05'),
|
|
82
|
+
endDate: new Date('2024-07-07'),
|
|
83
|
+
interval: ONE_DAY_IN_MILLISECONDS,
|
|
84
|
+
},
|
|
85
|
+
}}
|
|
86
|
+
bars={testTimeBars}
|
|
87
|
+
/>
|
|
88
|
+
</ChartLegendWrapper>
|
|
89
89
|
</Wrapper>,
|
|
90
90
|
);
|
|
91
91
|
|
|
@@ -100,21 +100,20 @@ describe('Barchart', () => {
|
|
|
100
100
|
const { Wrapper } = getWrapper();
|
|
101
101
|
render(
|
|
102
102
|
<Wrapper>
|
|
103
|
-
<
|
|
104
|
-
|
|
105
|
-
type
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
/>
|
|
103
|
+
<ChartLegendWrapper colorSet={testColorSet}>
|
|
104
|
+
<Barchart
|
|
105
|
+
type={{
|
|
106
|
+
type: 'time',
|
|
107
|
+
timeRange: {
|
|
108
|
+
startDate: new Date('2024-07-03'),
|
|
109
|
+
endDate: new Date('2024-07-07'),
|
|
110
|
+
interval: ONE_DAY_IN_MILLISECONDS,
|
|
111
|
+
},
|
|
112
|
+
}}
|
|
113
|
+
// data starts on 2024-07-05
|
|
114
|
+
bars={testTimeBars}
|
|
115
|
+
/>
|
|
116
|
+
</ChartLegendWrapper>
|
|
118
117
|
</Wrapper>,
|
|
119
118
|
);
|
|
120
119
|
expect(screen.getByText('Wed03Jul')).toBeInTheDocument();
|
|
@@ -154,14 +153,14 @@ describe('Barchart', () => {
|
|
|
154
153
|
const { Wrapper } = getWrapper();
|
|
155
154
|
render(
|
|
156
155
|
<Wrapper>
|
|
157
|
-
<
|
|
158
|
-
type={type}
|
|
159
|
-
bars={bars}
|
|
156
|
+
<ChartLegendWrapper
|
|
160
157
|
colorSet={{
|
|
161
158
|
Success: 'lineColor1',
|
|
162
159
|
Failed: 'lineColor2',
|
|
163
160
|
}}
|
|
164
|
-
|
|
161
|
+
>
|
|
162
|
+
<Barchart type={type} bars={bars} />
|
|
163
|
+
</ChartLegendWrapper>
|
|
165
164
|
</Wrapper>,
|
|
166
165
|
);
|
|
167
166
|
|
|
@@ -202,13 +201,9 @@ describe('Barchart', () => {
|
|
|
202
201
|
const { Wrapper } = getWrapper();
|
|
203
202
|
render(
|
|
204
203
|
<Wrapper>
|
|
205
|
-
<
|
|
206
|
-
type={type}
|
|
207
|
-
|
|
208
|
-
colorSet={{
|
|
209
|
-
Success: 'lineColor1',
|
|
210
|
-
}}
|
|
211
|
-
/>
|
|
204
|
+
<ChartLegendWrapper colorSet={testColorSet}>
|
|
205
|
+
<Barchart type={type} bars={testTimeBars} />
|
|
206
|
+
</ChartLegendWrapper>
|
|
212
207
|
</Wrapper>,
|
|
213
208
|
);
|
|
214
209
|
await waitFor(() => {
|
|
@@ -236,20 +231,19 @@ describe('Barchart', () => {
|
|
|
236
231
|
const { Wrapper } = getWrapper();
|
|
237
232
|
render(
|
|
238
233
|
<Wrapper>
|
|
239
|
-
<
|
|
240
|
-
|
|
241
|
-
type
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
/>
|
|
234
|
+
<ChartLegendWrapper colorSet={testColorSet}>
|
|
235
|
+
<Barchart
|
|
236
|
+
type={{
|
|
237
|
+
type: 'time',
|
|
238
|
+
timeRange: {
|
|
239
|
+
startDate: new Date('2024-07-05T10:00:00'),
|
|
240
|
+
endDate: new Date('2024-07-05T12:00:00'),
|
|
241
|
+
interval: ONE_HOUR_IN_MILLISECONDS,
|
|
242
|
+
},
|
|
243
|
+
}}
|
|
244
|
+
bars={testHourlyBars}
|
|
245
|
+
/>
|
|
246
|
+
</ChartLegendWrapper>
|
|
253
247
|
</Wrapper>,
|
|
254
248
|
);
|
|
255
249
|
|
|
@@ -284,15 +278,9 @@ describe('Barchart', () => {
|
|
|
284
278
|
const { Wrapper } = getWrapper();
|
|
285
279
|
render(
|
|
286
280
|
<Wrapper>
|
|
287
|
-
<
|
|
288
|
-
type="category"
|
|
289
|
-
|
|
290
|
-
stacked={true}
|
|
291
|
-
colorSet={{
|
|
292
|
-
Success: 'lineColor1',
|
|
293
|
-
Failed: 'lineColor2',
|
|
294
|
-
}}
|
|
295
|
-
/>
|
|
281
|
+
<ChartLegendWrapper colorSet={testColorSet}>
|
|
282
|
+
<Barchart type="category" bars={testStackedBars} stacked={true} />
|
|
283
|
+
</ChartLegendWrapper>
|
|
296
284
|
</Wrapper>,
|
|
297
285
|
);
|
|
298
286
|
|
|
@@ -317,18 +305,17 @@ describe('Barchart', () => {
|
|
|
317
305
|
const { Wrapper } = getWrapper();
|
|
318
306
|
render(
|
|
319
307
|
<Wrapper>
|
|
320
|
-
<
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
/>
|
|
308
|
+
<ChartLegendWrapper colorSet={testColorSet}>
|
|
309
|
+
<Barchart
|
|
310
|
+
type="category"
|
|
311
|
+
bars={testBars}
|
|
312
|
+
defaultSort={(pointA, pointB) => {
|
|
313
|
+
const valueA = pointA.Success;
|
|
314
|
+
const valueB = pointB.Success;
|
|
315
|
+
return valueB - valueA > 0 ? 1 : valueB - valueA < 0 ? -1 : 0;
|
|
316
|
+
}}
|
|
317
|
+
/>
|
|
318
|
+
</ChartLegendWrapper>
|
|
332
319
|
</Wrapper>,
|
|
333
320
|
);
|
|
334
321
|
|
|
@@ -343,14 +330,9 @@ describe('Barchart', () => {
|
|
|
343
330
|
const { Wrapper } = getWrapper();
|
|
344
331
|
render(
|
|
345
332
|
<Wrapper>
|
|
346
|
-
<
|
|
347
|
-
type="category"
|
|
348
|
-
|
|
349
|
-
isLoading
|
|
350
|
-
colorSet={{
|
|
351
|
-
Success: 'lineColor1',
|
|
352
|
-
}}
|
|
353
|
-
/>
|
|
333
|
+
<ChartLegendWrapper colorSet={testColorSet}>
|
|
334
|
+
<Barchart type="category" bars={[]} isLoading />
|
|
335
|
+
</ChartLegendWrapper>
|
|
354
336
|
</Wrapper>,
|
|
355
337
|
);
|
|
356
338
|
expect(screen.getByText('Loading Chart Data...')).toBeInTheDocument();
|
|
@@ -359,17 +341,16 @@ describe('Barchart', () => {
|
|
|
359
341
|
const { Wrapper } = getWrapper();
|
|
360
342
|
render(
|
|
361
343
|
<Wrapper>
|
|
362
|
-
<
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
/>
|
|
344
|
+
<ChartLegendWrapper colorSet={testColorSet}>
|
|
345
|
+
<Barchart
|
|
346
|
+
type="category"
|
|
347
|
+
bars={[]}
|
|
348
|
+
title="Test Title"
|
|
349
|
+
secondaryTitle="Test Secondary Title"
|
|
350
|
+
rightTitle="Test Right Title"
|
|
351
|
+
helpTooltip="Test Help Tooltip"
|
|
352
|
+
/>
|
|
353
|
+
</ChartLegendWrapper>
|
|
373
354
|
</Wrapper>,
|
|
374
355
|
);
|
|
375
356
|
|
|
@@ -19,6 +19,7 @@ import { IconHelp } from '../iconhelper/IconHelper';
|
|
|
19
19
|
import { Loader } from '../loader/Loader.component';
|
|
20
20
|
import { Text } from '../text/Text.component';
|
|
21
21
|
import { renderTooltipContent, UnitRange, useChartData } from './utils';
|
|
22
|
+
import { useChartLegend } from '../chartlegend/ChartLegendWrapper';
|
|
22
23
|
|
|
23
24
|
const CHART_CONSTANTS = {
|
|
24
25
|
TICK_WIDTH_OFFSET: 5,
|
|
@@ -70,7 +71,6 @@ export type BarchartSortFn<T extends BarchartBars> = (
|
|
|
70
71
|
export type BarchartProps<T extends BarchartBars> = {
|
|
71
72
|
type: 'category' | TimeType;
|
|
72
73
|
bars: T;
|
|
73
|
-
colorSet: Record<T[number]['label'], ChartColors | (string & {})>;
|
|
74
74
|
tooltip?: BarchartTooltipFn<T>;
|
|
75
75
|
defaultSort?: BarchartSortFn<T>;
|
|
76
76
|
unitRange?: UnitRange;
|
|
@@ -196,13 +196,13 @@ const Loading = ({ height }: { height: number }) => {
|
|
|
196
196
|
|
|
197
197
|
export const Barchart = <T extends BarchartBars>(props: BarchartProps<T>) => {
|
|
198
198
|
const theme = useTheme();
|
|
199
|
+
const { getColor } = useChartLegend();
|
|
199
200
|
const [hoveredValue, setHoveredValue] = useState<string | undefined>();
|
|
200
201
|
|
|
201
202
|
const {
|
|
202
203
|
height = CHART_CONSTANTS.DEFAULT_HEIGHT,
|
|
203
204
|
bars,
|
|
204
205
|
type = 'category',
|
|
205
|
-
colorSet,
|
|
206
206
|
unitRange,
|
|
207
207
|
stacked,
|
|
208
208
|
defaultSort,
|
|
@@ -214,6 +214,18 @@ export const Barchart = <T extends BarchartBars>(props: BarchartProps<T>) => {
|
|
|
214
214
|
isLoading,
|
|
215
215
|
} = props;
|
|
216
216
|
|
|
217
|
+
// Create colorSet from ChartLegendWrapper
|
|
218
|
+
const colorSet = bars.reduce(
|
|
219
|
+
(acc, bar) => {
|
|
220
|
+
const color = getColor(bar.label);
|
|
221
|
+
if (color) {
|
|
222
|
+
acc[bar.label] = color;
|
|
223
|
+
}
|
|
224
|
+
return acc;
|
|
225
|
+
},
|
|
226
|
+
{} as Record<string, ChartColors | string>,
|
|
227
|
+
);
|
|
228
|
+
|
|
217
229
|
const { rechartsBars, unitLabel, roundReferenceValue, rechartsData } =
|
|
218
230
|
useChartData(bars, type, colorSet, stacked, defaultSort, unitRange);
|
|
219
231
|
|
|
@@ -2,6 +2,7 @@ import { coreUIAvailableThemes } from '../../style/theme';
|
|
|
2
2
|
import {
|
|
3
3
|
applySortingToData,
|
|
4
4
|
computeUnitLabelAndRoundReferenceValue,
|
|
5
|
+
filterChartDataAndBarsByLegendSelection,
|
|
5
6
|
formatPrometheusDataToRechartsDataAndBars,
|
|
6
7
|
getMaxBarValue,
|
|
7
8
|
getRoundReferenceValue,
|
|
@@ -780,3 +781,119 @@ describe('renderTooltipContent', () => {
|
|
|
780
781
|
});
|
|
781
782
|
});
|
|
782
783
|
});
|
|
784
|
+
|
|
785
|
+
describe('filterChartDataAndBarsByLegendSelection', () => {
|
|
786
|
+
const mockChartData = [
|
|
787
|
+
{ category: 'Jan', Success: 10, Failed: 5, Warning: 3, Pending: 2 },
|
|
788
|
+
{ category: 'Feb', Success: 20, Failed: 8, Warning: 6, Pending: 4 },
|
|
789
|
+
{ category: 'Mar', Success: 15, Failed: 12, Warning: 9, Pending: 7 },
|
|
790
|
+
];
|
|
791
|
+
|
|
792
|
+
const mockRechartsBars = [
|
|
793
|
+
{ dataKey: 'Success', fill: '#00D100', stackId: undefined },
|
|
794
|
+
{ dataKey: 'Failed', fill: '#D10000', stackId: undefined },
|
|
795
|
+
{ dataKey: 'Warning', fill: '#FFA500', stackId: 'stacked' },
|
|
796
|
+
{ dataKey: 'Pending', fill: '#337FBD', stackId: 'stacked' },
|
|
797
|
+
];
|
|
798
|
+
|
|
799
|
+
it('should return all data and bars when no resources are selected (empty array)', () => {
|
|
800
|
+
const result = filterChartDataAndBarsByLegendSelection(
|
|
801
|
+
mockChartData,
|
|
802
|
+
mockRechartsBars,
|
|
803
|
+
[],
|
|
804
|
+
);
|
|
805
|
+
|
|
806
|
+
expect(result.filteredData).toEqual(mockChartData);
|
|
807
|
+
expect(result.filteredRechartsBars).toEqual(mockRechartsBars);
|
|
808
|
+
expect(result.filteredData).toHaveLength(3);
|
|
809
|
+
expect(result.filteredRechartsBars).toHaveLength(4);
|
|
810
|
+
// Verify all properties are preserved
|
|
811
|
+
expect(Object.keys(result.filteredData[0])).toEqual([
|
|
812
|
+
'category',
|
|
813
|
+
'Success',
|
|
814
|
+
'Failed',
|
|
815
|
+
'Warning',
|
|
816
|
+
'Pending',
|
|
817
|
+
]);
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
it('should return only selected resources in both data and bars when resources are selected', () => {
|
|
821
|
+
const selectedResources = ['Success', 'Warning'];
|
|
822
|
+
const result = filterChartDataAndBarsByLegendSelection(
|
|
823
|
+
mockChartData,
|
|
824
|
+
mockRechartsBars,
|
|
825
|
+
selectedResources,
|
|
826
|
+
);
|
|
827
|
+
|
|
828
|
+
expect(result.filteredData).toHaveLength(3);
|
|
829
|
+
expect(result.filteredData).toEqual([
|
|
830
|
+
{ category: 'Jan', Success: 10, Warning: 3 },
|
|
831
|
+
{ category: 'Feb', Success: 20, Warning: 6 },
|
|
832
|
+
{ category: 'Mar', Success: 15, Warning: 9 },
|
|
833
|
+
]);
|
|
834
|
+
|
|
835
|
+
expect(result.filteredRechartsBars).toHaveLength(2);
|
|
836
|
+
expect(result.filteredRechartsBars).toEqual([
|
|
837
|
+
{ dataKey: 'Success', fill: '#00D100', stackId: undefined },
|
|
838
|
+
{ dataKey: 'Warning', fill: '#FFA500', stackId: 'stacked' },
|
|
839
|
+
]);
|
|
840
|
+
});
|
|
841
|
+
|
|
842
|
+
it('should return single resource when only one resource is selected', () => {
|
|
843
|
+
const selectedResources = ['Failed'];
|
|
844
|
+
const result = filterChartDataAndBarsByLegendSelection(
|
|
845
|
+
mockChartData,
|
|
846
|
+
mockRechartsBars,
|
|
847
|
+
selectedResources,
|
|
848
|
+
);
|
|
849
|
+
|
|
850
|
+
expect(result.filteredData).toHaveLength(3);
|
|
851
|
+
expect(result.filteredData).toEqual([
|
|
852
|
+
{ category: 'Jan', Failed: 5 },
|
|
853
|
+
{ category: 'Feb', Failed: 8 },
|
|
854
|
+
{ category: 'Mar', Failed: 12 },
|
|
855
|
+
]);
|
|
856
|
+
|
|
857
|
+
expect(result.filteredRechartsBars).toHaveLength(1);
|
|
858
|
+
expect(result.filteredRechartsBars).toEqual([
|
|
859
|
+
{ dataKey: 'Failed', fill: '#D10000', stackId: undefined },
|
|
860
|
+
]);
|
|
861
|
+
});
|
|
862
|
+
|
|
863
|
+
it('should handle empty data array', () => {
|
|
864
|
+
const result = filterChartDataAndBarsByLegendSelection(
|
|
865
|
+
[],
|
|
866
|
+
mockRechartsBars,
|
|
867
|
+
['Success'],
|
|
868
|
+
);
|
|
869
|
+
|
|
870
|
+
expect(result.filteredData).toEqual([]);
|
|
871
|
+
expect(result.filteredRechartsBars).toHaveLength(1);
|
|
872
|
+
expect(result.filteredRechartsBars).toEqual([
|
|
873
|
+
{ dataKey: 'Success', fill: '#00D100', stackId: undefined },
|
|
874
|
+
]);
|
|
875
|
+
});
|
|
876
|
+
|
|
877
|
+
it('should preserve order of selected resources based on data object keys', () => {
|
|
878
|
+
const selectedResources = ['Pending', 'Success', 'Failed']; // Different order
|
|
879
|
+
const result = filterChartDataAndBarsByLegendSelection(
|
|
880
|
+
mockChartData,
|
|
881
|
+
mockRechartsBars,
|
|
882
|
+
selectedResources,
|
|
883
|
+
);
|
|
884
|
+
|
|
885
|
+
// Should maintain the order they appear in selectedResources
|
|
886
|
+
expect(Object.keys(result.filteredData[0])).toEqual([
|
|
887
|
+
'category',
|
|
888
|
+
'Pending',
|
|
889
|
+
'Success',
|
|
890
|
+
'Failed',
|
|
891
|
+
]);
|
|
892
|
+
|
|
893
|
+
// Should maintain original bar order regardless of selection order
|
|
894
|
+
expect(result.filteredRechartsBars).toHaveLength(3);
|
|
895
|
+
expect(result.filteredRechartsBars[0].dataKey).toBe('Success');
|
|
896
|
+
expect(result.filteredRechartsBars[1].dataKey).toBe('Failed');
|
|
897
|
+
expect(result.filteredRechartsBars[2].dataKey).toBe('Pending');
|
|
898
|
+
});
|
|
899
|
+
});
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
import { DAY_MONTH_FORMATER, TIME_FORMATER } from '../date/FormattedDateTime';
|
|
7
7
|
import { TooltipContentProps } from 'recharts';
|
|
8
8
|
import { chartColors, ChartColors } from '../../style/theme';
|
|
9
|
+
import { useChartLegend } from '../chartlegend/ChartLegendWrapper';
|
|
9
10
|
|
|
10
11
|
export const getRoundReferenceValue = (value: number): number => {
|
|
11
12
|
if (value <= 0) return 10; // Default for zero or negative values
|
|
@@ -248,7 +249,7 @@ export const applySortingToData = <T extends BarchartBars>(
|
|
|
248
249
|
|
|
249
250
|
const getRechartsBarsAndBarDataKeys = (
|
|
250
251
|
bars: BarchartBars,
|
|
251
|
-
colorSet: Record<
|
|
252
|
+
colorSet: Record<string, ChartColors | string>,
|
|
252
253
|
stacked?: boolean,
|
|
253
254
|
) => {
|
|
254
255
|
const rechartsBars: { dataKey: string; fill: string; stackId?: string }[] =
|
|
@@ -284,7 +285,7 @@ export const formatPrometheusDataToRechartsDataAndBars = <
|
|
|
284
285
|
>(
|
|
285
286
|
bars: T,
|
|
286
287
|
type: BarchartProps<T>['type'],
|
|
287
|
-
colorSet: Record<
|
|
288
|
+
colorSet: Record<string, ChartColors | string>,
|
|
288
289
|
stacked?: boolean,
|
|
289
290
|
defaultSort?: BarchartProps<T>['defaultSort'],
|
|
290
291
|
): {
|
|
@@ -456,14 +457,53 @@ export const renderTooltipContent = <T extends BarchartBars>(
|
|
|
456
457
|
return tooltip(currentPoint);
|
|
457
458
|
};
|
|
458
459
|
|
|
460
|
+
/**
|
|
461
|
+
* Filters both chart data and recharts bars to only include selected resources from legend
|
|
462
|
+
* @param data - Array of chart data objects with category and resource values
|
|
463
|
+
* @param rechartsBars - Array of recharts bar configurations
|
|
464
|
+
* @param selectedResources - Array of selected resource names
|
|
465
|
+
* @returns Object containing filtered data and recharts bars
|
|
466
|
+
*/
|
|
467
|
+
export const filterChartDataAndBarsByLegendSelection = (
|
|
468
|
+
data: { [key: string]: string | number }[],
|
|
469
|
+
rechartsBars: { dataKey: string; fill: string; stackId?: string }[],
|
|
470
|
+
selectedResources: string[],
|
|
471
|
+
) => {
|
|
472
|
+
// If no resources are selected, show all data and bars (default behavior)
|
|
473
|
+
if (selectedResources.length === 0) {
|
|
474
|
+
return { filteredData: data, filteredRechartsBars: rechartsBars };
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// Filter recharts bars
|
|
478
|
+
const filteredRechartsBars = rechartsBars.filter((bar) =>
|
|
479
|
+
selectedResources.includes(bar.dataKey),
|
|
480
|
+
);
|
|
481
|
+
|
|
482
|
+
// Filter data to only include selected resources
|
|
483
|
+
const filteredData = data.map((item) => {
|
|
484
|
+
const filteredItem: { [key: string]: string | number } = {
|
|
485
|
+
category: item.category,
|
|
486
|
+
};
|
|
487
|
+
selectedResources.forEach((resource) => {
|
|
488
|
+
if (resource in item) {
|
|
489
|
+
filteredItem[resource] = item[resource];
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
return filteredItem;
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
return { filteredData, filteredRechartsBars };
|
|
496
|
+
};
|
|
497
|
+
|
|
459
498
|
export const useChartData = <T extends BarchartBars>(
|
|
460
499
|
bars: T,
|
|
461
500
|
type: BarchartProps<T>['type'],
|
|
462
|
-
colorSet: Record<
|
|
501
|
+
colorSet: Record<string, ChartColors | string>,
|
|
463
502
|
stacked?: boolean,
|
|
464
503
|
defaultSort?: BarchartProps<T>['defaultSort'],
|
|
465
504
|
unitRange?: UnitRange,
|
|
466
505
|
) => {
|
|
506
|
+
const { selectedResources } = useChartLegend();
|
|
467
507
|
const { data, rechartsBars } = formatPrometheusDataToRechartsDataAndBars(
|
|
468
508
|
bars,
|
|
469
509
|
type,
|
|
@@ -472,13 +512,21 @@ export const useChartData = <T extends BarchartBars>(
|
|
|
472
512
|
defaultSort,
|
|
473
513
|
);
|
|
474
514
|
|
|
475
|
-
|
|
515
|
+
// Filter both data and bars to only include selected resources for accurate maxValue calculation
|
|
516
|
+
const { filteredData, filteredRechartsBars } =
|
|
517
|
+
filterChartDataAndBarsByLegendSelection(
|
|
518
|
+
data,
|
|
519
|
+
rechartsBars,
|
|
520
|
+
selectedResources,
|
|
521
|
+
);
|
|
522
|
+
|
|
523
|
+
const maxValue = getMaxBarValue(filteredData, stacked);
|
|
476
524
|
|
|
477
525
|
const { unitLabel, roundReferenceValue, rechartsData } =
|
|
478
|
-
computeUnitLabelAndRoundReferenceValue(
|
|
526
|
+
computeUnitLabelAndRoundReferenceValue(filteredData, maxValue, unitRange);
|
|
479
527
|
|
|
480
528
|
return {
|
|
481
|
-
rechartsBars,
|
|
529
|
+
rechartsBars: filteredRechartsBars,
|
|
482
530
|
unitLabel,
|
|
483
531
|
roundReferenceValue,
|
|
484
532
|
rechartsData,
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
import { useChartLegend } from './ChartLegendWrapper';
|
|
3
|
+
import { Text } from '../text/Text.component';
|
|
4
|
+
import { chartColors } from '../../style/theme';
|
|
5
|
+
import { useCallback } from 'react';
|
|
6
|
+
|
|
7
|
+
type ChartLegendProps = {
|
|
8
|
+
shape: 'line' | 'rectangle';
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
direction?: 'horizontal' | 'vertical';
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const Legend = styled.div<{ direction: 'horizontal' | 'vertical' }>`
|
|
14
|
+
display: flex;
|
|
15
|
+
flex-direction: ${({ direction }) =>
|
|
16
|
+
direction === 'horizontal' ? 'row' : 'column'};
|
|
17
|
+
gap: ${({ direction }) => (direction === 'horizontal' ? '16px' : '8px')};
|
|
18
|
+
flex-wrap: wrap;
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
const LegendItem = styled.div<{ disabled?: boolean; selected?: boolean }>`
|
|
22
|
+
display: flex;
|
|
23
|
+
align-items: center;
|
|
24
|
+
gap: 8px;
|
|
25
|
+
cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
|
|
26
|
+
opacity: ${({ selected, disabled }) => (disabled ? 0.5 : selected ? 1 : 0.7)};
|
|
27
|
+
transition: opacity 0.2s ease;
|
|
28
|
+
|
|
29
|
+
&:hover {
|
|
30
|
+
opacity: ${({ disabled }) => (disabled ? 0.5 : 1)};
|
|
31
|
+
}
|
|
32
|
+
`;
|
|
33
|
+
|
|
34
|
+
const LegendShape = styled.div<{
|
|
35
|
+
color?: string;
|
|
36
|
+
shape: 'line' | 'rectangle';
|
|
37
|
+
chartColors: Record<string, string>;
|
|
38
|
+
}>`
|
|
39
|
+
${({ shape, color, chartColors }) => {
|
|
40
|
+
if (shape === 'line') {
|
|
41
|
+
return `
|
|
42
|
+
width: 20px;
|
|
43
|
+
height: 2px;
|
|
44
|
+
background-color: ${chartColors[color as keyof typeof chartColors] || color};
|
|
45
|
+
`;
|
|
46
|
+
} else if (shape === 'rectangle') {
|
|
47
|
+
return `
|
|
48
|
+
width: 12px;
|
|
49
|
+
height: 12px;
|
|
50
|
+
background-color: ${chartColors[color as keyof typeof chartColors] || color};
|
|
51
|
+
border-radius: 2px;
|
|
52
|
+
`;
|
|
53
|
+
} else {
|
|
54
|
+
console.error(
|
|
55
|
+
'The shape is not valid. Please use "line" or "rectangle".',
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
}}
|
|
59
|
+
`;
|
|
60
|
+
|
|
61
|
+
export const ChartLegend = ({
|
|
62
|
+
shape,
|
|
63
|
+
disabled = false,
|
|
64
|
+
direction = 'horizontal',
|
|
65
|
+
}: ChartLegendProps) => {
|
|
66
|
+
const {
|
|
67
|
+
listResources,
|
|
68
|
+
getColor,
|
|
69
|
+
isSelected,
|
|
70
|
+
addSelectedResource,
|
|
71
|
+
removeSelectedResource,
|
|
72
|
+
} = useChartLegend();
|
|
73
|
+
|
|
74
|
+
const resources = listResources();
|
|
75
|
+
|
|
76
|
+
const handleLegendClick = useCallback(
|
|
77
|
+
(resource: string) => {
|
|
78
|
+
if (disabled) return;
|
|
79
|
+
|
|
80
|
+
if (isSelected(resource)) {
|
|
81
|
+
removeSelectedResource(resource);
|
|
82
|
+
} else {
|
|
83
|
+
addSelectedResource(resource);
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
[disabled, isSelected, addSelectedResource, removeSelectedResource],
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<Legend direction={direction}>
|
|
91
|
+
{resources.map((resource) => {
|
|
92
|
+
const color = getColor(resource);
|
|
93
|
+
const selected = isSelected(resource);
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<LegendItem
|
|
97
|
+
key={resource}
|
|
98
|
+
disabled={disabled}
|
|
99
|
+
selected={selected}
|
|
100
|
+
onClick={() => handleLegendClick(resource)}
|
|
101
|
+
>
|
|
102
|
+
<LegendShape
|
|
103
|
+
color={color}
|
|
104
|
+
shape={shape}
|
|
105
|
+
chartColors={chartColors}
|
|
106
|
+
/>
|
|
107
|
+
<Text variant="Basic">{resource}</Text>
|
|
108
|
+
</LegendItem>
|
|
109
|
+
);
|
|
110
|
+
})}
|
|
111
|
+
</Legend>
|
|
112
|
+
);
|
|
113
|
+
};
|