From f0928abdd3baf02a854ae119fb84c2450c36c8b1 Mon Sep 17 00:00:00 2001 From: Ethan Crawford Date: Thu, 23 Feb 2023 20:49:46 +0800 Subject: [PATCH] Use getAttribute to find duplicate ids, not the id property Since form controls can be accessed in javascript directly by name through the form object, (e.g. `my_form.my_input`), `c.id` may end up referring to an _element_ whose name is `id`, instead of referring to the global id _attribute_. Explicitly retrieving the id with `getAttribute` avoids this. --- src/elements/stream_element.js | 6 ++-- src/tests/unit/stream_element_tests.js | 50 ++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/src/elements/stream_element.js b/src/elements/stream_element.js index b9bcf35f0..b02f22afc 100644 --- a/src/elements/stream_element.js +++ b/src/elements/stream_element.js @@ -67,10 +67,10 @@ export class StreamElement extends HTMLElement { * Gets the list of duplicate children (i.e. those with the same ID) */ get duplicateChildren() { - const existingChildren = this.targetElements.flatMap((e) => [...e.children]).filter((c) => !!c.id) - const newChildrenIds = [...(this.templateContent?.children || [])].filter((c) => !!c.id).map((c) => c.id) + const existingChildren = this.targetElements.flatMap((e) => [...e.children]).filter((c) => !!c.getAttribute("id")) + const newChildrenIds = [...(this.templateContent?.children || [])].filter((c) => !!c.getAttribute("id")).map((c) => c.getAttribute("id")) - return existingChildren.filter((c) => newChildrenIds.includes(c.id)) + return existingChildren.filter((c) => newChildrenIds.includes(c.getAttribute("id"))) } /** diff --git a/src/tests/unit/stream_element_tests.js b/src/tests/unit/stream_element_tests.js index 8ca8c48f3..a9c9b90af 100644 --- a/src/tests/unit/stream_element_tests.js +++ b/src/tests/unit/stream_element_tests.js @@ -48,6 +48,33 @@ test("action=append", async () => { assert.isNull(element2.parentElement) }) +test("action=append with a form template containing an input named id", async () => { + const element = createStreamElement( + "append", + "hello", + createTemplateElement('
tail1 ') + ) + const element2 = createStreamElement( + "append", + "hello", + createTemplateElement( + '
tail2 ' + ) + ) + assert.equal(subject.find("#hello")?.textContent, "Hello Turbo") + + subject.append(element) + await nextAnimationFrame() + + assert.equal(subject.find("#hello")?.innerHTML, 'Hello Turbo
tail1 ') + assert.isNull(element.parentElement) + + subject.append(element2) + await nextAnimationFrame() + + assert.equal(subject.find("#hello")?.innerHTML, 'Hello Turbo tail1
tail2 ') +}) + test("action=append with children ID already present in target", async () => { const element = createStreamElement("append", "hello", createTemplateElement('
First
tail1 ')) const element2 = createStreamElement( @@ -87,6 +114,29 @@ test("action=prepend", async () => { assert.isNull(element.parentElement) }) +test("action=prepend with a form template containing an input named id", async () => { + const element = createStreamElement("prepend", "hello", createTemplateElement('
tail1 ')) + const element2 = createStreamElement( + "prepend", + "hello", + createTemplateElement( + '
tail2 ' + ) + ) + assert.equal(subject.find("#hello")?.textContent, "Hello Turbo") + + subject.append(element) + await nextAnimationFrame() + + assert.equal(subject.find("#hello")?.innerHTML, '
tail1 Hello Turbo') + assert.isNull(element.parentElement) + + subject.append(element2) + await nextAnimationFrame() + + assert.equal(subject.find("#hello")?.innerHTML, '
tail2 tail1 Hello Turbo') +}) + test("action=prepend with children ID already present in target", async () => { const element = createStreamElement("prepend", "hello", createTemplateElement('
First
tail1 ')) const element2 = createStreamElement(