@scality/core-ui 0.171.0 → 0.173.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.map +1 -1
- package/dist/components/barchartv2/Barchart.component.js +2 -2
- package/dist/components/barchartv2/BarchartTooltip.d.ts +11 -0
- package/dist/components/barchartv2/BarchartTooltip.d.ts.map +1 -0
- package/dist/components/barchartv2/BarchartTooltip.js +27 -0
- package/dist/components/charttooltip/ChartTooltip.d.ts +13 -0
- package/dist/components/charttooltip/ChartTooltip.d.ts.map +1 -0
- package/dist/components/charttooltip/ChartTooltip.js +49 -0
- package/dist/components/globalhealthbar/GlobalHealthBar.component.d.ts +4 -0
- package/dist/components/globalhealthbar/GlobalHealthBar.component.d.ts.map +1 -1
- package/dist/components/globalhealthbar/GlobalHealthBar.component.js +4 -0
- package/dist/components/globalhealthbar/GlobalHealthBarRecharts.component.d.ts +10 -0
- package/dist/components/globalhealthbar/GlobalHealthBarRecharts.component.d.ts.map +1 -0
- package/dist/components/globalhealthbar/GlobalHealthBarRecharts.component.js +78 -0
- package/dist/components/globalhealthbar/components/GlobalHealthBarTooltip.d.ts +18 -0
- package/dist/components/globalhealthbar/components/GlobalHealthBarTooltip.d.ts.map +1 -0
- package/dist/components/globalhealthbar/components/GlobalHealthBarTooltip.js +95 -0
- package/dist/components/globalhealthbar/components/HealthBarXAxis.d.ts +7 -0
- package/dist/components/globalhealthbar/components/HealthBarXAxis.d.ts.map +1 -0
- package/dist/components/globalhealthbar/components/HealthBarXAxis.js +25 -0
- package/dist/components/globalhealthbar/healthBarUtils.d.ts +77 -0
- package/dist/components/globalhealthbar/healthBarUtils.d.ts.map +1 -0
- package/dist/components/globalhealthbar/healthBarUtils.js +196 -0
- package/dist/components/globalhealthbar/healthBarUtils.spec.d.ts +2 -0
- package/dist/components/globalhealthbar/healthBarUtils.spec.d.ts.map +1 -0
- package/dist/components/globalhealthbar/healthBarUtils.spec.js +391 -0
- package/dist/components/globalhealthbar/useHealthBarData.d.ts +18 -0
- package/dist/components/globalhealthbar/useHealthBarData.d.ts.map +1 -0
- package/dist/components/globalhealthbar/useHealthBarData.js +46 -0
- package/dist/components/globalhealthbar/useHealthBarData.spec.d.ts +2 -0
- package/dist/components/globalhealthbar/useHealthBarData.spec.d.ts.map +1 -0
- package/dist/components/globalhealthbar/useHealthBarData.spec.js +207 -0
- package/dist/components/icon/Icon.component.js +2 -2
- package/dist/components/linetimeseriechart/linetimeseriechart.component.d.ts +0 -2
- package/dist/components/linetimeseriechart/linetimeseriechart.component.d.ts.map +1 -1
- package/dist/components/linetimeseriechart/linetimeseriechart.component.js +17 -57
- package/dist/components/sparkline/sparkline.component.d.ts +16 -0
- package/dist/components/sparkline/sparkline.component.d.ts.map +1 -0
- package/dist/components/sparkline/sparkline.component.js +20 -0
- package/dist/components/text/Text.component.d.ts +1 -1
- package/dist/components/text/Text.component.d.ts.map +1 -1
- package/dist/components/text/Text.component.js +6 -1
- package/dist/next.d.ts +4 -1
- package/dist/next.d.ts.map +1 -1
- package/dist/next.js +4 -1
- package/package.json +2 -2
- package/src/lib/components/barchartv2/Barchart.component.tsx +3 -2
- package/src/lib/components/barchartv2/{ChartTooltip.test.tsx → BarchartTooltip.test.tsx} +35 -12
- package/src/lib/components/barchartv2/BarchartTooltip.tsx +89 -0
- package/src/lib/components/charttooltip/ChartTooltip.tsx +83 -0
- package/src/lib/components/globalhealthbar/GlobalHealthBar.component.tsx +4 -1
- package/src/lib/components/globalhealthbar/GlobalHealthBarRecharts.component.tsx +203 -0
- package/src/lib/components/globalhealthbar/components/GlobalHealthBarTooltip.tsx +173 -0
- package/src/lib/components/globalhealthbar/components/HealthBarXAxis.tsx +94 -0
- package/src/lib/components/globalhealthbar/healthBarUtils.spec.ts +701 -0
- package/src/lib/components/globalhealthbar/healthBarUtils.ts +311 -0
- package/src/lib/components/globalhealthbar/useHealthBarData.spec.tsx +487 -0
- package/src/lib/components/globalhealthbar/useHealthBarData.ts +74 -0
- package/src/lib/components/icon/Icon.component.tsx +2 -2
- package/src/lib/components/linetimeseriechart/linetimeseriechart.component.tsx +50 -77
- package/src/lib/components/sparkline/sparkline.component.tsx +54 -0
- package/src/lib/components/text/Text.component.tsx +8 -2
- package/src/lib/next.ts +9 -1
- package/stories/GlobalHealthBar/globalhealthbarRecharts.stories.tsx +145 -0
- package/stories/GlobalHealthBar/globalheathbarrecharts.guideline.mdx +5 -0
- package/stories/InlineInput/InlineInput.stories.tsx +7 -1
- package/stories/globalhealthbar.stories.tsx +25 -5
- package/stories/sparkline.stories.tsx +168 -0
- package/dist/components/barchartv2/ChartTooltip.d.ts +0 -14
- package/dist/components/barchartv2/ChartTooltip.d.ts.map +0 -1
- package/dist/components/barchartv2/ChartTooltip.js +0 -41
- package/src/lib/components/barchartv2/ChartTooltip.tsx +0 -106
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { fontSize } from '../../style/theme';
|
|
2
|
+
// =============================================================================
|
|
3
|
+
// CONSTANTS
|
|
4
|
+
// =============================================================================
|
|
5
|
+
export const CHART_CONFIG = {
|
|
6
|
+
RADIUS_SIZE: 4,
|
|
7
|
+
EDGE_THRESHOLD: 8,
|
|
8
|
+
CHART_HEIGHT: 50,
|
|
9
|
+
BAR_SIZE: 8,
|
|
10
|
+
TICK_SIZE: 4,
|
|
11
|
+
TOOLTIP_OFFSET: 24,
|
|
12
|
+
FONT_SIZE: fontSize.smaller,
|
|
13
|
+
TEXT_DY_OFFSET: 12,
|
|
14
|
+
TICK_INTERVAL: 0,
|
|
15
|
+
MARGINS: { left: 28, right: 28, bottom: 4, top: 4 },
|
|
16
|
+
};
|
|
17
|
+
export const TIME_CONSTANTS = {
|
|
18
|
+
ONE_HOUR: 60 * 60 * 1000,
|
|
19
|
+
ONE_DAY: 24 * 60 * 60 * 1000,
|
|
20
|
+
ONE_WEEK: 7 * 24 * 60 * 60 * 1000,
|
|
21
|
+
MARGIN_HOURS: 6,
|
|
22
|
+
FIFTEEN_MINUTES: 15 * 60 * 1000,
|
|
23
|
+
SIX_HOURS: 6 * 60 * 60 * 1000,
|
|
24
|
+
};
|
|
25
|
+
export const LABEL_CONFIG = {
|
|
26
|
+
MIN_SPACE_PER_TICK: 80,
|
|
27
|
+
MODULO_CONFIG: {
|
|
28
|
+
[TIME_CONSTANTS.ONE_WEEK]: 2,
|
|
29
|
+
[TIME_CONSTANTS.ONE_DAY]: 3,
|
|
30
|
+
[TIME_CONSTANTS.ONE_HOUR]: 3,
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
// =============================================================================
|
|
34
|
+
// TICK CALCULATIONS
|
|
35
|
+
// =============================================================================
|
|
36
|
+
const { ONE_HOUR, ONE_DAY, MARGIN_HOURS, FIFTEEN_MINUTES, SIX_HOURS } = TIME_CONSTANTS;
|
|
37
|
+
const generateTickArray = (endTimestamp, count, interval) => {
|
|
38
|
+
return Array.from({ length: count }, (_, i) => endTimestamp - i * interval);
|
|
39
|
+
};
|
|
40
|
+
const roundToNearestHalfDay = (timestamp) => {
|
|
41
|
+
const date = new Date(timestamp);
|
|
42
|
+
const hours = date.getHours();
|
|
43
|
+
if (hours <= 12) {
|
|
44
|
+
return new Date(timestamp).setHours(0, 0, 0, 0);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
return new Date(timestamp).setHours(12, 0, 0, 0);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
export const calculateSevenDayTicks = (endTimestamp) => {
|
|
51
|
+
const marginedEnd = endTimestamp - MARGIN_HOURS * ONE_HOUR;
|
|
52
|
+
const roundedEnd = roundToNearestHalfDay(marginedEnd);
|
|
53
|
+
return generateTickArray(roundedEnd, 7, ONE_DAY);
|
|
54
|
+
};
|
|
55
|
+
export const calculateDayTicks = (endTimestamp) => {
|
|
56
|
+
const is6HourTick = endTimestamp % SIX_HOURS === 0;
|
|
57
|
+
const closest6Hours = Math.floor(endTimestamp / SIX_HOURS) * SIX_HOURS;
|
|
58
|
+
const tickCount = is6HourTick ? 5 : 4;
|
|
59
|
+
return generateTickArray(closest6Hours, tickCount, SIX_HOURS);
|
|
60
|
+
};
|
|
61
|
+
export const calculateHourTicks = (endTimestamp) => {
|
|
62
|
+
const is15MinuteTick = endTimestamp % FIFTEEN_MINUTES === 0;
|
|
63
|
+
const closest15Minutes = Math.floor(endTimestamp / FIFTEEN_MINUTES) * FIFTEEN_MINUTES;
|
|
64
|
+
const tickCount = is15MinuteTick ? 5 : 4;
|
|
65
|
+
return generateTickArray(closest15Minutes, tickCount, FIFTEEN_MINUTES);
|
|
66
|
+
};
|
|
67
|
+
export const getEdgeMargin = (index, totalTicks, isDaySpan) => {
|
|
68
|
+
if (isDaySpan && totalTicks === 5) {
|
|
69
|
+
return index === 0 ? -8 : index === totalTicks - 1 ? 8 : 0;
|
|
70
|
+
}
|
|
71
|
+
return 0;
|
|
72
|
+
};
|
|
73
|
+
export const getTicks = (startTimestamp, endTimestamp) => {
|
|
74
|
+
const span = endTimestamp - startTimestamp;
|
|
75
|
+
if (span === 7 * ONE_DAY) {
|
|
76
|
+
return calculateSevenDayTicks(endTimestamp);
|
|
77
|
+
}
|
|
78
|
+
else if (span === 24 * ONE_HOUR) {
|
|
79
|
+
return calculateDayTicks(endTimestamp);
|
|
80
|
+
}
|
|
81
|
+
else if (span === ONE_HOUR) {
|
|
82
|
+
return calculateHourTicks(endTimestamp);
|
|
83
|
+
}
|
|
84
|
+
return [];
|
|
85
|
+
};
|
|
86
|
+
export const calculateLabelVisibility = (chartWidth, totalTicks, span, index, endTimestamp) => {
|
|
87
|
+
const hasEnoughSpace = chartWidth / totalTicks > LABEL_CONFIG.MIN_SPACE_PER_TICK;
|
|
88
|
+
// If enough space, show all labels
|
|
89
|
+
if (hasEnoughSpace)
|
|
90
|
+
return true;
|
|
91
|
+
// Apply specific rules for each time range
|
|
92
|
+
if (span === TIME_CONSTANTS.ONE_WEEK) {
|
|
93
|
+
return index % 2 === 0;
|
|
94
|
+
}
|
|
95
|
+
if (span === TIME_CONSTANTS.ONE_DAY) {
|
|
96
|
+
const isRoundHour = endTimestamp % (60 * 60 * 1000) === 0;
|
|
97
|
+
const roundHourInterval = index % 2 === 0;
|
|
98
|
+
const defaultInterval = index % 3 === 0;
|
|
99
|
+
const result = isRoundHour ? roundHourInterval : defaultInterval;
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
if (span === TIME_CONSTANTS.ONE_HOUR) {
|
|
103
|
+
const isRound15Minute = endTimestamp % (15 * 60 * 1000) === 0;
|
|
104
|
+
const round15MinuteInterval = index % 2 === 0;
|
|
105
|
+
const defaultInterval = index % 3 === 0;
|
|
106
|
+
const result = isRound15Minute ? round15MinuteInterval : defaultInterval;
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
return false;
|
|
110
|
+
};
|
|
111
|
+
// =============================================================================
|
|
112
|
+
// RECTANGLE PROPERTIES
|
|
113
|
+
// =============================================================================
|
|
114
|
+
/**
|
|
115
|
+
* Core calculation for alert positioning within a time range
|
|
116
|
+
*/
|
|
117
|
+
export const calculateAlertPosition = (alertStartTimestamp, alertEndTimestamp, chartStartTimestamp, chartEndTimestamp, availableWidth, baseX = 0) => {
|
|
118
|
+
const start = Math.max(alertStartTimestamp, chartStartTimestamp);
|
|
119
|
+
const end = Math.min(alertEndTimestamp, chartEndTimestamp);
|
|
120
|
+
const totalTimeSpan = chartEndTimestamp - chartStartTimestamp;
|
|
121
|
+
const relativeSize = (end - start) / totalTimeSpan;
|
|
122
|
+
// Calculate start position relative to baseX
|
|
123
|
+
let startX = baseX;
|
|
124
|
+
if (alertStartTimestamp > chartStartTimestamp) {
|
|
125
|
+
const alertStartRelative = (alertStartTimestamp - chartStartTimestamp) / totalTimeSpan;
|
|
126
|
+
startX = baseX + alertStartRelative * availableWidth;
|
|
127
|
+
}
|
|
128
|
+
const width = relativeSize * availableWidth;
|
|
129
|
+
return { startX, width, relativeSize };
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* Maps keyboard events to navigation actions
|
|
133
|
+
*/
|
|
134
|
+
export const getNavigationAction = (key) => {
|
|
135
|
+
switch (key) {
|
|
136
|
+
case 'ArrowLeft':
|
|
137
|
+
case 'ArrowUp':
|
|
138
|
+
return 'previous';
|
|
139
|
+
case 'ArrowRight':
|
|
140
|
+
case 'ArrowDown':
|
|
141
|
+
return 'next';
|
|
142
|
+
case 'Home':
|
|
143
|
+
return 'first';
|
|
144
|
+
case 'End':
|
|
145
|
+
return 'last';
|
|
146
|
+
case 'Escape':
|
|
147
|
+
return 'escape';
|
|
148
|
+
default:
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
/**
|
|
153
|
+
* Calculates new index based on navigation action
|
|
154
|
+
*/
|
|
155
|
+
export const calculateNavigationIndex = (action, currentIndex, arrayLength) => {
|
|
156
|
+
if (arrayLength === 0)
|
|
157
|
+
return -1;
|
|
158
|
+
switch (action) {
|
|
159
|
+
case 'previous':
|
|
160
|
+
return currentIndex <= 0 ? arrayLength - 1 : currentIndex - 1;
|
|
161
|
+
case 'next':
|
|
162
|
+
return currentIndex >= arrayLength - 1 ? 0 : currentIndex + 1;
|
|
163
|
+
case 'first':
|
|
164
|
+
return 0;
|
|
165
|
+
case 'last':
|
|
166
|
+
return arrayLength - 1;
|
|
167
|
+
case 'escape':
|
|
168
|
+
return -1;
|
|
169
|
+
default:
|
|
170
|
+
return currentIndex;
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
/**
|
|
174
|
+
* Gets complete navigation state update for a given action
|
|
175
|
+
*/
|
|
176
|
+
export const getNavigationStateUpdate = (action, currentIndex, alerts) => {
|
|
177
|
+
const newIndex = calculateNavigationIndex(action, currentIndex, alerts.length);
|
|
178
|
+
return {
|
|
179
|
+
newIndex,
|
|
180
|
+
selectedAlert: newIndex >= 0 ? alerts[newIndex] : null,
|
|
181
|
+
shouldActivateKeyboard: action !== 'escape',
|
|
182
|
+
};
|
|
183
|
+
};
|
|
184
|
+
// =============================================================================
|
|
185
|
+
// TOOLTIP UTILS
|
|
186
|
+
// =============================================================================
|
|
187
|
+
/**
|
|
188
|
+
* Calculates tooltip position for an alert based on its time range
|
|
189
|
+
*/
|
|
190
|
+
export const getTooltipPosition = (alert, startTimestamp, endTimestamp, chartUsableWidth) => {
|
|
191
|
+
const alertStart = new Date(alert.startsAt).getTime();
|
|
192
|
+
const alertEnd = new Date(alert.endsAt).getTime();
|
|
193
|
+
const { startX, width } = calculateAlertPosition(alertStart, alertEnd, startTimestamp, endTimestamp, chartUsableWidth, CHART_CONFIG.MARGINS.left);
|
|
194
|
+
// Return center position of the alert
|
|
195
|
+
return startX + width / 2;
|
|
196
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"healthBarUtils.spec.d.ts","sourceRoot":"","sources":["../../../src/lib/components/globalhealthbar/healthBarUtils.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
import { TIME_CONSTANTS, calculateSevenDayTicks, calculateDayTicks, calculateHourTicks, getTicks, getEdgeMargin, calculateLabelVisibility, calculateAlertPosition, getNavigationAction, calculateNavigationIndex, getNavigationStateUpdate, } from './healthBarUtils';
|
|
2
|
+
describe('Health Bar Utils', () => {
|
|
3
|
+
describe('Tick Calculations', () => {
|
|
4
|
+
describe('calculateSevenDayTicks', () => {
|
|
5
|
+
it('should calculate 7 ticks for a week span with proper spacing', () => {
|
|
6
|
+
const endTimestamp = new Date('2023-12-07T12:00:00Z').getTime();
|
|
7
|
+
const ticks = calculateSevenDayTicks(endTimestamp);
|
|
8
|
+
expect(ticks).toHaveLength(7);
|
|
9
|
+
// Check that ticks are spaced by one day
|
|
10
|
+
for (let i = 1; i < ticks.length; i++) {
|
|
11
|
+
expect(ticks[i - 1] - ticks[i]).toBe(TIME_CONSTANTS.ONE_DAY);
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
it('should apply margin hours offset', () => {
|
|
15
|
+
const endTimestamp = new Date('2023-12-07T12:00:00Z').getTime();
|
|
16
|
+
const ticks = calculateSevenDayTicks(endTimestamp);
|
|
17
|
+
// First tick should be less than endTimestamp due to margin
|
|
18
|
+
expect(ticks[0]).toBeLessThan(endTimestamp);
|
|
19
|
+
// The difference should account for margin hours
|
|
20
|
+
const expectedMargin = TIME_CONSTANTS.MARGIN_HOURS * TIME_CONSTANTS.ONE_HOUR;
|
|
21
|
+
expect(endTimestamp - ticks[0]).toBeGreaterThan(expectedMargin);
|
|
22
|
+
});
|
|
23
|
+
it('should round to nearest half day', () => {
|
|
24
|
+
const endTimestamp = new Date('2023-12-07T15:30:45Z').getTime();
|
|
25
|
+
const ticks = calculateSevenDayTicks(endTimestamp);
|
|
26
|
+
const firstTickDate = new Date(ticks[0]);
|
|
27
|
+
// Should be rounded to either 00:00 or 12:00
|
|
28
|
+
expect([0, 12]).toContain(firstTickDate.getUTCHours());
|
|
29
|
+
expect(firstTickDate.getUTCMinutes()).toBe(0);
|
|
30
|
+
expect(firstTickDate.getUTCSeconds()).toBe(0);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
describe('calculateDayTicks', () => {
|
|
34
|
+
it('should calculate 5 ticks when end timestamp aligns with 6-hour boundary', () => {
|
|
35
|
+
const endTimestamp = new Date('2023-12-07T12:00:00Z').getTime();
|
|
36
|
+
const ticks = calculateDayTicks(endTimestamp);
|
|
37
|
+
expect(ticks).toHaveLength(5);
|
|
38
|
+
});
|
|
39
|
+
it('should calculate 4 ticks when end timestamp does not align with 6-hour boundary', () => {
|
|
40
|
+
const endTimestamp = new Date('2023-12-07T13:30:00Z').getTime();
|
|
41
|
+
const ticks = calculateDayTicks(endTimestamp);
|
|
42
|
+
expect(ticks).toHaveLength(4);
|
|
43
|
+
});
|
|
44
|
+
it('should space ticks by 6 hours', () => {
|
|
45
|
+
const endTimestamp = new Date('2023-12-07T12:00:00Z').getTime();
|
|
46
|
+
const ticks = calculateDayTicks(endTimestamp);
|
|
47
|
+
for (let i = 1; i < ticks.length; i++) {
|
|
48
|
+
expect(ticks[i - 1] - ticks[i]).toBe(TIME_CONSTANTS.SIX_HOURS);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
it('should start from closest 6-hour boundary', () => {
|
|
52
|
+
const endTimestamp = new Date('2023-12-07T14:30:00Z').getTime();
|
|
53
|
+
const ticks = calculateDayTicks(endTimestamp);
|
|
54
|
+
const firstTickDate = new Date(ticks[0]);
|
|
55
|
+
expect(firstTickDate.getUTCHours() % 6).toBe(0);
|
|
56
|
+
expect(firstTickDate.getUTCMinutes()).toBe(0);
|
|
57
|
+
expect(firstTickDate.getUTCSeconds()).toBe(0);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
describe('calculateHourTicks', () => {
|
|
61
|
+
it('should calculate 5 ticks when end timestamp aligns with 15-minute boundary', () => {
|
|
62
|
+
const endTimestamp = new Date('2023-12-07T12:15:00Z').getTime();
|
|
63
|
+
const ticks = calculateHourTicks(endTimestamp);
|
|
64
|
+
expect(ticks).toHaveLength(5);
|
|
65
|
+
});
|
|
66
|
+
it('should calculate 4 ticks when end timestamp does not align with 15-minute boundary', () => {
|
|
67
|
+
const endTimestamp = new Date('2023-12-07T12:17:00Z').getTime();
|
|
68
|
+
const ticks = calculateHourTicks(endTimestamp);
|
|
69
|
+
expect(ticks).toHaveLength(4);
|
|
70
|
+
});
|
|
71
|
+
it('should space ticks by 15 minutes', () => {
|
|
72
|
+
const endTimestamp = new Date('2023-12-07T12:15:00Z').getTime();
|
|
73
|
+
const ticks = calculateHourTicks(endTimestamp);
|
|
74
|
+
for (let i = 1; i < ticks.length; i++) {
|
|
75
|
+
expect(ticks[i - 1] - ticks[i]).toBe(TIME_CONSTANTS.FIFTEEN_MINUTES);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
it('should start from closest 15-minute boundary', () => {
|
|
79
|
+
const endTimestamp = new Date('2023-12-07T12:17:30Z').getTime();
|
|
80
|
+
const ticks = calculateHourTicks(endTimestamp);
|
|
81
|
+
const firstTickDate = new Date(ticks[0]);
|
|
82
|
+
// Test time is 12:17 so the first tick should be 12:15
|
|
83
|
+
expect(firstTickDate.getUTCHours()).toBe(12);
|
|
84
|
+
expect(firstTickDate.getUTCMinutes()).toBe(15);
|
|
85
|
+
expect(firstTickDate.getUTCSeconds()).toBe(0);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
describe('getTicks', () => {
|
|
89
|
+
it('should return seven day ticks for week span', () => {
|
|
90
|
+
const startTimestamp = new Date('2023-12-01T00:00:00Z').getTime();
|
|
91
|
+
const endTimestamp = startTimestamp + 7 * TIME_CONSTANTS.ONE_DAY;
|
|
92
|
+
const ticks = getTicks(startTimestamp, endTimestamp);
|
|
93
|
+
expect(ticks).toHaveLength(7);
|
|
94
|
+
});
|
|
95
|
+
it('should return day ticks for 24-hour span', () => {
|
|
96
|
+
const startTimestamp = new Date('2023-12-07T00:00:00Z').getTime();
|
|
97
|
+
const endTimestamp = startTimestamp + 24 * TIME_CONSTANTS.ONE_HOUR;
|
|
98
|
+
const ticks = getTicks(startTimestamp, endTimestamp);
|
|
99
|
+
expect(ticks.length).toBe(5);
|
|
100
|
+
});
|
|
101
|
+
it('should return hour ticks for 1-hour span', () => {
|
|
102
|
+
const startTimestamp = new Date('2023-12-07T12:00:00Z').getTime();
|
|
103
|
+
const endTimestamp = startTimestamp + TIME_CONSTANTS.ONE_HOUR;
|
|
104
|
+
const ticks = getTicks(startTimestamp, endTimestamp);
|
|
105
|
+
expect(ticks.length).toBe(5);
|
|
106
|
+
});
|
|
107
|
+
it('should return empty array for unsupported time spans', () => {
|
|
108
|
+
const startTimestamp = new Date('2023-12-07T12:00:00Z').getTime();
|
|
109
|
+
const endTimestamp = startTimestamp + 2 * TIME_CONSTANTS.ONE_HOUR;
|
|
110
|
+
const ticks = getTicks(startTimestamp, endTimestamp);
|
|
111
|
+
expect(ticks).toEqual([]);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
describe('Edge Margin Calculations', () => {
|
|
116
|
+
describe('getEdgeMargin', () => {
|
|
117
|
+
it('should return -8 for first tick in day span with 5 total ticks', () => {
|
|
118
|
+
const margin = getEdgeMargin(0, 5, true);
|
|
119
|
+
expect(margin).toBe(-8);
|
|
120
|
+
});
|
|
121
|
+
it('should return 8 for last tick in day span with 5 total ticks', () => {
|
|
122
|
+
const margin = getEdgeMargin(4, 5, true);
|
|
123
|
+
expect(margin).toBe(8);
|
|
124
|
+
});
|
|
125
|
+
it('should return 0 for middle ticks in day span with 5 total ticks', () => {
|
|
126
|
+
const margin = getEdgeMargin(2, 5, true);
|
|
127
|
+
expect(margin).toBe(0);
|
|
128
|
+
});
|
|
129
|
+
it('should return 0 for non-day spans', () => {
|
|
130
|
+
const margin = getEdgeMargin(0, 5, false);
|
|
131
|
+
expect(margin).toBe(0);
|
|
132
|
+
});
|
|
133
|
+
it('should return 0 for day span with non-5 total ticks', () => {
|
|
134
|
+
const margin = getEdgeMargin(0, 4, true);
|
|
135
|
+
expect(margin).toBe(0);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
describe('Label Visibility', () => {
|
|
140
|
+
describe('calculateLabelVisibility', () => {
|
|
141
|
+
it('should return true when chart has enough space per tick', () => {
|
|
142
|
+
const chartWidth = 500;
|
|
143
|
+
const totalTicks = 5;
|
|
144
|
+
const span = TIME_CONSTANTS.ONE_DAY;
|
|
145
|
+
const index = 0;
|
|
146
|
+
const endTimestamp = Date.now();
|
|
147
|
+
const visible = calculateLabelVisibility(chartWidth, totalTicks, span, index, endTimestamp);
|
|
148
|
+
expect(visible).toBe(true);
|
|
149
|
+
});
|
|
150
|
+
it('should apply week span rules when space is limited', () => {
|
|
151
|
+
const chartWidth = 200;
|
|
152
|
+
const totalTicks = 7;
|
|
153
|
+
const span = TIME_CONSTANTS.ONE_WEEK;
|
|
154
|
+
const endTimestamp = Date.now();
|
|
155
|
+
expect(calculateLabelVisibility(chartWidth, totalTicks, span, 0, endTimestamp)).toBe(true);
|
|
156
|
+
expect(calculateLabelVisibility(chartWidth, totalTicks, span, 1, endTimestamp)).toBe(false);
|
|
157
|
+
expect(calculateLabelVisibility(chartWidth, totalTicks, span, 2, endTimestamp)).toBe(true);
|
|
158
|
+
});
|
|
159
|
+
it('should apply day span rules when space is limited', () => {
|
|
160
|
+
const chartWidth = 200;
|
|
161
|
+
const totalTicks = 4;
|
|
162
|
+
const span = TIME_CONSTANTS.ONE_DAY;
|
|
163
|
+
const endTimestamp = new Date('2023-12-07T12:10:00Z').getTime();
|
|
164
|
+
expect(calculateLabelVisibility(chartWidth, totalTicks, span, 0, endTimestamp)).toBe(true);
|
|
165
|
+
expect(calculateLabelVisibility(chartWidth, totalTicks, span, 1, endTimestamp)).toBe(false);
|
|
166
|
+
expect(calculateLabelVisibility(chartWidth, totalTicks, span, 2, endTimestamp)).toBe(false);
|
|
167
|
+
expect(calculateLabelVisibility(chartWidth, totalTicks, span, 3, endTimestamp)).toBe(true);
|
|
168
|
+
});
|
|
169
|
+
it('should handle special cases for day span with hour-aligned end timestamp', () => {
|
|
170
|
+
const chartWidth = 200;
|
|
171
|
+
const totalTicks = 5;
|
|
172
|
+
const span = TIME_CONSTANTS.ONE_DAY;
|
|
173
|
+
const endTimestamp = new Date('2023-12-07T12:00:00Z').getTime();
|
|
174
|
+
expect(calculateLabelVisibility(chartWidth, totalTicks, span, 0, endTimestamp)).toBe(true);
|
|
175
|
+
expect(calculateLabelVisibility(chartWidth, totalTicks, span, 2, endTimestamp)).toBe(true);
|
|
176
|
+
expect(calculateLabelVisibility(chartWidth, totalTicks, span, 4, endTimestamp)).toBe(true);
|
|
177
|
+
expect(calculateLabelVisibility(chartWidth, totalTicks, span, 3, endTimestamp)).toBe(false);
|
|
178
|
+
});
|
|
179
|
+
it('should apply day span rules when space is limited for non-round hour', () => {
|
|
180
|
+
const chartWidth = 200;
|
|
181
|
+
const totalTicks = 4;
|
|
182
|
+
const span = TIME_CONSTANTS.ONE_DAY;
|
|
183
|
+
const endTimestamp = new Date('2023-12-07T12:10:00Z').getTime();
|
|
184
|
+
expect(calculateLabelVisibility(chartWidth, totalTicks, span, 0, endTimestamp)).toBe(true);
|
|
185
|
+
expect(calculateLabelVisibility(chartWidth, totalTicks, span, 1, endTimestamp)).toBe(false);
|
|
186
|
+
expect(calculateLabelVisibility(chartWidth, totalTicks, span, 2, endTimestamp)).toBe(false);
|
|
187
|
+
expect(calculateLabelVisibility(chartWidth, totalTicks, span, 3, endTimestamp)).toBe(true);
|
|
188
|
+
});
|
|
189
|
+
it('should handle special cases for hour span with 15-minute aligned end timestamp', () => {
|
|
190
|
+
const chartWidth = 200;
|
|
191
|
+
const totalTicks = 5;
|
|
192
|
+
const span = TIME_CONSTANTS.ONE_HOUR;
|
|
193
|
+
const endTimestamp = new Date('2023-12-07T12:15:00Z').getTime();
|
|
194
|
+
expect(calculateLabelVisibility(chartWidth, totalTicks, span, 0, endTimestamp)).toBe(true);
|
|
195
|
+
expect(calculateLabelVisibility(chartWidth, totalTicks, span, 2, endTimestamp)).toBe(true);
|
|
196
|
+
expect(calculateLabelVisibility(chartWidth, totalTicks, span, 4, endTimestamp)).toBe(true);
|
|
197
|
+
expect(calculateLabelVisibility(chartWidth, totalTicks, span, 3, endTimestamp)).toBe(false);
|
|
198
|
+
});
|
|
199
|
+
it('should return false for unsupported time spans when space is limited', () => {
|
|
200
|
+
const chartWidth = 200;
|
|
201
|
+
const totalTicks = 5;
|
|
202
|
+
const span = 123456;
|
|
203
|
+
const index = 0;
|
|
204
|
+
const endTimestamp = Date.now();
|
|
205
|
+
const visible = calculateLabelVisibility(chartWidth, totalTicks, span, index, endTimestamp);
|
|
206
|
+
expect(visible).toBe(false);
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
describe('Alert Position Calculations', () => {
|
|
211
|
+
describe('calculateAlertPosition', () => {
|
|
212
|
+
it('should calculate position for alert within time range', () => {
|
|
213
|
+
const alertStartTimestamp = 2000;
|
|
214
|
+
const alertEndTimestamp = 3000;
|
|
215
|
+
const chartStartTimestamp = 1000;
|
|
216
|
+
const chartEndTimestamp = 5000;
|
|
217
|
+
const availableWidth = 200;
|
|
218
|
+
const baseX = 50;
|
|
219
|
+
const result = calculateAlertPosition(alertStartTimestamp, alertEndTimestamp, chartStartTimestamp, chartEndTimestamp, availableWidth, baseX);
|
|
220
|
+
const expectedRelativeSize = (3000 - 2000) / (5000 - 1000);
|
|
221
|
+
const expectedWidth = expectedRelativeSize * 200;
|
|
222
|
+
const expectedStartX = 50 + ((2000 - 1000) / (5000 - 1000)) * 200;
|
|
223
|
+
expect(result.width).toBe(expectedWidth);
|
|
224
|
+
expect(result.startX).toBe(expectedStartX);
|
|
225
|
+
expect(result.relativeSize).toBe(expectedRelativeSize);
|
|
226
|
+
});
|
|
227
|
+
it('should handle alert starting before time range', () => {
|
|
228
|
+
const alertStartTimestamp = 1000;
|
|
229
|
+
const alertEndTimestamp = 3000;
|
|
230
|
+
const chartStartTimestamp = 2000;
|
|
231
|
+
const chartEndTimestamp = 5000;
|
|
232
|
+
const availableWidth = 200;
|
|
233
|
+
const baseX = 50;
|
|
234
|
+
const result = calculateAlertPosition(alertStartTimestamp, alertEndTimestamp, chartStartTimestamp, chartEndTimestamp, availableWidth, baseX);
|
|
235
|
+
const expectedRelativeSize = (3000 - 2000) / (5000 - 2000);
|
|
236
|
+
const expectedWidth = expectedRelativeSize * 200;
|
|
237
|
+
const expectedStartX = 50; // baseX since alert starts before chart range
|
|
238
|
+
expect(result.width).toBe(expectedWidth);
|
|
239
|
+
expect(result.startX).toBe(expectedStartX);
|
|
240
|
+
expect(result.relativeSize).toBe(expectedRelativeSize);
|
|
241
|
+
});
|
|
242
|
+
it('should handle alert ending after time range', () => {
|
|
243
|
+
const alertStartTimestamp = 2500;
|
|
244
|
+
const alertEndTimestamp = 6000;
|
|
245
|
+
const chartStartTimestamp = 1000;
|
|
246
|
+
const chartEndTimestamp = 4000;
|
|
247
|
+
const availableWidth = 200;
|
|
248
|
+
const baseX = 50;
|
|
249
|
+
const result = calculateAlertPosition(alertStartTimestamp, alertEndTimestamp, chartStartTimestamp, chartEndTimestamp, availableWidth, baseX);
|
|
250
|
+
const expectedRelativeSize = (4000 - 2500) / (4000 - 1000);
|
|
251
|
+
const expectedWidth = expectedRelativeSize * 200;
|
|
252
|
+
const expectedStartX = 50 + ((2500 - 1000) / (4000 - 1000)) * 200;
|
|
253
|
+
expect(result.width).toBe(expectedWidth);
|
|
254
|
+
expect(result.startX).toBe(expectedStartX);
|
|
255
|
+
expect(result.relativeSize).toBe(expectedRelativeSize);
|
|
256
|
+
});
|
|
257
|
+
it('should handle alert spanning entire time range', () => {
|
|
258
|
+
const alertStartTimestamp = 1000;
|
|
259
|
+
const alertEndTimestamp = 5000;
|
|
260
|
+
const chartStartTimestamp = 2000;
|
|
261
|
+
const chartEndTimestamp = 4000;
|
|
262
|
+
const availableWidth = 200;
|
|
263
|
+
const baseX = 50;
|
|
264
|
+
const result = calculateAlertPosition(alertStartTimestamp, alertEndTimestamp, chartStartTimestamp, chartEndTimestamp, availableWidth, baseX);
|
|
265
|
+
const expectedStartX = 50; // baseX since alert starts before chart range
|
|
266
|
+
expect(result.width).toBe(200);
|
|
267
|
+
expect(result.startX).toBe(expectedStartX);
|
|
268
|
+
expect(result.relativeSize).toBe(1);
|
|
269
|
+
});
|
|
270
|
+
it('should work with default baseX of 0', () => {
|
|
271
|
+
const alertStartTimestamp = 2000;
|
|
272
|
+
const alertEndTimestamp = 3000;
|
|
273
|
+
const chartStartTimestamp = 1000;
|
|
274
|
+
const chartEndTimestamp = 5000;
|
|
275
|
+
const availableWidth = 200;
|
|
276
|
+
const result = calculateAlertPosition(alertStartTimestamp, alertEndTimestamp, chartStartTimestamp, chartEndTimestamp, availableWidth);
|
|
277
|
+
const expectedRelativeSize = (3000 - 2000) / (5000 - 1000);
|
|
278
|
+
const expectedWidth = expectedRelativeSize * 200;
|
|
279
|
+
const expectedStartX = 0 + ((2000 - 1000) / (5000 - 1000)) * 200;
|
|
280
|
+
expect(result.width).toBe(expectedWidth);
|
|
281
|
+
expect(result.startX).toBe(expectedStartX);
|
|
282
|
+
expect(result.relativeSize).toBe(expectedRelativeSize);
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
describe('Keyboard Navigation Utils', () => {
|
|
287
|
+
describe('getNavigationAction', () => {
|
|
288
|
+
it('should map arrow keys to navigation actions', () => {
|
|
289
|
+
expect(getNavigationAction('ArrowLeft')).toBe('previous');
|
|
290
|
+
expect(getNavigationAction('ArrowUp')).toBe('previous');
|
|
291
|
+
expect(getNavigationAction('ArrowRight')).toBe('next');
|
|
292
|
+
expect(getNavigationAction('ArrowDown')).toBe('next');
|
|
293
|
+
});
|
|
294
|
+
it('should map Home and End keys', () => {
|
|
295
|
+
expect(getNavigationAction('Home')).toBe('first');
|
|
296
|
+
expect(getNavigationAction('End')).toBe('last');
|
|
297
|
+
});
|
|
298
|
+
it('should map Escape key', () => {
|
|
299
|
+
expect(getNavigationAction('Escape')).toBe('escape');
|
|
300
|
+
});
|
|
301
|
+
it('should return null for unmapped keys', () => {
|
|
302
|
+
expect(getNavigationAction('Enter')).toBe(null);
|
|
303
|
+
expect(getNavigationAction('Space')).toBe(null);
|
|
304
|
+
expect(getNavigationAction('Tab')).toBe(null);
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
describe('calculateNavigationIndex', () => {
|
|
308
|
+
const arrayLength = 5;
|
|
309
|
+
it('should handle previous navigation', () => {
|
|
310
|
+
expect(calculateNavigationIndex('previous', 2, arrayLength)).toBe(1);
|
|
311
|
+
expect(calculateNavigationIndex('previous', 0, arrayLength)).toBe(4); // wraps to end
|
|
312
|
+
});
|
|
313
|
+
it('should handle next navigation', () => {
|
|
314
|
+
expect(calculateNavigationIndex('next', 2, arrayLength)).toBe(3);
|
|
315
|
+
expect(calculateNavigationIndex('next', 4, arrayLength)).toBe(0); // wraps to start
|
|
316
|
+
});
|
|
317
|
+
it('should handle first navigation', () => {
|
|
318
|
+
expect(calculateNavigationIndex('first', 3, arrayLength)).toBe(0);
|
|
319
|
+
});
|
|
320
|
+
it('should handle last navigation', () => {
|
|
321
|
+
expect(calculateNavigationIndex('last', 1, arrayLength)).toBe(4);
|
|
322
|
+
});
|
|
323
|
+
it('should handle escape navigation', () => {
|
|
324
|
+
expect(calculateNavigationIndex('escape', 2, arrayLength)).toBe(-1);
|
|
325
|
+
});
|
|
326
|
+
it('should return -1 for empty array', () => {
|
|
327
|
+
expect(calculateNavigationIndex('next', 0, 0)).toBe(-1);
|
|
328
|
+
expect(calculateNavigationIndex('previous', 0, 0)).toBe(-1);
|
|
329
|
+
});
|
|
330
|
+
it('should handle single item array', () => {
|
|
331
|
+
expect(calculateNavigationIndex('next', 0, 1)).toBe(0);
|
|
332
|
+
expect(calculateNavigationIndex('previous', 0, 1)).toBe(0);
|
|
333
|
+
expect(calculateNavigationIndex('first', 0, 1)).toBe(0);
|
|
334
|
+
expect(calculateNavigationIndex('last', 0, 1)).toBe(0);
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
describe('getNavigationStateUpdate', () => {
|
|
338
|
+
const mockAlerts = [
|
|
339
|
+
{ id: 1, name: 'Alert 1' },
|
|
340
|
+
{ id: 2, name: 'Alert 2' },
|
|
341
|
+
{ id: 3, name: 'Alert 3' },
|
|
342
|
+
];
|
|
343
|
+
it('should return correct state for next navigation', () => {
|
|
344
|
+
const result = getNavigationStateUpdate('next', 0, mockAlerts);
|
|
345
|
+
expect(result.newIndex).toBe(1);
|
|
346
|
+
expect(result.selectedAlert).toEqual(mockAlerts[1]);
|
|
347
|
+
expect(result.shouldActivateKeyboard).toBe(true);
|
|
348
|
+
});
|
|
349
|
+
it('should return correct state for previous navigation', () => {
|
|
350
|
+
const result = getNavigationStateUpdate('previous', 2, mockAlerts);
|
|
351
|
+
expect(result.newIndex).toBe(1);
|
|
352
|
+
expect(result.selectedAlert).toEqual(mockAlerts[1]);
|
|
353
|
+
expect(result.shouldActivateKeyboard).toBe(true);
|
|
354
|
+
});
|
|
355
|
+
it('should return correct state for first navigation', () => {
|
|
356
|
+
const result = getNavigationStateUpdate('first', 2, mockAlerts);
|
|
357
|
+
expect(result.newIndex).toBe(0);
|
|
358
|
+
expect(result.selectedAlert).toEqual(mockAlerts[0]);
|
|
359
|
+
expect(result.shouldActivateKeyboard).toBe(true);
|
|
360
|
+
});
|
|
361
|
+
it('should return correct state for last navigation', () => {
|
|
362
|
+
const result = getNavigationStateUpdate('last', 0, mockAlerts);
|
|
363
|
+
expect(result.newIndex).toBe(2);
|
|
364
|
+
expect(result.selectedAlert).toEqual(mockAlerts[2]);
|
|
365
|
+
expect(result.shouldActivateKeyboard).toBe(true);
|
|
366
|
+
});
|
|
367
|
+
it('should return correct state for escape navigation', () => {
|
|
368
|
+
const result = getNavigationStateUpdate('escape', 1, mockAlerts);
|
|
369
|
+
expect(result.newIndex).toBe(-1);
|
|
370
|
+
expect(result.selectedAlert).toBe(null);
|
|
371
|
+
expect(result.shouldActivateKeyboard).toBe(false);
|
|
372
|
+
});
|
|
373
|
+
it('should handle empty array', () => {
|
|
374
|
+
const result = getNavigationStateUpdate('next', 0, []);
|
|
375
|
+
expect(result.newIndex).toBe(-1);
|
|
376
|
+
expect(result.selectedAlert).toBe(null);
|
|
377
|
+
expect(result.shouldActivateKeyboard).toBe(true);
|
|
378
|
+
});
|
|
379
|
+
it('should handle wrapping navigation', () => {
|
|
380
|
+
// Next from last index should wrap to first
|
|
381
|
+
const nextResult = getNavigationStateUpdate('next', 2, mockAlerts);
|
|
382
|
+
expect(nextResult.newIndex).toBe(0);
|
|
383
|
+
expect(nextResult.selectedAlert).toEqual(mockAlerts[0]);
|
|
384
|
+
// Previous from first index should wrap to last
|
|
385
|
+
const prevResult = getNavigationStateUpdate('previous', 0, mockAlerts);
|
|
386
|
+
expect(prevResult.newIndex).toBe(2);
|
|
387
|
+
expect(prevResult.selectedAlert).toEqual(mockAlerts[2]);
|
|
388
|
+
});
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface Alert {
|
|
2
|
+
description: string;
|
|
3
|
+
startsAt: string;
|
|
4
|
+
endsAt: string;
|
|
5
|
+
severity: 'warning' | 'critical' | 'unavailable';
|
|
6
|
+
}
|
|
7
|
+
export declare const useHealthBarData: (alerts: Alert[], startTimestamp: number, endTimestamp: number, id: string) => {
|
|
8
|
+
chartData: {
|
|
9
|
+
range: number[];
|
|
10
|
+
}[];
|
|
11
|
+
alertsMap: Record<string, Alert>;
|
|
12
|
+
alertKeys: {
|
|
13
|
+
warningKeys: string[];
|
|
14
|
+
criticalKeys: string[];
|
|
15
|
+
unavailableKeys: string[];
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=useHealthBarData.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useHealthBarData.d.ts","sourceRoot":"","sources":["../../../src/lib/components/globalhealthbar/useHealthBarData.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,KAAK;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,SAAS,GAAG,UAAU,GAAG,aAAa,CAAC;CAClD;AAED,eAAO,MAAM,gBAAgB,WACnB,KAAK,EAAE,kBACC,MAAM,gBACR,MAAM,MAChB,MAAM;;;;;;;;;;CA4DX,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
export const useHealthBarData = (alerts, startTimestamp, endTimestamp, id) => {
|
|
3
|
+
// Filter alerts to only include alerts that are within the start and end timestamp
|
|
4
|
+
const filteredAlerts = useMemo(() => alerts.filter((alert) => (new Date(alert.endsAt).getTime() >= startTimestamp &&
|
|
5
|
+
new Date(alert.startsAt).getTime() <= endTimestamp) ||
|
|
6
|
+
(new Date(alert.startsAt).getTime() <= endTimestamp &&
|
|
7
|
+
new Date(alert.endsAt).getTime() >= startTimestamp)), [alerts, startTimestamp, endTimestamp]);
|
|
8
|
+
// Create chart data and alerts map separately
|
|
9
|
+
const { chartData, alertsMap, alertKeys } = useMemo(() => {
|
|
10
|
+
const alertBars = {};
|
|
11
|
+
const alertsMapData = {};
|
|
12
|
+
filteredAlerts.forEach((alert, index) => {
|
|
13
|
+
// Use alert index with severity to create unique keys for bars dataKey
|
|
14
|
+
// Bars format is: dataKey: [startTimestamp, endTimestamp]
|
|
15
|
+
const uniqueKey = `${alert.severity}_${index}`;
|
|
16
|
+
alertBars[uniqueKey] = [
|
|
17
|
+
new Date(alert.startsAt).getTime(),
|
|
18
|
+
new Date(alert.endsAt).getTime(),
|
|
19
|
+
];
|
|
20
|
+
// Store alert data separately for tooltip access
|
|
21
|
+
alertsMapData[uniqueKey] = {
|
|
22
|
+
...alert,
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
// Chart data - ready for BarChart (as array)
|
|
26
|
+
const chartDataArray = [
|
|
27
|
+
{
|
|
28
|
+
range: [startTimestamp, endTimestamp],
|
|
29
|
+
...alertBars,
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
// Alert keys for bar rendering
|
|
33
|
+
const allKeys = Object.keys(alertBars);
|
|
34
|
+
const alertKeysData = {
|
|
35
|
+
warningKeys: allKeys.filter((key) => key.startsWith('warning')),
|
|
36
|
+
criticalKeys: allKeys.filter((key) => key.startsWith('critical')),
|
|
37
|
+
unavailableKeys: allKeys.filter((key) => key.startsWith('unavailable')),
|
|
38
|
+
};
|
|
39
|
+
return {
|
|
40
|
+
chartData: chartDataArray,
|
|
41
|
+
alertsMap: alertsMapData,
|
|
42
|
+
alertKeys: alertKeysData,
|
|
43
|
+
};
|
|
44
|
+
}, [filteredAlerts, startTimestamp, endTimestamp]);
|
|
45
|
+
return { chartData, alertsMap, alertKeys };
|
|
46
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useHealthBarData.spec.d.ts","sourceRoot":"","sources":["../../../src/lib/components/globalhealthbar/useHealthBarData.spec.tsx"],"names":[],"mappings":""}
|