Skip to content

Commit

Permalink
Change NodeTemplatePart.replace type signature
Browse files Browse the repository at this point in the history
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<string | Node>)`:

```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
  • Loading branch information
seanpdoyle committed Dec 13, 2024
1 parent 0678eb7 commit 79b4ee6
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 7 deletions.
13 changes: 7 additions & 6 deletions src/node-template-part.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {TemplatePart} from './types.js'

const parts = new WeakMap<NodeTemplatePart, ChildNode[]>()
const parts = new WeakMap<NodeTemplatePart, Node[]>()
export class NodeTemplatePart implements TemplatePart {
constructor(
node: ChildNode,
node: Node,
public expression: string,
) {
parts.set(this, [node])
Expand All @@ -29,14 +29,15 @@ export class NodeTemplatePart implements TemplatePart {
return parts.get(this)![parts.get(this)!.length - 1].nextSibling
}

replace(...nodes: Array<string | ChildNode>): void {
const normalisedNodes: ChildNode[] = nodes.map(node => {
replace(...nodes: Array<string | Node>): 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)
}
}
21 changes: 20 additions & 1 deletion test/template-instance.ts
Original file line number Diff line number Diff line change
@@ -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', () => {
Expand Down Expand Up @@ -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: '<div>{{a}}</div>',
})
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('<div>after</div>')
})

it('replaces an empty replace() call with an empty text node', () => {
const template = document.createElement('template')
template.innerHTML = `<div>{{a}}</div>`
Expand Down

0 comments on commit 79b4ee6

Please sign in to comment.