Skip to content

chore: Adds iframe helper and iframe vr test #61

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pages/06-visual-tests/cartesian-tooltip.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ export default function () {
<Page title="Cartesian chart tooltip visual regression page">
<CoreChart
{...omit(chartProps.cartesian, "ref")}
ariaLabel="Line chart"
options={{
lang: { accessibility: { chartContainerLabel: "Line chart" } },
series: series,
xAxis: [{ type: "datetime", title: { text: "Time (UTC)" }, valueFormatter: dateFormatter }],
yAxis: [{ title: { text: "Events" } }],
Expand Down
48 changes: 48 additions & 0 deletions pages/06-visual-tests/in-iframe.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import { omit } from "lodash";

import CoreChart from "../../lib/components/internal-do-not-use/core-chart";
import { useChartSettings } from "../common/page-settings";
import { Page } from "../common/templates";
import { IframeWrapper } from "../utils/iframe-wrapper";

export default function () {
return (
<IframeWrapper
AppComponent={() => {
const { chartProps } = useChartSettings();
return (
<Page title="Chart inside iframe visual regression page">
<CoreChart
{...omit(chartProps.pie, "ref")}
ariaLabel="Pie chart"
options={{
series: [
{
name: "Resource count",
type: "pie",
data: [
{ name: "Running", y: 60 },
{ name: "Failed", y: 30 },
{ name: "In-progress", y: 10 },
],
},
],
}}
callback={(api) => {
setTimeout(() => {
if (api.chart.series) {
const point = api.chart.series[0].data.find((p) => p.y === 10)!;
api.highlightChartPoint(point);
}
}, 0);
}}
/>
</Page>
);
}}
/>
);
}
4 changes: 2 additions & 2 deletions pages/06-visual-tests/pie-tooltip.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ export default function () {
return (
<Page title="Pie chart tooltip visual regression page">
<CoreChart
{...omit(chartProps.cartesian, "ref")}
{...omit(chartProps.pie, "ref")}
ariaLabel="Pie chart"
options={{
lang: { accessibility: { chartContainerLabel: "Pie chart" } },
series: series,
}}
chartHeight={400}
Expand Down
14 changes: 14 additions & 0 deletions pages/utils/iframe-wrapper.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

.full-screen {
inline-size: 100%;
block-size: calc(
100vh - var(--awsui-sticky-vertical-top-offset, 0px) - var(--awsui-sticky-vertical-bottom-offset, 0px)
);
border-inline: 0;
border-block: 0;
display: block;
}
69 changes: 69 additions & 0 deletions pages/utils/iframe-wrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import { useEffect, useRef } from "react";
import { createRoot } from "react-dom/client";

import styles from "./iframe-wrapper.module.scss";

export function IframeWrapper({ id = "iframe", AppComponent }: { id?: string; AppComponent: React.ComponentType }) {
const iframeRef = useRef<HTMLIFrameElement>(null);

useEffect(() => {
const iframe = iframeRef.current;
if (!iframe) {
return;
}

const iframeDocument = iframe.contentDocument!;
// Prevent iframe document instance from reload
// https://bugzilla.mozilla.org/show_bug.cgi?id=543435
iframeDocument.open();
// set html5 doctype
iframeDocument.writeln("<!DOCTYPE html>");
iframeDocument.close();

const innerAppRoot = iframeDocument.createElement("div");
iframeDocument.body.appendChild(innerAppRoot);
copyStyles(document, iframeDocument);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we expect our consumers to copy their styles in a similar way?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, but the iframe should have all necessary styles bundled.

iframeDocument.dir = document.dir;
const syncClassesCleanup = syncClasses(document.body, iframeDocument.body);
const root = createRoot(innerAppRoot);
root.render(<AppComponent />);
return () => {
syncClassesCleanup();
root.unmount();
};
}, [id, AppComponent]);

return <iframe ref={iframeRef} id={id} title={id} className={styles["full-screen"]}></iframe>;
}

function copyStyles(srcDoc: Document, targetDoc: Document) {
for (const stylesheet of Array.from(srcDoc.querySelectorAll("link[rel=stylesheet]"))) {
const newStylesheet = targetDoc.createElement("link");
for (const attr of stylesheet.getAttributeNames()) {
newStylesheet.setAttribute(attr, stylesheet.getAttribute(attr)!);
}
targetDoc.head.appendChild(newStylesheet);
}

for (const styleEl of Array.from(srcDoc.querySelectorAll("style"))) {
const newStyle = targetDoc.createElement("style");
newStyle.textContent = styleEl.textContent;
targetDoc.head.appendChild(newStyle);
}
}

function syncClasses(from: HTMLElement, to: HTMLElement) {
to.className = from.className;
const observer = new MutationObserver(() => {
to.className = from.className;
});

observer.observe(from, { attributes: true, attributeFilter: ["class"] });

return () => {
observer.disconnect();
};
}
Loading