@scality/core-ui 0.168.0 → 0.170.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 (47) hide show
  1. package/.github/workflows/github-pages.yml +5 -3
  2. package/.storybook/preview.js +1 -0
  3. package/dist/components/barchartv2/Barchart.component.d.ts.map +1 -1
  4. package/dist/components/barchartv2/Barchart.component.js +6 -5
  5. package/dist/components/barchartv2/ChartTooltip.d.ts +9 -13
  6. package/dist/components/barchartv2/ChartTooltip.d.ts.map +1 -1
  7. package/dist/components/barchartv2/ChartTooltip.js +14 -4
  8. package/dist/components/barchartv2/utils.d.ts +9 -2
  9. package/dist/components/barchartv2/utils.d.ts.map +1 -1
  10. package/dist/components/barchartv2/utils.js +12 -16
  11. package/dist/components/buttonv2/Buttonv2.component.d.ts.map +1 -1
  12. package/dist/components/buttonv2/Buttonv2.component.js +27 -6
  13. package/dist/components/date/FormattedDateTime.d.ts +54 -1
  14. package/dist/components/date/FormattedDateTime.d.ts.map +1 -1
  15. package/dist/components/date/FormattedDateTime.js +78 -6
  16. package/dist/components/date/FormattedDateTime.spec.js +12 -3
  17. package/dist/components/layout/v2/index.d.ts +2 -1
  18. package/dist/components/layout/v2/index.d.ts.map +1 -1
  19. package/dist/components/layout/v2/index.js +3 -3
  20. package/dist/components/linetimeseriechart/linetimeseriechart.component.d.ts.map +1 -1
  21. package/dist/components/linetimeseriechart/linetimeseriechart.component.js +30 -32
  22. package/dist/components/linetimeseriechart/utils.d.ts +16 -0
  23. package/dist/components/linetimeseriechart/utils.d.ts.map +1 -0
  24. package/dist/components/linetimeseriechart/utils.js +28 -0
  25. package/dist/style/theme.d.ts +2 -2
  26. package/dist/style/theme.d.ts.map +1 -1
  27. package/dist/style/theme.js +26 -0
  28. package/package.json +5 -4
  29. package/src/lib/components/barchartv2/Barchart.component.test.tsx +1 -1
  30. package/src/lib/components/barchartv2/Barchart.component.tsx +14 -6
  31. package/src/lib/components/barchartv2/ChartTooltip.test.tsx +119 -0
  32. package/src/lib/components/barchartv2/ChartTooltip.tsx +49 -19
  33. package/src/lib/components/barchartv2/utils.test.ts +29 -44
  34. package/src/lib/components/barchartv2/utils.ts +22 -29
  35. package/src/lib/components/buttonv2/Buttonv2.component.tsx +27 -6
  36. package/src/lib/components/date/FormattedDateTime.spec.tsx +27 -2
  37. package/src/lib/components/date/FormattedDateTime.tsx +89 -10
  38. package/src/lib/components/layout/v2/index.tsx +5 -3
  39. package/src/lib/components/linetimeseriechart/linetimeseriechart.component.tsx +61 -57
  40. package/src/lib/components/linetimeseriechart/linetimeseriechart.test.tsx +58 -55
  41. package/src/lib/components/linetimeseriechart/utils.test.ts +87 -0
  42. package/src/lib/components/linetimeseriechart/utils.ts +43 -0
  43. package/src/lib/style/theme.ts +26 -0
  44. package/stories/BarChart/barchart.stories.tsx +1 -1
  45. package/stories/color.mdx +12 -0
  46. package/stories/formattedate.stories.tsx +7 -0
  47. package/stories/layout.stories.tsx +19 -0
@@ -12,7 +12,9 @@ import { spacing } from '../../spacing';
12
12
  import { getUnitLabel } from '../linetemporalchart/ChartUtil';
13
13
  import { Icon } from '../icon/Icon.component';
14
14
  import { Tooltip as TooltipComponent } from '../tooltip/Tooltip.component';
15
- import { DAY_MONTH_ABBREVIATED_HOUR_MINUTE, FormattedDateTime, YEAR_MONTH_DAY_FORMATTER, } from '../date/FormattedDateTime';
15
+ import { FormattedDateTime } from '../date/FormattedDateTime';
16
+ import { Box } from '../box/Box';
17
+ import { formatXAxisLabel } from './utils';
16
18
  const LineTemporalChartWrapper = styled.div `
17
19
  display: flex;
18
20
  flex-direction: column;
@@ -44,6 +46,8 @@ const TooltipValue = styled.div `
44
46
  color: ${(props) => props.theme.textSecondary};
45
47
  display: flex;
46
48
  align-items: flex-start;
49
+ justify-content: space-between;
50
+ width: 100%;
47
51
  `;
48
52
  const TooltipLegend = styled.div `
49
53
  width: 12px;
@@ -53,19 +57,21 @@ const TooltipLegend = styled.div `
53
57
  flex-shrink: 0;
54
58
  margin-top: 8px;
55
59
  `;
56
- const TooltipContent = styled.div `
60
+ const TooltipLeftGroup = styled.div `
57
61
  display: flex;
62
+ align-items: flex-start;
58
63
  min-width: 0;
59
64
  flex: 1;
60
65
  `;
61
66
  const TooltipName = styled.div `
62
- margin-right: 4px;
63
67
  word-wrap: break-word;
64
68
  word-break: break-word;
65
- justify-content: flex-start;
69
+ flex: 1;
66
70
  `;
67
71
  const TooltipInstanceValue = styled.div `
68
- justify-content: flex-end;
72
+ margin-left: 16px;
73
+ flex-shrink: 0;
74
+ text-align: right;
69
75
  `;
70
76
  const CustomTooltip = ({ active, payload, label, unitLabel, timeFormat, }) => {
71
77
  if (!active || !payload || !payload.length || !label)
@@ -73,8 +79,8 @@ const CustomTooltip = ({ active, payload, label, unitLabel, timeFormat, }) => {
73
79
  // We can't use the default itemSorter method because it's a custom tooltip.
74
80
  // Sort the payload here instead
75
81
  const sortedPayload = [...payload].sort((a, b) => {
76
- const aValue = Number(a.value);
77
- const bValue = Number(b.value);
82
+ const aValue = a.value;
83
+ const bValue = b.value;
78
84
  if (aValue >= 0 && bValue >= 0) {
79
85
  return bValue - aValue; // Higher positive values first
80
86
  }
@@ -85,9 +91,9 @@ const CustomTooltip = ({ active, payload, label, unitLabel, timeFormat, }) => {
85
91
  });
86
92
  return (_jsxs(TooltipContainer, { children: [_jsx(TooltipTime, { children: _jsx(FormattedDateTime, { format: timeFormat === 'date-time'
87
93
  ? 'day-month-abbreviated-hour-minute-second'
88
- : 'long-date', value: new Date(label) }) }), sortedPayload.map((entry, index) => (_jsxs(TooltipValue, { children: [_jsx(TooltipLegend, { color: entry.color }), _jsxs(TooltipContent, { children: [_jsx(TooltipName, { children: entry.name }), _jsx(TooltipInstanceValue, { children: isNaN(Number(entry.value))
89
- ? '-'
90
- : `${Number(entry.value).toFixed(2)}${unitLabel}` })] })] }, index)))] }));
94
+ : 'long-date-without-weekday', value: new Date(label) }) }), sortedPayload.map((entry, index) => (_jsxs(TooltipValue, { children: [_jsxs(TooltipLeftGroup, { children: [_jsx(TooltipLegend, { color: entry.color }), _jsx(TooltipName, { children: entry.name })] }), _jsx(TooltipInstanceValue, { children: !Number.isFinite(entry.value)
95
+ ? '-'
96
+ : `${entry.value.toFixed(2)} ${unitLabel}` })] }, index)))] }));
91
97
  };
92
98
  const isSymmetricalSeries = (series) => {
93
99
  return 'above' in series && 'below' in series;
@@ -137,25 +143,24 @@ export function LineTimeSerieChart({ series, title, height, startingTimeStamp, i
137
143
  // Convert object to array for Recharts
138
144
  return Object.values(dataPointsByTime).sort((a, b) => a.timestamp - b.timestamp);
139
145
  }, [series, startingTimeStamp, duration, interval, yAxisType]);
140
- // Calculate 5 perfectly evenly spaced ticks
146
+ // Calculate evenly spaced ticks that avoid the very beginning and end
141
147
  const xAxisTicks = useMemo(() => {
142
148
  if (!chartData || chartData.length === 0)
143
149
  return [];
144
150
  const timestamps = chartData.map((d) => d.timestamp);
145
151
  const minTimestamp = Math.min(...timestamps);
146
152
  const maxTimestamp = Math.max(...timestamps);
147
- // Calculate 5 perfectly evenly spaced ticks
148
153
  const timeRange = maxTimestamp - minTimestamp;
149
- const interval = timeRange / 4; // 4 intervals create 5 points
150
- const exactEvenTicks = [
151
- minTimestamp,
152
- minTimestamp + interval,
153
- minTimestamp + interval * 2,
154
- minTimestamp + interval * 3,
155
- maxTimestamp,
156
- ];
157
- // Return perfectly even ticks (guaranteed to be evenly divided)
158
- return exactEvenTicks;
154
+ // Add padding to avoid labels at the very edges (10% padding on each side)
155
+ const padding = timeRange * 0.1;
156
+ const paddedStart = minTimestamp + padding;
157
+ const paddedEnd = maxTimestamp - padding;
158
+ const paddedRange = paddedEnd - paddedStart;
159
+ // Create 5 evenly spaced ticks within the padded range
160
+ const numTicks = 5;
161
+ const tickInterval = paddedRange / (numTicks - 1);
162
+ const evenlySpacedTicks = Array.from({ length: numTicks }, (_, index) => paddedStart + index * tickInterval);
163
+ return evenlySpacedTicks;
159
164
  }, [chartData]);
160
165
  // 3. Transform the data base on the valuebase
161
166
  const { topValue, unitLabel, rechartsData } = useMemo(() => {
@@ -219,15 +224,8 @@ export function LineTimeSerieChart({ series, title, height, startingTimeStamp, i
219
224
  };
220
225
  }, [series, getColor]);
221
226
  // Format time for display the tick in the x axis
222
- const formatXAxisLabel = useCallback((timestamp) => {
223
- const date = new Date(timestamp);
224
- return timeFormat === 'date-time'
225
- ? DAY_MONTH_ABBREVIATED_HOUR_MINUTE.format(date).replace(',', '')
226
- : timeFormat === 'date'
227
- ? YEAR_MONTH_DAY_FORMATTER.format(date)
228
- : '';
229
- }, [timeFormat]);
230
- return (_jsxs(LineTemporalChartWrapper, { children: [_jsxs(ChartHeader, { children: [_jsxs(ChartTitleText, { children: [title, " ", unitLabel && `(${unitLabel})`] }), helpText && (_jsx(TooltipComponent, { placement: 'right', overlay: _jsx(SmallerText, { children: helpText }), children: _jsx(Icon, { name: "Info", color: theme.buttonSecondary }) })), isLoading && _jsx(Loader, {})] }), _jsx(ResponsiveContainer, { 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}`, 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: formatXAxisLabel, tickCount: 5, tick: {
227
+ const formatXAxisLabelCallback = useCallback((timestamp) => formatXAxisLabel(timestamp, timeFormat, chartData), [timeFormat, chartData]);
228
+ 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(ResponsiveContainer, { 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}`, 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: {
231
229
  fill: theme.textSecondary,
232
230
  fontSize: fontSize.smaller,
233
231
  }, axisLine: { stroke: theme.border } }), _jsx(YAxis, { orientation: "right", allowDataOverflow: false, label: {
@@ -246,7 +244,7 @@ export function LineTimeSerieChart({ series, title, height, startingTimeStamp, i
246
244
  : [0, topValue], axisLine: { stroke: theme.border }, tick: {
247
245
  fill: theme.textSecondary,
248
246
  fontSize: fontSize.smaller,
249
- }, tickFormatter: (value) => Math.round(value).toString() }), _jsx(Tooltip, { content: _jsx(CustomTooltip, { unitLabel: unitLabel, timeFormat: timeFormat }) }), yAxisType === 'symmetrical' && (_jsx(ReferenceLine, { y: 0, stroke: theme.border })), Object.entries(groupedSeries).map(([resource, resourceSeries]) => resourceSeries.map((serie, serieIndex) => {
247
+ }, tickFormatter: (value) => Math.round(value).toString(), tickCount: 5, interval: 'preserveStartEnd' }), _jsx(Tooltip, { content: _jsx(CustomTooltip, { unitLabel: unitLabel, timeFormat: timeFormat }) }), yAxisType === 'symmetrical' && (_jsx(ReferenceLine, { y: 0, stroke: theme.border })), Object.entries(groupedSeries).map(([resource, resourceSeries]) => resourceSeries.map((serie, serieIndex) => {
250
248
  const label = serie.getTooltipLabel(serie.metricPrefix, serie.resource);
251
249
  return (_jsx(Line, { type: "monotone", dataKey: label, stroke: colorMapping[resource], dot: false, isAnimationActive: false }, `${title}-${resource}-${serieIndex}`));
252
250
  }))] }) })] }));
@@ -0,0 +1,16 @@
1
+ export declare const ONE_YEAR_MILLISECONDS: number;
2
+ export type ChartDataPoint = {
3
+ timestamp: number;
4
+ } & Record<string, number | null>;
5
+ /**
6
+ * Formats timestamp for X-axis labels based on time format and data range:
7
+ * For 'date-time' format, return day-month-abbreviated-hour-minute format
8
+ * For 'date' format, return YYYY-MM-DD format if time range is greater than 1 year, otherwise return MM-DD format
9
+ *
10
+ * @param timestamp - The timestamp to format in milliseconds
11
+ * @param timeFormat - The format type ('date-time' or 'date')
12
+ * @param chartData - The chart data to determine time range for optimal formatting
13
+ * @returns Formatted string for display on X-axis
14
+ */
15
+ export declare const formatXAxisLabel: (timestamp: number, timeFormat?: "date-time" | "date", chartData?: ChartDataPoint[]) => string;
16
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,28 @@
1
+ import { DAY_MONTH_ABBREVIATED_HOUR_MINUTE, YEAR_MONTH_DAY_FORMATTER, MONTH_DAY_FORMATTER, } from '../date/FormattedDateTime';
2
+ export const ONE_YEAR_MILLISECONDS = 366 * 24 * 60 * 60 * 1000;
3
+ /**
4
+ * Formats timestamp for X-axis labels based on time format and data range:
5
+ * For 'date-time' format, return day-month-abbreviated-hour-minute format
6
+ * For 'date' format, return YYYY-MM-DD format if time range is greater than 1 year, otherwise return MM-DD format
7
+ *
8
+ * @param timestamp - The timestamp to format in milliseconds
9
+ * @param timeFormat - The format type ('date-time' or 'date')
10
+ * @param chartData - The chart data to determine time range for optimal formatting
11
+ * @returns Formatted string for display on X-axis
12
+ */
13
+ export const formatXAxisLabel = (timestamp, timeFormat = 'date-time', chartData = []) => {
14
+ const date = new Date(timestamp);
15
+ if (!chartData.length) {
16
+ return YEAR_MONTH_DAY_FORMATTER.format(date);
17
+ }
18
+ if (timeFormat === 'date-time') {
19
+ return DAY_MONTH_ABBREVIATED_HOUR_MINUTE.format(date).replace(',', '');
20
+ }
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
+ };
@@ -52,7 +52,7 @@ export type CoreUITheme = {
52
52
  textReverse: string;
53
53
  textLink: string;
54
54
  };
55
- export declare const coreUIAvailableThemesNames: readonly ["darkRebrand", "artescaLight", "ring9dark"];
55
+ export declare const coreUIAvailableThemesNames: readonly ["darkRebrand", "artescaLight", "ring9dark", "G-Dark"];
56
56
  export type CoreUIThemeName = (typeof coreUIAvailableThemesNames)[number];
57
57
  export declare const coreUIAvailableThemes: Record<CoreUIThemeName, CoreUITheme>;
58
58
  /**
@@ -62,7 +62,7 @@ export declare const coreUIAvailableThemes: Record<CoreUIThemeName, CoreUITheme>
62
62
  *
63
63
  * import { coreUIAvailableThemes } from '@scality/core-ui/dist/style/theme';
64
64
  */
65
- export declare const defaultTheme: Record<"darkRebrand" | "artescaLight" | "ring9dark", CoreUITheme>;
65
+ export declare const defaultTheme: Record<"darkRebrand" | "artescaLight" | "ring9dark" | "G-Dark", CoreUITheme>;
66
66
  /**
67
67
  * @deprecated
68
68
  * Instead should use useTheme()
@@ -1 +1 @@
1
- {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/lib/style/theme.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/lib/style/theme.ts"],"names":[],"mappings":"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"}
@@ -33,6 +33,7 @@ export const coreUIAvailableThemesNames = [
33
33
  'darkRebrand',
34
34
  'artescaLight',
35
35
  'ring9dark',
36
+ 'G-Dark'
36
37
  ];
37
38
  export const coreUIAvailableThemes = {
38
39
  darkRebrand: {
@@ -110,6 +111,31 @@ export const coreUIAvailableThemes = {
110
111
  textReverse: '#000000',
111
112
  textLink: '#71AEFF',
112
113
  },
114
+ 'G-Dark': {
115
+ statusHealthy: '#0AADA6',
116
+ statusHealthyRGB: '10,173,166',
117
+ statusWarning: '#F8F32B',
118
+ statusWarningRGB: '248,243,43',
119
+ statusCritical: '#E84855',
120
+ statusCriticalRGB: '232,72,85',
121
+ selectedActive: '#037AFF',
122
+ highlight: '#1A3C75',
123
+ border: '#4A4A4A',
124
+ buttonPrimary: 'linear-gradient(130deg, #9355E7 0%, #2E4AA3 60%)',
125
+ buttonSecondary: 'linear-gradient(130deg, #595A78 0%, #44455F 100%)',
126
+ buttonDelete: '#3D0808',
127
+ infoPrimary: '#8E8EAC',
128
+ infoSecondary: '#333366',
129
+ backgroundLevel1: '#121219',
130
+ backgroundLevel2: '#323245',
131
+ backgroundLevel3: '#232331',
132
+ backgroundLevel4: '#1B1B27',
133
+ textPrimary: '#EAEAEA',
134
+ textSecondary: '#B5B5B5',
135
+ textTertiary: '#DFDFDF',
136
+ textReverse: '#000000',
137
+ textLink: '#71AEFF',
138
+ },
113
139
  };
114
140
  /**
115
141
  * @deprecated
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scality/core-ui",
3
- "version": "0.168.0",
3
+ "version": "0.170.0",
4
4
  "description": "Scality common React component library",
5
5
  "author": "Scality Engineering",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -22,7 +22,7 @@
22
22
  "test": "jest",
23
23
  "storybook": "storybook dev -p 3000",
24
24
  "build-storybook": "storybook build",
25
- "storybook:deploy": "storybook-to-ghpages"
25
+ "storybook:deploy": "npm run build-storybook && gh-pages -d storybook-static -u \"github-actions-bot <support+actions@github.com>\""
26
26
  },
27
27
  "keywords": [],
28
28
  "browserslist": [
@@ -67,6 +67,7 @@
67
67
  "eslint": "^8.15.0",
68
68
  "eslint-config-react-app": "^7.0.1",
69
69
  "eslint-plugin-storybook": "^0.10.1",
70
+ "gh-pages": "^6.3.0",
70
71
  "husky": "^3.0.5",
71
72
  "jest": "^30.0.5",
72
73
  "jest-canvas-mock": "^2.3.1",
@@ -84,7 +85,7 @@
84
85
  },
85
86
  "dependencies": {
86
87
  "@floating-ui/dom": "^1.6.3",
87
- "@floating-ui/react": "^0.26.28",
88
+ "@floating-ui/react": "^0.27.15",
88
89
  "@fortawesome/fontawesome-free": "^5.10.2",
89
90
  "@fortawesome/fontawesome-svg-core": "^1.2.35",
90
91
  "@fortawesome/free-regular-svg-icons": "^5.15.3",
@@ -96,7 +97,7 @@
96
97
  "polished": "3.4.1",
97
98
  "pretty-bytes": "^5.6.0",
98
99
  "react-dropzone": "^14.2.3",
99
- "react-hook-form": "^7.49.2",
100
+ "react-hook-form": "^7.62.0",
100
101
  "react-query": "^3.34.0",
101
102
  "react-router": "7.8.1",
102
103
  "react-router-dom": "7.8.1",
@@ -1,6 +1,6 @@
1
1
  import { render, screen, waitFor } from '@testing-library/react';
2
2
  import { getWrapper } from '../../testUtils';
3
- import { Barchart, CustomTick, formatDate } from './Barchart.component';
3
+ import { Barchart, CustomTick } from './Barchart.component';
4
4
  import { ChartLegendWrapper } from '../chartlegend/ChartLegendWrapper';
5
5
  import React from 'react';
6
6
 
@@ -14,13 +14,14 @@ import styled, { useTheme } from 'styled-components';
14
14
  import { spacing, Stack, Wrap } from '../../spacing';
15
15
  import { chartColors, ChartColors, fontSize } from '../../style/theme';
16
16
  import { Box } from '../box/Box';
17
+ import { useChartLegend } from '../chartlegend/ChartLegendWrapper';
17
18
  import { ConstrainedText } from '../constrainedtext/Constrainedtext.component';
19
+ import { FormattedDateTime } from '../date/FormattedDateTime';
18
20
  import { IconHelp } from '../iconhelper/IconHelper';
19
21
  import { Loader } from '../loader/Loader.component';
20
22
  import { Text } from '../text/Text.component';
21
- import { renderTooltipContent, UnitRange, useChartData } from './utils';
22
- import { useChartLegend } from '../chartlegend/ChartLegendWrapper';
23
- import { FormattedDateTime } from '../date/FormattedDateTime';
23
+ import { ChartTooltip } from './ChartTooltip';
24
+ import { UnitRange, useChartData } from './utils';
24
25
 
25
26
  const CHART_CONSTANTS = {
26
27
  TICK_WIDTH_OFFSET: 5,
@@ -344,6 +345,7 @@ export const Barchart = <T extends BarchartBars>(props: BarchartProps<T>) => {
344
345
  fill={chartColors[fill] || fill}
345
346
  minPointSize={stacked ? 0 : CHART_CONSTANTS.MIN_POINT_SIZE}
346
347
  stackId={stackId}
348
+ isAnimationActive={false}
347
349
  onMouseOver={() => setHoveredValue(dataKey)}
348
350
  onMouseLeave={() => setHoveredValue(undefined)}
349
351
  />
@@ -390,9 +392,15 @@ export const Barchart = <T extends BarchartBars>(props: BarchartProps<T>) => {
390
392
  />
391
393
 
392
394
  <Tooltip
393
- content={(props: TooltipContentProps<number, string>) =>
394
- renderTooltipContent(props, tooltip, hoveredValue)
395
- }
395
+ content={(props: TooltipContentProps<number, string>) => (
396
+ <ChartTooltip
397
+ type={type}
398
+ colorSet={colorSet}
399
+ tooltipProps={props}
400
+ hoveredValue={hoveredValue}
401
+ tooltip={tooltip}
402
+ />
403
+ )}
396
404
  cursor={false}
397
405
  />
398
406
  </BarChart>
@@ -0,0 +1,119 @@
1
+ import React from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import { ChartTooltip } from './ChartTooltip';
4
+
5
+ const ONE_DAY_IN_MILLISECONDS = 24 * 60 * 60 * 1000;
6
+ const ONE_HOUR_IN_MILLISECONDS = 60 * 60 * 1000;
7
+ const SUCCESS_VALUE = 39;
8
+ const FAILED_VALUE = 13;
9
+ const testTooltipProps = {
10
+ payload: [
11
+ { name: 'Success', value: SUCCESS_VALUE },
12
+ { name: 'Failed', value: FAILED_VALUE },
13
+ ],
14
+ label: 'Test',
15
+ coordinate: { x: 10, y: 10 },
16
+ active: true,
17
+ accessibilityLayer: false,
18
+ };
19
+ const testTooltip = () => <div>Test Tooltip</div>;
20
+ const date = new Date('2024-07-01T00:00:00').getTime();
21
+
22
+ describe('ChartTooltip', () => {
23
+ const selectors = {
24
+ tooltip: () => screen.queryByText(/Test Tooltip/),
25
+ category: () => screen.queryByText(/Test/),
26
+ success: () => screen.queryByText(/Success/),
27
+ successValue: () => screen.queryByText(SUCCESS_VALUE),
28
+ failed: () => screen.queryByText(/Failed/),
29
+ failedValue: () => screen.queryByText(FAILED_VALUE),
30
+ date: () => screen.queryByText(/Monday, 01 July 2024/),
31
+ time: () => screen.queryByText(/00:00/),
32
+ };
33
+ it('should render the ChartTooltip component', () => {
34
+ render(
35
+ <ChartTooltip
36
+ type={{ type: 'category' }}
37
+ tooltipProps={testTooltipProps}
38
+ hoveredValue="Success"
39
+ tooltip={undefined}
40
+ />,
41
+ );
42
+ expect(selectors.category()).toBeInTheDocument();
43
+ expect(selectors.success()).toBeInTheDocument();
44
+ expect(selectors.successValue()).toBeInTheDocument();
45
+ expect(selectors.failed()).toBeInTheDocument();
46
+ expect(selectors.failedValue()).toBeInTheDocument();
47
+ });
48
+ it('should render tooltip when tooltip is provided', () => {
49
+ render(
50
+ <ChartTooltip
51
+ type={{ type: 'category' }}
52
+ tooltipProps={testTooltipProps}
53
+ hoveredValue="Success"
54
+ tooltip={testTooltip}
55
+ />,
56
+ );
57
+ expect(selectors.tooltip()).toBeInTheDocument();
58
+ });
59
+ it('should not render tooltip when tooltipProps is not active', () => {
60
+ render(
61
+ <ChartTooltip
62
+ type={{ type: 'category' }}
63
+ tooltipProps={{ ...testTooltipProps, active: false }}
64
+ hoveredValue="Success"
65
+ tooltip={testTooltip}
66
+ />,
67
+ );
68
+ expect(selectors.tooltip()).not.toBeInTheDocument();
69
+ });
70
+ it('should render time tooltip when type is time', () => {
71
+ // timestamp for Mon Jul 01 2024 00:00:00 GMT+0000 (Coordinated Universal Time)
72
+ const label = date;
73
+ render(
74
+ <ChartTooltip
75
+ type={{
76
+ type: 'time',
77
+ timeRange: {
78
+ startDate: new Date(),
79
+ endDate: new Date(),
80
+ interval: ONE_DAY_IN_MILLISECONDS,
81
+ },
82
+ }}
83
+ tooltipProps={{ ...testTooltipProps, label }}
84
+ hoveredValue="Success"
85
+ />,
86
+ );
87
+
88
+ expect(selectors.success()).toBeInTheDocument();
89
+ expect(selectors.successValue()).toBeInTheDocument();
90
+ expect(selectors.failed()).toBeInTheDocument();
91
+ expect(selectors.failedValue()).toBeInTheDocument();
92
+
93
+ expect(selectors.date()).toBeInTheDocument();
94
+ expect(selectors.time()).not.toBeInTheDocument();
95
+ });
96
+ it('should render time tooltip when type is time and interval is one hour', () => {
97
+ const label = date;
98
+ render(
99
+ <ChartTooltip
100
+ type={{
101
+ type: 'time',
102
+ timeRange: {
103
+ startDate: new Date(),
104
+ endDate: new Date(),
105
+ interval: ONE_HOUR_IN_MILLISECONDS,
106
+ },
107
+ }}
108
+ tooltipProps={{ ...testTooltipProps, label }}
109
+ hoveredValue="Success"
110
+ />,
111
+ );
112
+ expect(selectors.success()).toBeInTheDocument();
113
+ expect(selectors.successValue()).toBeInTheDocument();
114
+ expect(selectors.failed()).toBeInTheDocument();
115
+ expect(selectors.failedValue()).toBeInTheDocument();
116
+ expect(selectors.date()).toBeInTheDocument();
117
+ expect(selectors.time()).toBeInTheDocument();
118
+ });
119
+ });
@@ -1,10 +1,17 @@
1
1
  import styled from 'styled-components';
2
2
  import { spacing, Stack, Wrap } from '../../spacing';
3
3
  import { Text } from '../text/Text.component';
4
- import { BarchartBars } from './Barchart.component';
4
+ import {
5
+ BarchartBars,
6
+ BarchartTooltipFn,
7
+ CategoryType,
8
+ TimeType,
9
+ } from './Barchart.component';
5
10
  import { fontSize, fontWeight } from '../../style/theme';
6
11
  import { LegendShape } from '../chartlegend/ChartLegend';
7
12
  import { FormattedDateTime } from '../date/FormattedDateTime';
13
+ import { TooltipContentProps } from 'recharts';
14
+ import { getCurrentPoint } from './utils';
8
15
 
9
16
  export const ChartTooltipContainer = styled.div`
10
17
  background-color: ${({ theme }) => theme.backgroundLevel1};
@@ -30,24 +37,45 @@ export const ChartTooltipItem = styled.div<{ isHovered: boolean }>`
30
37
 
31
38
  export const ChartTooltip = <T extends BarchartBars>({
32
39
  type,
33
- currentPoint,
40
+ tooltipProps,
34
41
  colorSet,
42
+ hoveredValue,
43
+ tooltip,
35
44
  }: {
36
- type: 'time' | 'category';
37
- currentPoint: {
38
- category: string | number;
39
- values: { label: T[number]['label']; value: number; isHovered: boolean }[];
40
- };
41
- colorSet: Record<string, string>;
45
+ type: TimeType | CategoryType;
46
+ tooltipProps: TooltipContentProps<number, string>;
47
+ colorSet?: Record<string, string>;
48
+ hoveredValue: string | undefined;
49
+ tooltip?: BarchartTooltipFn<T>;
42
50
  }) => {
51
+ const { active } = tooltipProps;
52
+
53
+ if (!active) {
54
+ return null;
55
+ }
56
+
57
+ const currentPoint = getCurrentPoint(tooltipProps, hoveredValue);
58
+ if (tooltip) {
59
+ return tooltip(currentPoint);
60
+ }
61
+
43
62
  return (
44
63
  <ChartTooltipContainer>
45
64
  <Text isEmphazed>
46
- {type === 'time' ? (
47
- <FormattedDateTime
48
- format="long-date"
49
- value={new Date(currentPoint.category)}
50
- />
65
+ {type.type === 'time' ? (
66
+ <>
67
+ <FormattedDateTime
68
+ format="long-date"
69
+ value={new Date(currentPoint.category)}
70
+ />{' '}
71
+ {type.type === 'time' &&
72
+ type.timeRange.interval < 24 * 60 * 60 * 1000 && (
73
+ <FormattedDateTime
74
+ format="time"
75
+ value={new Date(currentPoint.category)}
76
+ />
77
+ )}
78
+ </>
51
79
  ) : (
52
80
  currentPoint.category
53
81
  )}
@@ -55,13 +83,15 @@ export const ChartTooltip = <T extends BarchartBars>({
55
83
  <Stack direction="vertical" gap="r8" style={{ width: '100%' }}>
56
84
  {currentPoint.values.map((value) => {
57
85
  return (
58
- <Wrap key={value.label}>
86
+ <Wrap key={value.label} gap={spacing.r32}>
59
87
  <ChartTooltipItem isHovered={value.isHovered}>
60
- <LegendShape
61
- color={colorSet[value.label as keyof typeof colorSet]}
62
- shape="rectangle"
63
- chartColors={colorSet}
64
- />
88
+ {colorSet && (
89
+ <LegendShape
90
+ color={colorSet[value.label as keyof typeof colorSet]}
91
+ shape="rectangle"
92
+ chartColors={colorSet}
93
+ />
94
+ )}
65
95
  {value.label}
66
96
  </ChartTooltipItem>
67
97
  <ChartTooltipItem isHovered={value.isHovered}>