-
Notifications
You must be signed in to change notification settings - Fork 53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow creating a CustomElement without defining the tagName #55
base: master
Are you sure you want to change the base?
Conversation
👍 This would be so useful for our use case. In our app, we are dynamically importing web components and registering them manually so we avoid name collisions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes a lot of sense to me! Couple nits, can we use a named export and tweak the name?
@@ -1,6 +1,6 @@ | |||
import { h, cloneElement, render, hydrate } from 'preact'; | |||
|
|||
export default function register(Component, tagName, propNames, options) { | |||
function toCustomElement(Component, propNames, options) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
function toCustomElement(Component, propNames, options) { | |
export function createCustomElement(Component, propNames, options) { |
} | ||
|
||
export default function register(Component, tagName, propNames, options) { | ||
const PreactElement = toCustomElement(Component, propNames, options); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const PreactElement = toCustomElement(Component, propNames, options); | |
const PreactElement = createCustomElement(Component, propNames, options); |
return customElements.define( | ||
tagName || Component.tagName || Component.displayName || Component.name, | ||
PreactElement | ||
); | ||
} | ||
|
||
register.toCustomElement = toCustomElement; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
register.toCustomElement = toCustomElement; |
return customElements.define( | ||
tagName || Component.tagName || Component.displayName || Component.name, | ||
PreactElement | ||
); | ||
} | ||
|
||
register.toCustomElement = toCustomElement; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
} | ||
|
||
it('allows manual deferred registration by just exposing the element', async () => { | ||
const CustomElement = registerElement.toCustomElement( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const CustomElement = registerElement.toCustomElement( | |
const CustomElement = createCustomElement( |
will need to add an import up top obv
This can be achieved by calling the `toCustomElements` function on the default export: | ||
|
||
```js | ||
import register from 'preact-custom-element' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
import register from 'preact-custom-element' | |
import { toCustomElement } from 'preact-custom-element' |
return <span>Hello {name}!</span> | ||
} | ||
|
||
export const MyElement = register.toCustomElement(MyComponent, ['name']) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
export const MyElement = register.toCustomElement(MyComponent, ['name']) | |
export const MyElement = createCustomElement(MyComponent, ['name']) |
export const MyElement = register.toCustomElement(MyComponent, ['name']) | ||
``` | ||
|
||
`toCustomElement` has the same signature as `register`, omitting the second parameter (tag name). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
`toCustomElement` has the same signature as `register`, omitting the second parameter (tag name). | |
`createCustomElement` has the same signature as `register`, omitting the second parameter (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: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be achieved by calling the `toCustomElements` function on the default export: | |
This can be achieved by calling the named export `createCustomElement`: |
What does this PR do
The goal of this PR is to separate the Preact-to-CustomElement process from the tag-name registration process.
Why is this PR needed
Registering a CustomElement using
window.customElements.define
uses a shared, global registry for custom-elements. Thus, only one CustomElement can be associated with a given tag-name. This can cause problems when authoring a component library: we "reserve" keywords on behalf of our end users. It also causes isolation issue for patterns such a micro-frontend architecture, where two versions of our library can't be used together, as they would clash while registering components.Implementation details
This PR moves all the creation of
PreactElement
into its own function,toCustomElement
. This function takes the same parameters as the defaultregister
export, omitting the second parameter (tag name).The default exports is unchanged, and still registers the element right away.
Usage is as follow:
Misc
Right now the
toCustomElement
function is attached as a property ofregister
, as to not introduce a named export next to the default one (microbundle was not happy with that). However it's not the most elegant thing. We could maybe introduce a new option to theregister
function, calleddefine
:boolean, defaults to "true": whether to define the element after creating it. If false, the customElement will be returned instead of being defined