Skip to content

Commit 1c49ad3

Browse files
authored
Merge branch 'chromaui:master' into intro-to-storybook-2024
2 parents 98a4ae8 + 1003415 commit 1c49ad3

40 files changed

+813
-575
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -244,14 +244,14 @@ Currently, the [Intro to Storybook tutorial](https://storybook.js.org/tutorials/
244244
| | ZH-TW ||
245245
| | Mainland Chinese ||
246246
| | Dutch ||
247-
| | Korean | |
247+
| | Korean | |
248248
| | Japanese ||
249249
| | French ||
250250
| | Italian ||
251251
| | German ||
252252
| React Native | English ||
253253
| | Spanish ||
254-
| | Portuguese | |
254+
| | Portuguese | |
255255
| Vue | English ||
256256
| | Italian ||
257257
| | Spanish ||

content/intro-to-storybook/angular/en/composite-component.md

+9-12
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ Next, create `Tasklist`’s test states in the story file.
6868
```ts:title=src/app/components/task-list.stories.ts
6969
import type { Meta, StoryObj } from '@storybook/angular';
7070

71-
import { argsToTemplate, componentWrapperDecorator, moduleMetadata } from '@storybook/angular';
71+
import { componentWrapperDecorator, moduleMetadata } from '@storybook/angular';
7272

7373
import { CommonModule } from '@angular/common';
7474

@@ -92,14 +92,9 @@ const meta: Meta<TaskListComponent> = {
9292
(story) => `<div style="margin: 3em">${story}</div>`
9393
),
9494
],
95-
render: (args: TaskListComponent) => ({
96-
props: {
97-
...args,
98-
onPinTask: TaskStories.actionsData.onPinTask,
99-
onArchiveTask: TaskStories.actionsData.onArchiveTask,
100-
},
101-
template: `<app-task-list ${argsToTemplate(args)}></app-task-list>`,
102-
}),
95+
args: {
96+
...TaskStories.ActionsData,
97+
},
10398
};
10499
export default meta;
105100
type Story = StoryObj<TaskListComponent>;
@@ -146,10 +141,12 @@ export const Empty: Story = {
146141
```
147142

148143
<div class="aside">
149-
💡 <a href="https://storybook.js.org/docs/angular/writing-stories/decorators"><b>Decorators</b></a> are a way to provide arbitrary wrappers to stories. In this case we’re using a decorator key on the default export to add some <code>padding</code> around the rendered component. They can also be used to wrap stories in “providers”–-i.e., library components that set some context.
144+
145+
💡[**Decorators**](https://storybook.js.org/docs/writing-stories/decorators) are a way to provide arbitrary wrappers to stories. In this case we’re using a decorator key on the default export to add some `margin` around the rendered component. They can also be used to wrap stories in “providers”–-i.e., library components that set some context.
146+
150147
</div>
151148

152-
By importing `TaskStories`, we were able to [compose](https://storybook.js.org/docs/angular/writing-stories/args#args-composition) the arguments (args for short) in our stories with minimal effort. That way, the data and actions (mocked callbacks) expected by both components are preserved.
149+
By importing `TaskStories`, we were able to [compose](https://storybook.js.org/docs/writing-stories/args#args-composition) the arguments (args for short) in our stories with minimal effort. That way, the data and actions (mocked callbacks) expected by both components are preserved.
153150

154151
Now check Storybook for the new `TaskList` stories.
155152

@@ -236,7 +233,7 @@ The added markup results in the following UI:
236233

237234
<video autoPlay muted playsInline loop>
238235
<source
239-
src="/intro-to-storybook/finished-tasklist-states-6-0.mp4"
236+
src="/intro-to-storybook/finished-tasklist-states-7-0.mp4"
240237
type="video/mp4"
241238
/>
242239
</video>

content/intro-to-storybook/angular/en/conclusion.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Storybook is a powerful tool for React, React Native, Vue, Angular, Svelte and m
1616

1717
Want to dive deeper? Here are helpful resources.
1818

19-
- [**Official Storybook documentation**](https://storybook.js.org/docs/angular/get-started/introduction) has API documentation, community links, and the addon gallery.
19+
- [**Official Storybook documentation**](https://storybook.js.org/docs/get-started/install?renderer=angular) has API documentation, community links, and the addon gallery.
2020

2121
- [**UI Testing Playbook**](https://storybook.js.org/blog/ui-testing-playbook/) highlights workflow best practices used by high-velocity teams at Twilio, Adobe, Peloton, and Shopify.
2222

content/intro-to-storybook/angular/en/data.md

+5-10
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ In `src/app/components/pure-task-list.component.ts`:
139139
```diff:title=src/app/components/pure-task-list.component.ts
140140
import { Component, Input, Output, EventEmitter } from '@angular/core';
141141
import { Task } from '../models/task.model';
142+
142143
@Component({
143144
- selector:'app-task-list',
144145
+ selector: 'app-pure-task-list',
@@ -304,12 +305,11 @@ The reason to keep the presentational version of the `TaskList` separate is that
304305
```ts:title=src/app/components/pure-task-list.stories.ts
305306
import type { Meta, StoryObj } from '@storybook/angular';
306307

307-
import { argsToTemplate, componentWrapperDecorator, moduleMetadata } from '@storybook/angular';
308+
import { componentWrapperDecorator, moduleMetadata } from '@storybook/angular';
308309

309310
import { CommonModule } from '@angular/common';
310311

311312
import PureTaskListComponent from './pure-task-list.component';
312-
313313
import TaskComponent from './task.component';
314314

315315
import * as TaskStories from './task.stories';
@@ -329,14 +329,9 @@ const meta: Meta<PureTaskListComponent> = {
329329
(story) => `<div style="margin: 3em">${story}</div>`
330330
),
331331
],
332-
render: (args: PureTaskListComponent) => ({
333-
props: {
334-
...args,
335-
onPinTask: TaskStories.actionsData.onPinTask,
336-
onArchiveTask: TaskStories.actionsData.onArchiveTask,
337-
},
338-
template: `<app-pure-task-list ${argsToTemplate(args)}></app-pure-task-list>`,
339-
}),
332+
args: {
333+
...TaskStories.ActionsData,
334+
},
340335
};
341336
export default meta;
342337
type Story = StoryObj<PureTaskListComponent>;

content/intro-to-storybook/angular/en/deploy.md

+14-6
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Running `npm run build-storybook` will output a static Storybook in the `storybo
1515

1616
## Publish Storybook
1717

18-
This tutorial uses <a href="https://www.chromatic.com/?utm_source=storybook_website&utm_medium=link&utm_campaign=storybook">Chromatic</a>, a free publishing service made by the Storybook maintainers. It allows us to deploy and host our Storybook safely and securely in the cloud.
18+
This tutorial uses [Chromatic](https://www.chromatic.com/?utm_source=storybook_website&utm_medium=link&utm_campaign=storybook), a free publishing service made by the Storybook maintainers. It allows us to deploy and host our Storybook safely and securely in the cloud.
1919

2020
### Set up a repository in GitHub
2121

@@ -94,20 +94,28 @@ jobs:
9494
runs-on: ubuntu-latest
9595
# Job steps
9696
steps:
97-
- uses: actions/checkout@v3
97+
- uses: actions/checkout@v4
9898
with:
9999
fetch-depth: 0
100-
- run: yarn
101-
#👇 Adds Chromatic as a step in the workflow
102-
- uses: chromaui/action@v1
100+
- uses: actions/setup-node@v4
101+
with:
102+
node-version: 20
103+
- name: Install dependencies
104+
run: npm ci
105+
- name: "Run Chromatic"
106+
uses: chromaui/action@latest
103107
# Options required for Chromatic's GitHub Action
104108
with:
105109
#👇 Chromatic projectToken, see https://storybook.js.org/tutorials/intro-to-storybook/angular/en/deploy/ to obtain it
106110
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
107111
token: ${{ secrets.GITHUB_TOKEN }}
108112
```
109113

110-
<div class="aside"><p>💡 For brevity purposes <a href=" https://docs.github.com/en/actions/security-guides/encrypted-secrets#creating-encrypted-secrets-for-a-repository ">GitHub secrets</a> weren't mentioned. Secrets are secure environment variables provided by GitHub so that you don't need to hard code the <code>project-token</code>.</p></div>
114+
<div class="aside">
115+
116+
💡 For brevity purposes [GitHub secrets](https://docs.github.com/en/actions/security-guides/encrypted-secrets#creating-encrypted-secrets-for-a-repository) weren't mentioned. Secrets are secure environment variables provided by GitHub so that you don't need to hard code the `project-token`.
117+
118+
</div>
111119

112120
### Commit the action
113121

content/intro-to-storybook/angular/en/get-started.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ Now we can quickly check that the various environments of our application are wo
3333

3434
```shell:clipboard=false
3535
# Start the component explorer on port 6006:
36-
ng run taskbox:storybook
36+
npm run storybook
3737
3838
# Run the frontend app proper on port 4200:
3939
ng serve

content/intro-to-storybook/angular/en/screen.md

+14-10
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,9 @@ One way to sidestep this problem is to never render container components anywher
291291
However, developers **will** inevitably need to render containers further down the component hierarchy. If we want to render most or all of the app in Storybook (we do!), we need a solution to this issue.
292292

293293
<div class="aside">
294-
💡 As an aside, passing data down the hierarchy is a legitimate approach, especially when using <a href="http://graphql.org/">GraphQL</a>. It’s how we have built <a href="https://www.chromatic.com/?utm_source=storybook_website&utm_medium=link&utm_campaign=storybook">Chromatic</a> alongside 800+ stories.
294+
295+
💡 As an aside, passing data down the hierarchy is a legitimate approach, especially when using [GraphQL](http://graphql.org/). It’s how we have built [Chromatic](https://www.chromatic.com/?utm_source=storybook_website&utm_medium=link&utm_campaign=storybook) alongside 800+ stories.
296+
295297
</div>
296298

297299
## Supplying context with decorators
@@ -352,15 +354,15 @@ Cycling through states in Storybook makes it easy to test we’ve done this corr
352354
/>
353355
</video>
354356

355-
## Interaction tests
357+
## Component tests
356358

357359
So far, we've been able to build a fully functional application from the ground up, starting from a simple component up to a screen and continuously testing each change using our stories. But each new story also requires a manual check on all the other stories to ensure the UI doesn't break. That's a lot of extra work.
358360

359361
Can't we automate this workflow and test our component interactions automatically?
360362

361-
### Write an interaction test using the play function
363+
### Write a component test using the play function
362364

363-
Storybook's [`play`](https://storybook.js.org/docs/angular/writing-stories/play-function) and [`@storybook/addon-interactions`](https://storybook.js.org/docs/angular/writing-tests/interaction-testing) help us with that. A play function includes small snippets of code that run after the story renders.
365+
Storybook's [`play`](https://storybook.js.org/docs/writing-stories/play-function) and [`@storybook/addon-interactions`](https://storybook.js.org/docs/writing-tests/component-testing) help us with that. A play function includes small snippets of code that run after the story renders.
364366

365367
The play function helps us verify what happens to the UI when tasks are updated. It uses framework-agnostic DOM APIs, which means we can write stories with the play function to interact with the UI and simulate human behavior no matter the frontend framework.
366368

@@ -441,9 +443,9 @@ Check your newly-created story. Click the `Interactions` panel to see the list o
441443

442444
With Storybook's play function, we were able to sidestep our problem, allowing us to interact with our UI and quickly check how it responds if we update our tasks—keeping the UI consistent at no extra manual effort.
443445

444-
But, if we take a closer look at our Storybook, we can see that it only runs the interaction tests when viewing the story. Therefore, we'd still have to go through each story to run all checks if we make a change. Couldn't we automate it?
446+
But, if we take a closer look at our Storybook, we can see that it only runs the component tests when viewing the story. Therefore, we'd still have to go through each story to run all checks if we make a change. Couldn't we automate it?
445447

446-
The good news is that we can! Storybook's [test runner](https://storybook.js.org/docs/angular/writing-tests/test-runner) allows us to do just that. It's a standalone utility—powered by [Playwright](https://playwright.dev/)—that runs all our interactions tests and catches broken stories.
448+
The good news is that we can! Storybook's [test runner](https://storybook.js.org/docs/writing-tests/test-runner) allows us to do just that. It's a standalone utility—powered by [Playwright](https://playwright.dev/)—that runs all our interactions tests and catches broken stories.
447449

448450
Let's see how it works! Run the following command to install it:
449451

@@ -464,13 +466,15 @@ Next, update your `package.json` `scripts` and add a new test task:
464466
Finally, with your Storybook running, open up a new terminal window and run the following command:
465467

466468
```shell
467-
npm run test-storybook -- --watch
469+
npm run test-storybook -- --url http://localhost:6006/ -- --watch
468470
```
469471

470472
<div class="aside">
471-
💡 Interaction testing with the play function is a fantastic way to test your UI components. It can do much more than we've seen here; we recommend reading the <a href="https://storybook.js.org/docs/angular/writing-tests/interaction-testing">official documentation</a> to learn more about it.
472-
<br />
473-
For an even deeper dive into testing, check out the <a href="/ui-testing-handbook">Testing Handbook</a>. It covers testing strategies used by scaled-front-end teams to supercharge your development workflow.
473+
474+
💡 Component testing with the play function is a fantastic way to test your UI components. It can do much more than we've seen here; we recommend reading the [official documentation](https://storybook.js.org/docs/writing-tests/component-testing) to learn more about it.
475+
476+
For an even deeper dive into testing, check out the [Testing Handbook](/ui-testing-handbook). It covers testing strategies used by scaled-front-end teams to supercharge your development workflow.
477+
474478
</div>
475479

476480
![Storybook test runner successfully runs all tests](/intro-to-storybook/storybook-test-runner-execution.png)

content/intro-to-storybook/angular/en/simple-component.md

+16-28
Original file line numberDiff line numberDiff line change
@@ -66,30 +66,24 @@ Below we build out Task’s three test states in the story file:
6666
```ts:title=src/app/components/task.stories.ts
6767
import type { Meta, StoryObj } from '@storybook/angular';
6868

69-
import { argsToTemplate } from '@storybook/angular';
70-
71-
import { action } from '@storybook/addon-actions';
69+
import { fn } from '@storybook/test';
7270

7371
import TaskComponent from './task.component';
7472

75-
export const actionsData = {
76-
onPinTask: action('onPinTask'),
77-
onArchiveTask: action('onArchiveTask'),
73+
export const ActionsData = {
74+
onArchiveTask: fn(),
75+
onPinTask: fn(),
7876
};
7977

8078
const meta: Meta<TaskComponent> = {
8179
title: 'Task',
8280
component: TaskComponent,
81+
//👇 Our exports that end in "Data" are not stories.
8382
excludeStories: /.*Data$/,
8483
tags: ['autodocs'],
85-
render: (args: TaskComponent) => ({
86-
props: {
87-
...args,
88-
onPinTask: actionsData.onPinTask,
89-
onArchiveTask: actionsData.onArchiveTask,
90-
},
91-
template: `<app-task ${argsToTemplate(args)}></app-task>`,
92-
}),
84+
args: {
85+
...ActionsData,
86+
},
9387
};
9488

9589
export default meta;
@@ -125,7 +119,9 @@ export const Archived: Story = {
125119
```
126120

127121
<div class="aside">
128-
💡 <a href="https://storybook.js.org/docs/angular/essentials/actions"><b>Actions</b></a> help you verify interactions when building UI components in isolation. Oftentimes you won't have access to the functions and state you have in context of the app. Use <code>action()</code> to stub them in.
122+
123+
💡 [**Actions**](https://storybook.js.org/docs/essentials/actions) help you verify interactions when building UI components in isolation. Oftentimes you won't have access to the functions and state you have in context of the app. Use `fn()` to stub them in.
124+
129125
</div>
130126

131127
There are two basic levels of organization in Storybook: the component and its child stories. Think of each story as a permutation of a component. You can have as many stories per component as you need.
@@ -141,16 +137,15 @@ To tell Storybook about the component we are documenting, we create a `default`
141137
- `title` -- how to group or categorize the component in the Storybook sidebar
142138
- `tags` -- to automatically generate documentation for our components
143139
- `excludeStories`-- additional information required by the story but should not be rendered in Storybook
144-
- `render` -- a custom [render function](https://storybook.js.org/docs/angular/api/csf#custom-render-functions) that allows us how the component is rendered in Storybook
145-
- `argsToTemplate` -- a helper function that converts the args to property and event bindings for the component, providing robust workflow support for Storybook's controls and the component's inputs and outputs
140+
- `args` -- define the action [args](https://storybook.js.org/docs/essentials/actions#action-args) that the component expects to mock out the custom events
146141

147-
To define our stories, we'll use Component Story Format 3 (also known as [CSF3](https://storybook.js.org/docs/angular/api/csf) ) to build out each of our test cases. This format is designed to build out each of our test cases in a concise way. By exporting an object containing each component state, we can define our tests more intuitively and author and reuse stories more efficiently.
142+
To define our stories, we'll use Component Story Format 3 (also known as [CSF3](https://storybook.js.org/docs/api/csf) ) to build out each of our test cases. This format is designed to build out each of our test cases in a concise way. By exporting an object containing each component state, we can define our tests more intuitively and author and reuse stories more efficiently.
148143

149-
Arguments or [`args`](https://storybook.js.org/docs/angular/writing-stories/args) for short, allow us to live-edit our components with the controls addon without restarting Storybook. Once an [`args`](https://storybook.js.org/docs/angular/writing-stories/args) value changes, so does the component.
144+
Arguments or [`args`](https://storybook.js.org/docs/writing-stories/args) for short, allow us to live-edit our components with the controls addon without restarting Storybook. Once an [`args`](https://storybook.js.org/docs/writing-stories/args) value changes, so does the component.
150145

151-
`action()` allows us to create a callback that appears in the **actions** panel of the Storybook UI when clicked. So when we build a pin button, we’ll be able to determine if a button click is successful in the UI.
146+
`fn()` allows us to create a callback that appears in the **Actions** panel of the Storybook UI when clicked. So when we build a pin button, we’ll be able to determine if a button click is successful in the UI.
152147

153-
As we need to pass the same set of actions to all permutations of our component, it is convenient to bundle them up into a single `actionsData` variable and pass them into our story definition each time. Another nice thing about bundling the `actionsData` that a component needs is that you can `export` them and use them in stories for components that reuse this component, as we'll see later.
148+
As we need to pass the same set of actions to all permutations of our component, it is convenient to bundle them up into a single `ActionsData` variable and pass them into our story definition each time. Another nice thing about bundling the `ActionsData` that a component needs is that you can `export` them and use them in stories for components that reuse this component, as we'll see later.
154149

155150
When creating a story, we use a base `task` arg to build out the shape of the task the component expects. Typically modeled from what the actual data looks like. Again, `export`-ing this shape will enable us to reuse it in later stories, as we'll see.
156151

@@ -172,9 +167,6 @@ const config: StorybookConfig = {
172167
name: '@storybook/angular',
173168
options: {},
174169
},
175-
docs: {
176-
autodocs: 'tag',
177-
},
178170
};
179171
export default config;
180172
```
@@ -331,12 +323,8 @@ const config: StorybookConfig = {
331323
name: '@storybook/angular',
332324
options: {},
333325
},
334-
docs: {
335-
autodocs: 'tag',
336-
},
337326
};
338327
export default config;
339-
340328
```
341329

342330
Finally, restart your Storybook to see the new addon enabled in the UI.

0 commit comments

Comments
 (0)