From 416d9af41f1481fb78e5737c6b61eb91addfddeb Mon Sep 17 00:00:00 2001 From: jvandersande Date: Wed, 6 Jan 2021 12:20:30 +0100 Subject: [PATCH] Allow creating a CustomElement without defining the tagName --- README.md | 17 +++++++++++++++++ src/index.js | 10 +++++++++- src/index.test.jsx | 28 ++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 660e04b..d2a4c87 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,23 @@ FullName.propTypes = { register(FullName, 'full-name'); ``` +### Creating a CustomElements without registering a tag name + +If authoring a custom-elements library, you might want to expose your CustomElements without registering their tag name, to let your users register their own tags. + +This can be achieved by calling the `toCustomElements` function on the default export: + +```js +import register from 'preact-custom-element' + +function MyComponent({ name = "World" }) { + return Hello {name}! +} + +export const MyElement = register.toCustomElement(MyComponent, ['name']) +``` + +`toCustomElement` has the same signature as `register`, omitting the second parameter (tag name). ## Related diff --git a/src/index.js b/src/index.js index 42318d7..9cb4453 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,6 @@ import { h, cloneElement, render, hydrate } from 'preact'; -export default function register(Component, tagName, propNames, options) { +function toCustomElement(Component, propNames, options) { function PreactElement() { const inst = Reflect.construct(HTMLElement, [], PreactElement); inst._vdomComponent = Component; @@ -49,12 +49,20 @@ export default function register(Component, tagName, propNames, options) { }); }); + return PreactElement; +} + +export default function register(Component, tagName, propNames, options) { + const PreactElement = toCustomElement(Component, propNames, options); + return customElements.define( tagName || Component.tagName || Component.displayName || Component.name, PreactElement ); } +register.toCustomElement = toCustomElement; + function ContextProvider(props) { this.getChildContext = () => props.context; // eslint-disable-next-line no-unused-vars diff --git a/src/index.test.jsx b/src/index.test.jsx index 00da646..5b5154a 100644 --- a/src/index.test.jsx +++ b/src/index.test.jsx @@ -245,4 +245,32 @@ describe('web components', () => { }); assert.equal(getShadowHTML(), '

Active theme: sunny

'); }); + + function ManualRegistration() { + return
I'm manually registered
; + } + + it('allows manual deferred registration by just exposing the element', async () => { + const CustomElement = registerElement.toCustomElement( + ManualRegistration, + [], + { shadow: false } + ); + + const el = document.createElement('x-manually-registered'); + + root.appendChild(el); + assert.equal( + root.innerHTML, + '' + ); + + window.customElements.define('x-manually-registered', CustomElement); + + root.appendChild(el); + assert.equal( + root.innerHTML, + "
I'm manually registered
" + ); + }); });