Skip to content
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

[web components]: experiment with scoped custom element registry #17718

Open
kennylam opened this issue Jul 11, 2023 · 3 comments
Open

[web components]: experiment with scoped custom element registry #17718

kennylam opened this issue Jul 11, 2023 · 3 comments

Comments

@kennylam
Copy link
Member

As of Carbon for IBM.com v2, components in @carbon/ibmdotcom-web-components will get a new prefix (TBD). Anticipating registry clashes of different component versions (v2 -> v3) with the same name, a test should be done with this polyfill. More information about this proposal here proposal.

@abdonrd
Copy link
Contributor

abdonrd commented Jul 11, 2023

You may also be interested in seeing this: @lit-labs/scoped-registry-mixin

@m4olivei
Copy link
Contributor

m4olivei commented Oct 31, 2023

I've been researching this issue. I want to offer my notes on the topic for consideration and also offer myself and @andy-blum to be available for a call to discuss.

I don't have a concrete path forward. My hope here is to offer info on what scoped custom element registries are (and what they are not) to help advance the dicussion.

What are Scoped Custom Element Registries

Scoped Element Registries are an enhancement to the current CustomElementRegistry API. As defined by MDN:

The CustomElementRegistry interface provides methods for registering custom elements and querying registered elements. To get an instance of it, use the [window.customElements](https://developer.mozilla.org/en-US/docs/Web/API/Window/customElements) property.

Given we’re using Lit, we typically interact with the CustomElementRegistry via the @customElement decorator. Recently we overrode that decorator such that we use a customized @carbonElement decorator. @carbonElement has support to mitigate custom element registry errors. We try to register a tag name, constructor pair, if it already exists, we catch the error and issue a console.warn. The first registration of a given tag name wins. Most of the time that fits with what we want. Duplicate registration issues we’ve run into have been related to bundling, with the duplicate registration being for the same version of the same component.

The key thing to know is that browsers presently support only a single CustomElementRegistry . Thats a problem when there is contention for a tag name from multiple libraries, multiple versions of a library, or bundling issues as noted above. The infamous namespace collision error:

Uncaught DOMException: Failed to execute 'define' on 'CustomElementRegistry': the name "dds-button-expressive" has already been used with this registry

Scoped Custom Element Registries is a proposed attempt to help with this by allowing multiple Custom Element Registries to be created. The steps would include:

  • Create a new CustomElementRegistry()
  • Associate the registry with a shadow root when calling attachShadow
  • Define any custom elements used in the shadow root in the CustomElementRegistry instance
  • Any custom elements that are used in the shadow root will first look up their definition in the CustomElementRegistry on the shadow root. If not found will fallback to the global CustomElementRegistry (window.customElements).

With the ability to have custom elements define the custom elements they use in a new custom element registry, you can avoid polluting the global namespace unnecessarily, and thus avoid some types of namespace collisions.

A common mis-conception (I had this coming into this issue), is that scoped custom element registries would somehow allow you to have multiple instances of CustomElementRegistry at the global level. You could register all your v3 CWC in a new CustomElementRegistry, avoiding the global CustomElementRegistry (window.customElements), avoiding naming collisions. This is not the case however. Any new CustomElementRegistry must be associated with a shadow root (via the registry option in the call to attachShadow).

When you think about it, this makes sense. Consider wanting two different versions of <bx-button> at the document level, in the light DOM. The first is registered in registryA , the second in registryB both registries global. Each registry has an entry for bx-button. Given a call to document.createElement('bx-button'), which definition should the browser use? The browser has no way to know. This is why new CustomElementRegistry objects are associated with a shadow root. Elements created in the shadow DOM first look to any registry associated with the shadow DOM for the definition, if not found, fallback to looking at the global registry. Scoped Custom Element registries do not generally solve for using two different versions of a custom element in the same page. You can still have naming collisions at the top level document, in the light DOM, if you’re running more than one version of a package and don’t have some other way to mitigate.

For any custom element using custom elements in it’s shadow DOM, you can use a scoped custom element registry in the parent, and avoid polluting the global custom element registry with the sub-custom-elements. In this way, higher level components, eg. <dds-search-with-typeahead> and <footer-composite>, could register sub-components, eg. <bx-select-item>, in a scoped custom element registry. With that in place, they could theoretically be using different versions of CWC, thus different versions of <bx-select-item>. Further, the global scope would be free to register some other version of <bx-select-item> on the global window.customElements object.

Additional caveats / limitations

Let's say we decide to make use of scoped custom element registries. There are some additional caveats / limitations fo consider. Much of this was adapted from @open-wc/scoped-elements documentation. That package is comparable to @lit-labs/scoped-registry-mixin and uses the polyfill we've talked about.

  • Custom elements should not be self registering
    • This is a side-effect, pollutes the global namespace and defeats a consumer from registering a component in a scoped custom element registry instead.
    • All of our custom elements in CWC and C4IBM are self registering. This would need to change, somehow offering a side-effect free export (no self registration) of each component class.
  • Every custom element that uses custom elements in it’s shadow root, should have those child custom elements defined in a custom element registry. The @lit-labs/scoped-registry-mixin package can help with this, it offers:
    1. Automatically creates a scoped CustomElementRegistry for the class
    2. Provides sugar for defining custom elements in the scoped registry
    3. Passes the registry to attachShadow() to enable scoping
    4. Passes the shadow root to lit-html to create DOM within the scope
  • At the moment, you need a polyfill for this to work, mentioned in the issue description.

Current State of WICG Proposal

Next steps

Apologies for the brain dump.

For me the thread that we might continue exploring is component self-registration. Self-registration is a side-effect, one that is leading to registry name collisions. Other library authors in the proposal thread (ref1, ref2) alluded to shifting the responsibilty of registration to the consumer. The consumer has ultimate authority over the global namespace, which still has to be managed and can't be completely avoided by using scoped custom element registries.

@kennylam kennylam transferred this issue from carbon-design-system/carbon-for-ibm-dotcom Oct 10, 2024
@kennylam kennylam moved this to 🕵️‍♀️ Triage in Design System Oct 10, 2024
@kennylam kennylam self-assigned this Oct 10, 2024
@tay1orjones tay1orjones added package: @carbon/web-components @carbon/web-components type: enhancement 💡 proposal: open This request has gone through triaging. We're determining whether we take this on or not. labels Oct 23, 2024
Copy link
Contributor

Thank you for submitting a feature request. Your proposal is open and will soon be triaged by the Carbon team.

If your proposal is accepted and the Carbon team has bandwidth they will take on the issue, or else request you or other volunteers from the community to work on this issue.

@github-project-automation github-project-automation bot moved this to Triage in Roadmap Oct 23, 2024
@sstrubberg sstrubberg added role: dev 🤖 and removed proposal: open This request has gone through triaging. We're determining whether we take this on or not. labels Nov 12, 2024
@sstrubberg sstrubberg moved this from Triage to Later 🧊 in Roadmap Nov 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Later 🧊
Development

No branches or pull requests

5 participants