@scality/core-ui 0.168.0 → 0.170.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 (47) hide show
  1. package/.github/workflows/github-pages.yml +5 -3
  2. package/.storybook/preview.js +1 -0
  3. package/dist/components/barchartv2/Barchart.component.d.ts.map +1 -1
  4. package/dist/components/barchartv2/Barchart.component.js +6 -5
  5. package/dist/components/barchartv2/ChartTooltip.d.ts +9 -13
  6. package/dist/components/barchartv2/ChartTooltip.d.ts.map +1 -1
  7. package/dist/components/barchartv2/ChartTooltip.js +14 -4
  8. package/dist/components/barchartv2/utils.d.ts +9 -2
  9. package/dist/components/barchartv2/utils.d.ts.map +1 -1
  10. package/dist/components/barchartv2/utils.js +12 -16
  11. package/dist/components/buttonv2/Buttonv2.component.d.ts.map +1 -1
  12. package/dist/components/buttonv2/Buttonv2.component.js +27 -6
  13. package/dist/components/date/FormattedDateTime.d.ts +54 -1
  14. package/dist/components/date/FormattedDateTime.d.ts.map +1 -1
  15. package/dist/components/date/FormattedDateTime.js +78 -6
  16. package/dist/components/date/FormattedDateTime.spec.js +12 -3
  17. package/dist/components/layout/v2/index.d.ts +2 -1
  18. package/dist/components/layout/v2/index.d.ts.map +1 -1
  19. package/dist/components/layout/v2/index.js +3 -3
  20. package/dist/components/linetimeseriechart/linetimeseriechart.component.d.ts.map +1 -1
  21. package/dist/components/linetimeseriechart/linetimeseriechart.component.js +30 -32
  22. package/dist/components/linetimeseriechart/utils.d.ts +16 -0
  23. package/dist/components/linetimeseriechart/utils.d.ts.map +1 -0
  24. package/dist/components/linetimeseriechart/utils.js +28 -0
  25. package/dist/style/theme.d.ts +2 -2
  26. package/dist/style/theme.d.ts.map +1 -1
  27. package/dist/style/theme.js +26 -0
  28. package/package.json +5 -4
  29. package/src/lib/components/barchartv2/Barchart.component.test.tsx +1 -1
  30. package/src/lib/components/barchartv2/Barchart.component.tsx +14 -6
  31. package/src/lib/components/barchartv2/ChartTooltip.test.tsx +119 -0
  32. package/src/lib/components/barchartv2/ChartTooltip.tsx +49 -19
  33. package/src/lib/components/barchartv2/utils.test.ts +29 -44
  34. package/src/lib/components/barchartv2/utils.ts +22 -29
  35. package/src/lib/components/buttonv2/Buttonv2.component.tsx +27 -6
  36. package/src/lib/components/date/FormattedDateTime.spec.tsx +27 -2
  37. package/src/lib/components/date/FormattedDateTime.tsx +89 -10
  38. package/src/lib/components/layout/v2/index.tsx +5 -3
  39. package/src/lib/components/linetimeseriechart/linetimeseriechart.component.tsx +61 -57
  40. package/src/lib/components/linetimeseriechart/linetimeseriechart.test.tsx +58 -55
  41. package/src/lib/components/linetimeseriechart/utils.test.ts +87 -0
  42. package/src/lib/components/linetimeseriechart/utils.ts +43 -0
  43. package/src/lib/style/theme.ts +26 -0
  44. package/stories/BarChart/barchart.stories.tsx +1 -1
  45. package/stories/color.mdx +12 -0
  46. package/stories/formattedate.stories.tsx +7 -0
  47. package/stories/layout.stories.tsx +19 -0
@@ -4,9 +4,9 @@ import {
4
4
  computeUnitLabelAndRoundReferenceValue,
5
5
  filterChartDataAndBarsByLegendSelection,
6
6
  formatPrometheusDataToRechartsDataAndBars,
7
+ getCurrentPoint,
7
8
  getMaxBarValue,
8
9
  getRoundReferenceValue,
9
- renderTooltipContent,
10
10
  sortStackedBars,
11
11
  transformCategoryData,
12
12
  transformTimeData,
@@ -770,53 +770,38 @@ describe('sortStackedBars', () => {
770
770
  });
771
771
  });
772
772
 
773
- describe('renderTooltipContent', () => {
774
- it('should return null when active is false', () => {
775
- const props = {
776
- active: false,
777
- payload: [],
778
- label: 'test',
779
- coordinate: { x: 0, y: 0 },
780
- accessibilityLayer: false,
781
- };
782
- const result = renderTooltipContent(props, undefined, undefined);
783
- expect(result).toBeNull();
784
- });
785
-
786
- it('should return null when tooltip is undefined', () => {
787
- const props = {
788
- active: true,
789
- payload: [{ name: 'test', value: 10 }],
790
- label: 'test',
791
- coordinate: { x: 0, y: 0 },
792
- accessibilityLayer: false,
793
- };
794
- const result = renderTooltipContent(props, undefined, 'test');
795
- expect(result).toBeNull();
796
- });
797
- it('should call tooltip with the correct props', () => {
798
- const tooltip = jest.fn();
799
- const props = {
800
- active: true,
801
- payload: [
802
- { name: 'Success', value: 10 },
803
- { name: 'Failed', value: 20 },
804
- ],
805
- label: 'Test',
806
- coordinate: { x: 0, y: 0 },
807
- accessibilityLayer: false,
808
- };
809
- renderTooltipContent(props, tooltip, 'Success');
810
- expect(tooltip).toHaveBeenCalledWith({
773
+ describe('getCurrentPoint', () => {
774
+ it('should return the current point', () => {
775
+ const result = getCurrentPoint(
776
+ {
777
+ payload: [{ name: 'Success', value: 10 }],
778
+ label: 'Test',
779
+ coordinate: { x: 10, y: 10 },
780
+ active: true,
781
+ accessibilityLayer: false,
782
+ },
783
+ 'Success',
784
+ );
785
+ expect(result).toEqual({
786
+ category: 'Test',
787
+ values: [{ label: 'Success', value: 10, isHovered: true }],
788
+ });
789
+ const result2 = getCurrentPoint(
790
+ {
791
+ payload: [{ name: 'Success', value: 10 }],
792
+ label: 'Test',
793
+ coordinate: { x: 10, y: 10 },
794
+ active: true,
795
+ accessibilityLayer: false,
796
+ },
797
+ 'Failed',
798
+ );
799
+ expect(result2).toEqual({
811
800
  category: 'Test',
812
- values: [
813
- { label: 'Success', value: 10, isHovered: true },
814
- { label: 'Failed', value: 20, isHovered: false },
815
- ],
801
+ values: [{ label: 'Success', value: 10, isHovered: false }],
816
802
  });
817
803
  });
818
804
  });
819
-
820
805
  describe('filterChartDataAndBarsByLegendSelection', () => {
821
806
  const mockChartData = [
822
807
  { category: 'Jan', Success: 10, Failed: 5, Warning: 3, Pending: 2 },
@@ -428,35 +428,6 @@ export const sortStackedBars = (
428
428
  return barAverages.map(({ average, ...bar }) => bar);
429
429
  };
430
430
 
431
- export const renderTooltipContent = <T extends BarchartBars>(
432
- props: TooltipContentProps<number, string>,
433
- tooltip: BarchartTooltipFn<T> | undefined,
434
- hoveredValue: string | undefined,
435
- ) => {
436
- const { active, payload, label } = props;
437
-
438
- if (!active || !tooltip) {
439
- return null;
440
- }
441
-
442
- const tooltipValues: {
443
- label: T[number]['label'];
444
- value: number;
445
- isHovered: boolean;
446
- }[] = payload.map((item) => ({
447
- label: item.name,
448
- value: item.value,
449
- isHovered: item.name === hoveredValue,
450
- }));
451
-
452
- const currentPoint = {
453
- category: label as string | number,
454
- values: tooltipValues,
455
- };
456
-
457
- return tooltip(currentPoint);
458
- };
459
-
460
431
  /**
461
432
  * Filters both chart data and recharts bars to only include selected resources from legend
462
433
  * @param data - Array of chart data objects with category and resource values
@@ -538,3 +509,25 @@ export const useChartData = <T extends BarchartBars>(
538
509
  rechartsData,
539
510
  };
540
511
  };
512
+
513
+ export const getCurrentPoint = <T extends BarchartBars>(
514
+ props: TooltipContentProps<number, string>,
515
+ hoveredValue: string | undefined,
516
+ ) => {
517
+ const { payload, label } = props;
518
+
519
+ const tooltipValues: {
520
+ label: T[number]['label'];
521
+ value: number;
522
+ isHovered: boolean;
523
+ }[] = payload.map((item) => ({
524
+ label: item.name,
525
+ value: item.value,
526
+ isHovered: item.name === hoveredValue,
527
+ }));
528
+
529
+ return {
530
+ category: label as string | number,
531
+ values: tooltipValues,
532
+ };
533
+ };
@@ -51,8 +51,10 @@ export const ButtonStyled = styled.button<Props>`
51
51
  switch (props.variant) {
52
52
  case 'primary':
53
53
  return css`
54
- background-color: ${brand.buttonPrimary};
55
- border: ${spacing.r1} solid ${brand.buttonPrimary};
54
+ background: ${brand.buttonPrimary};
55
+ background-clip: padding-box, border-box;
56
+ border: ${spacing.r1} solid transparent;
57
+ border-color: ${brand.buttonPrimary};
56
58
  color: ${brand.textPrimary};
57
59
  &:hover:enabled {
58
60
  cursor: pointer;
@@ -73,8 +75,10 @@ export const ButtonStyled = styled.button<Props>`
73
75
 
74
76
  case 'secondary':
75
77
  return css`
76
- background-color: ${brand.buttonSecondary};
77
- border: ${spacing.r1} solid ${brand.buttonSecondary};
78
+ background: ${brand.buttonSecondary};
79
+ background-clip: padding-box, border-box;
80
+ border: ${spacing.r1} solid transparent;
81
+ border-color: ${brand.buttonSecondary};
78
82
  color: ${brand.textPrimary};
79
83
  &:hover:enabled {
80
84
  cursor: pointer;
@@ -88,7 +92,8 @@ export const ButtonStyled = styled.button<Props>`
88
92
  &:active:enabled {
89
93
  cursor: pointer;
90
94
  color: ${brand.textPrimary};
91
- border: ${spacing.r1} solid ${brand.buttonSecondary};
95
+ border: ${spacing.r1} solid transparent;
96
+ border-color: ${brand.buttonSecondary};
92
97
  }
93
98
  `;
94
99
 
@@ -112,13 +117,19 @@ export const ButtonStyled = styled.button<Props>`
112
117
 
113
118
  case 'outline':
114
119
  return css`
115
- border: ${spacing.r1} solid ${brand.buttonSecondary};
120
+ border: ${spacing.r1} solid transparent;
121
+ border-color: ${brand.border}; // fallback for linear-gradient button themes
122
+ border-color: ${brand.buttonSecondary};
116
123
  background-color: transparent;
117
124
  color: ${brand.textPrimary};
118
125
  &:hover:enabled {
119
126
  cursor: pointer;
120
127
  border-color: ${brand.infoPrimary};
121
128
  color: ${brand.textPrimary};
129
+
130
+ &::before {
131
+ background-image: ${brand.buttonPrimary};
132
+ }
122
133
  }
123
134
  &:focus-visible:enabled {
124
135
  ${FocusVisibleStyle}
@@ -129,6 +140,16 @@ export const ButtonStyled = styled.button<Props>`
129
140
  border: ${spacing.r1} solid ${brand.infoSecondary};
130
141
  color: ${brand.textPrimary};
131
142
  }
143
+ &::before {
144
+ content: '';
145
+ position: absolute;
146
+ inset: 0;
147
+ padding: ${spacing.r1};
148
+ border-radius: inherit;
149
+ mask: linear-gradient(white, white) content-box, linear-gradient(white, white);
150
+ mask-composite: exclude;
151
+ pointer-events: none;
152
+ }
132
153
  `;
133
154
 
134
155
  default:
@@ -224,7 +224,7 @@ describe('FormatttedDateTime', () => {
224
224
  />,
225
225
  );
226
226
  //V
227
- expect(screen.getByText('6 Oct 18:33')).toBeInTheDocument();
227
+ expect(screen.getByText('06 Oct 18:33')).toBeInTheDocument();
228
228
  });
229
229
 
230
230
  it('should display the date in the expected format of date in the chart', () => {
@@ -236,6 +236,31 @@ describe('FormatttedDateTime', () => {
236
236
  />,
237
237
  );
238
238
  //V
239
- expect(screen.getByText('6 Oct 18:33:00')).toBeInTheDocument();
239
+ expect(screen.getByText('06 Oct 18:33:00')).toBeInTheDocument();
240
+ });
241
+ it('should display 3 letter month for September date', () => {
242
+ //S
243
+ render(
244
+ <>
245
+ <FormattedDateTime
246
+ format="day-month-abbreviated-hour-minute"
247
+ value={new Date('2022-09-06T18:33:00Z')}
248
+ />
249
+ <FormattedDateTime
250
+ format="day-month-abbreviated-hour-minute-second"
251
+ value={new Date('2022-09-06T18:33:00Z')}
252
+ />
253
+
254
+ <FormattedDateTime
255
+ format="chart-date"
256
+ value={new Date('2022-09-06T18:33:00Z')}
257
+ />
258
+ </>,
259
+ );
260
+ //V
261
+ expect(screen.getByText(/06 Sep 18:33/)).toBeInTheDocument();
262
+ expect(screen.getByText(/06 Sep 18:33:00/)).toBeInTheDocument();
263
+ expect(screen.getByText(/Tue06Sep/)).toBeInTheDocument();
264
+ expect(screen.queryByText(/Sept/)).not.toBeInTheDocument();
240
265
  });
241
266
  });
@@ -1,13 +1,31 @@
1
1
  import { getDateDaysDiff } from './dateDiffer';
2
2
  import { Tooltip } from '../tooltip/Tooltip.component';
3
3
 
4
+ /**
5
+ * @description Long date formatter, with weekday, year, month and day. Used for describing long term date.
6
+ * @example Wednesday 6 October 2025
7
+ */
4
8
  export const LONG_DATE_FORMATER = Intl.DateTimeFormat('en-GB', {
5
9
  weekday: 'long',
6
10
  year: 'numeric',
7
11
  month: 'long',
8
- day: 'numeric',
12
+ day: '2-digit',
13
+ });
14
+
15
+ /**
16
+ * @description Long date formatter, without weekday.
17
+ * @example 01 September 2025
18
+ */
19
+ export const LONG_DATE_FORMATER_WITHOUT_WEEKDAY = Intl.DateTimeFormat('en-GB', {
20
+ year: 'numeric',
21
+ month: 'long',
22
+ day: '2-digit',
9
23
  });
10
24
 
25
+ /**
26
+ * @description Date formatter, with year, month and day. Used for describing long term date.
27
+ * @example 2025-01-01
28
+ */
11
29
  export const DATE_FORMATER = Intl.DateTimeFormat('fr-CA', {
12
30
  year: 'numeric',
13
31
  month: '2-digit',
@@ -15,12 +33,20 @@ export const DATE_FORMATER = Intl.DateTimeFormat('fr-CA', {
15
33
  hour12: false,
16
34
  });
17
35
 
36
+ /**
37
+ * @description Day month formatter, with weekday, day and month. Used for describing long term date.
38
+ * @example Wed 6 Oct
39
+ */
18
40
  export const DAY_MONTH_FORMATER = Intl.DateTimeFormat('en-GB', {
19
41
  weekday: 'short',
20
42
  day: '2-digit',
21
43
  month: 'short',
22
44
  });
23
45
 
46
+ /**
47
+ * @description Time formatter, with hour, minute and second. Used for describing long term date.
48
+ * @example 18:33:00
49
+ */
24
50
  export const TIME_SECOND_FORMATER = Intl.DateTimeFormat('en-GB', {
25
51
  hour12: false,
26
52
  hour: '2-digit',
@@ -28,16 +54,24 @@ export const TIME_SECOND_FORMATER = Intl.DateTimeFormat('en-GB', {
28
54
  second: '2-digit',
29
55
  });
30
56
 
57
+ /**
58
+ * @description Time formatter, with hour and minute. Used for describing long term date.
59
+ * @example 18:33
60
+ */
31
61
  export const TIME_FORMATER = Intl.DateTimeFormat('en-GB', {
32
62
  hour12: false,
33
63
  hour: '2-digit',
34
64
  minute: '2-digit',
35
65
  });
36
66
 
67
+ /**
68
+ * @description Day month abbreviated hour minute second formatter. Used for describing long term date.
69
+ * @example 6 Oct 18:33:00
70
+ */
37
71
  export const DAY_MONTH_ABBREVIATED_HOUR_MINUTE_SECOND = Intl.DateTimeFormat(
38
72
  'en-GB',
39
73
  {
40
- day: 'numeric',
74
+ day: '2-digit',
41
75
  month: 'short',
42
76
  hour: '2-digit',
43
77
  minute: '2-digit',
@@ -46,8 +80,12 @@ export const DAY_MONTH_ABBREVIATED_HOUR_MINUTE_SECOND = Intl.DateTimeFormat(
46
80
  },
47
81
  );
48
82
 
83
+ /**
84
+ * @description Day month abbreviated hour minute formatter. Used for describing long term date.
85
+ * @example 6 Oct 18:33
86
+ */
49
87
  export const DAY_MONTH_ABBREVIATED_HOUR_MINUTE = Intl.DateTimeFormat('en-GB', {
50
- day: 'numeric',
88
+ day: '2-digit',
51
89
  month: 'short',
52
90
  hour: '2-digit',
53
91
  minute: '2-digit',
@@ -64,6 +102,15 @@ export const YEAR_MONTH_DAY_FORMATTER = Intl.DateTimeFormat('en-CA', {
64
102
  day: '2-digit',
65
103
  });
66
104
 
105
+ /**
106
+ * @description Month day formatter, without year. Used for short term date ranges.
107
+ * @example 01-15
108
+ */
109
+ export const MONTH_DAY_FORMATTER = Intl.DateTimeFormat('en-CA', {
110
+ month: '2-digit',
111
+ day: '2-digit',
112
+ });
113
+
67
114
  type FormattedDateTimeProps = {
68
115
  format:
69
116
  | 'date'
@@ -75,8 +122,10 @@ type FormattedDateTimeProps = {
75
122
  | 'day-month-abbreviated-hour-minute'
76
123
  | 'day-month-abbreviated-hour-minute-second'
77
124
  | 'long-date'
125
+ | 'long-date-without-weekday'
78
126
  | 'chart-date'
79
- | 'year-month-day';
127
+ | 'year-month-day'
128
+ | 'month-day';
80
129
 
81
130
  value: Date;
82
131
  };
@@ -92,6 +141,21 @@ const isItFutureOrIsItPast = (
92
141
  }
93
142
  };
94
143
 
144
+ /**
145
+ * @description Formats the date and time according to the format specified.
146
+ * @example
147
+ * date: '2025-01-01'
148
+ * 'date-time': '2025-01-01 00:00'
149
+ * 'date-time-second': '2025-01-01 00:00:00'
150
+ * time: '00:00'
151
+ * 'time-second': '00:00:00'
152
+ * relative: '1 month ago'
153
+ * 'day-month-abbreviated-hour-minute': '6 Oct 18:33'
154
+ * 'day-month-abbreviated-hour-minute-second': '6 Oct 18:33:00'
155
+ * 'long-date': 'Wednesday 6 October 2025'
156
+ * 'chart-date': '6 Oct'
157
+ * 'year-month-day': '2025-10-06'
158
+ */
95
159
  export const FormattedDateTime = ({
96
160
  format,
97
161
  value,
@@ -194,23 +258,38 @@ export const FormattedDateTime = ({
194
258
  );
195
259
  case 'day-month-abbreviated-hour-minute':
196
260
  return (
197
- <>{DAY_MONTH_ABBREVIATED_HOUR_MINUTE.format(value).replace(',', '')}</>
261
+ <>
262
+ {DAY_MONTH_ABBREVIATED_HOUR_MINUTE.format(value)
263
+ .replace(',', '')
264
+ .replace(/Sept/g, 'Sep')}
265
+ </>
198
266
  );
199
267
  case 'day-month-abbreviated-hour-minute-second':
200
268
  return (
201
269
  <>
202
- {DAY_MONTH_ABBREVIATED_HOUR_MINUTE_SECOND.format(value).replace(
203
- ',',
204
- '',
205
- )}
270
+ {DAY_MONTH_ABBREVIATED_HOUR_MINUTE_SECOND.format(value)
271
+ .replace(',', '')
272
+ // replace Sept with Sep to keep 3 letter month
273
+ .replace(/Sept/g, 'Sep')}
206
274
  </>
207
275
  );
208
276
  case 'long-date':
209
277
  return <>{LONG_DATE_FORMATER.format(value)}</>;
278
+ case 'long-date-without-weekday':
279
+ return <>{LONG_DATE_FORMATER_WITHOUT_WEEKDAY.format(value)}</>;
210
280
  case 'chart-date':
211
- return <>{DAY_MONTH_FORMATER.format(value).replace(/[ ,]/g, '')}</>;
281
+ return (
282
+ <>
283
+ {DAY_MONTH_FORMATER.format(value)
284
+ .replace(/[ ,]/g, '')
285
+ // replace Sept with Sep to keep 3 letter month
286
+ .replace(/Sept/g, 'Sep')}
287
+ </>
288
+ );
212
289
  case 'year-month-day':
213
290
  return <>{YEAR_MONTH_DAY_FORMATTER.format(value)}</>;
291
+ case 'month-day':
292
+ return <>{MONTH_DAY_FORMATTER.format(value)}</>;
214
293
  default:
215
294
  return <></>;
216
295
  }
@@ -2,12 +2,12 @@ import { ReactElement } from 'react';
2
2
  import styled from 'styled-components';
3
3
  import { navbarHeight } from '../../../style/theme';
4
4
 
5
- const LayoutContainer = styled.div`
5
+ const LayoutContainer = styled.div<{ variant?: 'transparent' }>`
6
6
  display: flex;
7
7
  flex-direction: column;
8
8
  height: 100vh;
9
9
  box-sizing: border-box;
10
- background: ${(props) => props.theme['backgroundLevel1']};
10
+ background: ${props => props.variant === 'transparent' ? 'transparent' : props.theme.backgroundLevel1};
11
11
  `;
12
12
 
13
13
  const Navigation = styled.div`
@@ -17,12 +17,14 @@ const Navigation = styled.div`
17
17
  export function Layout({
18
18
  children: app,
19
19
  headerNavigation,
20
+ variant,
20
21
  }: {
21
22
  children: ReactElement | ReactElement[];
22
23
  headerNavigation: ReactElement;
24
+ variant?: 'transparent';
23
25
  }) {
24
26
  return (
25
- <LayoutContainer className="layout-container">
27
+ <LayoutContainer className="layout-container" variant={variant}>
26
28
  <Navigation>{headerNavigation}</Navigation>
27
29
  {app}
28
30
  </LayoutContainer>