1

I want to use web component Button (lit) in my project with similar tag. For example: if tag <button-test> exists on website then create tag <button-test-12345>. That's why I use ScopedElementsMixin.

Is it possible to call the button-test tag so that the Element converts it to a tag with the number is the button-test exists on the page?

Lit element

import { Button } from '../button/Button';
export class MyElement extends ScopedElementsMixin(LitElement) {
   constructor() {
       super();
       this.defineScopedElement('button-test', Button);
   }

   render() {
       return html`
           <slot></slot>
       `;
   }
}

React call

if (!customElements.get("my-element")) {
    customElements.define("my-element", MyElement);
}
let result: JSX.Element = <my-element">
   <button-test
      disabled={false}
      text={this.getTextElement()}
   </button-test>
</my-element">;
funfelfonfafis
  • 187
  • 2
  • 15

1 Answers1

0

Two options come to mind when making an assumption that you want to provide a nice React component name for button-test while using an obfuscated or scoped custom element tag name behind the scenes.

Wrap the component for React

One option is to wrap the Button custom element with a React wrapper, which allows renaming it for usage within React independent of the underlying custom element tag name.

This would allow using a very obfuscated button-test custom element name which is unlikely to collide with another registered name while also providing a nice React component name. For example:

// This simulates a jsx file using the `ButtonTest` React component.

const root = ReactDOM.createRoot(document.querySelector('#app'));
root.render(<div>
    <p>Custom element with renamed React component name <code>ButtonTest</code>:</p>
    <ButtonTest></ButtonTest>
  </div>);
  <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script type="module">
import {LitElement, html} from 'https://cdn.jsdelivr.net/gh/lit/dist@2/core/lit-core.min.js';
import {createComponent} from 'https://cdn.jsdelivr.net/npm/@lit-labs/react@1.1.0/index.min.js';

export class Button extends LitElement {
  render() {
    return html`<button>Test Button</button>`;
  }
}
// Register the button with a hard to collide name.
const obfuscatedName = "button-test-12345";
customElements.define(obfuscatedName, Button);

// Use @lit-labs/react to create a React component with a nice name.
const ButtonTest = createComponent({
  react: React,
  tagName: obfuscatedName,
  elementClass: Button,
});

// Assigning the react wrapped component to Window to use in the jsx file.
// This is only for StackOverflow.
window.ButtonTest = ButtonTest
</script>

<div id="app"></div>

Instantiate the custom element from the class

Another option is changing how the button is constructed. Once a custom element has been defined, it is possible to instantiate it from the class, e.g. new Button(). This could be used to abstract away the custom element tag name and could maybe be used for creating an abstraction for React usage. E.g.

class Button extends HTMLElement {
  constructor() {
    super()
    this.attachShadow({ mode: "open" })
    this.shadowRoot.innerHTML = "<button>button test</button>";
  }
}
customElements.define('button-test-12345', Button)

// Attach button without using obfuscated tag name.
// This will throw if the class has not been defined
// as was done on line 8.
document.querySelector('#app').append(new Button())
<div id="app"></div>

The scoped button-test element could be created from my-element with a React ref with something like: const buttonEl = MyElementRef.shadowRoot.createElement('button-test'), and then that could be used.

YouCodeThings
  • 590
  • 3
  • 13