storybook Documentation

Storybook is the industry standard workshop for building, documenting, and testing UI components in isolation

RAW Doc

File: README.md

Table of contents

Getting Started

Visit Storybook's website to learn more about Storybook and to get started.

Documentation

Documentation can be found on Storybook's docs site.

Examples

View Component Encyclopedia to see how leading teams use Storybook.

Use storybook.new to quickly create an example project in Stackblitz.

Storybook comes with a lot of addons for component design, documentation, testing, interactivity, and so on. Storybook's API makes it possible to configure and extend in various ways. It has even been extended to support React Native, Android, iOS, and Flutter development for mobile.

Community

For additional help, share your issue in the repo's GitHub Discussions.

Projects

Supported Frameworks

Renderer Demo
React
Angular
Vue 3
Web components
React Native
HTML
Ember
Svelte
Preact
Qwik
SolidJS
Android, iOS, Flutter

Addons

Addons
a11y Test components for user accessibility in Storybook
actions Log actions as users interact with components in the Storybook UI
backgrounds Let users choose backgrounds in the Storybook UI
cssresources Dynamically add/remove CSS resources to the component iframe
design assets View images, videos, and weblinks alongside your story
docs Add high quality documentation to your components
events Interactively fire events to components that respond to EventEmitter
google-analytics Reports google analytics on stories
graphql Query a GraphQL server within Storybook stories
jest View the results of components' unit tests in Storybook
links Create links between stories
measure Visually inspect the layout and box model within the Storybook UI
outline Visually debug the CSS layout and alignment within the Storybook UI
query params Mock query params
viewport Change display sizes and layouts for responsive components using Storybook

See Addon / Framework Support Table

To continue improving your experience, we have to eventually deprecate or remove certain addons in favor of new and better tools.

If you're using info/notes, we highly recommend you migrate to docs instead, and here is a guide to help you.

If you're using contexts, we highly recommend you migrate to toolbars and here is a guide to help you.

If you're using addon-storyshots, we highly recommend you migrate to the Storybook test-runner and here is a guide to help you.

Badges & Presentation materials

We have a badge! Link it to your live Storybook example.

md
[](link to site)

If you're looking for material to use in your Storybook presentation, such as logos, video material, and the colors we use, you can find it all on our brand repo.

Community

Contributing

Contributions to Storybook are always welcome!

  • ๐Ÿ“ฅ Pull requests and ๐ŸŒŸ Stars are always welcome.
  • Read our contributing guide to get started,
    or find us on Discord, we will take the time to guide you.

Looking for a first issue to tackle?

  • We tag issues with when we think they are well suited for people who are new to the codebase or OSS in general.
  • Talk to us, we'll find something that suits your skills and learning interests.

Development scripts

Storybook is organized as a monorepo. Useful scripts include:

`yarn start`

Runs a sandbox template storybook with test stories

`yarn task`

As above, but gives you options to customize the sandbox (e.g. selecting other frameworks)

`yarn lint`

boolean check if code conforms to linting rules - uses remark & eslint

  • yarn lint:js - will check js
  • yarn lint:md - will check markdown + code samples
  • yarn lint:js --fix - will automatically fix js

`yarn test`

boolean check if unit tests all pass - uses jest

  • yarn run test --core --watch - will run core tests in watch-mode

Sponsors

Become a sponsor to have your logo and website URL on our README on Github. [Become a sponsor]


























Backers

By making a recurring donation, you can support us and our work. [Become a backer]

License

MIT

-the end-


File: addons/viewport/README.md

Storybook Viewport Addon

Storybook Viewport Addon allows your stories to be displayed in different sizes and layouts in Storybook. This helps build responsive components inside of Storybook.

Framework Support

Installation

Viewport is part of essentials and so is installed in all new Storybooks by default. If you need to add it to your Storybook, you can run:

sh
npm i -D @storybook/addon-viewport

Then, add following content to .storybook/main.js:

js
module.exports = {
  addons: ['@storybook/addon-viewport'],
};

You should now be able to see the viewport addon icon in the the toolbar at the top of the screen.

Usage

The usage is documented in the documentation.


File: addons/toolbars/README.md

Storybook Toolbars Addon

The Toolbars addon controls global story rendering options from Storybook's toolbar UI. It's a general purpose addon that can be used to:

  • set a theme for your components
  • set your components' internationalization (i18n) locale
  • configure just about anything in Storybook that makes use of a global variable

Framework Support

Installation

Toolbars is part of essentials and so is installed in all new Storybooks by default. If you need to add it to your Storybook, you can run:

sh
npm i -D @storybook/addon-toolbars

Then, add following content to .storybook/main.js:

js
module.exports = {
  addons: ['@storybook/addon-toolbars'],
};

Usage

The usage is documented in the documentation.

FAQs

How does this compare to `addon-contexts`?

Addon-toolbars is the successor to addon-contexts, which provided convenient global toolbars in Storybook's toolbar.

The primary difference between the two packages is that addon-toolbars makes use of Storybook's new Story Args feature, which has the following advantages:

  • Standardization. Args are built into Storybook in 6.x. Since addon-toolbars is based on args, you don't need to learn any addon-specific APIs to use it.

  • Ergonomics. Global args are easy to consume in stories, in Storybook Docs, or even in other addons.

  • Framework compatibility. Args are completely framework-independent, so addon-toolbars is compatible with React, Vue, Angular, etc. out of the box with no framework logic needed in the addon.

File: addons/storysource/README.md

Storybook Storysource Addon

This addon is used to show stories source in the addon panel.

Framework Support

Getting Started

First, install the addon

sh
yarn add @storybook/addon-storysource --dev

You can add configuration for this addon by using a preset or by using the addon config with webpack

Install using preset

Add the following to your .storybook/main.js exports:

js
module.exports = {
  addons: ['@storybook/addon-storysource'],
};

You can pass configurations into the addon-storysource loader in your .storybook/main.js file, e.g.:

js
module.exports = {
  addons: [
    {
      name: '@storybook/addon-storysource',
      options: {
        rule: {
          // test: [/\.stories\.jsx?$/], This is default
          include: [path.resolve(__dirname, '../src')], // You can specify directories
        },
        loaderOptions: {
          prettierConfig: { printWidth: 80, singleQuote: false },
        },
      },
    },
  ],
};

To customize the source-loader, pass loaderOptions. Valid configurations are documented in the source-loader README.

Theming

Storysource will automatically use the light or dark syntax theme based on your storybook theme. See Theming Storybook for more information.

Displaying full source

Storybook 6.0 introduced an unintentional change to source-loader, in which only the source of the selected story is shown in the addon. To restore the old behavior, pass theinjectStoryParameters: false option.

If you're using addon-docs:

js
module.exports = {
  addons: [
    {
      name: '@storybook/addon-docs',
      options: {
        sourceLoaderOptions: {
          injectStoryParameters: false,
        },
      },
    },
  ],
};

If not:

js
module.exports = {
  addons: [
    {
      name: '@storybook/addon-storysource',
      options: {
        loaderOptions: {
          injectStoryParameters: false,
        },
      },
    },
  ],
};

This bug will be resolved in a future version of the addon.


File: addons/storyshots/storyshots-core/README.md

StoryShots

StoryShots adds automatic Jest Snapshot Testing for Storybook.

Framework Support

To use StoryShots, you must use your existing Storybook stories as the input for Jest Snapshot Testing.

Getting Started

Add the following module into your app.

sh
yarn add @storybook/addon-storyshots --dev

Configure Storyshots for HTML snapshots

Create a new test file with the name Storyshots.test.js. (Or whatever the name you prefer, as long as it matches Jest's config testMatch).
Then add following content to it:

js
import initStoryshots from '@storybook/addon-storyshots';

initStoryshots();

That's all.

Now run your Jest test command. (Usually, npm test.) Then you can see all of your stories are converted as Jest snapshot tests.

Testing stories that rely on addon-added decorators

If you have stories in your Storybook that can only render inside a decorator (for instance the apollo-storybook-decorator), you'll need to ensure those decorators are applied in Storyshots.

If you export those decorators from your .storybook/preview.js then Storyshots will apply those decorators for you in the same way that Storybook does. However if the addon automatically adds the decorator for you (which is a new feature in Storybook 6.0), you will find the decorator does not get added in Storyshots. This is a limitation in Storyshots currently.

To ensure such decorators get added, export them from .storybook/preview.js:

js
import addonDecorator from 'some-addon';

export const decorators = [addonDecorator];

Configure your app for Jest

In many cases, for example Create React App, it's already configured for Jest. You need to create a filename with the extension .test.js.

If you still need to configure jest you can use the resources mentioned below:

Note: If you use React 16, you'll need to follow these additional instructions.

Note: Make sure you have added the json extension to moduleFileExtensions in jest.config.json. If this is missing it leads to the following error: Cannot find module 'spdx-license-ids' from 'scan.js'.

Note: Please make sure you are using jsdom as the testEnvironment on your jest config file.

Configure Jest to work with Webpack's [require.context()](https://webpack.js.org/guides/dependency-management/#require-context)

NOTE: if you are using Storybook 5.3's main.js to list story files, this is no longer needed.

Sometimes it's useful to configure Storybook with Webpack's require.context feature. You could be loading stories one of two ways.

  1. If you're using the storiesOf API, you can integrate it this way:
js
import { configure } from '@storybook/react';

const req = require.context('../stories', true, /\.stories\.js$/); // <- import all the stories at once

function loadStories() {
  req.keys().forEach((filename) => req(filename));
}

configure(loadStories, module);
  1. If you're using Component Story Format (CSF), you'll integrate it like so:
js
import { configure } from '@storybook/react';

const req = require.context('../stories', true, /\.stories\.js$/); // <- import all the stories at once

configure(req, module);

The problem here is that it will work only during the build with webpack,
other tools may lack this feature. Since Storyshot is running under Jest,
we need to polyfill this functionality to work with Jest. The easiest
way is to integrate it to babel.

You can do this with a Babel plugin or macro. If you're using create-react-app (v2 or above), use the macro.

Option 1: Plugin

First, install it:

sh
yarn add babel-plugin-require-context-hook --dev

Next, it needs to be registered and loaded before each test. To register it, create a file with the following register function .jest/register-context.js:

js
import registerRequireContextHook from 'babel-plugin-require-context-hook/register';
registerRequireContextHook();

That file needs to be added as a setup file for Jest. To do that, add (or create) a property in Jest's config called setupFiles. Add the file name and path to this array.

json
setupFiles: ['<rootDir>/.jest/register-context.js']

Finally, add the plugin to .babelrc:

json
{
  "presets": ["..."],
  "plugins": ["..."],
  "env": {
    "test": {
      "plugins": ["require-context-hook"]
    }
  }
}

The plugin is only added to the test environment otherwise it could replace webpack's version of it.

Option 2: Macro

First, install it:

sh
yarn add require-context.macro --dev

Now, inside of your Storybook config file, import the macro and run it in place of require.context, like so:

javascript
import requireContext from 'require-context.macro';

// const req = require.context('../stories', true, /\.stories\.js$/); <-- replaced
const req = requireContext('../stories', true, /\.stories\.js$/);

Configure Jest for React

StoryShots addon for React is dependent on react-test-renderer, but
doesn't install it, so you need to install it separately.

sh
yarn add react-test-renderer --dev

Configure Jest for Angular

StoryShots addon for Angular is dependent on jest-preset-angular, but
doesn't install it, so you need to install it separately.

sh
yarn add jest-preset-angular

If you already use Jest for testing your angular app - probably you already have the needed jest configuration.
Anyway you can add these lines to your jest config:

js
module.exports = {
  globals: {
    __TRANSFORM_HTML__: true,
  },
  transform: {
    '^.+\\.jsx?$': 'babel-jest',
    '^.+\\.(ts|html)$': '<rootDir>/node_modules/jest-preset-angular/preprocessor.js',
  },
  moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node', '.html'],
};

Configure Jest for Vue

StoryShots addon for Vue is dependent on jest-vue-preprocessor, but
doesn't install it, so you need to install it separately.

sh
yarn add jest-vue-preprocessor

If you already use Jest for testing your vue app - probably you already have the needed jest configuration.
Anyway you can add these lines to your jest config:

js
module.exports = {
  transform: {
    '^.+\\.jsx?$': 'babel-jest',
    '.*\\.(vue)$': '<rootDir>/node_modules/jest-vue-preprocessor',
  },
  transformIgnorePatterns: ['/node_modules/(?!(@storybook/.*\\.vue$))'],
  moduleFileExtensions: ['vue', 'js', 'jsx', 'json', 'node'],
};

Configure Jest for Vue 3

StoryShots addon for Vue is dependent on vue-jest v5, but
doesn't install it, so you need to install it separately.

sh
yarn add [email protected]

If you already use Jest for testing your vue app - probably you already have the needed jest configuration.
Anyway you can add these lines to your jest config:

js
module.exports = {
  transform: {
    '^.+\\.jsx?$': 'babel-jest',
    '.*\\.(vue)$': '<rootDir>/node_modules/vue-jest',
  },
  transformIgnorePatterns: ['/node_modules/(?!(@storybook/.*\\.vue$))'],
  moduleFileExtensions: ['vue', 'js', 'jsx', 'json', 'node'],
};

Configure Jest for Preact

StoryShots addon for Preact is dependent on preact-render-to-json, but
doesn't install it, so you need to install it separately.

sh
yarn add preact-render-to-json --dev

Configure Jest for Web Components

StoryShots addon for Web Components requires jsdom 16 or later to fully support the
web component shadow dom. To use jsdom 16 or later you can set the Jest testEnvironment configuration key to
jest-environment-jsdom-sixteen. This should work back to Jest 24 and is the default in Jest 26 and later.

Configure Jest for MDX Docs Add-On Stories

If using the Docs add-on with
MDX stories you will need
to configure Jest to transform MDX stories into something Storyshots can understand:

Add the following to your Jest configuration:

json
{
  "transform": {
    "^.+\\.[tj]sx?$": "babel-jest",
    "^.+\\.mdx?$": "@storybook/addon-docs/jest-transform-mdx"
  }
}

Why don't we install dependencies of each framework ?

Storyshots addon is currently supporting React, Angular and Vue. Each framework needs its own packages to be integrated with Jest. We don't want people that use only React will need to bring other dependencies that do not make sense for them.

dependencies - will installed an exact version of the particular dep - Storyshots can work with different versions of the same framework (let's say React v16 and React v15), that have to be compatible with a version of its plugin (react-test-renderer).

optionalDependencies - behaves like a regular dependency, but do not fail the installation in case there is a problem to bring the dep.

peerDependencies - listing all the deps in peer will trigger warnings during the installation - we don't want users to install unneeded deps by hand.

optionalPeerDependencies - unfortunately there is nothing like this =(

For more information read npm docs

Using `createNodeMock` to mock refs

react-test-renderer doesn't provide refs for rendered components. By
default, it returns null when the refs are referenced. In order to mock
out elements that rely on refs, you will have to use the
createNodeMock option added to React starting with version 15.4.0.

Here is an example of how to specify the createNodeMock option in Storyshots:

js
import initStoryshots, { snapshotWithOptions } from '@storybook/addon-storyshots';
import TextareaThatUsesRefs from '../component/TextareaThatUsesRefs';

initStoryshots({
  test: snapshotWithOptions({
    createNodeMock: (element) => {
      if (element.type === TextareaThatUsesRefs) {
        return document.createElement('textarea');
      }
    },
  }),
});

Provide a function to have story-specific options:

js
initStoryshots({
  test: snapshotWithOptions((story) => ({
    createNodeMock: (element) => {
      if (story.name == 'foobar') {
        return null;
      }
      return element;
    },
  })),
});

Using a custom renderer

By design, react-test-renderer doesn't use a browser environment or JSDOM. Because of this difference, some stories might render in your browser, but not in Storyshots. If you encounter this problem, you may want to switch for an higher level renderer such as mount from Enzyme or render from React Testing Library.

Example with React Testing Library

js
import initStoryshots from "@storybook/addon-storyshots";
import { render } from "@testing-library/react";

const reactTestingLibrarySerializer = {
  print: (val, serialize, indent) => serialize(val.container.firstChild),
  test: val => val && val.hasOwnProperty("container")
};

initStoryshots({
  renderer: render,
  snapshotSerializers: [reactTestingLibrarySerializer]
});

Example with Enzyme

js
import initStoryshots from '@storybook/addon-storyshots';
import { mount } from 'enzyme';

initStoryshots({
  renderer: mount,
});

If you are using enzyme, you need to make sure jest knows how to serialize rendered components.
For that, you can pass an enzyme-compatible snapshotSerializer (like enzyme-to-json, jest-serializer-enzyme etc.) with the snapshotSerializer option (see below).

StoryShots for async rendered components

You can make use of Jest done callback to test components that render asynchronously. This callback is passed as param to test method passed to initStoryshots(...) when the asyncJest option is given as true.

Example

The following example shows how we can use the done callback to take StoryShots of a Relay component. Each kind of story is written into its own snapshot file with the use of getSnapshotFileName.

Add stories of UserForm in the file: UserForm.story.jsx

text
/* Detailed source-code truncated for AI context efficiency. */

Then, init Storyshots for async component in the file: StoryShots.test.js

jsx
import initStoryshots, { Stories2SnapsConverter } from '@storybook/addon-storyshots';
import { mount } from 'enzyme';
import toJson from 'enzyme-to-json';

// Runner
initStoryshots({
  asyncJest: true, // this is the option that activates the async behaviour
  test: ({
    story,
    context,
    done, // --> callback passed to test method when asyncJest option is true
  }) => {
    const converter = new Stories2SnapsConverter();
    const snapshotFilename = converter.getSnapshotFileName(context);
    const storyElement = story.render();

    // mount the story
    const tree = mount(storyElement);

    // wait until the mount is updated, in our app mostly by Relay
    // but maybe something else updating the state of the component
    // somewhere
    const waitTime = 1;
    setTimeout(() => {
      if (snapshotFilename) {
        expect(toJson(tree.update())).toMatchSpecificSnapshot(snapshotFilename);
      }

      done();
    }, waitTime);
  },
  // other options here
});

NOTICE that When using the asyncJest: true option, you also must specify a test method that calls the done() callback.

This is a really powerful technique to write stories of Relay components because it integrates data fetching with component rendering. So instead of passing data props manually, we can let Relay do the job for us as it does in our application.

Whenever you change your data requirements by adding (and rendering) or (accidentally) deleting fields in your graphql query fragments, you'll get a different snapshot and thus an error in the StoryShot test.

Using a custom directory

Depending on your project's needs, you can configure the @storybook/addon-storyshots to use a custom directory for the snapshots. You can read more about it in the official docs.

Options

`config`

The config parameter must be a function that helps to configure storybook like the preview.js does.
If it's not specified, storyshots will try to use configPath parameter.

js
import initStoryshots from '@storybook/addon-storyshots';

initStoryshots({
  config: ({ configure }) =>
    configure(() => {
      require('../stories/Button.story.js');
    }, module),
});

`configPath`

By default, Storyshots assumes the config directory path for your project as below:

  • Storybook for React: .storybook
  • Storybook for React Native: storybook

If you are using a different config directory path, you could change it like this:

js
import initStoryshots from '@storybook/addon-storyshots';

initStoryshots({
  configPath: '.my-storybook-config-dir',
});

Or, as a more complex example, if we have a package in our lerna project called app with the path ./packages/app/src/__tests__/storyshots.js and the storybook config directory ./packages/app/.storybook:

js
import path from 'path';
import initStoryshots from '@storybook/addon-storyshots';

initStoryshots({ configPath: path.resolve(__dirname, '../../.storybook') });

configPath can also specify path to the preview.js itself. In this case, config directory will be
a base directory of the configPath. It may be useful when the preview.js for test should differ from the
original one. It also may be useful for separating tests to different test configs:

js
initStoryshots({
  configPath: '.my-storybook-config-dir/testConfig1.js',
});

initStoryshots({
  configPath: '.my-storybook-config-dir/testConfig2.js',
});

`suite`

By default, Storyshots groups stories inside a Jest test suite called "Storyshots". You could change it like this:

js
import initStoryshots from '@storybook/addon-storyshots';

initStoryshots({
  suite: 'MyStoryshots',
});

`storyKindRegex`

If you'd like to only run a subset of the stories for your snapshot tests based on the story's kind:

js
import initStoryshots from '@storybook/addon-storyshots';

initStoryshots({
  storyKindRegex: /^MyComponent$/,
});

This can be useful if you want to separate the snapshots in directories next to each component. See an example here.

If you want to run all stories except stories of a specific kind, you can write an inverse regex which is true for all kinds except those with a specific word such as DontTest

js
import initStoryshots from '@storybook/addon-storyshots';

initStoryshots({
  storyKindRegex: /^((?!.*?DontTest).)*$/,
});

This can be useful while testing react components which make use of the findDomNode API since they always fail with snapshot testing
while using react-test-renderer see here

`storyNameRegex`

If you'd like to only run a subset of the stories for your snapshot tests based on the story's name:

js
import initStoryshots from '@storybook/addon-storyshots';

initStoryshots({
  storyNameRegex: /buttons/,
});

`framework`

If you are running tests from outside of your app's directory, storyshots' detection of which framework you are using may fail. Pass "react" or "react-native" to short-circuit this.

For example:

js
// storybook.test.js

import path from 'path';
import initStoryshots from '@storybook/addon-storyshots';

initStoryshots({
  framework: 'react', // Manually specify the project's framework
  configPath: path.join(__dirname, '.storybook'),
  integrityOptions: { cwd: path.join(__dirname, 'src', 'stories') },
  // Other configurations
});

Use this table as a reference for manually specifying the framework.

angular html preact
react riot react-native
svelte vue vue3
web-components rax

`test`

Run a custom test function for each story, rather than the default (a vanilla snapshot test).
Setting test will take precedence over the renderer option.
You can still overwrite what renderer is used for the test function:

js
import initStoryshots, { renderWithOptions } from '@storybook/addon-storyshots';
import { mount } from 'enzyme';

initStoryshots({
  test: renderWithOptions({
    renderer: mount,
  }),
});

`renderer`

Pass a custom renderer (such as enzymes mount) to record snapshots.
This may be necessary if you want to use React features that are not supported by the default test renderer,
such as ref or Portals.
Note that setting test overrides renderer.

`snapshotSerializers`

Pass an array of snapshotSerializers to the jest runtime that serializes your story (such as enzyme-to-json).

js
import initStoryshots from '@storybook/addon-storyshots';
import { createSerializer } from 'enzyme-to-json';

initStoryshots({
  renderer: mount,
  snapshotSerializers: [createSerializer()],
});

This option needs to be set if either:

  • the multiSnapshot function is used to create multiple snapshot files (i.e. one per story), since it ignores any serializers specified in your jest config.
  • serializers not specified in your jest config should be used when snapshotting stories.

`serializer` (deprecated)

Pass a custom serializer (such as enzyme-to-json) to serialize components to snapshot-comparable data. The functionality of this option is completely covered by snapshotSerializers which should be used instead.

js
import initStoryshots from '@storybook/addon-storyshots';
import toJSON from 'enzyme-to-json';

initStoryshots({
  renderer: mount,
  serializer: toJSON,
});

This option only needs to be set if the default snapshotSerializers is not set in your jest config.

`stories2snapsConverter`

This parameter should be an instance of the Stories2SnapsConverter (or a derived from it) Class that is used to convert story-file name to snapshot-file name and vice versa.

By default, the instance of this class is created with these default options:

js
{
  snapshotsDirName: '__snapshots__',
  snapshotExtension: '.storyshot',
  storiesExtensions: ['.js', '.jsx', '.ts', '.tsx'],
}

This class might be overridden to extend the existing conversion functionality or instantiated to provide different options:

js
import initStoryshots, { Stories2SnapsConverter } from '@storybook/addon-storyshots';

initStoryshots({
  stories2snapsConverter: new Stories2SnapsConverter({
    snapshotExtension: '.storypuke',
    storiesExtensions: ['.foo'],
  }),
});

Exports

Apart from the default export (initStoryshots), Storyshots also exports some named test functions (see the test option above):

`snapshot`

The default, render the story as normal and take a Jest snapshot.

`renderOnly`

Just render the story, don't check the output at all. This is useful as a low-effort way of smoke testing your
components to ensure they do not error.

`snapshotWithOptions(options)`

Like the default, but allows you to specify a set of options for the test renderer. See for example here.

`renderWithOptions(options)`

Like the default, but allows you to specify a set of options for the renderer, just like snapshotWithOptions.

`multiSnapshotWithOptions(options)`

Like snapshotWithOptions, but generate a separate snapshot file for each stories file rather than a single monolithic file (as is the convention in Jest). This makes it dramatically easier to review changes. If you'd like the benefit of separate snapshot files, but don't have custom options to pass, you can pass an empty object.
If you use Component Story Format, you may also need to add an additional Jest transform to automate detecting story file names:

js
// jest.config.js
module.exports = {
  transform: {
    '^.+\\.stories\\.jsx?$': '@storybook/addon-storyshots/injectFileName',
    '^.+\\.jsx?$': 'babel-jest',
  },
};

integrityOptions

This option is useful when running test with multiSnapshotWithOptions(options) in order to track snapshots are matching the stories. (disabled by default).
The value is a settings to a glob object, that searches for the snapshot files.

js
initStoryshots({
  integrityOptions: { cwd: __dirname }, // it will start searching from the current directory
  test: multiSnapshotWithOptions(),
});

`shallowSnapshot`

Take a snapshot of a shallow-rendered version of the component. Note that this option will be overridden if you pass a renderer option.

`Stories2SnapsConverter`

This is a class that generates snapshot's name based on the story (kind, story & filename) and vice versa.

Example:

Let's say we wanted to create a test function for shallow && multi-file snapshots:

js
import initStoryshots, { Stories2SnapsConverter } from '@storybook/addon-storyshots';
import { shallow } from 'enzyme';
import toJson from 'enzyme-to-json';

const converter = new Stories2SnapsConverter();

initStoryshots({
  test: ({ story, context }) => {
    const snapshotFileName = converter.getSnapshotFileName(context);
    const storyElement = story.render();
    const shallowTree = shallow(storyElement);

    if (snapshotFileName) {
      expect(toJson(shallowTree)).toMatchSpecificSnapshot(snapshotFileName);
    }
  },
});

`asyncJest`

Enables Jest done() callback in the StoryShots tests for async testing. See StoryShots for async rendered components for more info.

Story Parameters

`disable`

Some stories are difficult or impossible to snapshot, such as those covering components that use external DOM-modifying libraries, and those that deliberately throw errors. It is possible to skip stories like these by giving them a parameter of storyshots: {disable: true}. There is also a shorthand for this, storyshots: false.

js
export const Exception = () => {
  throw new Error('storyFn threw an error! WHOOPS');
};
Exception.storyName = 'story throws exception';
Exception.parameters = {
  storyshots: { disable: true },
};

File: addons/storyshots/storyshots-puppeteer/README.md

StoryShots + [Puppeteer](https://github.com/GoogleChrome/puppeteer)

Getting Started

Add the following modules into your app.

sh
npm install @storybook/addon-storyshots-puppeteer puppeteer --save-dev

โš ๏ธ As of Storybook 5.3 puppeteer is no more included in addon dependencies and must be added to your project directly.

Configure Storyshots for Puppeteer tests

โš ๏ธ React-native is not supported by this test function.

When willing to run Puppeteer tests for your stories, you have two options:

  • Have a storybook running (ie. accessible via http(s), for instance using npm run storybook)
  • Have a static build of the storybook (for instance, using npm run build-storybook)

Then you will need to reference the storybook URL (file://... if local, http(s)://... if served)

_puppeteerTest_

Allows to define arbitrary Puppeteer tests as story.parameters.puppeteerTest function.

You can either create a new Storyshots instance or edit the one you previously used:

js
import initStoryshots from '@storybook/addon-storyshots';
import { puppeteerTest } from '@storybook/addon-storyshots-puppeteer';

initStoryshots({ suite: 'Puppeteer storyshots', test: puppeteerTest() });

Then, in your stories:

js
export const myExample = () => {
  ...
};
myExample.parameters = {
  async puppeteerTest(page) {
    const element = await page.$('<some-selector>');
    await element.click();
    expect(something).toBe(something);
  },
};

This will assume you have a storybook running on at http://localhost:6006.
Internally here are the steps:

Specifying the storybook URL

If you want to set specific storybook URL, you can specify via the storybookUrl parameter, see below:

js
import initStoryshots from '@storybook/addon-storyshots';
import { puppeteerTest } from '@storybook/addon-storyshots-puppeteer';

initStoryshots({
  suite: 'Puppeteer storyshots',
  test: puppeteerTest({ storybookUrl: 'http://my-specific-domain.com:9010' }),
});

The above config will use https://my-specific-domain.com:9010 for tests. You can also use query parameters in your URL (e.g. for setting a different background for your storyshots, if you use @storybook/addon-backgrounds).

You may also use a local static build of storybook if you do not want to run the webpack dev-server:

js
import initStoryshots from '@storybook/addon-storyshots';
import { puppeteerTest } from '@storybook/addon-storyshots-puppeteer';

initStoryshots({
  suite: 'Puppeteer storyshots',
  test: puppeteerTest({
    storybookUrl: 'file:///path/to/my/storybook-static',
    // storybookUrl: 'file://${path.resolve(__dirname, '../storybook-static')}'
  }),
});

Specifying options to _goto()_ (Puppeteer API)

You might use getGotoOptions to specify options when the storybook is navigating to a story (using the goto method). Will be passed to Puppeteer .goto() fn

js
import initStoryshots from '@storybook/addon-storyshots';
import { puppeteerTest } from '@storybook/addon-storyshots-puppeteer';

const getGotoOptions = ({ context, url }) => {
  return {
    waitUntil: 'networkidle0',
  };
};

initStoryshots({
  suite: 'Puppeteer storyshots',
  test: puppeteerTest({ storybookUrl: 'http://localhost:6006', getGotoOptions }),
});

Specifying custom Chrome executable path (Puppeteer API)

You might use chromeExecutablePath to specify the path to a different version of Chrome, without downloading Chromium. Will be passed to Runs a bundled version of Chromium

js
import initStoryshots from '@storybook/addon-storyshots';
import { puppeteerTest } from '@storybook/addon-storyshots-puppeteer';

const chromeExecutablePath = '/usr/local/bin/chrome';

initStoryshots({
  suite: 'Puppeteer storyshots',
  test: puppeteerTest({ storybookUrl: 'http://localhost:6006', chromeExecutablePath }),
});

Specifying a custom Puppeteer `browser` instance

You might use the async getCustomBrowser function to obtain a custom instance of a Puppeteer browser object. This will prevent storyshots-puppeteer from creating its own browser. It will create and close pages within the browser, and it is your responsibility to manage the lifecycle of the browser itself.

js
import initStoryshots from '@storybook/addon-storyshots';
import { puppeteerTest } from '@storybook/addon-storyshots-puppeteer';
import puppeteer from 'puppeteer';

(async function () {
  initStoryshots({
    suite: 'Puppeteer storyshots',
    test: puppeteerTest({
      storybookUrl: 'http://localhost:6006',
      getCustomBrowser: () => puppeteer.connect({ browserWSEndpoint: 'ws://yourUrl' }),
    }),
  });
})();

Customizing a `page` instance

Sometimes, there is a need to customize a page before it calls the goto api.

An example of device emulation:

js
import initStoryshots from '@storybook/addon-storyshots';
import { puppeteerTest } from '@storybook/addon-storyshots-puppeteer';
const devices = require('puppeteer/DeviceDescriptors');

const iPhone = devices['iPhone 6'];

function customizePage(page) {
  return page.emulate(iPhone);
}

initStoryshots({
  suite: 'Puppeteer storyshots',
  test: puppeteerTest({
    storybookUrl: 'http://localhost:6006',
    customizePage,
  }),
});

Specifying setup and tests timeout

By default, @storybook/addon-storyshots-puppeteer uses 15 second timeouts for browser setup and test functions.
Those can be customized with setupTimeout and testTimeout parameters.

Integrate Puppeteer storyshots with regular app

You may want to use another Jest project to run your Puppeteer storyshots as they require more resources: Chrome and Storybook built/served.
You can find a working example of this in the official-storybook example.

Integrate Puppeteer storyshots with [Create React App](https://github.com/facebookincubator/create-react-app)

You have two options here, you can either:

  • Add the storyshots configuration inside any of your test.js file. You must ensure you have either a running storybook or a static build available.

  • Create a custom test file using Jest outside of the CRA scope:

    A more robust approach would be to separate existing test files ran by create-react-app (anything (test|spec).js suffixed files) from the test files to run Puppeteer storyshots.
    This use case can be achieved by using a custom name for the test file, ie something like puppeteer-storyshots.runner.js. This file will contain the initStoryshots call with Puppeteer storyshots configuration.
    Then you will create a separate script entry in your package.json, for instance

    json
    {
      "scripts": {
        "puppeteer-storyshots": "jest puppeteer-storyshots.runner.js --config path/to/custom/jest.config.json"
      }
    }

    Note that you will certainly need a custom config file for Jest as you run it outside of the CRA scope and thus you do not have the built-in config.

    Once that's setup, you can run npm run puppeteer-storyshots.

Reminder

Puppeteer launches a web browser (Chrome) internally.

The browser opens a page (either using the static build of storybook or a running instance of Storybook)

If you run your test without either the static build or a running instance, this wont work.

To make sure your tests run against the latest changes of your Storybook, you must keep your static build or running Storybook up-to-date.
This can be achieved by adding a step before running the test ie: npm run build-storybook && npm run image-snapshots.
If you run the Puppeteer storyshots against a running Storybook in dev mode, you don't have to worry about the stories being up-to-date because the dev-server is watching changes and rebuilds automatically.

_axeTest_

Runs Axe accessibility checks and verifies that they pass using jest-puppeteer-axe.

js
import initStoryshots from '@storybook/addon-storyshots';
import { axeTest } from '@storybook/addon-storyshots-puppeteer';

initStoryshots({ suite: 'A11y checks', test: axeTest() });

For configuration, it uses the same story.parameters.a11y parameter as @storybook/addon-a11y

_imageSnapshots_

Generates and compares screenshots of your stories using jest-image-snapshot.

js
import initStoryshots from '@storybook/addon-storyshots';
import { imageSnapshot } from '@storybook/addon-storyshots-puppeteer';

initStoryshots({ suite: 'Image storyshots', test: imageSnapshot() });

It saves all images under __image_snapshots__ folder.

Specifying options to _jest-image-snapshots_

If you wish to customize jest-image-snapshot, then you can provide a getMatchOptions parameter that should return the options config object. Additionally, you can provide beforeScreenshot which is called before the screenshot is captured and a afterScreenshot handler which is called after the screenshot and receives the just created image.

js
import initStoryshots from '@storybook/addon-storyshots';
import { imageSnapshot } from '@storybook/addon-storyshots-puppeteer';
const getMatchOptions = ({ context: { kind, story }, url }) => {
  return {
    failureThreshold: 0.2,
    failureThresholdType: 'percent',
  };
};
const beforeScreenshot = (page, { context: { kind, story }, url }) => {
  return new Promise((resolve) =>
    setTimeout(() => {
      resolve();
    }, 600)
  );
};
const afterScreenshot = ({ image, context }) => {
  return new Promise((resolve) =>
    setTimeout(() => {
      resolve();
    }, 600)
  );
};
initStoryshots({
  suite: 'Image storyshots',
  test: imageSnapshot({
    storybookUrl: 'http://localhost:6006',
    getMatchOptions,
    beforeScreenshot,
    afterScreenshot,
  }),
});

getMatchOptions receives an object: { context: {kind, story}, url}. kind is the kind of the story and the story its name. url is the URL the browser will use to screenshot.

beforeScreenshot receives the Puppeteer page instance and an object: { context: {kind, story}, url}. kind is the kind of the story and the story its name. url is the URL the browser will use to screenshot. beforeScreenshot is part of the promise chain and is called after the browser navigation is completed but before the screenshot is taken. It allows for triggering events on the page elements and delaying the screenshot and can be used avoid regressions due to mounting animations.

afterScreenshot receives the created image from puppeteer.

Specifying options to _screenshot()_ (Puppeteer API)

You might use getScreenshotOptions to specify options for screenshot. Will be passed to Puppeteer .screenshot() fn

js
import initStoryshots from '@storybook/addon-storyshots';
import { imageSnapshot } from '@storybook/addon-storyshots-puppeteer';
const getScreenshotOptions = ({ context, url }) => {
  return {
    encoding: 'base64', // encoding: 'base64' is a property required by puppeteer
    fullPage: false, // Do not take the full page screenshot. Default is 'true' in Storyshots.,
  };
};
initStoryshots({
  suite: 'Image storyshots',
  test: imageSnapshot({ storybookUrl: 'http://localhost:6006', getScreenshotOptions }),
});

getScreenshotOptions receives an object { context: {kind, story}, url}. kind is the kind of the story and the story its name. url is the URL the browser will use to screenshot.

To create a screenshot of just a single element (with its children), rather than the page or current viewport, an ElementHandle can be returned from beforeScreenshot:

js
import initStoryshots from '@storybook/addon-storyshots';
import { imageSnapshot } from '@storybook/addon-storyshots-puppeteer';

const beforeScreenshot = (page) => page.$('#root > *');

initStoryshots({
  suite: 'Image storyshots',
  test: imageSnapshot({ storybookUrl: 'http://localhost:6006', beforeScreenshot }),
});

File: addons/storyshots/README.md

StoryShots


File: addons/queryparams/README.md

storybook-addon-queryparams

This storybook addon can be helpful if your components need special query parameters to work the way you want them to. It allows you to mock query params per story so that you can easily reproduce different states of your component.

Getting started

First, install the addon.

sh
$ yarn add @storybook/addon-queryparams --dev

Register it by adding it in the addons attribute in your main.js file (create this file inside your storybook config directory if needed).

js
module.exports = {
  addons: ['@storybook/addon-queryparams'],
};

In your story, add the withQuery decorator and define the query parameters you want to mock:

js
import React from 'react';
import { Button } from '@storybook/react/demo';
import { withQuery } from '@storybook/addon-queryparams';

export default {
  title: 'Button',
  component: Button,
  decorators: [withQuery],
  parameters: {
    query: {
      mock: 'Hello world!',
    },
  },
};

export const WithMockedSearch = () => {
  const urlParams = new URLSearchParams(document.location.search);
  const mockedParam = urlParams.get('mock');
  return Mocked value: {mockedParam};
};

Example with storiesOf API

js
import React from 'react';
import { storiesOf } from '@storybook/react';

storiesOf('button', module)
  .addParameters({
    query: {
      mock: 'Hello World!',
    },
  })
  .add('Prints the mocked parameter', () => {
    const urlParams = new URLSearchParams(document.location.search);
    const mockedParam = urlParams.get('mock');
    return Mocked value: {mockedParam};
  });

Story Links Addon

The Storybook Links addon can be used to create links that navigate between stories in Storybook.

Framework Support

Getting Started

Install this addon by adding the @storybook/addon-links dependency:

sh
yarn add -D @storybook/addon-links

within .storybook/main.js:

js
module.exports = {
  addons: ['@storybook/addon-links']
}

Then you can import linkTo in your stories and use like this:

js
import { linkTo } from '@storybook/addon-links'

export default {
  title: 'Button',
};

export const first = () => (
  Go to "Second"
);
export const second = () => (
  Go to "First"
);

Have a look at the linkTo function:

js
import { linkTo } from '@storybook/addon-links'

linkTo('Toggle', 'off')
linkTo(() => 'Toggle', () => 'off')
linkTo('Toggle') // Links to the first story in the 'Toggle' kind

With that, you can link an event in a component to any story in the Storybook.

  • First parameter is the story kind name (what you named with title).
  • Second (optional) parameter is the story name (what you named with exported name).
    If the second parameter is omitted, the link will point to the first story in the given kind.

You can also pass a function instead for any of above parameter. That function accepts arguments emitted by the event and it should return a string:

js
import { LinkTo, linkTo } from '@storybook/addon-links';

export default {
  title: 'Select',
};

export const index = () => (
  <select value="Index" onChange={linkTo('Select', e => e.currentTarget.value)}>
    <option>index</option>
    <option>first</option>
    <option>second</option>
    <option>third</option>
  </select>  
);
export const first = () => (
  <LinkTo story="index">Go back</LinkTo>
);
export const second = () => (
  <LinkTo story="index">Go back</LinkTo>
);
export const third = () => (
  <LinkTo story="index">Go back</LinkTo>
);

hrefTo function

If you want to get an URL for a particular story, you may use hrefTo function. It returns a promise, which resolves to string containing a relative URL:

js
import { hrefTo } from '@storybook/addon-links';
import { action } from '@storybook/addon-actions';

export default {
  title: 'Href',
};

export const log = () => {
  hrefTo('Href', 'log').then(action('URL of this story'));

  return See action logger;
};

withLinks decorator enables a declarative way of defining story links, using data attributes.
Here is an example in React, but it works with any framework:

js
import { withLinks } from '@storybook/addon-links'

export default {
  title: 'Button',
  decorators: [withLinks],
};

export const first = () => (
  Go to "OtherStory"
);

LinkTo component (React only)

One possible way of using hrefTo is to create a component that uses native a element, but prevents page reloads on plain left click, so that one can still use default browser methods to open link in new tab.
A React implementation of such a component can be imported from @storybook/addon-links package:

js
import LinkTo from '@storybook/addon-links/react';

export default {
  title: 'Link',
};

export const first = () => (
  <LinkTo story="second">Go to Second</LinkTo>
);
export const second = () => (
  <LinkTo story="first">Go to First</LinkTo>
);

It accepts all the props the a element does, plus story and kind. It the kind prop is omitted, the current kind will be preserved.

js
<LinkTo
  kind="Toggle"
  story="off"
  target="_blank"
  title="link to second story"
  style={{color: '#1474f3'}}
>Go to Second</LinkTo>

To implement such a component for another framework, you need to add special handling for click event on native a element. See RoutedLink sources for reference.


File: addons/knobs/README.md

Storybook Addon Knobs

Storybook Addon Knobs allow you to edit props dynamically using the Storybook UI.
You can also use Knobs as a dynamic variable inside stories in Storybook.

Framework Support.

This is what Knobs looks like:

Checkout the above Live Storybook or watch this video.

Getting Started

First of all, you need to install Knobs into your project as a dev dependency.

sh
yarn add @storybook/addon-knobs --dev

within .storybook/main.js:

js
module.exports = {
  addons: ['@storybook/addon-knobs'],
};

Now, write your stories with Knobs.

With React

js
import React from 'react';
import { withKnobs, text, boolean, number } from '@storybook/addon-knobs';

export default {
  title: 'Storybook Knobs',
  decorators: [withKnobs],
};
// Add the `withKnobs` decorator to add knobs support to your stories.
// You can also configure `withKnobs` as a global decorator.

// Knobs for React props
export const withAButton = () => (
  {text('Label', 'Hello Storybook')}
);

// Knobs as dynamic variables.
export const asDynamicVariables = () => {
  const name = text('Name', 'James');
  const age = number('Age', 35);
  const content = `I am ${name} and I'm ${age} years old.`;

  return {content};
};

With Vue.js

MyButton.story.js:

js
import { storiesOf } from '@storybook/vue';
import { withKnobs, text, boolean } from '@storybook/addon-knobs';

import MyButton from './MyButton.vue';

export default {
  title: 'Storybook Knobs',
  decorators: [withKnobs],
};

// Assign `props` to the story's component, calling
// knob methods within the `default` property of each prop,
// then pass the story's prop data to the componentโ€™s prop in
// the template with `v-bind:` or by placing the prop within
// the componentโ€™s slot.
export const exampleWithKnobs = () => ({
  components: { MyButton },
  props: {
    isDisabled: {
      default: boolean('Disabled', false),
    },
    text: {
      default: text('Text', 'Hello Storybook'),
    },
  },
  template: `<MyButton :isDisabled="isDisabled">{{ text }}</MyButton>`,
});

MyButton.vue:

vue
<template>
  
    <slot></slot>
  
</template>

<script>
export default {
  props: {
    isDisabled: {
      type: Boolean,
      default: false,
    },
  },
};
</script>

With Angular

js
import { storiesOf } from '@storybook/angular';
import { boolean, number, text, withKnobs } from '@storybook/addon-knobs';

import { Button } from '@storybook/angular/demo';

export default {
  title: 'Storybook Knobs',
  decorators: [withKnobs],
};

export const withKnobs = () => ({
  component: Button,
  props: {
    text: text('text', 'Hello Storybook'), // The first param of the knob function has to be exactly the same as the component input.
  },
});

With Ember

js
import { withKnobs, text, boolean } from '@storybook/addon-knobs';
import { hbs } from 'ember-cli-htmlbars';

export default {
  title: 'StoryBook with Knobs',
  decorators: [withKnobs],
};

export const button = () => ({
  template: hbs`
    {{label}}
  `,
  context: {
    label: text('label', 'Hello Storybook'),
    disabled: boolean('disabled', false),
  },
});

Categorization

Categorize your Knobs by assigning them a groupId. When a groupId exists, tabs will appear in the Knobs storybook panel to filter between the groups. Knobs without a groupId are automatically categorized into the ALL group.

js
export const inGroups = () => {
  const personalGroupId = 'personal info';
  const generalGroupId = 'general info';

  const name = text('Name', 'James', personalGroupId);
  const age = number('Age', 35, { min: 0, max: 99 }, personalGroupId);
  const message = text('Hello!', 35, generalGroupId);
  const content = `
    I am ${name} and I'm ${age} years old.
    ${message}
  `;

  return {content};
};

You can see your Knobs in a Storybook panel as shown below.

Available Knobs

These are the Knobs available for you to use. You can import these Knobs from the @storybook/addon-knobs module.
Here's how to import the text Knob.

js
import { text } from '@storybook/addon-knobs';

Just like that, you can import any other following Knobs:

text

Allows you to get some text from the user.

js
import { text } from '@storybook/addon-knobs';

const label = 'Your Name';
const defaultValue = 'James';
const groupId = 'GROUP-ID1';

const value = text(label, defaultValue, groupId);

boolean

Allows you to get a boolean value from the user.

js
import { boolean } from '@storybook/addon-knobs';

const label = 'Agree?';
const defaultValue = false;
const groupId = 'GROUP-ID1';

const value = boolean(label, defaultValue, groupId);

number

Allows you to get a number from the user.

js
import { number } from '@storybook/addon-knobs';

const label = 'Age';
const defaultValue = 78;
const groupId = 'GROUP-ID1';

const value = number(label, defaultValue);

For use with groupId, pass the default options as the third argument.

js
const value = number(label, defaultValue, {}, groupId);

number bound by range

Allows you to get a number from the user using a range slider.

js
import { number } from '@storybook/addon-knobs';

const label = 'Temperature';
const defaultValue = 73;
const options = {
  range: true,
  min: 60,
  max: 90,
  step: 1,
};
const groupId = 'GROUP-ID1';

const value = number(label, defaultValue, options, groupId);

color

Allows you to get a colour from the user.

js
import { color } from '@storybook/addon-knobs';

const label = 'Color';
const defaultValue = '#ff00ff';
const groupId = 'GROUP-ID1';

const value = color(label, defaultValue, groupId);

object

Allows you to get a JSON object or array from the user.

js
import { object } from '@storybook/addon-knobs';

const label = 'Styles';
const defaultValue = {
  backgroundColor: 'red',
};
const groupId = 'GROUP-ID1';

const value = object(label, defaultValue, groupId);

Make sure to enter valid JSON syntax while editing values inside the knob.

array

Allows you to get an array of strings from the user.

js
import { array } from '@storybook/addon-knobs';

const label = 'Styles';
const defaultValue = ['Red'];
const groupId = 'GROUP-ID1';

const value = array(label, defaultValue);

While editing values inside the knob, you will need to use a separator.
By default it is a comma, but this can be overridden by passing a separator variable.

js
import { array } from '@storybook/addon-knobs';

const label = 'Styles';
const defaultValue = ['Red'];
const separator = ':';
const value = array(label, defaultValue, separator);

For use with groupId, pass the default separator as the third argument.

js
const value = array(label, defaultValue, ',', groupId);

select

It allows you to get a value from a select box from the user.

js
import { select } from '@storybook/addon-knobs';

const label = 'Colors';
const options = {
  Red: 'red',
  Blue: 'blue',
  Yellow: 'yellow',
  Rainbow: ['red', 'orange', 'etc'],
  None: null,
};
const defaultValue = 'red';
const groupId = 'GROUP-ID1';

const value = select(label, options, defaultValue, groupId);

Options can also be an array:

js
import { select } from '@storybook/addon-knobs';
const label = 'Cats';
const options = ['linus', 'eleanor', 'lover'];
const defaultValue = 'eleanor';
const groupId = 'GROUP-ID2';
const value = select(label, options, defaultValue, groupId);

Options can also be an array OF objects:

js
const label = 'Dogs';
const arrayOfObjects = [
  {
    label: 'Sparky',
    dogParent: 'Matthew',
    location: 'Austin',
  },
  {
    label: 'Juniper',
    dogParent: 'Joshua',
    location: 'Austin',
  },
];
const defaultValue = arrayOfObjects[0];
const groupId = 'GROUP-ID3';
const value = select(label, arrayOfObjects, defaultValue, groupId);

radio buttons

It allows you to get a value from a list of radio buttons from the user.

js
import { radios } from '@storybook/addon-knobs';

const label = 'Fruits';
const options = {
  Kiwi: 'kiwi',
  Guava: 'guava',
  Watermelon: 'watermelon',
};
const defaultValue = 'kiwi';
const groupId = 'GROUP-ID1';

const value = radios(label, options, defaultValue, groupId);

options

Configurable UI for selecting a value from a set of options.

js
import { optionsKnob } from '@storybook/addon-knobs';

const label = 'Fruits';
const valuesObj = {
  Kiwi: 'kiwi',
  Guava: 'guava',
  Watermelon: 'watermelon',
};
const defaultValue = 'kiwi';
const optionsObj = {
  display: 'inline-radio',
};
const groupId = 'GROUP-ID1';

const value = optionsKnob(label, valuesObj, defaultValue, optionsObj, groupId);

Alternatively you can use this import:

text
import { optionsKnob as options } from '@storybook/addon-knobs';

...

const value = options(label, valuesObj, defaultValue, optionsObj, groupId);

The display property for optionsObj accepts:

  • radio
  • inline-radio
  • check
  • inline-check
  • select
  • multi-select

files

It allows you to get a value from a file input from the user.

js
import { files } from '@storybook/addon-knobs';

const label = 'Images';
const accept = '.xlsx, .pdf';
const defaultValue = [];
const groupId = 'GROUP-ID1';

const value = files(label, accept, defaultValue, groupId);

You can optionally specify a list of file types which the file input should accept.
Multiple files can be selected, and will be returned as an array of Data URLs.

date

Allows you to get date (and time) from the user.

js
import { date } from '@storybook/addon-knobs';

const label = 'Event Date';
const defaultValue = new Date('Jan 20 2017');
const groupId = 'GROUP-ID1';

const value = date(label, defaultValue, groupId);

Note: the default value must not change - e.g., do not do date('Label', new Date()) or date('Label').

The date knob returns the selected date as stringified Unix timestamp (e.g. "1510913096516").
If your component needs the date in a different form you can wrap the date function:

js
function myDateKnob(name, defaultValue) {
  const stringTimestamp = date(name, defaultValue);
  return new Date(stringTimestamp);
}

button

It allows you to include a button and associated handler.

js
import { button } from '@storybook/addon-knobs';

const label = 'Do Something';
const handler = () => doSomething('foobar');
const groupId = 'GROUP-ID1';

button(label, handler, groupId);

Button knobs cause the story to re-render after the handler fires.
You can prevent this by having the handler return false.

withKnobs options

withKnobs also accepts two optional options as story parameters.
Usage:

js
import { withKnobs } from '@storybook/addon-knobs';

export default {
  title: 'Storybook Knobs',
  decorators: [withKnobs],
};

export const defaultView = () => ;
defaultView.parameters = {
  knobs: {
    // Doesn't emit events while user is typing.
    timestamps: true,

    // Escapes strings to be safe for inserting as innerHTML. This option is true by default. It's safe to set it to `false` with frameworks like React which do escaping on their side.
    // You can still set it to false, but it's strongly discouraged to set to true in cases when you host your storybook on some route of your main site or web app.
    escapeHTML: true,
  },
};

Typescript

If you are using Typescript, make sure you have the type definitions installed for the following libs:

  • node
  • react

You can install them using: (assuming you are using Typescript >2.0.)

sh
yarn add @types/node @types/react --dev