Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Re-structure turbo-stream[action=morph] support #1240

Merged
merged 1 commit into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 17 additions & 11 deletions src/core/streams/stream_actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,31 @@ export const StreamActions = {
},

replace() {
this.targetElements.forEach((e) => e.replaceWith(this.templateContent))
const method = this.getAttribute("method")

this.targetElements.forEach((targetElement) => {
if (method === "morph") {
morphElements(targetElement, this.templateContent)
} else {
targetElement.replaceWith(this.templateContent)
}
})
},

update() {
const method = this.getAttribute("method")

this.targetElements.forEach((targetElement) => {
targetElement.innerHTML = ""
targetElement.append(this.templateContent)
if (method === "morph") {
morphChildren(targetElement, this.templateContent)
} else {
targetElement.innerHTML = ""
targetElement.append(this.templateContent)
}
})
},

refresh() {
session.refresh(this.baseURI, this.requestId)
},

morph() {
const morph = this.hasAttribute("children-only") ?
morphChildren :
morphElements

this.targetElements.forEach((targetElement) => morph(targetElement, this.templateContent))
}
}
16 changes: 0 additions & 16 deletions src/tests/fixtures/morph_stream_action.html

This file was deleted.

4 changes: 4 additions & 0 deletions src/tests/fixtures/stream.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,9 @@
<div id="container">
<input id="container-element">
</div>

<div id="message_1">
<div>Morph me</div>
</div>
</body>
</html>
48 changes: 0 additions & 48 deletions src/tests/functional/morph_stream_action_tests.js

This file was deleted.

46 changes: 45 additions & 1 deletion src/tests/functional/stream_tests.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { test } from "@playwright/test"
import { expect, test } from "@playwright/test"
import { assert } from "chai"
import {
hasSelector,
nextBeat,
nextEventNamed,
nextEventOnTarget,
noNextEventOnTarget,
readEventLogs,
waitUntilNoSelector,
waitUntilText
Expand Down Expand Up @@ -182,6 +184,48 @@ test("receiving a remove stream message preserves focus blurs the activeElement"
assert.notOk(await hasSelector(page, ":focus"))
})

test("dispatches a turbo:before-morph-element & turbo:morph-element for each morph stream action", async ({ page }) => {
await page.evaluate(() => {
window.Turbo.renderStreamMessage(`
<turbo-stream action="replace" method="morph" target="message_1">
<template>
<div id="message_1">
<h1>Morphed</h1>
</div>
</template>
</turbo-stream>
`)
})

await nextEventOnTarget(page, "message_1", "turbo:before-morph-element")
await nextEventOnTarget(page, "message_1", "turbo:morph-element")
await expect(page.locator("#message_1")).toHaveText("Morphed")
})

test("preventing a turbo:before-morph-element prevents the morph", async ({ page }) => {
await page.evaluate(() => {
addEventListener("turbo:before-morph-element", (event) => {
event.preventDefault()
})
})

await page.evaluate(() => {
window.Turbo.renderStreamMessage(`
<turbo-stream action="replace" method="morph" target="message_1">
<template>
<div id="message_1">
<h1>Morphed</h1>
</div>
</template>
</turbo-stream>
`)
})

await nextEventOnTarget(page, "message_1", "turbo:before-morph-element")
await noNextEventOnTarget(page, "message_1", "turbo:morph-element")
await expect(page.locator("#message_1")).toHaveText("Morph me")
})

async function getReadyState(page, id) {
return page.evaluate((id) => {
const element = document.getElementById(id)
Expand Down
15 changes: 8 additions & 7 deletions src/tests/unit/stream_element_tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import { assert } from "@open-wc/testing"
import { sleep } from "../helpers/page"
import * as Turbo from "../../index"

function createStreamElement(action, target, templateElement) {
function createStreamElement(action, target, templateElement, attributes = {}) {
const element = new StreamElement()
if (action) element.setAttribute("action", action)
if (target) element.setAttribute("target", target)
if (templateElement) element.appendChild(templateElement)
Object.entries(attributes).forEach((attribute) => element.setAttribute(...attribute))
return element
}

Expand Down Expand Up @@ -197,9 +198,9 @@ test("test action=refresh discarded when matching request id", async () => {
assert.ok(document.body.hasAttribute("data-modified"))
})

test("action=morph", async () => {
test("action=replace method=morph", async () => {
const templateElement = createTemplateElement(`<h1 id="hello">Hello Turbo Morphed</h1>`)
const element = createStreamElement("morph", "hello", templateElement)
const element = createStreamElement("replace", "hello", templateElement, { method: "morph" })

assert.equal(subject.find("div#hello")?.textContent, "Hello Turbo")

Expand All @@ -210,9 +211,9 @@ test("action=morph", async () => {
assert.equal(subject.find("h1#hello")?.textContent, "Hello Turbo Morphed")
})

test("action=morph with text content change", async () => {
test("action=replace method=morph with text content change", async () => {
const templateElement = createTemplateElement(`<div id="hello">Hello Turbo Morphed</div>`)
const element = createStreamElement("morph", "hello", templateElement)
const element = createStreamElement("replace", "hello", templateElement, { method: "morph" })

assert.equal(subject.find("div#hello")?.textContent, "Hello Turbo")

Expand All @@ -223,9 +224,9 @@ test("action=morph with text content change", async () => {
assert.equal(subject.find("div#hello")?.textContent, "Hello Turbo Morphed")
})

test("action=morph children-only", async () => {
test("action=update method=morph", async () => {
const templateElement = createTemplateElement(`<h1 id="hello-child-element">Hello Turbo Morphed</h1>`)
const element = createStreamElement("morph", "hello", templateElement)
const element = createStreamElement("update", "hello", templateElement, { method: "morph" })
const target = subject.find("div#hello")
assert.equal(target?.textContent, "Hello Turbo")
element.setAttribute("children-only", true)
Expand Down
Loading