From 66531e3c932cf65cb43dd9c60cc3fa4f2f429bab Mon Sep 17 00:00:00 2001 From: Thijs Louisse Date: Tue, 5 Apr 2022 15:23:08 +0200 Subject: [PATCH] fix(core): scoped slots --- .changeset/green-rocks-rule.md | 5 ++ packages/core/src/SlotMixin.js | 6 ++- packages/core/test/SlotMixin.test.js | 77 ++++++++++++++++++++++------ 3 files changed, 71 insertions(+), 17 deletions(-) create mode 100644 .changeset/green-rocks-rule.md diff --git a/.changeset/green-rocks-rule.md b/.changeset/green-rocks-rule.md new file mode 100644 index 000000000..28ee9bd9c --- /dev/null +++ b/.changeset/green-rocks-rule.md @@ -0,0 +1,5 @@ +--- +'@lion/core': patch +--- + +SlotMixin: support scoped elements diff --git a/packages/core/src/SlotMixin.js b/packages/core/src/SlotMixin.js index 4b8e04bb4..c2b0a1883 100644 --- a/packages/core/src/SlotMixin.js +++ b/packages/core/src/SlotMixin.js @@ -39,7 +39,11 @@ const SlotMixinImplementation = superclass => * @param {import('@lion/core').TemplateResult} template */ __renderAsNodes(template) { - const tempRenderTarget = document.createElement('div'); + // @ts-expect-error wait for browser support + const supportsScopedRegistry = !!ShadowRoot.prototype.createElement; + const registryRoot = supportsScopedRegistry ? this.shadowRoot : document; + // @ts-expect-error wait for browser support + const tempRenderTarget = registryRoot.createElement('div'); render(template, tempRenderTarget, this.renderOptions); return Array.from(tempRenderTarget.childNodes); } diff --git a/packages/core/test/SlotMixin.test.js b/packages/core/test/SlotMixin.test.js index f15dfeb8b..abb1013b7 100644 --- a/packages/core/test/SlotMixin.test.js +++ b/packages/core/test/SlotMixin.test.js @@ -3,6 +3,20 @@ import { defineCE, expect, fixture } from '@open-wc/testing'; import { SlotMixin } from '../src/SlotMixin.js'; import { LitElement, ScopedElementsMixin, html } from '../index.js'; +function mockScopedRegistry() { + // @ts-expect-error wait for browser support + ShadowRoot.prototype.createElement = () => {}; + // @ts-expect-error wait for browser support + window.CustomElementRegistry = class {}; +} + +function unMockScopedRegistry() { + // @ts-expect-error wait for browser support + delete ShadowRoot.prototype.createElement; + // @ts-expect-error wait for browser support + delete window.CustomElementRegistry; +} + describe('SlotMixin', () => { it('inserts provided element into lightdom and sets slot', async () => { const tag = defineCE( @@ -188,7 +202,7 @@ describe('SlotMixin', () => { } }, ); - const el = await fixture(`<${tag}><${tag}>`); + const el = await fixture(`<${tag}>`); const slot = /** @type HTMLSpanElement */ ( Array.from(el.children).find(elm => elm.slot === 'template') ); @@ -196,14 +210,10 @@ describe('SlotMixin', () => { expect(slot.tagName).to.equal('SPAN'); }); - it('supports scoped elements', async () => { - const scopedSpy = sinon.spy(); - class ScopedEl extends LitElement { - connectedCallback() { - super.connectedCallback(); - scopedSpy(); - } - } + it('supports scoped elements when polyfill loaded', async () => { + mockScopedRegistry(); + + class ScopedEl extends LitElement {} const tag = defineCE( class extends ScopedElementsMixin(SlotMixin(LitElement)) { @@ -222,12 +232,44 @@ describe('SlotMixin', () => { }; } - connectedCallback() { - super.connectedCallback(); + render() { + return html``; + } + }, + ); - // Not rendered to shadowRoot, notScopedSpy should not be called - const notScoped = document.createElement('not-scoped'); - this.appendChild(notScoped); + let error = ''; + try { + // @ts-ignore + await fixture(html`<${tag}>`, { scopedElements: true }); + } catch (e) { + // @ts-ignore + error = e.toString(); + } + // it throws when it uses our temp mock (error is browser specific, so we check overlapping part) + expect(error).to.include('.importNode is not a function'); + + unMockScopedRegistry(); + }); + + it('does not scope elements when polyfill not loaded', async () => { + class ScopedEl extends LitElement {} + + const tag = defineCE( + class extends ScopedElementsMixin(SlotMixin(LitElement)) { + static get scopedElements() { + return { + // @ts-expect-error + ...super.scopedElements, + 'scoped-el': ScopedEl, + }; + } + + get slots() { + return { + ...super.slots, + template: () => html``, + }; } render() { @@ -236,7 +278,10 @@ describe('SlotMixin', () => { }, ); - await fixture(`<${tag}><${tag}>`); - expect(scopedSpy).to.have.been.called; + const docSpy = sinon.spy(document, 'createElement'); + await fixture(html`<${tag}>`); + // one for the fixture, one for the scoped slot + expect(docSpy).to.have.been.calledTwice; + docSpy.restore(); }); });