Skip to content

Commit

Permalink
feat(): new brick: eo-iframe
Browse files Browse the repository at this point in the history
  • Loading branch information
weareoutman committed Sep 4, 2024
1 parent d8cb188 commit 5c0f3c4
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 1 deletion.
1 change: 1 addition & 0 deletions bricks/basic/src/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,4 @@ import "./event-agent/index.js";
import "./message-listener/index.js";
import "./broadcast-channel/index.js";
import "./home-redirect/index.js";
import "./iframe/index.js";
43 changes: 43 additions & 0 deletions bricks/basic/src/iframe/index.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { describe, test, expect, jest } from "@jest/globals";
import { act } from "react-dom/test-utils";
import { fireEvent } from "@testing-library/dom";
import "./";
import type { Iframe } from "./index.js";

jest.mock("@next-core/theme", () => ({}));

describe("eo-iframe", () => {
test("basic usage", async () => {
const element = document.createElement("eo-iframe") as Iframe;
element.src = "http://localhost/iframe";

const onLoad = jest.fn();
element.addEventListener("load", onLoad);

act(() => {
document.body.appendChild(element);
});
expect(element.shadowRoot?.childNodes.length).toBeGreaterThan(1);

const iframe = element.shadowRoot?.querySelector(
"iframe"
) as HTMLIFrameElement;
fireEvent.load(iframe);
expect(onLoad).toBeCalledTimes(1);

const mockPostMessage = jest.fn();
Object.defineProperty(iframe, "contentWindow", {
get() {
return {
postMessage: mockPostMessage,
} as any;
},
});
element.postMessage("hello", location.origin);
expect(mockPostMessage).toBeCalledWith("hello", location.origin);

act(() => {
document.body.removeChild(element);
});
});
});
120 changes: 120 additions & 0 deletions bricks/basic/src/iframe/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import React, {
createRef,
forwardRef,
useEffect,
useImperativeHandle,
useRef,
type CSSProperties,
type Ref,
} from "react";
import { createDecorators, type EventEmitter } from "@next-core/element";
import { ReactNextElement } from "@next-core/react-element";
import styleText from "./styles.shadow.css";

const { defineElement, property, event, method } = createDecorators();

export interface IframeProps {
src?: string;
iframeStyle?: CSSProperties;
}

type PostMessageParameters =
| [message: unknown, targetOrigin: string, transfer?: Transferable[]]
| [message: unknown, options?: WindowPostMessageOptions];

interface IframeRef {
postMessage: (...args: PostMessageParameters) => void;
}

const IframeComponent = forwardRef<IframeRef, IframeComponentProps>(
LegacyIframeComponent
);

/**
* 构件 `eo-iframe`
*/
export
@defineElement("eo-iframe", {
styleTexts: [styleText],
})
class Iframe extends ReactNextElement implements IframeProps {
/**
* @required
*/
@property() accessor src: string | undefined;

/**
* Default style:
*
* ```css
* iframe {
* margin: 0;
* border: 0;
* padding: 0;
* width: 100%;
* height: 100%;
* vertical-align: top;
* }
* ```
*/
@property({ attribute: false })
accessor iframeStyle: CSSProperties | undefined;

@event({ type: "load" })
accessor #loadEvent!: EventEmitter<void>;

#handleLoad = () => {
this.#loadEvent.emit();
};

#iframeRef = createRef<IframeRef>();

@method()
postMessage(...args: PostMessageParameters) {
this.#iframeRef.current?.postMessage(...args);
}

render() {
return (
<IframeComponent
src={this.src}
iframeStyle={this.iframeStyle}
onLoad={this.#handleLoad}
ref={this.#iframeRef}
/>
);
}
}

export interface IframeComponentProps extends IframeProps {
onLoad: () => void;
}

export function LegacyIframeComponent(
{ src, iframeStyle, onLoad }: IframeComponentProps,
ref: Ref<IframeRef>
) {
const iframeRef = useRef<HTMLIFrameElement>(null);

useImperativeHandle(
ref,
() => ({
postMessage(...args: PostMessageParameters) {
iframeRef.current?.contentWindow?.postMessage(
...(args as Parameters<Window["postMessage"]>)
);
},
}),
[]
);

useEffect(() => {
const iframe = iframeRef.current;
iframe?.addEventListener("load", onLoad);
return () => {
iframe?.removeEventListener("load", onLoad);
};
}, [onLoad]);

return <iframe src={src} style={iframeStyle} ref={iframeRef} />;
}
16 changes: 16 additions & 0 deletions bricks/basic/src/iframe/styles.shadow.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
:host {
display: block;
}

:host([hidden]) {
display: none;
}

iframe {
border: 0;
margin: 0;
padding: 0;
width: 100%;
height: 100%;
vertical-align: top;
}
3 changes: 2 additions & 1 deletion shared/common-bricks/common-bricks.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
"eo-formatter-number",
"eo-event-agent",
"eo-message-listener",
"eo-broadcast-channel"
"eo-broadcast-channel",
"eo-iframe"
],
"icons": [
"eo-antd-icon",
Expand Down

0 comments on commit 5c0f3c4

Please sign in to comment.