@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
@@ -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
@@ -17,10 +18,14 @@ export const getRoundReferenceValue = (value: number): number => {
17
18
  const normalized = value / magnitude;
18
19
 
19
20
  // Round to nice numbers based on normalized value
20
- if (normalized <= 1) return magnitude;
21
- if (normalized <= 2.5) return 2.5 * magnitude;
22
- if (normalized <= 5) return 5 * magnitude;
23
- return 10 * magnitude;
21
+ let result: number;
22
+ if (normalized <= 1) result = magnitude;
23
+ else if (normalized <= 2.5) result = 2.5 * magnitude;
24
+ else if (normalized <= 5) result = 5 * magnitude;
25
+ else result = 10 * magnitude;
26
+
27
+ // Ensure minimum value of 5 for better chart appearance
28
+ return Math.max(result, 5);
24
29
  };
25
30
 
26
31
  export const getMaxBarValue = (
@@ -45,9 +50,9 @@ export const getMaxBarValue = (
45
50
  .filter((key) => key !== 'category')
46
51
  .map((key) => Number(item[key]));
47
52
  // Get the max value among the values in the object (corresponding to one bar)
48
- return Math.max(...numberValues);
53
+ return Math.max(...numberValues, 0); // Ensure we don't get -Infinity
49
54
  });
50
- return Math.max(...values);
55
+ return Math.max(...values, 0);
51
56
  };
52
57
 
53
58
  /**
@@ -63,9 +68,6 @@ const generateTimeRanges = (
63
68
  interval: number,
64
69
  ): { start: Date; end: Date }[] => {
65
70
  const ranges: { start: Date; end: Date }[] = [];
66
- if (!startDate || !endDate || !interval) {
67
- return ranges;
68
- }
69
71
 
70
72
  let currentDate = new Date(startDate.getTime());
71
73
  while (currentDate.getTime() <= endDate.getTime()) {
@@ -248,7 +250,7 @@ export const applySortingToData = <T extends BarchartBars>(
248
250
 
249
251
  const getRechartsBarsAndBarDataKeys = (
250
252
  bars: BarchartBars,
251
- colorSet: Record<BarchartBars[number]['label'], ChartColors | (string & {})>,
253
+ colorSet: Record<string, ChartColors | string>,
252
254
  stacked?: boolean,
253
255
  ) => {
254
256
  const rechartsBars: { dataKey: string; fill: string; stackId?: string }[] =
@@ -284,9 +286,10 @@ export const formatPrometheusDataToRechartsDataAndBars = <
284
286
  >(
285
287
  bars: T,
286
288
  type: BarchartProps<T>['type'],
287
- colorSet: Record<T[number]['label'], ChartColors | (string & {})>,
289
+ colorSet: Record<string, ChartColors | string>,
288
290
  stacked?: boolean,
289
291
  defaultSort?: BarchartProps<T>['defaultSort'],
292
+ legendOrder?: string[],
290
293
  ): {
291
294
  data: { [key: string]: string | number }[];
292
295
  rechartsBars: { dataKey: string; fill: string; stackId?: string }[];
@@ -306,7 +309,12 @@ export const formatPrometheusDataToRechartsDataAndBars = <
306
309
  data = applySortingToData(data, barDataKeys, defaultSort);
307
310
  }
308
311
 
309
- const sortedRechartsBars = sortStackedBars(rechartsBars, data, stacked);
312
+ const sortedRechartsBars = sortStackedBars(
313
+ rechartsBars,
314
+ data,
315
+ stacked,
316
+ legendOrder,
317
+ );
310
318
 
311
319
  return {
312
320
  rechartsBars: sortedRechartsBars,
@@ -329,7 +337,7 @@ export const computeUnitLabelAndRoundReferenceValue = (
329
337
  return { unitLabel: '', roundReferenceValue, rechartsData: data };
330
338
  }
331
339
 
332
- const { valueBase, unitLabel } = getUnitLabel(unitRange ?? [], maxValue);
340
+ const { valueBase, unitLabel } = getUnitLabel(unitRange, maxValue);
333
341
  const topValue = Math.ceil(maxValue / valueBase / 10) * 10;
334
342
  const roundReferenceValue = getRoundReferenceValue(topValue);
335
343
  const rechartsData = data.map((dataPoint) => {
@@ -396,8 +404,8 @@ export function getUnitLabel(
396
404
  };
397
405
  }
398
406
 
399
- // Sort stacked bars by their average values in descending order
400
- // This ensures the largest bars appear at the bottom of the stack
407
+ // Sort stacked bars by their average values in descending order or by legend order
408
+ // This ensures the largest bars appear at the bottom of the stack (default) or follow legend order
401
409
  export const sortStackedBars = (
402
410
  rechartsBars: {
403
411
  dataKey: string;
@@ -408,10 +416,33 @@ export const sortStackedBars = (
408
416
  [key: string]: string | number;
409
417
  }[],
410
418
  stacked?: boolean,
419
+ legendOrder?: string[],
411
420
  ) => {
412
421
  if (!stacked) {
413
422
  return rechartsBars;
414
423
  }
424
+
425
+ // If legend order is provided, sort by legend order
426
+ if (legendOrder && legendOrder.length > 0) {
427
+ return [...rechartsBars].sort((a, b) => {
428
+ const indexA = legendOrder.indexOf(a.dataKey);
429
+ const indexB = legendOrder.indexOf(b.dataKey);
430
+
431
+ // If both items are in legend order, sort by their position
432
+ if (indexA !== -1 && indexB !== -1) {
433
+ return indexA - indexB;
434
+ }
435
+
436
+ // If only one item is in legend order, prioritize it
437
+ if (indexA !== -1) return -1;
438
+ if (indexB !== -1) return 1;
439
+
440
+ // If neither is in legend order, maintain original order
441
+ return 0;
442
+ });
443
+ }
444
+
445
+ // Default behavior: sort by average values
415
446
  const barAverages = rechartsBars.map((bar) => {
416
447
  const values = data
417
448
  .map((item) => Number(item[bar.dataKey]) || 0)
@@ -456,29 +487,82 @@ export const renderTooltipContent = <T extends BarchartBars>(
456
487
  return tooltip(currentPoint);
457
488
  };
458
489
 
490
+ /**
491
+ * Filters both chart data and recharts bars to only include selected resources from legend
492
+ * @param data - Array of chart data objects with category and resource values
493
+ * @param rechartsBars - Array of recharts bar configurations
494
+ * @param selectedResources - Array of selected resource names
495
+ * @returns Object containing filtered data and recharts bars
496
+ */
497
+ export const filterChartDataAndBarsByLegendSelection = (
498
+ data: { [key: string]: string | number }[],
499
+ rechartsBars: { dataKey: string; fill: string; stackId?: string }[],
500
+ selectedResources: string[],
501
+ ) => {
502
+ // If no resources are selected, show all data and bars (default behavior)
503
+ if (selectedResources.length === 0) {
504
+ return { filteredData: data, filteredRechartsBars: rechartsBars };
505
+ }
506
+
507
+ // Filter recharts bars
508
+ const filteredRechartsBars = rechartsBars.filter((bar) =>
509
+ selectedResources.includes(bar.dataKey),
510
+ );
511
+
512
+ // Filter data to only include selected resources
513
+ const filteredData = data.map((item) => {
514
+ const filteredItem: { [key: string]: string | number } = {
515
+ category: item.category,
516
+ };
517
+ selectedResources.forEach((resource) => {
518
+ if (resource in item) {
519
+ filteredItem[resource] = item[resource];
520
+ }
521
+ });
522
+ return filteredItem;
523
+ });
524
+
525
+ return { filteredData, filteredRechartsBars };
526
+ };
527
+
459
528
  export const useChartData = <T extends BarchartBars>(
460
529
  bars: T,
461
530
  type: BarchartProps<T>['type'],
462
- colorSet: Record<T[number]['label'], ChartColors | (string & {})>,
531
+ colorSet: Record<string, ChartColors | string>,
463
532
  stacked?: boolean,
464
533
  defaultSort?: BarchartProps<T>['defaultSort'],
465
534
  unitRange?: UnitRange,
535
+ stackedBarSort?: 'default' | 'legend',
466
536
  ) => {
537
+ const { selectedResources, listResources } = useChartLegend();
538
+
539
+ // Get legend order when stackedBarSort is 'legend'
540
+ const legendOrder = stackedBarSort === 'legend' ? listResources() : undefined;
541
+
467
542
  const { data, rechartsBars } = formatPrometheusDataToRechartsDataAndBars(
468
543
  bars,
469
544
  type,
470
545
  colorSet,
471
546
  stacked,
472
547
  defaultSort,
548
+ legendOrder,
473
549
  );
474
550
 
475
- const maxValue = getMaxBarValue(data, stacked);
551
+ // Filter both data and bars to only include selected resources for accurate maxValue calculation
552
+ const { filteredData, filteredRechartsBars } =
553
+ filterChartDataAndBarsByLegendSelection(
554
+ data,
555
+ rechartsBars,
556
+ selectedResources,
557
+ );
558
+
559
+ const maxValue = getMaxBarValue(filteredData, stacked);
476
560
 
477
561
  const { unitLabel, roundReferenceValue, rechartsData } =
478
- computeUnitLabelAndRoundReferenceValue(data, maxValue, unitRange);
562
+ computeUnitLabelAndRoundReferenceValue(filteredData, maxValue, unitRange);
479
563
 
480
564
  return {
481
- rechartsBars,
565
+ rechartsBars: filteredRechartsBars,
482
566
  unitLabel,
483
567
  roundReferenceValue,
484
568
  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
+ };
@@ -0,0 +1,85 @@
1
+ import { createContext, useContext, useState, ReactNode, useMemo, useCallback } from 'react';
2
+ import { ChartColors } from '../../style/theme';
3
+
4
+ export type ChartLegendState = {
5
+ selectedResources: string[];
6
+ addSelectedResource: (resource: string) => void;
7
+ removeSelectedResource: (resource: string) => void;
8
+ isSelected: (resource: string) => boolean;
9
+ getColor: (resource: string) => string | undefined;
10
+ listResources: () => string[];
11
+ };
12
+
13
+ const ChartLegendContext = createContext<ChartLegendState | null>(null);
14
+
15
+ export type ChartLegendWrapperProps = {
16
+ children: ReactNode;
17
+ colorSet: Record<string, ChartColors | string>;
18
+ };
19
+
20
+ export const ChartLegendWrapper = ({
21
+ children,
22
+ colorSet,
23
+ }: ChartLegendWrapperProps) => {
24
+ const [selectedResources, setSelectedResources] = useState<string[]>([]);
25
+
26
+ const addSelectedResource = useCallback((resource: string) => {
27
+ setSelectedResources((prev) =>
28
+ prev.includes(resource) ? prev : [...prev, resource],
29
+ );
30
+ }, []);
31
+
32
+ const removeSelectedResource = useCallback((resource: string) => {
33
+ setSelectedResources((prev) => prev.filter((r) => r !== resource));
34
+ }, []);
35
+
36
+ const isSelected = useCallback((resource: string) => {
37
+ return selectedResources.includes(resource);
38
+ }, [selectedResources]);
39
+
40
+ const getColor = useCallback((resource: string) => {
41
+ const color = colorSet[resource];
42
+ if (!color) {
43
+ console.warn(
44
+ `ChartLegendWrapper: No color defined for resource "${resource}"`,
45
+ );
46
+ return undefined;
47
+ }
48
+ return color;
49
+ }, [colorSet]);
50
+
51
+ const listResources = useCallback(() => {
52
+ return Object.keys(colorSet);
53
+ }, [colorSet]);
54
+
55
+ const chartLegendState = useMemo(() => ({
56
+ selectedResources,
57
+ addSelectedResource,
58
+ removeSelectedResource,
59
+ isSelected,
60
+ getColor,
61
+ listResources,
62
+ }), [
63
+ selectedResources,
64
+ addSelectedResource,
65
+ removeSelectedResource,
66
+ isSelected,
67
+ getColor,
68
+ listResources,
69
+ ]);
70
+
71
+ return (
72
+ <ChartLegendContext.Provider value={chartLegendState}>
73
+ {children}
74
+ </ChartLegendContext.Provider>
75
+ );
76
+ };
77
+
78
+ // Hook for accessing legend state in custom components
79
+ export const useChartLegend = () => {
80
+ const context = useContext(ChartLegendContext);
81
+ if (!context) {
82
+ throw new Error('useChartLegend must be used within a ChartLegendWrapper');
83
+ }
84
+ return context;
85
+ };
@@ -214,4 +214,28 @@ describe('FormatttedDateTime', () => {
214
214
  //V
215
215
  expect(screen.getByText('2022-12-12 11:57:26')).toBeInTheDocument();
216
216
  });
217
+
218
+ it('should display the date in the expected format of the xaxis tick in the chart', () => {
219
+ //S
220
+ render(
221
+ <FormattedDateTime
222
+ format="day-month-abbreviated-hour-minute"
223
+ value={new Date('2022-10-06T18:33:00Z')}
224
+ />,
225
+ );
226
+ //V
227
+ expect(screen.getByText('6 Oct 18:33')).toBeInTheDocument();
228
+ });
229
+
230
+ it('should display the date in the expected format of date in the chart', () => {
231
+ //S
232
+ render(
233
+ <FormattedDateTime
234
+ format="day-month-abbreviated-hour-minute-second"
235
+ value={new Date('2022-10-06T18:33:00Z')}
236
+ />,
237
+ );
238
+ //V
239
+ expect(screen.getByText('6 Oct 18:33:00')).toBeInTheDocument();
240
+ });
217
241
  });
@@ -27,6 +27,26 @@ export const TIME_FORMATER = Intl.DateTimeFormat('en-GB', {
27
27
  minute: '2-digit',
28
28
  });
29
29
 
30
+ export const DAY_MONTH_ABBREVIATED_HOUR_MINUTE_SECOND = Intl.DateTimeFormat(
31
+ 'en-GB',
32
+ {
33
+ day: 'numeric',
34
+ month: 'short',
35
+ hour: '2-digit',
36
+ minute: '2-digit',
37
+ second: '2-digit',
38
+ hour12: false,
39
+ },
40
+ );
41
+
42
+ export const DAY_MONTH_ABBREVIATED_HOUR_MINUTE = Intl.DateTimeFormat('en-GB', {
43
+ day: 'numeric',
44
+ month: 'short',
45
+ hour: '2-digit',
46
+ minute: '2-digit',
47
+ hour12: false,
48
+ });
49
+
30
50
  type FormattedDateTimeProps = {
31
51
  format:
32
52
  | 'date'
@@ -34,7 +54,9 @@ type FormattedDateTimeProps = {
34
54
  | 'date-time-second'
35
55
  | 'time'
36
56
  | 'time-second'
37
- | 'relative';
57
+ | 'relative'
58
+ | 'day-month-abbreviated-hour-minute'
59
+ | 'day-month-abbreviated-hour-minute-second';
38
60
  value: Date;
39
61
  };
40
62
 
@@ -149,7 +171,19 @@ export const FormattedDateTime = ({
149
171
  few seconds ago
150
172
  </Tooltip>
151
173
  );
152
- //TO FINISH
174
+ case 'day-month-abbreviated-hour-minute':
175
+ return (
176
+ <>{DAY_MONTH_ABBREVIATED_HOUR_MINUTE.format(value).replace(',', '')}</>
177
+ );
178
+ case 'day-month-abbreviated-hour-minute-second':
179
+ return (
180
+ <>
181
+ {DAY_MONTH_ABBREVIATED_HOUR_MINUTE_SECOND.format(value).replace(
182
+ ',',
183
+ '',
184
+ )}
185
+ </>
186
+ );
153
187
  default:
154
188
  return <></>;
155
189
  }
@@ -2,27 +2,27 @@ import {
2
2
  HealthSelector,
3
3
  optionsDefaultConfiguration,
4
4
  } from './HealthSelector.component';
5
- import React from 'react';
6
- import { render, screen } from '@testing-library/react';
5
+ import { act } from 'react';
6
+ import { render, screen, waitFor } from '@testing-library/react';
7
7
  import userEvent from '@testing-library/user-event';
8
- import { QueryClient, QueryClientProvider } from 'react-query';
9
8
  import { getWrapper } from '../../testUtils';
10
9
  describe('HealthSelector', () => {
11
- it('should display correctly without any props and select first option', () => {
10
+ it('should display correctly without any props and select first option', async () => {
12
11
  const { Wrapper } = getWrapper();
13
12
  const { getByText } = render(
14
13
  <Wrapper>
15
14
  <HealthSelector id="health" onChange={() => {}} />
16
15
  </Wrapper>,
17
16
  );
17
+ await waitFor(() => screen.findByRole('img', { hidden: true }));
18
18
  const input = screen.getByRole('textbox');
19
19
 
20
20
  // open the menu
21
- userEvent.click(input);
21
+ await act(() => userEvent.click(input));
22
22
  const healthyOption = getByText(/healthy/i);
23
23
  expect(healthyOption).toBeInTheDocument();
24
24
  });
25
- it('should call the onChange function when it change', () => {
25
+ it('should call the onChange function when it change', async () => {
26
26
  const { Wrapper } = getWrapper();
27
27
  const onChange = jest.fn();
28
28
  const { getByText } = render(
@@ -31,12 +31,12 @@ describe('HealthSelector', () => {
31
31
  </Wrapper>,
32
32
  );
33
33
  const input = screen.getByRole('textbox');
34
- userEvent.click(input);
34
+ await act(() => userEvent.click(input));
35
35
  const warningOption = getByText(/warning/i);
36
- userEvent.click(warningOption);
36
+ await act(() => userEvent.click(warningOption));
37
37
  expect(onChange).toHaveBeenCalledWith('warning');
38
38
  });
39
- it('should not display hidden options', () => {
39
+ it('should not display hidden options', async () => {
40
40
  const { Wrapper } = getWrapper();
41
41
  const { queryByText } = render(
42
42
  <Wrapper>
@@ -55,7 +55,7 @@ describe('HealthSelector', () => {
55
55
 
56
56
  // open the menu
57
57
  const input = screen.getByRole('textbox');
58
- userEvent.click(input);
58
+ await act(() => userEvent.click(input));
59
59
  const healthyOption = queryByText(/healthy/i);
60
60
  expect(healthyOption).not.toBeInTheDocument();
61
61
  });