diff --git a/playwright.config.ts b/playwright.config.ts index b51563c..d73ec77 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -34,10 +34,10 @@ export default defineConfig({ /* Configure projects for major browsers */ projects: [ - { - name: "chromium", - use: { ...devices["Desktop Chrome"] }, - }, + // { + // name: "chromium", + // use: { ...devices["Desktop Chrome"] }, + // }, { name: "firefox", diff --git a/src/app/components/Highlight.astro b/src/app/components/Highlight.astro index 4a3fb78..93001d4 100644 --- a/src/app/components/Highlight.astro +++ b/src/app/components/Highlight.astro @@ -21,7 +21,8 @@ const frameworkName = Object.keys(Astro.props)[0]; margin-left: auto; margin-right: auto; - max-width: 60%; + + min-width: 60%; } .wrap { padding: 10px; diff --git a/src/frontends/react/HelloWorld.tsx b/src/frontends/react/HelloWorld.tsx index 611037d..75296ee 100644 --- a/src/frontends/react/HelloWorld.tsx +++ b/src/frontends/react/HelloWorld.tsx @@ -1,26 +1,143 @@ import React from "react"; import useShape from "../../ng-mock/js-land/frontendAdapters/react/useShape"; +import flattenObject from "../utils/flattenObject"; export function HelloWorldReact() { - const state = useShape("Shape1", ""); + const state = useShape("TestShape", ""); + // @ts-expect-error window.reactState = state; - console.log("react render", state); + // console.log("[react] rendering", state); if (!state) return <>Loading state>; + // Create a table from the state object: One column for keys, one for values, one with an input to change the value. + return (
Hello World from React!
- {state.name} lives at {state.address.street} - - +Rendered in React
+ +Key | +Value | +Edit | +
---|---|---|
{key} | ++ {value instanceof Set + ? Array.from(value).join(", ") + : Array.isArray(value) + ? `[${value.join(", ")}]` + : JSON.stringify(value)} + | +
+ {typeof value === "string" ? (
+ {
+ setNestedValue(state, key, e.target.value);
+ }}
+ />
+ ) : typeof value === "number" ? (
+ {
+ setNestedValue(state, key, Number(e.target.value));
+ }}
+ />
+ ) : typeof value === "boolean" ? (
+ {
+ setNestedValue(state, key, e.target.checked);
+ }}
+ />
+ ) : Array.isArray(value) ? (
+
+
+
+
+ ) : value instanceof Set ? (
+
+
+
+
+ ) : (
+ "N/A"
+ )}
+ |
+
Hello World from Svelte!
+ function getNestedValue(obj: any, path: string) { + return path + .split(".") + .reduce((cur, k) => (cur == null ? cur : cur[k]), obj); + } + function setNestedValue(obj: any, path: string, value: any) { + const keys = path.split("."); + let cur = obj; + for (let i = 0; i < keys.length - 1; i++) { + cur = cur[keys[i]]; + if (cur == null) return; + } + cur[keys[keys.length - 1]] = value; + } + const flatEntries = $derived( + $shapeObject ? flattenObject($shapeObject as any) : [] + ); + $effect(() => { + (window as any).svelteState = $shapeObject; + }); + -{JSON.stringify($nestedObject, null, 4)}- -
Rendered in Svelte
+Key | +Value | +Edit | +
---|---|---|
{key} | ++ {#if value instanceof Set} + {Array.from(value).join(", ")} + {:else if Array.isArray(value)} + [{value.join(", ")}] + {:else} + {JSON.stringify(value)} + {/if} + | +
+ {#if typeof value === "string"}
+
+ setNestedValue($shapeObject, key, e.target.value)}
+ />
+ {:else if typeof value === "number"}
+
+ setNestedValue($shapeObject, key, Number(e.target.value))}
+ />
+ {:else if typeof value === "boolean"}
+
+ setNestedValue($shapeObject, key, e.target.checked)}
+ />
+ {:else if Array.isArray(value)}
+
+
+
+
+ {:else if value instanceof Set}
+
+
+
+
+ {:else}
+ N/A
+ {/if}
+ |
+
Loading state
+{/if} diff --git a/src/frontends/tests/reactiveCrossFramework.spec.ts b/src/frontends/tests/reactiveCrossFramework.spec.ts index 21f6e0b..4550602 100644 --- a/src/frontends/tests/reactiveCrossFramework.spec.ts +++ b/src/frontends/tests/reactiveCrossFramework.spec.ts @@ -1,5 +1,20 @@ import { test, expect } from "@playwright/test"; +const mockTestObject = { + type: "TestObject", + stringValue: "string", + numValue: 42, + boolValue: true, + nullValue: null, + arrayValue: [1, 2, 3], + objectValue: { + nestedString: "nested", + nestedNum: 7, + nestedArray: [10, 12], + }, + setValue: new Set(["v1", "v2", "v3"]), +}; + test("components load", async ({ page }) => { await page.goto("/"); await page.waitForSelector(".vue astro-island"); @@ -8,3 +23,159 @@ test("components load", async ({ page }) => { await expect(page.locator(".react .title")).toHaveText("react"); await expect(page.locator(".svelte .title")).toHaveText("svelte"); }); + +// TODO: Test without signal pooling. +test.describe("cross framework propagation", () => { + const frameworks = ["vue", "react", "svelte"] as const; + + const isPlainObject = (v: unknown): v is RecordHello World from Vue!
-Type is {{shapeObj.type}} with street {{shapeObj.address.street}}
- - -Rendered in Vue
+ + +Key | +Value | +Edit | +
---|---|---|
{{ key }} | + + ++ + {{ Array.from(value).join(', ') }} + + + [{{ value.join(', ') }}] + + + {{ JSON.stringify(value) }} + + | + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ N/A
+
+ |
+
Loading state
+