@scality/core-ui 0.175.0 → 0.177.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 +4 -5
- package/dist/components/barchartv2/Barchart.component.d.ts.map +1 -1
- package/dist/components/barchartv2/Barchart.component.js +23 -27
- package/dist/components/barchartv2/BarchartTooltip.d.ts +4 -3
- package/dist/components/barchartv2/BarchartTooltip.d.ts.map +1 -1
- package/dist/components/barchartv2/BarchartTooltip.js +10 -12
- package/dist/components/barchartv2/utils.d.ts +6 -1
- package/dist/components/barchartv2/utils.d.ts.map +1 -1
- package/dist/components/barchartv2/utils.js +34 -8
- package/dist/components/chartlegend/ChartLegendWrapper.js +1 -1
- package/dist/components/charttooltip/ChartTooltip.d.ts +29 -0
- package/dist/components/charttooltip/ChartTooltip.d.ts.map +1 -1
- package/dist/components/charttooltip/ChartTooltip.js +105 -1
- 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/globalhealthbar/GlobalHealthBarRecharts.component.d.ts.map +1 -1
- package/dist/components/globalhealthbar/GlobalHealthBarRecharts.component.js +27 -1
- package/dist/components/globalhealthbar/components/GlobalHealthBarTooltip.d.ts +1 -1
- package/dist/components/globalhealthbar/components/GlobalHealthBarTooltip.d.ts.map +1 -1
- package/dist/components/globalhealthbar/components/GlobalHealthBarTooltip.js +19 -59
- package/dist/components/globalhealthbar/components/HealthBarXAxis.js +1 -1
- package/dist/components/globalhealthbar/useHealthBarData.d.ts +1 -0
- package/dist/components/globalhealthbar/useHealthBarData.d.ts.map +1 -1
- package/dist/components/globalhealthbar/useHealthBarData.js +1 -0
- package/dist/components/globalhealthbar/useHealthBarData.spec.js +2 -0
- 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 +45 -47
- 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/dist/style/theme.js +1 -1
- package/package.json +1 -1
- package/src/lib/components/barchartv2/Barchart.component.test.tsx +23 -25
- package/src/lib/components/barchartv2/Barchart.component.tsx +41 -39
- package/src/lib/components/barchartv2/BarchartTooltip.test.tsx +33 -3
- package/src/lib/components/barchartv2/BarchartTooltip.tsx +33 -24
- package/src/lib/components/barchartv2/utils.test.ts +72 -17
- package/src/lib/components/barchartv2/utils.ts +40 -12
- package/src/lib/components/chartlegend/ChartLegendWrapper.tsx +1 -1
- package/src/lib/components/charttooltip/ChartTooltip.tsx +174 -1
- package/src/lib/components/date/FormattedDateTime.tsx +73 -8
- package/src/lib/components/globalhealthbar/GlobalHealthBarRecharts.component.tsx +56 -11
- package/src/lib/components/globalhealthbar/components/GlobalHealthBarTooltip.tsx +75 -117
- package/src/lib/components/globalhealthbar/components/HealthBarXAxis.tsx +1 -1
- package/src/lib/components/globalhealthbar/useHealthBarData.spec.tsx +2 -0
- package/src/lib/components/globalhealthbar/useHealthBarData.ts +2 -0
- package/src/lib/components/linetimeseriechart/linetimeseriechart.component.tsx +101 -90
- package/src/lib/components/linetimeseriechart/utils.test.ts +30 -68
- package/src/lib/components/linetimeseriechart/utils.ts +13 -17
- package/src/lib/style/theme.ts +1 -1
- package/stories/BarChart/barchart.stories.tsx +23 -8
- package/stories/formattedate.stories.tsx +2 -0
- package/stories/linetimeseriechart.stories.tsx +1 -0
|
@@ -16,6 +16,7 @@ describe('useHealthBarData', () => {
|
|
|
16
16
|
severity,
|
|
17
17
|
startsAt,
|
|
18
18
|
endsAt,
|
|
19
|
+
key: `${severity}_${startsAt}`,
|
|
19
20
|
});
|
|
20
21
|
|
|
21
22
|
describe('Alert Filtering', () => {
|
|
@@ -231,6 +232,7 @@ describe('useHealthBarData', () => {
|
|
|
231
232
|
severity: 'warning',
|
|
232
233
|
startsAt: '2023-12-01T02:00:00Z',
|
|
233
234
|
endsAt: '2023-12-01T04:00:00Z',
|
|
235
|
+
key: 'warning_0',
|
|
234
236
|
});
|
|
235
237
|
});
|
|
236
238
|
|
|
@@ -5,6 +5,7 @@ export interface Alert {
|
|
|
5
5
|
startsAt: string;
|
|
6
6
|
endsAt: string;
|
|
7
7
|
severity: 'warning' | 'critical' | 'unavailable';
|
|
8
|
+
key: string;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
export const useHealthBarData = (
|
|
@@ -44,6 +45,7 @@ export const useHealthBarData = (
|
|
|
44
45
|
// Store alert data separately for tooltip access
|
|
45
46
|
alertsMapData[uniqueKey] = {
|
|
46
47
|
...alert,
|
|
48
|
+
key: uniqueKey, // Add the consistent key to the alert object
|
|
47
49
|
};
|
|
48
50
|
});
|
|
49
51
|
|
|
@@ -8,30 +8,32 @@ 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
|
-
import {
|
|
13
|
+
import { Stack } from '../../spacing';
|
|
14
14
|
import { fontSize } from '../../style/theme';
|
|
15
|
-
import { Box } from '../box/Box';
|
|
16
15
|
import { useChartLegend } from '../chartlegend/ChartLegendWrapper';
|
|
17
|
-
import { FormattedDateTime } from '../date/FormattedDateTime';
|
|
18
|
-
import { Icon } from '../icon/Icon.component';
|
|
19
16
|
import {
|
|
20
17
|
addMissingDataPoint,
|
|
21
18
|
getUnitLabel,
|
|
22
19
|
} from '../linetemporalchart/ChartUtil';
|
|
23
20
|
import { Loader } from '../loader/Loader.component';
|
|
24
|
-
import { ChartTitleText
|
|
25
|
-
import { Tooltip as TooltipComponent } from '../tooltip/Tooltip.component';
|
|
21
|
+
import { ChartTitleText } from '../text/Text.component';
|
|
26
22
|
import { formatXAxisLabel } from './utils';
|
|
27
23
|
import {
|
|
28
|
-
|
|
24
|
+
ChartTooltipPortal,
|
|
29
25
|
ChartTooltipItem,
|
|
30
26
|
ChartTooltipHeader,
|
|
31
27
|
ChartTooltipItemsContainer,
|
|
28
|
+
ChartTooltipSeparator,
|
|
29
|
+
TooltipHeader,
|
|
32
30
|
} from '../charttooltip/ChartTooltip';
|
|
33
31
|
import { LegendShape } from '../chartlegend/ChartLegend';
|
|
34
32
|
import { StyledResponsiveContainer } from '../barchartv2/Barchart.component';
|
|
33
|
+
import { getRoundReferenceValue, getTicks } from '../barchartv2/utils';
|
|
34
|
+
import { IconHelp } from '../iconhelper/IconHelper';
|
|
35
|
+
|
|
36
|
+
const maxWidthTooltip = { maxWidth: '20rem' };
|
|
35
37
|
|
|
36
38
|
const LineTemporalChartWrapper = styled.div`
|
|
37
39
|
display: flex;
|
|
@@ -39,11 +41,6 @@ const LineTemporalChartWrapper = styled.div`
|
|
|
39
41
|
justify-content: flex-start;
|
|
40
42
|
`;
|
|
41
43
|
|
|
42
|
-
const ChartHeader = styled.div`
|
|
43
|
-
display: flex;
|
|
44
|
-
align-items: center;
|
|
45
|
-
`;
|
|
46
|
-
|
|
47
44
|
export type Serie = {
|
|
48
45
|
// the name of the resource
|
|
49
46
|
resource: string;
|
|
@@ -99,92 +96,112 @@ export type LineChartProps = (
|
|
|
99
96
|
renderTooltip?: (
|
|
100
97
|
tooltipProps: TooltipContentProps<number, string>,
|
|
101
98
|
unitLabel?: string,
|
|
102
|
-
|
|
99
|
+
duration?: number,
|
|
103
100
|
) => React.ReactNode;
|
|
104
101
|
};
|
|
105
102
|
|
|
106
103
|
const LineTimeSerieChartTooltip = ({
|
|
107
104
|
unitLabel,
|
|
108
|
-
|
|
105
|
+
duration,
|
|
109
106
|
isChartActive,
|
|
110
107
|
tooltipProps,
|
|
111
108
|
renderTooltip,
|
|
112
109
|
hoveredValue,
|
|
110
|
+
isSymmetrical,
|
|
111
|
+
chartContainerRef,
|
|
113
112
|
}: {
|
|
114
113
|
tooltipProps: TooltipContentProps<number, string>;
|
|
115
114
|
unitLabel?: string;
|
|
116
|
-
|
|
115
|
+
duration: number;
|
|
117
116
|
isChartActive?: boolean;
|
|
118
117
|
renderTooltip?: (
|
|
119
118
|
tooltipProps: TooltipContentProps<number, string>,
|
|
120
119
|
unitLabel?: string,
|
|
121
|
-
|
|
120
|
+
duration?: number,
|
|
122
121
|
) => React.ReactNode;
|
|
123
122
|
hoveredValue?: string;
|
|
123
|
+
isSymmetrical?: boolean;
|
|
124
|
+
chartContainerRef: React.RefObject<HTMLDivElement>;
|
|
124
125
|
}) => {
|
|
125
|
-
const { active, payload, label } = tooltipProps;
|
|
126
|
+
const { active, payload, label, coordinate } = tooltipProps;
|
|
126
127
|
|
|
127
128
|
if (!active || !payload || !payload.length || !label || !isChartActive)
|
|
128
129
|
return null;
|
|
129
130
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
// Sort the payload here instead
|
|
135
|
-
const sortedPayload = [...payload].sort((a, b) => {
|
|
136
|
-
const aValue = a.value;
|
|
137
|
-
const bValue = b.value;
|
|
138
|
-
|
|
139
|
-
if (aValue >= 0 && bValue >= 0) {
|
|
140
|
-
return bValue - aValue; // Higher positive values first
|
|
141
|
-
}
|
|
142
|
-
if (aValue < 0 && bValue < 0) {
|
|
143
|
-
return bValue - aValue; // Lower negative values first
|
|
144
|
-
}
|
|
145
|
-
return bValue - aValue; // Positives before negatives
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
return (
|
|
149
|
-
<ChartTooltipContainer>
|
|
131
|
+
const tooltipContent = renderTooltip ? (
|
|
132
|
+
renderTooltip(tooltipProps, unitLabel, duration)
|
|
133
|
+
) : (
|
|
134
|
+
<>
|
|
150
135
|
<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
|
-
/>
|
|
136
|
+
<TooltipHeader duration={duration} value={label} />
|
|
159
137
|
</ChartTooltipHeader>
|
|
160
138
|
<ChartTooltipItemsContainer>
|
|
161
|
-
{
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
value={formattedValue}
|
|
181
|
-
legendIcon={legendIcon}
|
|
182
|
-
isHovered={isHovered}
|
|
183
|
-
/>
|
|
139
|
+
{(() => {
|
|
140
|
+
// We can't use the default itemSorter method because it's a custom tooltip.
|
|
141
|
+
// Sort the payload here instead
|
|
142
|
+
const sortedPayload = [...payload].sort((a, b) => {
|
|
143
|
+
const aValue = a.value;
|
|
144
|
+
const bValue = b.value;
|
|
145
|
+
|
|
146
|
+
if (aValue >= 0 && bValue >= 0) {
|
|
147
|
+
return bValue - aValue; // Higher positive values first
|
|
148
|
+
}
|
|
149
|
+
if (aValue < 0 && bValue < 0) {
|
|
150
|
+
return bValue - aValue; // Lower negative values first
|
|
151
|
+
}
|
|
152
|
+
return bValue - aValue; // Positives before negatives
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// Find the transition point between positive and negative values
|
|
156
|
+
const separatorIndex = sortedPayload.findIndex(
|
|
157
|
+
(entry) => entry.value < 0,
|
|
184
158
|
);
|
|
185
|
-
|
|
159
|
+
const hasBothPositiveAndNegative =
|
|
160
|
+
separatorIndex > 0 && separatorIndex < sortedPayload.length;
|
|
161
|
+
|
|
162
|
+
return sortedPayload.map((entry, index) => {
|
|
163
|
+
const legendIcon = (
|
|
164
|
+
<LegendShape
|
|
165
|
+
color={entry.color}
|
|
166
|
+
shape="line"
|
|
167
|
+
chartColors={{ [entry.color]: entry.color }}
|
|
168
|
+
/>
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
const isHovered = entry.name === hoveredValue;
|
|
172
|
+
|
|
173
|
+
const formattedValue = !Number.isFinite(entry.value)
|
|
174
|
+
? '-'
|
|
175
|
+
: `${entry.value.toFixed(2)} ${unitLabel}`;
|
|
176
|
+
|
|
177
|
+
return (
|
|
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>
|
|
190
|
+
);
|
|
191
|
+
});
|
|
192
|
+
})()}
|
|
186
193
|
</ChartTooltipItemsContainer>
|
|
187
|
-
|
|
194
|
+
</>
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
return (
|
|
198
|
+
<ChartTooltipPortal
|
|
199
|
+
coordinate={coordinate}
|
|
200
|
+
chartContainerRef={chartContainerRef}
|
|
201
|
+
isVisible={active && isChartActive}
|
|
202
|
+
>
|
|
203
|
+
{tooltipContent}
|
|
204
|
+
</ChartTooltipPortal>
|
|
188
205
|
);
|
|
189
206
|
};
|
|
190
207
|
|
|
@@ -376,8 +393,8 @@ export function LineTimeSerieChart({
|
|
|
376
393
|
const maxValue = Math.max(top, bottom);
|
|
377
394
|
|
|
378
395
|
const { valueBase, unitLabel } = getUnitLabel(unitRange ?? [], maxValue);
|
|
379
|
-
|
|
380
|
-
const topValue =
|
|
396
|
+
// Use round reference value to add extra padding to the top value
|
|
397
|
+
const topValue = getRoundReferenceValue(maxValue / valueBase);
|
|
381
398
|
|
|
382
399
|
const rechartsData = chartData.map((dataPoint) => {
|
|
383
400
|
const normalizedDataPoint = { ...dataPoint };
|
|
@@ -438,28 +455,21 @@ export function LineTimeSerieChart({
|
|
|
438
455
|
|
|
439
456
|
// Format time for display the tick in the x axis
|
|
440
457
|
const formatXAxisLabelCallback = useCallback(
|
|
441
|
-
(timestamp: number) => formatXAxisLabel(timestamp,
|
|
442
|
-
[
|
|
458
|
+
(timestamp: number) => formatXAxisLabel(timestamp, duration),
|
|
459
|
+
[duration],
|
|
443
460
|
);
|
|
444
461
|
|
|
445
462
|
return (
|
|
446
463
|
<LineTemporalChartWrapper>
|
|
447
|
-
<
|
|
464
|
+
<Stack gap="r4">
|
|
448
465
|
<ChartTitleText>
|
|
449
466
|
{title} {unitLabel && `(${unitLabel})`}
|
|
450
467
|
</ChartTitleText>
|
|
451
468
|
{helpText && (
|
|
452
|
-
<
|
|
453
|
-
<TooltipComponent
|
|
454
|
-
placement={'right'}
|
|
455
|
-
overlay={<SmallerText>{helpText}</SmallerText>}
|
|
456
|
-
>
|
|
457
|
-
<Icon name="Info" color={theme.buttonSecondary} />
|
|
458
|
-
</TooltipComponent>
|
|
459
|
-
</Box>
|
|
469
|
+
<IconHelp tooltipMessage={helpText} overlayStyle={maxWidthTooltip} />
|
|
460
470
|
)}
|
|
461
471
|
{isLoading && <Loader />}
|
|
462
|
-
</
|
|
472
|
+
</Stack>
|
|
463
473
|
<div
|
|
464
474
|
onFocus={() => setIsChartActive(true)}
|
|
465
475
|
onBlur={() => setIsChartActive(false)}
|
|
@@ -504,9 +514,8 @@ export function LineTimeSerieChart({
|
|
|
504
514
|
label={{
|
|
505
515
|
value: yAxisTitle,
|
|
506
516
|
angle: 90,
|
|
507
|
-
|
|
517
|
+
dx: 20,
|
|
508
518
|
style: {
|
|
509
|
-
textAnchor: 'middle',
|
|
510
519
|
fill: theme.textSecondary,
|
|
511
520
|
fontSize: fontSize.smaller,
|
|
512
521
|
},
|
|
@@ -524,20 +533,22 @@ export function LineTimeSerieChart({
|
|
|
524
533
|
fontSize: fontSize.smaller,
|
|
525
534
|
}}
|
|
526
535
|
tickFormatter={(value) =>
|
|
527
|
-
new Intl.NumberFormat('fr-FR').format(value
|
|
536
|
+
new Intl.NumberFormat('fr-FR').format(value)
|
|
528
537
|
}
|
|
529
|
-
|
|
538
|
+
ticks={getTicks(topValue, yAxisType === 'symmetrical')}
|
|
530
539
|
interval={0}
|
|
531
540
|
/>
|
|
532
541
|
<Tooltip
|
|
533
542
|
content={(props: TooltipContentProps<number, string>) => (
|
|
534
543
|
<LineTimeSerieChartTooltip
|
|
535
544
|
unitLabel={unitLabel}
|
|
536
|
-
|
|
545
|
+
duration={duration}
|
|
537
546
|
renderTooltip={renderTooltip}
|
|
547
|
+
isSymmetrical={yAxisType === 'symmetrical'}
|
|
538
548
|
tooltipProps={props}
|
|
539
549
|
isChartActive={isChartActive}
|
|
540
550
|
hoveredValue={hoveredValue}
|
|
551
|
+
chartContainerRef={chartRef}
|
|
541
552
|
/>
|
|
542
553
|
)}
|
|
543
554
|
/>
|
|
@@ -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('
|
|
12
|
-
it('should format timestamp with
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
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('
|
|
23
|
-
it('should
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
|
|
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');
|
|
14
|
+
describe('medium duration (≤ 7 days)', () => {
|
|
15
|
+
it('should format timestamp with day-month-abbreviated-hour-minute format', () => {
|
|
16
|
+
const duration = 3 * 24 * 60 * 60; // 3 days
|
|
17
|
+
const result = formatXAxisLabel(mockTimestamp, duration);
|
|
18
|
+
expect(result).toBe('15 Sep 14:30');
|
|
53
19
|
});
|
|
20
|
+
});
|
|
54
21
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
const
|
|
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('
|
|
67
|
-
it('should handle
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
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
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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 14:30');
|
|
41
|
+
});
|
|
83
42
|
|
|
84
|
-
|
|
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
|
+
TIME_FORMATER,
|
|
3
|
+
DAY_MONTH_ABBREVIATED_YEAR,
|
|
2
4
|
DAY_MONTH_ABBREVIATED_HOUR_MINUTE,
|
|
3
|
-
YEAR_MONTH_DAY_FORMATTER,
|
|
4
|
-
MONTH_DAY_FORMATTER,
|
|
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
|
-
|
|
26
|
-
chartData: ChartDataPoint[] = [],
|
|
25
|
+
duration: number,
|
|
27
26
|
): string => {
|
|
28
27
|
const date = new Date(timestamp);
|
|
29
|
-
if (
|
|
30
|
-
return
|
|
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_HOUR_MINUTE.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
|
};
|
package/src/lib/style/theme.ts
CHANGED
|
@@ -39,17 +39,17 @@ export const Playground: Story = {
|
|
|
39
39
|
{
|
|
40
40
|
label: 'Success',
|
|
41
41
|
data: [
|
|
42
|
-
['category1',
|
|
43
|
-
['category2',
|
|
44
|
-
['category3',
|
|
42
|
+
['category1', 1],
|
|
43
|
+
['category2', 1],
|
|
44
|
+
['category3', 2],
|
|
45
45
|
],
|
|
46
46
|
},
|
|
47
47
|
{
|
|
48
48
|
label: 'Failed',
|
|
49
49
|
data: [
|
|
50
|
-
['category1',
|
|
51
|
-
['category2',
|
|
52
|
-
['category3',
|
|
50
|
+
['category1', 1],
|
|
51
|
+
['category2', 1],
|
|
52
|
+
['category3', 2],
|
|
53
53
|
],
|
|
54
54
|
},
|
|
55
55
|
] as const;
|
|
@@ -61,7 +61,11 @@ export const Playground: Story = {
|
|
|
61
61
|
}}
|
|
62
62
|
>
|
|
63
63
|
<Stack direction="vertical" gap="r16">
|
|
64
|
-
<Barchart
|
|
64
|
+
<Barchart
|
|
65
|
+
type={{ type: 'category' }}
|
|
66
|
+
bars={exampleData}
|
|
67
|
+
title="Playground"
|
|
68
|
+
/>
|
|
65
69
|
<ChartLegend shape="rectangle" direction="horizontal" />
|
|
66
70
|
</Stack>
|
|
67
71
|
</ChartLegendWrapper>
|
|
@@ -122,6 +126,7 @@ export const Time7Days: Story = {
|
|
|
122
126
|
>
|
|
123
127
|
<Stack direction="vertical" gap="r16">
|
|
124
128
|
<Barchart
|
|
129
|
+
title="Time 7 Days"
|
|
125
130
|
type={{
|
|
126
131
|
type: 'time',
|
|
127
132
|
timeRange: {
|
|
@@ -192,6 +197,7 @@ export const Time7DaysWithMissingData: Story = {
|
|
|
192
197
|
}}
|
|
193
198
|
>
|
|
194
199
|
<Barchart
|
|
200
|
+
title="Time 7 Days With Missing Data"
|
|
195
201
|
type={{
|
|
196
202
|
type: 'time',
|
|
197
203
|
timeRange: {
|
|
@@ -285,6 +291,7 @@ export const TimeLast24Hours: Story = {
|
|
|
285
291
|
}}
|
|
286
292
|
>
|
|
287
293
|
<Barchart
|
|
294
|
+
title="Time Last 24 Hours"
|
|
288
295
|
type={{
|
|
289
296
|
type: 'time',
|
|
290
297
|
timeRange: {
|
|
@@ -332,6 +339,7 @@ export const CapacityWithUnitRange: Story = {
|
|
|
332
339
|
}}
|
|
333
340
|
>
|
|
334
341
|
<Barchart
|
|
342
|
+
title="Capacity With Unit Range"
|
|
335
343
|
type={{ type: 'category' }}
|
|
336
344
|
bars={capacityDataWithUnitRange}
|
|
337
345
|
unitRange={[
|
|
@@ -402,7 +410,12 @@ export const Stacked: Story = {
|
|
|
402
410
|
}}
|
|
403
411
|
>
|
|
404
412
|
<Stack direction="vertical" gap="r16">
|
|
405
|
-
<Barchart
|
|
413
|
+
<Barchart
|
|
414
|
+
type={{ type: 'category' }}
|
|
415
|
+
bars={stackedData}
|
|
416
|
+
stacked
|
|
417
|
+
title="Stacked"
|
|
418
|
+
/>
|
|
406
419
|
<ChartLegend shape="rectangle" />
|
|
407
420
|
</Stack>
|
|
408
421
|
</ChartLegendWrapper>
|
|
@@ -457,6 +470,7 @@ export const DefaultSort: Story = {
|
|
|
457
470
|
stacked
|
|
458
471
|
bars={defaultSortData}
|
|
459
472
|
defaultSort={customSort}
|
|
473
|
+
title="Default Sort"
|
|
460
474
|
/>
|
|
461
475
|
</ChartLegendWrapper>
|
|
462
476
|
);
|
|
@@ -531,6 +545,7 @@ export const WithCustomTooltip: Story = {
|
|
|
531
545
|
>
|
|
532
546
|
<Stack direction="vertical" gap="r16">
|
|
533
547
|
<Barchart
|
|
548
|
+
title="Custom Tooltip"
|
|
534
549
|
type={{ type: 'category' }}
|
|
535
550
|
bars={exampleData}
|
|
536
551
|
tooltip={customTooltip}
|