@openshift-migration-advisor/shared-components 0.27.0-ad43dbdf05ab → 0.28.0-09381b4d22e5
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.map +1 -1
- package/dist/charts/MigrationDonutChart.js +7 -0
- package/dist/report/ChartExportProvider.d.ts +1 -1
- package/dist/report/ChartExportProvider.d.ts.map +1 -1
- package/dist/report/ChartExportProvider.js +4 -1
- package/dist/report/FeatureStatusBadge.js +1 -1
- package/dist/report/ReportExportMenu.d.ts +1 -0
- package/dist/report/ReportExportMenu.d.ts.map +1 -1
- package/dist/report/ReportExportMenu.js +38 -4
- package/dist/report/StorageOverview.d.ts +27 -0
- package/dist/report/StorageOverview.d.ts.map +1 -0
- package/dist/report/StorageOverview.js +319 -0
- package/dist/report/VMMigrationStatus.d.ts +33 -0
- package/dist/report/VMMigrationStatus.d.ts.map +1 -0
- package/dist/report/VMMigrationStatus.js +129 -0
- package/dist/report/captureChartElement.d.ts +6 -1
- package/dist/report/captureChartElement.d.ts.map +1 -1
- package/dist/report/captureChartElement.js +13 -4
- package/dist/report/chartExport.js +1 -1
- package/dist/report/dashboardStyles.d.ts +6 -0
- package/dist/report/dashboardStyles.d.ts.map +1 -1
- package/dist/report/dashboardStyles.js +7 -4
- package/dist/report/index.d.ts +3 -1
- package/dist/report/index.d.ts.map +1 -1
- package/dist/report/index.js +2 -0
- package/dist/report/reportExportOptions.d.ts +13 -2
- package/dist/report/reportExportOptions.d.ts.map +1 -1
- package/dist/report/reportExportOptions.js +13 -5
- package/package.json +2 -2
- package/src/charts/MigrationDonutChart.tsx +9 -0
- package/src/report/ChartExportProvider.tsx +5 -1
- package/src/report/FeatureStatusBadge.tsx +1 -1
- package/src/report/ReportExportMenu.tsx +69 -13
- package/src/report/StorageOverview.tsx +629 -0
- package/src/report/VMMigrationStatus.tsx +358 -0
- package/src/report/__tests__/StorageOverview.test.tsx +223 -0
- package/src/report/__tests__/VMMigrationStatus.test.tsx +178 -0
- package/src/report/__tests__/captureChartElement.test.ts +47 -0
- package/src/report/captureChartElement.ts +18 -3
- package/src/report/chartExport.ts +1 -1
- package/src/report/dashboardStyles.ts +7 -4
- package/src/report/index.ts +12 -0
- package/src/report/reportExportOptions.ts +28 -5
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
import { cleanup, render, screen } from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import type { MigrationDonutChartProps } from "../../charts/MigrationDonutChart.js";
|
|
6
|
+
import { chartColorFailure, chartColorSuccess } from "../constants.js";
|
|
7
|
+
import { VMMigrationStatus } from "../VMMigrationStatus.js";
|
|
8
|
+
|
|
9
|
+
const donutProps = vi.hoisted(() => ({
|
|
10
|
+
current: null as MigrationDonutChartProps | null,
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
vi.mock("../../charts/MigrationDonutChart.js", () => ({
|
|
14
|
+
MigrationDonutChart: (props: MigrationDonutChartProps): JSX.Element => {
|
|
15
|
+
donutProps.current = props;
|
|
16
|
+
return (
|
|
17
|
+
<div data-testid="migration-donut">
|
|
18
|
+
{props.title} {props.subTitle}
|
|
19
|
+
</div>
|
|
20
|
+
);
|
|
21
|
+
},
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
donutProps.current = null;
|
|
26
|
+
cleanup();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const renderedDonut = (): MigrationDonutChartProps => {
|
|
30
|
+
const props = donutProps.current;
|
|
31
|
+
if (!props) {
|
|
32
|
+
throw new Error("expected a migration-status donut to render");
|
|
33
|
+
}
|
|
34
|
+
return props;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const selectView = async (name: string): Promise<void> => {
|
|
38
|
+
const user = userEvent.setup();
|
|
39
|
+
await user.click(
|
|
40
|
+
screen.getByRole("button", { name: "No issues vs with issues" }),
|
|
41
|
+
);
|
|
42
|
+
await user.click(screen.getByRole("menuitem", { name }));
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
describe("VMMigrationStatus", () => {
|
|
46
|
+
it("shows the empty state when there are no VMs", () => {
|
|
47
|
+
render(<VMMigrationStatus data={{ migratable: 0, nonMigratable: 0 }} />);
|
|
48
|
+
|
|
49
|
+
expect(screen.getByText("VM Migration Status")).toBeInTheDocument();
|
|
50
|
+
expect(
|
|
51
|
+
screen.getByText("Migration status data not collected"),
|
|
52
|
+
).toBeInTheDocument();
|
|
53
|
+
expect(screen.queryByTestId("migration-donut")).not.toBeInTheDocument();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("renders the migratable vs not-ready donut", () => {
|
|
57
|
+
render(<VMMigrationStatus data={{ migratable: 7, nonMigratable: 3 }} />);
|
|
58
|
+
|
|
59
|
+
expect(screen.getByTestId("migration-donut")).toHaveTextContent("10 VMs");
|
|
60
|
+
|
|
61
|
+
const props = renderedDonut();
|
|
62
|
+
expect(props.data.map((slice) => [slice.name, slice.count])).toEqual([
|
|
63
|
+
["Migratable", 7],
|
|
64
|
+
["Not ready for migration", 3],
|
|
65
|
+
]);
|
|
66
|
+
expect(props.legend).toEqual({
|
|
67
|
+
Migratable: chartColorSuccess,
|
|
68
|
+
"Not ready for migration": chartColorFailure,
|
|
69
|
+
});
|
|
70
|
+
expect(props.legendVariant).toBe("html");
|
|
71
|
+
expect(props.subTitleColor).toBe(
|
|
72
|
+
"var(--pf-t--global--text--color--subtle)",
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("drills down into readiness when a donut slice is clicked", () => {
|
|
77
|
+
const onReadinessSelect = vi.fn();
|
|
78
|
+
render(
|
|
79
|
+
<VMMigrationStatus
|
|
80
|
+
data={{ migratable: 7, nonMigratable: 3 }}
|
|
81
|
+
onReadinessSelect={onReadinessSelect}
|
|
82
|
+
/>,
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const props = renderedDonut();
|
|
86
|
+
const [migratable, notReady] = props.data;
|
|
87
|
+
if (!migratable || !notReady) {
|
|
88
|
+
throw new Error("expected both readiness slices");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
props.onItemClick?.(migratable);
|
|
92
|
+
expect(onReadinessSelect).toHaveBeenLastCalledWith(true);
|
|
93
|
+
|
|
94
|
+
props.onItemClick?.(notReady);
|
|
95
|
+
expect(onReadinessSelect).toHaveBeenLastCalledWith(false);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("forwards the center-title click", () => {
|
|
99
|
+
const onTitleClick = vi.fn();
|
|
100
|
+
render(
|
|
101
|
+
<VMMigrationStatus
|
|
102
|
+
data={{ migratable: 1, nonMigratable: 0 }}
|
|
103
|
+
onTitleClick={onTitleClick}
|
|
104
|
+
/>,
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
renderedDonut().onTitleClick?.();
|
|
108
|
+
expect(onTitleClick).toHaveBeenCalledTimes(1);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("renders the issue breakdown bars in category order", async () => {
|
|
112
|
+
render(
|
|
113
|
+
<VMMigrationStatus
|
|
114
|
+
data={{ migratable: 1, nonMigratable: 9 }}
|
|
115
|
+
issuesBreakdown={{
|
|
116
|
+
critical: 3,
|
|
117
|
+
error: 2,
|
|
118
|
+
warning: 5,
|
|
119
|
+
information: 1,
|
|
120
|
+
advisory: 0,
|
|
121
|
+
}}
|
|
122
|
+
/>,
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
await selectView("With issues breakdown");
|
|
126
|
+
|
|
127
|
+
expect(screen.queryByTestId("migration-donut")).not.toBeInTheDocument();
|
|
128
|
+
expect(screen.getByTitle("Critical: 3 VMs")).toBeInTheDocument();
|
|
129
|
+
expect(screen.getByTitle("Warning: 5 VMs")).toBeInTheDocument();
|
|
130
|
+
expect(screen.getByTitle("Advisory: 0 VMs")).toBeInTheDocument();
|
|
131
|
+
expect(
|
|
132
|
+
screen.getByText(/Totals may exceed the unique VM count/),
|
|
133
|
+
).toBeInTheDocument();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("drills down into a concern category when a bar is clicked", async () => {
|
|
137
|
+
const onCategorySelect = vi.fn();
|
|
138
|
+
render(
|
|
139
|
+
<VMMigrationStatus
|
|
140
|
+
data={{ migratable: 1, nonMigratable: 9 }}
|
|
141
|
+
issuesBreakdown={{
|
|
142
|
+
critical: 3,
|
|
143
|
+
error: 2,
|
|
144
|
+
warning: 5,
|
|
145
|
+
information: 1,
|
|
146
|
+
advisory: 0,
|
|
147
|
+
}}
|
|
148
|
+
onCategorySelect={onCategorySelect}
|
|
149
|
+
/>,
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
await selectView("With issues breakdown");
|
|
153
|
+
await userEvent.setup().click(screen.getByTitle("Warning: 5 VMs"));
|
|
154
|
+
|
|
155
|
+
expect(onCategorySelect).toHaveBeenCalledWith("Warning");
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("shows the breakdown empty state when no issue data was collected", async () => {
|
|
159
|
+
render(<VMMigrationStatus data={{ migratable: 1, nonMigratable: 9 }} />);
|
|
160
|
+
|
|
161
|
+
await selectView("With issues breakdown");
|
|
162
|
+
|
|
163
|
+
expect(
|
|
164
|
+
screen.getByText("Issues breakdown data not collected"),
|
|
165
|
+
).toBeInTheDocument();
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("passes the chart legend variant through", () => {
|
|
169
|
+
render(
|
|
170
|
+
<VMMigrationStatus
|
|
171
|
+
legendVariant="chart"
|
|
172
|
+
data={{ migratable: 1, nonMigratable: 1 }}
|
|
173
|
+
/>,
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
expect(renderedDonut().legendVariant).toBe("chart");
|
|
177
|
+
});
|
|
178
|
+
});
|
|
@@ -202,4 +202,51 @@ describe("captureChartElement", () => {
|
|
|
202
202
|
).toBe(true);
|
|
203
203
|
expect(card.style.backgroundColor).toBe("rgb(30, 30, 30)");
|
|
204
204
|
});
|
|
205
|
+
|
|
206
|
+
it("relights tables and labels via the clone cascade, not by hand-painting them", async () => {
|
|
207
|
+
document.documentElement.classList.add("pf-v6-theme-dark");
|
|
208
|
+
const root = document.createElement("div");
|
|
209
|
+
const label = document.createElement("span");
|
|
210
|
+
label.className = "pf-v6-c-label pf-m-compact pf-m-outline";
|
|
211
|
+
const labelContent = document.createElement("span");
|
|
212
|
+
labelContent.className = "pf-v6-c-label__content";
|
|
213
|
+
label.append(labelContent);
|
|
214
|
+
root.append(label);
|
|
215
|
+
document.body.append(root);
|
|
216
|
+
|
|
217
|
+
mockedHtml2Canvas.mockImplementation(async (_element, options) => {
|
|
218
|
+
const clonedDoc = document.implementation.createHTMLDocument("export");
|
|
219
|
+
clonedDoc.documentElement.classList.add("pf-v6-theme-dark");
|
|
220
|
+
const cloneRoot = clonedDoc.createElement("div");
|
|
221
|
+
const cloneTh = clonedDoc.createElement("th");
|
|
222
|
+
cloneTh.className = "pf-v6-c-table__th";
|
|
223
|
+
const cloneLabelContent = clonedDoc.createElement("span");
|
|
224
|
+
cloneLabelContent.className = "pf-v6-c-label__content";
|
|
225
|
+
cloneRoot.append(cloneTh, cloneLabelContent);
|
|
226
|
+
clonedDoc.body.append(cloneRoot);
|
|
227
|
+
|
|
228
|
+
await options?.onclone?.(clonedDoc, cloneRoot);
|
|
229
|
+
|
|
230
|
+
// Dropping the dark class off the clone root is what relights these
|
|
231
|
+
// surfaces — the cascade does the work. We must NOT hand-paint inline
|
|
232
|
+
// colors onto PatternFly table/label nodes, and the live page stays dark.
|
|
233
|
+
expect(
|
|
234
|
+
clonedDoc.documentElement.classList.contains("pf-v6-theme-dark"),
|
|
235
|
+
).toBe(false);
|
|
236
|
+
expect(cloneTh.style.backgroundColor).toBe("");
|
|
237
|
+
expect(cloneLabelContent.style.backgroundColor).toBe("");
|
|
238
|
+
|
|
239
|
+
const canvas = document.createElement("canvas");
|
|
240
|
+
canvas.width = 1;
|
|
241
|
+
canvas.height = 1;
|
|
242
|
+
return canvas;
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
await captureChartElement(root);
|
|
246
|
+
|
|
247
|
+
expect(
|
|
248
|
+
document.documentElement.classList.contains("pf-v6-theme-dark"),
|
|
249
|
+
).toBe(true);
|
|
250
|
+
expect(labelContent.style.backgroundColor).toBe("");
|
|
251
|
+
});
|
|
205
252
|
});
|
|
@@ -11,8 +11,15 @@ import {
|
|
|
11
11
|
shouldIgnoreChartExportElement,
|
|
12
12
|
} from "./chartExport.js";
|
|
13
13
|
|
|
14
|
-
/**
|
|
15
|
-
const
|
|
14
|
+
/** Default scale for bulk exports; single-chart PNG uses a higher scale. */
|
|
15
|
+
const CHART_CAPTURE_SCALE_DEFAULT = 1;
|
|
16
|
+
export const CHART_CAPTURE_SCALE_SINGLE = 2;
|
|
17
|
+
const EXPORT_FONT_FAMILY = "RedHatText, Overpass, Helvetica, Arial, sans-serif";
|
|
18
|
+
|
|
19
|
+
export type CaptureChartElementOptions = {
|
|
20
|
+
/** Higher scale sharpens text in PNG exports; keep at 1 for bulk PDF/zip. */
|
|
21
|
+
scale?: number;
|
|
22
|
+
};
|
|
16
23
|
const HIDDEN_LAYOUT_STYLE_KEYS = [
|
|
17
24
|
"display",
|
|
18
25
|
"position",
|
|
@@ -187,6 +194,7 @@ function applyLightThemeSvgText(root: HTMLElement): void {
|
|
|
187
194
|
}
|
|
188
195
|
node.style.fill = LIGHT_EXPORT_TEXT;
|
|
189
196
|
node.style.color = LIGHT_EXPORT_TEXT;
|
|
197
|
+
node.style.fontFamily = EXPORT_FONT_FAMILY;
|
|
190
198
|
}
|
|
191
199
|
}
|
|
192
200
|
|
|
@@ -359,7 +367,9 @@ function prepareCaptureLayout(element: HTMLElement): () => void {
|
|
|
359
367
|
|
|
360
368
|
export async function captureChartElement(
|
|
361
369
|
element: HTMLElement,
|
|
370
|
+
options: CaptureChartElementOptions = {},
|
|
362
371
|
): Promise<HTMLCanvasElement> {
|
|
372
|
+
const scale = options.scale ?? CHART_CAPTURE_SCALE_DEFAULT;
|
|
363
373
|
const restoreHidden = revealHiddenChartAncestors(element);
|
|
364
374
|
const restoreLayout = prepareCaptureLayout(element);
|
|
365
375
|
element.setAttribute(CHART_EXPORT_CAPTURING_ATTR, "");
|
|
@@ -378,15 +388,20 @@ export async function captureChartElement(
|
|
|
378
388
|
useCORS: true,
|
|
379
389
|
backgroundColor: LIGHT_EXPORT_BACKGROUND,
|
|
380
390
|
logging: false,
|
|
381
|
-
scale
|
|
391
|
+
scale,
|
|
382
392
|
imageTimeout: 0,
|
|
383
393
|
ignoreElements: (node) =>
|
|
384
394
|
node instanceof Element &&
|
|
385
395
|
shouldIgnoreChartExportElement(node, element),
|
|
386
396
|
onclone: (clonedDoc, clonedElement) => {
|
|
397
|
+
// Dropping the dark-theme class off the clone root makes every
|
|
398
|
+
// PatternFly CSS-variable surface (cards, tables, labels) resolve to
|
|
399
|
+
// light automatically — html2canvas reads computed styles from this
|
|
400
|
+
// clone. Only Victory's inlined SVG text needs explicit recoloring.
|
|
387
401
|
applyLightThemeToDocument(clonedDoc);
|
|
388
402
|
applyComputedStylesToClone(element, clonedElement);
|
|
389
403
|
applyLightThemeSvgText(clonedElement);
|
|
404
|
+
clonedElement.style.fontFamily = EXPORT_FONT_FAMILY;
|
|
390
405
|
},
|
|
391
406
|
});
|
|
392
407
|
} finally {
|
|
@@ -30,6 +30,12 @@ export const dashboardStyles = {
|
|
|
30
30
|
height: 100%;
|
|
31
31
|
`,
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Flex child of {@link dashboardStyles.card}. `min-height: 0` lets the body
|
|
35
|
+
* shrink inside the card max-height; `overflow: auto` scrolls tables and tall
|
|
36
|
+
* charts when needed (phantom scrollbars on donut cards are avoided by the
|
|
37
|
+
* card layout and ChartExportSurface sizing, not a second body class).
|
|
38
|
+
*/
|
|
33
39
|
cardBodyScrollable: css`
|
|
34
40
|
overflow: auto;
|
|
35
41
|
`,
|
|
@@ -38,12 +44,9 @@ export const dashboardStyles = {
|
|
|
38
44
|
// at the same specificity, which wins in the production build due to stylesheet
|
|
39
45
|
// injection order. `!important` forces `hidden` regardless of that order.
|
|
40
46
|
card: css`
|
|
41
|
-
|
|
42
|
-
max-height: 520px;
|
|
43
|
-
height: 100%;
|
|
47
|
+
height: 520px;
|
|
44
48
|
display: flex;
|
|
45
49
|
flex-direction: column;
|
|
46
|
-
justify-content: space-between;
|
|
47
50
|
overflow: hidden !important;
|
|
48
51
|
`,
|
|
49
52
|
|
package/src/report/index.ts
CHANGED
|
@@ -192,9 +192,16 @@ export {
|
|
|
192
192
|
export { ReportTable, type ReportTableProps } from "./ReportTable.js";
|
|
193
193
|
export {
|
|
194
194
|
type ReportExportOption,
|
|
195
|
+
type ReportExportSection,
|
|
195
196
|
type StandardReportExportHandlers,
|
|
197
|
+
type StandardReportExportOptionsConfig,
|
|
196
198
|
standardReportExportOptions,
|
|
197
199
|
} from "./reportExportOptions.js";
|
|
200
|
+
export {
|
|
201
|
+
type StorageDiskTierData,
|
|
202
|
+
StorageOverview,
|
|
203
|
+
type StorageOverviewProps,
|
|
204
|
+
} from "./StorageOverview.js";
|
|
198
205
|
export { SupportTierBadge } from "./SupportTierBadge.js";
|
|
199
206
|
export {
|
|
200
207
|
ALL_TIERS_FILTER,
|
|
@@ -206,6 +213,11 @@ export {
|
|
|
206
213
|
VCenterClusterDetails,
|
|
207
214
|
type VCenterClusterDetailsProps,
|
|
208
215
|
} from "./VCenterClusterDetails.js";
|
|
216
|
+
export {
|
|
217
|
+
type IssuesBreakdownLike,
|
|
218
|
+
VMMigrationStatus,
|
|
219
|
+
type VMMigrationStatusProps,
|
|
220
|
+
} from "./VMMigrationStatus.js";
|
|
209
221
|
export {
|
|
210
222
|
VmPowerStates,
|
|
211
223
|
type VmPowerStatesProps,
|
|
@@ -1,9 +1,16 @@
|
|
|
1
|
+
export interface ReportExportSection {
|
|
2
|
+
id: string;
|
|
3
|
+
label: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
1
6
|
export interface ReportExportOption {
|
|
2
7
|
key: string;
|
|
3
8
|
label: string;
|
|
4
9
|
description?: string;
|
|
5
10
|
onSelect: () => void;
|
|
6
11
|
isDisabled?: boolean;
|
|
12
|
+
/** When set, the menu groups rows and shows `label` as the group heading. */
|
|
13
|
+
section?: ReportExportSection;
|
|
7
14
|
}
|
|
8
15
|
|
|
9
16
|
export interface StandardReportExportHandlers {
|
|
@@ -12,22 +19,38 @@ export interface StandardReportExportHandlers {
|
|
|
12
19
|
onExportHtml?: () => void;
|
|
13
20
|
}
|
|
14
21
|
|
|
22
|
+
export type StandardReportExportOptionsConfig = StandardReportExportHandlers & {
|
|
23
|
+
/** Dropdown group label for PDF / HTML / PNG. Default: `"Assessment report"`. */
|
|
24
|
+
assessmentSectionLabel?: string;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const DEFAULT_ASSESSMENT_SECTION: ReportExportSection = {
|
|
28
|
+
id: "assessment-report",
|
|
29
|
+
label: "Assessment report",
|
|
30
|
+
};
|
|
31
|
+
|
|
15
32
|
/**
|
|
16
|
-
*
|
|
33
|
+
* Optional helper for chart-based assessment downloads. Omit a handler to hide
|
|
34
|
+
* that format. Override `assessmentSectionLabel` if product copy differs per app.
|
|
17
35
|
*/
|
|
18
36
|
export const standardReportExportOptions = ({
|
|
19
37
|
onExportPdf,
|
|
20
38
|
onExportPng,
|
|
21
39
|
onExportHtml,
|
|
22
|
-
|
|
40
|
+
assessmentSectionLabel,
|
|
41
|
+
}: StandardReportExportOptionsConfig): ReportExportOption[] => {
|
|
42
|
+
const section: ReportExportSection = assessmentSectionLabel
|
|
43
|
+
? { ...DEFAULT_ASSESSMENT_SECTION, label: assessmentSectionLabel }
|
|
44
|
+
: DEFAULT_ASSESSMENT_SECTION;
|
|
45
|
+
|
|
23
46
|
const options: ReportExportOption[] = [];
|
|
24
47
|
|
|
25
48
|
if (onExportPdf) {
|
|
26
49
|
options.push({
|
|
27
50
|
key: "pdf",
|
|
28
51
|
label: "PDF",
|
|
29
|
-
description: "Export the report as a PDF",
|
|
30
52
|
onSelect: onExportPdf,
|
|
53
|
+
section,
|
|
31
54
|
});
|
|
32
55
|
}
|
|
33
56
|
|
|
@@ -35,8 +58,8 @@ export const standardReportExportOptions = ({
|
|
|
35
58
|
options.push({
|
|
36
59
|
key: "html",
|
|
37
60
|
label: "HTML",
|
|
38
|
-
description: "Export the report as HTML",
|
|
39
61
|
onSelect: onExportHtml,
|
|
62
|
+
section,
|
|
40
63
|
});
|
|
41
64
|
}
|
|
42
65
|
|
|
@@ -44,8 +67,8 @@ export const standardReportExportOptions = ({
|
|
|
44
67
|
options.push({
|
|
45
68
|
key: "png",
|
|
46
69
|
label: "PNG",
|
|
47
|
-
description: "Download all charts as PNG files",
|
|
48
70
|
onSelect: onExportPng,
|
|
71
|
+
section,
|
|
49
72
|
});
|
|
50
73
|
}
|
|
51
74
|
|