@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.
Files changed (55) hide show
  1. package/dist/components/barchartv2/Barchart.component.d.ts +4 -5
  2. package/dist/components/barchartv2/Barchart.component.d.ts.map +1 -1
  3. package/dist/components/barchartv2/Barchart.component.js +23 -27
  4. package/dist/components/barchartv2/BarchartTooltip.d.ts +4 -3
  5. package/dist/components/barchartv2/BarchartTooltip.d.ts.map +1 -1
  6. package/dist/components/barchartv2/BarchartTooltip.js +10 -12
  7. package/dist/components/barchartv2/utils.d.ts +6 -1
  8. package/dist/components/barchartv2/utils.d.ts.map +1 -1
  9. package/dist/components/barchartv2/utils.js +34 -8
  10. package/dist/components/chartlegend/ChartLegendWrapper.js +1 -1
  11. package/dist/components/charttooltip/ChartTooltip.d.ts +29 -0
  12. package/dist/components/charttooltip/ChartTooltip.d.ts.map +1 -1
  13. package/dist/components/charttooltip/ChartTooltip.js +105 -1
  14. package/dist/components/date/FormattedDateTime.d.ts +23 -8
  15. package/dist/components/date/FormattedDateTime.d.ts.map +1 -1
  16. package/dist/components/date/FormattedDateTime.js +51 -7
  17. package/dist/components/globalhealthbar/GlobalHealthBarRecharts.component.d.ts.map +1 -1
  18. package/dist/components/globalhealthbar/GlobalHealthBarRecharts.component.js +27 -1
  19. package/dist/components/globalhealthbar/components/GlobalHealthBarTooltip.d.ts +1 -1
  20. package/dist/components/globalhealthbar/components/GlobalHealthBarTooltip.d.ts.map +1 -1
  21. package/dist/components/globalhealthbar/components/GlobalHealthBarTooltip.js +19 -59
  22. package/dist/components/globalhealthbar/components/HealthBarXAxis.js +1 -1
  23. package/dist/components/globalhealthbar/useHealthBarData.d.ts +1 -0
  24. package/dist/components/globalhealthbar/useHealthBarData.d.ts.map +1 -1
  25. package/dist/components/globalhealthbar/useHealthBarData.js +1 -0
  26. package/dist/components/globalhealthbar/useHealthBarData.spec.js +2 -0
  27. package/dist/components/linetimeseriechart/linetimeseriechart.component.d.ts +2 -1
  28. package/dist/components/linetimeseriechart/linetimeseriechart.component.d.ts.map +1 -1
  29. package/dist/components/linetimeseriechart/linetimeseriechart.component.js +45 -47
  30. package/dist/components/linetimeseriechart/utils.d.ts +1 -1
  31. package/dist/components/linetimeseriechart/utils.d.ts.map +1 -1
  32. package/dist/components/linetimeseriechart/utils.js +13 -13
  33. package/dist/style/theme.js +1 -1
  34. package/package.json +1 -1
  35. package/src/lib/components/barchartv2/Barchart.component.test.tsx +23 -25
  36. package/src/lib/components/barchartv2/Barchart.component.tsx +41 -39
  37. package/src/lib/components/barchartv2/BarchartTooltip.test.tsx +33 -3
  38. package/src/lib/components/barchartv2/BarchartTooltip.tsx +33 -24
  39. package/src/lib/components/barchartv2/utils.test.ts +72 -17
  40. package/src/lib/components/barchartv2/utils.ts +40 -12
  41. package/src/lib/components/chartlegend/ChartLegendWrapper.tsx +1 -1
  42. package/src/lib/components/charttooltip/ChartTooltip.tsx +174 -1
  43. package/src/lib/components/date/FormattedDateTime.tsx +73 -8
  44. package/src/lib/components/globalhealthbar/GlobalHealthBarRecharts.component.tsx +56 -11
  45. package/src/lib/components/globalhealthbar/components/GlobalHealthBarTooltip.tsx +75 -117
  46. package/src/lib/components/globalhealthbar/components/HealthBarXAxis.tsx +1 -1
  47. package/src/lib/components/globalhealthbar/useHealthBarData.spec.tsx +2 -0
  48. package/src/lib/components/globalhealthbar/useHealthBarData.ts +2 -0
  49. package/src/lib/components/linetimeseriechart/linetimeseriechart.component.tsx +101 -90
  50. package/src/lib/components/linetimeseriechart/utils.test.ts +30 -68
  51. package/src/lib/components/linetimeseriechart/utils.ts +13 -17
  52. package/src/lib/style/theme.ts +1 -1
  53. package/stories/BarChart/barchart.stories.tsx +23 -8
  54. package/stories/formattedate.stories.tsx +2 -0
  55. package/stories/linetimeseriechart.stories.tsx +1 -0
@@ -3,7 +3,7 @@ import { getDateDaysDiff } from './dateDiffer';
3
3
  import { Tooltip } from '../tooltip/Tooltip.component';
4
4
  /**
5
5
  * @description Long date formatter, with weekday, year, month and day. Used for describing long term date.
6
- * @example Wednesday 6 October 2025
6
+ * @example Wednesday 06 October 2025
7
7
  */
8
8
  export const LONG_DATE_FORMATER = Intl.DateTimeFormat('en-GB', {
9
9
  weekday: 'long',
@@ -32,7 +32,7 @@ export const DATE_FORMATER = Intl.DateTimeFormat('fr-CA', {
32
32
  });
33
33
  /**
34
34
  * @description Day month formatter, with weekday, day and month. Used for describing long term date.
35
- * @example Wed 6 Oct
35
+ * @example Wed 06 Oct
36
36
  */
37
37
  export const DAY_MONTH_FORMATER = Intl.DateTimeFormat('en-GB', {
38
38
  weekday: 'short',
@@ -58,9 +58,28 @@ export const TIME_FORMATER = Intl.DateTimeFormat('en-GB', {
58
58
  hour: '2-digit',
59
59
  minute: '2-digit',
60
60
  });
61
+ /**
62
+ * @description Day month abbreviated formatter. Used for describing long term date.
63
+ * @example 06 Oct
64
+ */
65
+ export const DAY_MONTH_ABBREVIATED = Intl.DateTimeFormat('en-GB', {
66
+ day: '2-digit',
67
+ month: 'short',
68
+ hour12: false,
69
+ });
70
+ /**
71
+ * @description Day month abbreviated formatter. Used for describing long term date.
72
+ * @example 06 Oct 25
73
+ */
74
+ export const DAY_MONTH_ABBREVIATED_YEAR = Intl.DateTimeFormat('en-GB', {
75
+ day: '2-digit',
76
+ month: 'short',
77
+ year: '2-digit',
78
+ hour12: false,
79
+ });
61
80
  /**
62
81
  * @description Day month abbreviated hour minute second formatter. Used for describing long term date.
63
- * @example 6 Oct 18:33:00
82
+ * @example 06 Oct 18:33:00
64
83
  */
65
84
  export const DAY_MONTH_ABBREVIATED_HOUR_MINUTE_SECOND = Intl.DateTimeFormat('en-GB', {
66
85
  day: '2-digit',
@@ -72,7 +91,7 @@ export const DAY_MONTH_ABBREVIATED_HOUR_MINUTE_SECOND = Intl.DateTimeFormat('en-
72
91
  });
73
92
  /**
74
93
  * @description Day month abbreviated hour minute formatter. Used for describing long term date.
75
- * @example 6 Oct 18:33
94
+ * @example 06 Oct 18:33
76
95
  */
77
96
  export const DAY_MONTH_ABBREVIATED_HOUR_MINUTE = Intl.DateTimeFormat('en-GB', {
78
97
  day: '2-digit',
@@ -81,6 +100,18 @@ export const DAY_MONTH_ABBREVIATED_HOUR_MINUTE = Intl.DateTimeFormat('en-GB', {
81
100
  minute: '2-digit',
82
101
  hour12: false,
83
102
  });
103
+ /**
104
+ * @description Day month abbreviated year hour minute formatter. Used for describing long term date.
105
+ * @example 06 Oct 2025 18:33
106
+ */
107
+ export const DAY_MONTH_ABBREVIATED_YEAR_HOUR_MINUTE = Intl.DateTimeFormat('en-GB', {
108
+ day: '2-digit',
109
+ month: 'short',
110
+ year: 'numeric',
111
+ hour: '2-digit',
112
+ minute: '2-digit',
113
+ hour12: false,
114
+ });
84
115
  /**
85
116
  * @description Year month day formatter, without time. Used for describing long term date.
86
117
  * @example 2025-01-01
@@ -115,10 +146,10 @@ const isItFutureOrIsItPast = (timeDiff, stringToBeFormatted) => {
115
146
  * time: '00:00'
116
147
  * 'time-second': '00:00:00'
117
148
  * relative: '1 month ago'
118
- * 'day-month-abbreviated-hour-minute': '6 Oct 18:33'
119
- * 'day-month-abbreviated-hour-minute-second': '6 Oct 18:33:00'
149
+ * 'day-month-abbreviated-hour-minute': '06 Oct 18:33'
150
+ * 'day-month-abbreviated-hour-minute-second': '06 Oct 18:33:00'
120
151
  * 'long-date': 'Wednesday 6 October 2025'
121
- * 'chart-date': '6 Oct'
152
+ * 'chart-date': '06 Oct'
122
153
  * 'year-month-day': '2025-10-06'
123
154
  */
124
155
  export const FormattedDateTime = ({ format, value, }) => {
@@ -176,6 +207,19 @@ export const FormattedDateTime = ({ format, value, }) => {
176
207
  return _jsx(_Fragment, { children: YEAR_MONTH_DAY_FORMATTER.format(value) });
177
208
  case 'month-day':
178
209
  return _jsx(_Fragment, { children: MONTH_DAY_FORMATTER.format(value) });
210
+ case 'day-month-abbreviated-year-hour-minute':
211
+ return (_jsx(_Fragment, { children: DAY_MONTH_ABBREVIATED_YEAR_HOUR_MINUTE.format(value)
212
+ .replace(',', '')
213
+ .replace(/Sept/g, 'Sep') }));
214
+ case 'day-month-abbreviated':
215
+ return (_jsx(_Fragment, { children: DAY_MONTH_ABBREVIATED.format(value)
216
+ .replace(',', '')
217
+ .replace(/Sept/g, 'Sep') }));
218
+ case 'chart-long-term-date':
219
+ return (_jsx(_Fragment, { children: DAY_MONTH_ABBREVIATED_YEAR.format(value)
220
+ .replace(/[ ,]/g, '')
221
+ // replace Sept with Sep to keep 3 letter month
222
+ .replace(/Sept/g, 'Sep') }));
179
223
  default:
180
224
  return _jsx(_Fragment, {});
181
225
  }
@@ -1 +1 @@
1
- {"version":3,"file":"GlobalHealthBarRecharts.component.d.ts","sourceRoot":"","sources":["../../../src/lib/components/globalhealthbar/GlobalHealthBarRecharts.component.tsx"],"names":[],"mappings":"AAiBA,OAAO,EAAE,KAAK,EAAoB,MAAM,oBAAoB,CAAC;AAE7D,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,KAAK,EAAE,IAAI,CAAC;IACZ,GAAG,EAAE,IAAI,CAAC;CACX;AAMD,wBAAgB,eAAe,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,iBAAiB,2CAyK5E;AAGD,YAAY,EAAE,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"GlobalHealthBarRecharts.component.d.ts","sourceRoot":"","sources":["../../../src/lib/components/globalhealthbar/GlobalHealthBarRecharts.component.tsx"],"names":[],"mappings":"AAiBA,OAAO,EAAE,KAAK,EAAoB,MAAM,oBAAoB,CAAC;AAE7D,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,KAAK,EAAE,IAAI,CAAC;IACZ,GAAG,EAAE,IAAI,CAAC;CACX;AAUD,wBAAgB,eAAe,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,iBAAiB,2CAkN5E;AAGD,YAAY,EAAE,KAAK,EAAE,CAAC"}
@@ -8,11 +8,16 @@ import { CHART_CONFIG, getNavigationAction, getNavigationStateUpdate, } from './
8
8
  import { useHealthBarData } from './useHealthBarData';
9
9
  const ChartInteractiveContainer = styled.div `
10
10
  position: relative;
11
+ outline: none;
12
+ .recharts-surface {
13
+ outline: none;
14
+ }
11
15
  `;
12
16
  export function GlobalHealthBar({ id, alerts, start, end }) {
13
17
  const [tooltipData, setTooltipData] = useState(null);
14
18
  const [focusedAlertIndex, setFocusedAlertIndex] = useState(-1);
15
19
  const [keyboardActive, setKeyboardActive] = useState(false);
20
+ const [activeBarKey, setActiveBarKey] = useState(null);
16
21
  const chartContainerRef = useRef(null);
17
22
  const theme = useTheme();
18
23
  const startTimestamp = new Date(start).getTime();
@@ -20,10 +25,12 @@ export function GlobalHealthBar({ id, alerts, start, end }) {
20
25
  const { chartData, alertsMap, alertKeys } = useHealthBarData(alerts, startTimestamp, endTimestamp, id);
21
26
  const handlePointerEnter = useCallback((key) => {
22
27
  setTooltipData(alertsMap[key]);
28
+ setActiveBarKey(key);
23
29
  }, [alertsMap]);
24
30
  const handlePointerLeave = useCallback(() => {
25
31
  if (!keyboardActive) {
26
32
  setTooltipData(null);
33
+ setActiveBarKey(null);
27
34
  }
28
35
  }, [keyboardActive]);
29
36
  const { warningKeys, criticalKeys, unavailableKeys } = alertKeys;
@@ -43,6 +50,10 @@ export function GlobalHealthBar({ id, alerts, start, end }) {
43
50
  setFocusedAlertIndex(update.newIndex);
44
51
  setTooltipData(update.selectedAlert);
45
52
  setKeyboardActive(update.shouldActivateKeyboard);
53
+ // Set active bar key for keyboard navigation
54
+ if (update.selectedAlert) {
55
+ setActiveBarKey(update.selectedAlert.key);
56
+ }
46
57
  }, [allAlertKeys, focusedAlertIndex]);
47
58
  // Handle focus events
48
59
  const handleFocus = useCallback(() => {
@@ -50,12 +61,15 @@ export function GlobalHealthBar({ id, alerts, start, end }) {
50
61
  setFocusedAlertIndex(0);
51
62
  setTooltipData(allAlertKeys[0]);
52
63
  setKeyboardActive(true);
64
+ // Set active bar key for initial focus
65
+ setActiveBarKey(allAlertKeys[0].key);
53
66
  }
54
67
  }, [allAlertKeys, focusedAlertIndex]);
55
68
  const handleBlur = useCallback(() => {
56
69
  setKeyboardActive(false);
57
70
  setFocusedAlertIndex(-1);
58
71
  setTooltipData(null);
72
+ setActiveBarKey(null);
59
73
  }, []);
60
74
  // Handle mouse enter to disable keyboard mode
61
75
  const handleMouseEnter = useCallback(() => {
@@ -74,5 +88,17 @@ export function GlobalHealthBar({ id, alerts, start, end }) {
74
88
  position: 'fixed',
75
89
  }, content: (props) => {
76
90
  return (_jsx(GlobalHealthBarTooltip, { tooltipData: tooltipData, tooltipProps: props, chartContainerRef: chartContainerRef, isKeyboardActive: keyboardActive, startTimestamp: startTimestamp, endTimestamp: endTimestamp }));
77
- } }), _jsx(YAxis, { yAxisId: 'background', type: "category", hide: true }), allAlertBars.map(({ key }) => (_jsx(YAxis, { yAxisId: key, type: "category", hide: true }, `yAxis${key}`))), _jsx(Bar, { dataKey: "range", fill: theme.statusHealthy, radius: CHART_CONFIG.RADIUS_SIZE, yAxisId: "background", isAnimationActive: false }), allAlertBars.map(({ key, fill }) => (_jsx(Bar, { dataKey: key, yAxisId: key, fill: fill, onPointerEnter: () => handlePointerEnter(key), onPointerLeave: () => handlePointerLeave(), isAnimationActive: false })))] }) }) }));
91
+ } }), _jsx(YAxis, { yAxisId: 'background', type: "category", hide: true }), allAlertBars.map(({ key }) => (_jsx(YAxis, { yAxisId: key, type: "category", hide: true }, `yAxis${key}`))), _jsx(Bar, { dataKey: "range", fill: theme.statusHealthy, radius: CHART_CONFIG.RADIUS_SIZE, yAxisId: "background", isAnimationActive: false }), allAlertBars.map(({ key, fill }) => {
92
+ const isActive = key === activeBarKey;
93
+ // Skip active bar here - it will be rendered last
94
+ if (isActive)
95
+ return null;
96
+ return (_jsx(Bar, { dataKey: key, yAxisId: key, fill: fill, onPointerEnter: () => handlePointerEnter(key), onPointerLeave: () => handlePointerLeave(), isAnimationActive: false }, key));
97
+ }), activeBarKey &&
98
+ (() => {
99
+ const activeBar = allAlertBars.find((bar) => bar.key === activeBarKey);
100
+ if (!activeBar)
101
+ return null;
102
+ return (_jsx(Bar, { dataKey: activeBar.key, yAxisId: activeBar.key, fill: activeBar.fill, stroke: theme.selectedActive, onPointerEnter: () => handlePointerEnter(activeBar.key), onPointerLeave: () => handlePointerLeave(), isAnimationActive: false }, `${activeBar.key}-active`));
103
+ })()] }) }) }));
78
104
  }
@@ -13,6 +13,6 @@ interface GlobalHealthBarTooltipProps {
13
13
  startTimestamp?: number;
14
14
  endTimestamp?: number;
15
15
  }
16
- export declare const GlobalHealthBarTooltip: (props: GlobalHealthBarTooltipProps) => React.ReactPortal | null;
16
+ export declare const GlobalHealthBarTooltip: (props: GlobalHealthBarTooltipProps) => import("react/jsx-runtime").JSX.Element | null;
17
17
  export {};
18
18
  //# sourceMappingURL=GlobalHealthBarTooltip.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"GlobalHealthBarTooltip.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/globalhealthbar/components/GlobalHealthBarTooltip.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAY1B,OAAO,EAAE,KAAK,EAAE,MAAM,sCAAsC,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAI/C,UAAU,2BAA2B;IACnC,WAAW,EAAE,KAAK,GAAG,IAAI,CAAC;IAC1B,UAAU,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACtC,YAAY,EAAE,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClD,iBAAiB,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IACnD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAoBD,eAAO,MAAM,sBAAsB,UAAW,2BAA2B,6BA+HxE,CAAC"}
1
+ {"version":3,"file":"GlobalHealthBarTooltip.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/globalhealthbar/components/GlobalHealthBarTooltip.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,EAAE,KAAK,EAAE,MAAM,sCAAsC,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAK/C,UAAU,2BAA2B;IACnC,WAAW,EAAE,KAAK,GAAG,IAAI,CAAC;IAC1B,UAAU,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACtC,YAAY,EAAE,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClD,iBAAiB,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IACnD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAoBD,eAAO,MAAM,sBAAsB,UAAW,2BAA2B,mDA6FxE,CAAC"}
@@ -1,11 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { createPortal } from 'react-dom';
3
- import { useEffect, useState } from 'react';
4
2
  import styled, { css, useTheme } from 'styled-components';
5
- import { useFloating, autoUpdate, offset, flip, shift, } from '@floating-ui/react';
6
3
  import { FormattedDateTime, Stack, Text, Wrap, spacing } from '../../../index';
7
4
  import { zIndex } from '../../../style/theme';
8
5
  import { CHART_CONFIG, getTooltipPosition } from '../healthBarUtils';
6
+ import { ChartTooltipPortal } from '../../charttooltip/ChartTooltip';
9
7
  const TooltipContainer = styled.div `
10
8
  ${(props) => {
11
9
  const theme = useTheme();
@@ -25,28 +23,11 @@ const TooltipContainer = styled.div `
25
23
  export const GlobalHealthBarTooltip = (props) => {
26
24
  const { tooltipData, tooltipProps, chartContainerRef, isKeyboardActive = false, startTimestamp = 0, endTimestamp = 0, } = props;
27
25
  const { coordinate } = tooltipProps;
28
- const [virtualElement, setVirtualElement] = useState(null);
29
- const { refs, floatingStyles } = useFloating({
30
- elements: {
31
- reference: virtualElement,
32
- },
33
- middleware: [
34
- offset(({ placement }) => {
35
- // Use larger offset when tooltip is on top
36
- // to avoid tooltip over bar
37
- return placement.includes('top') ? 20 : 30;
38
- }),
39
- flip(),
40
- shift({ padding: 10 }),
41
- ],
42
- whileElementsMounted: autoUpdate,
43
- });
44
- // Create virtual element from coordinate
45
- useEffect(() => {
46
- if (chartContainerRef.current) {
47
- const chartRect = chartContainerRef.current.getBoundingClientRect();
48
- let tooltipX;
49
- let tooltipY;
26
+ if (!tooltipData)
27
+ return null;
28
+ const { description, startsAt, endsAt, severity } = tooltipData;
29
+ const tooltipContent = (_jsxs(Stack, { direction: "vertical", gap: "r8", children: [_jsxs(Wrap, { children: [_jsx(Text, { variant: "Smaller", children: "Severity" }), _jsx(Text, { color: "textPrimary", variant: "Smaller", children: severity })] }), _jsxs(Wrap, { children: [_jsx(Text, { variant: "Smaller", children: "Start" }), _jsx(Text, { color: "textPrimary", variant: "Smaller", children: _jsx(FormattedDateTime, { format: "date-time", value: new Date(startsAt) }) })] }), _jsxs(Wrap, { children: [_jsx(Text, { variant: "Smaller", children: "End" }), _jsx(Text, { color: "textPrimary", variant: "Smaller", children: _jsx(FormattedDateTime, { format: "date-time", value: new Date(endsAt) }) })] }), _jsxs(Wrap, { children: [_jsx(Text, { variant: "Smaller", style: { paddingRight: spacing.r32 }, children: "Description" }), _jsx(Text, { color: "textPrimary", variant: "Smaller", style: { whiteSpace: 'wrap', textAlign: 'justify' }, children: description })] })] }));
30
+ return (_jsx(ChartTooltipPortal, { coordinate: coordinate, chartContainerRef: chartContainerRef, isVisible: !!tooltipData, customPosition: (chartRect, coordinate) => {
50
31
  if (isKeyboardActive && tooltipData && startTimestamp && endTimestamp) {
51
32
  // Calculate the chart's usable width (excluding margins)
52
33
  const chartUsableWidth = chartRect.width -
@@ -54,42 +35,21 @@ export const GlobalHealthBarTooltip = (props) => {
54
35
  CHART_CONFIG.MARGINS.right;
55
36
  // Use the same positioning logic as alert bars
56
37
  const alertCenterX = getTooltipPosition(tooltipData, startTimestamp, endTimestamp, chartUsableWidth);
57
- // Position tooltip at the center of the alert's time span
58
- // alertCenterX already includes the margin offset, so just add chartRect.left
59
- tooltipX = chartRect.left + alertCenterX;
60
- tooltipY = chartRect.top + CHART_CONFIG.BAR_SIZE;
38
+ return {
39
+ x: chartRect.left + alertCenterX,
40
+ y: chartRect.top + CHART_CONFIG.BAR_SIZE,
41
+ };
61
42
  }
62
43
  else {
63
44
  // For mouse navigation, use the provided coordinate
64
- tooltipX = chartRect.left + (coordinate === null || coordinate === void 0 ? void 0 : coordinate.x);
65
- tooltipY = chartRect.top + (coordinate === null || coordinate === void 0 ? void 0 : coordinate.y);
45
+ return {
46
+ x: chartRect.left + ((coordinate === null || coordinate === void 0 ? void 0 : coordinate.x) || 0),
47
+ y: chartRect.top + ((coordinate === null || coordinate === void 0 ? void 0 : coordinate.y) || 0),
48
+ };
66
49
  }
67
- setVirtualElement({
68
- getBoundingClientRect() {
69
- return {
70
- width: 0,
71
- height: 0,
72
- x: tooltipX,
73
- y: tooltipY,
74
- left: tooltipX,
75
- top: tooltipY,
76
- right: tooltipX,
77
- bottom: tooltipY,
78
- };
79
- },
80
- });
81
- }
82
- }, [
83
- coordinate,
84
- chartContainerRef,
85
- isKeyboardActive,
86
- tooltipData,
87
- startTimestamp,
88
- endTimestamp,
89
- ]);
90
- if (!tooltipData)
91
- return null;
92
- const { description, startsAt, endsAt, severity } = tooltipData;
93
- const tooltipContent = (_jsx(TooltipContainer, { ref: refs.setFloating, style: floatingStyles, children: _jsxs(Stack, { direction: "vertical", gap: "r8", children: [_jsxs(Wrap, { children: [_jsx(Text, { variant: "Smaller", children: "Severity" }), _jsx(Text, { color: "textPrimary", variant: "Smaller", children: severity })] }), _jsxs(Wrap, { children: [_jsx(Text, { variant: "Smaller", children: "Start" }), _jsx(Text, { color: "textPrimary", variant: "Smaller", children: _jsx(FormattedDateTime, { format: "date-time", value: new Date(startsAt) }) })] }), _jsxs(Wrap, { children: [_jsx(Text, { variant: "Smaller", children: "End" }), _jsx(Text, { color: "textPrimary", variant: "Smaller", children: _jsx(FormattedDateTime, { format: "date-time", value: new Date(endsAt) }) })] }), _jsxs(Wrap, { children: [_jsx(Text, { variant: "Smaller", style: { paddingRight: spacing.r32 }, children: "Description" }), _jsx(Text, { color: "textPrimary", variant: "Smaller", style: { whiteSpace: 'wrap', textAlign: 'justify' }, children: description })] })] }) }));
94
- return createPortal(tooltipContent, document.body);
50
+ }, containerComponent: TooltipContainer, offset: ({ placement }) => {
51
+ // Use larger offset when tooltip is on top
52
+ // to avoid tooltip over bar
53
+ return placement.includes('top') ? 20 : 30;
54
+ }, children: tooltipContent }));
95
55
  };
@@ -14,7 +14,7 @@ const CustomTick = ({ tickProps, startTimestamp, endTimestamp, }) => {
14
14
  const edgeMargin = getEdgeMargin(index, visibleTicksCount, isDaySpan);
15
15
  return (
16
16
  // use coordinate to center the text
17
- shouldShowLabel && (_jsx("g", { transform: `translate(${payload.coordinate},${y})`, children: _jsx("text", { textAnchor: "middle", dy: 10, dx: edgeMargin, fontSize: fontSize.smaller, fill: theme.textSecondary, children: is7DaySpan || isDaySpan ? (_jsx(FormattedDateTime, { format: "day-month-abbreviated-hour-minute", value: new Date(payload.value) })) : (_jsx(FormattedDateTime, { format: "time", value: new Date(payload.value) })) }) })));
17
+ shouldShowLabel && (_jsx("g", { transform: `translate(${payload.coordinate},${y})`, children: _jsx("text", { textAnchor: "middle", dy: 10, dx: edgeMargin, fontSize: fontSize.smaller, fill: theme.textSecondary, children: is7DaySpan ? (_jsx(FormattedDateTime, { format: "day-month-abbreviated-hour-minute", value: new Date(payload.value) })) : (_jsx(FormattedDateTime, { format: "time", value: new Date(payload.value) })) }) })));
18
18
  };
19
19
  export const HealthBarXAxis = ({ startTimestamp, endTimestamp, }) => {
20
20
  const theme = useTheme();
@@ -3,6 +3,7 @@ export interface Alert {
3
3
  startsAt: string;
4
4
  endsAt: string;
5
5
  severity: 'warning' | 'critical' | 'unavailable';
6
+ key: string;
6
7
  }
7
8
  export declare const useHealthBarData: (alerts: Alert[], startTimestamp: number, endTimestamp: number, id: string) => {
8
9
  chartData: {
@@ -1 +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"}
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;IACjD,GAAG,EAAE,MAAM,CAAC;CACb;AAED,eAAO,MAAM,gBAAgB,WACnB,KAAK,EAAE,kBACC,MAAM,gBACR,MAAM,MAChB,MAAM;;;;;;;;;;CA6DX,CAAC"}
@@ -20,6 +20,7 @@ export const useHealthBarData = (alerts, startTimestamp, endTimestamp, id) => {
20
20
  // Store alert data separately for tooltip access
21
21
  alertsMapData[uniqueKey] = {
22
22
  ...alert,
23
+ key: uniqueKey, // Add the consistent key to the alert object
23
24
  };
24
25
  });
25
26
  // Chart data - ready for BarChart (as array)
@@ -10,6 +10,7 @@ describe('useHealthBarData', () => {
10
10
  severity,
11
11
  startsAt,
12
12
  endsAt,
13
+ key: `${severity}_${startsAt}`,
13
14
  });
14
15
  describe('Alert Filtering', () => {
15
16
  it('should include alerts that are completely within the time range', () => {
@@ -93,6 +94,7 @@ describe('useHealthBarData', () => {
93
94
  severity: 'warning',
94
95
  startsAt: '2023-12-01T02:00:00Z',
95
96
  endsAt: '2023-12-01T04:00:00Z',
97
+ key: 'warning_0',
96
98
  });
97
99
  });
98
100
  it('should handle multiple alerts with correct indexing', () => {
@@ -1,4 +1,5 @@
1
1
  import { TooltipContentProps } from 'recharts';
2
+ import React from 'react';
2
3
  export type Serie = {
3
4
  resource: string;
4
5
  data: [number, number | string | null][];
@@ -37,7 +38,7 @@ export type LineChartProps = (NonSymmetricalChartSerie | SymmetricalChartSerie)
37
38
  timeFormat?: 'date-time' | 'date';
38
39
  yAxisTitle?: string;
39
40
  helpText?: string;
40
- renderTooltip?: (tooltipProps: TooltipContentProps<number, string>, unitLabel?: string, timeFormat?: 'date-time' | 'date') => React.ReactNode;
41
+ renderTooltip?: (tooltipProps: TooltipContentProps<number, string>, unitLabel?: string, duration?: number) => React.ReactNode;
41
42
  };
42
43
  export declare function LineTimeSerieChart({ series, title, height, startingTimeStamp, interval, duration, unitRange, isLoading, timeFormat, yAxisType, yAxisTitle, helpText, syncId, renderTooltip, ...rest }: LineChartProps): import("react/jsx-runtime").JSX.Element;
43
44
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"linetimeseriechart.component.d.ts","sourceRoot":"","sources":["../../../src/lib/components/linetimeseriechart/linetimeseriechart.component.tsx"],"names":[],"mappings":"AAAA,OAAO,EAML,mBAAmB,EAGpB,MAAM,UAAU,CAAC;AAqClB,MAAM,MAAM,KAAK,GAAG;IAElB,QAAQ,EAAE,MAAM,CAAC;IAEjB,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;IAEzC,eAAe,EAAE,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAEtE,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,KAAK,wBAAwB,GAAG;IAC9B,SAAS,CAAC,EAAE,SAAS,GAAG,YAAY,CAAC;IACrC,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC;CAC7B,CAAC;AAGF,KAAK,qBAAqB,GAAG;IAC3B,SAAS,EAAE,aAAa,CAAC;IACzB,MAAM,EACF;QACE,KAAK,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC;QAC3B,KAAK,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC;KAC5B,GACD,SAAS,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,CACzB,wBAAwB,GACxB,qBAAqB,CACxB,GAAG;IACF,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE;QACV,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC;KACf,EAAE,CAAC;IACJ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,CACd,YAAY,EAAE,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,EACjD,SAAS,CAAC,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE,WAAW,GAAG,MAAM,KAC9B,KAAK,CAAC,SAAS,CAAC;CACtB,CAAC;AA6FF,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,KAAK,EACL,MAAM,EACN,iBAAiB,EACjB,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,SAAiB,EACjB,UAAwB,EACxB,SAAqB,EACrB,UAAU,EACV,QAAQ,EACR,MAAM,EACN,aAAa,EACb,GAAG,IAAI,EACR,EAAE,cAAc,2CA2WhB"}
1
+ {"version":3,"file":"linetimeseriechart.component.d.ts","sourceRoot":"","sources":["../../../src/lib/components/linetimeseriechart/linetimeseriechart.component.tsx"],"names":[],"mappings":"AAAA,OAAO,EAML,mBAAmB,EAGpB,MAAM,UAAU,CAAC;AAClB,OAAO,KAAiD,MAAM,OAAO,CAAC;AAiCtE,MAAM,MAAM,KAAK,GAAG;IAElB,QAAQ,EAAE,MAAM,CAAC;IAEjB,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;IAEzC,eAAe,EAAE,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAEtE,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,KAAK,wBAAwB,GAAG;IAC9B,SAAS,CAAC,EAAE,SAAS,GAAG,YAAY,CAAC;IACrC,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC;CAC7B,CAAC;AAGF,KAAK,qBAAqB,GAAG;IAC3B,SAAS,EAAE,aAAa,CAAC;IACzB,MAAM,EACF;QACE,KAAK,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC;QAC3B,KAAK,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC;KAC5B,GACD,SAAS,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,CACzB,wBAAwB,GACxB,qBAAqB,CACxB,GAAG;IACF,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE;QACV,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC;KACf,EAAE,CAAC;IACJ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,CACd,YAAY,EAAE,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,EACjD,SAAS,CAAC,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,MAAM,KACd,KAAK,CAAC,SAAS,CAAC;CACtB,CAAC;AAiHF,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,KAAK,EACL,MAAM,EACN,iBAAiB,EACjB,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,SAAiB,EACjB,UAAwB,EACxB,SAAqB,EACrB,UAAU,EACV,QAAQ,EACR,MAAM,EACN,aAAa,EACb,GAAG,IAAI,EACR,EAAE,cAAc,2CAqWhB"}
@@ -1,60 +1,58 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { CartesianGrid, Line, LineChart, ReferenceLine, Tooltip, XAxis, YAxis, } from 'recharts';
3
- import { useCallback, useMemo, useRef, useState } from 'react';
3
+ import React, { useCallback, useMemo, useRef, useState } from 'react';
4
4
  import styled, { useTheme } from 'styled-components';
5
- import { spacing } from '../../spacing';
5
+ import { Stack } from '../../spacing';
6
6
  import { fontSize } from '../../style/theme';
7
- import { Box } from '../box/Box';
8
7
  import { useChartLegend } from '../chartlegend/ChartLegendWrapper';
9
- import { FormattedDateTime } from '../date/FormattedDateTime';
10
- import { Icon } from '../icon/Icon.component';
11
8
  import { addMissingDataPoint, getUnitLabel, } from '../linetemporalchart/ChartUtil';
12
9
  import { Loader } from '../loader/Loader.component';
13
- import { ChartTitleText, SmallerText } from '../text/Text.component';
14
- import { Tooltip as TooltipComponent } from '../tooltip/Tooltip.component';
10
+ import { ChartTitleText } from '../text/Text.component';
15
11
  import { formatXAxisLabel } from './utils';
16
- import { ChartTooltipContainer, ChartTooltipItem, ChartTooltipHeader, ChartTooltipItemsContainer, } from '../charttooltip/ChartTooltip';
12
+ import { ChartTooltipPortal, ChartTooltipItem, ChartTooltipHeader, ChartTooltipItemsContainer, ChartTooltipSeparator, TooltipHeader, } from '../charttooltip/ChartTooltip';
17
13
  import { LegendShape } from '../chartlegend/ChartLegend';
18
14
  import { StyledResponsiveContainer } from '../barchartv2/Barchart.component';
15
+ import { getRoundReferenceValue, getTicks } from '../barchartv2/utils';
16
+ import { IconHelp } from '../iconhelper/IconHelper';
17
+ const maxWidthTooltip = { maxWidth: '20rem' };
19
18
  const LineTemporalChartWrapper = styled.div `
20
19
  display: flex;
21
20
  flex-direction: column;
22
21
  justify-content: flex-start;
23
22
  `;
24
- const ChartHeader = styled.div `
25
- display: flex;
26
- align-items: center;
27
- `;
28
- const LineTimeSerieChartTooltip = ({ unitLabel, timeFormat, isChartActive, tooltipProps, renderTooltip, hoveredValue, }) => {
29
- const { active, payload, label } = tooltipProps;
23
+ const LineTimeSerieChartTooltip = ({ unitLabel, duration, isChartActive, tooltipProps, renderTooltip, hoveredValue, isSymmetrical, chartContainerRef, }) => {
24
+ const { active, payload, label, coordinate } = tooltipProps;
30
25
  if (!active || !payload || !payload.length || !label || !isChartActive)
31
26
  return null;
32
- if (renderTooltip) {
33
- return renderTooltip(tooltipProps, unitLabel, timeFormat);
34
- }
35
- // We can't use the default itemSorter method because it's a custom tooltip.
36
- // Sort the payload here instead
37
- const sortedPayload = [...payload].sort((a, b) => {
38
- const aValue = a.value;
39
- const bValue = b.value;
40
- if (aValue >= 0 && bValue >= 0) {
41
- return bValue - aValue; // Higher positive values first
42
- }
43
- if (aValue < 0 && bValue < 0) {
44
- return bValue - aValue; // Lower negative values first
45
- }
46
- return bValue - aValue; // Positives before negatives
47
- });
48
- return (_jsxs(ChartTooltipContainer, { children: [_jsx(ChartTooltipHeader, { children: _jsx(FormattedDateTime, { format: timeFormat === 'date-time'
49
- ? 'day-month-abbreviated-hour-minute-second'
50
- : 'long-date-without-weekday', value: new Date(label) }) }), _jsx(ChartTooltipItemsContainer, { children: sortedPayload.map((entry, index) => {
51
- const legendIcon = (_jsx(LegendShape, { color: entry.color, shape: "line", chartColors: { [entry.color]: entry.color } }));
52
- const isHovered = entry.name === hoveredValue;
53
- const formattedValue = !Number.isFinite(entry.value)
54
- ? '-'
55
- : `${entry.value.toFixed(2)} ${unitLabel}`;
56
- return (_jsx(ChartTooltipItem, { label: entry.name, value: formattedValue, legendIcon: legendIcon, isHovered: isHovered }, index));
57
- }) })] }));
27
+ const tooltipContent = renderTooltip ? (renderTooltip(tooltipProps, unitLabel, duration)) : (_jsxs(_Fragment, { children: [_jsx(ChartTooltipHeader, { children: _jsx(TooltipHeader, { duration: duration, value: label }) }), _jsx(ChartTooltipItemsContainer, { children: (() => {
28
+ // We can't use the default itemSorter method because it's a custom tooltip.
29
+ // Sort the payload here instead
30
+ const sortedPayload = [...payload].sort((a, b) => {
31
+ const aValue = a.value;
32
+ const bValue = b.value;
33
+ if (aValue >= 0 && bValue >= 0) {
34
+ return bValue - aValue; // Higher positive values first
35
+ }
36
+ if (aValue < 0 && bValue < 0) {
37
+ return bValue - aValue; // Lower negative values first
38
+ }
39
+ return bValue - aValue; // Positives before negatives
40
+ });
41
+ // Find the transition point between positive and negative values
42
+ const separatorIndex = sortedPayload.findIndex((entry) => entry.value < 0);
43
+ const hasBothPositiveAndNegative = separatorIndex > 0 && separatorIndex < sortedPayload.length;
44
+ return sortedPayload.map((entry, index) => {
45
+ const legendIcon = (_jsx(LegendShape, { color: entry.color, shape: "line", chartColors: { [entry.color]: entry.color } }));
46
+ const isHovered = entry.name === hoveredValue;
47
+ const formattedValue = !Number.isFinite(entry.value)
48
+ ? '-'
49
+ : `${entry.value.toFixed(2)} ${unitLabel}`;
50
+ return (_jsxs(React.Fragment, { children: [isSymmetrical &&
51
+ hasBothPositiveAndNegative &&
52
+ index === separatorIndex && _jsx(ChartTooltipSeparator, {}), _jsx(ChartTooltipItem, { label: entry.name, value: formattedValue, legendIcon: legendIcon, isHovered: isHovered })] }, index));
53
+ });
54
+ })() })] }));
55
+ return (_jsx(ChartTooltipPortal, { coordinate: coordinate, chartContainerRef: chartContainerRef, isVisible: active && isChartActive, children: tooltipContent }));
58
56
  };
59
57
  const isSymmetricalSeries = (series) => {
60
58
  return 'above' in series && 'below' in series;
@@ -170,7 +168,8 @@ export function LineTimeSerieChart({ series, title, height, startingTimeStamp, i
170
168
  const bottom = Math.abs(Math.min(...values));
171
169
  const maxValue = Math.max(top, bottom);
172
170
  const { valueBase, unitLabel } = getUnitLabel(unitRange !== null && unitRange !== void 0 ? unitRange : [], maxValue);
173
- const topValue = Math.ceil(maxValue / valueBase / 10) * 10;
171
+ // Use round reference value to add extra padding to the top value
172
+ const topValue = getRoundReferenceValue(maxValue / valueBase);
174
173
  const rechartsData = chartData.map((dataPoint) => {
175
174
  const normalizedDataPoint = { ...dataPoint };
176
175
  Object.entries(dataPoint).forEach(([key, value]) => {
@@ -219,16 +218,15 @@ export function LineTimeSerieChart({ series, title, height, startingTimeStamp, i
219
218
  };
220
219
  }, [series, getColor, selectedResources]);
221
220
  // Format time for display the tick in the x axis
222
- const formatXAxisLabelCallback = useCallback((timestamp) => formatXAxisLabel(timestamp, timeFormat, chartData), [timeFormat, chartData]);
223
- return (_jsxs(LineTemporalChartWrapper, { children: [_jsxs(ChartHeader, { children: [_jsxs(ChartTitleText, { children: [title, " ", unitLabel && `(${unitLabel})`] }), helpText && (_jsx(Box, { ml: spacing.r4, children: _jsx(TooltipComponent, { placement: 'right', overlay: _jsx(SmallerText, { children: helpText }), children: _jsx(Icon, { name: "Info", color: theme.buttonSecondary }) }) })), isLoading && _jsx(Loader, {})] }), _jsx("div", { onFocus: () => setIsChartActive(true), onBlur: () => setIsChartActive(false), onFocusCapture: () => setIsChartActive(true), onBlurCapture: () => setIsChartActive(false), children: _jsx(StyledResponsiveContainer, { width: "100%", height: height, children: _jsxs(LineChart, { data: rechartsData, ref: chartRef, margin: { top: 0, right: 0, bottom: 0, left: 0 }, "aria-label": `Time series chart for ${title}`, syncId: syncId, onMouseEnter: () => setIsChartActive(true), onMouseLeave: () => setIsChartActive(false), accessibilityLayer: true, children: [_jsx(CartesianGrid, { vertical: true, horizontal: true, verticalPoints: [0], horizontalPoints: [0], stroke: theme.border, fill: theme.backgroundLevel4, strokeWidth: 1 }), _jsx(XAxis, { dataKey: "timestamp", type: "number", domain: ['dataMin', 'dataMax'], ticks: xAxisTicks, tickFormatter: formatXAxisLabelCallback, tickCount: 5, tick: {
221
+ const formatXAxisLabelCallback = useCallback((timestamp) => formatXAxisLabel(timestamp, duration), [duration]);
222
+ return (_jsxs(LineTemporalChartWrapper, { children: [_jsxs(Stack, { gap: "r4", children: [_jsxs(ChartTitleText, { children: [title, " ", unitLabel && `(${unitLabel})`] }), helpText && (_jsx(IconHelp, { tooltipMessage: helpText, overlayStyle: maxWidthTooltip })), isLoading && _jsx(Loader, {})] }), _jsx("div", { onFocus: () => setIsChartActive(true), onBlur: () => setIsChartActive(false), onFocusCapture: () => setIsChartActive(true), onBlurCapture: () => setIsChartActive(false), children: _jsx(StyledResponsiveContainer, { width: "100%", height: height, children: _jsxs(LineChart, { data: rechartsData, ref: chartRef, margin: { top: 0, right: 0, bottom: 0, left: 0 }, "aria-label": `Time series chart for ${title}`, syncId: syncId, onMouseEnter: () => setIsChartActive(true), onMouseLeave: () => setIsChartActive(false), accessibilityLayer: true, children: [_jsx(CartesianGrid, { vertical: true, horizontal: true, verticalPoints: [0], horizontalPoints: [0], stroke: theme.border, fill: theme.backgroundLevel4, strokeWidth: 1 }), _jsx(XAxis, { dataKey: "timestamp", type: "number", domain: ['dataMin', 'dataMax'], ticks: xAxisTicks, tickFormatter: formatXAxisLabelCallback, tickCount: 5, tick: {
224
223
  fill: theme.textSecondary,
225
224
  fontSize: fontSize.smaller,
226
225
  }, axisLine: { stroke: theme.border } }), _jsx(YAxis, { orientation: "right", label: {
227
226
  value: yAxisTitle,
228
227
  angle: 90,
229
- position: 'insideRight',
228
+ dx: 20,
230
229
  style: {
231
- textAnchor: 'middle',
232
230
  fill: theme.textSecondary,
233
231
  fontSize: fontSize.smaller,
234
232
  },
@@ -239,7 +237,7 @@ export function LineTimeSerieChart({ series, title, height, startingTimeStamp, i
239
237
  : [0, topValue], axisLine: { stroke: theme.border }, tick: {
240
238
  fill: theme.textSecondary,
241
239
  fontSize: fontSize.smaller,
242
- }, tickFormatter: (value) => new Intl.NumberFormat('fr-FR').format(value.toFixed(0)), tickCount: 5, interval: 0 }), _jsx(Tooltip, { content: (props) => (_jsx(LineTimeSerieChartTooltip, { unitLabel: unitLabel, timeFormat: timeFormat, renderTooltip: renderTooltip, tooltipProps: props, isChartActive: isChartActive, hoveredValue: hoveredValue })) }), yAxisType === 'symmetrical' && (_jsx(ReferenceLine, { y: 0, stroke: theme.border })), Object.entries(groupedSeries).map(([resource, resourceSeries]) => resourceSeries.map((serie, serieIndex) => {
240
+ }, tickFormatter: (value) => new Intl.NumberFormat('fr-FR').format(value), ticks: getTicks(topValue, yAxisType === 'symmetrical'), interval: 0 }), _jsx(Tooltip, { content: (props) => (_jsx(LineTimeSerieChartTooltip, { unitLabel: unitLabel, duration: duration, renderTooltip: renderTooltip, isSymmetrical: yAxisType === 'symmetrical', tooltipProps: props, isChartActive: isChartActive, hoveredValue: hoveredValue, chartContainerRef: chartRef })) }), yAxisType === 'symmetrical' && (_jsx(ReferenceLine, { y: 0, stroke: theme.border })), Object.entries(groupedSeries).map(([resource, resourceSeries]) => resourceSeries.map((serie, serieIndex) => {
243
241
  const label = serie.getTooltipLabel(serie.metricPrefix, serie.resource);
244
242
  return (_jsx(Line, { type: "monotone", dataKey: label, stroke: colorMapping[resource], dot: false, isAnimationActive: false, strokeDasharray: serie.isLineDashed ? '4 4' : undefined, onMouseEnter: () => setHoveredValue(label), onMouseLeave: () => setHoveredValue(undefined) }, `${title}-${resource}-${serieIndex}`));
245
243
  }))] }) }) })] }));
@@ -12,5 +12,5 @@ export type ChartDataPoint = {
12
12
  * @param chartData - The chart data to determine time range for optimal formatting
13
13
  * @returns Formatted string for display on X-axis
14
14
  */
15
- export declare const formatXAxisLabel: (timestamp: number, timeFormat?: "date-time" | "date", chartData?: ChartDataPoint[]) => string;
15
+ export declare const formatXAxisLabel: (timestamp: number, duration: number) => string;
16
16
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/lib/components/linetimeseriechart/utils.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,qBAAqB,QAA4B,CAAC;AAE/D,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;AAElC;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,cAChB,MAAM,eACL,WAAW,GAAG,MAAM,cACrB,cAAc,EAAE,KAC1B,MAgBF,CAAC"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/lib/components/linetimeseriechart/utils.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,qBAAqB,QAA4B,CAAC;AAE/D,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;AAElC;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,cAChB,MAAM,YACP,MAAM,KACf,MAaF,CAAC"}
@@ -1,4 +1,4 @@
1
- import { DAY_MONTH_ABBREVIATED_HOUR_MINUTE, YEAR_MONTH_DAY_FORMATTER, MONTH_DAY_FORMATTER, } from '../date/FormattedDateTime';
1
+ import { TIME_FORMATER, DAY_MONTH_ABBREVIATED_YEAR, DAY_MONTH_ABBREVIATED_HOUR_MINUTE, } from '../date/FormattedDateTime';
2
2
  export const ONE_YEAR_MILLISECONDS = 366 * 24 * 60 * 60 * 1000;
3
3
  /**
4
4
  * Formats timestamp for X-axis labels based on time format and data range:
@@ -10,19 +10,19 @@ export const ONE_YEAR_MILLISECONDS = 366 * 24 * 60 * 60 * 1000;
10
10
  * @param chartData - The chart data to determine time range for optimal formatting
11
11
  * @returns Formatted string for display on X-axis
12
12
  */
13
- export const formatXAxisLabel = (timestamp, timeFormat = 'date-time', chartData = []) => {
13
+ export const formatXAxisLabel = (timestamp, duration) => {
14
14
  const date = new Date(timestamp);
15
- if (!chartData.length) {
16
- return YEAR_MONTH_DAY_FORMATTER.format(date);
15
+ if (duration <= 24 * 60 * 60) {
16
+ return TIME_FORMATER.format(date);
17
17
  }
18
- if (timeFormat === 'date-time') {
19
- return DAY_MONTH_ABBREVIATED_HOUR_MINUTE.format(date).replace(',', '');
18
+ else if (duration <= 7 * 24 * 60 * 60) {
19
+ return DAY_MONTH_ABBREVIATED_HOUR_MINUTE.format(date)
20
+ .replace(',', '')
21
+ .replace(/Sept/g, 'Sep');
22
+ }
23
+ else {
24
+ return DAY_MONTH_ABBREVIATED_YEAR.format(date)
25
+ .replace(/[ ,]/g, '')
26
+ .replace(/Sept/g, 'Sep');
20
27
  }
21
- const timestamps = chartData.map((d) => d.timestamp);
22
- const minTimestamp = Math.min(...timestamps);
23
- const maxTimestamp = Math.max(...timestamps);
24
- const timeRangeMilliseconds = maxTimestamp - minTimestamp;
25
- return timeRangeMilliseconds >= ONE_YEAR_MILLISECONDS
26
- ? YEAR_MONTH_DAY_FORMATTER.format(date)
27
- : MONTH_DAY_FORMATTER.format(date);
28
28
  };
@@ -33,7 +33,7 @@ export const coreUIAvailableThemesNames = [
33
33
  'darkRebrand',
34
34
  'artescaLight',
35
35
  'ring9dark',
36
- 'G-Dark'
36
+ 'G-Dark',
37
37
  ];
38
38
  export const coreUIAvailableThemes = {
39
39
  darkRebrand: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scality/core-ui",
3
- "version": "0.175.0",
3
+ "version": "0.177.0",
4
4
  "description": "Scality common React component library",
5
5
  "author": "Scality Engineering",
6
6
  "license": "SEE LICENSE IN LICENSE",