Skip to content

Commit

Permalink
Fix morphing root level state (#4169)
Browse files Browse the repository at this point in the history
* Fix morph when x-for is used inside x-teleport

* Fix morphing teleports with root-level `x-data` state

* wip
  • Loading branch information
calebporzio committed Apr 22, 2024
1 parent c97aaff commit 54c7eb7
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 7 deletions.
6 changes: 4 additions & 2 deletions packages/alpinejs/src/directives/x-teleport.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,11 @@ directive('teleport', (el, { modifiers, expression }, { cleanup }) => {
mutateDom(() => {
placeInDom(clone, target, modifiers)

skipDuringClone(() => initTree(clone))()
skipDuringClone(() => {
initTree(clone)

clone._x_ignore = true
clone._x_ignore = true
})()
})

el._x_teleportPutBack = () => {
Expand Down
9 changes: 4 additions & 5 deletions packages/morph/src/morph.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ export function morph(from, toHtml, options) {
// Initialize the server-side HTML element with Alpine...
if (from.nodeType === 1 && window.Alpine) {
window.Alpine.cloneNode(from, to)

if (from._x_teleport && to._x_teleport) {
patch(from._x_teleport, to._x_teleport)
}
}

if (textOrComment(to)) {
Expand Down Expand Up @@ -120,11 +124,6 @@ export function morph(from, toHtml, options) {
}

function patchChildren(from, to) {
// If we hit a <template x-teleport="body">,
// let's use the teleported nodes for this patch...
if (from._x_teleport) from = from._x_teleport
if (to._x_teleport) to = to._x_teleport

let fromKeys = keyToMap(from.children)
let fromKeyHoldovers = {}

Expand Down
74 changes: 74 additions & 0 deletions tests/cypress/integration/plugins/morph.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,51 @@ test('can morph teleports',
},
)

test('can morph teleports in different places with IDs',
[html`
<div x-data="{ count: 1 }" id="a">
<button @click="count++">Inc</button>
<template x-teleport="#b" id="template">
<div>
<h1 x-text="count"></h1>
<h2>hey</h2>
</div>
</template>
<div>moving placeholder</div>
</div>
<div id="b"></div>
`],
({ get }, reload, window, document) => {
let toHtml = html`
<div x-data="{ count: 1 }" id="a">
<button @click="count++">Inc</button>
<div>moving placeholder</div>
<template x-teleport="#b" id="template">
<div>
<h1 x-text="count"></h1>
<h2>there</h2>
</div>
</template>
</div>
`
get('h1').should(haveText('1'))
get('h2').should(haveText('hey'))
get('button').click()
get('h1').should(haveText('2'))
get('h2').should(haveText('hey'))

get('div#a').then(([el]) => window.Alpine.morph(el, toHtml))

get('h1').should(haveText('2'))
get('h2').should(haveText('there'))
},
)

test('can morph',
[html`
<ul>
Expand Down Expand Up @@ -578,3 +623,32 @@ test('can morph teleports with x-for',
get('button').should(haveText('3'));
},
)

test('can morph teleports with root-level state',
[html`
<main x-data>
<template x-teleport="body">
<div x-data="{ foo: 'bar' }">
<h1 x-text="foo"></h1>
</div>
</template>
</main>
`],
({ get }, reload, window, document) => {
let toHtml = html`
<main x-data>
<template x-teleport="body">
<div x-data="{ foo: 'bar' }">
<h1 x-text="foo"></h1>
</div>
</template>
</main>
`

get('h1').should(haveText('bar'));

get('main').then(([el]) => window.Alpine.morph(el, toHtml));

get('h1').should(haveText('bar'));
},
)

0 comments on commit 54c7eb7

Please sign in to comment.