fix(core): scoped slots

This commit is contained in:
Thijs Louisse 2022-04-05 15:23:08 +02:00 committed by Thomas Allmer
parent e913489344
commit 66531e3c93
3 changed files with 71 additions and 17 deletions

View file

@ -0,0 +1,5 @@
---
'@lion/core': patch
---
SlotMixin: support scoped elements

View file

@ -39,7 +39,11 @@ const SlotMixinImplementation = superclass =>
* @param {import('@lion/core').TemplateResult} template * @param {import('@lion/core').TemplateResult} template
*/ */
__renderAsNodes(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); render(template, tempRenderTarget, this.renderOptions);
return Array.from(tempRenderTarget.childNodes); return Array.from(tempRenderTarget.childNodes);
} }

View file

@ -3,6 +3,20 @@ import { defineCE, expect, fixture } from '@open-wc/testing';
import { SlotMixin } from '../src/SlotMixin.js'; import { SlotMixin } from '../src/SlotMixin.js';
import { LitElement, ScopedElementsMixin, html } from '../index.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', () => { describe('SlotMixin', () => {
it('inserts provided element into lightdom and sets slot', async () => { it('inserts provided element into lightdom and sets slot', async () => {
const tag = defineCE( const tag = defineCE(
@ -188,7 +202,7 @@ describe('SlotMixin', () => {
} }
}, },
); );
const el = await fixture(`<${tag}><${tag}>`); const el = await fixture(`<${tag}></${tag}>`);
const slot = /** @type HTMLSpanElement */ ( const slot = /** @type HTMLSpanElement */ (
Array.from(el.children).find(elm => elm.slot === 'template') Array.from(el.children).find(elm => elm.slot === 'template')
); );
@ -196,14 +210,10 @@ describe('SlotMixin', () => {
expect(slot.tagName).to.equal('SPAN'); expect(slot.tagName).to.equal('SPAN');
}); });
it('supports scoped elements', async () => { it('supports scoped elements when polyfill loaded', async () => {
const scopedSpy = sinon.spy(); mockScopedRegistry();
class ScopedEl extends LitElement {
connectedCallback() { class ScopedEl extends LitElement {}
super.connectedCallback();
scopedSpy();
}
}
const tag = defineCE( const tag = defineCE(
class extends ScopedElementsMixin(SlotMixin(LitElement)) { class extends ScopedElementsMixin(SlotMixin(LitElement)) {
@ -222,12 +232,44 @@ describe('SlotMixin', () => {
}; };
} }
connectedCallback() { render() {
super.connectedCallback(); return html`<slot name="template"></slot>`;
}
},
);
// Not rendered to shadowRoot, notScopedSpy should not be called let error = '';
const notScoped = document.createElement('not-scoped'); try {
this.appendChild(notScoped); // @ts-ignore
await fixture(html`<${tag}></${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`<scoped-el></scoped-el>`,
};
} }
render() { render() {
@ -236,7 +278,10 @@ describe('SlotMixin', () => {
}, },
); );
await fixture(`<${tag}><${tag}>`); const docSpy = sinon.spy(document, 'createElement');
expect(scopedSpy).to.have.been.called; await fixture(html`<${tag}></${tag}>`);
// one for the fixture, one for the scoped slot
expect(docSpy).to.have.been.calledTwice;
docSpy.restore();
}); });
}); });