Merge pull request #11701 from storybookjs/migrate_workflows_6_0

Migrate Workflows section to 6 0
This commit is contained in:
jonniebigodes 2020-07-29 20:36:55 +01:00 committed by GitHub
commit cc28ed73c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 620 additions and 0 deletions

View File

@ -21,5 +21,22 @@ module.exports = {
prefix: 'writing-docs',
pages: ['introduction', 'docs-page', 'mdx', 'doc-blocks'],
},
{
title:'Workflows',
prefix:'workflows',
pages:[
'publish-storybook',
'build-pages-with-storybook',
'stories-for-multiple-components',
'testing-with-storybook',
'unit-testing',
'visual-testing',
'interaction-testing',
'snapshot-testing',
'storybook-composition',
'package-composition'
]
}
],
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,133 @@
---
title: 'Building pages with Storybook'
---
Storybook helps you build any component, from small “atomic” components to composed pages. But as you move up the component hierarchy toward the level of pages, you end up dealing with more complexity.
There are many ways to build pages in Storybook. Here are common patterns and solutions.
- Pure presentational pages.
- Connected components (e.g. network requests, context, browser environment).
### Pure presentational pages
Teams at the BBC, The Guardian, and the Storybook maintainers themselves build pure presentational pages. If you take this approach, you don't need to do anything special to render your pages in Storybook.
It's straightforward to write components to be fully presentational all the way up to the screen level. That makes it easy to show in Storybook. The idea is you then do all the messy “connected” logic in a single wrapper component in your app outside of Storybook. You can see an example of this approach in the [Data](https://www.learnstorybook.com/intro-to-storybook/react/en/data/) chapter of Learn Storybook.
The benefits:
- Easy to write stories once components are in this form.
- All the data for the story is encoded in the args of the story, which works well with other parts of Storybook's tooling (e.g. [controls](../essentials/controls)).
The downsides:
- Your existing app may not be structured in this way and it may be difficult to change it.
- Fetching data in one place means that you need to drill it down to the components that actually use it. This can be natural in a page that composes one big GraphQL query (for instance), but in other data fetching approaches may make this less appropriate.
- It's less flexible if you want to load data incrementally in different places on the screen.
#### Args composition for presentational screens
When you are building screens in this way, it is typical that the inputs of a composite component are a combination of the inputs of the various sub-components it renders. For instance, if your screen renders a page layout (containing details of the current user), a header (describing the document you are looking at), and a list (of the subdocuments), the inputs of the screen may consist of the user, document and subdocuments.
```js
// your-page.js
import React from 'react';
import PageLayout from './PageLayout';
import DocumentHeader from './DocumentHeader';
import DocumentList from './DocumentList';
function DocumentScreen({ user, document, subdocuments }) {
return (
<PageLayout user={user}>
<DocumentHeader document={document} />
<DocumentList documents={subdocuments} />
</PageLayout>
);
}
```
In such cases it is natural to use [args composition](../writing-stories/args#args-composition) to build the stories for the page based on the stories of the sub-components:
```js
// your-page.story.js
import React from 'react';
import DocumentScreen from './DocumentScreen';
import PageLayout from './PageLayout.stories';
import DocumentHeader from './DocumentHeader.stories';
import DocumentList from './DocumentList.stories';
export default {
component: DocumentScreen,
title: 'DocumentScreen',
}
const Template = (args) => <DocumentScreen {...args} />;
export const Simple = Template.bind({});
Simple.args = {
user: PageLayout.Simple.user,
document: DocumentHeader.Simple.document,
subdocuments: DocumentList.Simple.documents,
};
```
This approach is particularly useful when the various subcomponents export a complex list of different stories, which you can pick and choose to build realistic scenarios for your screen-level stories without repeating yourself. By reusing the data and taking a Don't-Repeat-Yourself(DRY) philosophy, your story maintenance burden is minimal.
### Mocking connected components
Render a connected component in Storybook by mocking the network requests that it makes to fetch its data. There are various layers in which you can do that.
#### Mocking providers
If you are using a provider that supplies data via the context, you can wrap your story in a decorator that supplies a mocked version of that provider. For example, in the [Screens](https://www.learnstorybook.com/intro-to-storybook/react/en/screen/) chapter of Learn Storybook we mock a Redux provider with mock data.
Additionally, there may be addons that supply such providers and nice APIs to set the data they provide. For instance [`storybook-addon-apollo-client`](https://www.npmjs.com/package/storybook-addon-apollo-client) provides this API:
```js
// my-component-with-query.story.js
import MyComponentThatHasAQuery, {
MyQuery,
} from '../component-that-has-a-query';
export const LoggedOut = () => <MyComponentThatHasAQuery />;
LoggedOut.parameters: {
apolloClient: {
mocks: [
{ request: { query: MyQuery }, result: { data: { viewer: null } } },
],
},
};
```
#### Mocking imports
It is also possible to mock imports directly, similar to Jest, using webpacks aliasing. This is useful if your component makes network requests directly.
```js
// .storybook/main.js
module.exports = {
// your Storybook configuration
webpackFinal: config => {
config.resolve.alias.fetch = '../__mocks__/fetch.js'
return config;
}
};
```
You would still need to write the fetch mock and wire up a decorator to provide results to it based on the current story.
#### Specific mocks
Another mocking approach is to use libraries that intercept calls at a lower level. For instance you can use [`fetch-mock`](https://www.npmjs.com/package/fetch-mock) to mock fetch requests specifically, or [`msw`](https://www.npmjs.com/package/msw) to mock all kinds of network traffic.
Similar to the import mocking above, once you have a mock youll still want to set the return value of the mock on a per-story basis. Do this in Storybook with a decorator that reads story parameters.

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 499 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -0,0 +1,23 @@
---
title: 'Interaction testing'
---
Stories are useful for verifying the known states of a component. But sometimes you need to test how a component changes in response to user interaction.
Stories are convenient **starting points** and **harnesses** for interaction tests using end-to-end tools like [Enzyme](https://enzymejs.github.io/enzyme/) and [Cypress](https://www.cypress.io/).
Luckily, this is straightforward. Point your interaction testing tool at Storybooks isolated iframe [URL for a specific story](../configure/user-interface#permalinking-to-stories) then execute the test script as usual. Heres an example using Cypress:
```js
// My-component_spec.js
describe('My Component', () => {
it('should respond to click on button with warning', () => {
cy.visit('http://localhost:6006/iframe.html?id=my-component--basic-story);
cy.get('#button').click();
cy.get('#warning').should('contain.text', 'You need to fill in the form!');
});
})
```

View File

@ -0,0 +1,96 @@
---
title: 'Package Composition'
---
Storybook is widely used by component libraries and design systems. Design system authors can automatically compose their design systems inside their consumers Storybooks.
For example, if you use a design system package, its stories can appear alongside your own. That makes it convenient to cross reference usage documentation without leaving Storybook.
### For package consumers
Composition happens automatically if the package [supports](#for-package-authors) it. When you install the package, Storybook will load its stories alongside your own.
![Package composition workflow](./package-composition.png)
#### Configuring
If you want to configure how the composed Storybook behaves, you can disable the `ref` element in your [`.storybook/main.js`](../configure/overview#configure-story-rendering)
```js
// .storybook/main.js
module.exports = {
// your Storybook configuration
refs: {
'package-name': { disable: true }
}
```
#### Changing versions
Change the version of the composed Storybook to see how the library evolves. This requires [configuration](#providing-a-version-section) from the package author.
![Package composition workflow](./composition-versioning.png)
### For package authors
Component library authors can expand adoption by composing their components in their consumers Storybooks.
Add a `storybook` property in your published `package.json `that contains an object with a `url` field. Point the URL field to a published Storybook at the version you want.
```json
// Your component librarys package.json
{
"storybook": {
"url": "https://host.com/your-storybook-for-this-version"
}
}
```
#### Automatic version selection
If you are using a [CHP level 1 service](#chp-level-1) for hosting (such as [Chromatic.com](https://www.chromatic.com/)), you can provide a single URL for your Storybook in the `storybook.url` field. You do not need to change the URL each time you publish a new version. Storybook will automatically find the correct URL for your package.
For example, for Chromatic, you might do:
```json
{
"storybook": {
"url": "https://master--xyz123.chromatic.com"
}
}
```
In this example `xyz123` is your projects id. Storybook will automatically compose in the Storybook published to that project corresponding to the version the user has installed.
#### Providing a version section
Similarly, if you're using a [CHP level 1 service](#chp-level-1) (such as chromatic.com) for hosting, you can provide a list of versions for the user to [choose from](#changing-versions) to experiment with other versions of your package.
### Component Hosting Protocol (CHP)
Storybook can communicate with services that host built Storybooks online. This enables features such as [Composition](./storybook-composition). We categorize services via compliance with the "Component Hosting Protocol" (CHP) with various levels of support in Storybook.
#### CHP level 1
<div style="background-color:#F8FAFC">
TODO: "version=x.y.z query parameter".(vet this)
</div>
The service serves uploaded Storybooks and makes the following available:
- Versioned endpoints, URLs that resolve to different published Storybooks depending on a version=x.y.z query parameter.
- Support for /stories.json
- Support for /metadata.json and the releases field.
Examples of such services:
- [chromatic.com](https://www.chromatic.com/)
#### CHP level 0
The service can serve uploaded Storybooks. There is no special integration with Storybook APIs.
Examples of such services:
- [Netlify](https://www.netlify.com/)
- [S3](https://aws.amazon.com/en/s3/)

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,57 @@
---
title: 'Publish Storybook'
---
Storybook is more than a UI component development tool. Teams also publish Storybook online to review and collaborate on works in progress. That allows developers, designers, and PMs to check if UI looks right without touching code or needing a local dev environment.
### Build Storybook as a static web application
First, well need to build Storybook as a static web application using `build-storybook`, a command thats installed by default.
```shell
yarn build-storybook -o ./path/to/build
```
Storybook will create a static web application at the path you specify. This can be served by any web server. Try it out locally by running:
```shell
npx http-server ./path/to/build
```
### Publish Storybook online
Once your Storybook is built as a static web app it can be deployed to any static site hosting services. The Storybook team uses [Chromatic](https://www.chromatic.com/), a free publishing service made by Storybook maintainers that documents, versions, and indexes your UI components securely in the cloud.
We also maintain [`storybook-deployer`](https://github.com/storybookjs/storybook-deployer) to deploy to GitHub pages or AWS S3.
<video autoPlay muted playsInline loop>
<source
src="storybook-publish-review-optimized.mp4"
type="video/mp4"
/>
</video>
### Review with your team
Publishing Storybook as part of the development process makes it quick and easy to [gather team feedback](https://www.learnstorybook.com/design-systems-for-developers/react/en/review/).
A common method to ask for review is to paste a link to the published Storybook in a pull request or Slack.
If you publish your Storybook to Chromatic, you can use the [UI Review](https://www.chromatic.com/features/publish) feature to automatically scan your PRs for new and updated stories. That makes it easy to identify what changed and give feedback.
![Storybook publishing workflow](./workflow-publish.png)
### Reference external Storybooks
Storybook allows you to browse components from any [Storybook published online](./storybook-composition) inside your local Storybook. It unlocks common workflows that teams often struggle with:
- 👩‍💻 UI developers can quickly reference prior art without switching between Storybooks.
- 🎨 Design systems can expand adoption by composing themselves into their users Storybooks.
- 🛠 Frontend platform can audit how components are used across projects.
![Storybook reference external](./reference-external-storybooks-composition.jpg)
Toggle between multiple versions of Storybook to see how components change between versions. This is useful for design system package authors who maintain many versions at once.
**Requires** a [CHP level 1](./package-composition#chp-level-1) server (such as [chromatic.com](https://www.chromatic.com/)),

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -0,0 +1,54 @@
---
title: 'Snapshot testing'
---
Snapshot tests compare the rendered markup of every story against known baselines. Its an easy way to identify markup changes that trigger rendering errors and warnings.
Storybook is a convenient tool for snapshot testing because every story is essentially a test specification. Any time you write or update a story you get a snapshot test for free.
> Snapshot vs visual tests. Visual tests take screenshots of stories and compare them against known baselines. When used to test appearance, visual tests are often a more robust solution than snapshot tests because verifying markup doesnt test for visual changes.
Storyshots is an [official addon](https://github.com/storybookjs/storybook/tree/master/addons/storyshots/storyshots-core) that enables snapshot testing. Its powered by Jest so youll need to [install that](https://jestjs.io/docs/en/getting-started) first. Continue on if you already have Jest.
Install the addon. **Make sure** the version of Storyshots and your projects Storybook version are identical.
```shell
yarn add --dev @storybook/addon-storyshots
```
Configure Storyshots by adding the following test file to your project:
```js
// storybook.test.js
import initStoryshots from '@storybook/addon-storyshots';
initStoryshots();
```
> You can name the file whatever you like as long as it's picked up by Jest.
Run your first test. Storyshot will recognize all your CSF files (based on [`.storybook/main.js`](../configure/overview#configure-story-rendering)) and produces snapshots.
```shell
yarn test storybook.test.js
```
<div style="background-color:#F8FAFC">
TODO: ask for clarification on this note below. What extra steps?
</div>
> If you are loading stories via `.storybook/main.js`, you will need to follow some more steps to ensure Jest finds them.
This will create an initial set of snapshots inside your Storybook config directory.
![Successfull snapshot tests](./storyshots-pass.png)
When you make changes to your components or stories, run the test again to identify the changes to the rendered markup.
![Failing snapshots](./storyshots-fail.png)
If the changes are intentional we can accept them as new baselines. If the changes are bugs, fix the underlying code then run the snapshot tests again.
Storyshots has many options for advanced use cases; read more in the [addons documentation](https://github.com/storybookjs/storybook/tree/master/addons/storyshots/storyshots-core).

View File

@ -0,0 +1,107 @@
---
title: 'Stories for multiple components'
---
It's useful to write stories that [render two or more components](../writing-stories/introduction#stories-for-two-or-more-components) at once if those components are designed to work together. For example, `ButtonGroups`, `Lists`, and `Page` components.
```js
// List.story.js
import List from './List';
import ListItem from './ListItem';
export default {
component: List,
subcomponents: [ListItem],
title: 'List',
};
export const Empty = (args) => <List {...args} />;
export const OneItem = (args) => (
<List {...args}>
<ListItem />
</List>
);
```
Note that by adding `subcomponents` to the default export, we get an extra pane on the ArgsTable, listing the props of `ListItem`:
![Storybook story with subcomponent argstable](./argstable-subcomponents.png)
The downside of the above approach is that it does not take advantage of Storybook [Args](../writing-stories/args) meaning:
1. You cannot change the stories via the controls panel
2. There is no [args reuse](../writing-stories/introduction#using-args) possible, which makes the stories harder to maintain.
Let's talk about some techniques you can use to mitigate the above, which are especially useful in more complicated situations.
### Reusing subcomponent stories
The simplest change we can make to the above is to reuse the stories of the `ListItem` in the `List`:
```js
// List.story.js
import List from './List';
// Instead of importing the ListItem, we import its stories
import { Unchecked } from './ListItem.stories';
export const OneItem = (args) => (
<List {...args}>
<Unchecked {...Unchecked.args} />
</List>
);
```
By rendering the `Unchecked` story with its args, we are able to reuse the input data from the `ListItem` stories in the `List`.
However, we still arent using args to control the `ListItem` stories, which means we cannot change them with controls and we cannot reuse them in other, more complex component stories.
### Using children as an arg
One way we improve that situation is by pulling the render subcomponent out into a `children` arg:
```js
// List.story.js
const Template = (args) => <List {...args} />
export const OneItem = Template.bind({});
OneItem.args = {
children: <Unchecked {...Unchecked.args} />
}
```
Now that `children` is an arg, we can potentially reuse it in another story. As things stand (we hope to improve this soon) you cannot edit children in a control yet.
### Creating a Template Component
Another option that is more “data”-based is to create a special “story-generating” template component:
```js
// List.story.js
import React from 'react';
import List from './List';
import ListItem from './ListItem';
import { Unchecked } from './ListItem.stories';
const ListTemplate = ({ items, ...args }) => (
<List>
{items.map(item => <ListItem {...item} />)}
</List>
);
export const Empty = ListTemplate.bind({});
Empty.args = { items: [] };
export const OneItem = ListTemplate.bind({});
OneItem.args = { items: [Unchecked.args] };
```
This approach is a little more complex to setup, but it means you can more easily reuse the `args` to each story in a composite component. It also means that you can alter the args to the component with the Controls addon:
<div style="background-color:#F8FAFC">
TODO: mention of a gif in the SB 6.0 doc (needs to be vetted)
</div>

View File

@ -0,0 +1,53 @@
---
title: 'Storybook Composition'
---
Composition allows you to embed components from any Storybook inside your local Storybook.
Its made for teams who adopt Storybook in multiple projects but cant ensure that the projects have the same tech stack or share the same repo.
You can compose any Storybook [published online](./publish-storybook) or running locally no matter the view layer, tech stack, or dependencies.
![Storybook composition](./combine-storybooks.png)
### Compose published Storybooks
In your [`storybook/main.js`](../configure/overview#configure-story-rendering) file add a `refs` field with information about the reference Storybook. Pass in a URL to a statically built Storybook.
```js
//.storybook/main.js
module.exports={
// your Storybook configuration
refs: {
'design-system': {
title: "Storybook Design System",
url: "https://5ccbc373887ca40020446347-yldsqjoxzb.chromatic.com"
}
}`
}
```
### Compose local Storybooks
You can also compose Storybook that are running locally. For instance, if you have a React Storybook and a Angular Storybook running on different ports:
```js
//.storybook/main.js
module.exports={
// your Storybook configuration
refs: {
react: {
title: "React",
url: 'http://localhost:7007'
},
angular: {
title: "Angular",
url: 'http://localhost:7008'
}
}
}
```
This composes the React and Angular Storybooks into your current Storybook. When either code base changes, hot-module-reload will work perfectly. That enables you to develop both frameworks in sync.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 439 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View File

@ -0,0 +1,19 @@
---
title: 'Testing with Storybook'
---
Storybook is a development tool that helps you build components in isolation and record their states as stories. Stories make it easy to explore a component in all its permutations no matter how complex. They also serve as excellent visual test cases.
A story records a way your component can be used. That means your complete set of stories is a catalogue of all the important use cases to test in your component.
The simplest testing method is manual. [Publish](./publish-storybook) your Storybook or run it locally, then look at every story to verify its appearance and behavior. This is appropriate for smaller Storybooks.
![Changing stories with Storybook](./storybook-switch-stories.gif)
As you add more stories, manual testing becomes infeasible. We recommend automating testing to catch bugs and regressions. A complete Storybook testing strategy combines the following techniques to balance coverage, accuracy, and maintainability:
- Manual tests rely on developers to manually look at a component to verify it for correctness. They help us sanity check a components appearance as we build.
- [Unit tests](./unit-testing) verify that the output of a component remains the same given a fixed input. Theyre great for testing the functional qualities of a component.
- [Visual regression tests](./visual-testing) capturing screenshots of every story and comparing them against known baselines. Theyre great for catching UI appearance bugs.
- [Interaction tests](./interaction-testing) will render a story and then interact with it in the browser, asserting things about the way it renders and changes.
- [Snapshot tests](./snapshot-testing) compare the rendered markup of every story against known baselines. This catches markup changes that cause rendering errors and warnings.

View File

@ -0,0 +1,32 @@
---
title: 'Unit testing'
---
Unit tests are useful for verifying functional aspects of components. They verify that the output of a component remains the same given a fixed input.
![Unit testing with a component](./component-unit-testing.gif)
Thanks to the [CSF format](../../formats/component-story-format/), your stories are reusable in unit testing tools. Each [named export](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export) is “renderable” without depending on Storybook. That means your testing framework will also be able to render that story.
Additionally, the Storybook framework packages have an export that makes this easy and doesnt rely on any other Storybook dependencies.
Here is an example of how you can use it in a testing library:
```js
// Button.test.js
import React from 'react';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { renderStory } from '@storybook/react/render';
import { Primary } from './Button.stories';
it('renders the button in the primary state, () => {
render(renderStory(Primary));
expect(screen.getByRole('button')).toHaveTextContent(Primary);
});
```
Unit tests can be brittle and expensive to maintain for _every_ component. We recommend combining unit tests with other testing methods like [visual regression testing](./visual-testing) for comprehensive coverage with less maintenance work.

View File

@ -0,0 +1,29 @@
---
title: 'Visual Testing'
---
Visual tests, also called visual regression tests, catch bugs in UI appearance. They work by taking screenshots of every story and comparing them commit-to-commit to identify changes.
This is ideal for verifying what the user sees:
- 🖼️ Layout.
- 🎨 Color.
- 📐 Size.
- 🔳 Contrast.
Storybook is a fantastic tool for visual testing because every story is essentially a test specification. Any time you write or update a story you get a spec for free.
> Visual vs snapshot tests. Snapshot tests compare the rendered markup of every story against known baselines. When used to test how things look, snapshot tests generate a lot of false positives because code changes dont always yield visual changes.
![Visually testing a component in Storybook](./component-visual-testing.gif)
There are [many tools](https://github.com/mojoaxel/awesome-regression-testing) for visual testing. Storybook uses [Chromatic](https://www.chromatic.com), a visual testing service made by Storybook maintainers to run tests in the cloud across browsers.
This prevents UI bugs in our:
- 🖥️ [Application](https://www.chromatic.com/library?appId=5a375b97f4b14f0020b0cda3).
- ⚙️ [Design system](https://www.chromatic.com/library?appId=5ccbc373887ca40020446347)
- 🔗 [Website](https://www.chromatic.com/library?appId=5be26744d2f6250024a9117d).
We also maintain [StoryShots](https://github.com/storybookjs/storybook/tree/master/addons/storyshots), a snapshot testing addon that integrates with [jest-image-snapshot](https://github.com/storybookjs/storybook/tree/master/addons/storyshots#configure-storyshots-for-image-snapshots).

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB