Skip to content

Commit

Permalink
Keep dynamically added frames on morph refreshes
Browse files Browse the repository at this point in the history
If some JS code adds a remote frame to the page, we want to keep it
and reload its contents on morphing refreshes. This is useful, for example,
for pagination, where we load pages into frames.
  • Loading branch information
afcapel committed Nov 16, 2023
1 parent a247b35 commit 439755a
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 4 deletions.
12 changes: 8 additions & 4 deletions src/core/drive/morph_renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,9 @@ export class MorphRenderer extends Renderer {
}

#shouldMorphElement = (oldNode, newNode) => {
if (!(oldNode instanceof HTMLElement) || this.isMorphingTurboFrame) {
if (this.isMorphingTurboFrame) {
return true
}
else if (oldNode.hasAttribute("data-turbo-permanent")) {
} else if (!this.#morphableNode(oldNode)) {
return false
} else {
return !this.#remoteFrameReplacement(oldNode, newNode)
Expand All @@ -59,7 +58,11 @@ export class MorphRenderer extends Renderer {
}

#shouldRemoveElement = (node) => {
return this.#shouldMorphElement(node)
return this.#morphableNode(node) && !this.#isRemoteFrame(node)
}

#morphableNode(node) {
return !(node instanceof HTMLElement) || !node.hasAttribute("data-turbo-permanent")
}

#reloadRemoteFrames() {
Expand Down Expand Up @@ -89,6 +92,7 @@ export class MorphRenderer extends Renderer {
return node instanceof HTMLElement && node.nodeName.toLowerCase() === "turbo-frame" && node.getAttribute("src")
}


#remoteFrames() {
return Array.from(document.querySelectorAll('turbo-frame[src]')).filter(frame => {
return !frame.closest('[data-turbo-permanent]')
Expand Down
4 changes: 4 additions & 0 deletions src/tests/fixtures/page_refresh.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
<body>
<h1>Page to be refreshed</h1>

<turbo-frame id="frame" src="/src/tests/fixtures/frames/frame.html">
<h2>A regular frame</h2>
</turbo-frame>

<turbo-frame id="remote-frame" src="/src/tests/fixtures/frame_refresh_morph.html">
<h2>Frame to be morphed</h2>
</turbo-frame>
Expand Down
45 changes: 45 additions & 0 deletions src/tests/functional/page_refresh_tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,51 @@ test("remote frames are excluded from full page morphing", async ({ page }) => {
await expect(page.locator("#remote-frame")).toHaveText("Loaded morphed frame")
})

test("remote frames are reloaded if their src changes", async ({ page }) => {
await page.goto("/src/tests/fixtures/page_refresh.html")

await page.evaluate(() => {
const frame = document.getElementById("frame")
frame.setAttribute("src", "/src/tests/fixtures/frames.html")
})
await expect(page.locator("#frame")).toHaveText(/Frames: #frame/)

await page.evaluate(() => {
document.getElementById("frame").innerHTML = `Modified frame!`
})

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

await expect(page.locator("#frame")).not.toHaveText(/Frames: #frame/)
await expect(page.locator("#frame")).toHaveText(/Frame: Loaded/)
})

test("dynamically created remote frames are kept and reloaded", async ({ page }) => {
await page.goto("/src/tests/fixtures/page_refresh.html")

await page.evaluate(() => {
const container = document.getElementById("container")
container.innerHTML = `<turbo-frame id="hello" src="/src/tests/fixtures/frames/hello.html"></turbo-frame>`
})
await expect(page.locator("#hello")).toHaveText(/Hello from a frame/)

await page.evaluate(() => {
document.getElementById("hello").innerHTML = `Modified frame!`
})

await expect(page.locator("#hello")).not.toHaveText(/Hello from a frame/)
await expect(page.locator("#hello")).toHaveText(/Modified frame!/)

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

await expect(page.locator("#hello")).not.toHaveText(/Modified frame!/)
await expect(page.locator("#hello")).toHaveText(/Hello from a frame/)
})

test("it preserves the scroll position when the turbo-refresh-scroll meta tag is 'preserve'", async ({ page }) => {
await page.goto("/src/tests/fixtures/page_refresh.html")

Expand Down

0 comments on commit 439755a

Please sign in to comment.