Skip to content

yacinehmito/ts-transformer-stories-tmp

Repository files navigation

ts-transformer-stories

A TypeScript transformer to remove some of Storybook's boilerplate code.

When you write Storybook stories, it often looks like this:

// components/Button/index.stories.ts
import * as React from 'react';
import { storiesOf } from '@storybook/react';
import { Button } from './.';

storiesOf('components/Button', module)
  .add('Hello world!', () => (
    <Button onClick={() => alert('Hello world!')}>
      Hello world!
    </Button>
));

This is short and mostly declarative but two things could still be automated:

  • Passing module to storiesOf is done to enable Hot Module Replacement on the stories. This cannot be factored out with functions because module is injected by webpack and relies on the file in which it is used, but we're still expected to write this code every time.
  • The name of the kind (i.e. the group of stories, in our case Components/Button) can often be inferred from the location of the file. If you are maintaining a Storybook for your collection of components, chances are that they are all tightly organized and that you want the hierarchy of stories to match your hierarchy of files.

With ts-transformer-stories, you can turn the code above into this:

// components/Button/index.stories.ts
import * as React from 'react';
import stories from 'stories';
import { Button } from './.';

stories.add('Hello world!', () => (
  <Button onClick={() => alert('Hello world!')}>
    Hello world!
  </Button>
));

You don't need to use module anymore, but still have the benefits of Hot Module Replacement. Also, you don't need to give a name to the kind: it is automatically inferred from the location of the file. Here, it will be components/Button.

Setup

Installation

To add ts-transformer-stories to you Storybook, first install it with the following command:

npm install --dev ts-transformer-stories

Run yarn add --dev ts-transformer-stories instead if you are using yarn.

Then, in the webpack.config.js for Storybook, add the customer transformer to awesome-typescript-loader, as follows:

Webpack configuration

// webpack.config.js
const { storiesTransformer } = require('ts-transformer-stories');

module.exports = (baseConfig, env, config) => {
  config.module.rules.push({
    test: /\.(ts|tsx)$/,
    use: [
      {
        loader: 'awesome-typescript-loader',
        options: {
          getCustomTransformers() {
            return { before: [storiesTransformer()] };
          },
        },
      },
    ],
  });
  config.resolve.extensions.push('.ts', '.tsx');
  return config;
};

By default, this will define a module called stories in all the files matching *.stories.tsx?. The default export will be the result of storiesOf(inferredKindName, module), where storiesOf originated from @storybook/react and inferredKindName is a string based on the file path. Both are customizable, as described in the next section.

Type safety

Chances are that the TypeScript integration of your editor of choices does not know about ts-transformer-stories, so it will not be able to resolve the module stories. Instead of configuring it to allow for custom transformers, you can add a declaration file for it anywhere in your project.

For example, at the root of your project, add a file stories.d.ts with the following content:

declare module 'stories' {
  import { Story as Kind } from '@storybook/react';
  const stories: Kind;
  export default stories;
}

If you had previously installed @types/storybook__react, this will allow you to type-check your stories as if the module stories was statically defined.

The same thing can be done for other view libraries, such as Vue. See the folder declarations for more examples.

Configuration

To tweak the behavior of the transformer so that it suits your needs, you can provide an object to storiesTransformer() with the following properties:

property type description default value
storiesModule string The name of the module that exports the kind 'stories'
storybookModule string The module from with to import storiesOf; pick the right one for your view library '@storybook/react'
pathToKind Function A function that, given the path of the file, will return the name for the kind A function that removes the extension and suffix from the file path, then further removes the base name if it is 'index' (this makes it roughly match the node resolution algorithm)
unnamedKind string The name of kinds for which pathToKind() returns an empty string 'Unnamed'
pattern RegExp Identifies the files on which the transformer must be run /\.stories\.tsx?$/
rootDir string The directory relative to which the file paths are set before being fed to pathToKind() The current working directory

License

ts-transformer-stories is licensed under MIT.

Copyright (c) 2019 Yacine Hmito

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published