@scality/core-ui 0.161.0 → 0.162.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 (136) hide show
  1. package/README.md +15 -15
  2. package/dist/components/accordion/Accordion.component.d.ts +0 -1
  3. package/dist/components/accordion/Accordion.component.d.ts.map +1 -1
  4. package/dist/components/barchartv2/Barchart.component.d.ts +55 -0
  5. package/dist/components/barchartv2/Barchart.component.d.ts.map +1 -0
  6. package/dist/components/barchartv2/Barchart.component.js +76 -0
  7. package/dist/components/barchartv2/utils.d.ts +95 -0
  8. package/dist/components/barchartv2/utils.d.ts.map +1 -0
  9. package/dist/components/barchartv2/utils.js +305 -0
  10. package/dist/components/buttonv2/Buttonv2.component.d.ts +1 -1
  11. package/dist/components/buttonv2/Buttonv2.component.d.ts.map +1 -1
  12. package/dist/components/constrainedtext/Constrainedtext.component.d.ts +2 -1
  13. package/dist/components/constrainedtext/Constrainedtext.component.d.ts.map +1 -1
  14. package/dist/components/constrainedtext/Constrainedtext.component.js +5 -4
  15. package/dist/components/coreuithemeprovider/CoreUiThemeProvider.d.ts +0 -1
  16. package/dist/components/coreuithemeprovider/CoreUiThemeProvider.d.ts.map +1 -1
  17. package/dist/components/date/FormattedDateTime.d.ts +1 -0
  18. package/dist/components/date/FormattedDateTime.d.ts.map +1 -1
  19. package/dist/components/date/FormattedDateTime.js +5 -0
  20. package/dist/components/emptytable/Emptytable.component.d.ts +0 -1
  21. package/dist/components/emptytable/Emptytable.component.d.ts.map +1 -1
  22. package/dist/components/emptytable/Emptytable.component.js +1 -0
  23. package/dist/components/error-pages/ErrorPage401.component.d.ts +0 -1
  24. package/dist/components/error-pages/ErrorPage401.component.d.ts.map +1 -1
  25. package/dist/components/error-pages/ErrorPage404.component.d.ts +0 -1
  26. package/dist/components/error-pages/ErrorPage404.component.d.ts.map +1 -1
  27. package/dist/components/error-pages/ErrorPage500.component.d.ts +0 -1
  28. package/dist/components/error-pages/ErrorPage500.component.d.ts.map +1 -1
  29. package/dist/components/error-pages/ErrorPageAuth.component.d.ts.map +1 -1
  30. package/dist/components/form/Form.component.d.ts +2 -2
  31. package/dist/components/form/Form.component.d.ts.map +1 -1
  32. package/dist/components/infomessage/InfoMessage.component.d.ts +0 -1
  33. package/dist/components/infomessage/InfoMessage.component.d.ts.map +1 -1
  34. package/dist/components/lateralnavbarlayout/LateralNavbarLayout.component.d.ts.map +1 -1
  35. package/dist/components/layout/Layout.component.d.ts.map +1 -1
  36. package/dist/components/layout/v2/panels.d.ts.map +1 -1
  37. package/dist/components/modal/Modal.component.js +2 -2
  38. package/dist/components/navbar/Navbar.component.js +2 -2
  39. package/dist/components/scrollbarwrapper/ScrollbarWrapper.component.d.ts +0 -1
  40. package/dist/components/scrollbarwrapper/ScrollbarWrapper.component.d.ts.map +1 -1
  41. package/dist/components/searchinput/SearchInput.component.d.ts +1 -2
  42. package/dist/components/searchinput/SearchInput.component.d.ts.map +1 -1
  43. package/dist/components/selectv2/Selectv2.component.d.ts +5 -5
  44. package/dist/components/selectv2/Selectv2.component.d.ts.map +1 -1
  45. package/dist/components/statuswrapper/Statuswrapper.component.d.ts +0 -1
  46. package/dist/components/statuswrapper/Statuswrapper.component.d.ts.map +1 -1
  47. package/dist/components/tablev2/Search.js +2 -2
  48. package/dist/components/tablev2/SingleSelectableContent.d.ts +1 -2
  49. package/dist/components/tablev2/SingleSelectableContent.d.ts.map +1 -1
  50. package/dist/components/tablev2/TableCommon.d.ts +2 -2
  51. package/dist/components/tablev2/TableCommon.d.ts.map +1 -1
  52. package/dist/components/tablev2/TableSync.d.ts +8 -0
  53. package/dist/components/tablev2/TableSync.d.ts.map +1 -0
  54. package/dist/components/tablev2/TableSync.js +11 -0
  55. package/dist/components/tablev2/Tablev2.component.d.ts +2 -1
  56. package/dist/components/tablev2/Tablev2.component.d.ts.map +1 -1
  57. package/dist/components/tablev2/Tablev2.component.js +10 -9
  58. package/dist/components/tabsv2/ScrollButton.d.ts +1 -2
  59. package/dist/components/tabsv2/ScrollButton.d.ts.map +1 -1
  60. package/dist/components/tabsv2/ScrollButton.js +2 -2
  61. package/dist/components/tabsv2/Tabsv2.component.d.ts +2 -2
  62. package/dist/components/tabsv2/Tabsv2.component.d.ts.map +1 -1
  63. package/dist/components/tabsv2/Tabsv2.component.js +2 -2
  64. package/dist/components/text/Text.component.d.ts +0 -1
  65. package/dist/components/text/Text.component.d.ts.map +1 -1
  66. package/dist/components/textarea/TextArea.component.d.ts +3 -3
  67. package/dist/components/textarea/TextArea.component.d.ts.map +1 -1
  68. package/dist/components/textbadge/TextBadge.component.d.ts +0 -1
  69. package/dist/components/textbadge/TextBadge.component.d.ts.map +1 -1
  70. package/dist/components/toast/Toast.component.d.ts +1 -1
  71. package/dist/components/toast/Toast.component.d.ts.map +1 -1
  72. package/dist/components/toast/ToastProvider.d.ts.map +1 -1
  73. package/dist/components/vegachartv2/SyncedCursorCharts.d.ts +1 -2
  74. package/dist/components/vegachartv2/SyncedCursorCharts.d.ts.map +1 -1
  75. package/dist/components/vegachartv2/VegaChartV2.component.d.ts +1 -2
  76. package/dist/components/vegachartv2/VegaChartV2.component.d.ts.map +1 -1
  77. package/dist/components/vegachartv2/VegaChartV2.component.js +2 -2
  78. package/dist/icons/branding.d.ts.map +1 -1
  79. package/dist/icons/scality-loading.d.ts.map +1 -1
  80. package/dist/next.d.ts +1 -0
  81. package/dist/next.d.ts.map +1 -1
  82. package/dist/next.js +1 -0
  83. package/dist/style/theme.d.ts +19 -0
  84. package/dist/style/theme.d.ts.map +1 -1
  85. package/dist/style/theme.js +18 -1
  86. package/package.json +6 -3
  87. package/setupTests.js +6 -0
  88. package/src/lib/components/accordion/Accordion.component.tsx +1 -1
  89. package/src/lib/components/barchartv2/Barchart.component.test.tsx +383 -0
  90. package/src/lib/components/barchartv2/Barchart.component.tsx +309 -0
  91. package/src/lib/components/barchartv2/utils.test.ts +782 -0
  92. package/src/lib/components/barchartv2/utils.ts +486 -0
  93. package/src/lib/components/buttonv2/Buttonv2.component.tsx +1 -1
  94. package/src/lib/components/constrainedtext/Constrainedtext.component.tsx +22 -3
  95. package/src/lib/components/coreuithemeprovider/CoreUiThemeProvider.tsx +0 -1
  96. package/src/lib/components/date/FormattedDateTime.tsx +6 -0
  97. package/src/lib/components/emptytable/Emptytable.component.tsx +1 -1
  98. package/src/lib/components/error-pages/ErrorPage401.component.tsx +0 -1
  99. package/src/lib/components/error-pages/ErrorPage404.component.tsx +0 -1
  100. package/src/lib/components/error-pages/ErrorPage500.component.tsx +0 -1
  101. package/src/lib/components/error-pages/ErrorPageAuth.component.tsx +0 -1
  102. package/src/lib/components/form/Form.component.tsx +1 -1
  103. package/src/lib/components/infomessage/InfoMessage.component.tsx +0 -1
  104. package/src/lib/components/lateralnavbarlayout/LateralNavbarLayout.component.tsx +0 -1
  105. package/src/lib/components/layout/Layout.component.tsx +0 -1
  106. package/src/lib/components/layout/v2/panels.tsx +1 -1
  107. package/src/lib/components/modal/Modal.component.tsx +2 -2
  108. package/src/lib/components/navbar/Navbar.component.tsx +2 -2
  109. package/src/lib/components/scrollbarwrapper/ScrollbarWrapper.component.tsx +0 -1
  110. package/src/lib/components/searchinput/SearchInput.component.tsx +0 -1
  111. package/src/lib/components/selectv2/Selectv2.component.tsx +11 -9
  112. package/src/lib/components/sidebar/Sidebar.component.tsx +1 -1
  113. package/src/lib/components/statuswrapper/Statuswrapper.component.tsx +0 -1
  114. package/src/lib/components/tablev2/Search.tsx +2 -2
  115. package/src/lib/components/tablev2/SingleSelectableContent.tsx +2 -2
  116. package/src/lib/components/tablev2/TableCommon.tsx +1 -1
  117. package/src/lib/components/tablev2/TableSync.test.tsx +31 -0
  118. package/src/lib/components/tablev2/TableSync.tsx +36 -0
  119. package/src/lib/components/tablev2/Tablev2.component.tsx +11 -9
  120. package/src/lib/components/tabsv2/ScrollButton.tsx +2 -2
  121. package/src/lib/components/tabsv2/Tabsv2.component.tsx +6 -6
  122. package/src/lib/components/text/Text.component.tsx +4 -5
  123. package/src/lib/components/textarea/TextArea.component.tsx +3 -2
  124. package/src/lib/components/textbadge/TextBadge.component.tsx +0 -1
  125. package/src/lib/components/toast/Toast.component.tsx +1 -1
  126. package/src/lib/components/toast/ToastProvider.tsx +3 -1
  127. package/src/lib/components/vegachartv2/SyncedCursorCharts.tsx +1 -1
  128. package/src/lib/components/vegachartv2/VegaChartV2.component.tsx +2 -2
  129. package/src/lib/icons/branding.tsx +0 -2
  130. package/src/lib/icons/scality-loading.tsx +0 -2
  131. package/src/lib/next.ts +5 -0
  132. package/src/lib/style/theme.ts +24 -1
  133. package/stories/BarChart/barchart.stories.tsx +655 -0
  134. package/stories/areachart.stories.tsx +0 -1
  135. package/stories/tablev2.stories.tsx +41 -0
  136. package/tsconfig.json +5 -2
@@ -0,0 +1,486 @@
1
+ import {
2
+ BarchartProps,
3
+ BarchartBars,
4
+ BarchartTooltipFn,
5
+ } from './Barchart.component';
6
+ import { DAY_MONTH_FORMATER, TIME_FORMATER } from '../date/FormattedDateTime';
7
+ import { TooltipContentProps } from 'recharts';
8
+ import { chartColors, ChartColors } from '../../style/theme';
9
+
10
+ export const getRoundReferenceValue = (value: number): number => {
11
+ if (value <= 0) return 10; // Default for zero or negative values
12
+
13
+ // Get the magnitude (10^n where n is the number of digits - 1)
14
+ const magnitude = Math.pow(10, Math.floor(Math.log10(value)));
15
+
16
+ // Normalized value between 1 and 10
17
+ const normalized = value / magnitude;
18
+
19
+ // 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;
24
+ };
25
+
26
+ export const getMaxBarValue = (
27
+ data: { [key: string]: string | number }[],
28
+ stacked?: boolean,
29
+ ) => {
30
+ const values = data.map((item) => {
31
+ // If stacked, we need to filter out category and sum the values in the same object
32
+ if (stacked) {
33
+ // Get objects keys except category
34
+ const filterOutCategory = Object.keys(item).filter(
35
+ (key) => key !== 'category',
36
+ );
37
+ // Sum the values in the same object (corresponding to one bar) based on the keys
38
+ const sumValues = filterOutCategory.reduce((acc, curr) => {
39
+ return acc + Number(item[curr]);
40
+ }, 0);
41
+ return sumValues;
42
+ }
43
+ //filter out the category key
44
+ const numberValues = Object.keys(item)
45
+ .filter((key) => key !== 'category')
46
+ .map((key) => Number(item[key]));
47
+ // Get the max value among the values in the object (corresponding to one bar)
48
+ return Math.max(...numberValues);
49
+ });
50
+ return Math.max(...values);
51
+ };
52
+
53
+ /**
54
+ * Generates time ranges between start and end dates based on the given interval
55
+ * @param startDate - Start date
56
+ * @param endDate - End date
57
+ * @param interval - Interval in milliseconds
58
+ * @returns Array of time ranges with start and end properties as Date objects
59
+ */
60
+ const generateTimeRanges = (
61
+ startDate: Date,
62
+ endDate: Date,
63
+ interval: number,
64
+ ): { start: Date; end: Date }[] => {
65
+ const ranges: { start: Date; end: Date }[] = [];
66
+ if (!startDate || !endDate || !interval) {
67
+ return ranges;
68
+ }
69
+
70
+ let currentDate = new Date(startDate.getTime());
71
+ while (currentDate.getTime() <= endDate.getTime()) {
72
+ const rangeEnd = new Date(currentDate.getTime() + interval);
73
+
74
+ ranges.push({
75
+ start: new Date(currentDate.getTime()),
76
+ end: rangeEnd,
77
+ });
78
+
79
+ currentDate = new Date(currentDate.getTime() + interval);
80
+ }
81
+
82
+ return ranges;
83
+ };
84
+
85
+ /**
86
+ * Formats a date based on the interval
87
+ * @param date - Date object
88
+ * @param interval - Interval in milliseconds
89
+ * @returns Formatted string
90
+ */
91
+ const formatDate = (date: Date, interval: number): string => {
92
+ if (interval > 24 * 60 * 60 * 1000) {
93
+ return (
94
+ DAY_MONTH_FORMATER.format(date).replace(/[ ,]/g, '') +
95
+ ' ' +
96
+ TIME_FORMATER.format(date)
97
+ );
98
+ } else if (interval === 24 * 60 * 60 * 1000) {
99
+ // Daily or longer intervals - use day format
100
+ return DAY_MONTH_FORMATER.format(date).replace(/[ ,]/g, '');
101
+ } else if (interval >= 60 * 1000) {
102
+ //Handle hourly and minute intervals - use minute format
103
+ return TIME_FORMATER.format(date);
104
+ } else {
105
+ // Second intervals or less - use full timestamp
106
+ return date.toISOString();
107
+ }
108
+ };
109
+
110
+ /**
111
+ * Finds the time range that contains the given date
112
+ * @param date - Data point date
113
+ * @param ranges - Array of time ranges
114
+ * @returns The range that contains the date, or null if not found
115
+ */
116
+ const findRangeForDate = (
117
+ date: Date,
118
+ ranges: { start: Date; end: Date }[],
119
+ ): { start: Date; end: Date } | null => {
120
+ const timestamp = date.getTime();
121
+ return (
122
+ ranges.find(
123
+ (range) =>
124
+ timestamp >= range.start.getTime() && timestamp < range.end.getTime(),
125
+ ) || null
126
+ );
127
+ };
128
+
129
+ /**
130
+ * Transforms time-based data into chart format
131
+ */
132
+ export const transformTimeData = <T extends BarchartBars>(
133
+ bars: T,
134
+ type: {
135
+ type: 'time';
136
+ timeRange: {
137
+ startDate: Date;
138
+ endDate: Date;
139
+ interval: number;
140
+ };
141
+ },
142
+ barDataKeys: string[],
143
+ ) => {
144
+ const timeRanges = generateTimeRanges(
145
+ type.timeRange.startDate,
146
+ type.timeRange.endDate,
147
+ type.timeRange.interval,
148
+ );
149
+
150
+ const categoryMap = new Map<
151
+ string | number,
152
+ { [key: string]: string | number }
153
+ >();
154
+
155
+ // Initialize all ranges with zeros
156
+ timeRanges.forEach((range) => {
157
+ const categoryDisplay = formatDate(range.start, type.timeRange.interval);
158
+ const initialData: { [key: string]: string | number } = {
159
+ category: categoryDisplay,
160
+ };
161
+ barDataKeys.forEach((dataKey) => {
162
+ initialData[dataKey] = 0;
163
+ });
164
+ categoryMap.set(range.start.getTime(), initialData);
165
+ });
166
+
167
+ // Populate with actual data
168
+ bars.forEach((bar) => {
169
+ bar.data.forEach(([dateValue, value]) => {
170
+ // Convert to Date if it's not already a Date object
171
+ const date =
172
+ dateValue instanceof Date
173
+ ? dateValue
174
+ : new Date(dateValue as string | number);
175
+ const range = findRangeForDate(date, timeRanges);
176
+ if (range) {
177
+ const existingData = categoryMap.get(range.start.getTime())!;
178
+ existingData[bar.label] = value;
179
+ }
180
+ });
181
+ });
182
+
183
+ return Array.from(categoryMap.values());
184
+ };
185
+
186
+ /**
187
+ * Transforms category-based data into chart format
188
+ */
189
+ export const transformCategoryData = <T extends BarchartBars>(
190
+ bars: T,
191
+ barDataKeys: string[],
192
+ ) => {
193
+ const categoryMap = new Map<
194
+ string | number,
195
+ { [key: string]: string | number }
196
+ >();
197
+
198
+ bars.forEach((bar) => {
199
+ bar.data.forEach(([key, value]) => {
200
+ const categoryKey = String(key);
201
+
202
+ if (!categoryMap.has(categoryKey)) {
203
+ const newData: { [key: string]: string | number } = {
204
+ category: categoryKey,
205
+ };
206
+ barDataKeys.forEach((dataKey) => {
207
+ newData[dataKey] = 0;
208
+ });
209
+ categoryMap.set(categoryKey, newData);
210
+ }
211
+
212
+ const existingData = categoryMap.get(categoryKey)!;
213
+ existingData[bar.label] = value;
214
+ });
215
+ });
216
+
217
+ return Array.from(categoryMap.values());
218
+ };
219
+
220
+ /**
221
+ * Applies custom sorting to chart data
222
+ */
223
+ export const applySortingToData = <T extends BarchartBars>(
224
+ data: { [key: string]: string | number }[],
225
+ barDataKeys: string[],
226
+ defaultSort: BarchartProps<T>['defaultSort'],
227
+ ) => {
228
+ const points = data.map((item) => {
229
+ const point: any = { category: item.category };
230
+ barDataKeys.forEach((dataKey) => {
231
+ point[dataKey] = Number(item[dataKey]) || 0;
232
+ });
233
+ return point;
234
+ });
235
+
236
+ points.sort(defaultSort);
237
+
238
+ return points.map((point) => {
239
+ const dataItem: { [key: string]: string | number } = {
240
+ category: point.category,
241
+ };
242
+ barDataKeys.forEach((dataKey) => {
243
+ dataItem[dataKey] = point[dataKey];
244
+ });
245
+ return dataItem;
246
+ });
247
+ };
248
+
249
+ const getRechartsBarsAndBarDataKeys = (
250
+ bars: BarchartBars,
251
+ colorSet: Record<BarchartBars[number]['label'], ChartColors | (string & {})>,
252
+ stacked?: boolean,
253
+ ) => {
254
+ const rechartsBars: { dataKey: string; fill: string; stackId?: string }[] =
255
+ [];
256
+ const barDataKeys: string[] = [];
257
+
258
+ bars.forEach((bar) => {
259
+ const colorName = colorSet[bar.label];
260
+ const rechartsBar = {
261
+ dataKey: bar.label,
262
+ fill: chartColors[colorName] || colorName,
263
+ stackId: stacked ? 'stacked' : undefined,
264
+ };
265
+
266
+ rechartsBars.push(rechartsBar);
267
+ barDataKeys.push(bar.label);
268
+ });
269
+
270
+ return {
271
+ rechartsBars,
272
+ barDataKeys,
273
+ };
274
+ };
275
+
276
+ /**
277
+ * Converts prometheus data to recharts data format
278
+ * @param bars - The bars to convert
279
+ * @param type - The chart type (category or time)
280
+ * @returns Recharts data format
281
+ */
282
+ export const formatPrometheusDataToRechartsDataAndBars = <
283
+ T extends BarchartBars,
284
+ >(
285
+ bars: T,
286
+ type: BarchartProps<T>['type'],
287
+ colorSet: Record<T[number]['label'], ChartColors | (string & {})>,
288
+ stacked?: boolean,
289
+ defaultSort?: BarchartProps<T>['defaultSort'],
290
+ ): {
291
+ data: { [key: string]: string | number }[];
292
+ rechartsBars: { dataKey: string; fill: string; stackId?: string }[];
293
+ } => {
294
+ const { rechartsBars, barDataKeys } = getRechartsBarsAndBarDataKeys(
295
+ bars,
296
+ colorSet,
297
+ stacked,
298
+ );
299
+
300
+ let data =
301
+ type !== 'category' && type.type === 'time'
302
+ ? transformTimeData(bars, type, barDataKeys)
303
+ : transformCategoryData(bars, barDataKeys);
304
+
305
+ if (type === 'category' && defaultSort) {
306
+ data = applySortingToData(data, barDataKeys, defaultSort);
307
+ }
308
+
309
+ const sortedRechartsBars = sortStackedBars(rechartsBars, data, stacked);
310
+
311
+ return {
312
+ rechartsBars: sortedRechartsBars,
313
+ data,
314
+ };
315
+ };
316
+
317
+ export type UnitRange = {
318
+ threshold: number;
319
+ label: string;
320
+ }[];
321
+
322
+ export const computeUnitLabelAndRoundReferenceValue = (
323
+ data: any,
324
+ maxValue: number,
325
+ unitRange: UnitRange | undefined,
326
+ ) => {
327
+ if (!unitRange) {
328
+ const roundReferenceValue = getRoundReferenceValue(maxValue);
329
+ return { unitLabel: '', roundReferenceValue, rechartsData: data };
330
+ }
331
+
332
+ const { valueBase, unitLabel } = getUnitLabel(unitRange ?? [], maxValue);
333
+ const topValue = Math.ceil(maxValue / valueBase / 10) * 10;
334
+ const roundReferenceValue = getRoundReferenceValue(topValue);
335
+ const rechartsData = data.map((dataPoint) => {
336
+ const normalizedDataPoint = { ...dataPoint };
337
+ Object.entries(dataPoint).forEach(([key, value]) => {
338
+ if (key !== 'category' && typeof value === 'number') {
339
+ normalizedDataPoint[key] = value / valueBase;
340
+ }
341
+ });
342
+ return normalizedDataPoint;
343
+ });
344
+ return { unitLabel, roundReferenceValue, rechartsData };
345
+ };
346
+
347
+ /**
348
+ * Return the unit label base on the current dataset, and the valueBase which is used to convert the data
349
+ * @param {any} unitRange
350
+ * @param {any} maxValue the maximum value among the data set
351
+ * @returns {any}
352
+ */
353
+ export function getUnitLabel(
354
+ unitRange: {
355
+ threshold: number;
356
+ label: string;
357
+ }[],
358
+ maxValue: number,
359
+ ): {
360
+ valueBase: number;
361
+ unitLabel: string;
362
+ } {
363
+ // first sort the unitRange
364
+ unitRange.sort(
365
+ (
366
+ unitA: {
367
+ threshold: number;
368
+ label: string;
369
+ },
370
+ unitB: {
371
+ threshold: number;
372
+ label: string;
373
+ },
374
+ ) => {
375
+ return unitA.threshold - unitB.threshold;
376
+ },
377
+ );
378
+ let index = unitRange.findIndex((range) => range.threshold > maxValue);
379
+
380
+ // last unit
381
+ if (index === -1) {
382
+ index = unitRange.length;
383
+ }
384
+
385
+ if (index === 0) {
386
+ return {
387
+ valueBase: unitRange[index].threshold,
388
+ unitLabel: unitRange[index].label,
389
+ };
390
+ }
391
+
392
+ return {
393
+ // if the threshold is 0, we use 1 as the value base to avoid division by 0
394
+ valueBase: unitRange[index - 1].threshold || 1,
395
+ unitLabel: unitRange[index - 1].label,
396
+ };
397
+ }
398
+
399
+ // Sort stacked bars by their average values in descending order
400
+ // This ensures the largest bars appear at the bottom of the stack
401
+ export const sortStackedBars = (
402
+ rechartsBars: {
403
+ dataKey: string;
404
+ fill: string;
405
+ stackId?: string;
406
+ }[],
407
+ data: {
408
+ [key: string]: string | number;
409
+ }[],
410
+ stacked?: boolean,
411
+ ) => {
412
+ if (!stacked) {
413
+ return rechartsBars;
414
+ }
415
+ const barAverages = rechartsBars.map((bar) => {
416
+ const values = data
417
+ .map((item) => Number(item[bar.dataKey]) || 0)
418
+ .filter((value) => !isNaN(value));
419
+ const average =
420
+ values.length > 0 ? values.reduce((a, b) => a + b, 0) / values.length : 0;
421
+ return { ...bar, average };
422
+ });
423
+
424
+ // Sort by average in descending order (largest first, which will be at bottom in stack)
425
+ barAverages.sort((a, b) => b.average - a.average);
426
+ // Remove the average property and keep only the bar data
427
+ return barAverages.map(({ average, ...bar }) => bar);
428
+ };
429
+
430
+ export const renderTooltipContent = <T extends BarchartBars>(
431
+ props: TooltipContentProps<number, string>,
432
+ tooltip: BarchartTooltipFn<T> | undefined,
433
+ hoveredValue: string | undefined,
434
+ ) => {
435
+ const { active, payload, label } = props;
436
+
437
+ if (!active || !tooltip) {
438
+ return null;
439
+ }
440
+
441
+ const tooltipValues: {
442
+ label: T[number]['label'];
443
+ value: number;
444
+ isHovered: boolean;
445
+ }[] = payload.map((item) => ({
446
+ label: item.name,
447
+ value: item.value,
448
+ isHovered: item.name === hoveredValue,
449
+ }));
450
+
451
+ const currentPoint = {
452
+ category: label as string | number,
453
+ values: tooltipValues,
454
+ };
455
+
456
+ return tooltip(currentPoint);
457
+ };
458
+
459
+ export const useChartData = <T extends BarchartBars>(
460
+ bars: T,
461
+ type: BarchartProps<T>['type'],
462
+ colorSet: Record<T[number]['label'], ChartColors | (string & {})>,
463
+ stacked?: boolean,
464
+ defaultSort?: BarchartProps<T>['defaultSort'],
465
+ unitRange?: UnitRange,
466
+ ) => {
467
+ const { data, rechartsBars } = formatPrometheusDataToRechartsDataAndBars(
468
+ bars,
469
+ type,
470
+ colorSet,
471
+ stacked,
472
+ defaultSort,
473
+ );
474
+
475
+ const maxValue = getMaxBarValue(data, stacked);
476
+
477
+ const { unitLabel, roundReferenceValue, rechartsData } =
478
+ computeUnitLabelAndRoundReferenceValue(data, maxValue, unitRange);
479
+
480
+ return {
481
+ rechartsBars,
482
+ unitLabel,
483
+ roundReferenceValue,
484
+ rechartsData,
485
+ };
486
+ };
@@ -1,4 +1,4 @@
1
- import React, { ButtonHTMLAttributes } from 'react';
1
+ import { ButtonHTMLAttributes } from 'react';
2
2
  import styled, { css } from 'styled-components';
3
3
  import { spacing } from '../../spacing';
4
4
  import { fontSize, fontWeight } from '../../style/theme';
@@ -12,6 +12,7 @@ type Props = {
12
12
  tooltipStyle?: $PropertyType<TooltipProps, 'overlayStyle'>;
13
13
  tooltipPlacement?: $PropertyType<TooltipProps, 'placement'>;
14
14
  lineClamp?: number;
15
+ centered?: boolean;
15
16
  };
16
17
  // for lineClamp cf https://css-tricks.com/almanac/properties/l/line-clamp/
17
18
  // it should work on all major navigator, despite the --webkit prefix
@@ -19,6 +20,7 @@ type Props = {
19
20
  const ConstrainedTextContainer = styled.div`
20
21
  overflow: hidden;
21
22
  text-overflow: ellipsis;
23
+ text-align: ${(props) => (props.centered ? 'center' : 'left')};
22
24
 
23
25
  ${(props) =>
24
26
  props.lineClamp > 1
@@ -49,12 +51,18 @@ function isEllipsisActive(element: HTMLDivElement) {
49
51
  );
50
52
  }
51
53
 
52
- function getConstrainedTextContainer(constrainedTextRef, lineClamp, text) {
54
+ function getConstrainedTextContainer(
55
+ constrainedTextRef,
56
+ lineClamp,
57
+ text,
58
+ centered,
59
+ ) {
53
60
  return (
54
61
  <ConstrainedTextContainer
55
62
  ref={constrainedTextRef}
56
63
  className="sc-constrainedtext"
57
64
  lineClamp={lineClamp}
65
+ centered={centered}
58
66
  >
59
67
  {text}
60
68
  </ConstrainedTextContainer>
@@ -66,6 +74,7 @@ function ConstrainedText({
66
74
  tooltipStyle,
67
75
  tooltipPlacement,
68
76
  lineClamp = 1,
77
+ centered = false,
69
78
  }: Props): JSX.Element {
70
79
  const [displayToolTip, setDisplayToolTip] = useState(false);
71
80
  const constrainedTextRef = useCallback(
@@ -83,12 +92,22 @@ function ConstrainedText({
83
92
  placement={tooltipPlacement}
84
93
  >
85
94
  <Text>
86
- {getConstrainedTextContainer(constrainedTextRef, lineClamp, text)}
95
+ {getConstrainedTextContainer(
96
+ constrainedTextRef,
97
+ lineClamp,
98
+ text,
99
+ centered,
100
+ )}
87
101
  </Text>
88
102
  </Tooltip>
89
103
  ) : (
90
104
  <Text>
91
- {getConstrainedTextContainer(constrainedTextRef, lineClamp, text)}
105
+ {getConstrainedTextContainer(
106
+ constrainedTextRef,
107
+ lineClamp,
108
+ text,
109
+ centered,
110
+ )}
92
111
  </Text>
93
112
  )}
94
113
  </BlockTooltip>
@@ -1,4 +1,3 @@
1
- import React from 'react';
2
1
  import { ThemeProvider } from 'styled-components';
3
2
  import { space, fontSize, CoreUITheme } from '../../style/theme';
4
3
 
@@ -8,6 +8,12 @@ export const DATE_FORMATER = Intl.DateTimeFormat('fr-CA', {
8
8
  hour12: false,
9
9
  });
10
10
 
11
+ export const DAY_MONTH_FORMATER = Intl.DateTimeFormat('en-GB', {
12
+ weekday: 'short',
13
+ day: '2-digit',
14
+ month: 'short',
15
+ });
16
+
11
17
  export const TIME_SECOND_FORMATER = Intl.DateTimeFormat('en-GB', {
12
18
  hour12: false,
13
19
  hour: '2-digit',
@@ -1,5 +1,5 @@
1
1
  // @ts-nocheck
2
- import * as React from 'react';
2
+
3
3
  import styled, { css } from 'styled-components';
4
4
  import { spacing } from '../../spacing';
5
5
 
@@ -1,4 +1,3 @@
1
- import React from 'react';
2
1
  import {
3
2
  ErrorPageContainer,
4
3
  Row,
@@ -1,4 +1,3 @@
1
- import React from 'react';
2
1
  import {
3
2
  ErrorPageContainer,
4
3
  Row,
@@ -1,4 +1,3 @@
1
- import React from 'react';
2
1
  import {
3
2
  ErrorPageContainer,
4
3
  Row,
@@ -1,4 +1,3 @@
1
- import React from 'react';
2
1
  import { Icon } from '../icon/Icon.component';
3
2
  import {
4
3
  ErrorPageContainer,
@@ -1,4 +1,4 @@
1
- import React, {
1
+ import {
2
2
  Children,
3
3
  createContext,
4
4
  FormHTMLAttributes,
@@ -1,4 +1,3 @@
1
- import React from 'react';
2
1
  import styled, { useTheme } from 'styled-components';
3
2
  import { Stack } from '../../spacing';
4
3
  import { Icon } from '../icon/Icon.component';
@@ -1,4 +1,3 @@
1
- import React from 'react';
2
1
  import styled from 'styled-components';
3
2
 
4
3
  import { Props as SidebarProps } from '../sidebar/Sidebar.component';
@@ -1,4 +1,3 @@
1
- import React from 'react';
2
1
  import styled from 'styled-components';
3
2
 
4
3
  import { Navbar } from '../navbar/Navbar.component';
@@ -1,5 +1,5 @@
1
- import { ReactElement } from 'react';
2
1
  import styled from 'styled-components';
2
+ import { ReactElement } from 'react';
3
3
  import { ThemeColors } from '../../../style/theme';
4
4
  import { AppContainer } from './AppContainer';
5
5
 
@@ -1,5 +1,5 @@
1
1
  import { ReactNode, useEffect, useLayoutEffect, useRef } from 'react';
2
- import ReactDom from 'react-dom';
2
+ import { createPortal } from 'react-dom';
3
3
  import styled from 'styled-components';
4
4
  import { Wrap, spacing } from '../../spacing';
5
5
  import { zIndex } from '../../style/theme';
@@ -95,7 +95,7 @@ const Modal = ({
95
95
  }
96
96
  }, [isOpen]);
97
97
  return isOpen
98
- ? ReactDom.createPortal(
98
+ ? createPortal(
99
99
  <ModalContainer
100
100
  className="sc-modal"
101
101
  role={role}
@@ -1,4 +1,4 @@
1
- import React, { Fragment } from 'react';
1
+ import { cloneElement, Fragment } from 'react';
2
2
  import styled, { css } from 'styled-components';
3
3
  import { Logo } from '../../icons/branding';
4
4
  import { spacing } from '../../spacing';
@@ -210,7 +210,7 @@ function NavBar({
210
210
  );
211
211
  }
212
212
  return link ? (
213
- React.cloneElement(link, {
213
+ cloneElement(link, {
214
214
  className: selected ? 'selected' : '',
215
215
  'aria-selected': selected,
216
216
  role: 'tab',
@@ -1,4 +1,3 @@
1
- import React from 'react';
2
1
  import styled, { createGlobalStyle, css } from 'styled-components';
3
2
 
4
3
  type Props = {
@@ -1,4 +1,3 @@
1
- import React from 'react';
2
1
  import { ChangeEvent, forwardRef, useEffect, useRef, useState } from 'react';
3
2
  import styled, { css } from 'styled-components';
4
3
  import { Icon } from '../icon/Icon.component';