fix: synchronize forwarded component props
Quality / quality (ubuntu-latest) (push) Failing after 10m57s
Quality / quality (windows-latest) (push) Canceled after 0s

This commit is contained in:
2026-08-23 20:20:43 +05:30
parent f13e83fd74
commit 1a94179b5f
4 changed files with 28 additions and 6 deletions
+1 -1
View File
@@ -342,7 +342,7 @@
}, },
"packages/csr": { "packages/csr": {
"name": "@wrnexus/csr", "name": "@wrnexus/csr",
"version": "0.8.32", "version": "0.8.33",
"dependencies": { "dependencies": {
"@wrnexus/core": "workspace:*", "@wrnexus/core": "workspace:*",
}, },
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/csr", "name": "@wrnexus/csr",
"version": "0.8.32", "version": "0.8.33",
"type": "module", "type": "module",
"main": "src/index.ts", "main": "src/index.ts",
"exports": { "exports": {
+10 -4
View File
@@ -2775,9 +2775,15 @@ export const REACTIVE_RUNTIME = String.raw`
// server forwards these markers onto the rendered child root; evaluate // server forwards these markers onto the rendered child root; evaluate
// them here and write changes into the child's prop signals. // them here and write changes into the child's prop signals.
toArray(el.querySelectorAll("*")) toArray(el.querySelectorAll("*"))
.filter(isScopeRoot) .filter(function (node) {
return toArray(node.attributes).some(function (attr) {
return attr.name.indexOf("data-wrn-prop-bind-") === 0;
});
})
.forEach(function (node) { .forEach(function (node) {
if (!node.parentNode || ownerScope(node.parentNode) !== el) return; var targetScope = isScopeRoot(node) ? node : ownerScope(node);
if (!targetScope || targetScope === el) return;
if (!targetScope.parentNode || ownerScope(targetScope.parentNode) !== el) return;
toArray(node.attributes).forEach(function (attr) { toArray(node.attributes).forEach(function (attr) {
if (attr.name.indexOf("data-wrn-prop-bind-") !== 0) return; if (attr.name.indexOf("data-wrn-prop-bind-") !== 0) return;
var binding = pairBinding(attr.value); var binding = pairBinding(attr.value);
@@ -2796,9 +2802,9 @@ export const REACTIVE_RUNTIME = String.raw`
}); });
} catch (_) { return; } } catch (_) { return; }
var apply = function () { var apply = function () {
if (node.__wrnexusScopeApi) node.__wrnexusScopeApi.set(propName, value); if (targetScope.__wrnexusScopeApi) targetScope.__wrnexusScopeApi.set(propName, value);
}; };
if (node.__wrnexusScopeApi) apply(); if (targetScope.__wrnexusScopeApi) apply();
else queueMicrotask(apply); else queueMicrotask(apply);
}); });
}); });
+16
View File
@@ -454,6 +454,22 @@ test("parent state updates a mounted child's reactive prop", async () => {
expect(win.document.querySelector("#child-value")?.textContent).toBe("2"); expect(win.document.querySelector("#child-value")?.textContent).toBe("2");
}); });
test("a prop marker forwarded onto a child control still updates the child scope", async () => {
const win = mount(
`<div data-scope="enabled: false">` +
`<button data-on-click="enabled = true">enable</button>` +
`<div data-scope="disabled: true">` +
`<button id="child" disabled data-wrn-bind-0='["disabled","{disabled}"]' ` +
`data-wrn-prop-bind-0='["disabled","{!enabled}"]'>child</button>` +
`</div></div>`,
);
await Promise.resolve();
const buttons = win.document.querySelectorAll("button");
expect((buttons[1] as unknown as HTMLButtonElement).disabled).toBe(true);
(buttons[0] as unknown as HTMLElement).click();
expect((buttons[1] as unknown as HTMLButtonElement).disabled).toBe(false);
});
test("independent signals in one scope update correctly (dependency tracking)", () => { test("independent signals in one scope update correctly (dependency tracking)", () => {
const win = mount( const win = mount(
`<div data-scope="a: 0, b: 100"> `<div data-scope="a: 0, b: 100">