From 79b4ee611aec5f9b7578844b06fad4e6a6e6b824 Mon Sep 17 00:00:00 2001 From: Sean Doyle Date: Thu, 12 Dec 2024 17:53:22 -0500 Subject: [PATCH] Change `NodeTemplatePart.replace` type signature Replace `ChildNode` with `Node` in `NodeTemplatePart.replace(...nodes)` signature. According to the [3.2. Template Parts and Custom Template Process Callback][] section of the specification, the `NodeTemplatePart` interface declares `replace(...nodes: Array)`: ```typescript interface NodeTemplatePart : TemplatePart { readonly attribute ContainerNode parentNode; readonly attribute Node? previousSibling; readonly attribute Node? nextSibling; [NewObject] readonly NodeList replacementNodes; void replace((Node or DOMString)... nodes); void replaceHTML(DOMString html); } ``` This commit changes the type signature and adds coverage for replacing a `NodeTemplatePart` with a `DocumentFragment` instance. [3.2. Template Parts and Custom Template Process Callback]: https://github.com/WICG/webcomponents/blob/gh-pages/proposals/Template-Instantiation.md#32-template-parts-and-custom-template-process-callback --- src/node-template-part.ts | 13 +++++++------ test/template-instance.ts | 21 ++++++++++++++++++++- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/node-template-part.ts b/src/node-template-part.ts index 978840c..413251f 100644 --- a/src/node-template-part.ts +++ b/src/node-template-part.ts @@ -1,9 +1,9 @@ import {TemplatePart} from './types.js' -const parts = new WeakMap() +const parts = new WeakMap() export class NodeTemplatePart implements TemplatePart { constructor( - node: ChildNode, + node: Node, public expression: string, ) { parts.set(this, [node]) @@ -29,14 +29,15 @@ export class NodeTemplatePart implements TemplatePart { return parts.get(this)![parts.get(this)!.length - 1].nextSibling } - replace(...nodes: Array): void { - const normalisedNodes: ChildNode[] = nodes.map(node => { + replace(...nodes: Array): void { + const normalisedNodes: Node[] = nodes.map(node => { if (typeof node === 'string') return new Text(node) return node }) if (!normalisedNodes.length) normalisedNodes.push(new Text('')) - parts.get(this)![0].before(...normalisedNodes) - for (const part of parts.get(this)!) part.remove() + const node = parts.get(this)![0] + for (const normalisedNode of normalisedNodes) node.parentNode?.insertBefore(normalisedNode, node) + for (const part of parts.get(this)!) part.parentNode?.removeChild(part) parts.set(this, normalisedNodes) } } diff --git a/test/template-instance.ts b/test/template-instance.ts index 5b5ba07..4b1242b 100644 --- a/test/template-instance.ts +++ b/test/template-instance.ts @@ -1,6 +1,6 @@ import {expect} from '@open-wc/testing' import {TemplateInstance} from '../src/template-instance' -import type {NodeTemplatePart} from '../src/node-template-part' +import {NodeTemplatePart} from '../src/node-template-part' import {propertyIdentityOrBooleanAttribute, createProcessor} from '../src/processors' describe('template-instance', () => { @@ -238,6 +238,25 @@ describe('template-instance', () => { describe('edge cases', () => { describe('NodeTemplatePart', () => { + it('replace supports a DocumentFragment Node that is not a ChildNode', () => { + const template = Object.assign(document.createElement('template'), { + innerHTML: '
{{a}}
', + }) + const {content} = Object.assign(document.createElement('template'), { + innerHTML: 'after', + }) + const instance = new TemplateInstance( + template, + {a: 'before'}, + createProcessor(part => { + if (part instanceof NodeTemplatePart) part.replace(content) + }), + ) + const root = document.createElement('div') + root.appendChild(instance) + expect(root.innerHTML).to.equal('
after
') + }) + it('replaces an empty replace() call with an empty text node', () => { const template = document.createElement('template') template.innerHTML = `
{{a}}
`