Skip to content

Commit

Permalink
Don't add new [data-turbo-permanent] elements when they already exist…
Browse files Browse the repository at this point in the history
… in the page.

It can happen that a same element exists but that idiomorph won't match it
because some JS moved the element to another position. This will handle such
scenarios automatically.
  • Loading branch information
jorgemanrubia committed Oct 27, 2023
1 parent 0c94a8f commit 6777ddf
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 12 deletions.
29 changes: 17 additions & 12 deletions src/core/drive/morph_renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,30 @@ export class MorphRenderer extends Renderer {
Idiomorph.morph(currentElement, newElement, {
morphStyle: morphStyle,
callbacks: {
beforeNodeAdded: this.#shouldAddElement,
beforeNodeMorphed: this.#shouldMorphElement,
beforeNodeRemoved: this.#shouldRemoveElement,
afterNodeMorphed: this.#reloadStimulusControllers
}
})
}

#shouldAddElement = (node) => {
return !(node.id && node.hasAttribute("data-turbo-permanent") && document.getElementById(node.id))
}

#shouldMorphElement = (node) => {
if (node instanceof HTMLElement) {
return !node.hasAttribute("data-turbo-permanent") && (this.isMorphingTurboFrame || !this.#isRemoteFrame(node))
} else {
return true
}
}

#shouldRemoveElement = (node) => {
return this.#shouldMorphElement(node)
}

#reloadRemoteFrames() {
this.#remoteFrames().forEach((frame) => {
if (this.#isRemoteFrame(frame)) {
Expand All @@ -61,18 +78,6 @@ export class MorphRenderer extends Renderer {
this.#morphElements(currentElement, newElement.children, "innerHTML")
}

#shouldRemoveElement = (node) => {
return this.#shouldMorphElement(node)
}

#shouldMorphElement = (node) => {
if (node instanceof HTMLElement) {
return !node.hasAttribute("data-turbo-permanent") && (this.isMorphingTurboFrame || !this.#isRemoteFrame(node))
} else {
return true
}
}

#reloadStimulusControllers = async (node) => {
if (node instanceof HTMLElement && node.hasAttribute("data-controller")) {
const originalAttribute = node.getAttribute("data-controller")
Expand Down
3 changes: 3 additions & 0 deletions src/tests/fixtures/page_refresh.html
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,8 @@ <h3>Element with Stimulus controller</h3>
<input type="hidden" name="sleep" value="50">
<input id="form-submit" type="submit" value="form[method=post]">
</form>

<div id="container">
</div>
</body>
</html>
18 changes: 18 additions & 0 deletions src/tests/functional/page_refresh_tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,24 @@ test("it preserves data-turbo-permanent elements", async ({ page }) => {
await expect(page.locator("#preserve-me")).toHaveText("Preserve me, I have a family!")
})

test("it preserves data-turbo-permanent elements that don't match when their ids do", async ({ page }) => {
await page.goto("/src/tests/fixtures/page_refresh.html")

await page.evaluate(() => {
const element = document.getElementById("preserve-me")

element.textContent = "Preserve me, I have a family!"
document.getElementById("container").append(element)
})

await expect(page.locator("#preserve-me")).toHaveText("Preserve me, I have a family!")

await page.click("#form-submit")
await nextEventNamed(page, "turbo:render", { renderMethod: "morph" })

await expect(page.locator("#preserve-me")).toHaveText("Preserve me, I have a family!")
})

test("it reloads data-controller attributes after a morph", async ({ page }) => {
await page.goto("/src/tests/fixtures/page_refresh.html")

Expand Down

0 comments on commit 6777ddf

Please sign in to comment.