@scality/core-ui 0.162.0 → 0.164.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 (73) hide show
  1. package/dist/components/barchartv2/Barchart.component.d.ts +9 -3
  2. package/dist/components/barchartv2/Barchart.component.d.ts.map +1 -1
  3. package/dist/components/barchartv2/Barchart.component.js +22 -5
  4. package/dist/components/barchartv2/utils.d.ts +26 -3
  5. package/dist/components/barchartv2/utils.d.ts.map +1 -1
  6. package/dist/components/barchartv2/utils.js +76 -22
  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 +2 -0
  35. package/dist/next.d.ts.map +1 -1
  36. package/dist/next.js +2 -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 +8 -16
  42. package/src/lib/components/barchartv2/Barchart.component.test.tsx +117 -111
  43. package/src/lib/components/barchartv2/Barchart.component.tsx +54 -7
  44. package/src/lib/components/barchartv2/utils.test.ts +127 -2
  45. package/src/lib/components/barchartv2/utils.ts +103 -19
  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 +10 -10
  51. package/src/lib/components/icon/Icon.component.tsx +48 -60
  52. package/src/lib/components/infomessage/InfoMessageUtils.test.tsx +0 -1
  53. package/src/lib/components/inlineinput/InlineInput.test.tsx +28 -22
  54. package/src/lib/components/inputlist/InputList.test.tsx +22 -21
  55. package/src/lib/components/linetemporalchart/ChartUtil.test.ts +5 -4
  56. package/src/lib/components/linetimeseriechart/linetimeseriechart.component.tsx +502 -0
  57. package/src/lib/components/searchinput/SearchInput.test.tsx +3 -7
  58. package/src/lib/components/selectv2/Selectv2.component.tsx +13 -5
  59. package/src/lib/components/selectv2/selectv2.test.tsx +70 -61
  60. package/src/lib/components/steppers/Stepper.component.tsx +10 -8
  61. package/src/lib/components/tablev2/TableSync.test.tsx +8 -12
  62. package/src/lib/components/tablev2/TableUtils.test.ts +6 -3
  63. package/src/lib/components/tablev2/Tablev2.test.tsx +38 -40
  64. package/src/lib/components/toast/ToastProvider.tsx +14 -6
  65. package/src/lib/components/toggle/Toggle.test.tsx +1 -1
  66. package/src/lib/components/vegachartv2/SyncedCursorCharts.tsx +5 -7
  67. package/src/lib/index.ts +1 -0
  68. package/src/lib/next.ts +2 -0
  69. package/src/lib/style/theme.ts +29 -0
  70. package/stories/BarChart/barchart.stories.tsx +387 -129
  71. package/stories/format.mdx +4 -2
  72. package/stories/linetimeseriechart.stories.tsx +485 -0
  73. package/tsconfig.json +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scality/core-ui",
3
- "version": "0.162.0",
3
+ "version": "0.164.0",
4
4
  "description": "Scality common React component library",
5
5
  "author": "Scality Engineering",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -108,7 +108,7 @@
108
108
  "@fortawesome/fontawesome-svg-core": "^1.2.35",
109
109
  "@fortawesome/free-regular-svg-icons": "^5.15.3",
110
110
  "@fortawesome/free-solid-svg-icons": "^5.15.3",
111
- "@fortawesome/react-fontawesome": "^0.1.14",
111
+ "@fortawesome/react-fontawesome": "^0.2.3",
112
112
  "@js-temporal/polyfill": "^0.4.4",
113
113
  "@storybook/preview-api": "^8.3.6",
114
114
  "downshift": "^7.0.5",
@@ -1,8 +1,7 @@
1
- import { render, screen } from '@testing-library/react';
2
- import React, { useState } from 'react';
1
+ import { render, screen, waitFor } from '@testing-library/react';
2
+ import { useState } from 'react';
3
3
  import { Accordion } from './Accordion.component';
4
4
  import userEvent from '@testing-library/user-event';
5
- import { QueryClient, QueryClientProvider } from 'react-query';
6
5
 
7
6
  describe('Accordion', () => {
8
7
  const selectors = {
@@ -11,20 +10,18 @@ describe('Accordion', () => {
11
10
  accordionContent: () => screen.queryByText(/Test content/i),
12
11
  };
13
12
  const SUT = ({ open = false }) => {
14
- const queryClient = new QueryClient();
15
13
  return (
16
- <QueryClientProvider client={queryClient}>
17
- <Accordion title="Advanced Testings" id="test-accordion" open={open}>
18
- <div>Test content</div>
19
- </Accordion>
20
- </QueryClientProvider>
14
+ <Accordion title="Advanced Testings" id="test-accordion" open={open}>
15
+ <div>Test content</div>
16
+ </Accordion>
21
17
  );
22
18
  };
23
19
  const renderAccordion = (open = false) => {
24
20
  render(<SUT open={open} />);
25
21
  };
26
- it('should render the Accordion component with title and content', () => {
22
+ it('should render the Accordion component with title and content', async () => {
27
23
  renderAccordion();
24
+ await waitFor(() => screen.findByRole('img', { hidden: true }));
28
25
 
29
26
  const accordionToggle = selectors.accordionToggle();
30
27
  expect(accordionToggle).toBeInTheDocument();
@@ -54,7 +51,6 @@ describe('Accordion', () => {
54
51
  });
55
52
 
56
53
  it('should toggle the content when open prop changes', () => {
57
- const queryClient = new QueryClient();
58
54
  const TestWrapper = () => {
59
55
  const [isOpen, setisOpen] = useState(false);
60
56
  return (
@@ -65,11 +61,7 @@ describe('Accordion', () => {
65
61
  );
66
62
  };
67
63
 
68
- render(
69
- <QueryClientProvider client={queryClient}>
70
- <TestWrapper />
71
- </QueryClientProvider>,
72
- );
64
+ render(<TestWrapper />);
73
65
 
74
66
  userEvent.click(screen.getByRole('button', { name: /Test button/i }));
75
67
  expect(selectors.accordionContent()).toBeInTheDocument();
@@ -1,7 +1,7 @@
1
1
  import { render, screen, waitFor } from '@testing-library/react';
2
- import React from 'react';
3
2
  import { getWrapper } from '../../testUtils';
4
3
  import { Barchart } from './Barchart.component';
4
+ import { ChartLegendWrapper } from '../chartlegend/ChartLegendWrapper';
5
5
 
6
6
  const ONE_DAY_IN_MILLISECONDS = 24 * 60 * 60 * 1000;
7
7
  const ONE_HOUR_IN_MILLISECONDS = 60 * 60 * 1000;
@@ -14,7 +14,7 @@ jest.mock('recharts', () => {
14
14
  ...OriginalResponsiveContainerModule,
15
15
  ResponsiveContainer: ({ height, children }) => (
16
16
  <OriginalResponsiveContainerModule.ResponsiveContainer
17
- width={800}
17
+ aspect={3}
18
18
  height={300}
19
19
  data-testid="responsive-container"
20
20
  >
@@ -48,19 +48,19 @@ const testTimeBars = [
48
48
  },
49
49
  ] as const;
50
50
 
51
+ const testColorSet = {
52
+ Success: 'lineColor1',
53
+ };
54
+
51
55
  describe('Barchart', () => {
52
56
  describe('Basic rendering', () => {
53
57
  it('should render the Barchart component with category data', async () => {
54
58
  const { Wrapper } = getWrapper();
55
59
  render(
56
60
  <Wrapper>
57
- <Barchart
58
- type="category"
59
- bars={testBars}
60
- colorSet={{
61
- Success: 'lineColor1',
62
- }}
63
- />
61
+ <ChartLegendWrapper colorSet={testColorSet}>
62
+ <Barchart type="category" bars={testBars} />
63
+ </ChartLegendWrapper>
64
64
  </Wrapper>,
65
65
  );
66
66
 
@@ -72,20 +72,19 @@ describe('Barchart', () => {
72
72
  const { Wrapper } = getWrapper();
73
73
  render(
74
74
  <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
- />
75
+ <ChartLegendWrapper colorSet={testColorSet}>
76
+ <Barchart
77
+ type={{
78
+ type: 'time',
79
+ timeRange: {
80
+ startDate: new Date('2024-07-05'),
81
+ endDate: new Date('2024-07-07'),
82
+ interval: ONE_DAY_IN_MILLISECONDS,
83
+ },
84
+ }}
85
+ bars={testTimeBars}
86
+ />
87
+ </ChartLegendWrapper>
89
88
  </Wrapper>,
90
89
  );
91
90
 
@@ -93,6 +92,43 @@ describe('Barchart', () => {
93
92
  expect(screen.getByText('Sat06Jul')).toBeInTheDocument();
94
93
  expect(screen.getByText('Sun07Jul')).toBeInTheDocument();
95
94
  });
95
+ it('should render the Barchart component with error state', async () => {
96
+ const { Wrapper } = getWrapper();
97
+ render(
98
+ <Wrapper>
99
+ <ChartLegendWrapper colorSet={testColorSet}>
100
+ <Barchart type="category" bars={[]} isError />
101
+ </ChartLegendWrapper>
102
+ </Wrapper>,
103
+ );
104
+ expect(
105
+ screen.getByText('Chart data is not available'),
106
+ ).toBeInTheDocument();
107
+ });
108
+ it('should render the Barchart component with loading state', async () => {
109
+ const { Wrapper } = getWrapper();
110
+ render(
111
+ <Wrapper>
112
+ <ChartLegendWrapper colorSet={testColorSet}>
113
+ <Barchart type="category" bars={[]} isLoading />
114
+ </ChartLegendWrapper>
115
+ </Wrapper>,
116
+ );
117
+ expect(screen.getByText('Loading Chart Data...')).toBeInTheDocument();
118
+ });
119
+ it('should render the Barchart component with undefined bars', async () => {
120
+ const { Wrapper } = getWrapper();
121
+ render(
122
+ <Wrapper>
123
+ <ChartLegendWrapper colorSet={testColorSet}>
124
+ <Barchart type="category" bars={undefined} />
125
+ </ChartLegendWrapper>
126
+ </Wrapper>,
127
+ );
128
+ expect(
129
+ screen.getByText('Chart data is not available'),
130
+ ).toBeInTheDocument();
131
+ });
96
132
  });
97
133
 
98
134
  describe('Time data', () => {
@@ -100,21 +136,20 @@ describe('Barchart', () => {
100
136
  const { Wrapper } = getWrapper();
101
137
  render(
102
138
  <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
- />
139
+ <ChartLegendWrapper colorSet={testColorSet}>
140
+ <Barchart
141
+ type={{
142
+ type: 'time',
143
+ timeRange: {
144
+ startDate: new Date('2024-07-03'),
145
+ endDate: new Date('2024-07-07'),
146
+ interval: ONE_DAY_IN_MILLISECONDS,
147
+ },
148
+ }}
149
+ // data starts on 2024-07-05
150
+ bars={testTimeBars}
151
+ />
152
+ </ChartLegendWrapper>
118
153
  </Wrapper>,
119
154
  );
120
155
  expect(screen.getByText('Wed03Jul')).toBeInTheDocument();
@@ -154,14 +189,14 @@ describe('Barchart', () => {
154
189
  const { Wrapper } = getWrapper();
155
190
  render(
156
191
  <Wrapper>
157
- <Barchart
158
- type={type}
159
- bars={bars}
192
+ <ChartLegendWrapper
160
193
  colorSet={{
161
194
  Success: 'lineColor1',
162
195
  Failed: 'lineColor2',
163
196
  }}
164
- />
197
+ >
198
+ <Barchart type={type} bars={bars} />
199
+ </ChartLegendWrapper>
165
200
  </Wrapper>,
166
201
  );
167
202
 
@@ -202,13 +237,9 @@ describe('Barchart', () => {
202
237
  const { Wrapper } = getWrapper();
203
238
  render(
204
239
  <Wrapper>
205
- <Barchart
206
- type={type}
207
- bars={testTimeBars}
208
- colorSet={{
209
- Success: 'lineColor1',
210
- }}
211
- />
240
+ <ChartLegendWrapper colorSet={testColorSet}>
241
+ <Barchart type={type} bars={testTimeBars} />
242
+ </ChartLegendWrapper>
212
243
  </Wrapper>,
213
244
  );
214
245
  await waitFor(() => {
@@ -236,20 +267,19 @@ describe('Barchart', () => {
236
267
  const { Wrapper } = getWrapper();
237
268
  render(
238
269
  <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
- />
270
+ <ChartLegendWrapper colorSet={testColorSet}>
271
+ <Barchart
272
+ type={{
273
+ type: 'time',
274
+ timeRange: {
275
+ startDate: new Date('2024-07-05T10:00:00'),
276
+ endDate: new Date('2024-07-05T12:00:00'),
277
+ interval: ONE_HOUR_IN_MILLISECONDS,
278
+ },
279
+ }}
280
+ bars={testHourlyBars}
281
+ />
282
+ </ChartLegendWrapper>
253
283
  </Wrapper>,
254
284
  );
255
285
 
@@ -284,15 +314,9 @@ describe('Barchart', () => {
284
314
  const { Wrapper } = getWrapper();
285
315
  render(
286
316
  <Wrapper>
287
- <Barchart
288
- type="category"
289
- bars={testStackedBars}
290
- stacked={true}
291
- colorSet={{
292
- Success: 'lineColor1',
293
- Failed: 'lineColor2',
294
- }}
295
- />
317
+ <ChartLegendWrapper colorSet={{ ...testColorSet, Failed: 'red' }}>
318
+ <Barchart type="category" bars={testStackedBars} stacked={true} />
319
+ </ChartLegendWrapper>
296
320
  </Wrapper>,
297
321
  );
298
322
 
@@ -317,18 +341,17 @@ describe('Barchart', () => {
317
341
  const { Wrapper } = getWrapper();
318
342
  render(
319
343
  <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
- />
344
+ <ChartLegendWrapper colorSet={testColorSet}>
345
+ <Barchart
346
+ type="category"
347
+ bars={testBars}
348
+ defaultSort={(pointA, pointB) => {
349
+ const valueA = pointA.Success;
350
+ const valueB = pointB.Success;
351
+ return valueB - valueA > 0 ? 1 : valueB - valueA < 0 ? -1 : 0;
352
+ }}
353
+ />
354
+ </ChartLegendWrapper>
332
355
  </Wrapper>,
333
356
  );
334
357
 
@@ -339,37 +362,20 @@ describe('Barchart', () => {
339
362
  expect(categories[2]).toHaveTextContent('category1'); // 10 (lowest)
340
363
  });
341
364
 
342
- it('should render the Barchart component with loading state', () => {
343
- const { Wrapper } = getWrapper();
344
- render(
345
- <Wrapper>
346
- <Barchart
347
- type="category"
348
- bars={[]}
349
- isLoading
350
- colorSet={{
351
- Success: 'lineColor1',
352
- }}
353
- />
354
- </Wrapper>,
355
- );
356
- expect(screen.getByText('Loading Chart Data...')).toBeInTheDocument();
357
- });
358
365
  it('should render header with title, secondary title, right title and help tooltip', async () => {
359
366
  const { Wrapper } = getWrapper();
360
367
  render(
361
368
  <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
- />
369
+ <ChartLegendWrapper colorSet={testColorSet}>
370
+ <Barchart
371
+ type="category"
372
+ bars={[]}
373
+ title="Test Title"
374
+ secondaryTitle="Test Secondary Title"
375
+ rightTitle="Test Right Title"
376
+ helpTooltip="Test Help Tooltip"
377
+ />
378
+ </ChartLegendWrapper>
373
379
  </Wrapper>,
374
380
  );
375
381
 
@@ -19,11 +19,12 @@ 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,
25
26
  BAR_SIZE: 12,
26
- MIN_POINT_SIZE: 1,
27
+ MIN_POINT_SIZE: 3,
27
28
  DEFAULT_HEIGHT: 200,
28
29
  CHART_MARGIN: {
29
30
  left: 0,
@@ -69,18 +70,25 @@ export type BarchartSortFn<T extends BarchartBars> = (
69
70
 
70
71
  export type BarchartProps<T extends BarchartBars> = {
71
72
  type: 'category' | TimeType;
72
- bars: T;
73
- colorSet: Record<T[number]['label'], ChartColors | (string & {})>;
73
+ bars?: T;
74
74
  tooltip?: BarchartTooltipFn<T>;
75
75
  defaultSort?: BarchartSortFn<T>;
76
76
  unitRange?: UnitRange;
77
77
  helpTooltip?: string;
78
78
  stacked?: boolean;
79
+ /**
80
+ * Sort the bars by default or by legend order
81
+ * legend will sort the bars by the order of the colorSet property of the ChartLegendWrapper component
82
+ * default will sort the bars by average values in descending order (biggest values will be at bottom)
83
+ * @default 'default'
84
+ */
85
+ stackedBarSort?: 'default' | 'legend';
79
86
  title?: string;
80
87
  secondaryTitle?: string;
81
88
  rightTitle?: React.ReactNode;
82
89
  height?: number;
83
90
  isLoading?: boolean;
91
+ isError?: boolean;
84
92
  };
85
93
 
86
94
  interface CustomTickProps {
@@ -177,6 +185,21 @@ const ChartHeader = ({
177
185
  );
178
186
  };
179
187
 
188
+ const Error = ({ height }: { height: number }) => {
189
+ return (
190
+ <Box
191
+ height={height}
192
+ style={{
193
+ alignItems: 'center',
194
+ justifyContent: 'center',
195
+ display: 'flex',
196
+ }}
197
+ >
198
+ <Text>Chart data is not available</Text>
199
+ </Box>
200
+ );
201
+ };
202
+
180
203
  const Loading = ({ height }: { height: number }) => {
181
204
  return (
182
205
  <Box
@@ -196,15 +219,16 @@ const Loading = ({ height }: { height: number }) => {
196
219
 
197
220
  export const Barchart = <T extends BarchartBars>(props: BarchartProps<T>) => {
198
221
  const theme = useTheme();
222
+ const { getColor } = useChartLegend();
199
223
  const [hoveredValue, setHoveredValue] = useState<string | undefined>();
200
224
 
201
225
  const {
202
226
  height = CHART_CONSTANTS.DEFAULT_HEIGHT,
203
227
  bars,
204
228
  type = 'category',
205
- colorSet,
206
229
  unitRange,
207
230
  stacked,
231
+ stackedBarSort = 'default',
208
232
  defaultSort,
209
233
  tooltip,
210
234
  title,
@@ -212,10 +236,31 @@ export const Barchart = <T extends BarchartBars>(props: BarchartProps<T>) => {
212
236
  helpTooltip,
213
237
  rightTitle,
214
238
  isLoading,
239
+ isError,
215
240
  } = props;
216
241
 
242
+ // Create colorSet from ChartLegendWrapper
243
+ const colorSet = bars?.reduce(
244
+ (acc, bar) => {
245
+ const color = getColor(bar.label);
246
+ if (color) {
247
+ acc[bar.label] = color;
248
+ }
249
+ return acc;
250
+ },
251
+ {} as Record<string, ChartColors | string>,
252
+ );
253
+
217
254
  const { rechartsBars, unitLabel, roundReferenceValue, rechartsData } =
218
- useChartData(bars, type, colorSet, stacked, defaultSort, unitRange);
255
+ useChartData(
256
+ bars || [],
257
+ type,
258
+ colorSet || {},
259
+ stacked,
260
+ defaultSort,
261
+ unitRange,
262
+ stackedBarSort,
263
+ );
219
264
 
220
265
  return (
221
266
  <Stack direction="vertical" gap="r8">
@@ -225,7 +270,9 @@ export const Barchart = <T extends BarchartBars>(props: BarchartProps<T>) => {
225
270
  helpTooltip={helpTooltip}
226
271
  rightTitle={rightTitle}
227
272
  />
228
- {isLoading ? (
273
+ {isError || (!bars && !isLoading) ? (
274
+ <Error height={height} />
275
+ ) : isLoading ? (
229
276
  <Loading height={height} />
230
277
  ) : (
231
278
  <StyledResponsiveContainer width="100%" height={height}>
@@ -248,7 +295,7 @@ export const Barchart = <T extends BarchartBars>(props: BarchartProps<T>) => {
248
295
  key={dataKey}
249
296
  dataKey={dataKey}
250
297
  fill={chartColors[fill] || fill}
251
- minPointSize={CHART_CONSTANTS.MIN_POINT_SIZE}
298
+ minPointSize={stacked ? 0 : CHART_CONSTANTS.MIN_POINT_SIZE}
252
299
  stackId={stackId}
253
300
  onMouseOver={() => setHoveredValue(dataKey)}
254
301
  onMouseLeave={() => setHoveredValue(undefined)}
@@ -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,
@@ -481,8 +482,8 @@ describe('applySortingToData', () => {
481
482
 
482
483
  describe('getRoundReferenceValue', () => {
483
484
  it('should return appropriate rounded values', () => {
484
- expect(getRoundReferenceValue(1)).toBe(1);
485
- expect(getRoundReferenceValue(2)).toBe(2.5);
485
+ expect(getRoundReferenceValue(1)).toBe(5);
486
+ expect(getRoundReferenceValue(2)).toBe(5);
486
487
  expect(getRoundReferenceValue(3)).toBe(5);
487
488
  expect(getRoundReferenceValue(7)).toBe(10);
488
489
  expect(getRoundReferenceValue(15)).toBe(25);
@@ -716,6 +717,14 @@ describe('sortStackedBars', () => {
716
717
  { dataKey: 'bar1', fill: 'blue' },
717
718
  ]);
718
719
  });
720
+ it('should sort bars by legend order when stacked is true and legendOrder is provided', () => {
721
+ const result = sortStackedBars(bars, data, true, ['bar3', 'bar2', 'bar1']);
722
+ expect(result).toEqual([
723
+ { dataKey: 'bar3', fill: 'green' },
724
+ { dataKey: 'bar2', fill: 'red' },
725
+ { dataKey: 'bar1', fill: 'blue' },
726
+ ]);
727
+ });
719
728
  it('should not sort bars when stacked is false', () => {
720
729
  const result = sortStackedBars(bars, data, false);
721
730
  expect(result).toEqual([
@@ -780,3 +789,119 @@ describe('renderTooltipContent', () => {
780
789
  });
781
790
  });
782
791
  });
792
+
793
+ describe('filterChartDataAndBarsByLegendSelection', () => {
794
+ const mockChartData = [
795
+ { category: 'Jan', Success: 10, Failed: 5, Warning: 3, Pending: 2 },
796
+ { category: 'Feb', Success: 20, Failed: 8, Warning: 6, Pending: 4 },
797
+ { category: 'Mar', Success: 15, Failed: 12, Warning: 9, Pending: 7 },
798
+ ];
799
+
800
+ const mockRechartsBars = [
801
+ { dataKey: 'Success', fill: '#00D100', stackId: undefined },
802
+ { dataKey: 'Failed', fill: '#D10000', stackId: undefined },
803
+ { dataKey: 'Warning', fill: '#FFA500', stackId: 'stacked' },
804
+ { dataKey: 'Pending', fill: '#337FBD', stackId: 'stacked' },
805
+ ];
806
+
807
+ it('should return all data and bars when no resources are selected (empty array)', () => {
808
+ const result = filterChartDataAndBarsByLegendSelection(
809
+ mockChartData,
810
+ mockRechartsBars,
811
+ [],
812
+ );
813
+
814
+ expect(result.filteredData).toEqual(mockChartData);
815
+ expect(result.filteredRechartsBars).toEqual(mockRechartsBars);
816
+ expect(result.filteredData).toHaveLength(3);
817
+ expect(result.filteredRechartsBars).toHaveLength(4);
818
+ // Verify all properties are preserved
819
+ expect(Object.keys(result.filteredData[0])).toEqual([
820
+ 'category',
821
+ 'Success',
822
+ 'Failed',
823
+ 'Warning',
824
+ 'Pending',
825
+ ]);
826
+ });
827
+
828
+ it('should return only selected resources in both data and bars when resources are selected', () => {
829
+ const selectedResources = ['Success', 'Warning'];
830
+ const result = filterChartDataAndBarsByLegendSelection(
831
+ mockChartData,
832
+ mockRechartsBars,
833
+ selectedResources,
834
+ );
835
+
836
+ expect(result.filteredData).toHaveLength(3);
837
+ expect(result.filteredData).toEqual([
838
+ { category: 'Jan', Success: 10, Warning: 3 },
839
+ { category: 'Feb', Success: 20, Warning: 6 },
840
+ { category: 'Mar', Success: 15, Warning: 9 },
841
+ ]);
842
+
843
+ expect(result.filteredRechartsBars).toHaveLength(2);
844
+ expect(result.filteredRechartsBars).toEqual([
845
+ { dataKey: 'Success', fill: '#00D100', stackId: undefined },
846
+ { dataKey: 'Warning', fill: '#FFA500', stackId: 'stacked' },
847
+ ]);
848
+ });
849
+
850
+ it('should return single resource when only one resource is selected', () => {
851
+ const selectedResources = ['Failed'];
852
+ const result = filterChartDataAndBarsByLegendSelection(
853
+ mockChartData,
854
+ mockRechartsBars,
855
+ selectedResources,
856
+ );
857
+
858
+ expect(result.filteredData).toHaveLength(3);
859
+ expect(result.filteredData).toEqual([
860
+ { category: 'Jan', Failed: 5 },
861
+ { category: 'Feb', Failed: 8 },
862
+ { category: 'Mar', Failed: 12 },
863
+ ]);
864
+
865
+ expect(result.filteredRechartsBars).toHaveLength(1);
866
+ expect(result.filteredRechartsBars).toEqual([
867
+ { dataKey: 'Failed', fill: '#D10000', stackId: undefined },
868
+ ]);
869
+ });
870
+
871
+ it('should handle empty data array', () => {
872
+ const result = filterChartDataAndBarsByLegendSelection(
873
+ [],
874
+ mockRechartsBars,
875
+ ['Success'],
876
+ );
877
+
878
+ expect(result.filteredData).toEqual([]);
879
+ expect(result.filteredRechartsBars).toHaveLength(1);
880
+ expect(result.filteredRechartsBars).toEqual([
881
+ { dataKey: 'Success', fill: '#00D100', stackId: undefined },
882
+ ]);
883
+ });
884
+
885
+ it('should preserve order of selected resources based on data object keys', () => {
886
+ const selectedResources = ['Pending', 'Success', 'Failed']; // Different order
887
+ const result = filterChartDataAndBarsByLegendSelection(
888
+ mockChartData,
889
+ mockRechartsBars,
890
+ selectedResources,
891
+ );
892
+
893
+ // Should maintain the order they appear in selectedResources
894
+ expect(Object.keys(result.filteredData[0])).toEqual([
895
+ 'category',
896
+ 'Pending',
897
+ 'Success',
898
+ 'Failed',
899
+ ]);
900
+
901
+ // Should maintain original bar order regardless of selection order
902
+ expect(result.filteredRechartsBars).toHaveLength(3);
903
+ expect(result.filteredRechartsBars[0].dataKey).toBe('Success');
904
+ expect(result.filteredRechartsBars[1].dataKey).toBe('Failed');
905
+ expect(result.filteredRechartsBars[2].dataKey).toBe('Pending');
906
+ });
907
+ });