@scality/core-ui 0.167.0 → 0.169.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/.github/workflows/github-pages.yml +5 -3
- package/.storybook/preview.js +1 -0
- package/dist/components/barchartv2/Barchart.component.d.ts +5 -1
- package/dist/components/barchartv2/Barchart.component.d.ts.map +1 -1
- package/dist/components/barchartv2/Barchart.component.js +12 -7
- package/dist/components/barchartv2/ChartTooltip.d.ts +9 -13
- package/dist/components/barchartv2/ChartTooltip.d.ts.map +1 -1
- package/dist/components/barchartv2/ChartTooltip.js +14 -4
- package/dist/components/barchartv2/utils.d.ts +9 -2
- package/dist/components/barchartv2/utils.d.ts.map +1 -1
- package/dist/components/barchartv2/utils.js +14 -18
- package/dist/components/buttonv2/Buttonv2.component.d.ts.map +1 -1
- package/dist/components/buttonv2/Buttonv2.component.js +27 -6
- package/dist/components/date/FormattedDateTime.d.ts +20 -1
- package/dist/components/date/FormattedDateTime.d.ts.map +1 -1
- package/dist/components/date/FormattedDateTime.js +36 -0
- package/dist/components/linetemporalchart/ChartUtil.d.ts.map +1 -1
- package/dist/components/linetemporalchart/ChartUtil.js +6 -0
- package/dist/components/linetimeseriechart/linetimeseriechart.component.d.ts +7 -1
- package/dist/components/linetimeseriechart/linetimeseriechart.component.d.ts.map +1 -1
- package/dist/components/linetimeseriechart/linetimeseriechart.component.js +36 -32
- package/dist/components/linetimeseriechart/utils.d.ts +16 -0
- package/dist/components/linetimeseriechart/utils.d.ts.map +1 -0
- package/dist/components/linetimeseriechart/utils.js +28 -0
- package/dist/style/theme.d.ts +2 -2
- package/dist/style/theme.d.ts.map +1 -1
- package/dist/style/theme.js +26 -0
- package/package.json +5 -4
- package/src/lib/components/barchartv2/Barchart.component.test.tsx +12 -8
- package/src/lib/components/barchartv2/Barchart.component.tsx +29 -9
- package/src/lib/components/barchartv2/ChartTooltip.test.tsx +119 -0
- package/src/lib/components/barchartv2/ChartTooltip.tsx +49 -19
- package/src/lib/components/barchartv2/utils.test.ts +31 -46
- package/src/lib/components/barchartv2/utils.ts +24 -31
- package/src/lib/components/buttonv2/Buttonv2.component.tsx +27 -6
- package/src/lib/components/date/FormattedDateTime.tsx +43 -1
- package/src/lib/components/linetemporalchart/ChartUtil.ts +6 -0
- package/src/lib/components/linetimeseriechart/linetimeseriechart.component.tsx +81 -54
- package/src/lib/components/linetimeseriechart/linetimeseriechart.test.tsx +71 -0
- package/src/lib/components/linetimeseriechart/utils.test.ts +87 -0
- package/src/lib/components/linetimeseriechart/utils.ts +43 -0
- package/src/lib/style/theme.ts +26 -0
- package/stories/BarChart/barchart.stories.tsx +108 -13
- package/stories/color.mdx +12 -0
- package/stories/linetimeseriechart.stories.tsx +97 -0
|
@@ -8,7 +8,8 @@ import {
|
|
|
8
8
|
YAxis,
|
|
9
9
|
CartesianGrid,
|
|
10
10
|
} from 'recharts';
|
|
11
|
-
import {
|
|
11
|
+
import type { Payload } from 'recharts/types/component/DefaultTooltipContent';
|
|
12
|
+
import { useCallback, useMemo, useRef } from 'react';
|
|
12
13
|
import { useTheme } from 'styled-components';
|
|
13
14
|
import { addMissingDataPoint } from '../linetemporalchart/ChartUtil';
|
|
14
15
|
import styled from 'styled-components';
|
|
@@ -20,10 +21,15 @@ import { spacing } from '../../spacing';
|
|
|
20
21
|
import { getUnitLabel } from '../linetemporalchart/ChartUtil';
|
|
21
22
|
import { Icon } from '../icon/Icon.component';
|
|
22
23
|
import { Tooltip as TooltipComponent } from '../tooltip/Tooltip.component';
|
|
23
|
-
import {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
import { FormattedDateTime } from '../date/FormattedDateTime';
|
|
25
|
+
import { Box } from '../box/Box';
|
|
26
|
+
import { formatXAxisLabel } from './utils';
|
|
27
|
+
|
|
28
|
+
type TooltipPayload = Payload<number, string> & {
|
|
29
|
+
value: number;
|
|
30
|
+
name: string;
|
|
31
|
+
color: string;
|
|
32
|
+
};
|
|
27
33
|
|
|
28
34
|
const LineTemporalChartWrapper = styled.div`
|
|
29
35
|
display: flex;
|
|
@@ -60,6 +66,8 @@ const TooltipValue = styled.div`
|
|
|
60
66
|
color: ${(props) => props.theme.textSecondary};
|
|
61
67
|
display: flex;
|
|
62
68
|
align-items: flex-start;
|
|
69
|
+
justify-content: space-between;
|
|
70
|
+
width: 100%;
|
|
63
71
|
`;
|
|
64
72
|
|
|
65
73
|
const TooltipLegend = styled.div<{ color: string }>`
|
|
@@ -71,21 +79,23 @@ const TooltipLegend = styled.div<{ color: string }>`
|
|
|
71
79
|
margin-top: 8px;
|
|
72
80
|
`;
|
|
73
81
|
|
|
74
|
-
const
|
|
82
|
+
const TooltipLeftGroup = styled.div`
|
|
75
83
|
display: flex;
|
|
84
|
+
align-items: flex-start;
|
|
76
85
|
min-width: 0;
|
|
77
86
|
flex: 1;
|
|
78
87
|
`;
|
|
79
88
|
|
|
80
89
|
const TooltipName = styled.div`
|
|
81
|
-
margin-right: 4px;
|
|
82
90
|
word-wrap: break-word;
|
|
83
91
|
word-break: break-word;
|
|
84
|
-
|
|
92
|
+
flex: 1;
|
|
85
93
|
`;
|
|
86
94
|
|
|
87
95
|
const TooltipInstanceValue = styled.div`
|
|
88
|
-
|
|
96
|
+
margin-left: 16px;
|
|
97
|
+
flex-shrink: 0;
|
|
98
|
+
text-align: right;
|
|
89
99
|
`;
|
|
90
100
|
|
|
91
101
|
export type Serie = {
|
|
@@ -129,6 +139,12 @@ export type LineChartProps = (
|
|
|
129
139
|
label: string;
|
|
130
140
|
}[];
|
|
131
141
|
isLoading?: boolean;
|
|
142
|
+
/**
|
|
143
|
+
* The format of the x axis, default is 'date-time' which is like 01 Sep 16:00
|
|
144
|
+
* If you want to display the date only, you can set it to 'date' which is like 2025-09-01
|
|
145
|
+
* This will affect the format of the tooltip as well
|
|
146
|
+
*/
|
|
147
|
+
timeFormat?: 'date-time' | 'date';
|
|
132
148
|
yAxisTitle?: string;
|
|
133
149
|
helpText?: string;
|
|
134
150
|
};
|
|
@@ -138,23 +154,20 @@ const CustomTooltip = ({
|
|
|
138
154
|
payload,
|
|
139
155
|
label,
|
|
140
156
|
unitLabel,
|
|
157
|
+
timeFormat,
|
|
141
158
|
}: {
|
|
142
159
|
active?: boolean;
|
|
143
|
-
payload?: Array<
|
|
144
|
-
value: number;
|
|
145
|
-
name: string;
|
|
146
|
-
color: string;
|
|
147
|
-
dataKey: string;
|
|
148
|
-
}>;
|
|
160
|
+
payload?: Array<TooltipPayload>;
|
|
149
161
|
label?: string;
|
|
150
162
|
unitLabel?: string;
|
|
163
|
+
timeFormat?: 'date-time' | 'date';
|
|
151
164
|
}) => {
|
|
152
165
|
if (!active || !payload || !payload.length || !label) return null;
|
|
153
166
|
// We can't use the default itemSorter method because it's a custom tooltip.
|
|
154
167
|
// Sort the payload here instead
|
|
155
168
|
const sortedPayload = [...payload].sort((a, b) => {
|
|
156
|
-
const aValue =
|
|
157
|
-
const bValue =
|
|
169
|
+
const aValue = a.value;
|
|
170
|
+
const bValue = b.value;
|
|
158
171
|
|
|
159
172
|
if (aValue >= 0 && bValue >= 0) {
|
|
160
173
|
return bValue - aValue; // Higher positive values first
|
|
@@ -169,21 +182,25 @@ const CustomTooltip = ({
|
|
|
169
182
|
<TooltipContainer>
|
|
170
183
|
<TooltipTime>
|
|
171
184
|
<FormattedDateTime
|
|
172
|
-
format=
|
|
185
|
+
format={
|
|
186
|
+
timeFormat === 'date-time'
|
|
187
|
+
? 'day-month-abbreviated-hour-minute-second'
|
|
188
|
+
: 'long-date-without-weekday'
|
|
189
|
+
}
|
|
173
190
|
value={new Date(label)}
|
|
174
191
|
/>
|
|
175
192
|
</TooltipTime>
|
|
176
193
|
{sortedPayload.map((entry, index) => (
|
|
177
194
|
<TooltipValue key={index}>
|
|
178
|
-
<
|
|
179
|
-
|
|
195
|
+
<TooltipLeftGroup>
|
|
196
|
+
<TooltipLegend color={entry.color} />
|
|
180
197
|
<TooltipName>{entry.name}</TooltipName>
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
</
|
|
198
|
+
</TooltipLeftGroup>
|
|
199
|
+
<TooltipInstanceValue>
|
|
200
|
+
{!Number.isFinite(entry.value)
|
|
201
|
+
? '-'
|
|
202
|
+
: `${entry.value.toFixed(2)} ${unitLabel}`}
|
|
203
|
+
</TooltipInstanceValue>
|
|
187
204
|
</TooltipValue>
|
|
188
205
|
))}
|
|
189
206
|
</TooltipContainer>
|
|
@@ -205,6 +222,7 @@ export function LineTimeSerieChart({
|
|
|
205
222
|
duration,
|
|
206
223
|
unitRange,
|
|
207
224
|
isLoading = false,
|
|
225
|
+
timeFormat = 'date-time',
|
|
208
226
|
yAxisType = 'default',
|
|
209
227
|
yAxisTitle,
|
|
210
228
|
helpText,
|
|
@@ -291,7 +309,7 @@ export function LineTimeSerieChart({
|
|
|
291
309
|
);
|
|
292
310
|
}, [series, startingTimeStamp, duration, interval, yAxisType]);
|
|
293
311
|
|
|
294
|
-
// Calculate
|
|
312
|
+
// Calculate evenly spaced ticks that avoid the very beginning and end
|
|
295
313
|
const xAxisTicks = useMemo(() => {
|
|
296
314
|
if (!chartData || chartData.length === 0) return [];
|
|
297
315
|
|
|
@@ -299,20 +317,23 @@ export function LineTimeSerieChart({
|
|
|
299
317
|
const minTimestamp = Math.min(...timestamps);
|
|
300
318
|
const maxTimestamp = Math.max(...timestamps);
|
|
301
319
|
|
|
302
|
-
// Calculate 5 perfectly evenly spaced ticks
|
|
303
320
|
const timeRange = maxTimestamp - minTimestamp;
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
const
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
321
|
+
// Add padding to avoid labels at the very edges (10% padding on each side)
|
|
322
|
+
const padding = timeRange * 0.1;
|
|
323
|
+
const paddedStart = minTimestamp + padding;
|
|
324
|
+
const paddedEnd = maxTimestamp - padding;
|
|
325
|
+
const paddedRange = paddedEnd - paddedStart;
|
|
326
|
+
|
|
327
|
+
// Create 5 evenly spaced ticks within the padded range
|
|
328
|
+
const numTicks = 5;
|
|
329
|
+
const tickInterval = paddedRange / (numTicks - 1);
|
|
330
|
+
|
|
331
|
+
const evenlySpacedTicks = Array.from(
|
|
332
|
+
{ length: numTicks },
|
|
333
|
+
(_, index) => paddedStart + index * tickInterval,
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
return evenlySpacedTicks;
|
|
316
337
|
}, [chartData]);
|
|
317
338
|
|
|
318
339
|
// 3. Transform the data base on the valuebase
|
|
@@ -393,12 +414,9 @@ export function LineTimeSerieChart({
|
|
|
393
414
|
}, [series, getColor]);
|
|
394
415
|
|
|
395
416
|
// Format time for display the tick in the x axis
|
|
396
|
-
const
|
|
397
|
-
() => (timestamp
|
|
398
|
-
|
|
399
|
-
return DAY_MONTH_ABBREVIATED_HOUR_MINUTE.format(date).replace(',', '');
|
|
400
|
-
},
|
|
401
|
-
[],
|
|
417
|
+
const formatXAxisLabelCallback = useCallback(
|
|
418
|
+
(timestamp: number) => formatXAxisLabel(timestamp, timeFormat, chartData),
|
|
419
|
+
[timeFormat, chartData],
|
|
402
420
|
);
|
|
403
421
|
|
|
404
422
|
return (
|
|
@@ -408,12 +426,14 @@ export function LineTimeSerieChart({
|
|
|
408
426
|
{title} {unitLabel && `(${unitLabel})`}
|
|
409
427
|
</ChartTitleText>
|
|
410
428
|
{helpText && (
|
|
411
|
-
<
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
429
|
+
<Box ml={spacing.r4}>
|
|
430
|
+
<TooltipComponent
|
|
431
|
+
placement={'right'}
|
|
432
|
+
overlay={<SmallerText>{helpText}</SmallerText>}
|
|
433
|
+
>
|
|
434
|
+
<Icon name="Info" color={theme.buttonSecondary} />
|
|
435
|
+
</TooltipComponent>
|
|
436
|
+
</Box>
|
|
417
437
|
)}
|
|
418
438
|
{isLoading && <Loader />}
|
|
419
439
|
</ChartHeader>
|
|
@@ -438,7 +458,7 @@ export function LineTimeSerieChart({
|
|
|
438
458
|
type="number"
|
|
439
459
|
domain={['dataMin', 'dataMax']}
|
|
440
460
|
ticks={xAxisTicks}
|
|
441
|
-
tickFormatter={
|
|
461
|
+
tickFormatter={formatXAxisLabelCallback}
|
|
442
462
|
tickCount={5}
|
|
443
463
|
tick={{
|
|
444
464
|
fill: theme.textSecondary,
|
|
@@ -472,8 +492,14 @@ export function LineTimeSerieChart({
|
|
|
472
492
|
fontSize: fontSize.smaller,
|
|
473
493
|
}}
|
|
474
494
|
tickFormatter={(value) => Math.round(value).toString()}
|
|
495
|
+
tickCount={5}
|
|
496
|
+
interval={'preserveStartEnd'}
|
|
497
|
+
/>
|
|
498
|
+
<Tooltip
|
|
499
|
+
content={
|
|
500
|
+
<CustomTooltip unitLabel={unitLabel} timeFormat={timeFormat} />
|
|
501
|
+
}
|
|
475
502
|
/>
|
|
476
|
-
<Tooltip content={<CustomTooltip unitLabel={unitLabel} />} />
|
|
477
503
|
{/* Add horizontal line at y=0 for symmetrical charts */}
|
|
478
504
|
{yAxisType === 'symmetrical' && (
|
|
479
505
|
<ReferenceLine y={0} stroke={theme.border} />
|
|
@@ -493,6 +519,7 @@ export function LineTimeSerieChart({
|
|
|
493
519
|
dataKey={label}
|
|
494
520
|
stroke={colorMapping[resource]}
|
|
495
521
|
dot={false}
|
|
522
|
+
isAnimationActive={false}
|
|
496
523
|
/>
|
|
497
524
|
);
|
|
498
525
|
}),
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { render } from '@testing-library/react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import {
|
|
4
|
+
LineChartProps,
|
|
5
|
+
LineTimeSerieChart,
|
|
6
|
+
} from './linetimeseriechart.component';
|
|
7
|
+
import { ChartLegendWrapper } from '../chartlegend/ChartLegendWrapper';
|
|
8
|
+
import { ThemeProvider } from 'styled-components';
|
|
9
|
+
import { coreUIAvailableThemes } from '../../style/theme';
|
|
10
|
+
|
|
11
|
+
const TestSeries = [
|
|
12
|
+
{
|
|
13
|
+
resource: 'Series 1',
|
|
14
|
+
getTooltipLabel: () => `Series 1`,
|
|
15
|
+
data: [
|
|
16
|
+
[1622505600000, 10],
|
|
17
|
+
[1622509200000, 20],
|
|
18
|
+
[1622512800000, 30],
|
|
19
|
+
] as [number, number][],
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
resource: 'Series 2',
|
|
23
|
+
getTooltipLabel: () => `Series 2`,
|
|
24
|
+
data: [
|
|
25
|
+
[1622505600000, 15],
|
|
26
|
+
[1622509200000, 25],
|
|
27
|
+
[1622512800000, 35],
|
|
28
|
+
] as [number, number][],
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
const ColorSet = {
|
|
33
|
+
'Series 1': '#FF0000',
|
|
34
|
+
'Series 2': '#00FF00',
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const renderLineTimeSerieChart = (props: Partial<LineChartProps> = {}) => {
|
|
38
|
+
return render(
|
|
39
|
+
<ThemeProvider theme={coreUIAvailableThemes.artescaLight}>
|
|
40
|
+
<ChartLegendWrapper colorSet={ColorSet}>
|
|
41
|
+
<LineTimeSerieChart
|
|
42
|
+
{...({
|
|
43
|
+
title: 'Test Chart',
|
|
44
|
+
yAxisType: 'default',
|
|
45
|
+
series: TestSeries,
|
|
46
|
+
height: 400,
|
|
47
|
+
startingTimeStamp: TestSeries[0].data[0][0],
|
|
48
|
+
interval: TestSeries[0].data[1][0] - TestSeries[0].data[0][0],
|
|
49
|
+
duration:
|
|
50
|
+
TestSeries[0].data[TestSeries[0].data.length - 1][0] -
|
|
51
|
+
TestSeries[0].data[0][0],
|
|
52
|
+
unitRange: [{ label: 'units', value: 1 }],
|
|
53
|
+
...props,
|
|
54
|
+
} as LineChartProps)}
|
|
55
|
+
/>
|
|
56
|
+
</ChartLegendWrapper>
|
|
57
|
+
</ThemeProvider>,
|
|
58
|
+
);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
describe('LineTimeSerieChart', () => {
|
|
62
|
+
it('should render when with basic parameters', async () => {
|
|
63
|
+
const { container } = renderLineTimeSerieChart();
|
|
64
|
+
expect(container).toBeInTheDocument();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should render when no unitRange is provided', async () => {
|
|
68
|
+
const { container } = renderLineTimeSerieChart({ unitRange: undefined });
|
|
69
|
+
expect(container).toBeInTheDocument();
|
|
70
|
+
});
|
|
71
|
+
});
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { formatXAxisLabel } from './utils';
|
|
2
|
+
|
|
3
|
+
const createChartData = (startDate: Date, endDate: Date) => [
|
|
4
|
+
{ timestamp: startDate.getTime() },
|
|
5
|
+
{ timestamp: endDate.getTime() },
|
|
6
|
+
];
|
|
7
|
+
|
|
8
|
+
describe('formatXAxisLabel', () => {
|
|
9
|
+
const mockTimestamp = new Date('2025-09-15T14:30:00Z').getTime();
|
|
10
|
+
|
|
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');
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
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');
|
|
39
|
+
});
|
|
40
|
+
|
|
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');
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
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');
|
|
73
|
+
});
|
|
74
|
+
|
|
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);
|
|
83
|
+
|
|
84
|
+
expect(result).toBe('09-15');
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DAY_MONTH_ABBREVIATED_HOUR_MINUTE,
|
|
3
|
+
YEAR_MONTH_DAY_FORMATTER,
|
|
4
|
+
MONTH_DAY_FORMATTER,
|
|
5
|
+
} from '../date/FormattedDateTime';
|
|
6
|
+
|
|
7
|
+
export const ONE_YEAR_MILLISECONDS = 366 * 24 * 60 * 60 * 1000;
|
|
8
|
+
|
|
9
|
+
export type ChartDataPoint = {
|
|
10
|
+
timestamp: number;
|
|
11
|
+
} & Record<string, number | null>;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Formats timestamp for X-axis labels based on time format and data range:
|
|
15
|
+
* For 'date-time' format, return day-month-abbreviated-hour-minute format
|
|
16
|
+
* For 'date' format, return YYYY-MM-DD format if time range is greater than 1 year, otherwise return MM-DD format
|
|
17
|
+
*
|
|
18
|
+
* @param timestamp - The timestamp to format in milliseconds
|
|
19
|
+
* @param timeFormat - The format type ('date-time' or 'date')
|
|
20
|
+
* @param chartData - The chart data to determine time range for optimal formatting
|
|
21
|
+
* @returns Formatted string for display on X-axis
|
|
22
|
+
*/
|
|
23
|
+
export const formatXAxisLabel = (
|
|
24
|
+
timestamp: number,
|
|
25
|
+
timeFormat: 'date-time' | 'date' = 'date-time',
|
|
26
|
+
chartData: ChartDataPoint[] = [],
|
|
27
|
+
): string => {
|
|
28
|
+
const date = new Date(timestamp);
|
|
29
|
+
if (!chartData.length) {
|
|
30
|
+
return YEAR_MONTH_DAY_FORMATTER.format(date);
|
|
31
|
+
}
|
|
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
|
+
};
|
package/src/lib/style/theme.ts
CHANGED
|
@@ -60,6 +60,7 @@ export const coreUIAvailableThemesNames = [
|
|
|
60
60
|
'darkRebrand',
|
|
61
61
|
'artescaLight',
|
|
62
62
|
'ring9dark',
|
|
63
|
+
'G-Dark'
|
|
63
64
|
] as const;
|
|
64
65
|
export type CoreUIThemeName = (typeof coreUIAvailableThemesNames)[number];
|
|
65
66
|
|
|
@@ -139,6 +140,31 @@ export const coreUIAvailableThemes: Record<CoreUIThemeName, CoreUITheme> = {
|
|
|
139
140
|
textReverse: '#000000',
|
|
140
141
|
textLink: '#71AEFF',
|
|
141
142
|
},
|
|
143
|
+
'G-Dark': {
|
|
144
|
+
statusHealthy: '#0AADA6',
|
|
145
|
+
statusHealthyRGB: '10,173,166',
|
|
146
|
+
statusWarning: '#F8F32B',
|
|
147
|
+
statusWarningRGB: '248,243,43',
|
|
148
|
+
statusCritical: '#E84855',
|
|
149
|
+
statusCriticalRGB: '232,72,85',
|
|
150
|
+
selectedActive: '#037AFF',
|
|
151
|
+
highlight: '#1A3C75',
|
|
152
|
+
border: '#4A4A4A',
|
|
153
|
+
buttonPrimary: 'linear-gradient(130deg, #9355E7 0%, #2E4AA3 100%)',
|
|
154
|
+
buttonSecondary: 'linear-gradient(130deg, #595A78 0%, #44455F 100%)',
|
|
155
|
+
buttonDelete: '#3D0808',
|
|
156
|
+
infoPrimary: '#8E8EAC',
|
|
157
|
+
infoSecondary: '#333366',
|
|
158
|
+
backgroundLevel1: '#121219',
|
|
159
|
+
backgroundLevel2: '#323245',
|
|
160
|
+
backgroundLevel3: '#232331',
|
|
161
|
+
backgroundLevel4: '#1B1B27',
|
|
162
|
+
textPrimary: '#EAEAEA',
|
|
163
|
+
textSecondary: '#B5B5B5',
|
|
164
|
+
textTertiary: '#DFDFDF',
|
|
165
|
+
textReverse: '#000000',
|
|
166
|
+
textLink: '#71AEFF',
|
|
167
|
+
},
|
|
142
168
|
};
|
|
143
169
|
|
|
144
170
|
/**
|