@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.
Files changed (68) hide show
  1. package/dist/components/barchartv2/Barchart.component.d.ts +0 -2
  2. package/dist/components/barchartv2/Barchart.component.d.ts.map +1 -1
  3. package/dist/components/barchartv2/Barchart.component.js +11 -1
  4. package/dist/components/barchartv2/utils.d.ts +25 -2
  5. package/dist/components/barchartv2/utils.d.ts.map +1 -1
  6. package/dist/components/barchartv2/utils.js +35 -3
  7. package/dist/components/chartlegend/ChartLegend.d.ts +8 -0
  8. package/dist/components/chartlegend/ChartLegend.d.ts.map +1 -0
  9. package/dist/components/chartlegend/ChartLegend.js +65 -0
  10. package/dist/components/chartlegend/ChartLegendWrapper.d.ts +17 -0
  11. package/dist/components/chartlegend/ChartLegendWrapper.d.ts.map +1 -0
  12. package/dist/components/chartlegend/ChartLegendWrapper.js +50 -0
  13. package/dist/components/date/FormattedDateTime.d.ts +3 -1
  14. package/dist/components/date/FormattedDateTime.d.ts.map +1 -1
  15. package/dist/components/date/FormattedDateTime.js +19 -1
  16. package/dist/components/date/FormattedDateTime.spec.js +12 -0
  17. package/dist/components/icon/Icon.component.d.ts +5 -5
  18. package/dist/components/icon/Icon.component.d.ts.map +1 -1
  19. package/dist/components/icon/Icon.component.js +33 -31
  20. package/dist/components/linetimeseriechart/linetimeseriechart.component.d.ts +33 -0
  21. package/dist/components/linetimeseriechart/linetimeseriechart.component.d.ts.map +1 -0
  22. package/dist/components/linetimeseriechart/linetimeseriechart.component.js +249 -0
  23. package/dist/components/selectv2/Selectv2.component.d.ts.map +1 -1
  24. package/dist/components/selectv2/Selectv2.component.js +11 -6
  25. package/dist/components/steppers/Stepper.component.d.ts.map +1 -1
  26. package/dist/components/steppers/Stepper.component.js +9 -8
  27. package/dist/components/toast/ToastProvider.d.ts.map +1 -1
  28. package/dist/components/toast/ToastProvider.js +4 -5
  29. package/dist/components/vegachartv2/SyncedCursorCharts.d.ts.map +1 -1
  30. package/dist/components/vegachartv2/SyncedCursorCharts.js +3 -5
  31. package/dist/index.d.ts +1 -0
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +1 -0
  34. package/dist/next.d.ts +1 -0
  35. package/dist/next.d.ts.map +1 -1
  36. package/dist/next.js +1 -0
  37. package/dist/style/theme.d.ts +1 -0
  38. package/dist/style/theme.d.ts.map +1 -1
  39. package/dist/style/theme.js +28 -0
  40. package/package.json +2 -2
  41. package/src/lib/components/accordion/Accordion.test.tsx +7 -15
  42. package/src/lib/components/barchartv2/Barchart.component.test.tsx +82 -101
  43. package/src/lib/components/barchartv2/Barchart.component.tsx +14 -2
  44. package/src/lib/components/barchartv2/utils.test.ts +117 -0
  45. package/src/lib/components/barchartv2/utils.ts +54 -6
  46. package/src/lib/components/chartlegend/ChartLegend.tsx +113 -0
  47. package/src/lib/components/chartlegend/ChartLegendWrapper.tsx +85 -0
  48. package/src/lib/components/date/FormattedDateTime.spec.tsx +24 -0
  49. package/src/lib/components/date/FormattedDateTime.tsx +36 -2
  50. package/src/lib/components/healthselectorv2/HealthSelector.component.test.tsx +3 -3
  51. package/src/lib/components/icon/Icon.component.tsx +48 -60
  52. package/src/lib/components/inlineinput/InlineInput.test.tsx +22 -19
  53. package/src/lib/components/inputlist/InputList.test.tsx +21 -19
  54. package/src/lib/components/linetimeseriechart/linetimeseriechart.component.tsx +502 -0
  55. package/src/lib/components/searchinput/SearchInput.test.tsx +3 -7
  56. package/src/lib/components/selectv2/Selectv2.component.tsx +13 -5
  57. package/src/lib/components/selectv2/selectv2.test.tsx +62 -57
  58. package/src/lib/components/steppers/Stepper.component.tsx +10 -8
  59. package/src/lib/components/tablev2/TableSync.test.tsx +8 -11
  60. package/src/lib/components/tablev2/Tablev2.test.tsx +36 -37
  61. package/src/lib/components/toast/ToastProvider.tsx +14 -6
  62. package/src/lib/components/vegachartv2/SyncedCursorCharts.tsx +5 -7
  63. package/src/lib/index.ts +1 -0
  64. package/src/lib/next.ts +1 -0
  65. package/src/lib/style/theme.ts +29 -0
  66. package/stories/BarChart/barchart.stories.tsx +292 -125
  67. package/stories/format.mdx +4 -2
  68. 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
- <Barchart
58
- type="category"
59
- bars={testBars}
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
- <Barchart
76
- type={{
77
- type: 'time',
78
- timeRange: {
79
- startDate: new Date('2024-07-05'),
80
- endDate: new Date('2024-07-07'),
81
- interval: ONE_DAY_IN_MILLISECONDS,
82
- },
83
- }}
84
- bars={testTimeBars}
85
- colorSet={{
86
- Success: 'lineColor1',
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
- <Barchart
104
- type={{
105
- type: 'time',
106
- timeRange: {
107
- startDate: new Date('2024-07-03'),
108
- endDate: new Date('2024-07-07'),
109
- interval: ONE_DAY_IN_MILLISECONDS,
110
- },
111
- }}
112
- // data starts on 2024-07-05
113
- bars={testTimeBars}
114
- colorSet={{
115
- Success: 'lineColor1',
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
- <Barchart
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
- <Barchart
206
- type={type}
207
- bars={testTimeBars}
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
- <Barchart
240
- type={{
241
- type: 'time',
242
- timeRange: {
243
- startDate: new Date('2024-07-05T10:00:00'),
244
- endDate: new Date('2024-07-05T12:00:00'),
245
- interval: ONE_HOUR_IN_MILLISECONDS,
246
- },
247
- }}
248
- bars={testHourlyBars}
249
- colorSet={{
250
- Success: 'lineColor1',
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
- <Barchart
288
- type="category"
289
- bars={testStackedBars}
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
- <Barchart
321
- type="category"
322
- bars={testBars}
323
- defaultSort={(pointA, pointB) => {
324
- const valueA = pointA.Success;
325
- const valueB = pointB.Success;
326
- return valueB - valueA > 0 ? 1 : valueB - valueA < 0 ? -1 : 0;
327
- }}
328
- colorSet={{
329
- Success: 'lineColor1',
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
- <Barchart
347
- type="category"
348
- bars={[]}
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
- <Barchart
363
- type="category"
364
- bars={[]}
365
- title="Test Title"
366
- secondaryTitle="Test Secondary Title"
367
- rightTitle="Test Right Title"
368
- helpTooltip="Test Help Tooltip"
369
- colorSet={{
370
- Success: 'lineColor1',
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<BarchartBars[number]['label'], ChartColors | (string & {})>,
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<T[number]['label'], ChartColors | (string & {})>,
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<T[number]['label'], ChartColors | (string & {})>,
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
- const maxValue = getMaxBarValue(data, stacked);
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(data, maxValue, unitRange);
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
+ };