docs(testing-helpers): types for ScopedElementsMixin

This commit is contained in:
Benny Powers
2020-04-25 21:44:35 +03:00
committed by Benny Powers
parent f265d9e9d5
commit 9b827e7156
5 changed files with 94 additions and 71 deletions

View File

@@ -6,7 +6,9 @@ import { defineScopedElement, registerElement } from './registerElement.js';
import { shadyTemplateFactory } from './shadyTemplateFactory.js';
/**
* @typedef {import('./types').ScopedElementsMixin} ScopedElementsMixin
* @typedef {import('./types').ScopedElementsMap} ScopedElementsMap
* @typedef {import("lit-element").LitElement} LitElement
* @typedef {import('lit-html/lib/shady-render').ShadyRenderOptions} ShadyRenderOptions
* @typedef {function(TemplateResult, Element|DocumentFragment|ShadowRoot, ShadyRenderOptions): void} RenderFunction
*/
@@ -111,64 +113,63 @@ const scopedElementsTemplateFactory = (
return shadyTemplateFactory(scopeName)(newTemplate);
};
export const ScopedElementsMixin = dedupeMixin(
superclass =>
// eslint-disable-next-line no-shadow
class ScopedElementsMixin extends superclass {
/**
* Obtains the scoped elements definitions map
*
* @returns {ScopedElementsMap}
*/
static get scopedElements() {
return {};
/** @type {ScopedElementsMixin} */
const ScopedElementsMixinImplementation = superclass =>
class ScopedElementsHost extends superclass {
/**
* Obtains the scoped elements definitions map
*
* @returns {ScopedElementsMap}
*/
static get scopedElements() {
return {};
}
/** @override */
static render(template, container, options) {
if (!options || typeof options !== 'object' || !options.scopeName) {
throw new Error('The `scopeName` option is required.');
}
const { scopeName } = options;
/** @override */
static render(template, container, options) {
if (!options || typeof options !== 'object' || !options.scopeName) {
throw new Error('The `scopeName` option is required.');
}
const { scopeName } = options;
const templateCache = getTemplateCache(this);
const tagsCache = getTagsCache(this);
const { scopedElements } = this;
const templateCache = getTemplateCache(this);
const tagsCache = getTagsCache(this);
const { scopedElements } = this;
return super.render(template, container, {
...options,
templateFactory: scopedElementsTemplateFactory(
scopeName,
scopedElements,
templateCache,
tagsCache,
),
});
}
// @ts-ignore
return super.render(template, container, {
...options,
templateFactory: scopedElementsTemplateFactory(
scopeName,
scopedElements,
templateCache,
tagsCache,
),
});
}
/**
* Defines a scoped element
*
* @param {string} tagName
* @param {typeof HTMLElement} klass
*/
defineScopedElement(tagName, klass) {
return defineScopedElement(tagName, klass, getTagsCache(this.constructor));
}
/**
* Defines a scoped element
*
* @param {string} tagName
* @param {typeof HTMLElement} klass
*/
defineScopedElement(tagName, klass) {
return defineScopedElement(tagName, klass, getTagsCache(this.constructor));
}
/**
* Returns a scoped tag name
*
* @param {string} tagName
* @returns {string|undefined}
*/
static getScopedTagName(tagName) {
const klass = this.scopedElements[tagName];
/**
* Returns a scoped tag name
*
* @param {string} tagName
* @returns {string|undefined}
*/
static getScopedTagName(tagName) {
const klass = this.scopedElements[tagName];
return klass
? registerElement(tagName, klass, getTagsCache(this))
: getTagsCache(this).get(tagName);
}
};
return klass
? registerElement(tagName, klass, getTagsCache(this))
: getTagsCache(this).get(tagName);
}
},
);
export const ScopedElementsMixin = dedupeMixin(ScopedElementsMixinImplementation);

View File

@@ -1,3 +1,27 @@
import { Constructor } from "@open-wc/dedupe-mixin";
import { LitElement } from "lit-element";
export type ScopedElementsMap = {
[key: string]: typeof HTMLElement;
}
export declare class ScopedElementsHost {
/**
* Obtains the scoped elements definitions map
*/
static scopedElements: ScopedElementsMap;
/**
* Returns a scoped tag name
*/
static getScopedTagName(tagName: string): string;
/**
* Defines a scoped element
*/
defineScopedElement<T extends HTMLElement>(tagName: string, klass: Constructor<T>): void
}
declare function ScopedElementsMixinImplementation<T extends Constructor<LitElement>>(superclass: T): T & Constructor<ScopedElementsHost>
export type ScopedElementsMixin = typeof ScopedElementsMixinImplementation;

View File

@@ -22,7 +22,6 @@ describe('ScopedElementsMixin', () => {
it('has a default value for "static get scopedElements()" of {}', async () => {
const tag = defineCE(class extends ScopedElementsMixin(LitElement) {});
const el = await fixture(`<${tag}></${tag}>`);
// @ts-ignore
expect(el.constructor.scopedElements).to.deep.equal({});
});
@@ -257,7 +256,6 @@ describe('ScopedElementsMixin', () => {
expect(el.shadowRoot.children[1]).to.not.be.an.instanceOf(FeatureB);
expect(el.shadowRoot.children[2]).to.not.undefined;
// @ts-ignore
el.defineScopedElement('feature-b', FeatureB);
expect(el.shadowRoot.children[1]).to.be.an.instanceOf(FeatureB);
@@ -359,9 +357,7 @@ describe('ScopedElementsMixin', () => {
const el = await fixture(`<${tag}></${tag}>`);
// @ts-ignore
expect(el.constructor.getScopedTagName('feature-a')).to.match(tagRegExp);
// @ts-ignore
expect(el.constructor.getScopedTagName('feature-b')).to.match(tagRegExp);
});
@@ -387,7 +383,6 @@ describe('ScopedElementsMixin', () => {
const el = await fixture(`<${tag}></${tag}>`);
// @ts-ignore
expect(el.constructor.getScopedTagName('unregistered-feature')).to.match(tagRegExp);
});
});

View File

@@ -7,7 +7,8 @@ import { getScopedElementsTemplate } from './scopedElementsWrapper.js';
/**
* @typedef {
import('lit-html').TemplateResult | import('lit-html').TemplateResult[]
import('lit-html').TemplateResult
| import('lit-html').TemplateResult[]
| Node | Node[]
| string | string[]
| number | number[]
@@ -59,16 +60,20 @@ export function litFixtureSync(template, options = {}) {
* @returns {Promise<T>}
*/
export async function litFixture(template, options = {}) {
/** @type {T} */
// NB: in the case of scopedElements, this is ScopedElementsTestWrapper, not T,
// but that's only a small lie
const el = litFixtureSync(template, options);
await elementUpdated(el);
if (options.scopedElements) {
const [node] = Array.from(el.shadowRoot.childNodes).filter(isUsefulNode);
await elementUpdated(/** @type {T} */ (node));
const [node] =
/** @type {T[]} */
(Array.from(el.shadowRoot.childNodes).filter(isUsefulNode));
await elementUpdated(node.firstElementChild);
return /** @type {T} */ (node);
return node;
}
// @ts-ignore
return el;
}

View File

@@ -1,6 +1,5 @@
import { ScopedElementsMixin } from '@open-wc/scoped-elements';
import { html, LitElement, TemplateResult } from 'lit-element';
import { nothing } from 'lit-html';
import { isIterable } from './lib.js';
/** @typedef {import('@open-wc/scoped-elements').ScopedElementsMap} ScopedElementsMap */
@@ -31,13 +30,13 @@ class ScopedElementsTestWrapper extends ScopedElementsMixin(LitElement) {
/** @type {ScopedElementsMap} */
this.scopedElements = {};
/** @type {TemplateResult|{}} */
this.template = nothing;
/** @type {import('./litFixture').LitHTMLRenderable} */
// eslint-disable-next-line babel/no-unused-expressions
this.template;
}
async firstUpdated() {
// @ts-ignore
await super.firstUpdated();
firstUpdated(_changed) {
super.firstUpdated(_changed);
Object.keys(this.scopedElements).forEach(key =>
this.defineScopedElement(key, this.scopedElements[key]),
@@ -49,7 +48,6 @@ class ScopedElementsTestWrapper extends ScopedElementsMixin(LitElement) {
}
}
// @ts-ignore
customElements.define('scoped-elements-test-wrapper', ScopedElementsTestWrapper);
/**