diff --git a/package-lock.json b/package-lock.json
index 2a7bd2a8ac..e6276c6a90 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -36,6 +36,7 @@
"react-virtualized": "9.22.3",
"react-virtualized-auto-sizer": "^1.0.24",
"react-window": "^1.8.6",
+ "recharts": "^2.15.1",
"styled-components": "^5.2.1",
"styled-system": "^5.1.5",
"vega": "^5.17.3",
@@ -5186,6 +5187,60 @@
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="
},
+ "node_modules/@types/d3-array": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz",
+ "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg=="
+ },
+ "node_modules/@types/d3-color": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
+ "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="
+ },
+ "node_modules/@types/d3-ease": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
+ "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="
+ },
+ "node_modules/@types/d3-interpolate": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
+ "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
+ "dependencies": {
+ "@types/d3-color": "*"
+ }
+ },
+ "node_modules/@types/d3-path": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
+ "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg=="
+ },
+ "node_modules/@types/d3-scale": {
+ "version": "4.0.9",
+ "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",
+ "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
+ "dependencies": {
+ "@types/d3-time": "*"
+ }
+ },
+ "node_modules/@types/d3-shape": {
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz",
+ "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==",
+ "dependencies": {
+ "@types/d3-path": "*"
+ }
+ },
+ "node_modules/@types/d3-time": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",
+ "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="
+ },
+ "node_modules/@types/d3-timer": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
+ "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="
+ },
"node_modules/@types/debug": {
"version": "4.1.12",
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
@@ -8594,6 +8649,14 @@
"node": ">=0.10.0"
}
},
+ "node_modules/d3-ease": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
+ "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/d3-force": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
@@ -8859,6 +8922,11 @@
"integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==",
"dev": true
},
+ "node_modules/decimal.js-light": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz",
+ "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="
+ },
"node_modules/decode-named-character-reference": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz",
@@ -10853,6 +10921,11 @@
"node": ">= 0.6"
}
},
+ "node_modules/eventemitter3": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
+ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
+ },
"node_modules/events": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
@@ -11272,6 +11345,14 @@
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
},
+ "node_modules/fast-equals": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.2.2.tgz",
+ "integrity": "sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
"node_modules/fast-glob": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
@@ -18967,6 +19048,20 @@
"react": "^16.0.0 || ^17.0.0 || ^18.0.0"
}
},
+ "node_modules/react-smooth": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz",
+ "integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==",
+ "dependencies": {
+ "fast-equals": "^5.0.1",
+ "prop-types": "^15.8.1",
+ "react-transition-group": "^4.4.5"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
"node_modules/react-table": {
"version": "7.8.0",
"resolved": "https://registry.npmjs.org/react-table/-/react-table-7.8.0.tgz",
@@ -19214,6 +19309,49 @@
"node": ">= 4"
}
},
+ "node_modules/recharts": {
+ "version": "2.15.1",
+ "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.1.tgz",
+ "integrity": "sha512-v8PUTUlyiDe56qUj82w/EDVuzEFXwEHp9/xOowGAZwfLjB9uAy3GllQVIYMWF6nU+qibx85WF75zD7AjqoT54Q==",
+ "dependencies": {
+ "clsx": "^2.0.0",
+ "eventemitter3": "^4.0.1",
+ "lodash": "^4.17.21",
+ "react-is": "^18.3.1",
+ "react-smooth": "^4.0.4",
+ "recharts-scale": "^0.4.4",
+ "tiny-invariant": "^1.3.1",
+ "victory-vendor": "^36.6.8"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "peerDependencies": {
+ "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/recharts-scale": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz",
+ "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==",
+ "dependencies": {
+ "decimal.js-light": "^2.4.1"
+ }
+ },
+ "node_modules/recharts/node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/recharts/node_modules/react-is": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
+ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="
+ },
"node_modules/rechoir": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
@@ -21589,8 +21727,7 @@
"node_modules/tiny-invariant": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
- "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
- "dev": true
+ "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="
},
"node_modules/title-case": {
"version": "2.1.1",
@@ -23135,6 +23272,27 @@
"url": "https://opencollective.com/unified"
}
},
+ "node_modules/victory-vendor": {
+ "version": "36.9.2",
+ "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz",
+ "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==",
+ "dependencies": {
+ "@types/d3-array": "^3.0.3",
+ "@types/d3-ease": "^3.0.0",
+ "@types/d3-interpolate": "^3.0.1",
+ "@types/d3-scale": "^4.0.2",
+ "@types/d3-shape": "^3.1.0",
+ "@types/d3-time": "^3.0.0",
+ "@types/d3-timer": "^3.0.0",
+ "d3-array": "^3.1.6",
+ "d3-ease": "^3.0.1",
+ "d3-interpolate": "^3.0.1",
+ "d3-scale": "^4.0.2",
+ "d3-shape": "^3.1.0",
+ "d3-time": "^3.0.0",
+ "d3-timer": "^3.0.1"
+ }
+ },
"node_modules/w3c-hr-time": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
diff --git a/package.json b/package.json
index a78f212469..4685de5dbf 100644
--- a/package.json
+++ b/package.json
@@ -124,6 +124,7 @@
"react-virtualized": "9.22.3",
"react-virtualized-auto-sizer": "^1.0.24",
"react-window": "^1.8.6",
+ "recharts": "^2.15.1",
"styled-components": "^5.2.1",
"styled-system": "^5.1.5",
"vega": "^5.17.3",
diff --git a/src/lib/components/date/FormattedDateTime.spec.tsx b/src/lib/components/date/FormattedDateTime.spec.tsx
index 2f08380425..cb86edb5fe 100644
--- a/src/lib/components/date/FormattedDateTime.spec.tsx
+++ b/src/lib/components/date/FormattedDateTime.spec.tsx
@@ -214,4 +214,28 @@ describe('FormatttedDateTime', () => {
//V
expect(screen.getByText('2022-12-12 11:57:26')).toBeInTheDocument();
});
+
+ it('should display the date in the expected format of the xaxis tick in the chart', () => {
+ //S
+ render(
+ ,
+ );
+ //V
+ expect(screen.getByText('6 Oct 18:33')).toBeInTheDocument();
+ });
+
+ it('should display the date in the expected format of date in the chart', () => {
+ //S
+ render(
+ ,
+ );
+ //V
+ expect(screen.getByText('6 Oct 18:33:00')).toBeInTheDocument();
+ });
});
diff --git a/src/lib/components/date/FormattedDateTime.tsx b/src/lib/components/date/FormattedDateTime.tsx
index 4d877a5941..283eacd0b8 100644
--- a/src/lib/components/date/FormattedDateTime.tsx
+++ b/src/lib/components/date/FormattedDateTime.tsx
@@ -21,6 +21,26 @@ export const TIME_FORMATER = Intl.DateTimeFormat('en-GB', {
minute: '2-digit',
});
+export const DAY_MONTH_ABBREVIATED_HOUR_MINUTE_SECOND = Intl.DateTimeFormat(
+ 'en-GB',
+ {
+ day: 'numeric',
+ month: 'short',
+ hour: '2-digit',
+ minute: '2-digit',
+ second: '2-digit',
+ hour12: false,
+ },
+);
+
+export const DAY_MONTH_ABBREVIATED_HOUR_MINUTE = Intl.DateTimeFormat('en-GB', {
+ day: 'numeric',
+ month: 'short',
+ hour: '2-digit',
+ minute: '2-digit',
+ hour12: false,
+});
+
type FormattedDateTimeProps = {
format:
| 'date'
@@ -28,7 +48,9 @@ type FormattedDateTimeProps = {
| 'date-time-second'
| 'time'
| 'time-second'
- | 'relative';
+ | 'relative'
+ | 'day-month-abbreviated-hour-minute'
+ | 'day-month-abbreviated-hour-minute-second';
value: Date;
};
@@ -143,7 +165,19 @@ export const FormattedDateTime = ({
few seconds ago
);
- //TO FINISH
+ case 'day-month-abbreviated-hour-minute':
+ return (
+ <>{DAY_MONTH_ABBREVIATED_HOUR_MINUTE.format(value).replace(',', '')}>
+ );
+ case 'day-month-abbreviated-hour-minute-second':
+ return (
+ <>
+ {DAY_MONTH_ABBREVIATED_HOUR_MINUTE_SECOND.format(value).replace(
+ ',',
+ '',
+ )}
+ >
+ );
default:
return <>>;
}
diff --git a/src/lib/components/linetimeseriechart/linetimeseriechart.component.tsx b/src/lib/components/linetimeseriechart/linetimeseriechart.component.tsx
new file mode 100644
index 0000000000..13b2a6f0cf
--- /dev/null
+++ b/src/lib/components/linetimeseriechart/linetimeseriechart.component.tsx
@@ -0,0 +1,505 @@
+import {
+ Line,
+ LineChart,
+ ReferenceLine,
+ ResponsiveContainer,
+ Tooltip,
+ XAxis,
+ YAxis,
+ CartesianGrid,
+} from 'recharts';
+import { useMemo, useRef } from 'react';
+import { useTheme } from 'styled-components';
+import { useMetricsTimeSpan } from '../linetemporalchart/MetricTimespanProvider';
+import { addMissingDataPoint } from '../linetemporalchart/ChartUtil';
+import styled from 'styled-components';
+import {
+ fontSize,
+ fontWeight,
+ lineTimeSeriesColorRange,
+} from '../../style/theme';
+import { ChartTitleText, SmallerText } from '../text/Text.component';
+import { Loader } from '../loader/Loader.component';
+import { spacing } from '../../spacing';
+import { getUnitLabel } from '../linetemporalchart/ChartUtil';
+import { Icon } from '../icon/Icon.component';
+import { Tooltip as TooltipComponent } from '../tooltip/Tooltip.component';
+import {
+ DAY_MONTH_ABBREVIATED_HOUR_MINUTE,
+ FormattedDateTime,
+} from '../date/FormattedDateTime';
+
+const LineTemporalChartWrapper = styled.div`
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-start;
+ flex: 1;
+`;
+
+const ChartHeader = styled.div`
+ display: flex;
+ align-items: center;
+`;
+
+const TooltipContainer = styled.div`
+ background-color: ${(props) => props.theme.backgroundLevel1};
+ padding: ${spacing.r8};
+ border: 1px solid ${(props) => props.theme.border};
+ border-radius: 4px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+ max-width: 250px;
+`;
+
+const TooltipTime = styled.div`
+ margin-bottom: ${spacing.r8};
+ color: ${(props) => props.theme.textPrimary};
+ font-size: ${fontSize.smaller};
+ font-weight: ${fontWeight.bold};
+ text-align: center;
+`;
+
+const TooltipValue = styled.div`
+ font-size: ${fontSize.smaller};
+ margin-top: 4px;
+ color: ${(props) => props.theme.textSecondary};
+ display: flex;
+ align-items: flex-start;
+`;
+
+const TooltipLegend = styled.div<{ color: string }>`
+ width: 12px;
+ height: 3px;
+ background-color: ${(props) => props.color};
+ margin-right: 8px;
+ flex-shrink: 0;
+ margin-top: 8px;
+`;
+
+const TooltipContent = styled.div`
+ display: flex;
+ min-width: 0;
+ flex: 1;
+`;
+
+const TooltipName = styled.div`
+ margin-right: 4px;
+ word-wrap: break-word;
+ word-break: break-word;
+ justify-content: flex-start;
+`;
+
+const TooltipInstanceValue = styled.div`
+ justify-content: flex-end;
+`;
+
+export type Serie = {
+ // the name of the resource
+ resource: string;
+ // the original data format from prometheus
+ data: [number, string | null][];
+ // it's mandatory to display tooltip label in the tooltip
+ getTooltipLabel: (metricPrefix?: string, resource?: string) => string;
+ // get the legend label for each of the series
+ getLegendLabel?: (metricPrefix?: string, resource?: string) => string;
+ // optional color field to specify the color of the line
+ color?: string;
+ // the name of the metric prefix with read, write, in, out
+ metricPrefix?: string;
+ // to specify if the line is dash
+ isLineDashed?: boolean;
+};
+
+type NonSymmetricalChartSerie = {
+ yAxisType?: 'default' | 'percentage';
+ series: Serie[];
+};
+
+// The symmetrical chart props are used to display two series on the same chart, such as in/out, write/read
+type SymmetricalChartSerie = {
+ yAxisType: 'symmetrical';
+ series: {
+ above: Serie[];
+ below: Serie[];
+ };
+};
+
+export type LineChartProps = (
+ | NonSymmetricalChartSerie
+ | SymmetricalChartSerie
+) & {
+ title: string;
+ height: number;
+ startingTimeStamp: number;
+ unitRange?: {
+ threshold: number;
+ label: string;
+ }[];
+ isLoading?: boolean;
+ yAxisTitle?: string;
+ helpText?: string;
+};
+
+const CustomTooltip = ({
+ active,
+ payload,
+ label,
+ unitLabel,
+}: {
+ active?: boolean;
+ payload?: Array<{
+ value: number;
+ name: string;
+ color: string;
+ dataKey: string;
+ }>;
+ label?: string;
+ unitLabel?: string;
+}) => {
+ if (!active || !payload || !payload.length || !label) return null;
+ // We can't use the default itemSorter method because it's a custom tooltip.
+ // Sort the payload here instead
+ const sortedPayload = [...payload].sort((a, b) => {
+ const aValue = Number(a.value);
+ const bValue = Number(b.value);
+
+ if (aValue >= 0 && bValue >= 0) {
+ return bValue - aValue; // Higher positive values first
+ }
+ if (aValue < 0 && bValue < 0) {
+ return bValue - aValue; // Lower negative values first
+ }
+ return bValue - aValue; // Positives before negatives
+ });
+
+ return (
+
+
+
+
+ {sortedPayload.map((entry, index) => (
+
+
+
+ {entry.name}
+
+ {isNaN(Number(entry.value))
+ ? '-'
+ : `${Number(entry.value).toFixed(2)}${unitLabel}`}
+
+
+
+ ))}
+
+ );
+};
+
+const isSymmetricalSeries = (
+ series: Serie[] | { above: Serie[]; below: Serie[] },
+): series is { above: Serie[]; below: Serie[] } => {
+ return 'above' in series && 'below' in series;
+};
+
+export function LineTimeSerieChart({
+ series,
+ title,
+ height,
+ startingTimeStamp,
+ unitRange,
+ isLoading = false,
+ yAxisType = 'default',
+ yAxisTitle,
+ helpText,
+ ...rest
+}: LineChartProps) {
+ const theme = useTheme();
+ const { frequency, duration } = useMetricsTimeSpan();
+ const chartRef = useRef(null);
+
+ const chartData = useMemo(() => {
+ // 1. Add missing data points
+ const normalizedSeries =
+ yAxisType === 'symmetrical' && isSymmetricalSeries(series)
+ ? {
+ above: series.above.map((line) => ({
+ ...line,
+ data: addMissingDataPoint(
+ line.data,
+ startingTimeStamp,
+ duration,
+ frequency,
+ ),
+ })),
+ // Convert positive values to negative values
+ below: series.below.map((line) => ({
+ ...line,
+ data: addMissingDataPoint(
+ line.data,
+ startingTimeStamp,
+ duration,
+ frequency,
+ ).map(
+ ([timestamp, value]) =>
+ [timestamp, value === null ? null : `-${Number(value)}`] as [
+ number,
+ string | null,
+ ],
+ ),
+ })),
+ }
+ : (series as Serie[]).map((line) => ({
+ ...line,
+ data: addMissingDataPoint(
+ line.data,
+ startingTimeStamp,
+ duration,
+ frequency,
+ ),
+ }));
+
+ // 2. Convert directly to Recharts format
+ // Initialize an object to hold data points by timestamp
+ const dataPointsByTime: Record<
+ number,
+ { timestamp: number } & Record
+ > = {};
+ const seriesToProcess =
+ yAxisType === 'symmetrical' && isSymmetricalSeries(normalizedSeries)
+ ? [...normalizedSeries.above, ...normalizedSeries.below]
+ : (normalizedSeries as Serie[]);
+
+ seriesToProcess.forEach((serie) => {
+ const label = serie.getTooltipLabel(serie.metricPrefix, serie.resource);
+
+ serie.data.forEach((point) => {
+ const timestamp =
+ typeof point[0] === 'number' ? point[0] * 1000 : Number(point[0]);
+ const value = point[1];
+ // Initialize this timestamp if it doesn't exist
+ if (!dataPointsByTime[timestamp]) {
+ dataPointsByTime[timestamp] = { timestamp };
+ }
+ // Add this metric's value to the data point, and convert the value to a number if it's a string
+ dataPointsByTime[timestamp][label] =
+ typeof value === 'string' ? Number(value) : value;
+ });
+ });
+ // Convert object to array for Recharts
+ return Object.values(dataPointsByTime).sort(
+ (
+ a: { timestamp: number } & Record,
+ b: { timestamp: number } & Record,
+ ) => (a.timestamp as number) - (b.timestamp as number),
+ );
+ }, [series, startingTimeStamp, duration, frequency, yAxisType]);
+
+ // Calculate 5 perfectly evenly spaced ticks
+ const xAxisTicks = useMemo(() => {
+ if (!chartData || chartData.length === 0) return [];
+
+ const timestamps: number[] = chartData.map((d) => d.timestamp);
+ const minTimestamp = Math.min(...timestamps);
+ const maxTimestamp = Math.max(...timestamps);
+
+ // Calculate 5 perfectly evenly spaced ticks
+ const timeRange = maxTimestamp - minTimestamp;
+ const interval = timeRange / 4; // 4 intervals create 5 points
+
+ const exactEvenTicks = [
+ minTimestamp,
+ minTimestamp + interval,
+ minTimestamp + interval * 2,
+ minTimestamp + interval * 3,
+ maxTimestamp,
+ ];
+
+ // Return perfectly even ticks (guaranteed to be evenly divided)
+ return exactEvenTicks;
+ }, [chartData]);
+
+ // 3. Transform the data base on the valuebase
+ const { topValue, unitLabel, rechartsData } = useMemo(() => {
+ if (yAxisType === 'percentage')
+ return {
+ topValue: 100,
+ unitLabel: '%',
+ rechartsData: chartData,
+ };
+
+ const values = chartData.flatMap((dataPoint) =>
+ Object.entries(dataPoint)
+ .filter(([key]) => key !== 'timestamp')
+ .map(([_, value]) => {
+ const num =
+ typeof value === 'string' ? Number(value) : (value ?? Infinity);
+ return !isNaN(num) && num !== null ? num : null;
+ })
+ .filter((value): value is number => value !== null),
+ );
+
+ const top = Math.abs(Math.max(...values));
+ const bottom = Math.abs(Math.min(...values));
+ const maxValue = Math.max(top, bottom);
+
+ const { valueBase, unitLabel } = getUnitLabel(unitRange ?? [], maxValue);
+
+ const topValue = Math.ceil(maxValue / valueBase / 10) * 10;
+
+ const rechartsData = chartData.map((dataPoint) => {
+ const normalizedDataPoint = { ...dataPoint };
+ Object.entries(dataPoint).forEach(([key, value]) => {
+ if (key !== 'timestamp' && typeof value === 'number') {
+ normalizedDataPoint[key] = value / valueBase;
+ }
+ });
+ return normalizedDataPoint;
+ });
+
+ return { topValue, unitLabel, rechartsData };
+ }, [chartData, yAxisType, unitRange]);
+
+ // Group series by resource and create color mapping
+ const { colorMapping, groupedSeries } = useMemo(() => {
+ const mapping: Record = {};
+ const allSeries = isSymmetricalSeries(series)
+ ? [...series.above, ...series.below]
+ : (series as Serie[]);
+
+ // Group series by resource
+ const groups = allSeries.reduce(
+ (acc, serie) => {
+ const key = serie.resource;
+ if (!acc[key]) {
+ acc[key] = [];
+ }
+ acc[key].push(serie);
+ return acc;
+ },
+ {} as Record,
+ );
+
+ // Todo: The color will be assigned through the context.
+ Object.keys(groups).forEach((resource, index) => {
+ const color =
+ lineTimeSeriesColorRange[index % lineTimeSeriesColorRange.length];
+ mapping[resource] = color;
+ });
+
+ return {
+ colorMapping: mapping,
+ groupedSeries: groups,
+ };
+ }, [series]);
+
+ // Format time for display the tick in the x axis
+ const formatTime = useMemo(
+ () => (timestamp: number) => {
+ const date = new Date(timestamp);
+ return DAY_MONTH_ABBREVIATED_HOUR_MINUTE.format(date).replace(',', '');
+ },
+ [],
+ );
+
+ return (
+
+
+
+ {title} {unitLabel && `(${unitLabel})`}
+
+ {helpText && (
+ {helpText}}
+ >
+
+
+ )}
+ {isLoading && }
+
+
+
+
+
+ Math.round(value).toString()}
+ />
+ } />
+ {/* Add horizontal line at y=0 for symmetrical charts */}
+ {yAxisType === 'symmetrical' && (
+
+ )}
+
+ {/* Chart lines */}
+ {Object.entries(groupedSeries).map(([resource, resourceSeries]) =>
+ resourceSeries.map((serie, serieIndex) => {
+ const label = serie.getTooltipLabel(
+ serie.metricPrefix,
+ serie.resource,
+ );
+ return (
+
+ );
+ }),
+ )}
+
+
+
+ );
+}
diff --git a/src/lib/index.ts b/src/lib/index.ts
index d4c73ff939..662131580d 100644
--- a/src/lib/index.ts
+++ b/src/lib/index.ts
@@ -82,3 +82,4 @@ export { InfoMessage } from './components/infomessage/InfoMessage.component';
export { InputList } from './components/inputlist/InputList.component';
export { InlineInput } from './components/inlineinput/InlineInput';
export { UnsuccessfulResult } from './components/UnsuccessfulResult.component';
+export { LineTimeSerieChart } from './components/linetimeseriechart/linetimeseriechart.component';
diff --git a/src/lib/style/theme.ts b/src/lib/style/theme.ts
index 7d5b14e01d..e83c3e478d 100644
--- a/src/lib/style/theme.ts
+++ b/src/lib/style/theme.ts
@@ -1,3 +1,4 @@
+import { lighten, darken } from 'polished';
//== Colors
export const hotPink = '#E40046';
export const pink = '#EB4962';
@@ -268,3 +269,31 @@ export const navbarItemWidth = '4.286rem';
//sidebar
export const sidebarItemHeight = spacing.sp40;
export const sidebarWidth = spacing.sp40;
+
+// We use 8 main color from the palette and decline them (lighter/ darker) when we have more than 8 datasets
+export const lineTimeSeriesColorRange = [
+ lineColor1,
+ lineColor2,
+ lineColor3,
+ lineColor4,
+ lineColor5,
+ lineColor6,
+ lineColor7,
+ lineColor8,
+ lighten(0.3, lineColor1),
+ lighten(0.3, lineColor2),
+ lighten(0.3, lineColor3),
+ lighten(0.3, lineColor4),
+ lighten(0.3, lineColor5),
+ lighten(0.3, lineColor6),
+ lighten(0.3, lineColor7),
+ lighten(0.3, lineColor8),
+ darken(0.2, lineColor1),
+ darken(0.2, lineColor2),
+ darken(0.2, lineColor3),
+ darken(0.2, lineColor4),
+ darken(0.3, lineColor5),
+ darken(0.3, lineColor6),
+ darken(0.3, lineColor7),
+ darken(0.3, lineColor8),
+];
diff --git a/stories/format.mdx b/stories/format.mdx
index 50b9905ca0..ed7b10b9cc 100644
--- a/stories/format.mdx
+++ b/stories/format.mdx
@@ -32,8 +32,10 @@ import { Meta } from '@storybook/blocks';
| Short#1 | DD MMM | 20 Jul | 5 | Chart time axis |
| Short#2 | DDMMM HH:mm | 20Jul 09:00 | 11 | Limited space, year not needed |
| Short#3 | YYYY-MM-DD | 2020-07-20 | 10 | Tables |
-| Mid#1 | YYYY-MM-DD HH:mm | 2020-07-20 09:00 | 16 | Tables (creation/modification dates) |
-| Mid#2 | YYYY-MM-DD HH:mm:ss | 2020-07-20 09:00:00 | 19 | When the seconds are needed |
+| Mid#1 | DD MMM HH:mm | 20 Jul 09:00 | 12 | Chart Axis ticks |
+| Mid#2 | DD MMM HH:mm:ss | 20 Jul 09:00:00 | 15 | Chart Tooltip title |
+| Mid#3 | YYYY-MM-DD HH:mm | 2020-07-20 09:00 | 16 | Tables (creation/modification dates) |
+| Mid#4 | YYYY-MM-DD HH:mm:ss | 2020-07-20 09:00:00 | 19 | When the seconds are needed |
| Full#1 | EEE MMM DD YYYY HH:mm:ss | Mon Jul 20 2020 09:00:00 | 24 | When a lot of space (hover) - When precision is needed |
### Remarks:
diff --git a/stories/linetimeseriechart.stories.tsx b/stories/linetimeseriechart.stories.tsx
new file mode 100644
index 0000000000..ab48ae04e2
--- /dev/null
+++ b/stories/linetimeseriechart.stories.tsx
@@ -0,0 +1,462 @@
+import React from 'react';
+import { Meta, StoryObj } from '@storybook/react';
+import { MetricsTimeSpanProvider } from '../src/lib/components/linetemporalchart/MetricTimespanProvider';
+import { LineTimeSerieChart } from '../src/lib/components/linetimeseriechart/linetimeseriechart.component';
+
+const ChartWithProviders = (props) => {
+ return (
+
+
+
+ );
+};
+
+const meta: Meta = {
+ title: 'Components/Data Display/Charts/LineTimeSerieChart',
+ component: ChartWithProviders,
+ decorators: [
+ (Story) => (
+
+
+
+ ),
+ ],
+ parameters: {
+ layout: 'fullscreen',
+ },
+ tags: ['autodocs'],
+ argTypes: {
+ series: { control: 'object' },
+ title: { control: 'text' },
+ height: { control: 'number' },
+ startingTimeStamp: { control: 'number' },
+ unitRange: { control: 'object' },
+ isLoading: { control: 'boolean' },
+ yAxisType: {
+ control: 'select',
+ options: ['default', 'percentage', 'symmetrical'],
+ },
+ yAxisTitle: { control: 'text' },
+ },
+};
+
+export default meta;
+type Story = StoryObj;
+
+const prometheusData = [
+ [1740405600, '47.554166666666674'],
+ [1740406320, '53.00833333333337'],
+ [1740407760, '40.18750000000011'],
+ [1740408480, '59.187499999999886'],
+ [1740409200, '62.49583333333344'],
+ [1740409920, '57.449999999999854'],
+ [1740410640, '45.187500000000114'],
+ [1740411360, '58.620833333332975'],
+ [1740412080, '53.0786411974533'],
+ [1740412800, '64.90833333333342'],
+ [1740413520, '81.23333333333335'],
+ [1740414240, '64.81614215228686'],
+ [1740415680, '67.17291666666674'],
+ [1740416400, '55.233333333333405'],
+ [1740417120, '47.91666666666667'],
+ [1740417840, '72.1083333333335'],
+ [1740418560, '61.90904847636171'],
+ [1740419280, '51.06666666666646'],
+ [1740420000, '39.50416666666664'],
+ [1740420720, '46.91666666666682'],
+ [1740421440, '35.0541666666669'],
+ [1740422160, '46.666666666666856'],
+ [1740422880, '43.57083333333397'],
+ [1740423600, '36.795833333333114'],
+ [1740467520, '42.81249999999962'],
+ [1740468240, '53.44272951530158'],
+ [1740468960, '55.69583333333337'],
+ [1740469680, '46.09583333333338'],
+ [1740470400, '43.020833333333705'],
+ [1740471120, '55.395833333333634'],
+ [1740471840, '46.56249999999999'],
+ [1740472560, '47.16249999999983'],
+ [1740473280, '44.69166666666676'],
+ [1740474000, '55.29583333333411'],
+ [1740474720, '42.25833333333412'],
+ [1740475440, '61.570833333332764'],
+ [1740476160, '42.86666666666633'],
+ [1740476880, '38.03750000000036'],
+ [1740477600, '41.32916666666612'],
+ [1740478320, '52.22499999999779'],
+ [1740479040, '42.13333333333368'],
+ [1740479760, '34.791666666665904'],
+ [1740480480, '36.58333333333227'],
+ [1740481200, '37.20833333333226'],
+ [1740481920, '49.19583333333246'],
+ [1740482640, '41.016666666666126'],
+ [1740483360, '37.54166666666834'],
+ [1740484080, '35.38750000000013'],
+ [1740484800, '39.02500000000069'],
+ [1740485520, '47.14054738807047'],
+ [1740486240, '46.79166666666787'],
+ [1740486960, '42.950363357557244'],
+ [1740487680, '35.22916666666637'],
+ [1740488400, '34.94583333333519'],
+ [1740489120, '41.044577067983646'],
+ [1740489840, '58.17083333333282'],
+ [1740490560, '40.00000000000304'],
+ [1740491280, '45.57500000000194'],
+ [1740492000, '45.741666666666184'],
+];
+
+const prometheusData2 = [
+ [1740405600, '45.23'],
+ [1740406320, '120.45'],
+ [1740407760, '35.88'],
+ [1740408480, '42.98'],
+ [1740409200, '88.29'],
+ [1740409920, '25.84'],
+ [1740410640, '95.38'],
+ [1740411360, '30.22'],
+ [1740412080, '78.17'],
+ [1740412800, '110.40'],
+ [1740413520, '45.83'],
+ [1740414240, '92.21'],
+ [1740415680, '28.57'],
+ [1740416400, '75.43'],
+ [1740417120, '115.31'],
+ [1740417840, '38.50'],
+ [1740418560, '82.40'],
+ [1740419280, '27.86'],
+ [1740420000, '98.90'],
+ [1740420720, '31.21'],
+ [1740421440, '85.25'],
+ [1740422160, '105.86'],
+ [1740422880, '42.97'],
+ [1740423600, '71.19'],
+ [1740467520, '95.41'],
+ [1740468240, '36.84'],
+ [1740468960, '88.89'],
+ [1740469680, '29.39'],
+ [1740470400, '96.42'],
+ [1740471120, '33.79'],
+ [1740471840, '108.96'],
+ [1740472560, '45.56'],
+ [1740473280, '77.89'],
+ [1740474000, '102.49'],
+ [1740474720, '35.65'],
+ [1740475440, '88.97'],
+ [1740476160, '25.26'],
+ [1740476880, '91.43'],
+ [1740477600, '104.72'],
+ [1740478320, '39.62'],
+ [1740479040, '85.53'],
+ [1740479760, '28.19'],
+ [1740480480, '99.98'],
+ [1740481200, '41.60'],
+ [1740481920, '76.59'],
+ [1740482640, '113.41'],
+ [1740483360, '30.94'],
+ [1740484080, '88.78'],
+ [1740484800, '32.42'],
+ [1740485520, '95.54'],
+ [1740486240, '29.19'],
+ [1740486960, '86.35'],
+ [1740487680, '118.62'],
+ [1740488400, '37.34'],
+ [1740489120, '84.44'],
+ [1740489840, '34.57'],
+ [1740490560, '93.40'],
+ [1740491280, '28.97'],
+ [1740492000, '107.14'],
+];
+
+const prometheusData3 = [
+ [1740405600, '65.32'],
+ [1740406320, '145.78'],
+ [1740407760, '42.91'],
+ [1740408480, '88.45'],
+ [1740409200, '132.67'],
+ [1740409920, '35.89'],
+ [1740410640, '128.45'],
+ [1740411360, '48.23'],
+ [1740412080, '95.34'],
+ [1740412800, '155.67'],
+ [1740413520, '52.45'],
+ [1740414240, '115.89'],
+ [1740415680, '38.92'],
+ [1740416400, '92.45'],
+ [1740417120, '142.34'],
+ [1740417840, '45.67'],
+ [1740418560, '108.90'],
+ [1740419280, '32.45'],
+ [1740420000, '125.67'],
+ [1740420720, '41.23'],
+ [1740421440, '98.45'],
+ [1740422160, '138.90'],
+ [1740422880, '55.67'],
+ [1740423600, '85.34'],
+ [1740467520, '122.45'],
+ [1740468240, '44.56'],
+ [1740468960, '105.78'],
+ [1740469680, '36.89'],
+ [1740470400, '118.90'],
+ [1740471120, '48.23'],
+ [1740471840, '148.67'],
+ [1740472560, '58.90'],
+ [1740473280, '92.45'],
+ [1740474000, '135.67'],
+ [1740474720, '42.34'],
+ [1740475440, '108.90'],
+ [1740476160, '38.45'],
+ [1740476880, '115.67'],
+ [1740477600, '142.34'],
+ [1740478320, '52.45'],
+ [1740479040, '102.34'],
+ [1740479760, '35.67'],
+ [1740480480, '128.90'],
+ [1740481200, '45.67'],
+ [1740481920, '95.34'],
+ [1740482640, '152.45'],
+ [1740483360, '42.34'],
+ [1740484080, '112.45'],
+ [1740484800, '38.90'],
+ [1740485520, '122.34'],
+ [1740486240, '35.67'],
+ [1740486960, '105.78'],
+ [1740487680, '158.90'],
+ [1740488400, '48.23'],
+ [1740489120, '98.45'],
+ [1740489840, '42.34'],
+ [1740490560, '118.90'],
+ [1740491280, '35.67'],
+ [1740492000, '145.78'],
+];
+
+const prometheusData4 = [
+ [1740405600, '85.45'],
+ [1740406320, '178.92'],
+ [1740407760, '32.15'],
+ [1740408480, '142.78'],
+ [1740409200, '195.34'],
+ [1740409920, '28.67'],
+ [1740410640, '165.89'],
+ [1740411360, '45.23'],
+ [1740412080, '138.56'],
+ [1740412800, '188.90'],
+ [1740413520, '35.67'],
+ [1740414240, '155.45'],
+ [1740415680, '25.89'],
+ [1740416400, '128.90'],
+ [1740417120, '182.34'],
+ [1740417840, '42.56'],
+ [1740418560, '145.78'],
+ [1740419280, '22.34'],
+ [1740420000, '168.90'],
+ [1740420720, '38.45'],
+ [1740421440, '135.67'],
+ [1740422160, '192.45'],
+ [1740422880, '48.23'],
+ [1740423600, '158.90'],
+ [1740467520, '28.45'],
+ [1740468240, '175.67'],
+ [1740468960, '42.34'],
+ [1740469680, '148.90'],
+ [1740470400, '185.67'],
+ [1740471120, '32.45'],
+ [1740471840, '172.34'],
+ [1740472560, '45.67'],
+ [1740473280, '152.89'],
+ [1740474000, '198.45'],
+ [1740474720, '25.67'],
+ [1740475440, '162.34'],
+ [1740476160, '35.89'],
+ [1740476880, '145.67'],
+ [1740477600, '188.90'],
+ [1740478320, '42.34'],
+ [1740479040, '158.90'],
+ [1740479760, '28.45'],
+ [1740480480, '175.67'],
+ [1740481200, '38.90'],
+ [1740481920, '142.34'],
+ [1740482640, '192.45'],
+ [1740483360, '32.67'],
+ [1740484080, '165.89'],
+ [1740484800, '45.23'],
+ [1740485520, '155.67'],
+ [1740486240, '25.89'],
+ [1740486960, '168.90'],
+ [1740487680, '195.34'],
+ [1740488400, '35.67'],
+ [1740489120, '148.90'],
+ [1740489840, '42.34'],
+ [1740490560, '172.45'],
+ [1740491280, '28.67'],
+ [1740492000, '185.90'],
+];
+
+const prometheusData5 = [
+ [1740405600, '12850.45'],
+ [1740406320, '21780.92'],
+ [1740407760, '8320.15'],
+ [1740408480, '16420.78'],
+ [1740409200, '21950.34'],
+ [1740409920, '9280.67'],
+ [1740410640, '18605.89'],
+ [1740411360, '12450.23'],
+ [1740412080, '21308.56'],
+ [1740412800, '18808.90'],
+ [1740413520, '15305.67'],
+ [1740414240, '24550.45'],
+ [1740415680, '10205.89'],
+ [1740416400, '19208.90'],
+ [1740417120, '24802.34'],
+ [1740417840, '13402.56'],
+ [1740418560, '21450.78'],
+ [1740419280, '18220.34'],
+ [1740420000, '26608.90'],
+ [1740420720, '19380.45'],
+ [1740421440, '23350.67'],
+ [1740422160, '19920.45'],
+ [1740422880, '15480.23'],
+ [1740423600, '28580.90'],
+ [1740467520, '17280.45'],
+ [1740468240, '24750.67'],
+ [1740468960, '18420.34'],
+ [1740469680, '23480.90'],
+ [1740470400, '19850.67'],
+ [1740471120, '27320.45'],
+ [1740471840, '19720.34'],
+ [1740472560, '24405.67'],
+ [1740473280, '18502.89'],
+ [1740474000, '27908.45'],
+ [1740474720, '16205.67'],
+ [1740475440, '23602.34'],
+ [1740476160, '19305.89'],
+ [1740476880, '24450.67'],
+ [1740477600, '18858.90'],
+ [1740478320, '26492.34'],
+ [1740479040, '19588.90'],
+ [1740479760, '28288.45'],
+ [1740480480, '19765.67'],
+ [1740481200, '23398.90'],
+ [1740481920, '19492.34'],
+ [1740482640, '25929.45'],
+ [1740483360, '18329.67'],
+ [1740484080, '24659.89'],
+ [1740484800, '19459.23'],
+ [1740485520, '27559.67'],
+ [1740486240, '18259.89'],
+ [1740486960, '25689.90'],
+ [1740487680, '19959.34'],
+ [1740488400, '28359.67'],
+ [1740489120, '19489.90'],
+ [1740489840, '26429.34'],
+ [1740490560, '19729.45'],
+ [1740491280, '28289.67'],
+ [1740492000, '19859.90'],
+];
+
+export const PercentageChartExample: Story = {
+ args: {
+ series: [
+ {
+ data: prometheusData,
+ resource: 'ip-10-160-122-207.eu-north-1.compute.internal',
+ metricPrefix: 'instance:10.160.122.207:9100',
+ getTooltipLabel: (prefix, resource) => `${resource}`,
+ getLegendLabel: (prefix, resource) => `${resource}`,
+ },
+ ],
+ title: 'CPU Usage',
+ height: 200,
+ startingTimeStamp: prometheusData[0][0],
+ isLoading: false,
+ isLegendHidden: false,
+ helpText: 'This is the help text',
+ yAxisType: 'percentage',
+ yAxisTitle: '',
+ },
+};
+const UNIT_RANGE_BS = [
+ {
+ threshold: 1,
+ label: 'B/s',
+ },
+ {
+ threshold: 1024,
+ label: 'KiB/s',
+ },
+ {
+ threshold: 1024 * 1024,
+ label: 'MiB/s',
+ },
+ {
+ threshold: 1024 * 1024 * 1024,
+ label: 'GiB/s',
+ },
+ {
+ threshold: 1024 * 1024 * 1024 * 1024,
+ label: 'TiB/s',
+ },
+];
+export const SymmetricalAxisExample: Story = {
+ args: {
+ series: {
+ above: [
+ {
+ data: prometheusData,
+ resource: 'ip-10-160-122-207.eu-north-1.compute.internal',
+ metricPrefix: 'in',
+ getTooltipLabel: (prefix, resource) => `${resource}-${prefix}`,
+ getLegendLabel: (prefix, resource) => `${resource}}`,
+ },
+ {
+ data: prometheusData2,
+ resource: 'ip-10-160-122-207.eu-north-2.compute.internal',
+ metricPrefix: 'in',
+ getTooltipLabel: (prefix, resource) => `${resource}-${prefix}`,
+ getLegendLabel: (prefix, resource) => `${resource}}`,
+ },
+ ],
+ below: [
+ {
+ data: prometheusData3,
+ resource: 'ip-10-160-122-207.eu-north-1.compute.internal',
+ metricPrefix: 'out',
+ getTooltipLabel: (prefix, resource) => `${resource}-${prefix}`,
+ getLegendLabel: (prefix, resource) => `${resource}`,
+ },
+ {
+ data: prometheusData4,
+ resource: 'ip-10-160-122-207.eu-north-2.compute.internal',
+ metricPrefix: 'out',
+ getTooltipLabel: (prefix, resource) => `${resource}-${prefix}`,
+ getLegendLabel: (prefix, resource) => `${resource}`,
+ },
+ ],
+ },
+ title: 'ControlPlane Bandwidth',
+ height: 200,
+ startingTimeStamp: prometheusData[0][0],
+ unitRange: UNIT_RANGE_BS,
+ isLoading: false,
+ isLegendHidden: false,
+ yAxisType: 'symmetrical',
+ yAxisTitle: 'in(+)/out(-)',
+ },
+};
+export const AutoUnitChartExample: Story = {
+ args: {
+ series: [
+ {
+ data: prometheusData5,
+ resource: 'ip-10-160-122-207.eu-north-1.compute.internal',
+ metricPrefix: 'instance:10.160.122.207:9100',
+ getTooltipLabel: (prefix, resource) => `${resource}`,
+ getLegendLabel: (prefix, resource) => `${resource}`,
+ },
+ ],
+ title: 'Disk Throughput',
+ startingTimeStamp: prometheusData5[0][0],
+ height: 200,
+ unitRange: UNIT_RANGE_BS,
+ yAxisType: 'default',
+ },
+};