feat(scoped-elements): self-registering components compatibility

Reusing the global tag name can be useful in some situations in which not all component libraries are exporting the component classes or when components are looking in DOM by specific tag names. In those cases, although this change doesn't remove the limitation of having just one version of those elements in our app, it makes them work properly inside our scoped elements as we are going to use the tag name they use in their definition.
This commit is contained in:
Manuel Martin
2020-04-21 14:56:58 +02:00
committed by Manuel Martín
parent 417d890c7c
commit d4806e4397
3 changed files with 66 additions and 11 deletions

View File

@@ -50,6 +50,8 @@ npm i --save @open-wc/scoped-elements
> };
> }
> ```
>
> If you try to register the same element globally AND locally with the exact same name AND class instance it will reuse the global tag name and NOT scope it.
4. Use your components in your html.

View File

@@ -21,6 +21,26 @@ const defineElement = (tagName, klass, registry = customElements) => {
addToGlobalTagsCache(tagName, klass);
};
/**
* Stores a lazy element in the cache to be used in future
*
* @param {string} tagName
* @param {CustomElementRegistry} registry
* @param {Map<string, string>} tagsCache
* @returns {string}
*/
const storeLazyElementInCache = (tagName, registry, tagsCache) => {
const tag = createUniqueTag(tagName, registry);
if (!tagsCache) {
throw new Error('Lazy scoped elements requires the use of tags cache');
}
tagsCache.set(tagName, tag);
return tag;
};
/**
* Define a scoped custom element storing the scoped tag name in the cache
*
@@ -31,20 +51,22 @@ const defineElement = (tagName, klass, registry = customElements) => {
*/
const defineElementAndStoreInCache = (tagName, klass, tagsCache) => {
const registry = customElements;
const tag = createUniqueTag(tagName, registry);
if (extendsHTMLElement(klass)) {
// @ts-ignore
// we extend it just in case the class has been defined manually
defineElement(tag, klass, registry);
} else {
if (!tagsCache) {
throw new Error('Lazy scoped elements requires the use of tags cache');
}
tagsCache.set(tagName, tag);
if (!extendsHTMLElement(klass)) {
return storeLazyElementInCache(tagName, registry, tagsCache);
}
if (klass === customElements.get(tagName)) {
addToGlobalTagsCache(tagName, klass);
return tagName;
}
const tag = createUniqueTag(tagName, registry);
// @ts-ignore
// we extend it just in case the class has been defined manually
defineElement(tag, klass, registry);
return tag;
};

View File

@@ -304,6 +304,37 @@ describe('ScopedElementsMixin', () => {
await waitUntil(() => el.shadowRoot.children[1] instanceof LazyElement);
});
it('should reuse the global tag if defined with the same name and class reference', async () => {
class ItemA extends LitElement {
render() {
return html` <div>Item A</div> `;
}
}
customElements.define('item-a', ItemA);
const tag = defineCE(
class ContainerElement extends ScopedElementsMixin(LitElement) {
static get scopedElements() {
return {
...super.scopedElements,
'item-a': customElements.get('item-a'),
};
}
render() {
return html` <item-a></item-a> `;
}
},
);
const el = await fixture(`<${tag}></${tag}>`);
const firstElement = el.shadowRoot.children[0];
expect(firstElement.tagName.toLowerCase()).to.be.equal('item-a');
expect(firstElement).to.be.instanceof(ItemA);
});
describe('getScopedTagName', () => {
it('should return the scoped tag name for a registered element', async () => {
const chars = `-|\\.|[0-9]|[a-z]`;