@scality/core-ui 0.174.0 → 0.176.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 (46) hide show
  1. package/dist/components/barchartv2/Barchart.component.d.ts +4 -4
  2. package/dist/components/barchartv2/Barchart.component.d.ts.map +1 -1
  3. package/dist/components/barchartv2/Barchart.component.js +22 -32
  4. package/dist/components/barchartv2/BarchartTooltip.d.ts +1 -1
  5. package/dist/components/barchartv2/BarchartTooltip.d.ts.map +1 -1
  6. package/dist/components/barchartv2/BarchartTooltip.js +6 -6
  7. package/dist/components/barchartv2/utils.d.ts.map +1 -1
  8. package/dist/components/chartlegend/ChartLegendWrapper.js +1 -1
  9. package/dist/components/charttooltip/ChartTooltip.d.ts +6 -0
  10. package/dist/components/charttooltip/ChartTooltip.d.ts.map +1 -1
  11. package/dist/components/charttooltip/ChartTooltip.js +22 -0
  12. package/dist/components/date/FormattedDateTime.d.ts +23 -8
  13. package/dist/components/date/FormattedDateTime.d.ts.map +1 -1
  14. package/dist/components/date/FormattedDateTime.js +51 -7
  15. package/dist/components/icon/Icon.component.d.ts +1 -0
  16. package/dist/components/icon/Icon.component.d.ts.map +1 -1
  17. package/dist/components/icon/Icon.component.js +1 -0
  18. package/dist/components/linetimeseriechart/linetimeseriechart.component.d.ts +2 -1
  19. package/dist/components/linetimeseriechart/linetimeseriechart.component.d.ts.map +1 -1
  20. package/dist/components/linetimeseriechart/linetimeseriechart.component.js +19 -17
  21. package/dist/components/linetimeseriechart/utils.d.ts +1 -1
  22. package/dist/components/linetimeseriechart/utils.d.ts.map +1 -1
  23. package/dist/components/linetimeseriechart/utils.js +13 -13
  24. package/dist/components/scrollbarwrapper/ScrollbarWrapper.component.d.ts.map +1 -1
  25. package/dist/components/scrollbarwrapper/ScrollbarWrapper.component.js +2 -0
  26. package/dist/components/sparkline/sparkline.component.d.ts +2 -1
  27. package/dist/components/sparkline/sparkline.component.d.ts.map +1 -1
  28. package/dist/components/sparkline/sparkline.component.js +3 -3
  29. package/package.json +1 -1
  30. package/src/lib/components/barchartv2/Barchart.component.test.tsx +23 -25
  31. package/src/lib/components/barchartv2/Barchart.component.tsx +36 -45
  32. package/src/lib/components/barchartv2/BarchartTooltip.test.tsx +3 -3
  33. package/src/lib/components/barchartv2/BarchartTooltip.tsx +14 -18
  34. package/src/lib/components/barchartv2/utils.ts +1 -5
  35. package/src/lib/components/chartlegend/ChartLegendWrapper.tsx +1 -1
  36. package/src/lib/components/charttooltip/ChartTooltip.tsx +40 -0
  37. package/src/lib/components/date/FormattedDateTime.tsx +73 -8
  38. package/src/lib/components/icon/Icon.component.tsx +1 -0
  39. package/src/lib/components/linetimeseriechart/linetimeseriechart.component.tsx +43 -34
  40. package/src/lib/components/linetimeseriechart/utils.test.ts +30 -68
  41. package/src/lib/components/linetimeseriechart/utils.ts +14 -18
  42. package/src/lib/components/scrollbarwrapper/ScrollbarWrapper.component.tsx +3 -1
  43. package/src/lib/components/sparkline/sparkline.component.tsx +5 -3
  44. package/stories/formattedate.stories.tsx +2 -0
  45. package/stories/linetimeseriechart.stories.tsx +1 -0
  46. package/stories/sparkline.stories.tsx +19 -0
@@ -3,7 +3,7 @@ import { Tooltip } from '../tooltip/Tooltip.component';
3
3
 
4
4
  /**
5
5
  * @description Long date formatter, with weekday, year, month and day. Used for describing long term date.
6
- * @example Wednesday 6 October 2025
6
+ * @example Wednesday 06 October 2025
7
7
  */
8
8
  export const LONG_DATE_FORMATER = Intl.DateTimeFormat('en-GB', {
9
9
  weekday: 'long',
@@ -35,7 +35,7 @@ export const DATE_FORMATER = Intl.DateTimeFormat('fr-CA', {
35
35
 
36
36
  /**
37
37
  * @description Day month formatter, with weekday, day and month. Used for describing long term date.
38
- * @example Wed 6 Oct
38
+ * @example Wed 06 Oct
39
39
  */
40
40
  export const DAY_MONTH_FORMATER = Intl.DateTimeFormat('en-GB', {
41
41
  weekday: 'short',
@@ -64,9 +64,30 @@ export const TIME_FORMATER = Intl.DateTimeFormat('en-GB', {
64
64
  minute: '2-digit',
65
65
  });
66
66
 
67
+ /**
68
+ * @description Day month abbreviated formatter. Used for describing long term date.
69
+ * @example 06 Oct
70
+ */
71
+ export const DAY_MONTH_ABBREVIATED = Intl.DateTimeFormat('en-GB', {
72
+ day: '2-digit',
73
+ month: 'short',
74
+ hour12: false,
75
+ });
76
+
77
+ /**
78
+ * @description Day month abbreviated formatter. Used for describing long term date.
79
+ * @example 06 Oct 25
80
+ */
81
+ export const DAY_MONTH_ABBREVIATED_YEAR = Intl.DateTimeFormat('en-GB', {
82
+ day: '2-digit',
83
+ month: 'short',
84
+ year: '2-digit',
85
+ hour12: false,
86
+ });
87
+
67
88
  /**
68
89
  * @description Day month abbreviated hour minute second formatter. Used for describing long term date.
69
- * @example 6 Oct 18:33:00
90
+ * @example 06 Oct 18:33:00
70
91
  */
71
92
  export const DAY_MONTH_ABBREVIATED_HOUR_MINUTE_SECOND = Intl.DateTimeFormat(
72
93
  'en-GB',
@@ -82,7 +103,7 @@ export const DAY_MONTH_ABBREVIATED_HOUR_MINUTE_SECOND = Intl.DateTimeFormat(
82
103
 
83
104
  /**
84
105
  * @description Day month abbreviated hour minute formatter. Used for describing long term date.
85
- * @example 6 Oct 18:33
106
+ * @example 06 Oct 18:33
86
107
  */
87
108
  export const DAY_MONTH_ABBREVIATED_HOUR_MINUTE = Intl.DateTimeFormat('en-GB', {
88
109
  day: '2-digit',
@@ -92,6 +113,22 @@ export const DAY_MONTH_ABBREVIATED_HOUR_MINUTE = Intl.DateTimeFormat('en-GB', {
92
113
  hour12: false,
93
114
  });
94
115
 
116
+ /**
117
+ * @description Day month abbreviated year hour minute formatter. Used for describing long term date.
118
+ * @example 06 Oct 2025 18:33
119
+ */
120
+ export const DAY_MONTH_ABBREVIATED_YEAR_HOUR_MINUTE = Intl.DateTimeFormat(
121
+ 'en-GB',
122
+ {
123
+ day: '2-digit',
124
+ month: 'short',
125
+ year: 'numeric',
126
+ hour: '2-digit',
127
+ minute: '2-digit',
128
+ hour12: false,
129
+ },
130
+ );
131
+
95
132
  /**
96
133
  * @description Year month day formatter, without time. Used for describing long term date.
97
134
  * @example 2025-01-01
@@ -121,11 +158,14 @@ type FormattedDateTimeProps = {
121
158
  | 'relative'
122
159
  | 'day-month-abbreviated-hour-minute'
123
160
  | 'day-month-abbreviated-hour-minute-second'
161
+ | 'day-month-abbreviated-year-hour-minute'
124
162
  | 'long-date'
125
163
  | 'long-date-without-weekday'
126
164
  | 'chart-date'
127
165
  | 'year-month-day'
128
- | 'month-day';
166
+ | 'month-day'
167
+ | 'day-month-abbreviated'
168
+ | 'chart-long-term-date';
129
169
 
130
170
  value: Date;
131
171
  };
@@ -150,10 +190,10 @@ const isItFutureOrIsItPast = (
150
190
  * time: '00:00'
151
191
  * 'time-second': '00:00:00'
152
192
  * 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'
193
+ * 'day-month-abbreviated-hour-minute': '06 Oct 18:33'
194
+ * 'day-month-abbreviated-hour-minute-second': '06 Oct 18:33:00'
155
195
  * 'long-date': 'Wednesday 6 October 2025'
156
- * 'chart-date': '6 Oct'
196
+ * 'chart-date': '06 Oct'
157
197
  * 'year-month-day': '2025-10-06'
158
198
  */
159
199
  export const FormattedDateTime = ({
@@ -290,6 +330,31 @@ export const FormattedDateTime = ({
290
330
  return <>{YEAR_MONTH_DAY_FORMATTER.format(value)}</>;
291
331
  case 'month-day':
292
332
  return <>{MONTH_DAY_FORMATTER.format(value)}</>;
333
+ case 'day-month-abbreviated-year-hour-minute':
334
+ return (
335
+ <>
336
+ {DAY_MONTH_ABBREVIATED_YEAR_HOUR_MINUTE.format(value)
337
+ .replace(',', '')
338
+ .replace(/Sept/g, 'Sep')}
339
+ </>
340
+ );
341
+ case 'day-month-abbreviated':
342
+ return (
343
+ <>
344
+ {DAY_MONTH_ABBREVIATED.format(value)
345
+ .replace(',', '')
346
+ .replace(/Sept/g, 'Sep')}
347
+ </>
348
+ );
349
+ case 'chart-long-term-date':
350
+ return (
351
+ <>
352
+ {DAY_MONTH_ABBREVIATED_YEAR.format(value)
353
+ .replace(/[ ,]/g, '')
354
+ // replace Sept with Sep to keep 3 letter month
355
+ .replace(/Sept/g, 'Sep')}
356
+ </>
357
+ );
293
358
  default:
294
359
  return <></>;
295
360
  }
@@ -142,6 +142,7 @@ export const iconTable = {
142
142
  Mail: 'fas faEnvelope',
143
143
  ThumbsUp: 'far faThumbsUp',
144
144
  ThumbsDown: 'far faThumbsDown',
145
+ Sidebar: 'fas faColumns',
145
146
  };
146
147
 
147
148
  type IconProps = {
@@ -3,26 +3,24 @@ import {
3
3
  Line,
4
4
  LineChart,
5
5
  ReferenceLine,
6
- ResponsiveContainer,
7
6
  Tooltip,
8
7
  TooltipContentProps,
9
8
  XAxis,
10
9
  YAxis,
11
10
  } from 'recharts';
12
- import { useCallback, useMemo, useRef, useState } from 'react';
11
+ import React, { useCallback, useMemo, useRef, useState } from 'react';
13
12
  import styled, { useTheme } from 'styled-components';
14
13
  import { spacing } from '../../spacing';
15
14
  import { fontSize } from '../../style/theme';
16
15
  import { Box } from '../box/Box';
17
16
  import { useChartLegend } from '../chartlegend/ChartLegendWrapper';
18
- import { FormattedDateTime } from '../date/FormattedDateTime';
19
17
  import { Icon } from '../icon/Icon.component';
20
18
  import {
21
19
  addMissingDataPoint,
22
20
  getUnitLabel,
23
21
  } from '../linetemporalchart/ChartUtil';
24
22
  import { Loader } from '../loader/Loader.component';
25
- import { ChartTitleText, SmallerText } from '../text/Text.component';
23
+ import { ChartTitleText, Text } from '../text/Text.component';
26
24
  import { Tooltip as TooltipComponent } from '../tooltip/Tooltip.component';
27
25
  import { formatXAxisLabel } from './utils';
28
26
  import {
@@ -30,14 +28,16 @@ import {
30
28
  ChartTooltipItem,
31
29
  ChartTooltipHeader,
32
30
  ChartTooltipItemsContainer,
31
+ ChartTooltipSeparator,
32
+ TooltipHeader,
33
33
  } from '../charttooltip/ChartTooltip';
34
34
  import { LegendShape } from '../chartlegend/ChartLegend';
35
+ import { StyledResponsiveContainer } from '../barchartv2/Barchart.component';
35
36
 
36
37
  const LineTemporalChartWrapper = styled.div`
37
38
  display: flex;
38
39
  flex-direction: column;
39
40
  justify-content: flex-start;
40
- flex: 1;
41
41
  `;
42
42
 
43
43
  const ChartHeader = styled.div`
@@ -100,28 +100,30 @@ export type LineChartProps = (
100
100
  renderTooltip?: (
101
101
  tooltipProps: TooltipContentProps<number, string>,
102
102
  unitLabel?: string,
103
- timeFormat?: 'date-time' | 'date',
103
+ duration?: number,
104
104
  ) => React.ReactNode;
105
105
  };
106
106
 
107
107
  const LineTimeSerieChartTooltip = ({
108
108
  unitLabel,
109
- timeFormat,
109
+ duration,
110
110
  isChartActive,
111
111
  tooltipProps,
112
112
  renderTooltip,
113
113
  hoveredValue,
114
+ isSymmetrical,
114
115
  }: {
115
116
  tooltipProps: TooltipContentProps<number, string>;
116
117
  unitLabel?: string;
117
- timeFormat?: 'date-time' | 'date';
118
+ duration: number;
118
119
  isChartActive?: boolean;
119
120
  renderTooltip?: (
120
121
  tooltipProps: TooltipContentProps<number, string>,
121
122
  unitLabel?: string,
122
- timeFormat?: 'date-time' | 'date',
123
+ duration?: number,
123
124
  ) => React.ReactNode;
124
125
  hoveredValue?: string;
126
+ isSymmetrical?: boolean;
125
127
  }) => {
126
128
  const { active, payload, label } = tooltipProps;
127
129
 
@@ -129,7 +131,7 @@ const LineTimeSerieChartTooltip = ({
129
131
  return null;
130
132
 
131
133
  if (renderTooltip) {
132
- return renderTooltip(tooltipProps, unitLabel, timeFormat);
134
+ return renderTooltip(tooltipProps, unitLabel, duration);
133
135
  }
134
136
  // We can't use the default itemSorter method because it's a custom tooltip.
135
137
  // Sort the payload here instead
@@ -146,17 +148,15 @@ const LineTimeSerieChartTooltip = ({
146
148
  return bValue - aValue; // Positives before negatives
147
149
  });
148
150
 
151
+ // Find the transition point between positive and negative values
152
+ const separatorIndex = sortedPayload.findIndex((entry) => entry.value < 0);
153
+ const hasBothPositiveAndNegative =
154
+ separatorIndex > 0 && separatorIndex < sortedPayload.length;
155
+
149
156
  return (
150
157
  <ChartTooltipContainer>
151
158
  <ChartTooltipHeader>
152
- <FormattedDateTime
153
- format={
154
- timeFormat === 'date-time'
155
- ? 'day-month-abbreviated-hour-minute-second'
156
- : 'long-date-without-weekday'
157
- }
158
- value={new Date(label)}
159
- />
159
+ <TooltipHeader duration={duration} value={label} />
160
160
  </ChartTooltipHeader>
161
161
  <ChartTooltipItemsContainer>
162
162
  {sortedPayload.map((entry, index) => {
@@ -175,13 +175,18 @@ const LineTimeSerieChartTooltip = ({
175
175
  : `${entry.value.toFixed(2)} ${unitLabel}`;
176
176
 
177
177
  return (
178
- <ChartTooltipItem
179
- key={index}
180
- label={entry.name}
181
- value={formattedValue}
182
- legendIcon={legendIcon}
183
- isHovered={isHovered}
184
- />
178
+ <React.Fragment key={index}>
179
+ {/* Add separator between positive and negative values for symmetrical charts */}
180
+ {isSymmetrical &&
181
+ hasBothPositiveAndNegative &&
182
+ index === separatorIndex && <ChartTooltipSeparator />}
183
+ <ChartTooltipItem
184
+ label={entry.name}
185
+ value={formattedValue}
186
+ legendIcon={legendIcon}
187
+ isHovered={isHovered}
188
+ />
189
+ </React.Fragment>
185
190
  );
186
191
  })}
187
192
  </ChartTooltipItemsContainer>
@@ -439,8 +444,8 @@ export function LineTimeSerieChart({
439
444
 
440
445
  // Format time for display the tick in the x axis
441
446
  const formatXAxisLabelCallback = useCallback(
442
- (timestamp: number) => formatXAxisLabel(timestamp, timeFormat, chartData),
443
- [timeFormat, chartData],
447
+ (timestamp: number) => formatXAxisLabel(timestamp, duration),
448
+ [duration],
444
449
  );
445
450
 
446
451
  return (
@@ -453,7 +458,7 @@ export function LineTimeSerieChart({
453
458
  <Box ml={spacing.r4}>
454
459
  <TooltipComponent
455
460
  placement={'right'}
456
- overlay={<SmallerText>{helpText}</SmallerText>}
461
+ overlay={<Text>{helpText}</Text>}
457
462
  >
458
463
  <Icon name="Info" color={theme.buttonSecondary} />
459
464
  </TooltipComponent>
@@ -467,7 +472,7 @@ export function LineTimeSerieChart({
467
472
  onFocusCapture={() => setIsChartActive(true)}
468
473
  onBlurCapture={() => setIsChartActive(false)}
469
474
  >
470
- <ResponsiveContainer width="100%" height={height}>
475
+ <StyledResponsiveContainer width="100%" height={height}>
471
476
  <LineChart
472
477
  data={rechartsData}
473
478
  ref={chartRef}
@@ -476,6 +481,7 @@ export function LineTimeSerieChart({
476
481
  syncId={syncId}
477
482
  onMouseEnter={() => setIsChartActive(true)}
478
483
  onMouseLeave={() => setIsChartActive(false)}
484
+ accessibilityLayer
479
485
  >
480
486
  <CartesianGrid
481
487
  vertical={true}
@@ -501,7 +507,6 @@ export function LineTimeSerieChart({
501
507
  />
502
508
  <YAxis
503
509
  orientation="right"
504
- allowDataOverflow={false}
505
510
  label={{
506
511
  value: yAxisTitle,
507
512
  angle: 90,
@@ -524,16 +529,19 @@ export function LineTimeSerieChart({
524
529
  fill: theme.textSecondary,
525
530
  fontSize: fontSize.smaller,
526
531
  }}
527
- tickFormatter={(value) => Math.round(value).toString()}
532
+ tickFormatter={(value) =>
533
+ new Intl.NumberFormat('fr-FR').format(value.toFixed(0))
534
+ }
528
535
  tickCount={5}
529
- interval={'preserveStartEnd'}
536
+ interval={0}
530
537
  />
531
538
  <Tooltip
532
539
  content={(props: TooltipContentProps<number, string>) => (
533
540
  <LineTimeSerieChartTooltip
534
541
  unitLabel={unitLabel}
535
- timeFormat={timeFormat}
542
+ duration={duration}
536
543
  renderTooltip={renderTooltip}
544
+ isSymmetrical={yAxisType === 'symmetrical'}
537
545
  tooltipProps={props}
538
546
  isChartActive={isChartActive}
539
547
  hoveredValue={hoveredValue}
@@ -560,6 +568,7 @@ export function LineTimeSerieChart({
560
568
  stroke={colorMapping[resource]}
561
569
  dot={false}
562
570
  isAnimationActive={false}
571
+ strokeDasharray={serie.isLineDashed ? '4 4' : undefined}
563
572
  onMouseEnter={() => setHoveredValue(label)}
564
573
  onMouseLeave={() => setHoveredValue(undefined)}
565
574
  />
@@ -567,7 +576,7 @@ export function LineTimeSerieChart({
567
576
  }),
568
577
  )}
569
578
  </LineChart>
570
- </ResponsiveContainer>
579
+ </StyledResponsiveContainer>
571
580
  </div>
572
581
  </LineTemporalChartWrapper>
573
582
  );
@@ -1,87 +1,49 @@
1
1
  import { formatXAxisLabel } from './utils';
2
2
 
3
- const createChartData = (startDate: Date, endDate: Date) => [
4
- { timestamp: startDate.getTime() },
5
- { timestamp: endDate.getTime() },
6
- ];
7
-
8
3
  describe('formatXAxisLabel', () => {
9
4
  const mockTimestamp = new Date('2025-09-15T14:30:00Z').getTime();
10
5
 
11
- describe('date-time format', () => {
12
- it('should format timestamp with day-month-abbreviated-hour-minute format', () => {
13
- const chartData = createChartData(
14
- new Date('2022-01-01'),
15
- new Date('2022-01-02'),
16
- );
17
- const result = formatXAxisLabel(mockTimestamp, 'date-time', chartData);
18
- expect(result).toBe('15 Sept 14:30');
6
+ describe('short duration (≤ 24 hours)', () => {
7
+ it('should format timestamp with time format', () => {
8
+ const duration = 12 * 60 * 60; // 12 hours
9
+ const result = formatXAxisLabel(mockTimestamp, duration);
10
+ expect(result).toBe('14:30');
19
11
  });
20
12
  });
21
13
 
22
- describe('date format', () => {
23
- it('should use YYYY-MM-DD format for time ranges greater than 1 year', () => {
24
- const startDate = new Date('2022-01-01');
25
- const endDate = new Date('2024-01-01'); // More than 1 year
26
- const chartData = createChartData(startDate, endDate);
27
-
28
- const result = formatXAxisLabel(mockTimestamp, 'date', chartData);
29
- expect(result).toBe('2025-09-15');
30
- });
31
-
32
- it('should use MM-DD format for time ranges less than 1 year', () => {
33
- const startDate = new Date('2023-09-01');
34
- const endDate = new Date('2023-12-01'); // Less than 1 year
35
- const chartData = createChartData(startDate, endDate);
36
-
37
- const result = formatXAxisLabel(mockTimestamp, 'date', chartData);
38
- expect(result).toBe('09-15');
14
+ describe('medium duration (≤ 7 days)', () => {
15
+ it('should format timestamp with day-month-abbreviated format', () => {
16
+ const duration = 3 * 24 * 60 * 60; // 3 days
17
+ const result = formatXAxisLabel(mockTimestamp, duration);
18
+ expect(result).toBe('15 Sep');
39
19
  });
20
+ });
40
21
 
41
- it('should use YYYY-MM-DD format when chartData is empty', () => {
42
- const result = formatXAxisLabel(mockTimestamp, 'date', []);
43
- expect(result).toBe('2025-09-15');
44
- });
45
-
46
- it('should handle edge case of exactly 1 year time range', () => {
47
- const startDate = new Date('2022-09-15');
48
- const endDate = new Date('2023-09-15'); // Exactly 1 year
49
- const chartData = createChartData(startDate, endDate);
50
-
51
- const result = formatXAxisLabel(mockTimestamp, 'date', chartData);
52
- expect(result).toBe('09-15');
53
- });
54
-
55
- it('should handle leap year calculation correctly', () => {
56
- const startDate = new Date('2023-01-01');
57
- const endDate = new Date('2024-01-02'); // Just over 1 year including leap year
58
- const chartData = createChartData(startDate, endDate);
59
-
60
- const result = formatXAxisLabel(mockTimestamp, 'date', chartData);
61
-
62
- expect(result).toBe('2025-09-15');
22
+ describe('long duration (> 7 days)', () => {
23
+ it('should format timestamp with day-month-abbreviated-year format', () => {
24
+ const duration = 30 * 24 * 60 * 60; // 30 days
25
+ const result = formatXAxisLabel(mockTimestamp, duration);
26
+ expect(result).toBe('15Sep25');
63
27
  });
64
28
  });
65
29
 
66
- describe('chartData with various scenarios', () => {
67
- it('should handle chartData with single data point', () => {
68
- const chartData = [{ timestamp: mockTimestamp }];
69
-
70
- const result = formatXAxisLabel(mockTimestamp, 'date', chartData);
71
-
72
- expect(result).toBe('09-15');
30
+ describe('edge cases', () => {
31
+ it('should handle exactly 24 hours duration', () => {
32
+ const duration = 24 * 60 * 60; // exactly 24 hours
33
+ const result = formatXAxisLabel(mockTimestamp, duration);
34
+ expect(result).toBe('14:30');
73
35
  });
74
36
 
75
- it('should handle chartData with mixed timestamp values', () => {
76
- const chartData = [
77
- { timestamp: new Date('2023-01-01').getTime() },
78
- { timestamp: new Date('2023-06-01').getTime() },
79
- { timestamp: new Date('2023-12-01').getTime() },
80
- ];
81
-
82
- const result = formatXAxisLabel(mockTimestamp, 'date', chartData);
37
+ it('should handle exactly 7 days duration', () => {
38
+ const duration = 7 * 24 * 60 * 60; // exactly 7 days
39
+ const result = formatXAxisLabel(mockTimestamp, duration);
40
+ expect(result).toBe('15 Sep');
41
+ });
83
42
 
84
- expect(result).toBe('09-15');
43
+ it('should handle just over 7 days duration', () => {
44
+ const duration = 8 * 24 * 60 * 60; // 8 days
45
+ const result = formatXAxisLabel(mockTimestamp, duration);
46
+ expect(result).toBe('15Sep25');
85
47
  });
86
48
  });
87
49
  });
@@ -1,7 +1,7 @@
1
1
  import {
2
- DAY_MONTH_ABBREVIATED_HOUR_MINUTE,
3
- YEAR_MONTH_DAY_FORMATTER,
4
- MONTH_DAY_FORMATTER,
2
+ TIME_FORMATER,
3
+ DAY_MONTH_ABBREVIATED,
4
+ DAY_MONTH_ABBREVIATED_YEAR,
5
5
  } from '../date/FormattedDateTime';
6
6
 
7
7
  export const ONE_YEAR_MILLISECONDS = 366 * 24 * 60 * 60 * 1000;
@@ -22,22 +22,18 @@ export type ChartDataPoint = {
22
22
  */
23
23
  export const formatXAxisLabel = (
24
24
  timestamp: number,
25
- timeFormat: 'date-time' | 'date' = 'date-time',
26
- chartData: ChartDataPoint[] = [],
25
+ duration: number,
27
26
  ): string => {
28
27
  const date = new Date(timestamp);
29
- if (!chartData.length) {
30
- return YEAR_MONTH_DAY_FORMATTER.format(date);
28
+ if (duration <= 24 * 60 * 60) {
29
+ return TIME_FORMATER.format(date);
30
+ } else if (duration <= 7 * 24 * 60 * 60) {
31
+ return DAY_MONTH_ABBREVIATED.format(date)
32
+ .replace(',', '')
33
+ .replace(/Sept/g, 'Sep');
34
+ } else {
35
+ return DAY_MONTH_ABBREVIATED_YEAR.format(date)
36
+ .replace(/[ ,]/g, '')
37
+ .replace(/Sept/g, 'Sep');
31
38
  }
32
- if (timeFormat === 'date-time') {
33
- return DAY_MONTH_ABBREVIATED_HOUR_MINUTE.format(date).replace(',', '');
34
- }
35
- const timestamps = chartData.map((d) => d.timestamp);
36
- const minTimestamp = Math.min(...timestamps);
37
- const maxTimestamp = Math.max(...timestamps);
38
- const timeRangeMilliseconds = maxTimestamp - minTimestamp;
39
-
40
- return timeRangeMilliseconds >= ONE_YEAR_MILLISECONDS
41
- ? YEAR_MONTH_DAY_FORMATTER.format(date)
42
- : MONTH_DAY_FORMATTER.format(date);
43
39
  };
@@ -1,4 +1,4 @@
1
- import styled, { createGlobalStyle, css } from 'styled-components';
1
+ import { createGlobalStyle, css } from 'styled-components';
2
2
 
3
3
  type Props = {
4
4
  children: React.ReactNode;
@@ -24,6 +24,7 @@ ${(props) => {
24
24
  width: 4px;
25
25
  height: 4px;
26
26
  min-height: 20px;
27
+ background: ${brand.border}; // fallback for gradient themes
27
28
  background: ${brand.buttonSecondary};
28
29
  border-radius: 4px;
29
30
  -webkit-border-radius: 4px;
@@ -46,6 +47,7 @@ ${(props) => {
46
47
  }
47
48
 
48
49
  // Firefox
50
+ scrollbar-color: ${brand.border} ${brand.backgroundLevel3}; // fallback for gradient themes
49
51
  scrollbar-color: ${brand.buttonSecondary} ${brand.backgroundLevel3};
50
52
  scrollbar-width: thin;
51
53
  }
@@ -1,5 +1,5 @@
1
1
  import { useMemo } from "react";
2
- import { Area, AreaChart, CartesianGrid, ResponsiveContainer } from "recharts";
2
+ import { Area, AreaChart, CartesianGrid, ResponsiveContainer, YAxis } from "recharts";
3
3
  import { useTheme } from "styled-components";
4
4
  import { chartColors } from "../../style/theme";
5
5
  import { addMissingDataPoint } from "../linetemporalchart/ChartUtil";
@@ -11,14 +11,15 @@ type SparklineProps = {
11
11
  },
12
12
  startingTimeStamp: number,
13
13
  sampleDuration: number,
14
- sampleInterval: number
14
+ sampleInterval: number,
15
+ yAxisType?: 'default' | 'percentage',
15
16
  };
16
17
 
17
18
  /**
18
19
  * Sparkline is a simple dynamically sized area chart.
19
20
  * Used to show trends in data over time.
20
21
  */
21
- export function Sparkline({ serie, startingTimeStamp, sampleDuration, sampleInterval }: SparklineProps) {
22
+ export function Sparkline({ serie, startingTimeStamp, sampleDuration, sampleInterval, yAxisType }: SparklineProps) {
22
23
  const data = useMemo(
23
24
  () => {
24
25
  const dataMdp = addMissingDataPoint(serie.data, startingTimeStamp, sampleDuration, sampleInterval);
@@ -48,6 +49,7 @@ export function Sparkline({ serie, startingTimeStamp, sampleDuration, sampleInte
48
49
  activeDot={false}
49
50
  isAnimationActive={false}
50
51
  />
52
+ {yAxisType === 'percentage' && <YAxis domain={[0, 100]} hide />}
51
53
  </AreaChart>
52
54
  </ResponsiveContainer>
53
55
  );
@@ -31,6 +31,8 @@ export const FormattedDate = {
31
31
  'chart-date' as const,
32
32
  'year-month-day' as const,
33
33
  'month-day' as const,
34
+ 'day-month-abbreviated' as const,
35
+ 'chart-long-term-date' as const,
34
36
  ].map((format) => (
35
37
  <tr key={format}>
36
38
  <td>{format}</td>
@@ -837,6 +837,7 @@ export const DynamicColorSetExample: Story = {
837
837
  ]}
838
838
  title="Dynamic Chart 2"
839
839
  height={200}
840
+ unitRange={UNIT_RANGE_BS}
840
841
  startingTimeStamp={Number(prometheusData3[0][0])}
841
842
  isLoading={false}
842
843
  yAxisType={'percentage'}
@@ -166,3 +166,22 @@ export const FlatWithMissingData: Story = {
166
166
  },
167
167
  },
168
168
  };
169
+
170
+ export const PercentageYAxis: Story = {
171
+ args: {
172
+ serie: {
173
+ data: trendingUpData,
174
+ },
175
+ startingTimeStamp: 1740405600,
176
+ sampleDuration: 7200,
177
+ sampleInterval: 720,
178
+ yAxisType: 'percentage',
179
+ },
180
+ parameters: {
181
+ docs: {
182
+ description: {
183
+ story: 'Sparkline with Y-axis range is set as [0-100].',
184
+ },
185
+ },
186
+ },
187
+ };