Skip to main content

Command Palette

Search for a command to run...

Writing Storybook Stories with TSX for Stencil

Updated
2 min read
Writing Storybook Stories with TSX for Stencil
T

I have been around since the 90's (that's 1990) and even before as a geek girl. I have done development, product management, consulting, and system analysis, but my ultimate passion is system architecture and good coding practices. Today I work as an architect responsible for all the Frontend design, tooling, testing, and DevOps processes. I love Javascript and its ecosystem and will happily share this knowledge with fellow developers. Also, I am a Playwright Ambassador.

Yes, you can!

If you want to write your stencil stories for storybook using TSX (the Typescript version of JSX), this is actually possible. A bit of tweaking, and you should go to go. BUT - this is still a very preliminary work, and I expect it to suffer quite few bumps in different scenarios. Nevertheless, I thought it it worth sharing, as I know people were looking for a solution.

This is assuming you are using the html version of Storybook with Stencil. You can find a working version in my stencil-one demo repo. (This is my demo repo for unit testing in Stencil, so feel free to look around).

These are the main versions I worked with. I have no idea if it will be compatible with other versions.

  • Stencil: 2.15

  • Storybook: 6

  • webpack: 5

  • babel-loader: 8

Step 1: Write your story

This is what a story should look like, using CSF3 (Component Story Format). Obviously, it should be in a file named .tsx, so it will go thru the right tooling. Also, change the path that looks for your stories to include .tsx files. (stories option in the main file).

/** @jsx h */
/** @jsxRuntime classic */

import {h} from '@stencil/core';

export default {
  title: 'My Basic',
};

export const Default = {
  render: () => (<my-basic first="Millie" last="Brown"></my-basic>)
};

Note the @jsx directives. They seem to be necessary.

Step 2: Configure Webpack for jsx

We are going to tweak .storybook/main so it will include the babel jsx transformer. In order to do that we will find the rule that handles tsx and add the @babel/plugin-transform-react-jsx as plugin with the pragma: {'h'}.

  webpackFinal: async (config) => {
    const tsxRule = config.module.rules.find(rule => 'a.tsx'.match(rule.test));

    if(tsxRule) {
        const options = tsxRule.use[0].options;
        options.plugins = [
          ['@babel/plugin-transform-react-jsx', {
            prgama: 'h'
          }],
          ...options.plugins
        ];
    }
    return config;
  }

Step 3: Render the stories using Stencil's renderVDom

The last part is changes to the storybook/preview code. Create a function that will be used as a decorator and will render elements to the DOM.

const stencilWrapper = (storyFn, context) => {
  const host = document.createElement('div');
  stencilClient.renderVdom(
    {
      $ancestorComponent$: undefined,
      $flags$: 0,
      $modeName$: undefined,
      $cmpMeta$: {
        $flags$: 0,
        $tagName$: 'div',  
      },
      $hostElement$: host,
    },
    storyFn(context)
  );
  return host.children[0];
}

And then activate it: addDecorator(stencilWrapper);

You will also need to make sure that you are calling the defineCustomElements (which you should, anyway) in the preview code or the preview-manager html file.

As mentioned above, this is still prone to errors. If you have any suggestions, improvements, bugs fixes etc, please comment.