diff --git a/src/core/streams/stream_actions.ts b/src/core/streams/stream_actions.ts index 42611b5d2..14b86d882 100644 --- a/src/core/streams/stream_actions.ts +++ b/src/core/streams/stream_actions.ts @@ -2,10 +2,12 @@ import { StreamElement } from "../../elements/stream_element" export const StreamActions: { [action: string]: (this: StreamElement) => void } = { append() { + this.removeDuplicateTargetChildren() this.targetElement?.append(this.templateContent) }, prepend() { + this.removeDuplicateTargetChildren() this.targetElement?.prepend(this.templateContent) }, diff --git a/src/elements/stream_element.ts b/src/elements/stream_element.ts index 433278c25..ed1924ca7 100644 --- a/src/elements/stream_element.ts +++ b/src/elements/stream_element.ts @@ -28,7 +28,20 @@ export class StreamElement extends HTMLElement { disconnect() { try { this.remove() } catch {} } + + removeDuplicateTargetChildren() { + this.duplicateChildren.forEach(({targetChild}) => { + targetChild.remove(); + }) + } + get duplicateChildren() { + return [...this.templateContent?.children].map(templateChild => { + let targetChild = [...this.targetElement!.children].filter(c => c.id === templateChild.id)[0] + return { targetChild , templateChild } + }).filter(({targetChild}) => targetChild); + } + get performAction() { if (this.action) { const actionFunction = StreamActions[this.action] diff --git a/src/tests/unit/stream_element_tests.ts b/src/tests/unit/stream_element_tests.ts index b1f98bd29..df7c5662b 100644 --- a/src/tests/unit/stream_element_tests.ts +++ b/src/tests/unit/stream_element_tests.ts @@ -18,6 +18,23 @@ export class StreamElementTests extends DOMTestCase { this.assert.isNull(element.parentElement) } + async "test action=append with children ID already present in target"() { + const element = createStreamElement("append", "hello", createTemplateElement('
First
tail1 ')) + const element2 = createStreamElement("append", "hello", createTemplateElement('
New First
Second
tail2 ')) + this.assert.equal(this.find("#hello")?.textContent, "Hello Turbo") + + this.append(element) + await nextAnimationFrame() + + this.assert.equal(this.find("#hello")?.textContent, 'Hello Turbo First tail1 ') + this.assert.isNull(element.parentElement) + + this.append(element2) + await nextAnimationFrame() + + this.assert.equal(this.find("#hello")?.textContent, 'Hello Turbo tail1 New First Second tail2 ') + } + async "test action=prepend"() { const element = createStreamElement("prepend", "hello", createTemplateElement("Streams ")) this.assert.equal(this.find("#hello")?.textContent, "Hello Turbo") @@ -29,6 +46,23 @@ export class StreamElementTests extends DOMTestCase { this.assert.isNull(element.parentElement) } + async "test action=prepend with children ID already present in target"() { + const element = createStreamElement("prepend", "hello", createTemplateElement('
First
tail1 ')) + const element2 = createStreamElement("prepend", "hello", createTemplateElement('
New First
Second
tail2 ')) + this.assert.equal(this.find("#hello")?.textContent, "Hello Turbo") + + this.append(element) + await nextAnimationFrame() + + this.assert.equal(this.find("#hello")?.textContent, 'First tail1 Hello Turbo') + this.assert.isNull(element.parentElement) + + this.append(element2) + await nextAnimationFrame() + + this.assert.equal(this.find("#hello")?.textContent, 'New First Second tail2 tail1 Hello Turbo') + } + async "test action=remove"() { const element = createStreamElement("remove", "hello") this.assert.ok(this.find("#hello"))