Fix island reactivity lost on client-side navigation; add Playwright tests
When morphing DOM after server fetch, the morph engine reuses elements with the same tag. If old element was island A and new is island B, syncAttrs updates data-sx-island but the JS property _sxBoundisland-hydrated persists on the reused element. sx-hydrate-islands then skips it. Fix: in morphNode, when data-sx-island attribute changes between old and new elements, dispose the old island's signals and clear the hydration flag so the new island gets properly hydrated. New Playwright tests: - counter → temperature navigation: temperature signals work - temperature → counter navigation: counter signals work - Direct load verification for both islands - No JS errors during navigation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
// =========================================================================
|
||||
|
||||
var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } });
|
||||
var SX_VERSION = "2026-03-24T15:37:52Z";
|
||||
var SX_VERSION = "2026-03-24T17:35:05Z";
|
||||
|
||||
function isNil(x) { return x === NIL || x === null || x === undefined; }
|
||||
function isSxTruthy(x) { return x !== false && !isNil(x); }
|
||||
@@ -3925,7 +3925,7 @@ PRIMITIVES["revert-optimistic"] = revertOptimistic;
|
||||
PRIMITIVES["find-oob-swaps"] = findOobSwaps;
|
||||
|
||||
// morph-node
|
||||
var morphNode = function(oldNode, newNode) { return (isSxTruthy(sxOr(domHasAttr(oldNode, "sx-preserve"), domHasAttr(oldNode, "sx-ignore"))) ? NIL : (isSxTruthy((isSxTruthy(domHasAttr(oldNode, "data-sx-island")) && isSxTruthy(isProcessed(oldNode, "island-hydrated")) && isSxTruthy(domHasAttr(newNode, "data-sx-island")) && (domGetAttr(oldNode, "data-sx-island") == domGetAttr(newNode, "data-sx-island")))) ? morphIslandChildren(oldNode, newNode) : (isSxTruthy(sxOr(!isSxTruthy((domNodeType(oldNode) == domNodeType(newNode))), !isSxTruthy((domNodeName(oldNode) == domNodeName(newNode))))) ? domReplaceChild(domParent(oldNode), domClone(newNode), oldNode) : (isSxTruthy(sxOr((domNodeType(oldNode) == 3), (domNodeType(oldNode) == 8))) ? (isSxTruthy(!isSxTruthy((domTextContent(oldNode) == domTextContent(newNode)))) ? domSetTextContent(oldNode, domTextContent(newNode)) : NIL) : (isSxTruthy((domNodeType(oldNode) == 1)) ? (syncAttrs(oldNode, newNode), (isSxTruthy(!isSxTruthy((isSxTruthy(domIsActiveElement(oldNode)) && domIsInputElement(oldNode)))) ? morphChildren(oldNode, newNode) : NIL)) : NIL))))); };
|
||||
var morphNode = function(oldNode, newNode) { return (isSxTruthy(sxOr(domHasAttr(oldNode, "sx-preserve"), domHasAttr(oldNode, "sx-ignore"))) ? NIL : (isSxTruthy((isSxTruthy(domHasAttr(oldNode, "data-sx-island")) && isSxTruthy(isProcessed(oldNode, "island-hydrated")) && isSxTruthy(domHasAttr(newNode, "data-sx-island")) && (domGetAttr(oldNode, "data-sx-island") == domGetAttr(newNode, "data-sx-island")))) ? morphIslandChildren(oldNode, newNode) : (isSxTruthy(sxOr(!isSxTruthy((domNodeType(oldNode) == domNodeType(newNode))), !isSxTruthy((domNodeName(oldNode) == domNodeName(newNode))))) ? domReplaceChild(domParent(oldNode), domClone(newNode), oldNode) : (isSxTruthy(sxOr((domNodeType(oldNode) == 3), (domNodeType(oldNode) == 8))) ? (isSxTruthy(!isSxTruthy((domTextContent(oldNode) == domTextContent(newNode)))) ? domSetTextContent(oldNode, domTextContent(newNode)) : NIL) : (isSxTruthy((domNodeType(oldNode) == 1)) ? ((isSxTruthy((isSxTruthy(domHasAttr(oldNode, "data-sx-island")) && isSxTruthy(domHasAttr(newNode, "data-sx-island")) && !isSxTruthy((domGetAttr(oldNode, "data-sx-island") == domGetAttr(newNode, "data-sx-island"))))) ? (disposeIslandsIn(oldNode), clearProcessed(oldNode, "island-hydrated")) : NIL), syncAttrs(oldNode, newNode), (isSxTruthy(!isSxTruthy((isSxTruthy(domIsActiveElement(oldNode)) && domIsInputElement(oldNode)))) ? morphChildren(oldNode, newNode) : NIL)) : NIL))))); };
|
||||
PRIMITIVES["morph-node"] = morphNode;
|
||||
|
||||
// sync-attrs
|
||||
|
||||
Reference in New Issue
Block a user