Skip to content

Commit

Permalink
move remote-ui example into kitchen sink
Browse files Browse the repository at this point in the history
  • Loading branch information
robin-drexler committed Jan 10, 2025
1 parent 95367d7 commit e1536a2
Show file tree
Hide file tree
Showing 27 changed files with 160 additions and 683 deletions.
2 changes: 2 additions & 0 deletions e2e/basic.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import {test, expect} from '@playwright/test';
['iframe', 'vue'],
['iframe', 'htm'],
['iframe', 'react'],
['iframe', 'react-remote-ui'],
['worker', 'vanilla'],
['worker', 'preact'],
['worker', 'svelte'],
// ['worker', 'vue'],
['worker', 'htm'],
['worker', 'react'],
['worker', 'react-remote-ui'],
].forEach(([sandbox, example]) => {
test(`basic modal interaction with ${sandbox} sandbox and ${example} example`, async ({
page,
Expand Down
27 changes: 0 additions & 27 deletions e2e/remote-ui-legacy.e2e.ts

This file was deleted.

46 changes: 27 additions & 19 deletions examples/kitchen-sink/app/host.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {ThreadIframe, ThreadWebWorker} from '@quilted/threads';
import type {SandboxAPI} from './types.ts';
import {Button, Modal, Stack, Text, ControlPanel} from './host/components.tsx';
import {createState} from './host/state.ts';
import {adaptToLegacyRemoteChannel} from '@remote-dom/core/legacy';

// We will put any remote elements we want to render in this root element.
const uiRoot = document.querySelector('main')!;
Expand Down Expand Up @@ -42,6 +43,10 @@ const components = new Map([
['ui-button', createRemoteComponentRenderer(Button)],
['ui-stack', createRemoteComponentRenderer(Stack)],
['ui-modal', createRemoteComponentRenderer(Modal)],
['Text', createRemoteComponentRenderer(Text)],
['Button', createRemoteComponentRenderer(Button)],
['Stack', createRemoteComponentRenderer(Stack)],
['Modal', createRemoteComponentRenderer(Modal)],
// The `remote-fragment` element is a special element created by Remote DOM when
// it needs an unstyled container for a list of elements. This is primarily used
// to convert elements passed as a prop to React or Preact components into a slotted
Expand All @@ -59,27 +64,30 @@ const components = new Map([

const {receiver, example, sandbox} = createState(
async ({receiver, example, sandbox}) => {
if (sandbox === 'iframe') {
await iframeSandbox.imports.render(receiver.connection, {
sandbox,
example,
async alert(content) {
console.log(
`Alert API used by example ${example} in the iframe sandbox`,
);
window.alert(content);
},
const api = {
sandbox,
example,
async alert(content: string) {
console.log(
`Alert API used by example ${example} in the iframe sandbox`,
);
window.alert(content);
},
async closeModal() {
document.querySelector('dialog')?.close();
},
};

const sandboxToUse = sandbox === 'iframe' ? iframeSandbox : workerSandbox;

if (example === 'react-remote-ui') {
const remoteUiChannel = adaptToLegacyRemoteChannel(receiver.connection);
await sandboxToUse.imports.renderRemoteUi(remoteUiChannel, {
...api,
});
} else {
await workerSandbox.imports.render(receiver.connection, {
sandbox,
example,
async alert(content) {
console.log(
`Alert API used by example ${example} in the worker sandbox`,
);
window.alert(content);
},
await sandboxToUse.imports.render(receiver.connection, {
...api,
});
}
},
Expand Down
1 change: 1 addition & 0 deletions examples/kitchen-sink/app/host/components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ export function ControlPanel({
<option value="svelte">Svelte</option>
<option value="vue">Vue</option>
<option value="htm">htm</option>
<option value="react-remote-ui">React Remote UI</option>
</Select>
</section>

Expand Down
96 changes: 96 additions & 0 deletions examples/kitchen-sink/app/remote/examples/react-remote-ui.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/** @jsxRuntime automatic */
/** @jsxImportSource react */
import {retain} from '@quilted/threads';

import {createRemoteReactComponent} from '@remote-ui/react';
import {
ButtonProperties,
ModalProperties,
RenderAPI,
StackProperties,
TextProperties,
} from '../../types';
import {useState} from 'react';
import {createRoot, createRemoteRoot} from '@remote-ui/react';
import {RemoteChannel} from '@remote-ui/core';

const Button = createRemoteReactComponent<
'Button',
ButtonProperties & {modal?: React.ReactNode}
>('Button', {fragmentProps: ['modal']});

const Text = createRemoteReactComponent<'Text', TextProperties>('Text');
const Stack = createRemoteReactComponent<'Stack', StackProperties>('Stack');
const Modal = createRemoteReactComponent<
'Modal',
ModalProperties & {primaryAction?: React.ReactNode}
>('Modal', {fragmentProps: ['primaryAction']});

export function renderUsingReactRemoteUI(
channel: RemoteChannel,
api: RenderAPI,
) {
retain(api);
retain(channel);

const remoteRoot = createRemoteRoot(channel, {
components: ['Button', 'Text', 'Stack', 'Modal'],
});

createRoot(remoteRoot).render(<App api={api} />);
remoteRoot.mount();
}

function App({api}: {api: RenderAPI}) {
return (
<Stack spacing>
<Text>
Rendering example: <Text emphasis>{api.example}</Text>
</Text>
<Text>
Rendering in sandbox: <Text emphasis>{api.sandbox}</Text>
</Text>
<Button modal={<CountModal {...api} />}>Open modal</Button>
</Stack>
);
}

function CountModal({alert, closeModal}: RenderAPI) {
const [count, setCount] = useState(0);

const primaryAction = (
<Button
onPress={() => {
closeModal();
}}
>
Close
</Button>
);

return (
<Modal
primaryAction={primaryAction}
onClose={() => {
if (count > 0) {
alert(`You clicked ${count} times!`);
}

setCount(0);
}}
>
<Stack spacing>
<Text>
Click count: <Text emphasis>{count}</Text>
</Text>
<Button
onPress={() => {
setCount((count) => count + 1);
}}
>
Click me!
</Button>
</Stack>
</Modal>
);
}
1 change: 1 addition & 0 deletions examples/kitchen-sink/app/remote/examples/react.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const Button = createRemoteComponent('ui-button', ButtonElement, {
onPress: {event: 'press'},
},
});

const Stack = createRemoteComponent('ui-stack', StackElement);
const Modal = createRemoteComponent('ui-modal', ModalElement, {
eventProps: {
Expand Down
5 changes: 4 additions & 1 deletion examples/kitchen-sink/app/remote/iframe/sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {RemoteMutationObserver} from '@remote-dom/core/elements';
import {ThreadNestedIframe} from '@quilted/threads';

import '../elements.ts';
import {render} from '../render.ts';
import {render, renderRemoteUi} from '../render.ts';
import type {SandboxAPI} from '../../types.ts';

// We use the `@quilted/threads` library to create a “thread” for our iframe,
Expand Down Expand Up @@ -32,5 +32,8 @@ new ThreadNestedIframe<never, SandboxAPI>({

await render(root, api);
},
async renderRemoteUi(channel, api) {
await renderRemoteUi(channel, api);
},
},
});
8 changes: 8 additions & 0 deletions examples/kitchen-sink/app/remote/render.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {RemoteChannel} from '@remote-ui/core';
import type {RenderAPI} from '../types.ts';

// Defines the custom elements available to render in the remote environment.
Expand Down Expand Up @@ -31,3 +32,10 @@ export async function render(root: Element, api: RenderAPI) {
}
}
}

export async function renderRemoteUi(channel: RemoteChannel, api: RenderAPI) {
const {renderUsingReactRemoteUI} = await import(
'./examples/react-remote-ui.tsx'
);
return renderUsingReactRemoteUI(channel, api);
}
5 changes: 4 additions & 1 deletion examples/kitchen-sink/app/remote/worker/sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import '@remote-dom/react/polyfill';
import {ThreadWebWorker} from '@quilted/threads';

import '../elements.ts';
import {render} from '../render.ts';
import {render, renderRemoteUi} from '../render.ts';
import type {SandboxAPI} from '../../types.ts';

// We use the `@quilted/threads` library to create a “thread” for our iframe,
Expand All @@ -27,5 +27,8 @@ new ThreadWebWorker<never, SandboxAPI>(self as any as Worker, {

await render(root, api);
},
async renderRemoteUi(channel, api) {
await renderRemoteUi(channel, api);
},
},
});
10 changes: 9 additions & 1 deletion examples/kitchen-sink/app/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type {RemoteConnection} from '@remote-dom/core';
import {RemoteChannel} from '@remote-ui/core';

/**
* Describes the technology used to sandbox the “remote” code, so that it does
Expand All @@ -25,7 +26,8 @@ export type RenderExample =
| 'preact'
| 'react'
| 'svelte'
| 'vue';
| 'vue'
| 'react-remote-ui';

/**
* The object that the “host” page will pass to the “remote” environment. This
Expand All @@ -49,13 +51,19 @@ export interface RenderAPI {
* alert.
*/
alert(content: string): Promise<void>;

/**
* Closes the modal.
*/
closeModal(): void;
}

/**
*
*/
export interface SandboxAPI {
render(connection: RemoteConnection, api: RenderAPI): Promise<unknown>;
renderRemoteUi(channel: RemoteChannel, api: RenderAPI): Promise<unknown>;
}

// These property and method types will be used by both the host and remote environments.
Expand Down
2 changes: 2 additions & 0 deletions examples/kitchen-sink/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
"@remote-dom/preact": "workspace:*",
"@remote-dom/react": "workspace:*",
"@remote-dom/signals": "workspace:*",
"@remote-ui/core": "^2.2.4",
"@remote-ui/react": "^5.0.4",
"preact": "^10.22.0",
"react": "^18.3.0",
"react-dom": "^18.3.0",
Expand Down
11 changes: 0 additions & 11 deletions examples/remote-ui/README.md

This file was deleted.

Loading

0 comments on commit e1536a2

Please sign in to comment.