From ccdad00ca9a26c16e93c16b1b56e29b6131e72c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20Hilles=C3=B6?= Date: Wed, 15 Jan 2025 16:29:44 +0100 Subject: [PATCH] Some fixes (#211) * replaceChildren appends all new children * Remove unsupported method * Implement element.replaceWith function * Add test for event listeners on cloneNode * PR fixes * Update changelog and version number --- CHANGELOG.md | 6 +++ docs/API.md | 2 +- lib/DocumentFragment.js | 4 -- lib/Element.js | 21 ++++++++--- package.json | 2 +- test/elements-test.js | 81 ++++++++++++++++++++++++++++++++++++++++- 6 files changed, 104 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93a952dc..ba5ece6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ Changelog ========= +## 16.0.3 + +- Add replaceWith() method to element +- Removed the clone() method from DocumentFragment because it's not part of the specification. +- Bugfix: replaceChildren() adds all supplied elements instead of only the first one. + ## 16.0.2 - Fix: Ensure that `fetch._pendingRequests` doesn't resolve prematurely diff --git a/docs/API.md b/docs/API.md index 0d7e837a..6ae0e71c 100644 --- a/docs/API.md +++ b/docs/API.md @@ -1,5 +1,5 @@ -# 16.0.1 API Reference +# 16.0.3 API Reference diff --git a/lib/DocumentFragment.js b/lib/DocumentFragment.js index e9145b0d..f7b6f4d2 100644 --- a/lib/DocumentFragment.js +++ b/lib/DocumentFragment.js @@ -6,7 +6,6 @@ const Node = require("./Node.js"); const { DOCUMENT_FRAGMENT_NODE } = require("./nodeTypes.js"); const dollarSymbol = Symbol.for("$"); -const bodySymbol = Symbol.for("$body"); const loadedSymbol = Symbol.for("loaded"); module.exports = class DocumentFragment extends Node { @@ -45,9 +44,6 @@ module.exports = class DocumentFragment extends Node { if (!$lastChild.length) return null; return this.ownerDocument._getElement($lastChild); } - clone(deep) { - return new DocumentFragment(this.ownerDocument, deep && { $elm: this[bodySymbol] }); - } querySelector(selector) { return this.ownerDocument._getElement(this.$elm.find(selector).eq(0)) || null; } diff --git a/lib/Element.js b/lib/Element.js index 6e9512b1..4f4d9d20 100644 --- a/lib/Element.js +++ b/lib/Element.js @@ -365,15 +365,26 @@ module.exports = class Element extends Node { this.$elm.remove(); parentElement?._emitter.emit("_insert"); } - replaceChildren(newChildren) { + replaceChildren() { this.$elm.contents().remove(); - - if (newChildren !== undefined) { - this.appendChild(newChildren); - } else { + for (const child of arguments) { + this.appendChild(child); + } + if (arguments.length === 0) { this._emitter.emit("_insert"); } } + replaceWith() { + const params = []; + for (const newElement of arguments) { + if (newElement && newElement.$elm) { + params.push(newElement.$elm); + } else { + params.push(String(newElement)); + } + } + this.$elm.replaceWith(params); + } removeAttribute(name) { if (!this.hasAttribute(name)) return; this.$elm.removeAttr(name); diff --git a/package.json b/package.json index 424e0aba..201e6bcc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@expressen/tallahassee", - "version": "16.0.2", + "version": "16.0.3", "description": "Lightweight client testing framework", "main": "index.js", "license": "BSD-3-Clause", diff --git a/test/elements-test.js b/test/elements-test.js index 3900e11c..cade9dc2 100644 --- a/test/elements-test.js +++ b/test/elements-test.js @@ -586,7 +586,6 @@ describe("elements", () => { it("replaces all children with new child", () => { const parent = document.getElementById("grandparent"); - expect(parent.children.length).to.equal(2); const newChild = document.createElement("p"); @@ -595,6 +594,77 @@ describe("elements", () => { parent.replaceChildren(newChild); expect(parent.childNodes.length).to.equal(1); expect(parent.textContent.trim()).to.equal("I will replace you"); + expect(parent.innerHTML).to.equal("

I will replace you

"); + }); + + it("replaces all children with new children", () => { + const parent = document.getElementById("grandparent"); + expect(parent.children.length).to.equal(2); + + const newChild = document.createElement("p"); + newChild.textContent = "First"; + const newChild2 = document.createElement("p"); + newChild2.textContent = "Second"; + const newChild3 = document.createElement("p"); + newChild3.textContent = "Third"; + + parent.replaceChildren(newChild, newChild2, newChild3); + expect(parent.childNodes.length).to.equal(3); + expect(parent.innerHTML).to.equal("

First

Second

Third

"); + }); + }); + + describe("replaceWith()", () => { + let document; + let parent; + let span; + + beforeEach(() => { + document = new Document({ + text: ` + + +
+ åäö +
+ + `, + }); + parent = document.getElementById("parent"); + span = parent.getElementsByTagName("span")[0]; + }); + + it("replaces element in the children list of its parent with the supplied element", () => { + const p = document.createElement("p"); + p.textContent = "New text"; + span.replaceWith(p); + expect(parent.innerHTML.trim()).to.equal("

New text

"); + }); + + it("replaces element in the children list of its parent with the supplied elements", () => { + const p = document.createElement("p"); + p.textContent = "New text"; + const p2 = document.createElement("p"); + p2.textContent = "A new row"; + span.replaceWith(p, p2); + expect(parent.innerHTML.trim()).to.equal("

New text

A new row

"); + }); + + it("removes the element from the parent if no new element is supplied", () => { + span.replaceWith(); + expect(parent.innerHTML.trim()).to.equal(""); + }); + + [ "a new value", undefined, null ].forEach((value) => { + it(`replaces the element in the parent with the string ${value} if ${value} is passed to the function`, () => { + span.replaceWith(value); + expect(parent.innerHTML.trim()).to.equal(String(value)); + }); + }); + + it("replaces element in the children list of its parent with the supplied strings", () => { + span.replaceWith("1 ", "2"); + expect(parent.innerHTML.trim()).to.equal("1 2"); }); }); @@ -1418,6 +1488,15 @@ describe("elements", () => { expect(elmCloneChild === elmChild).to.be.false; }); + + it("returns a clone without event listerners", () => { + const elm = document.getElementsByClassName("block")[0]; + let clicked = false; + elm.addEventListener("click", () => (clicked = true)); + const elmClone = elm.cloneNode(true); + elmClone.click(); + expect(clicked).to.be.false; + }); }); describe("video element", () => {