From 6777ddfcf2c45f5fdce7342a0977e50bcce7273f Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Fri, 27 Oct 2023 18:28:11 +0200 Subject: [PATCH] Don't add new [data-turbo-permanent] elements when they already exist 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. --- src/core/drive/morph_renderer.js | 29 +++++++++++++--------- src/tests/fixtures/page_refresh.html | 3 +++ src/tests/functional/page_refresh_tests.js | 18 ++++++++++++++ 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/core/drive/morph_renderer.js b/src/core/drive/morph_renderer.js index 80bb7d8e6..6537b8d2f 100644 --- a/src/core/drive/morph_renderer.js +++ b/src/core/drive/morph_renderer.js @@ -31,6 +31,7 @@ export class MorphRenderer extends Renderer { Idiomorph.morph(currentElement, newElement, { morphStyle: morphStyle, callbacks: { + beforeNodeAdded: this.#shouldAddElement, beforeNodeMorphed: this.#shouldMorphElement, beforeNodeRemoved: this.#shouldRemoveElement, afterNodeMorphed: this.#reloadStimulusControllers @@ -38,6 +39,22 @@ export class MorphRenderer extends Renderer { }) } + #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)) { @@ -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") diff --git a/src/tests/fixtures/page_refresh.html b/src/tests/fixtures/page_refresh.html index d49c97d77..d8d948a63 100644 --- a/src/tests/fixtures/page_refresh.html +++ b/src/tests/fixtures/page_refresh.html @@ -48,5 +48,8 @@

Element with Stimulus controller

+ +
+
diff --git a/src/tests/functional/page_refresh_tests.js b/src/tests/functional/page_refresh_tests.js index e267d68c9..4d657db7f 100644 --- a/src/tests/functional/page_refresh_tests.js +++ b/src/tests/functional/page_refresh_tests.js @@ -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")