@openshift-migration-advisor/shared-components 0.21.0-9429b0a115bf → 0.22.0-5dc08904d608
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/charts/MigrationDonutChart.d.ts +18 -1
- package/dist/charts/MigrationDonutChart.d.ts.map +1 -1
- package/dist/charts/MigrationDonutChart.js +109 -43
- package/dist/report/constants.d.ts +12 -0
- package/dist/report/constants.d.ts.map +1 -1
- package/dist/report/constants.js +13 -2
- package/dist/report/osSupportTier.d.ts.map +1 -1
- package/dist/report/osSupportTier.js +1 -8
- package/package.json +3 -2
- package/src/charts/MigrationDonutChart.tsx +241 -111
- package/src/report/constants.ts +16 -2
- package/src/report/osSupportTier.ts +10 -8
|
@@ -15,6 +15,7 @@ export interface MigrationDonutChartDatum {
|
|
|
15
15
|
clusterNames?: string[];
|
|
16
16
|
networkNames?: string[];
|
|
17
17
|
}
|
|
18
|
+
export type MigrationDonutChartLegendVariant = "html" | "chart";
|
|
18
19
|
export interface MigrationDonutChartProps {
|
|
19
20
|
data: MigrationDonutChartDatum[];
|
|
20
21
|
legend?: Record<string, string>;
|
|
@@ -40,13 +41,29 @@ export interface MigrationDonutChartProps {
|
|
|
40
41
|
percent: number;
|
|
41
42
|
total: number;
|
|
42
43
|
}) => string;
|
|
44
|
+
/** Agent-ui: slice / legend click drill-down. */
|
|
43
45
|
onItemClick?: (item: MigrationDonutChartDatum) => void;
|
|
46
|
+
/** Agent-ui: clickable center title overlay. */
|
|
44
47
|
onTitleClick?: () => void;
|
|
45
|
-
/**
|
|
48
|
+
/** Agent-ui: custom HTML legend labels (ignored when legendVariant is "chart"). */
|
|
46
49
|
legendLabelFormatter?: (item: {
|
|
47
50
|
x: string;
|
|
48
51
|
countDisplay?: string | number;
|
|
49
52
|
}) => string;
|
|
53
|
+
/**
|
|
54
|
+
* Legend rendering mode.
|
|
55
|
+
* - `"html"` (default): custom clickable legend used by agent-ui
|
|
56
|
+
* - `"chart"`: PatternFly ChartLegend used by ui-app
|
|
57
|
+
*/
|
|
58
|
+
legendVariant?: MigrationDonutChartLegendVariant;
|
|
59
|
+
/** ui-app ChartLegend: total legend width. */
|
|
60
|
+
legendWidth?: number;
|
|
61
|
+
/** ui-app ChartLegend: columns per row. */
|
|
62
|
+
itemsPerRow?: number;
|
|
63
|
+
/** ui-app ChartLegend: label font size. */
|
|
64
|
+
labelFontSize?: number;
|
|
65
|
+
/** Prefer themed Victory ChartTooltip (default true for chart legend). */
|
|
66
|
+
useThemedTooltip?: boolean;
|
|
50
67
|
}
|
|
51
68
|
export declare const MigrationDonutChart: FC<MigrationDonutChartProps>;
|
|
52
69
|
export default MigrationDonutChart;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MigrationDonutChart.d.ts","sourceRoot":"","sources":["../../src/charts/MigrationDonutChart.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MigrationDonutChart.d.ts","sourceRoot":"","sources":["../../src/charts/MigrationDonutChart.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,EAAE,EAAgB,MAAM,OAAO,CAAC;AAG9C,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1C,WAAW,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,MAAM,gCAAgC,GAAG,MAAM,GAAG,OAAO,CAAC;AAEhE,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,wBAAwB,EAAE,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qBAAqB,CAAC,EAAE,CAAC,IAAI,EAAE;QAC7B,KAAK,EAAE;YACL,CAAC,EAAE,MAAM,CAAC;YACV,CAAC,EAAE,MAAM,CAAC;YACV,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;YAC/B,cAAc,EAAE,MAAM,CAAC;SACxB,CAAC;QACF,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;KACf,KAAK,MAAM,CAAC;IACb,iDAAiD;IACjD,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,wBAAwB,KAAK,IAAI,CAAC;IACvD,gDAAgD;IAChD,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,mFAAmF;IACnF,oBAAoB,CAAC,EAAE,CAAC,IAAI,EAAE;QAC5B,CAAC,EAAE,MAAM,CAAC;QACV,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;KAChC,KAAK,MAAM,CAAC;IACb;;;;OAIG;IACH,aAAa,CAAC,EAAE,gCAAgC,CAAC;IACjD,8CAA8C;IAC9C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,0EAA0E;IAC1E,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AA8ED,eAAO,MAAM,mBAAmB,EAAE,EAAE,CAAC,wBAAwB,CAiX5D,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
|
|
@@ -1,10 +1,40 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { css } from "@emotion/css";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
// Workaround
|
|
4
|
+
// Shared-components ships as strict ESM ("type": "module"), so webpack
|
|
5
|
+
// enforces fully-specified imports in consuming apps. The documented
|
|
6
|
+
// "@patternfly/react-charts/victory" subpath has no exports map entry and
|
|
7
|
+
// resolves to a directory, which webpack refuses under fully-specified —
|
|
8
|
+
// breaking the ui-app federated build.
|
|
9
|
+
import { ChartDonut, ChartLabel, ChartLegend, ChartTooltip, } from "@patternfly/react-charts/dist/esm/victory/index.js";
|
|
10
|
+
import { Flex, FlexItem } from "@patternfly/react-core";
|
|
6
11
|
import { useCallback, useMemo } from "react";
|
|
7
|
-
const legendColors = [
|
|
12
|
+
const legendColors = [
|
|
13
|
+
"#0066cc",
|
|
14
|
+
"#5e40be",
|
|
15
|
+
"#009596",
|
|
16
|
+
"#4cb140",
|
|
17
|
+
"#f0ab00",
|
|
18
|
+
"#b98412",
|
|
19
|
+
"#a30000",
|
|
20
|
+
"#b6a6e9",
|
|
21
|
+
];
|
|
22
|
+
/** Victory chart tooltips that respect PatternFly light/dark themes. */
|
|
23
|
+
const themedChartTooltipStyle = {
|
|
24
|
+
fontSize: 9,
|
|
25
|
+
fill: "var(--pf-t--global--text--color--regular)",
|
|
26
|
+
};
|
|
27
|
+
const themedChartTooltipFlyoutStyle = {
|
|
28
|
+
stroke: "var(--pf-t--global--border--color--default)",
|
|
29
|
+
strokeWidth: 1,
|
|
30
|
+
fill: "var(--pf-t--global--background--color--floating--default)",
|
|
31
|
+
};
|
|
32
|
+
const themedChartTooltipFlyoutPadding = {
|
|
33
|
+
top: 6,
|
|
34
|
+
bottom: 6,
|
|
35
|
+
left: 10,
|
|
36
|
+
right: 10,
|
|
37
|
+
};
|
|
8
38
|
const styles = {
|
|
9
39
|
legendIcon: css `
|
|
10
40
|
margin-right: 4px;
|
|
@@ -50,21 +80,25 @@ const styles = {
|
|
|
50
80
|
padding: var(--pf-t--global--spacer--ml);
|
|
51
81
|
`,
|
|
52
82
|
};
|
|
53
|
-
export const MigrationDonutChart = ({ data, legend, customColors, height = 260, width = 420, title, subTitle, titleColor = "
|
|
83
|
+
export const MigrationDonutChart = ({ data, legend, customColors, height = 260, width = 420, title, subTitle, titleColor = "var(--pf-t--global--text--color--regular)", subTitleColor = "var(--pf-t--global--text--color--subtle)", marginLeft = "0%", titleFontSize = 28, subTitleFontSize = 14, donutThickness = 45, padAngle = 1, tooltipLabelFormatter, onItemClick, onTitleClick, legendLabelFormatter, legendVariant = "html", legendWidth, itemsPerRow = 1, labelFontSize = 25, useThemedTooltip, }) => {
|
|
84
|
+
const showChartLegend = legendVariant === "chart";
|
|
85
|
+
const themedTooltipEnabled = useThemedTooltip ?? showChartLegend;
|
|
54
86
|
const dynamicLegend = useMemo(() => {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
87
|
+
const legendMap = {};
|
|
88
|
+
const seen = new Set();
|
|
89
|
+
for (const item of data) {
|
|
90
|
+
const key = item.legendCategory;
|
|
91
|
+
if (!seen.has(key)) {
|
|
92
|
+
seen.add(key);
|
|
93
|
+
legendMap[key] =
|
|
94
|
+
customColors?.[key] ??
|
|
95
|
+
legendColors[(seen.size - 1) % legendColors.length];
|
|
62
96
|
}
|
|
63
|
-
|
|
64
|
-
|
|
97
|
+
}
|
|
98
|
+
return legendMap;
|
|
65
99
|
}, [data, customColors]);
|
|
66
|
-
const chartLegend = legend
|
|
67
|
-
const getColor = useCallback((name) => chartLegend[name], [chartLegend]);
|
|
100
|
+
const chartLegend = legend ?? dynamicLegend;
|
|
101
|
+
const getColor = useCallback((name) => chartLegend[name] ?? legendColors[0], [chartLegend]);
|
|
68
102
|
const chartData = useMemo(() => {
|
|
69
103
|
return data.map((item) => ({
|
|
70
104
|
x: item.name,
|
|
@@ -76,6 +110,29 @@ export const MigrationDonutChart = ({ data, legend, customColors, height = 260,
|
|
|
76
110
|
const colorScale = useMemo(() => {
|
|
77
111
|
return chartData.map((item) => getColor(item.legendCategory));
|
|
78
112
|
}, [chartData, getColor]);
|
|
113
|
+
const legendData = useMemo(() => {
|
|
114
|
+
return chartData.map((item) => ({
|
|
115
|
+
name: `${item.x} (${item.countDisplay})`,
|
|
116
|
+
symbol: { fill: getColor(item.legendCategory) },
|
|
117
|
+
}));
|
|
118
|
+
}, [chartData, getColor]);
|
|
119
|
+
const legendWidthValue = legendWidth ?? 800;
|
|
120
|
+
const legendX = useMemo(() => {
|
|
121
|
+
const symbolAndGap = 34;
|
|
122
|
+
const charWidth = labelFontSize * 0.55;
|
|
123
|
+
const itemWidths = legendData.map((d) => symbolAndGap + d.name.length * charWidth);
|
|
124
|
+
const numCols = Math.min(legendData.length, itemsPerRow);
|
|
125
|
+
const gutter = 16;
|
|
126
|
+
let contentWidth = (numCols - 1) * gutter;
|
|
127
|
+
for (let c = 0; c < numCols; c++) {
|
|
128
|
+
let maxW = 0;
|
|
129
|
+
for (let i = c; i < itemWidths.length; i += itemsPerRow) {
|
|
130
|
+
maxW = Math.max(maxW, itemWidths[i] ?? 0);
|
|
131
|
+
}
|
|
132
|
+
contentWidth += maxW;
|
|
133
|
+
}
|
|
134
|
+
return Math.max(0, (legendWidthValue - contentWidth) / 2);
|
|
135
|
+
}, [legendData, itemsPerRow, legendWidthValue, labelFontSize]);
|
|
79
136
|
const innerRadius = useMemo(() => {
|
|
80
137
|
const outerApprox = Math.min(width, height) / 2;
|
|
81
138
|
const computed = outerApprox - donutThickness;
|
|
@@ -124,24 +181,42 @@ export const MigrationDonutChart = ({ data, legend, customColors, height = 260,
|
|
|
124
181
|
},
|
|
125
182
|
];
|
|
126
183
|
}, [onItemClick, handleClick]);
|
|
184
|
+
const donutTooltip = useMemo(() => themedTooltipEnabled ? (_jsx(ChartTooltip, { style: themedChartTooltipStyle, flyoutStyle: themedChartTooltipFlyoutStyle, flyoutPadding: themedChartTooltipFlyoutPadding })) : undefined, [themedTooltipEnabled]);
|
|
185
|
+
const formatLabel = useCallback((datum) => {
|
|
186
|
+
const percent = totalY > 0 ? (Number(datum.y) / totalY) * 100 : 0;
|
|
187
|
+
return tooltipLabelFormatter
|
|
188
|
+
? tooltipLabelFormatter({
|
|
189
|
+
datum: {
|
|
190
|
+
x: datum.x,
|
|
191
|
+
y: Number(datum.y),
|
|
192
|
+
countDisplay: datum.countDisplay,
|
|
193
|
+
legendCategory: datum.legendCategory,
|
|
194
|
+
},
|
|
195
|
+
percent,
|
|
196
|
+
total: totalY,
|
|
197
|
+
})
|
|
198
|
+
: `${datum.x}: ${datum.countDisplay ?? datum.y}`;
|
|
199
|
+
}, [tooltipLabelFormatter, totalY]);
|
|
127
200
|
if (!data || data.length === 0) {
|
|
128
|
-
return
|
|
201
|
+
return null;
|
|
129
202
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
},
|
|
203
|
+
const htmlLegend = (_jsx(Flex, { className: `${styles.legendContainer} ${css `margin-left: ${marginLeft};`}`, justifyContent: { default: "justifyContentCenter" }, alignItems: { default: "alignItemsFlexStart" }, children: _jsx(Flex, { className: styles.legendInner, spaceItems: { default: "spaceItemsMd" }, justifyContent: { default: "justifyContentCenter" }, alignItems: { default: "alignItemsCenter" }, flexWrap: { default: "wrap" }, children: data.map((item) => {
|
|
204
|
+
const label = legendLabelFormatter
|
|
205
|
+
? legendLabelFormatter({
|
|
206
|
+
x: item.name,
|
|
207
|
+
countDisplay: item.countDisplay,
|
|
208
|
+
})
|
|
209
|
+
: item.name;
|
|
210
|
+
const content = (_jsxs(_Fragment, { children: [_jsxs("svg", { width: "10", height: "10", "aria-hidden": "true", className: styles.legendIcon, children: [_jsx("title", { children: "Legend color indicator" }), _jsx("rect", { width: "10", height: "10", fill: getColor(item.legendCategory) })] }), _jsx("span", { children: label })] }));
|
|
211
|
+
return (_jsx(FlexItem, { children: onItemClick ? (_jsx("button", { type: "button", onClick: () => onItemClick(item), className: styles.legendButton, children: content })) : (_jsx("span", { className: styles.legendLabel, children: content })) }, `${item.legendCategory}-${item.name}`));
|
|
212
|
+
}) }) }));
|
|
213
|
+
const chartLegendElement = (_jsx(FlexItem, { flex: { default: "flex_1" }, children: _jsx(Flex, { justifyContent: { default: "justifyContentCenter" }, alignItems: { default: "alignItemsCenter" }, children: _jsx(FlexItem, { children: _jsx(ChartLegend, { data: legendData, orientation: "horizontal", height: 200, width: legendWidthValue, x: legendX, itemsPerRow: itemsPerRow, style: {
|
|
214
|
+
labels: {
|
|
215
|
+
fontSize: labelFontSize,
|
|
216
|
+
fill: "var(--pf-t--global--text--color--regular)",
|
|
217
|
+
},
|
|
218
|
+
} }) }) }) }));
|
|
219
|
+
return (_jsxs(Flex, { direction: { default: "column" }, alignItems: { default: "alignItemsCenter" }, className: `${onItemClick ? styles.cursorPointer : styles.cursorDefault} ${styles.chartContainer}`, children: [_jsxs("div", { className: styles.donutWrapper, children: [_jsx(ChartDonut, { ariaDesc: "Migration data donut chart", data: chartData, events: chartEvents, labelComponent: donutTooltip, labels: ({ datum, }) => formatLabel(datum), colorScale: colorScale, constrainToVisibleArea: true, innerRadius: innerRadius, padAngle: padAngle, title: title, subTitle: subTitle, height: height, width: width, padding: {
|
|
145
220
|
bottom: 5,
|
|
146
221
|
left: 20,
|
|
147
222
|
right: 20,
|
|
@@ -152,7 +227,7 @@ export const MigrationDonutChart = ({ data, legend, customColors, height = 260,
|
|
|
152
227
|
fontSize: titleFontSize,
|
|
153
228
|
fontWeight: "bold",
|
|
154
229
|
},
|
|
155
|
-
] })) : undefined, subTitleComponent: subTitle ? (_jsx(ChartLabel, { style: [
|
|
230
|
+
] })) : undefined, subTitleComponent: subTitle ? (_jsx(ChartLabel, { dy: showChartLegend ? 7 : undefined, style: [
|
|
156
231
|
{
|
|
157
232
|
fill: subTitleColor,
|
|
158
233
|
fontSize: subTitleFontSize,
|
|
@@ -172,15 +247,6 @@ export const MigrationDonutChart = ({ data, legend, customColors, height = 260,
|
|
|
172
247
|
e.preventDefault();
|
|
173
248
|
onTitleClick();
|
|
174
249
|
}
|
|
175
|
-
} }))] }),
|
|
176
|
-
const label = legendLabelFormatter
|
|
177
|
-
? legendLabelFormatter({
|
|
178
|
-
x: item.name,
|
|
179
|
-
countDisplay: item.countDisplay,
|
|
180
|
-
})
|
|
181
|
-
: item.name;
|
|
182
|
-
const content = (_jsxs(_Fragment, { children: [_jsxs("svg", { width: "10", height: "10", "aria-hidden": "true", className: styles.legendIcon, children: [_jsx("title", { children: "Legend color indicator" }), _jsx("rect", { width: "10", height: "10", fill: getColor(item.legendCategory) })] }), _jsx("span", { children: label })] }));
|
|
183
|
-
return (_jsx(FlexItem, { children: onItemClick ? (_jsx("button", { type: "button", onClick: () => onItemClick(item), className: styles.legendButton, children: content })) : (_jsx("span", { className: styles.legendLabel, children: content })) }, `${item.legendCategory}-${item.name}`));
|
|
184
|
-
}) }) })] }));
|
|
250
|
+
} }))] }), showChartLegend ? chartLegendElement : htmlLegend] }));
|
|
185
251
|
};
|
|
186
252
|
export default MigrationDonutChart;
|
|
@@ -6,6 +6,18 @@
|
|
|
6
6
|
export declare const chartColorSuccess: "#0066cc";
|
|
7
7
|
export declare const chartColorFailure: "#dca614";
|
|
8
8
|
export declare const REPORT_CARD_EMPTY_STATE_TITLES: {
|
|
9
|
+
readonly networks: "Network data not collected";
|
|
10
|
+
readonly nicCount: "NIC count data not collected";
|
|
11
|
+
readonly hosts: "Host data not collected";
|
|
12
|
+
readonly cpuMemory: "CPU and memory data not collected";
|
|
13
|
+
readonly cpu: "CPU data not collected";
|
|
14
|
+
readonly memory: "Memory data not collected";
|
|
15
|
+
readonly storage: "Storage data not collected";
|
|
16
|
+
readonly diskTypes: "Disk type data not collected";
|
|
17
|
+
readonly clusters: "Cluster data not collected";
|
|
18
|
+
readonly cpuOvercommitment: "CPU overcommitment data not collected";
|
|
9
19
|
readonly operatingSystems: "Operating system data not collected";
|
|
20
|
+
readonly migrationStatus: "Migration status data not collected";
|
|
21
|
+
readonly issuesBreakdown: "Issues breakdown data not collected";
|
|
10
22
|
};
|
|
11
23
|
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/report/constants.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/report/constants.ts"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,WAA6B,CAAC;AAC5D,eAAO,MAAM,iBAAiB,WAA+B,CAAC;AAE9D,eAAO,MAAM,8BAA8B;;;;;;;;;;;;;;CAcjC,CAAC"}
|
package/dist/report/constants.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { chart_color_blue_300 } from "@patternfly/react-tokens
|
|
2
|
-
import { chart_color_yellow_300 } from "@patternfly/react-tokens/dist/esm/chart_color_yellow_300";
|
|
1
|
+
import { chart_color_blue_300, chart_color_yellow_300, } from "@patternfly/react-tokens";
|
|
3
2
|
/**
|
|
4
3
|
* PatternFly chart color semantics (https://www.patternfly.org/charts/colors-for-charts/design-guidelines/#best-practices):
|
|
5
4
|
* Blue → success / migratable / supported
|
|
@@ -8,5 +7,17 @@ import { chart_color_yellow_300 } from "@patternfly/react-tokens/dist/esm/chart_
|
|
|
8
7
|
export const chartColorSuccess = chart_color_blue_300.value;
|
|
9
8
|
export const chartColorFailure = chart_color_yellow_300.value;
|
|
10
9
|
export const REPORT_CARD_EMPTY_STATE_TITLES = {
|
|
10
|
+
networks: "Network data not collected",
|
|
11
|
+
nicCount: "NIC count data not collected",
|
|
12
|
+
hosts: "Host data not collected",
|
|
13
|
+
cpuMemory: "CPU and memory data not collected",
|
|
14
|
+
cpu: "CPU data not collected",
|
|
15
|
+
memory: "Memory data not collected",
|
|
16
|
+
storage: "Storage data not collected",
|
|
17
|
+
diskTypes: "Disk type data not collected",
|
|
18
|
+
clusters: "Cluster data not collected",
|
|
19
|
+
cpuOvercommitment: "CPU overcommitment data not collected",
|
|
11
20
|
operatingSystems: "Operating system data not collected",
|
|
21
|
+
migrationStatus: "Migration status data not collected",
|
|
22
|
+
issuesBreakdown: "Issues breakdown data not collected",
|
|
12
23
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"osSupportTier.d.ts","sourceRoot":"","sources":["../../src/report/osSupportTier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"osSupportTier.d.ts","sourceRoot":"","sources":["../../src/report/osSupportTier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAYzD;;;GAGG;AACH,eAAO,MAAM,WAAW;;;;;CAKd,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,OAAO,WAAW,CAAC,CAAC;AAEzE,eAAO,MAAM,2BAA2B,+CACM,CAAC;AAE/C,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AASD,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAK3D,CAAC;AAEF,0DAA0D;AAC1D,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAShE,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,WAAW,EAK9C,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,MAAM,CAC5C,WAAW,EACX,WAAW,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAMjC,CAAC;AAEF,MAAM,WAAW,qBAAqB;IACpC,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,4FAA4F;AAC5F,eAAO,MAAM,gCAAgC,EAAE,MAAM,CACnD,WAAW,EACX,qBAAqB,CAkBtB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,WAAW,GAAG,SAAS,EACpC,SAAS,EAAE,OAAO,GACjB,WAAW,CAMb;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,GAC1C,OAAO,CAeT;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAEjE;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAEnE;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAElE;AAED,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,WAAW,GAChB,WAAW,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAElC;AAED,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,WAAW,GAChB,qBAAqB,CAEvB"}
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
import { t_color_blue_20 } from "@patternfly/react-tokens
|
|
2
|
-
import { t_color_blue_60 } from "@patternfly/react-tokens/dist/esm/t_color_blue_60";
|
|
3
|
-
import { t_color_green_20 } from "@patternfly/react-tokens/dist/esm/t_color_green_20";
|
|
4
|
-
import { t_color_green_60 } from "@patternfly/react-tokens/dist/esm/t_color_green_60";
|
|
5
|
-
import { t_color_orange_20 } from "@patternfly/react-tokens/dist/esm/t_color_orange_20";
|
|
6
|
-
import { t_color_orange_60 } from "@patternfly/react-tokens/dist/esm/t_color_orange_60";
|
|
7
|
-
import { t_color_yellow_20 } from "@patternfly/react-tokens/dist/esm/t_color_yellow_20";
|
|
8
|
-
import { t_color_yellow_60 } from "@patternfly/react-tokens/dist/esm/t_color_yellow_60";
|
|
1
|
+
import { t_color_blue_20, t_color_blue_60, t_color_green_20, t_color_green_60, t_color_orange_20, t_color_orange_60, t_color_yellow_20, t_color_yellow_60, } from "@patternfly/react-tokens";
|
|
9
2
|
/**
|
|
10
3
|
* Guest OS support tiers (matches agent-sdk / planner-sdk `OsInfoSupportTierEnum` values).
|
|
11
4
|
* Defined locally so this package stays SDK-agnostic.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openshift-migration-advisor/shared-components",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0-5dc08904d608",
|
|
4
4
|
"description": "Shared React UI components for Migration Advisor apps (report charts, forms, OS support tiers)",
|
|
5
5
|
"author": "Montse Ortega <mortegag@redhat.com>",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -35,7 +35,8 @@
|
|
|
35
35
|
"clean": "rm -rf node_modules dist .tmp",
|
|
36
36
|
"check": "yarn run -T biome check .",
|
|
37
37
|
"check:fix": "yarn run -T biome check --write .",
|
|
38
|
-
"test": "
|
|
38
|
+
"test": "yarn run -T vitest run",
|
|
39
|
+
"test:watch": "yarn run -T vitest",
|
|
39
40
|
"typecheck": "yarn run -T tsc -b --noEmit"
|
|
40
41
|
},
|
|
41
42
|
"files": [
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import { css } from "@emotion/css";
|
|
2
|
-
|
|
2
|
+
// Workaround
|
|
3
|
+
// Shared-components ships as strict ESM ("type": "module"), so webpack
|
|
4
|
+
// enforces fully-specified imports in consuming apps. The documented
|
|
5
|
+
// "@patternfly/react-charts/victory" subpath has no exports map entry and
|
|
6
|
+
// resolves to a directory, which webpack refuses under fully-specified —
|
|
7
|
+
// breaking the ui-app federated build.
|
|
3
8
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
} from "@patternfly/react-
|
|
9
|
-
import {
|
|
10
|
-
import type { FC } from "react";
|
|
9
|
+
ChartDonut,
|
|
10
|
+
ChartLabel,
|
|
11
|
+
ChartLegend,
|
|
12
|
+
ChartTooltip,
|
|
13
|
+
} from "@patternfly/react-charts/dist/esm/victory/index.js";
|
|
14
|
+
import { Flex, FlexItem } from "@patternfly/react-core";
|
|
15
|
+
import type { FC, ReactElement } from "react";
|
|
11
16
|
import { useCallback, useMemo } from "react";
|
|
12
17
|
|
|
13
18
|
export interface MigrationDonutChartDatum {
|
|
@@ -21,6 +26,8 @@ export interface MigrationDonutChartDatum {
|
|
|
21
26
|
networkNames?: string[];
|
|
22
27
|
}
|
|
23
28
|
|
|
29
|
+
export type MigrationDonutChartLegendVariant = "html" | "chart";
|
|
30
|
+
|
|
24
31
|
export interface MigrationDonutChartProps {
|
|
25
32
|
data: MigrationDonutChartDatum[];
|
|
26
33
|
legend?: Record<string, string>;
|
|
@@ -46,16 +53,60 @@ export interface MigrationDonutChartProps {
|
|
|
46
53
|
percent: number;
|
|
47
54
|
total: number;
|
|
48
55
|
}) => string;
|
|
56
|
+
/** Agent-ui: slice / legend click drill-down. */
|
|
49
57
|
onItemClick?: (item: MigrationDonutChartDatum) => void;
|
|
58
|
+
/** Agent-ui: clickable center title overlay. */
|
|
50
59
|
onTitleClick?: () => void;
|
|
51
|
-
/**
|
|
60
|
+
/** Agent-ui: custom HTML legend labels (ignored when legendVariant is "chart"). */
|
|
52
61
|
legendLabelFormatter?: (item: {
|
|
53
62
|
x: string;
|
|
54
63
|
countDisplay?: string | number;
|
|
55
64
|
}) => string;
|
|
65
|
+
/**
|
|
66
|
+
* Legend rendering mode.
|
|
67
|
+
* - `"html"` (default): custom clickable legend used by agent-ui
|
|
68
|
+
* - `"chart"`: PatternFly ChartLegend used by ui-app
|
|
69
|
+
*/
|
|
70
|
+
legendVariant?: MigrationDonutChartLegendVariant;
|
|
71
|
+
/** ui-app ChartLegend: total legend width. */
|
|
72
|
+
legendWidth?: number;
|
|
73
|
+
/** ui-app ChartLegend: columns per row. */
|
|
74
|
+
itemsPerRow?: number;
|
|
75
|
+
/** ui-app ChartLegend: label font size. */
|
|
76
|
+
labelFontSize?: number;
|
|
77
|
+
/** Prefer themed Victory ChartTooltip (default true for chart legend). */
|
|
78
|
+
useThemedTooltip?: boolean;
|
|
56
79
|
}
|
|
57
80
|
|
|
58
|
-
const legendColors = [
|
|
81
|
+
const legendColors = [
|
|
82
|
+
"#0066cc",
|
|
83
|
+
"#5e40be",
|
|
84
|
+
"#009596",
|
|
85
|
+
"#4cb140",
|
|
86
|
+
"#f0ab00",
|
|
87
|
+
"#b98412",
|
|
88
|
+
"#a30000",
|
|
89
|
+
"#b6a6e9",
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
/** Victory chart tooltips that respect PatternFly light/dark themes. */
|
|
93
|
+
const themedChartTooltipStyle = {
|
|
94
|
+
fontSize: 9,
|
|
95
|
+
fill: "var(--pf-t--global--text--color--regular)",
|
|
96
|
+
} as const;
|
|
97
|
+
|
|
98
|
+
const themedChartTooltipFlyoutStyle = {
|
|
99
|
+
stroke: "var(--pf-t--global--border--color--default)",
|
|
100
|
+
strokeWidth: 1,
|
|
101
|
+
fill: "var(--pf-t--global--background--color--floating--default)",
|
|
102
|
+
} as const;
|
|
103
|
+
|
|
104
|
+
const themedChartTooltipFlyoutPadding = {
|
|
105
|
+
top: 6,
|
|
106
|
+
bottom: 6,
|
|
107
|
+
left: 10,
|
|
108
|
+
right: 10,
|
|
109
|
+
} as const;
|
|
59
110
|
|
|
60
111
|
const styles = {
|
|
61
112
|
legendIcon: css`
|
|
@@ -111,8 +162,8 @@ export const MigrationDonutChart: FC<MigrationDonutChartProps> = ({
|
|
|
111
162
|
width = 420,
|
|
112
163
|
title,
|
|
113
164
|
subTitle,
|
|
114
|
-
titleColor = "
|
|
115
|
-
subTitleColor = "
|
|
165
|
+
titleColor = "var(--pf-t--global--text--color--regular)",
|
|
166
|
+
subTitleColor = "var(--pf-t--global--text--color--subtle)",
|
|
116
167
|
marginLeft = "0%",
|
|
117
168
|
titleFontSize = 28,
|
|
118
169
|
subTitleFontSize = 14,
|
|
@@ -122,30 +173,35 @@ export const MigrationDonutChart: FC<MigrationDonutChartProps> = ({
|
|
|
122
173
|
onItemClick,
|
|
123
174
|
onTitleClick,
|
|
124
175
|
legendLabelFormatter,
|
|
176
|
+
legendVariant = "html",
|
|
177
|
+
legendWidth,
|
|
178
|
+
itemsPerRow = 1,
|
|
179
|
+
labelFontSize = 25,
|
|
180
|
+
useThemedTooltip,
|
|
125
181
|
}) => {
|
|
182
|
+
const showChartLegend = legendVariant === "chart";
|
|
183
|
+
const themedTooltipEnabled = useThemedTooltip ?? showChartLegend;
|
|
184
|
+
|
|
126
185
|
const dynamicLegend = useMemo(() => {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
result: Record<string, string>[];
|
|
142
|
-
},
|
|
143
|
-
).result;
|
|
186
|
+
const legendMap: Record<string, string> = {};
|
|
187
|
+
const seen = new Set<string>();
|
|
188
|
+
|
|
189
|
+
for (const item of data) {
|
|
190
|
+
const key = item.legendCategory;
|
|
191
|
+
if (!seen.has(key)) {
|
|
192
|
+
seen.add(key);
|
|
193
|
+
legendMap[key] =
|
|
194
|
+
customColors?.[key] ??
|
|
195
|
+
legendColors[(seen.size - 1) % legendColors.length];
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return legendMap;
|
|
144
200
|
}, [data, customColors]);
|
|
145
201
|
|
|
146
|
-
const chartLegend = legend
|
|
202
|
+
const chartLegend = legend ?? dynamicLegend;
|
|
147
203
|
const getColor = useCallback(
|
|
148
|
-
(name: string): string => chartLegend[name],
|
|
204
|
+
(name: string): string => chartLegend[name] ?? legendColors[0],
|
|
149
205
|
[chartLegend],
|
|
150
206
|
);
|
|
151
207
|
|
|
@@ -162,6 +218,33 @@ export const MigrationDonutChart: FC<MigrationDonutChartProps> = ({
|
|
|
162
218
|
return chartData.map((item) => getColor(item.legendCategory));
|
|
163
219
|
}, [chartData, getColor]);
|
|
164
220
|
|
|
221
|
+
const legendData = useMemo(() => {
|
|
222
|
+
return chartData.map((item) => ({
|
|
223
|
+
name: `${item.x} (${item.countDisplay})`,
|
|
224
|
+
symbol: { fill: getColor(item.legendCategory) },
|
|
225
|
+
}));
|
|
226
|
+
}, [chartData, getColor]);
|
|
227
|
+
|
|
228
|
+
const legendWidthValue = legendWidth ?? 800;
|
|
229
|
+
const legendX = useMemo(() => {
|
|
230
|
+
const symbolAndGap = 34;
|
|
231
|
+
const charWidth = labelFontSize * 0.55;
|
|
232
|
+
const itemWidths = legendData.map(
|
|
233
|
+
(d) => symbolAndGap + d.name.length * charWidth,
|
|
234
|
+
);
|
|
235
|
+
const numCols = Math.min(legendData.length, itemsPerRow);
|
|
236
|
+
const gutter = 16;
|
|
237
|
+
let contentWidth = (numCols - 1) * gutter;
|
|
238
|
+
for (let c = 0; c < numCols; c++) {
|
|
239
|
+
let maxW = 0;
|
|
240
|
+
for (let i = c; i < itemWidths.length; i += itemsPerRow) {
|
|
241
|
+
maxW = Math.max(maxW, itemWidths[i] ?? 0);
|
|
242
|
+
}
|
|
243
|
+
contentWidth += maxW;
|
|
244
|
+
}
|
|
245
|
+
return Math.max(0, (legendWidthValue - contentWidth) / 2);
|
|
246
|
+
}, [legendData, itemsPerRow, legendWidthValue, labelFontSize]);
|
|
247
|
+
|
|
165
248
|
const innerRadius = useMemo(() => {
|
|
166
249
|
const outerApprox = Math.min(width, height) / 2;
|
|
167
250
|
const computed = outerApprox - donutThickness;
|
|
@@ -221,17 +304,132 @@ export const MigrationDonutChart: FC<MigrationDonutChartProps> = ({
|
|
|
221
304
|
];
|
|
222
305
|
}, [onItemClick, handleClick]);
|
|
223
306
|
|
|
307
|
+
const donutTooltip = useMemo(
|
|
308
|
+
() =>
|
|
309
|
+
themedTooltipEnabled ? (
|
|
310
|
+
<ChartTooltip
|
|
311
|
+
style={themedChartTooltipStyle}
|
|
312
|
+
flyoutStyle={themedChartTooltipFlyoutStyle}
|
|
313
|
+
flyoutPadding={themedChartTooltipFlyoutPadding}
|
|
314
|
+
/>
|
|
315
|
+
) : undefined,
|
|
316
|
+
[themedTooltipEnabled],
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
const formatLabel = useCallback(
|
|
320
|
+
(datum: {
|
|
321
|
+
x: string;
|
|
322
|
+
y: number;
|
|
323
|
+
legendCategory: string;
|
|
324
|
+
countDisplay?: string | number;
|
|
325
|
+
}) => {
|
|
326
|
+
const percent = totalY > 0 ? (Number(datum.y) / totalY) * 100 : 0;
|
|
327
|
+
return tooltipLabelFormatter
|
|
328
|
+
? tooltipLabelFormatter({
|
|
329
|
+
datum: {
|
|
330
|
+
x: datum.x,
|
|
331
|
+
y: Number(datum.y),
|
|
332
|
+
countDisplay: datum.countDisplay,
|
|
333
|
+
legendCategory: datum.legendCategory,
|
|
334
|
+
},
|
|
335
|
+
percent,
|
|
336
|
+
total: totalY,
|
|
337
|
+
})
|
|
338
|
+
: `${datum.x}: ${datum.countDisplay ?? datum.y}`;
|
|
339
|
+
},
|
|
340
|
+
[tooltipLabelFormatter, totalY],
|
|
341
|
+
);
|
|
342
|
+
|
|
224
343
|
if (!data || data.length === 0) {
|
|
225
|
-
return
|
|
226
|
-
<EmptyState
|
|
227
|
-
headingLevel="h3"
|
|
228
|
-
titleText="No data available"
|
|
229
|
-
icon={InboxIcon}
|
|
230
|
-
variant={EmptyStateVariant.xs}
|
|
231
|
-
/>
|
|
232
|
-
);
|
|
344
|
+
return null;
|
|
233
345
|
}
|
|
234
346
|
|
|
347
|
+
const htmlLegend: ReactElement = (
|
|
348
|
+
<Flex
|
|
349
|
+
className={`${styles.legendContainer} ${css`margin-left: ${marginLeft};`}`}
|
|
350
|
+
justifyContent={{ default: "justifyContentCenter" }}
|
|
351
|
+
alignItems={{ default: "alignItemsFlexStart" }}
|
|
352
|
+
>
|
|
353
|
+
<Flex
|
|
354
|
+
className={styles.legendInner}
|
|
355
|
+
spaceItems={{ default: "spaceItemsMd" }}
|
|
356
|
+
justifyContent={{ default: "justifyContentCenter" }}
|
|
357
|
+
alignItems={{ default: "alignItemsCenter" }}
|
|
358
|
+
flexWrap={{ default: "wrap" }}
|
|
359
|
+
>
|
|
360
|
+
{data.map((item) => {
|
|
361
|
+
const label = legendLabelFormatter
|
|
362
|
+
? legendLabelFormatter({
|
|
363
|
+
x: item.name,
|
|
364
|
+
countDisplay: item.countDisplay,
|
|
365
|
+
})
|
|
366
|
+
: item.name;
|
|
367
|
+
|
|
368
|
+
const content = (
|
|
369
|
+
<>
|
|
370
|
+
<svg
|
|
371
|
+
width="10"
|
|
372
|
+
height="10"
|
|
373
|
+
aria-hidden="true"
|
|
374
|
+
className={styles.legendIcon}
|
|
375
|
+
>
|
|
376
|
+
<title>Legend color indicator</title>
|
|
377
|
+
<rect
|
|
378
|
+
width="10"
|
|
379
|
+
height="10"
|
|
380
|
+
fill={getColor(item.legendCategory)}
|
|
381
|
+
/>
|
|
382
|
+
</svg>
|
|
383
|
+
<span>{label}</span>
|
|
384
|
+
</>
|
|
385
|
+
);
|
|
386
|
+
|
|
387
|
+
return (
|
|
388
|
+
<FlexItem key={`${item.legendCategory}-${item.name}`}>
|
|
389
|
+
{onItemClick ? (
|
|
390
|
+
<button
|
|
391
|
+
type="button"
|
|
392
|
+
onClick={() => onItemClick(item)}
|
|
393
|
+
className={styles.legendButton}
|
|
394
|
+
>
|
|
395
|
+
{content}
|
|
396
|
+
</button>
|
|
397
|
+
) : (
|
|
398
|
+
<span className={styles.legendLabel}>{content}</span>
|
|
399
|
+
)}
|
|
400
|
+
</FlexItem>
|
|
401
|
+
);
|
|
402
|
+
})}
|
|
403
|
+
</Flex>
|
|
404
|
+
</Flex>
|
|
405
|
+
);
|
|
406
|
+
|
|
407
|
+
const chartLegendElement: ReactElement = (
|
|
408
|
+
<FlexItem flex={{ default: "flex_1" }}>
|
|
409
|
+
<Flex
|
|
410
|
+
justifyContent={{ default: "justifyContentCenter" }}
|
|
411
|
+
alignItems={{ default: "alignItemsCenter" }}
|
|
412
|
+
>
|
|
413
|
+
<FlexItem>
|
|
414
|
+
<ChartLegend
|
|
415
|
+
data={legendData}
|
|
416
|
+
orientation="horizontal"
|
|
417
|
+
height={200}
|
|
418
|
+
width={legendWidthValue}
|
|
419
|
+
x={legendX}
|
|
420
|
+
itemsPerRow={itemsPerRow}
|
|
421
|
+
style={{
|
|
422
|
+
labels: {
|
|
423
|
+
fontSize: labelFontSize,
|
|
424
|
+
fill: "var(--pf-t--global--text--color--regular)",
|
|
425
|
+
},
|
|
426
|
+
}}
|
|
427
|
+
/>
|
|
428
|
+
</FlexItem>
|
|
429
|
+
</Flex>
|
|
430
|
+
</FlexItem>
|
|
431
|
+
);
|
|
432
|
+
|
|
235
433
|
return (
|
|
236
434
|
<Flex
|
|
237
435
|
direction={{ default: "column" }}
|
|
@@ -243,6 +441,7 @@ export const MigrationDonutChart: FC<MigrationDonutChartProps> = ({
|
|
|
243
441
|
ariaDesc="Migration data donut chart"
|
|
244
442
|
data={chartData}
|
|
245
443
|
events={chartEvents}
|
|
444
|
+
labelComponent={donutTooltip}
|
|
246
445
|
labels={({
|
|
247
446
|
datum,
|
|
248
447
|
}: {
|
|
@@ -252,21 +451,7 @@ export const MigrationDonutChart: FC<MigrationDonutChartProps> = ({
|
|
|
252
451
|
legendCategory: string;
|
|
253
452
|
countDisplay?: string | number;
|
|
254
453
|
};
|
|
255
|
-
}) =>
|
|
256
|
-
const percent = totalY > 0 ? (Number(datum.y) / totalY) * 100 : 0;
|
|
257
|
-
return tooltipLabelFormatter
|
|
258
|
-
? tooltipLabelFormatter({
|
|
259
|
-
datum: {
|
|
260
|
-
x: datum.x,
|
|
261
|
-
y: Number(datum.y),
|
|
262
|
-
countDisplay: datum.countDisplay,
|
|
263
|
-
legendCategory: datum.legendCategory,
|
|
264
|
-
},
|
|
265
|
-
percent,
|
|
266
|
-
total: totalY,
|
|
267
|
-
})
|
|
268
|
-
: `${datum.x}: ${datum.countDisplay ?? datum.y}`;
|
|
269
|
-
}}
|
|
454
|
+
}) => formatLabel(datum)}
|
|
270
455
|
colorScale={colorScale}
|
|
271
456
|
constrainToVisibleArea
|
|
272
457
|
innerRadius={innerRadius}
|
|
@@ -297,6 +482,7 @@ export const MigrationDonutChart: FC<MigrationDonutChartProps> = ({
|
|
|
297
482
|
subTitleComponent={
|
|
298
483
|
subTitle ? (
|
|
299
484
|
<ChartLabel
|
|
485
|
+
dy={showChartLegend ? 7 : undefined}
|
|
300
486
|
style={[
|
|
301
487
|
{
|
|
302
488
|
fill: subTitleColor,
|
|
@@ -334,63 +520,7 @@ export const MigrationDonutChart: FC<MigrationDonutChartProps> = ({
|
|
|
334
520
|
/>
|
|
335
521
|
)}
|
|
336
522
|
</div>
|
|
337
|
-
|
|
338
|
-
className={`${styles.legendContainer} ${css`margin-left: ${marginLeft};`}`}
|
|
339
|
-
justifyContent={{ default: "justifyContentCenter" }}
|
|
340
|
-
alignItems={{ default: "alignItemsFlexStart" }}
|
|
341
|
-
>
|
|
342
|
-
<Flex
|
|
343
|
-
className={styles.legendInner}
|
|
344
|
-
spaceItems={{ default: "spaceItemsMd" }}
|
|
345
|
-
justifyContent={{ default: "justifyContentCenter" }}
|
|
346
|
-
alignItems={{ default: "alignItemsCenter" }}
|
|
347
|
-
flexWrap={{ default: "wrap" }}
|
|
348
|
-
>
|
|
349
|
-
{data.map((item) => {
|
|
350
|
-
const label = legendLabelFormatter
|
|
351
|
-
? legendLabelFormatter({
|
|
352
|
-
x: item.name,
|
|
353
|
-
countDisplay: item.countDisplay,
|
|
354
|
-
})
|
|
355
|
-
: item.name;
|
|
356
|
-
|
|
357
|
-
const content = (
|
|
358
|
-
<>
|
|
359
|
-
<svg
|
|
360
|
-
width="10"
|
|
361
|
-
height="10"
|
|
362
|
-
aria-hidden="true"
|
|
363
|
-
className={styles.legendIcon}
|
|
364
|
-
>
|
|
365
|
-
<title>Legend color indicator</title>
|
|
366
|
-
<rect
|
|
367
|
-
width="10"
|
|
368
|
-
height="10"
|
|
369
|
-
fill={getColor(item.legendCategory)}
|
|
370
|
-
/>
|
|
371
|
-
</svg>
|
|
372
|
-
<span>{label}</span>
|
|
373
|
-
</>
|
|
374
|
-
);
|
|
375
|
-
|
|
376
|
-
return (
|
|
377
|
-
<FlexItem key={`${item.legendCategory}-${item.name}`}>
|
|
378
|
-
{onItemClick ? (
|
|
379
|
-
<button
|
|
380
|
-
type="button"
|
|
381
|
-
onClick={() => onItemClick(item)}
|
|
382
|
-
className={styles.legendButton}
|
|
383
|
-
>
|
|
384
|
-
{content}
|
|
385
|
-
</button>
|
|
386
|
-
) : (
|
|
387
|
-
<span className={styles.legendLabel}>{content}</span>
|
|
388
|
-
)}
|
|
389
|
-
</FlexItem>
|
|
390
|
-
);
|
|
391
|
-
})}
|
|
392
|
-
</Flex>
|
|
393
|
-
</Flex>
|
|
523
|
+
{showChartLegend ? chartLegendElement : htmlLegend}
|
|
394
524
|
</Flex>
|
|
395
525
|
);
|
|
396
526
|
};
|
package/src/report/constants.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
chart_color_blue_300,
|
|
3
|
+
chart_color_yellow_300,
|
|
4
|
+
} from "@patternfly/react-tokens";
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* PatternFly chart color semantics (https://www.patternfly.org/charts/colors-for-charts/design-guidelines/#best-practices):
|
|
@@ -10,5 +12,17 @@ export const chartColorSuccess = chart_color_blue_300.value;
|
|
|
10
12
|
export const chartColorFailure = chart_color_yellow_300.value;
|
|
11
13
|
|
|
12
14
|
export const REPORT_CARD_EMPTY_STATE_TITLES = {
|
|
15
|
+
networks: "Network data not collected",
|
|
16
|
+
nicCount: "NIC count data not collected",
|
|
17
|
+
hosts: "Host data not collected",
|
|
18
|
+
cpuMemory: "CPU and memory data not collected",
|
|
19
|
+
cpu: "CPU data not collected",
|
|
20
|
+
memory: "Memory data not collected",
|
|
21
|
+
storage: "Storage data not collected",
|
|
22
|
+
diskTypes: "Disk type data not collected",
|
|
23
|
+
clusters: "Cluster data not collected",
|
|
24
|
+
cpuOvercommitment: "CPU overcommitment data not collected",
|
|
13
25
|
operatingSystems: "Operating system data not collected",
|
|
26
|
+
migrationStatus: "Migration status data not collected",
|
|
27
|
+
issuesBreakdown: "Issues breakdown data not collected",
|
|
14
28
|
} as const;
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import type { LabelProps } from "@patternfly/react-core";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
2
|
+
import {
|
|
3
|
+
t_color_blue_20,
|
|
4
|
+
t_color_blue_60,
|
|
5
|
+
t_color_green_20,
|
|
6
|
+
t_color_green_60,
|
|
7
|
+
t_color_orange_20,
|
|
8
|
+
t_color_orange_60,
|
|
9
|
+
t_color_yellow_20,
|
|
10
|
+
t_color_yellow_60,
|
|
11
|
+
} from "@patternfly/react-tokens";
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* Guest OS support tiers (matches agent-sdk / planner-sdk `OsInfoSupportTierEnum` values).
|