DEV Community

Tally Barak
Tally Barak

Posted on

Writing Storybook Stories with TSX for Stencil

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>)
};
Enter fullscreen mode Exit fullscreen mode

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;
  }
Enter fullscreen mode Exit fullscreen mode

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];
}
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (5)

Collapse
 
bemagical profile image
Brian Enriquez • Edited

I've been tinkering with a stencil-storybook combo for the past couple days. This article is the only one that pushed me all the way through errors and wtfs. THANK YOU.

For my build, Step 3 only resulted in the spinning wheel of death (in storybook), so I omitted. It was the webpack piece that was throwing off my build.

Collapse
 
tallyb profile image
Tally Barak

Glad to hear

Collapse
 
sebastiandg7 profile image
Sebastián Duque G

Gratitude to you!

Collapse
 
timsnadden profile image
Tim Snadden

Thanks Tally - I'll look more closely at this.

FYI there's a typo in one of the code blocks prgama should be pragma. Cheers

Collapse
 
andrewbaisden profile image
Andrew Baisden

Never tried Stencil but love using Storybook.