Skip to content

Commit 2bf8838

Browse files
committed
feat: add support for "declarative shadow dom"
1 parent 13a117a commit 2bf8838

3 files changed

Lines changed: 101 additions & 0 deletions

File tree

src/auto-shadow-root.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export function autoShadowRoot(element: HTMLElement): void {
2+
for (const template of element.querySelectorAll<HTMLTemplateElement>('template[data-shadowroot]')) {
3+
if (template.parentElement === element) {
4+
element
5+
.attachShadow({
6+
mode: template.getAttribute('data-shadowroot') === 'closed' ? 'closed' : 'open'
7+
})
8+
.appendChild(template.content.cloneNode(true))
9+
}
10+
}
11+
}

src/controller.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {register} from './register'
22
import {bind} from './bind'
3+
import {autoShadowRoot} from './auto-shadow-root'
34
import {wrap} from './wrap'
45

56
interface CustomElement {
@@ -15,6 +16,7 @@ interface CustomElement {
1516
export function controller(classObject: CustomElement): void {
1617
wrap(classObject.prototype, 'connectedCallback', function (this: HTMLElement) {
1718
this.toggleAttribute('data-catalyst', true)
19+
autoShadowRoot(this)
1820
bind(this)
1921
})
2022
register(classObject)

test/auto-shadow-root.js

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import {autoShadowRoot} from '../lib/auto-shadow-root.js'
2+
3+
describe('autoShadowRoot', () => {
4+
window.customElements.define('autoshadowroot-test-element', class extends HTMLElement {})
5+
6+
let root
7+
8+
beforeEach(() => {
9+
root = document.createElement('div')
10+
document.body.appendChild(root)
11+
})
12+
13+
afterEach(() => {
14+
root.remove()
15+
})
16+
17+
it('automatically declares shadowroot for elements with `template[data-shadowroot]` children', () => {
18+
const instance = document.createElement('shadowroot-test-element')
19+
const template = document.createElement('template')
20+
template.innerHTML = 'Hello World'
21+
template.setAttribute('data-shadowroot', 'open')
22+
instance.appendChild(template)
23+
24+
autoShadowRoot(instance)
25+
26+
expect(instance).to.have.property('shadowRoot').not.equal(null)
27+
expect(instance.shadowRoot.textContent).to.equal('Hello World')
28+
})
29+
30+
it('does not attach shadowroot without a template`data-shadowroot` child', () => {
31+
const instance = document.createElement('shadowroot-test-element')
32+
const template = document.createElement('template')
33+
template.setAttribute('data-notshadowroot', 'open')
34+
const otherTemplate = document.createElement('div')
35+
otherTemplate.setAttribute('data-shadowroot', 'open')
36+
instance.appendChild(template, otherTemplate)
37+
38+
autoShadowRoot(instance)
39+
40+
expect(instance).to.have.property('shadowRoot').equal(null)
41+
})
42+
43+
it('does not attach shadowroots which are not direct children of the element', () => {
44+
const instance = document.createElement('shadowroot-test-element')
45+
const div = document.createElement('div')
46+
const template = document.createElement('template')
47+
template.setAttribute('data-notshadowroot', 'open')
48+
div.appendChild(template)
49+
instance.appendChild(div)
50+
51+
autoShadowRoot(instance)
52+
53+
expect(instance).to.have.property('shadowRoot').equal(null)
54+
})
55+
56+
it('attaches shadowRoot nodes open by default', () => {
57+
const instance = document.createElement('shadowroot-test-element')
58+
const template = document.createElement('template')
59+
template.innerHTML = 'Hello World'
60+
template.setAttribute('data-shadowroot', '')
61+
instance.appendChild(template)
62+
63+
autoShadowRoot(instance)
64+
65+
expect(instance).to.have.property('shadowRoot').not.equal(null)
66+
expect(instance.shadowRoot.textContent).to.equal('Hello World')
67+
})
68+
69+
it('attaches shadowRoot nodes closed if `data-shadowroot` is `closed`', () => {
70+
const instance = document.createElement('shadowroot-test-element')
71+
const template = document.createElement('template')
72+
template.innerHTML = 'Hello World'
73+
template.setAttribute('data-shadowroot', 'closed')
74+
instance.appendChild(template)
75+
76+
let shadowRoot = null
77+
chai.spy.on(instance, 'attachShadow', (...args) => {
78+
shadowRoot = Element.prototype.attachShadow.apply(instance, args)
79+
return shadowRoot
80+
})
81+
82+
autoShadowRoot(instance)
83+
84+
expect(instance).to.have.property('shadowRoot').equal(null)
85+
expect(instance.attachShadow).to.have.been.called.once.with.exactly({mode: 'closed'})
86+
expect(shadowRoot.textContent).to.equal('Hello World')
87+
})
88+
})

0 commit comments

Comments
 (0)