@scality/core-ui 0.169.0 → 0.171.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 (50) hide show
  1. package/__mocks__/uuid.js +11 -0
  2. package/dist/components/barchartv2/Barchart.component.js +2 -2
  3. package/dist/components/buttonv2/Buttonv2.component.js +1 -1
  4. package/dist/components/chartlegend/ChartLegend.d.ts +3 -1
  5. package/dist/components/chartlegend/ChartLegend.d.ts.map +1 -1
  6. package/dist/components/chartlegend/ChartLegend.js +2 -2
  7. package/dist/components/chartlegend/ChartLegendWrapper.d.ts +3 -1
  8. package/dist/components/chartlegend/ChartLegendWrapper.d.ts.map +1 -1
  9. package/dist/components/chartlegend/ChartLegendWrapper.js +43 -9
  10. package/dist/components/date/FormattedDateTime.d.ts +41 -2
  11. package/dist/components/date/FormattedDateTime.d.ts.map +1 -1
  12. package/dist/components/date/FormattedDateTime.js +55 -8
  13. package/dist/components/date/FormattedDateTime.spec.js +12 -3
  14. package/dist/components/icon/Icon.component.d.ts +2 -0
  15. package/dist/components/icon/Icon.component.d.ts.map +1 -1
  16. package/dist/components/icon/Icon.component.js +2 -0
  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/linetemporalchart/ChartUtil.d.ts.map +1 -1
  21. package/dist/components/linetemporalchart/ChartUtil.js +12 -0
  22. package/dist/components/linetimeseriechart/linetimeseriechart.component.d.ts +10 -5
  23. package/dist/components/linetimeseriechart/linetimeseriechart.component.d.ts.map +1 -1
  24. package/dist/components/linetimeseriechart/linetimeseriechart.component.js +84 -49
  25. package/dist/components/text/Text.component.d.ts +2 -1
  26. package/dist/components/text/Text.component.d.ts.map +1 -1
  27. package/dist/next.d.ts +1 -1
  28. package/dist/next.d.ts.map +1 -1
  29. package/dist/next.js +1 -1
  30. package/dist/style/theme.js +1 -1
  31. package/package.json +3 -1
  32. package/src/lib/components/barchartv2/Barchart.component.tsx +2 -2
  33. package/src/lib/components/barchartv2/ChartTooltip.test.tsx +1 -1
  34. package/src/lib/components/buttonv2/Buttonv2.component.tsx +1 -1
  35. package/src/lib/components/chartlegend/ChartLegend.tsx +4 -2
  36. package/src/lib/components/chartlegend/ChartLegendWrapper.test.tsx +197 -0
  37. package/src/lib/components/chartlegend/ChartLegendWrapper.tsx +65 -9
  38. package/src/lib/components/date/FormattedDateTime.spec.tsx +27 -2
  39. package/src/lib/components/date/FormattedDateTime.tsx +61 -11
  40. package/src/lib/components/icon/Icon.component.tsx +2 -0
  41. package/src/lib/components/layout/v2/index.tsx +5 -3
  42. package/src/lib/components/linetemporalchart/ChartUtil.ts +26 -0
  43. package/src/lib/components/linetimeseriechart/linetimeseriechart.component.tsx +227 -157
  44. package/src/lib/components/text/Text.component.tsx +8 -1
  45. package/src/lib/next.ts +4 -1
  46. package/src/lib/style/theme.ts +1 -1
  47. package/stories/BarChart/barchart.stories.tsx +7 -1
  48. package/stories/formattedate.stories.tsx +7 -0
  49. package/stories/layout.stories.tsx +19 -0
  50. package/stories/linetimeseriechart.stories.tsx +217 -1
@@ -5,9 +5,22 @@ import {
5
5
  ReactNode,
6
6
  useMemo,
7
7
  useCallback,
8
+ useEffect,
9
+ useRef,
8
10
  } from 'react';
11
+ import { v4 as uuidv4 } from 'uuid';
9
12
  import { ChartColors } from '../../style/theme';
10
13
 
14
+ export const useChartId = (): string => {
15
+ const idRef = useRef<string | null>(null);
16
+
17
+ if (idRef.current === null) {
18
+ idRef.current = uuidv4();
19
+ }
20
+
21
+ return idRef.current;
22
+ };
23
+
11
24
  export type ChartLegendState = {
12
25
  selectedResources: string[];
13
26
  addSelectedResource: (resource: string) => void;
@@ -18,23 +31,65 @@ export type ChartLegendState = {
18
31
  getColor: (resource: string) => string | undefined;
19
32
  listResources: () => string[];
20
33
  isOnlyOneSelected: () => boolean;
34
+ register: (chartId: string, seriesNames: string[]) => void;
21
35
  };
22
36
 
23
37
  const ChartLegendContext = createContext<ChartLegendState | null>(null);
24
38
 
25
39
  export type ChartLegendWrapperProps = {
26
40
  children: ReactNode;
27
- colorSet: Record<string, ChartColors | string>;
41
+ colorSet:
42
+ | Record<string, ChartColors | string>
43
+ | ((seriesNames: string[]) => Record<string, ChartColors | string>);
28
44
  };
29
45
 
30
46
  export const ChartLegendWrapper = ({
31
47
  children,
32
48
  colorSet,
33
49
  }: ChartLegendWrapperProps) => {
34
- const allResources = Object.keys(colorSet);
50
+ const [registeredColorSets, setRegisteredColorSets] = useState<
51
+ Record<string, string[]>
52
+ >({});
53
+
54
+ const [internalColorSet, setInternalColorSet] = useState<
55
+ Record<string, ChartColors | string>
56
+ >(() => {
57
+ return typeof colorSet === 'function' ? {} : colorSet;
58
+ });
59
+
60
+ useEffect(() => {
61
+ if (typeof colorSet === 'function') {
62
+ const allUniqueSeriesNames = Array.from(
63
+ new Set(Object.values(registeredColorSets).flat()),
64
+ );
65
+
66
+ if (allUniqueSeriesNames.length > 0) {
67
+ const newColorSet = colorSet(allUniqueSeriesNames);
68
+ setInternalColorSet(newColorSet);
69
+ }
70
+ } else {
71
+ setInternalColorSet(colorSet);
72
+ }
73
+ }, [registeredColorSets, colorSet]);
74
+
75
+ const allResources = useMemo(
76
+ () => Object.keys(internalColorSet),
77
+ [internalColorSet],
78
+ );
35
79
  const [selectedResources, setSelectedResources] =
36
80
  useState<string[]>(allResources);
37
81
 
82
+ useEffect(() => {
83
+ setSelectedResources(allResources);
84
+ }, [allResources]);
85
+
86
+ const register = useCallback((chartId: string, seriesNames: string[]) => {
87
+ setRegisteredColorSets((prev) => ({
88
+ ...prev,
89
+ [chartId]: seriesNames,
90
+ }));
91
+ }, []);
92
+
38
93
  const addSelectedResource = useCallback((resource: string) => {
39
94
  setSelectedResources((prev) =>
40
95
  prev.includes(resource) ? prev : [...prev, resource],
@@ -46,8 +101,8 @@ export const ChartLegendWrapper = ({
46
101
  }, []);
47
102
 
48
103
  const selectAllResources = useCallback(() => {
49
- setSelectedResources(allResources);
50
- }, [allResources]);
104
+ setSelectedResources(Object.keys(internalColorSet));
105
+ }, [internalColorSet]);
51
106
 
52
107
  const selectOnlyResource = useCallback((resource: string) => {
53
108
  setSelectedResources([resource]);
@@ -65,7 +120,7 @@ export const ChartLegendWrapper = ({
65
120
 
66
121
  const getColor = useCallback(
67
122
  (resource: string) => {
68
- const color = colorSet[resource];
123
+ const color = internalColorSet[resource];
69
124
  if (!color) {
70
125
  console.warn(
71
126
  `ChartLegendWrapper: No color defined for resource "${resource}"`,
@@ -74,12 +129,12 @@ export const ChartLegendWrapper = ({
74
129
  }
75
130
  return color;
76
131
  },
77
- [colorSet],
132
+ [internalColorSet],
78
133
  );
79
134
 
80
135
  const listResources = useCallback(() => {
81
- return Object.keys(colorSet);
82
- }, [colorSet]);
136
+ return Object.keys(internalColorSet);
137
+ }, [internalColorSet]);
83
138
 
84
139
  const chartLegendState = useMemo(
85
140
  () => ({
@@ -92,6 +147,7 @@ export const ChartLegendWrapper = ({
92
147
  getColor,
93
148
  listResources,
94
149
  isOnlyOneSelected,
150
+ register,
95
151
  }),
96
152
  [
97
153
  selectedResources,
@@ -103,6 +159,7 @@ export const ChartLegendWrapper = ({
103
159
  getColor,
104
160
  listResources,
105
161
  isOnlyOneSelected,
162
+ register,
106
163
  ],
107
164
  );
108
165
 
@@ -113,7 +170,6 @@ export const ChartLegendWrapper = ({
113
170
  );
114
171
  };
115
172
 
116
- // Hook for accessing legend state in custom components
117
173
  export const useChartLegend = () => {
118
174
  const context = useContext(ChartLegendContext);
119
175
  if (!context) {
@@ -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,11 +1,15 @@
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',
9
13
  });
10
14
 
11
15
  /**
@@ -18,6 +22,10 @@ export const LONG_DATE_FORMATER_WITHOUT_WEEKDAY = Intl.DateTimeFormat('en-GB', {
18
22
  day: '2-digit',
19
23
  });
20
24
 
25
+ /**
26
+ * @description Date formatter, with year, month and day. Used for describing long term date.
27
+ * @example 2025-01-01
28
+ */
21
29
  export const DATE_FORMATER = Intl.DateTimeFormat('fr-CA', {
22
30
  year: 'numeric',
23
31
  month: '2-digit',
@@ -25,12 +33,20 @@ export const DATE_FORMATER = Intl.DateTimeFormat('fr-CA', {
25
33
  hour12: false,
26
34
  });
27
35
 
36
+ /**
37
+ * @description Day month formatter, with weekday, day and month. Used for describing long term date.
38
+ * @example Wed 6 Oct
39
+ */
28
40
  export const DAY_MONTH_FORMATER = Intl.DateTimeFormat('en-GB', {
29
41
  weekday: 'short',
30
42
  day: '2-digit',
31
43
  month: 'short',
32
44
  });
33
45
 
46
+ /**
47
+ * @description Time formatter, with hour, minute and second. Used for describing long term date.
48
+ * @example 18:33:00
49
+ */
34
50
  export const TIME_SECOND_FORMATER = Intl.DateTimeFormat('en-GB', {
35
51
  hour12: false,
36
52
  hour: '2-digit',
@@ -38,16 +54,24 @@ export const TIME_SECOND_FORMATER = Intl.DateTimeFormat('en-GB', {
38
54
  second: '2-digit',
39
55
  });
40
56
 
57
+ /**
58
+ * @description Time formatter, with hour and minute. Used for describing long term date.
59
+ * @example 18:33
60
+ */
41
61
  export const TIME_FORMATER = Intl.DateTimeFormat('en-GB', {
42
62
  hour12: false,
43
63
  hour: '2-digit',
44
64
  minute: '2-digit',
45
65
  });
46
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
+ */
47
71
  export const DAY_MONTH_ABBREVIATED_HOUR_MINUTE_SECOND = Intl.DateTimeFormat(
48
72
  'en-GB',
49
73
  {
50
- day: 'numeric',
74
+ day: '2-digit',
51
75
  month: 'short',
52
76
  hour: '2-digit',
53
77
  minute: '2-digit',
@@ -57,11 +81,11 @@ export const DAY_MONTH_ABBREVIATED_HOUR_MINUTE_SECOND = Intl.DateTimeFormat(
57
81
  );
58
82
 
59
83
  /**
60
- * @description Day month abbreviated hour minute formatter, without second.
61
- * @example 15 Sept 14:30
84
+ * @description Day month abbreviated hour minute formatter. Used for describing long term date.
85
+ * @example 6 Oct 18:33
62
86
  */
63
87
  export const DAY_MONTH_ABBREVIATED_HOUR_MINUTE = Intl.DateTimeFormat('en-GB', {
64
- day: 'numeric',
88
+ day: '2-digit',
65
89
  month: 'short',
66
90
  hour: '2-digit',
67
91
  minute: '2-digit',
@@ -117,6 +141,21 @@ const isItFutureOrIsItPast = (
117
141
  }
118
142
  };
119
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
+ */
120
159
  export const FormattedDateTime = ({
121
160
  format,
122
161
  value,
@@ -219,15 +258,19 @@ export const FormattedDateTime = ({
219
258
  );
220
259
  case 'day-month-abbreviated-hour-minute':
221
260
  return (
222
- <>{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
+ </>
223
266
  );
224
267
  case 'day-month-abbreviated-hour-minute-second':
225
268
  return (
226
269
  <>
227
- {DAY_MONTH_ABBREVIATED_HOUR_MINUTE_SECOND.format(value).replace(
228
- ',',
229
- '',
230
- )}
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')}
231
274
  </>
232
275
  );
233
276
  case 'long-date':
@@ -235,7 +278,14 @@ export const FormattedDateTime = ({
235
278
  case 'long-date-without-weekday':
236
279
  return <>{LONG_DATE_FORMATER_WITHOUT_WEEKDAY.format(value)}</>;
237
280
  case 'chart-date':
238
- 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
+ );
239
289
  case 'year-month-day':
240
290
  return <>{YEAR_MONTH_DAY_FORMATTER.format(value)}</>;
241
291
  case 'month-day':
@@ -140,6 +140,8 @@ export const iconTable = {
140
140
  Stop: 'fas faStop',
141
141
  Play: 'fas faPlay',
142
142
  Mail: 'fas faEnvelope',
143
+ ThumbsUp: 'fas faThumbsUp',
144
+ ThumbsDown: 'fas faThumbsDown',
143
145
  };
144
146
 
145
147
  type IconProps = {
@@ -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>
@@ -148,8 +148,24 @@ export function addMissingDataPoint(
148
148
 
149
149
  const newValues: [number, number | string | null][] = [];
150
150
 
151
+ // add missing data points for the starting time
152
+ for (
153
+ let i = startingTimeStamp;
154
+ i < orginalValues[0][0];
155
+ i += sampleInterval
156
+ ) {
157
+ newValues.push([i, NAN_STRING]);
158
+ }
159
+
151
160
  // Process all but the last element
152
161
  for (let i = 0; i < orginalValues.length - 1; i++) {
162
+ if (
163
+ orginalValues[i][0] < startingTimeStamp ||
164
+ orginalValues[i][0] > startingTimeStamp + sampleDuration
165
+ ) {
166
+ continue;
167
+ }
168
+
153
169
  // Always add the current data point
154
170
  newValues.push(orginalValues[i]);
155
171
 
@@ -170,8 +186,18 @@ export function addMissingDataPoint(
170
186
  // Add the last element
171
187
  newValues.push(orginalValues[orginalValues.length - 1]);
172
188
 
189
+ // add missing data points for the ending time
190
+ for (
191
+ let i = orginalValues[orginalValues.length - 1][0] + sampleInterval;
192
+ i < startingTimeStamp + sampleDuration;
193
+ i += sampleInterval
194
+ ) {
195
+ newValues.push([i, NAN_STRING]);
196
+ }
197
+
173
198
  return newValues;
174
199
  }
200
+
175
201
  // get the value for the based value
176
202
  // TODO: We need to handle the negative value in the future
177
203
  export const getRelativeValue = (value: number, base: number) => {