@scality/core-ui 0.175.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 (34) hide show
  1. package/dist/components/barchartv2/Barchart.component.d.ts +3 -4
  2. package/dist/components/barchartv2/Barchart.component.d.ts.map +1 -1
  3. package/dist/components/barchartv2/Barchart.component.js +13 -21
  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/linetimeseriechart/linetimeseriechart.component.d.ts +2 -1
  16. package/dist/components/linetimeseriechart/linetimeseriechart.component.d.ts.map +1 -1
  17. package/dist/components/linetimeseriechart/linetimeseriechart.component.js +15 -13
  18. package/dist/components/linetimeseriechart/utils.d.ts +1 -1
  19. package/dist/components/linetimeseriechart/utils.d.ts.map +1 -1
  20. package/dist/components/linetimeseriechart/utils.js +13 -13
  21. package/package.json +1 -1
  22. package/src/lib/components/barchartv2/Barchart.component.test.tsx +23 -25
  23. package/src/lib/components/barchartv2/Barchart.component.tsx +22 -27
  24. package/src/lib/components/barchartv2/BarchartTooltip.test.tsx +3 -3
  25. package/src/lib/components/barchartv2/BarchartTooltip.tsx +14 -18
  26. package/src/lib/components/barchartv2/utils.ts +1 -5
  27. package/src/lib/components/chartlegend/ChartLegendWrapper.tsx +1 -1
  28. package/src/lib/components/charttooltip/ChartTooltip.tsx +40 -0
  29. package/src/lib/components/date/FormattedDateTime.tsx +73 -8
  30. package/src/lib/components/linetimeseriechart/linetimeseriechart.component.tsx +34 -27
  31. package/src/lib/components/linetimeseriechart/utils.test.ts +30 -68
  32. package/src/lib/components/linetimeseriechart/utils.ts +14 -18
  33. package/stories/formattedate.stories.tsx +2 -0
  34. package/stories/linetimeseriechart.stories.tsx +1 -0
@@ -89,9 +89,9 @@ describe('Barchart', () => {
89
89
  </Wrapper>,
90
90
  );
91
91
 
92
- expect(screen.getByText('Fri05Jul')).toBeInTheDocument();
93
- expect(screen.getByText('Sat06Jul')).toBeInTheDocument();
94
- expect(screen.getByText('Sun07Jul')).toBeInTheDocument();
92
+ expect(screen.getByText('05 Jul')).toBeInTheDocument();
93
+ expect(screen.getByText('06 Jul')).toBeInTheDocument();
94
+ expect(screen.getByText('07 Jul')).toBeInTheDocument();
95
95
  });
96
96
  it('should render the Barchart component with error state', async () => {
97
97
  const { Wrapper } = getWrapper();
@@ -153,11 +153,11 @@ describe('Barchart', () => {
153
153
  </ChartLegendWrapper>
154
154
  </Wrapper>,
155
155
  );
156
- expect(screen.getByText('Wed03Jul')).toBeInTheDocument();
157
- expect(screen.getByText('Thu04Jul')).toBeInTheDocument();
158
- expect(screen.getByText('Fri05Jul')).toBeInTheDocument();
159
- expect(screen.getByText('Sat06Jul')).toBeInTheDocument();
160
- expect(screen.getByText('Sun07Jul')).toBeInTheDocument();
156
+ expect(screen.getByText('03 Jul')).toBeInTheDocument();
157
+ expect(screen.getByText('04 Jul')).toBeInTheDocument();
158
+ expect(screen.getByText('05 Jul')).toBeInTheDocument();
159
+ expect(screen.getByText('06 Jul')).toBeInTheDocument();
160
+ expect(screen.getByText('07 Jul')).toBeInTheDocument();
161
161
  });
162
162
  it('should render when there are missing data in the time range', async () => {
163
163
  const bars = [
@@ -203,10 +203,10 @@ describe('Barchart', () => {
203
203
 
204
204
  // Check that all days are present
205
205
  await waitFor(() => {
206
- expect(screen.getByText('Fri05Jul')).toBeInTheDocument();
207
- expect(screen.getByText('Sat06Jul')).toBeInTheDocument();
208
- expect(screen.getByText('Sun07Jul')).toBeInTheDocument();
209
- expect(screen.getByText('Mon08Jul')).toBeInTheDocument();
206
+ expect(screen.getByText('05 Jul')).toBeInTheDocument();
207
+ expect(screen.getByText('06 Jul')).toBeInTheDocument();
208
+ expect(screen.getByText('07 Jul')).toBeInTheDocument();
209
+ expect(screen.getByText('08 Jul')).toBeInTheDocument();
210
210
  });
211
211
  });
212
212
  it('should render for a specific time range', async () => {
@@ -244,13 +244,13 @@ describe('Barchart', () => {
244
244
  </Wrapper>,
245
245
  );
246
246
  await waitFor(() => {
247
- expect(screen.getByText('Fri05Jul')).toBeInTheDocument();
248
- expect(screen.getByText('Sat06Jul')).toBeInTheDocument();
249
- expect(screen.getByText('Sun07Jul')).toBeInTheDocument();
250
- expect(screen.getByText('Mon08Jul')).toBeInTheDocument();
251
- expect(screen.getByText('Tue09Jul')).toBeInTheDocument();
252
- expect(screen.getByText('Wed10Jul')).toBeInTheDocument();
253
- expect(screen.getByText('Thu11Jul')).toBeInTheDocument();
247
+ expect(screen.getByText('05 Jul')).toBeInTheDocument();
248
+ expect(screen.getByText('06 Jul')).toBeInTheDocument();
249
+ expect(screen.getByText('07 Jul')).toBeInTheDocument();
250
+ expect(screen.getByText('08 Jul')).toBeInTheDocument();
251
+ expect(screen.getByText('09 Jul')).toBeInTheDocument();
252
+ expect(screen.getByText('10 Jul')).toBeInTheDocument();
253
+ expect(screen.getByText('11 Jul')).toBeInTheDocument();
254
254
  });
255
255
  });
256
256
  it('should render the Barchart component with hourly intervals', async () => {
@@ -411,7 +411,7 @@ describe('Barchart', () => {
411
411
  />
412
412
  </Wrapper>,
413
413
  );
414
- expect(screen.getByText('Fri05Jul 10:00')).toBeInTheDocument();
414
+ expect(screen.getByText('05 Jul')).toBeInTheDocument();
415
415
  });
416
416
 
417
417
  it('should render the CustomTick component with day format', () => {
@@ -435,7 +435,7 @@ describe('Barchart', () => {
435
435
  />
436
436
  </Wrapper>,
437
437
  );
438
- expect(screen.getByText('Fri05Jul')).toBeInTheDocument();
438
+ expect(screen.getByText('05 Jul')).toBeInTheDocument();
439
439
  });
440
440
  it('should render the CustomTick component with hour format', () => {
441
441
  const { Wrapper } = getWrapper();
@@ -458,7 +458,7 @@ describe('Barchart', () => {
458
458
  />
459
459
  </Wrapper>,
460
460
  );
461
- expect(screen.getByText('10:00')).toBeInTheDocument();
461
+ expect(screen.getByText('05 Jul')).toBeInTheDocument();
462
462
  });
463
463
  it('should render the CustomTick component with minute format', () => {
464
464
  const { Wrapper } = getWrapper();
@@ -481,9 +481,7 @@ describe('Barchart', () => {
481
481
  />
482
482
  </Wrapper>,
483
483
  );
484
- expect(
485
- screen.getByText(new Date('2024-07-05T10:00:00').getTime()),
486
- ).toBeInTheDocument();
484
+ expect(screen.getByText('05 Jul')).toBeInTheDocument();
487
485
  });
488
486
  });
489
487
  });
@@ -3,7 +3,6 @@ import {
3
3
  Bar,
4
4
  BarChart,
5
5
  CartesianGrid,
6
- ReferenceLine,
7
6
  ResponsiveContainer,
8
7
  Tooltip,
9
8
  TooltipContentProps,
@@ -112,33 +111,19 @@ interface CustomTickProps {
112
111
  /* ---------------------------------- COMPONENTS ---------------------------------- */
113
112
 
114
113
  /**
115
- * Formats a date based on the interval
116
- * @param timestamp - Timestamp
117
- * @param interval - Interval in milliseconds
114
+ * Get the format of the date based on the duration
115
+ * @param duration - Duration in milliseconds
118
116
  * @returns Formatted string
119
117
  */
120
118
  export const formatDate = (
121
- timestamp: number,
122
- interval: number,
123
- ): React.ReactNode => {
124
- const date = new Date(timestamp);
125
- // More than 24 hours interval - use day and time format
126
- if (interval > 24 * 60 * 60 * 1000) {
127
- return (
128
- <>
129
- <FormattedDateTime format="chart-date" value={date} />{' '}
130
- <FormattedDateTime format="time" value={date} />
131
- </>
132
- );
133
- } else if (interval === 24 * 60 * 60 * 1000) {
134
- // Daily interval - use day format
135
- return <FormattedDateTime format="chart-date" value={date} />;
136
- } else if (interval >= 60 * 1000) {
137
- //Hourly and minute intervals - use minute format
138
- return <FormattedDateTime format="time" value={date} />;
119
+ duration: number,
120
+ ): 'time' | 'day-month-abbreviated' | 'chart-long-term-date' => {
121
+ if (duration <= 24 * 60 * 60 * 1000) {
122
+ return 'time';
123
+ } else if (duration <= 7 * 24 * 60 * 60 * 1000) {
124
+ return 'day-month-abbreviated';
139
125
  } else {
140
- // minute interval or less - use full timestamp
141
- return timestamp;
126
+ return 'chart-long-term-date';
142
127
  }
143
128
  };
144
129
 
@@ -155,6 +140,11 @@ export const CustomTick = ({
155
140
  width / visibleTicksCount - CHART_CONSTANTS.TICK_WIDTH_OFFSET;
156
141
  const centerX = x - tickWidth / 2;
157
142
 
143
+ const duration =
144
+ type.type === 'time'
145
+ ? type.timeRange.endDate.getTime() - type.timeRange.startDate.getTime()
146
+ : 0;
147
+
158
148
  return (
159
149
  <foreignObject
160
150
  x={centerX}
@@ -167,9 +157,14 @@ export const CustomTick = ({
167
157
  color="textSecondary"
168
158
  text={
169
159
  <Text variant="Smaller">
170
- {type.type === 'time'
171
- ? formatDate(payload.value, type.timeRange.interval)
172
- : String(payload.value)}
160
+ {type.type === 'time' ? (
161
+ <FormattedDateTime
162
+ format={formatDate(duration)}
163
+ value={new Date(payload.value)}
164
+ />
165
+ ) : (
166
+ String(payload.value)
167
+ )}
173
168
  </Text>
174
169
  }
175
170
  centered
@@ -30,6 +30,7 @@ describe('ChartTooltip', () => {
30
30
  longDate: () => screen.queryByText(/01 July 2024/),
31
31
  date: () => screen.queryByText(/\b01 Jul\b/),
32
32
  time: () => screen.queryByText(/00:00:00/),
33
+ dateTime: () => screen.queryByText(/01 Jul 00:00:00/),
33
34
  };
34
35
  it('should render the BarchartTooltip component', () => {
35
36
  render(
@@ -89,9 +90,8 @@ describe('ChartTooltip', () => {
89
90
  expect(selectors.successValue()).toBeInTheDocument();
90
91
  expect(selectors.failed()).toBeInTheDocument();
91
92
  expect(selectors.failedValue()).toBeInTheDocument();
92
- expect(selectors.date()).not.toBeInTheDocument();
93
- expect(selectors.longDate()).toBeInTheDocument();
94
- expect(selectors.time()).not.toBeInTheDocument();
93
+
94
+ expect(selectors.dateTime()).toBeInTheDocument();
95
95
  });
96
96
  it('should render time tooltip when type is time and interval is one hour', () => {
97
97
  const label = date;
@@ -1,19 +1,19 @@
1
+ import { TooltipContentProps } from 'recharts';
2
+ import { LegendShape } from '../chartlegend/ChartLegend';
3
+ import {
4
+ ChartTooltipContainer,
5
+ ChartTooltipHeader,
6
+ ChartTooltipItem,
7
+ ChartTooltipItemsContainer,
8
+ TooltipHeader,
9
+ } from '../charttooltip/ChartTooltip';
1
10
  import {
2
11
  BarchartBars,
3
12
  BarchartTooltipFn,
4
13
  CategoryType,
5
14
  TimeType,
6
15
  } from './Barchart.component';
7
- import { FormattedDateTime } from '../date/FormattedDateTime';
8
- import { TooltipContentProps } from 'recharts';
9
16
  import { getCurrentPoint } from './utils';
10
- import {
11
- ChartTooltipContainer,
12
- ChartTooltipItem,
13
- ChartTooltipHeader,
14
- ChartTooltipItemsContainer,
15
- } from '../charttooltip/ChartTooltip';
16
- import { LegendShape } from '../chartlegend/ChartLegend';
17
17
 
18
18
  export const BarchartTooltip = <T extends BarchartBars>({
19
19
  type,
@@ -40,19 +40,15 @@ export const BarchartTooltip = <T extends BarchartBars>({
40
40
  if (tooltip) {
41
41
  return tooltip(currentPoint);
42
42
  }
43
-
43
+ const duration =
44
+ type.type === 'time'
45
+ ? type.timeRange.startDate.getTime() - type.timeRange.endDate.getTime()
46
+ : 0;
44
47
  return (
45
48
  <ChartTooltipContainer>
46
49
  <ChartTooltipHeader>
47
50
  {type.type === 'time' ? (
48
- <FormattedDateTime
49
- format={
50
- type.timeRange.interval < 24 * 60 * 60 * 1000
51
- ? 'day-month-abbreviated-hour-minute-second'
52
- : 'long-date-without-weekday'
53
- }
54
- value={new Date(currentPoint.category)}
55
- />
51
+ <TooltipHeader duration={duration} value={currentPoint.category} />
56
52
  ) : (
57
53
  currentPoint.category
58
54
  )}
@@ -1,8 +1,4 @@
1
- import {
2
- BarchartProps,
3
- BarchartBars,
4
- BarchartTooltipFn,
5
- } from './Barchart.component';
1
+ import { BarchartProps, BarchartBars } from './Barchart.component';
6
2
  import { TooltipContentProps } from 'recharts';
7
3
  import { chartColors, ChartColors } from '../../style/theme';
8
4
  import { useChartLegend } from '../chartlegend/ChartLegendWrapper';
@@ -133,7 +133,7 @@ export const ChartLegendWrapper = ({
133
133
  );
134
134
 
135
135
  const listResources = useCallback(() => {
136
- return Object.keys(internalColorSet);
136
+ return Object.keys(internalColorSet).sort();
137
137
  }, [internalColorSet]);
138
138
 
139
139
  const chartLegendState = useMemo(
@@ -2,6 +2,7 @@ import React from 'react';
2
2
  import styled from 'styled-components';
3
3
  import { spacing } from '../../spacing';
4
4
  import { fontSize, fontWeight } from '../../style/theme';
5
+ import { FormattedDateTime } from '../date/FormattedDateTime';
5
6
 
6
7
  export const ChartTooltipContainer = styled.div`
7
8
  border: 1px solid ${({ theme }) => theme.border};
@@ -81,3 +82,42 @@ export const ChartTooltipItemsContainer = styled.div`
81
82
  gap: ${spacing.r8};
82
83
  width: 100%;
83
84
  `;
85
+
86
+ export const ChartTooltipSeparator = styled.div`
87
+ height: 1px;
88
+ background-color: ${({ theme }) => theme.border};
89
+ margin: ${spacing.r4} 0;
90
+ width: 100%;
91
+ `;
92
+
93
+ export type TooltipDateFormat =
94
+ | 'day-month-abbreviated-year-hour-minute'
95
+ | 'day-month-abbreviated-hour-minute-second'
96
+ | 'day-month-abbreviated-hour-minute';
97
+
98
+ const getTooltipDateFormat: (duration: number) => TooltipDateFormat = (
99
+ duration: number,
100
+ ) => {
101
+ if (duration <= 60 * 60 * 1000) {
102
+ return 'day-month-abbreviated-hour-minute-second';
103
+ } else if (duration <= 7 * 24 * 60 * 60 * 1000) {
104
+ return 'day-month-abbreviated-hour-minute';
105
+ } else {
106
+ return 'day-month-abbreviated-year-hour-minute';
107
+ }
108
+ };
109
+
110
+ export const TooltipHeader = ({
111
+ duration,
112
+ value,
113
+ }: {
114
+ duration: number;
115
+ value: string | number;
116
+ }) => {
117
+ const timeFormat = getTooltipDateFormat(duration);
118
+ return (
119
+ <ChartTooltipHeader>
120
+ <FormattedDateTime format={timeFormat} value={new Date(value)} />
121
+ </ChartTooltipHeader>
122
+ );
123
+ };
@@ -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
  }
@@ -8,20 +8,19 @@ import {
8
8
  XAxis,
9
9
  YAxis,
10
10
  } from 'recharts';
11
- import { useCallback, useMemo, useRef, useState } from 'react';
11
+ import React, { useCallback, useMemo, useRef, useState } from 'react';
12
12
  import styled, { useTheme } from 'styled-components';
13
13
  import { spacing } from '../../spacing';
14
14
  import { fontSize } from '../../style/theme';
15
15
  import { Box } from '../box/Box';
16
16
  import { useChartLegend } from '../chartlegend/ChartLegendWrapper';
17
- import { FormattedDateTime } from '../date/FormattedDateTime';
18
17
  import { Icon } from '../icon/Icon.component';
19
18
  import {
20
19
  addMissingDataPoint,
21
20
  getUnitLabel,
22
21
  } from '../linetemporalchart/ChartUtil';
23
22
  import { Loader } from '../loader/Loader.component';
24
- import { ChartTitleText, SmallerText } from '../text/Text.component';
23
+ import { ChartTitleText, Text } from '../text/Text.component';
25
24
  import { Tooltip as TooltipComponent } from '../tooltip/Tooltip.component';
26
25
  import { formatXAxisLabel } from './utils';
27
26
  import {
@@ -29,6 +28,8 @@ import {
29
28
  ChartTooltipItem,
30
29
  ChartTooltipHeader,
31
30
  ChartTooltipItemsContainer,
31
+ ChartTooltipSeparator,
32
+ TooltipHeader,
32
33
  } from '../charttooltip/ChartTooltip';
33
34
  import { LegendShape } from '../chartlegend/ChartLegend';
34
35
  import { StyledResponsiveContainer } from '../barchartv2/Barchart.component';
@@ -99,28 +100,30 @@ export type LineChartProps = (
99
100
  renderTooltip?: (
100
101
  tooltipProps: TooltipContentProps<number, string>,
101
102
  unitLabel?: string,
102
- timeFormat?: 'date-time' | 'date',
103
+ duration?: number,
103
104
  ) => React.ReactNode;
104
105
  };
105
106
 
106
107
  const LineTimeSerieChartTooltip = ({
107
108
  unitLabel,
108
- timeFormat,
109
+ duration,
109
110
  isChartActive,
110
111
  tooltipProps,
111
112
  renderTooltip,
112
113
  hoveredValue,
114
+ isSymmetrical,
113
115
  }: {
114
116
  tooltipProps: TooltipContentProps<number, string>;
115
117
  unitLabel?: string;
116
- timeFormat?: 'date-time' | 'date';
118
+ duration: number;
117
119
  isChartActive?: boolean;
118
120
  renderTooltip?: (
119
121
  tooltipProps: TooltipContentProps<number, string>,
120
122
  unitLabel?: string,
121
- timeFormat?: 'date-time' | 'date',
123
+ duration?: number,
122
124
  ) => React.ReactNode;
123
125
  hoveredValue?: string;
126
+ isSymmetrical?: boolean;
124
127
  }) => {
125
128
  const { active, payload, label } = tooltipProps;
126
129
 
@@ -128,7 +131,7 @@ const LineTimeSerieChartTooltip = ({
128
131
  return null;
129
132
 
130
133
  if (renderTooltip) {
131
- return renderTooltip(tooltipProps, unitLabel, timeFormat);
134
+ return renderTooltip(tooltipProps, unitLabel, duration);
132
135
  }
133
136
  // We can't use the default itemSorter method because it's a custom tooltip.
134
137
  // Sort the payload here instead
@@ -145,17 +148,15 @@ const LineTimeSerieChartTooltip = ({
145
148
  return bValue - aValue; // Positives before negatives
146
149
  });
147
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
+
148
156
  return (
149
157
  <ChartTooltipContainer>
150
158
  <ChartTooltipHeader>
151
- <FormattedDateTime
152
- format={
153
- timeFormat === 'date-time'
154
- ? 'day-month-abbreviated-hour-minute-second'
155
- : 'long-date-without-weekday'
156
- }
157
- value={new Date(label)}
158
- />
159
+ <TooltipHeader duration={duration} value={label} />
159
160
  </ChartTooltipHeader>
160
161
  <ChartTooltipItemsContainer>
161
162
  {sortedPayload.map((entry, index) => {
@@ -174,13 +175,18 @@ const LineTimeSerieChartTooltip = ({
174
175
  : `${entry.value.toFixed(2)} ${unitLabel}`;
175
176
 
176
177
  return (
177
- <ChartTooltipItem
178
- key={index}
179
- label={entry.name}
180
- value={formattedValue}
181
- legendIcon={legendIcon}
182
- isHovered={isHovered}
183
- />
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>
184
190
  );
185
191
  })}
186
192
  </ChartTooltipItemsContainer>
@@ -438,8 +444,8 @@ export function LineTimeSerieChart({
438
444
 
439
445
  // Format time for display the tick in the x axis
440
446
  const formatXAxisLabelCallback = useCallback(
441
- (timestamp: number) => formatXAxisLabel(timestamp, timeFormat, chartData),
442
- [timeFormat, chartData],
447
+ (timestamp: number) => formatXAxisLabel(timestamp, duration),
448
+ [duration],
443
449
  );
444
450
 
445
451
  return (
@@ -452,7 +458,7 @@ export function LineTimeSerieChart({
452
458
  <Box ml={spacing.r4}>
453
459
  <TooltipComponent
454
460
  placement={'right'}
455
- overlay={<SmallerText>{helpText}</SmallerText>}
461
+ overlay={<Text>{helpText}</Text>}
456
462
  >
457
463
  <Icon name="Info" color={theme.buttonSecondary} />
458
464
  </TooltipComponent>
@@ -533,8 +539,9 @@ export function LineTimeSerieChart({
533
539
  content={(props: TooltipContentProps<number, string>) => (
534
540
  <LineTimeSerieChartTooltip
535
541
  unitLabel={unitLabel}
536
- timeFormat={timeFormat}
542
+ duration={duration}
537
543
  renderTooltip={renderTooltip}
544
+ isSymmetrical={yAxisType === 'symmetrical'}
538
545
  tooltipProps={props}
539
546
  isChartActive={isChartActive}
540
547
  hoveredValue={hoveredValue}