Skip to content

Latest commit

 

History

History

stories

Storybook

Our stories are defined alongside our components in *.stories.js. They are funnelled down through stories/stories.js and stories/stories-native.js and are then imported by stories/index.*.js which exports the global story loader. See examples in any *.stories.js for how to write a story loader. Once you've written a story, adding it to stories.js (or stories-native.js if it's a native-only story) will be enough to add it to the live storybook.

Running storybook

Desktop

The yarn run storybook command will spin up a local storybook server on localhost:6006

Native

There's a native storybook UI that controls the selected story. You can also control the current native view by running yarn run rn-storybook (with storybook running on a simulator) and navigating to localhost:7007.

Android

Storybook can be enabled by going to Build > Select Build Variant and selecting 'storyBook'. You can also run yarn rn-build-storybook-android && yarn rn-push-storybook-android.

# Enable storybook web UI connection
adb reverse tcp:7007 tcp:7007

iOS

Storybook is controlled by the constants return value in Storybook.m. Changing it to @{@"isStorybook": @true} enables storybook mode.

Storyshots

We use an addon to storybook called Storyshots that adds jest tests on all our defined stories. yarn test will run the jest tests and compare the output DOM to the stored snapshots, which are checked into the client repo. This means that updating components with stories can cause jest tests to fail if the snapshots haven't also been updated. Running yarn test -u will update the snapshots to the current output.

useSelector and storybooks

You may want to have a component that accesses state with useSelector. The storybook for this component should use a mock store.

Connected children (THE OLD WAY)

If a story has a child that is connected to the redux store, that child will still try to its connector in storybook mode, which will fail. To work around this, we patch connect in storybook mode to access the store in a different way. connect in storybook mode will assume the store is a map that looks like this:

{
  [componentDisplayName: string]: ownProps => viewProps
}

The patched connect will ignore anything defined in (mapStateToProps, mapDispatchToProps, mergeProps) and try to call the closure (the prop factory) in the store. The prop factory will get whatever is in ownProps and should return the props that the view expects.

In stories with connected children, a <Provider /> will need to wrap the top level (via addDecorator) and contain this map. For convenience, createPropProvider is exported from ./stories that creates a <Provider /> from a map. ./prop-providers contains some common prop factory creators, as well as a compose function to combine multiple maps into a single <Provider />. See devices/index.stories.js for a sample implementation.

Display names

Making a prop factory requires knowing the display name of the connected component(s), which is generally the variable name of the component. Sometimes the display name fails to get piped through to the connector, in which case a setDisplayName(n) will need to be chained immediately after the relevant connect call via recompose. Then the prop factory can be defined under n.

Mocks

Mocks are handled a couple of ways. How storybook and storyshots work is slightly different. Jest has some overrides in the package.json file in the 'jest' section. Desktop storybook has some overrides with webpack in the .storybook/webpack.config.js file. Native storybook has some overrides through metro in metro.config.js.