@openshift-migration-advisor/shared-components 0.21.0-8e9be07f572f → 0.21.0-bd9c6b9b659b
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/README.md +6 -0
- 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/charts/index.d.ts +1 -1
- package/dist/charts/index.d.ts.map +1 -1
- package/dist/forms/RadioButtonFormGroup.d.ts.map +1 -1
- package/dist/forms/RadioButtonFormGroup.js +2 -2
- package/dist/report/FeatureStatusBadge.d.ts +7 -0
- package/dist/report/FeatureStatusBadge.d.ts.map +1 -0
- package/dist/report/FeatureStatusBadge.js +13 -0
- package/dist/report/HostPowerStates.d.ts +9 -0
- package/dist/report/HostPowerStates.d.ts.map +1 -0
- package/dist/report/HostPowerStates.js +11 -0
- package/dist/report/InfrastructureSummary.d.ts +7 -0
- package/dist/report/InfrastructureSummary.d.ts.map +1 -0
- package/dist/report/InfrastructureSummary.js +5 -0
- package/dist/report/OSDistribution.d.ts.map +1 -1
- package/dist/report/OSDistribution.js +5 -13
- package/dist/report/OsSupportTiersHelpPopover.d.ts.map +1 -1
- package/dist/report/OsSupportTiersHelpPopover.js +2 -31
- package/dist/report/OsUpgradeNotice.d.ts.map +1 -1
- package/dist/report/OsUpgradeNotice.js +1 -5
- package/dist/report/PowerStateCard.d.ts +17 -0
- package/dist/report/PowerStateCard.d.ts.map +1 -0
- package/dist/report/PowerStateCard.js +7 -0
- package/dist/report/SupportTierBadge.d.ts.map +1 -1
- package/dist/report/SupportTierBadge.js +4 -2
- package/dist/report/VCenterClusterDetails.d.ts +10 -0
- package/dist/report/VCenterClusterDetails.d.ts.map +1 -0
- package/dist/report/VCenterClusterDetails.js +26 -0
- package/dist/report/VmPowerStates.d.ts +9 -0
- package/dist/report/VmPowerStates.d.ts.map +1 -0
- package/dist/report/VmPowerStates.js +11 -0
- package/dist/report/constants.d.ts +2 -0
- package/dist/report/constants.d.ts.map +1 -1
- package/dist/report/constants.js +3 -2
- package/dist/report/index.d.ts +10 -0
- package/dist/report/index.d.ts.map +1 -1
- package/dist/report/index.js +8 -0
- package/dist/report/infrastructureSummaryModel.d.ts +82 -0
- package/dist/report/infrastructureSummaryModel.d.ts.map +1 -0
- package/dist/report/infrastructureSummaryModel.js +139 -0
- package/dist/report/osSupportTier.d.ts.map +1 -1
- package/dist/report/osSupportTier.js +1 -8
- package/dist/report/powerStates.d.ts +11 -0
- package/dist/report/powerStates.d.ts.map +1 -0
- package/dist/report/powerStates.js +113 -0
- package/package.json +3 -2
- package/src/charts/MigrationDonutChart.tsx +241 -111
- package/src/charts/index.ts +1 -0
- package/src/forms/RadioButtonFormGroup.tsx +10 -3
- package/src/report/FeatureStatusBadge.tsx +30 -0
- package/src/report/HostPowerStates.tsx +41 -0
- package/src/report/InfrastructureSummary.tsx +56 -0
- package/src/report/OSDistribution.tsx +60 -67
- package/src/report/OsSupportTiersHelpPopover.tsx +32 -61
- package/src/report/OsUpgradeNotice.tsx +1 -7
- package/src/report/PowerStateCard.tsx +73 -0
- package/src/report/SupportTierBadge.tsx +4 -2
- package/src/report/VCenterClusterDetails.tsx +211 -0
- package/src/report/VmPowerStates.tsx +41 -0
- package/src/report/__tests__/HostPowerStates.test.tsx +47 -0
- package/src/report/__tests__/InfrastructureSummary.test.tsx +148 -0
- package/src/report/__tests__/infrastructureSummaryModel.test.ts +224 -0
- package/src/report/__tests__/powerStates.test.ts +70 -0
- package/src/report/constants.ts +6 -2
- package/src/report/index.ts +56 -0
- package/src/report/infrastructureSummaryModel.ts +281 -0
- package/src/report/osSupportTier.ts +10 -8
- package/src/report/powerStates.ts +169 -0
|
@@ -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/charts/index.ts
CHANGED
|
@@ -33,12 +33,19 @@ export default function RadioButtonFormGroup({
|
|
|
33
33
|
const showError = error && (isTouched || isSubmitted);
|
|
34
34
|
|
|
35
35
|
return (
|
|
36
|
-
<FormGroup
|
|
36
|
+
<FormGroup
|
|
37
|
+
label={label}
|
|
38
|
+
isRequired={isRequired}
|
|
39
|
+
fieldId={id}
|
|
40
|
+
role="radiogroup"
|
|
41
|
+
isInline
|
|
42
|
+
{...props}
|
|
43
|
+
>
|
|
37
44
|
<Controller
|
|
38
45
|
name={name}
|
|
39
46
|
control={methods.control}
|
|
40
47
|
render={({ field }) => (
|
|
41
|
-
|
|
48
|
+
<>
|
|
42
49
|
{options.map((option) => (
|
|
43
50
|
<Radio
|
|
44
51
|
key={option.value}
|
|
@@ -50,7 +57,7 @@ export default function RadioButtonFormGroup({
|
|
|
50
57
|
onBlur={field.onBlur}
|
|
51
58
|
/>
|
|
52
59
|
))}
|
|
53
|
-
|
|
60
|
+
</>
|
|
54
61
|
)}
|
|
55
62
|
/>
|
|
56
63
|
<FormFieldHelperText
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Label } from "@patternfly/react-core";
|
|
2
|
+
import { CheckCircleIcon, TimesCircleIcon } from "@patternfly/react-icons";
|
|
3
|
+
import type { FC } from "react";
|
|
4
|
+
import type { FeatureStatus } from "./infrastructureSummaryModel.js";
|
|
5
|
+
|
|
6
|
+
export interface FeatureStatusBadgeProps {
|
|
7
|
+
status: FeatureStatus;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const FeatureStatusBadge: FC<FeatureStatusBadgeProps> = ({ status }) => {
|
|
11
|
+
if (status === "unknown") {
|
|
12
|
+
return <span>—</span>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (status === "enabled") {
|
|
16
|
+
return (
|
|
17
|
+
<Label color="green" isCompact icon={<CheckCircleIcon />}>
|
|
18
|
+
Enabled
|
|
19
|
+
</Label>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<Label color="grey" isCompact icon={<TimesCircleIcon />}>
|
|
25
|
+
Disabled
|
|
26
|
+
</Label>
|
|
27
|
+
);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
FeatureStatusBadge.displayName = "FeatureStatusBadge";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { ServerIcon } from "@patternfly/react-icons";
|
|
2
|
+
import type { FC } from "react";
|
|
3
|
+
import { useMemo } from "react";
|
|
4
|
+
import type { MigrationDonutChartLegendVariant } from "../charts/MigrationDonutChart.js";
|
|
5
|
+
import { REPORT_CARD_EMPTY_STATE_TITLES } from "./constants.js";
|
|
6
|
+
import { PowerStateCard } from "./PowerStateCard.js";
|
|
7
|
+
import { buildHostPowerStateChart } from "./powerStates.js";
|
|
8
|
+
|
|
9
|
+
export interface HostPowerStatesProps {
|
|
10
|
+
hostPowerStates?: Record<string, number>;
|
|
11
|
+
isExportMode?: boolean;
|
|
12
|
+
legendVariant?: MigrationDonutChartLegendVariant;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const HostPowerStates: FC<HostPowerStatesProps> = ({
|
|
16
|
+
hostPowerStates,
|
|
17
|
+
isExportMode = false,
|
|
18
|
+
legendVariant,
|
|
19
|
+
}) => {
|
|
20
|
+
const chart = useMemo(
|
|
21
|
+
() => buildHostPowerStateChart(hostPowerStates),
|
|
22
|
+
[hostPowerStates],
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<PowerStateCard
|
|
27
|
+
id="esxi-host-power-states"
|
|
28
|
+
title="ESXi host power states"
|
|
29
|
+
icon={<ServerIcon />}
|
|
30
|
+
emptyTitle={REPORT_CARD_EMPTY_STATE_TITLES.hostPowerStates}
|
|
31
|
+
slices={chart.slices}
|
|
32
|
+
legend={chart.legend}
|
|
33
|
+
total={chart.total}
|
|
34
|
+
subTitle="Hosts"
|
|
35
|
+
isExportMode={isExportMode}
|
|
36
|
+
legendVariant={legendVariant}
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
HostPowerStates.displayName = "HostPowerStates";
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Card,
|
|
3
|
+
CardBody,
|
|
4
|
+
CardTitle,
|
|
5
|
+
DescriptionList,
|
|
6
|
+
DescriptionListDescription,
|
|
7
|
+
DescriptionListGroup,
|
|
8
|
+
DescriptionListTerm,
|
|
9
|
+
} from "@patternfly/react-core";
|
|
10
|
+
import type { FC } from "react";
|
|
11
|
+
import type { InfrastructureSummaryModel } from "./infrastructureSummaryModel.js";
|
|
12
|
+
|
|
13
|
+
export interface InfrastructureSummaryProps {
|
|
14
|
+
summary: InfrastructureSummaryModel;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const formatCount = (value: number | undefined): string =>
|
|
18
|
+
typeof value === "number" ? String(value) : "—";
|
|
19
|
+
|
|
20
|
+
export const InfrastructureSummary: FC<InfrastructureSummaryProps> = ({
|
|
21
|
+
summary,
|
|
22
|
+
}) => (
|
|
23
|
+
<Card isFullHeight id="infrastructure-summary">
|
|
24
|
+
<CardTitle>Infrastructure summary</CardTitle>
|
|
25
|
+
<CardBody>
|
|
26
|
+
<DescriptionList isAutoFit displaySize="lg">
|
|
27
|
+
<DescriptionListGroup>
|
|
28
|
+
<DescriptionListTerm>VMware version</DescriptionListTerm>
|
|
29
|
+
<DescriptionListDescription>
|
|
30
|
+
{summary.vmwareVersion}
|
|
31
|
+
</DescriptionListDescription>
|
|
32
|
+
</DescriptionListGroup>
|
|
33
|
+
<DescriptionListGroup>
|
|
34
|
+
<DescriptionListTerm>Datacenters</DescriptionListTerm>
|
|
35
|
+
<DescriptionListDescription>
|
|
36
|
+
{formatCount(summary.datacenters)}
|
|
37
|
+
</DescriptionListDescription>
|
|
38
|
+
</DescriptionListGroup>
|
|
39
|
+
<DescriptionListGroup>
|
|
40
|
+
<DescriptionListTerm>vCenters</DescriptionListTerm>
|
|
41
|
+
<DescriptionListDescription>
|
|
42
|
+
{formatCount(summary.vCenters)}
|
|
43
|
+
</DescriptionListDescription>
|
|
44
|
+
</DescriptionListGroup>
|
|
45
|
+
<DescriptionListGroup>
|
|
46
|
+
<DescriptionListTerm>ESXi hosts</DescriptionListTerm>
|
|
47
|
+
<DescriptionListDescription>
|
|
48
|
+
{formatCount(summary.esxiHosts)}
|
|
49
|
+
</DescriptionListDescription>
|
|
50
|
+
</DescriptionListGroup>
|
|
51
|
+
</DescriptionList>
|
|
52
|
+
</CardBody>
|
|
53
|
+
</Card>
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
InfrastructureSummary.displayName = "InfrastructureSummary";
|