Search code examples
typescriptvue.jsweb-componentvitestenciljs

Stencil web components wont react to custom attributes they're given in Vite/Vue3


Short background

I have a vue-cli environment with Vue 2 using options-api which i've started to convert to create-vue, a Vite based environment with Vue 3 and Typescript. I've used Stencil to make a npm-package to import it's web components in my project. It has worked pretty smooth with my earlier tech stack but now I can't seem to wrap my head around what's wrong.

My current state

When I started importing the npm-package in my new environment I got a bunch of errors which I resolved. I had to upgrade Stencil from v2.7.0 to v2.16.1 and add experimentalImportInjection to make sure Stencil compiled the components properly for Vite.

// stencil.config.ts

...
extras: {
  experimentalImportInjection: true
}

The main issue I have right now is that the components load and they look right but I'm having issues with sending props to them.

In stencil I'm declaring the props:

  @Prop() state: 'none' | 'focus' | 'active' = 'none';

  @Prop() label: string;

  @Prop() meta: string;

  @Prop() disabled = false;

  @Prop() valid?: true | false | null = null;

And in my Vue code I'm declaring the custom component like this:

<fds-input id="loginEmail" label="Email" meta="meta text" :valid="false" :disabled="isDisabled">
  <input type="email" v-model="email" @blur="checkEmail" data-cy="input-text-email"/>
</fds-input>

My main goal here is to change the value of valid to trigger my web component either valid or invalid state which has different css styles.

What I have discovered so far

  1. Changing the value of :valid does not change a thing, I've tried sending value as a string and as a bool. Same goes with disabled.
  2. Changing the meta or label works fine, either as a binded value (:meta="") or regular attribute (meta="").
  3. When I inspect the element in my browser (Chrome) I can only see id and class on the element.
  4. When I add a setTimeout and add vanilla javascript within to set the attribute valid="false" it triggers the state of the component I want.
  5. Upgrading Stencil to the latest version of v2 does not change anything.

Solution

  • This is now solved!

    I'm still unsure what the actual issue comes from but the main difference is that Vite/Rollup handles the generated custom elements from Stencil different than vue-cli/Webpack.

    My custom elements with vue-cli/Webpack shows up in the DOM tree like this:

    <fds-input id="loginEmail" label="Email" valid="false">
    ...
    </fds-input>
    

    And with Vite/Rollup it shows up like this:

    <fds-input id="loginEmail">
    ...
    </fds-input>
    
    

    Adding prop option reflect: true for the props I want to change in Stencil makes the attributes show up in the DOM and is then available for the CSS to target with styling.

    I did also change the prop to expect strings instead of bools. This makes it backward compatible and typescript seems happier. Having optional bools with the default as null was not an optimal approach.

    @Prop({ reflect: true }) valid?: 'true' | 'false';
    

    NOTE: All data sent to the custom element is received properly but it's only the CSS not working since it does not have anything to target.