Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update intro-to-storybook/ja #765

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
150 changes: 98 additions & 52 deletions content/intro-to-storybook/react/ja/composite-component.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,21 @@ commit: '429780a'

## TaskList (タスクリスト)

Taskbox はピン留めされたタスクを通常のタスクより上部に表示することで強調します。これにより `TaskList` に、タスクのリストが、通常のタスクのみである場合と、ピン留めされたタスクとの組み合わせである場合という、ストーリーを追加するべき 2 つのバリエーションができます。
Taskbox はピン留めされたタスクを通常のタスクより上部に表示することで強調します。これにより `TaskList` に、タスクのリストが通常のタスクのみである場合と、ピン留めされたタスクとの組み合わせである場合というストーリーを追加するべき 2 つのバリエーションができます。

![通常のタスクとピン留めされたタスク](/intro-to-storybook/tasklist-states-1.png)

`Task` のデータは非同期的に送信されるので、接続がないことを示すため、読み込み中の状態****必要となります。さらにタスクがない場合に備え、空の状態も必要です。
`Task` のデータは非同期に送信されるので、接続がないことを示すため、読み込み中の状態**も併せて**必要となります。さらにタスクがない場合に備え、空の状態も必要です。

![空の状態と読み込み中の状態](/intro-to-storybook/tasklist-states-2.png)

## セットアップする

複合的なコンポーネントも基本的なコンポーネントと大きな違いはありません。`TaskList` のコンポーネントとそのストーリーファイル、`src/components/TaskList.js` と `src/components/TaskList.stories.js` を作成しましょう。
複合的なコンポーネントも基本的なコンポーネントと大きな違いはありません。`TaskList` のコンポーネントとそのストーリーファイル、`src/components/TaskList.jsx` と `src/components/TaskList.stories.jsx` を作成しましょう。

まずは `TaskList` の大まかな実装から始めます。前の章で作成した `Task` コンポーネントをインポートし、属性とアクションを入力として渡します。

```js:title=src/components/TaskList.js
```jsx:title=src/components/TaskList.jsx
import React from 'react';

import Task from './Task';
Expand Down Expand Up @@ -54,61 +54,61 @@ export default function TaskList({ loading, tasks, onPinTask, onArchiveTask }) {

次に `Tasklist` のテスト状態をストーリーファイルに記述します。

```js:title=src/components/TaskList.stories.js
import React from 'react';

```jsx:title=src/components/TaskList.stories.jsx
import TaskList from './TaskList';

import * as TaskStories from './Task.stories';

export default {
component: TaskList,
title: 'TaskList',
decorators: [story => <div style={{ padding: '3rem' }}>{story()}</div>],
decorators: [(story) => <div style={{ padding: '3rem' }}>{story()}</div>],
tags: ['autodocs'],
};

const Template = args => <TaskList {...args} />;

export const Default = Template.bind({});
Default.args = {
// Shaping the stories through args composition.
// The data was inherited from the Default story in Task.stories.js.
tasks: [
{ ...TaskStories.Default.args.task, id: '1', title: 'Task 1' },
{ ...TaskStories.Default.args.task, id: '2', title: 'Task 2' },
{ ...TaskStories.Default.args.task, id: '3', title: 'Task 3' },
{ ...TaskStories.Default.args.task, id: '4', title: 'Task 4' },
{ ...TaskStories.Default.args.task, id: '5', title: 'Task 5' },
{ ...TaskStories.Default.args.task, id: '6', title: 'Task 6' },
],
export const Default = {
args: {
// Shaping the stories through args composition.
// The data was inherited from the Default story in Task.stories.jsx.
tasks: [
{ ...TaskStories.Default.args.task, id: '1', title: 'Task 1' },
{ ...TaskStories.Default.args.task, id: '2', title: 'Task 2' },
{ ...TaskStories.Default.args.task, id: '3', title: 'Task 3' },
{ ...TaskStories.Default.args.task, id: '4', title: 'Task 4' },
{ ...TaskStories.Default.args.task, id: '5', title: 'Task 5' },
{ ...TaskStories.Default.args.task, id: '6', title: 'Task 6' },
],
},
};

export const WithPinnedTasks = Template.bind({});
WithPinnedTasks.args = {
// Shaping the stories through args composition.
// Inherited data coming from the Default story.
tasks: [
...Default.args.tasks.slice(0, 5),
{ id: '6', title: 'Task 6 (pinned)', state: 'TASK_PINNED' },
],
export const WithPinnedTasks = {
args: {
tasks: [
...Default.args.tasks.slice(0, 5),
{ id: '6', title: 'Task 6 (pinned)', state: 'TASK_PINNED' },
],
},
};

export const Loading = Template.bind({});
Loading.args = {
tasks: [],
loading: true,
export const Loading = {
args: {
tasks: [],
loading: true,
},
};

export const Empty = Template.bind({});
Empty.args = {
// Shaping the stories through args composition.
// Inherited data coming from the Loading story.
...Loading.args,
loading: false,
export const Empty = {
args: {
// Shaping the stories through args composition.
// Inherited data coming from the Loading story.
...Loading.args,
loading: false,
},
};
```

<div class="aside">
💡 <a href="https://storybook.js.org/docs/react/writing-stories/decorators"><b>デコレーター</b></a>を使ってストーリーに任意のラッパーを設定できます。上記のコードでは、<code>decorators</code> というキーをデフォルトエクスポートに追加し、描画するコンポーネントの周りに <code>padding</code> を設定してます。ストーリーで使用する「プロバイダー」(例えば、React のコンテキストを設定するライブラリコンポーネントなど) を使うためにも使用します。
💡 <a href="https://storybook.js.org/docs/react/writing-stories/decorators"><b>デコレーター</b></a>を使ってストーリーに任意のラッパーを設定できます。上記のコードでは、<code>decorators</code> というキーをデフォルトエクスポートに追加し、描画するコンポーネントの周りに <code>padding</code> を設定しています。ストーリーで使用する「プロバイダー」(例えば、React のコンテキストを設定するライブラリコンポーネントなど) を使うためにも使用します。
</div>

`TaskStories` をインポートすることで、ストーリーに必要な引数 (args) を最小限の労力で[組み合わせる](https://storybook.js.org/docs/react/writing-stories/args#args-composition)ことができます。そうすることで、2 つのコンポーネントが想定するデータとアクション (呼び出しのモック) の一貫性が保たれます。
Expand All @@ -117,16 +117,16 @@ Empty.args = {

<video autoPlay muted playsInline loop>
<source
src="/intro-to-storybook/inprogress-tasklist-states-6-0.mp4"
src="/intro-to-storybook/inprogress-tasklist-states-7-0.mp4"
type="video/mp4"
/>
</video>

## 状態を作りこむ

今のコンポーネントはまだ粗削りですが、ストーリーは見えています。単に `.list-items` だけのためにラッパーを作るのは単純すぎると思うかもしれません。実際にその通りです。ほとんどの場合単なるラッパーのためだけに新しいコンポーネントは作りません。`TaskList` の**本当の複雑さ**は `withPinnedTasks`、`loading`、`empty` といったエッジケースに現れているのです
今のコンポーネントはまだ粗削りですが、ストーリーは見えています。単に `.list-items` だけのためにラッパーを作るのは単純すぎると思うかもしれません。実際、その通りです。ほとんどの場合、単なるラッパーのためだけに新しいコンポーネントは作りません。`TaskList` の**本当の複雑さ**は `withPinnedTasks`、`loading`、`empty` といったエッジケース(ユーザーが遭遇する可能性のあるまれなバグ)に現れているのです

```js:title=src/components/TaskList.js
```jsx:title=src/components/TaskList.jsx
import React from 'react';

import Task from './Task';
Expand Down Expand Up @@ -161,16 +161,16 @@ export default function TaskList({ loading, tasks, onPinTask, onArchiveTask }) {
<div className="list-items" key={"empty"} data-testid="empty">
<div className="wrapper-message">
<span className="icon-check" />
<div className="title-message">You have no tasks</div>
<div className="subtitle-message">Sit back and relax</div>
<p className="title-message">You have no tasks</p>
<p className="subtitle-message">Sit back and relax</p>
</div>
</div>
);
}

const tasksInOrder = [
...tasks.filter((t) => t.state === "TASK_PINNED"),
...tasks.filter((t) => t.state !== "TASK_PINNED"),
...tasks.filter((t) => t.state === 'TASK_PINNED'),
...tasks.filter((t) => t.state !== 'TASK_PINNED'),
];
return (
<div className="list-items">
Expand All @@ -182,11 +182,11 @@ export default function TaskList({ loading, tasks, onPinTask, onArchiveTask }) {
}
```

追加したマークアップで UI は以下のようになります:
追加したマークアップで UI は以下のようになります

<video autoPlay muted playsInline loop>
<source
src="/intro-to-storybook/finished-tasklist-states-6-0.mp4"
src="/intro-to-storybook/finished-tasklist-states-7-0.mp4"
type="video/mp4"
/>
</video>
Expand All @@ -197,14 +197,60 @@ export default function TaskList({ loading, tasks, onPinTask, onArchiveTask }) {

コンポーネントが大きくなるにつれ、入力の要件も増えていきます。`TaskList` のプロパティの要件を定義しましょう。`Task` が子供のコンポーネントなので、`Task` を表示するのに正しいデータ構造が渡されていることを確認しましょう。時間を節約するため、前の章で `Task` に定義した `propTypes` を再利用しましょう。

```diff:title=src/components/TaskList.js
```diff:title=src/components/TaskList.jsx
import React from 'react';
+ import PropTypes from 'prop-types';

import Task from './Task';

export default function TaskList({ loading, tasks, onPinTask, onArchiveTask }) {
...
const events = {
onPinTask,
onArchiveTask,
};
const LoadingRow = (
<div className="loading-item">
<span className="glow-checkbox" />
<span className="glow-text">
<span>Loading</span> <span>cool</span> <span>state</span>
</span>
</div>
);
if (loading) {
return (
<div className="list-items" data-testid="loading" key={"loading"}>
{LoadingRow}
{LoadingRow}
{LoadingRow}
{LoadingRow}
{LoadingRow}
{LoadingRow}
</div>
);
}
if (tasks.length === 0) {
return (
<div className="list-items" key={"empty"} data-testid="empty">
<div className="wrapper-message">
<span className="icon-check" />
<p className="title-message">You have no tasks</p>
<p className="subtitle-message">Sit back and relax</p>
</div>
</div>
);
}

const tasksInOrder = [
...tasks.filter((t) => t.state === 'TASK_PINNED'),
...tasks.filter((t) => t.state !== 'TASK_PINNED'),
];
return (
<div className="list-items">
{tasksInOrder.map((task) => (
<Task key={task.id} task={task} {...events} />
))}
</div>
);
}

+ TaskList.propTypes = {
Expand Down
16 changes: 8 additions & 8 deletions content/intro-to-storybook/react/ja/conclusion.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,30 @@ description: '今までの知識をまとめて、Storybook のテクニック

Storybook で最初の UI を作成しましたね。お疲れ様でした!ここまでの章で UI コンポーネントを作成し、複合させ、テストし、デプロイする方法を学びました。同じように進めていれば、リポジトリーとデプロイされた Storybook は以下のリンクと同じようになっていることでしょう。

[📕 **GitHub のリポジトリー: chromaui/learnstorybook-code**](https://github.com/chromaui/learnstorybook-code)
[📕 **GitHub のリポジトリ: chromaui/learnstorybook-code**](https://github.com/chromaui/learnstorybook-code)
<br/>
[🌎 **デプロイされた Storybook**](https://master--5ccbe484c994280020b6d128.chromatic.com)

Storybook は React、React Native、Vue、Angular、Svelte、その他のフレームワークにとって強力なツールです。開発者コミュニティーも活発でアドオンも充実しています。このチュートリアルで紹介した内容は、Storybook で出来ることの一部にすぎません。一度 Storybook を導入すれば、強固な UI を効率的に作れることに驚くことでしょう
Storybook は React、React Native、Vue、Angular、Svelte、その他のフレームワークにとって強力なツールです。開発者コミュニティーも活発でアドオンも充実しています。このチュートリアルで紹介した内容は、Storybook でできることのほんの一部にすぎません。一度 Storybook を導入すれば、強固な UI を効率的に作れることにきっと驚くことでしょう

## さらに学ぶには

もっと深く掘り下げたいですか?役に立つリソースを紹介します
もっと深く掘り下げたい方のために役に立つリソースを紹介します

- [**Storybook の公式ドキュメント**](https://storybook.js.org/docs/react/get-started/introduction)には API ドキュメント、コミュニティのリンク、アドオンのギャラリーがあります。

- [**楽しい Storybook のワークフロー**](https://www.chromatic.com/blog/the-delightful-storybook-workflow)では Squarespace や、メジャーリーグサッカー、ディスカバリーネットワーク、 Apollo GraphQL といった効率の良いチームにおけるワークフローのベストプラクティスを紹介しています
- [**楽しい Storybook のワークフロー**](https://www.chromatic.com/blog/the-delightful-storybook-workflow)では Twilio、Adobe、Peloton、Shopifyといった効率の良いチームにおけるワークフローのベストプラクティスを紹介しています

- [**視覚的なテストのハンドブック**](https://storybook.js.org/tutorials/visual-testing-handbook)では、コンポーネントを Storybook で視覚的にテストする方法を掘り下げています。無料の 31 ページある eBook です。
- [**視覚的なテストのハンドブック (Visual Testing Handbook)**](https://storybook.js.org/tutorials/visual-testing-handbook)では、コンポーネントを Storybook で視覚的にテストする方法を掘り下げています。無料の 31 ページある eBook です。

- [**Storybook Discord**](https://discord.gg/UUt2PJb) では Storybook のコミュニティに参加できます。他の Storybook ユーザーと協力しましょう。
- [**Storybook Discord**](https://discord.gg/UUt2PJb) ではStorybook のコミュニティに参加できます。他の Storybook ユーザーと協力しましょう。

- [**Storybook ブログ**](https://storybook.js.org/blog)ではリリース情報や、UI 開発のワークフローを合理的にするための機能を紹介します。

## 誰が Intro to Storybook チュートリアルを作成しているのでしょうか?

文書や、コード、製作は [Chromatic](https://www.chromatic.com/?utm_source=storybook_website&utm_medium=link&utm_campaign=storybook) の貢献です。このチュートリアルは Chromatic の [GraphQL + React tutorial series](https://www.chromatic.com/blog/graphql-react-tutorial-part-1-6) を参考にしています。
文書や、コード、製作は [Chromatic](https://www.chromatic.com/?utm_source=storybook_website&utm_medium=link&utm_campaign=storybook) の貢献によるものです。このチュートリアルは Chromatic の [GraphQL + React tutorial series](https://www.chromatic.com/blog/graphql-react-tutorial-part-1-6) を参考にしています。

このようなチュートリアルや記事をお望みならば、Storybook のメーリングリストにサインアップしてください
このようなチュートリアルや記事をさらに読みたい場合は、Storybook のメーリングリストに登録することをオススメします

<iframe style="height:400px;width:100%;max-width:800px;margin:0px auto;" src="https://upscri.be/d42fc0?as_embed"></iframe>
6 changes: 3 additions & 3 deletions content/intro-to-storybook/react/ja/contribute.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ title: '貢献する'
description: 'Storybook を世界に広めましょう'
---

Learn Storybook に協力してください!文法や句読点など小さなことなら、プルリクエストを送ってください。もし大きなことなら [GitHub の issue を追加](https://github.com/chromaui/learnstorybook.com/issues)して議論しましょう。
このチュートリアルの作成にご協力ください!文法や句読点などの些細な問題であれば、プルリクエストを送ってください。もし大規模な問題であれば、 [GitHub の issue を追加](https://github.com/chromaui/learnstorybook.com/issues)して議論しましょう。

Learn Storybook はコミュニティによって作成・運営されていますので、最新の状態を保ち、荒い部分を削るには皆さんの協力が必要です。どんな協力でも歓迎します
StoryBookのチュートリアルはコミュニティによって作成・運営されていますので、最新の状態を保ち、荒い部分を削るには皆さんのご協力が不可欠です。どのような協力でも歓迎します

## 翻訳

Storybook をすべての人が使用できるように、このチュートリアルを他の言語に翻訳するのを手伝ってください。中国語やスペイン語は特に歓迎します。興味があれば[この issue](https://github.com/chromaui/learnstorybook.com/issues/3) にコメントしてください。
Storybook をすべての人が使用できるように、このチュートリアルの他言語への翻訳にお力を貸してください。中国語やスペイン語はとくに歓迎します。ご興味があれば[この issue](https://github.com/chromaui/learnstorybook.com/issues/3) にコメントしてください。
Loading