@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,782 @@
1
+ import { coreUIAvailableThemes } from '../../style/theme';
2
+ import {
3
+ applySortingToData,
4
+ computeUnitLabelAndRoundReferenceValue,
5
+ formatPrometheusDataToRechartsDataAndBars,
6
+ getMaxBarValue,
7
+ getRoundReferenceValue,
8
+ renderTooltipContent,
9
+ sortStackedBars,
10
+ transformCategoryData,
11
+ transformTimeData,
12
+ UnitRange,
13
+ } from './utils';
14
+
15
+ // Mock theme object for tests
16
+ const mockTheme = {
17
+ statusHealthy: '#00D100',
18
+ statusHealthyRGB: '0, 209, 0',
19
+ statusCritical: '#D10000',
20
+ statusCriticalRGB: '209, 0, 0',
21
+ statusWarning: '#FFA500',
22
+ statusWarningRGB: '255, 165, 0',
23
+ selectedActive: '#337FBD',
24
+ highlight: '#337FBD',
25
+ border: '#C2C8CC',
26
+ buttonPrimary: '#337FBD',
27
+ buttonSecondary: '#68737D',
28
+ buttonDelete: '#EF3340',
29
+ infoPrimary: '#337FBD',
30
+ infoSecondary: '#68737D',
31
+ backgroundLevel1: '#ffffff',
32
+ backgroundLevel2: '#F9FAFB',
33
+ backgroundLevel3: '#E9EBED',
34
+ backgroundLevel4: '#D8DCDE',
35
+ textPrimary: '#2F3941',
36
+ textSecondary: '#68737D',
37
+ textTertiary: '#87929D',
38
+ textReverse: '#ffffff',
39
+ textLink: '#337FBD',
40
+ } as const;
41
+
42
+ describe('transformTimeData', () => {
43
+ it('should transform time data with daily intervals', () => {
44
+ const bars = [
45
+ {
46
+ label: 'Success',
47
+ data: [
48
+ [new Date('2024-07-05T00:00:00'), 10],
49
+ [new Date('2024-07-06T00:00:00'), 20],
50
+ ] as [Date, number][],
51
+ },
52
+ ];
53
+
54
+ const type = {
55
+ type: 'time' as const,
56
+ timeRange: {
57
+ startDate: new Date('2024-07-05T00:00:00'),
58
+ endDate: new Date('2024-07-06T00:00:00'),
59
+ interval: 24 * 60 * 60 * 1000, // 1 day
60
+ },
61
+ };
62
+
63
+ const barDataKeys = ['Success'];
64
+
65
+ const result = transformTimeData(bars, type, barDataKeys);
66
+
67
+ expect(result).toEqual([
68
+ { category: 'Fri05Jul', Success: 10 },
69
+ { category: 'Sat06Jul', Success: 20 },
70
+ ]);
71
+ });
72
+
73
+ it('should create empty time slots when data is missing', () => {
74
+ const bars = [
75
+ {
76
+ label: 'Success',
77
+ data: [
78
+ [new Date('2024-07-05T00:00:00'), 10],
79
+ // Missing July 6th
80
+ [new Date('2024-07-07T00:00:00'), 30],
81
+ ] as [Date, number][],
82
+ },
83
+ ];
84
+
85
+ const type = {
86
+ type: 'time' as const,
87
+ timeRange: {
88
+ startDate: new Date('2024-07-05T00:00:00'),
89
+ endDate: new Date('2024-07-07T00:00:00'),
90
+ interval: 24 * 60 * 60 * 1000, // 1 day
91
+ },
92
+ };
93
+
94
+ const barDataKeys = ['Success'];
95
+
96
+ const result = transformTimeData(bars, type, barDataKeys);
97
+
98
+ expect(result).toEqual([
99
+ { category: 'Fri05Jul', Success: 10 },
100
+ { category: 'Sat06Jul', Success: 0 }, // Missing data filled with 0
101
+ { category: 'Sun07Jul', Success: 30 },
102
+ ]);
103
+ });
104
+
105
+ it('should handle hourly intervals correctly', () => {
106
+ const bars = [
107
+ {
108
+ label: 'Success',
109
+ data: [
110
+ [new Date('2024-07-05T10:00:00'), 10],
111
+ [new Date('2024-07-05T11:00:00'), 20],
112
+ ] as [Date, number][],
113
+ },
114
+ ];
115
+
116
+ const type = {
117
+ type: 'time' as const,
118
+ timeRange: {
119
+ startDate: new Date('2024-07-05T10:00:00'),
120
+ endDate: new Date('2024-07-05T11:00:00'),
121
+ interval: 60 * 60 * 1000, // 1 hour
122
+ },
123
+ };
124
+
125
+ const barDataKeys = ['Success'];
126
+
127
+ const result = transformTimeData(bars, type, barDataKeys);
128
+
129
+ expect(result).toEqual([
130
+ { category: '10:00', Success: 10 },
131
+ { category: '11:00', Success: 20 },
132
+ ]);
133
+ });
134
+
135
+ it('should use latest value when multiple events occur in same time period', () => {
136
+ const bars = [
137
+ {
138
+ label: 'Success',
139
+ data: [
140
+ [new Date('2024-07-05T08:30:00'), 10], // 8:30 AM
141
+ [new Date('2024-07-05T14:45:00'), 25], // 2:45 PM (should overwrite 8:30 AM)
142
+ [new Date('2024-07-06T09:15:00'), 15], // Next day
143
+ ] as [Date, number][],
144
+ },
145
+ ];
146
+
147
+ const type = {
148
+ type: 'time' as const,
149
+ timeRange: {
150
+ startDate: new Date('2024-07-05T00:00:00'),
151
+ endDate: new Date('2024-07-06T00:00:00'),
152
+ interval: 24 * 60 * 60 * 1000, // 1 day
153
+ },
154
+ };
155
+
156
+ const barDataKeys = ['Success'];
157
+
158
+ const result = transformTimeData(bars, type, barDataKeys);
159
+
160
+ expect(result).toEqual([
161
+ { category: 'Fri05Jul', Success: 25 }, // Last value for July 5th
162
+ { category: 'Sat06Jul', Success: 15 }, // July 6th value
163
+ ]);
164
+ });
165
+
166
+ it('should preserve chronological order regardless of input order', () => {
167
+ const bars = [
168
+ {
169
+ label: 'Success',
170
+ data: [
171
+ [new Date('2024-07-07T10:00:00'), 30], // July 7th (latest)
172
+ [new Date('2024-07-05T08:00:00'), 10], // July 5th (earliest)
173
+ [new Date('2024-07-06T14:00:00'), 20], // July 6th (middle)
174
+ ] as [Date, number][],
175
+ },
176
+ ];
177
+
178
+ const type = {
179
+ type: 'time' as const,
180
+ timeRange: {
181
+ startDate: new Date('2024-07-05T00:00:00'),
182
+ endDate: new Date('2024-07-07T00:00:00'),
183
+ interval: 24 * 60 * 60 * 1000, // 1 day
184
+ },
185
+ };
186
+
187
+ const barDataKeys = ['Success'];
188
+
189
+ const result = transformTimeData(bars, type, barDataKeys);
190
+
191
+ // Should be in chronological order regardless of input order
192
+ expect(result).toEqual([
193
+ { category: 'Fri05Jul', Success: 10 },
194
+ { category: 'Sat06Jul', Success: 20 },
195
+ { category: 'Sun07Jul', Success: 30 },
196
+ ]);
197
+ });
198
+
199
+ it('should display multiple metrics with different time coverage', () => {
200
+ const bars = [
201
+ {
202
+ label: 'Success',
203
+ data: [[new Date('2024-07-05T00:00:00'), 10]] as [Date, number][],
204
+ },
205
+ {
206
+ label: 'Failed',
207
+ data: [[new Date('2024-07-06T00:00:00'), 5]] as [Date, number][],
208
+ },
209
+ ];
210
+
211
+ const type = {
212
+ type: 'time' as const,
213
+ timeRange: {
214
+ startDate: new Date('2024-07-05T00:00:00'),
215
+ endDate: new Date('2024-07-06T00:00:00'),
216
+ interval: 24 * 60 * 60 * 1000, // 1 day
217
+ },
218
+ };
219
+
220
+ const barDataKeys = ['Success', 'Failed'];
221
+
222
+ const result = transformTimeData(bars, type, barDataKeys);
223
+
224
+ expect(result).toEqual([
225
+ { category: 'Fri05Jul', Success: 10, Failed: 0 },
226
+ { category: 'Sat06Jul', Success: 0, Failed: 5 },
227
+ ]);
228
+ });
229
+ });
230
+
231
+ describe('transformCategoryData', () => {
232
+ it('should transform basic category data correctly', () => {
233
+ const bars = [
234
+ {
235
+ label: 'Success',
236
+ data: [
237
+ ['category1', 10],
238
+ ['category2', 20],
239
+ ] as [string, number][],
240
+ },
241
+ {
242
+ label: 'Failed',
243
+ data: [
244
+ ['category1', 5],
245
+ ['category2', 15],
246
+ ] as [string, number][],
247
+ },
248
+ ];
249
+
250
+ const barDataKeys = ['Success', 'Failed'];
251
+
252
+ const result = transformCategoryData(bars, barDataKeys);
253
+
254
+ expect(result).toEqual([
255
+ { category: 'category1', Success: 10, Failed: 5 },
256
+ { category: 'category2', Success: 20, Failed: 15 },
257
+ ]);
258
+ });
259
+
260
+ it('should show zero for categories missing in specific metrics', () => {
261
+ const bars = [
262
+ {
263
+ label: 'Success',
264
+ data: [
265
+ ['category1', 10],
266
+ ['category3', 30],
267
+ ] as [string, number][],
268
+ },
269
+ {
270
+ label: 'Failed',
271
+ data: [
272
+ ['category2', 20],
273
+ ['category3', 40],
274
+ ] as [string, number][],
275
+ },
276
+ ];
277
+
278
+ const barDataKeys = ['Success', 'Failed'];
279
+
280
+ const result = transformCategoryData(bars, barDataKeys);
281
+
282
+ expect(result).toEqual([
283
+ { category: 'category1', Success: 10, Failed: 0 }, // Failed missing, filled with 0
284
+ { category: 'category3', Success: 30, Failed: 40 }, // Appears in both bars
285
+ { category: 'category2', Success: 0, Failed: 20 }, // Success missing, filled with 0
286
+ ]);
287
+ });
288
+
289
+ it('should handle single bar data', () => {
290
+ const bars = [
291
+ {
292
+ label: 'Metric',
293
+ data: [
294
+ ['A', 100],
295
+ ['B', 200],
296
+ ['C', 300],
297
+ ] as [string, number][],
298
+ },
299
+ ];
300
+
301
+ const barDataKeys = ['Metric'];
302
+
303
+ const result = transformCategoryData(bars, barDataKeys);
304
+
305
+ expect(result).toEqual([
306
+ { category: 'A', Metric: 100 },
307
+ { category: 'B', Metric: 200 },
308
+ { category: 'C', Metric: 300 },
309
+ ]);
310
+ });
311
+
312
+ it('should convert category keys to strings', () => {
313
+ const bars = [
314
+ {
315
+ label: 'Count',
316
+ data: [
317
+ [123, 10], // Number key
318
+ ['text', 20], // String key
319
+ [null, 30], // Null key
320
+ ] as [any, number][],
321
+ },
322
+ ];
323
+
324
+ const barDataKeys = ['Count'];
325
+
326
+ const result = transformCategoryData(bars, barDataKeys);
327
+
328
+ expect(result).toEqual([
329
+ { category: '123', Count: 10 },
330
+ { category: 'text', Count: 20 },
331
+ { category: 'null', Count: 30 },
332
+ ]);
333
+ });
334
+
335
+ it('should handle empty data gracefully', () => {
336
+ const bars = [
337
+ {
338
+ label: 'Empty',
339
+ data: [] as [string, number][],
340
+ },
341
+ ];
342
+
343
+ const barDataKeys = ['Empty'];
344
+
345
+ const result = transformCategoryData(bars, barDataKeys);
346
+
347
+ expect(result).toEqual([]);
348
+ });
349
+ });
350
+
351
+ describe('applySortingToData', () => {
352
+ it('should apply custom sorting function to data', () => {
353
+ const data = [
354
+ { category: 'category1', Success: 50 },
355
+ { category: 'category2', Success: 20 },
356
+ { category: 'category3', Success: 30 },
357
+ { category: 'category4', Success: 40 },
358
+ ];
359
+
360
+ const barDataKeys = ['Success'];
361
+
362
+ // Sort by Success value ascending
363
+ const defaultSort = (pointA: any, pointB: any) => {
364
+ return pointA.Success - pointB.Success > 0 ? 1 : -1;
365
+ };
366
+
367
+ const result = applySortingToData(data, barDataKeys, defaultSort);
368
+
369
+ expect(result).toEqual([
370
+ { category: 'category2', Success: 20 },
371
+ { category: 'category3', Success: 30 },
372
+ { category: 'category4', Success: 40 },
373
+ { category: 'category1', Success: 50 },
374
+ ]);
375
+ });
376
+
377
+ it('should sort categories based on combined metric values', () => {
378
+ const data = [
379
+ { category: 'A', Bar1: 10, Bar2: 50 },
380
+ { category: 'B', Bar1: 30, Bar2: 20 },
381
+ { category: 'C', Bar1: 20, Bar2: 40 },
382
+ ];
383
+
384
+ const barDataKeys = ['Bar1', 'Bar2'];
385
+
386
+ // Sort by sum of Bar1 and Bar2
387
+ const defaultSort = (pointA: any, pointB: any) => {
388
+ const sumA = pointA.Bar1 + pointA.Bar2;
389
+ const sumB = pointB.Bar1 + pointB.Bar2;
390
+ return sumA < sumB ? -1 : sumA > sumB ? 1 : 0;
391
+ };
392
+
393
+ const result = applySortingToData(data, barDataKeys, defaultSort);
394
+
395
+ expect(result).toEqual([
396
+ { category: 'B', Bar1: 30, Bar2: 20 }, // Sum: 50
397
+ { category: 'A', Bar1: 10, Bar2: 50 }, // Sum: 60
398
+ { category: 'C', Bar1: 20, Bar2: 40 }, // Sum: 60
399
+ ]);
400
+ });
401
+
402
+ it('should sort text numbers as numeric values', () => {
403
+ const data = [
404
+ { category: 'A', Value: '100' },
405
+ { category: 'B', Value: '50' },
406
+ { category: 'C', Value: '75' },
407
+ ];
408
+
409
+ const barDataKeys = ['Value'];
410
+
411
+ // Sort by numeric value descending
412
+ const defaultSort = (pointA: any, pointB: any) => {
413
+ return pointB.Value > pointA.Value
414
+ ? 1
415
+ : pointB.Value < pointA.Value
416
+ ? -1
417
+ : 0;
418
+ };
419
+
420
+ const result = applySortingToData(data, barDataKeys, defaultSort);
421
+
422
+ expect(result).toEqual([
423
+ { category: 'A', Value: 100 }, // Converted to number and sorted
424
+ { category: 'C', Value: 75 },
425
+ { category: 'B', Value: 50 },
426
+ ]);
427
+ });
428
+
429
+ it('should treat invalid data as zero when sorting', () => {
430
+ const data = [
431
+ { category: 'A', Value: 30 },
432
+ { category: 'B', Value: 0 }, // Test with 0 value
433
+ { category: 'C', Value: 'NaN' }, // String that will become NaN
434
+ { category: 'D', Value: 10 },
435
+ ];
436
+
437
+ const barDataKeys = ['Value'];
438
+
439
+ // Sort by value ascending
440
+ const defaultSort = (pointA: any, pointB: any) => {
441
+ return pointA.Value < pointB.Value
442
+ ? -1
443
+ : pointA.Value > pointB.Value
444
+ ? 1
445
+ : 0;
446
+ };
447
+
448
+ const result = applySortingToData(data, barDataKeys, defaultSort);
449
+
450
+ expect(result).toEqual([
451
+ { category: 'B', Value: 0 }, // 0 value
452
+ { category: 'C', Value: 0 }, // 'NaN' converted to 0 (NaN becomes 0)
453
+ { category: 'D', Value: 10 },
454
+ { category: 'A', Value: 30 },
455
+ ]);
456
+ });
457
+
458
+ it('should preserve category data during sorting', () => {
459
+ const data = [
460
+ { category: 'Category Z', Metric: 1 },
461
+ { category: 'Category A', Metric: 3 },
462
+ { category: 'Category M', Metric: 2 },
463
+ ];
464
+
465
+ const barDataKeys = ['Metric'];
466
+
467
+ // Sort by category name alphabetically
468
+ const defaultSort = (pointA: any, pointB: any) => {
469
+ return pointA.category.localeCompare(pointB.category);
470
+ };
471
+
472
+ const result = applySortingToData(data, barDataKeys, defaultSort);
473
+
474
+ expect(result).toEqual([
475
+ { category: 'Category A', Metric: 3 },
476
+ { category: 'Category M', Metric: 2 },
477
+ { category: 'Category Z', Metric: 1 },
478
+ ]);
479
+ });
480
+ });
481
+
482
+ describe('getRoundReferenceValue', () => {
483
+ it('should return appropriate rounded values', () => {
484
+ expect(getRoundReferenceValue(1)).toBe(1);
485
+ expect(getRoundReferenceValue(2)).toBe(2.5);
486
+ expect(getRoundReferenceValue(3)).toBe(5);
487
+ expect(getRoundReferenceValue(7)).toBe(10);
488
+ expect(getRoundReferenceValue(15)).toBe(25);
489
+ expect(getRoundReferenceValue(35)).toBe(50);
490
+ expect(getRoundReferenceValue(75)).toBe(100);
491
+ expect(getRoundReferenceValue(150)).toBe(250);
492
+ expect(getRoundReferenceValue(350)).toBe(500);
493
+ expect(getRoundReferenceValue(750)).toBe(1000);
494
+ expect(getRoundReferenceValue(1500)).toBe(2500);
495
+ expect(getRoundReferenceValue(3500)).toBe(5000);
496
+ expect(getRoundReferenceValue(7500)).toBe(10000);
497
+ expect(getRoundReferenceValue(15000)).toBe(25000);
498
+ });
499
+ });
500
+
501
+ describe('getMaxBarValue', () => {
502
+ it('should return the maximum value from chart data', () => {
503
+ const data = [
504
+ { category: 'A', value1: 10, value2: 5 },
505
+ { category: 'B', value1: 20, value2: 15 },
506
+ { category: 'C', value1: 8, value2: 25 },
507
+ ];
508
+ expect(getMaxBarValue(data)).toBe(25);
509
+ });
510
+
511
+ it('should handle single value data', () => {
512
+ const data = [{ category: 'A', value: 42 }];
513
+ expect(getMaxBarValue(data)).toBe(42);
514
+ });
515
+
516
+ it('should return the maximum value from stacked data', () => {
517
+ const data = [
518
+ { category: 'A', value1: 10, value2: 5 },
519
+ { category: 'B', value1: 20, value2: 15 },
520
+ { category: 'C', value1: 8, value2: 25 },
521
+ ];
522
+ expect(getMaxBarValue(data, true)).toBe(35);
523
+ });
524
+ });
525
+
526
+ describe('formatPrometheusDataToRechartsDataAndBars', () => {
527
+ it('should format category data correctly', () => {
528
+ const bars = [
529
+ {
530
+ label: 'Success',
531
+ data: [
532
+ ['A', 10],
533
+ ['B', 20],
534
+ ] as [string, number][],
535
+ color: 'green',
536
+ },
537
+ {
538
+ label: 'Failed',
539
+ data: [
540
+ ['A', 5],
541
+ ['B', 15],
542
+ ] as [string, number][],
543
+ color: 'red',
544
+ },
545
+ ];
546
+
547
+ const result = formatPrometheusDataToRechartsDataAndBars(
548
+ bars,
549
+ 'category',
550
+ {
551
+ Success: coreUIAvailableThemes.darkRebrand.statusHealthy,
552
+ Failed: coreUIAvailableThemes.darkRebrand.statusCritical,
553
+ },
554
+ true, // stacked
555
+ );
556
+
557
+ // Should integrate: data transformation + color formatting + stacked sorting
558
+ expect(result.data).toEqual([
559
+ { category: 'A', Success: 10, Failed: 5 },
560
+ { category: 'B', Success: 20, Failed: 15 },
561
+ ]);
562
+ expect(result.rechartsBars).toEqual([
563
+ { dataKey: 'Success', fill: '#0AADA6', stackId: 'stacked' }, // statusHealthy = '#0AADA6'
564
+ { dataKey: 'Failed', fill: '#E84855', stackId: 'stacked' }, // statusCritical = '#E84855'
565
+ ]);
566
+ });
567
+
568
+ it('should format time data correctly', () => {
569
+ const bars = [
570
+ {
571
+ label: 'Success Count',
572
+ data: [[new Date('2024-07-05T00:00:00'), 10]] as [Date, number][],
573
+ },
574
+ {
575
+ label: 'Failed Count',
576
+ data: [[new Date('2024-07-05T00:00:00'), 5]] as [Date, number][],
577
+ },
578
+ ];
579
+
580
+ const result = formatPrometheusDataToRechartsDataAndBars(
581
+ bars,
582
+ {
583
+ type: 'time',
584
+ timeRange: {
585
+ startDate: new Date('2024-07-05T00:00:00'),
586
+ endDate: new Date('2024-07-05T00:00:00'),
587
+ interval: 24 * 60 * 60 * 1000,
588
+ },
589
+ },
590
+ {
591
+ 'Success Count': 'lineColor3',
592
+ 'Failed Count': 'blue',
593
+ },
594
+ false,
595
+ undefined,
596
+ );
597
+
598
+ // Should integrate: time transformation + status color assignment
599
+ expect(result.data).toEqual([
600
+ { category: 'Fri05Jul', 'Success Count': 10, 'Failed Count': 5 },
601
+ ]);
602
+ expect(result.rechartsBars).toEqual([
603
+ { dataKey: 'Success Count', fill: '#4BE4E2' }, // lineColor3
604
+ { dataKey: 'Failed Count', fill: 'blue' }, // blue
605
+ ]);
606
+ });
607
+
608
+ it('should integrate custom sorting with category data', () => {
609
+ const bars = [
610
+ {
611
+ label: 'Value',
612
+ data: [
613
+ ['C', 30],
614
+ ['A', 10],
615
+ ['B', 20],
616
+ ] as [string, number][],
617
+ },
618
+ ];
619
+
620
+ const result = formatPrometheusDataToRechartsDataAndBars(
621
+ bars,
622
+ 'category',
623
+ mockTheme,
624
+ false,
625
+ (pointA, pointB) => {
626
+ // Sort alphabetically by category
627
+ return String(pointA.category).localeCompare(
628
+ String(pointB.category),
629
+ ) as -1 | 0 | 1;
630
+ },
631
+ );
632
+
633
+ // Should integrate: category transformation + custom sorting
634
+ expect(result.data.map((item) => item.category)).toEqual(['A', 'B', 'C']);
635
+ });
636
+ });
637
+
638
+ describe('computeUnitLabelAndRoundReferenceValue', () => {
639
+ it('should compute the unit label and round reference value correctly when reaching threshold', () => {
640
+ const data = [
641
+ {
642
+ category: 'category1',
643
+ success: 1680,
644
+ },
645
+ ];
646
+ const maxValue = 1680;
647
+ const unitRange: UnitRange = [
648
+ {
649
+ threshold: 1000,
650
+ label: 'kB',
651
+ },
652
+ ];
653
+ const result = computeUnitLabelAndRoundReferenceValue(
654
+ data,
655
+ maxValue,
656
+ unitRange,
657
+ );
658
+
659
+ expect(result.unitLabel).toBe('kB');
660
+ expect(result.roundReferenceValue).toBe(10);
661
+ expect(result.rechartsData).toEqual([
662
+ {
663
+ category: 'category1',
664
+ success: 1.68,
665
+ },
666
+ ]);
667
+ });
668
+ it('should compute the unit label and round reference value correctly when threshold is 0', () => {
669
+ const data = [
670
+ {
671
+ category: 'category1',
672
+ success: 680,
673
+ },
674
+ ];
675
+ const maxValue = 680;
676
+ const unitRange: UnitRange = [
677
+ {
678
+ threshold: 0,
679
+ label: 'B',
680
+ },
681
+ {
682
+ threshold: 1000,
683
+ label: 'kB',
684
+ },
685
+ ];
686
+ const result = computeUnitLabelAndRoundReferenceValue(
687
+ data,
688
+ maxValue,
689
+ unitRange,
690
+ );
691
+
692
+ expect(result.unitLabel).toBe('B');
693
+ expect(result.roundReferenceValue).toBe(1000);
694
+ expect(result.rechartsData).toEqual([
695
+ { category: 'category1', success: 680 },
696
+ ]);
697
+ });
698
+ });
699
+
700
+ describe('sortStackedBars', () => {
701
+ const bars = [
702
+ { dataKey: 'bar1', fill: 'blue' },
703
+ { dataKey: 'bar2', fill: 'red' },
704
+ { dataKey: 'bar3', fill: 'green' },
705
+ ];
706
+ const data = [
707
+ { bar1: 10, bar2: 20, bar3: 30 },
708
+ { bar1: 40, bar2: 50, bar3: 60 },
709
+ { bar1: 70, bar2: 80, bar3: 90 },
710
+ ];
711
+ it('should sort bars by average values in descending order when stacked is true', () => {
712
+ const result = sortStackedBars(bars, data, true);
713
+ expect(result).toEqual([
714
+ { dataKey: 'bar3', fill: 'green' },
715
+ { dataKey: 'bar2', fill: 'red' },
716
+ { dataKey: 'bar1', fill: 'blue' },
717
+ ]);
718
+ });
719
+ it('should not sort bars when stacked is false', () => {
720
+ const result = sortStackedBars(bars, data, false);
721
+ expect(result).toEqual([
722
+ { dataKey: 'bar1', fill: 'blue' },
723
+ { dataKey: 'bar2', fill: 'red' },
724
+ { dataKey: 'bar3', fill: 'green' },
725
+ ]);
726
+ });
727
+ it('should not sort bars when stacked is undefined', () => {
728
+ const result = sortStackedBars(bars, data, undefined);
729
+ expect(result).toEqual([
730
+ { dataKey: 'bar1', fill: 'blue' },
731
+ { dataKey: 'bar2', fill: 'red' },
732
+ { dataKey: 'bar3', fill: 'green' },
733
+ ]);
734
+ });
735
+ });
736
+
737
+ describe('renderTooltipContent', () => {
738
+ it('should return null when active is false', () => {
739
+ const props = {
740
+ active: false,
741
+ payload: [],
742
+ label: 'test',
743
+ coordinate: { x: 0, y: 0 },
744
+ accessibilityLayer: false,
745
+ };
746
+ const result = renderTooltipContent(props, undefined, undefined);
747
+ expect(result).toBeNull();
748
+ });
749
+
750
+ it('should return null when tooltip is undefined', () => {
751
+ const props = {
752
+ active: true,
753
+ payload: [{ name: 'test', value: 10 }],
754
+ label: 'test',
755
+ coordinate: { x: 0, y: 0 },
756
+ accessibilityLayer: false,
757
+ };
758
+ const result = renderTooltipContent(props, undefined, 'test');
759
+ expect(result).toBeNull();
760
+ });
761
+ it('should call tooltip with the correct props', () => {
762
+ const tooltip = jest.fn();
763
+ const props = {
764
+ active: true,
765
+ payload: [
766
+ { name: 'Success', value: 10 },
767
+ { name: 'Failed', value: 20 },
768
+ ],
769
+ label: 'Test',
770
+ coordinate: { x: 0, y: 0 },
771
+ accessibilityLayer: false,
772
+ };
773
+ renderTooltipContent(props, tooltip, 'Success');
774
+ expect(tooltip).toHaveBeenCalledWith({
775
+ category: 'Test',
776
+ values: [
777
+ { label: 'Success', value: 10, isHovered: true },
778
+ { label: 'Failed', value: 20, isHovered: false },
779
+ ],
780
+ });
781
+ });
782
+ });