@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.
- package/dist/components/barchartv2/Barchart.component.d.ts +3 -4
- package/dist/components/barchartv2/Barchart.component.d.ts.map +1 -1
- package/dist/components/barchartv2/Barchart.component.js +13 -21
- package/dist/components/barchartv2/BarchartTooltip.d.ts +1 -1
- package/dist/components/barchartv2/BarchartTooltip.d.ts.map +1 -1
- package/dist/components/barchartv2/BarchartTooltip.js +6 -6
- package/dist/components/barchartv2/utils.d.ts.map +1 -1
- package/dist/components/chartlegend/ChartLegendWrapper.js +1 -1
- package/dist/components/charttooltip/ChartTooltip.d.ts +6 -0
- package/dist/components/charttooltip/ChartTooltip.d.ts.map +1 -1
- package/dist/components/charttooltip/ChartTooltip.js +22 -0
- package/dist/components/date/FormattedDateTime.d.ts +23 -8
- package/dist/components/date/FormattedDateTime.d.ts.map +1 -1
- package/dist/components/date/FormattedDateTime.js +51 -7
- package/dist/components/linetimeseriechart/linetimeseriechart.component.d.ts +2 -1
- package/dist/components/linetimeseriechart/linetimeseriechart.component.d.ts.map +1 -1
- package/dist/components/linetimeseriechart/linetimeseriechart.component.js +15 -13
- package/dist/components/linetimeseriechart/utils.d.ts +1 -1
- package/dist/components/linetimeseriechart/utils.d.ts.map +1 -1
- package/dist/components/linetimeseriechart/utils.js +13 -13
- package/package.json +1 -1
- package/src/lib/components/barchartv2/Barchart.component.test.tsx +23 -25
- package/src/lib/components/barchartv2/Barchart.component.tsx +22 -27
- package/src/lib/components/barchartv2/BarchartTooltip.test.tsx +3 -3
- package/src/lib/components/barchartv2/BarchartTooltip.tsx +14 -18
- package/src/lib/components/barchartv2/utils.ts +1 -5
- package/src/lib/components/chartlegend/ChartLegendWrapper.tsx +1 -1
- package/src/lib/components/charttooltip/ChartTooltip.tsx +40 -0
- package/src/lib/components/date/FormattedDateTime.tsx +73 -8
- package/src/lib/components/linetimeseriechart/linetimeseriechart.component.tsx +34 -27
- package/src/lib/components/linetimeseriechart/utils.test.ts +30 -68
- package/src/lib/components/linetimeseriechart/utils.ts +14 -18
- package/stories/formattedate.stories.tsx +2 -0
- package/stories/linetimeseriechart.stories.tsx +1 -0
|
@@ -89,9 +89,9 @@ describe('Barchart', () => {
|
|
|
89
89
|
</Wrapper>,
|
|
90
90
|
);
|
|
91
91
|
|
|
92
|
-
expect(screen.getByText('
|
|
93
|
-
expect(screen.getByText('
|
|
94
|
-
expect(screen.getByText('
|
|
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('
|
|
157
|
-
expect(screen.getByText('
|
|
158
|
-
expect(screen.getByText('
|
|
159
|
-
expect(screen.getByText('
|
|
160
|
-
expect(screen.getByText('
|
|
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('
|
|
207
|
-
expect(screen.getByText('
|
|
208
|
-
expect(screen.getByText('
|
|
209
|
-
expect(screen.getByText('
|
|
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('
|
|
248
|
-
expect(screen.getByText('
|
|
249
|
-
expect(screen.getByText('
|
|
250
|
-
expect(screen.getByText('
|
|
251
|
-
expect(screen.getByText('
|
|
252
|
-
expect(screen.getByText('
|
|
253
|
-
expect(screen.getByText('
|
|
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('
|
|
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('
|
|
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('
|
|
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
|
-
*
|
|
116
|
-
* @param
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
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
|
-
|
|
172
|
-
|
|
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
|
-
|
|
93
|
-
expect(selectors.
|
|
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
|
-
<
|
|
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
|
+
* @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
|
|
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
|
|
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
|
|
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': '
|
|
154
|
-
* 'day-month-abbreviated-hour-minute-second': '
|
|
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': '
|
|
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,
|
|
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
|
-
|
|
103
|
+
duration?: number,
|
|
103
104
|
) => React.ReactNode;
|
|
104
105
|
};
|
|
105
106
|
|
|
106
107
|
const LineTimeSerieChartTooltip = ({
|
|
107
108
|
unitLabel,
|
|
108
|
-
|
|
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
|
-
|
|
118
|
+
duration: number;
|
|
117
119
|
isChartActive?: boolean;
|
|
118
120
|
renderTooltip?: (
|
|
119
121
|
tooltipProps: TooltipContentProps<number, string>,
|
|
120
122
|
unitLabel?: string,
|
|
121
|
-
|
|
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,
|
|
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
|
-
<
|
|
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
|
-
<
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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,
|
|
442
|
-
[
|
|
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={<
|
|
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
|
-
|
|
542
|
+
duration={duration}
|
|
537
543
|
renderTooltip={renderTooltip}
|
|
544
|
+
isSymmetrical={yAxisType === 'symmetrical'}
|
|
538
545
|
tooltipProps={props}
|
|
539
546
|
isChartActive={isChartActive}
|
|
540
547
|
hoveredValue={hoveredValue}
|