Skip to content

Commit

Permalink
update 2024-07-09-controlled-components-in-storybook
Browse files Browse the repository at this point in the history
  • Loading branch information
yuheiy committed Jul 9, 2024
1 parent b62c76a commit bc278ed
Showing 1 changed file with 17 additions and 2 deletions.
19 changes: 17 additions & 2 deletions src/content/blog/2024-07-09-controlled-components-in-storybook.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { MouseEvent, ReactNode } from 'react';

export interface ButtonProps {
children?: ReactNode | undefined;
onClick?: (event: MouseEvent<HTMLButtonElement>) => void;
onClick?: ((event: MouseEvent<HTMLButtonElement>) => void) | undefined;
}

export function Button({ children, onClick }: ButtonProps) {
Expand Down Expand Up @@ -65,6 +65,8 @@ function Primary() {

一方、[controlledなコンポーネント](https://ja.react.dev/learn/sharing-state-between-components#controlled-and-uncontrolled-components)のstoryを作成する場合、そのコンポーネントの状態を外部から制御するための実装が必要になる。たとえば次のように。

`ToggleButton.tsx`:

```tsx
import { ReactNode, useCallback } from 'react';

Expand All @@ -87,8 +89,11 @@ export function ToggleButton({ isSelected, children, onChange }: ToggleButtonPro
}
```

`ToggleButton.stories.tsx`:

```tsx
import { useState } from 'react';
import { ToggleButton } from './ToggleButton';

function Primary() {
const [isSelected, setSelected] = useState(false);
Expand All @@ -105,6 +110,8 @@ function Primary() {

これをstoryとして表現するには、`component`プロパティにコンポーネントを指定するだけでは不十分である。そこでこの描画方法をカスタマイズするために、`render`メソッドを併せて実装する必要がある。

`ToggleButton.stories.tsx`:

```tsx
import type { Meta, StoryObj } from '@storybook/react';
import { fn } from '@storybook/test';
Expand Down Expand Up @@ -150,6 +157,8 @@ import withoutUseArgsVideo from './assets/2024-07-09-controlled-components-in-st

そのようなケースに対応すべく、argsの値を操作する[`useArgs`フック](https://storybook.js.org/docs/writing-stories/args#setting-args-from-within-a-story)がStorybookによって提供されている。これを利用することで次のような実装ができる。

`ToggleButton.stories.tsx`:

```tsx
import { useArgs } from '@storybook/preview-api';
import type { Meta, StoryObj } from '@storybook/react';
Expand Down Expand Up @@ -182,6 +191,8 @@ const meta = {

そんなわけで、このモックの呼び出しを有効にするには、次のように実装する。

`ToggleButton.stories.tsx`:

```tsx
import { useArgs } from '@storybook/preview-api';
import type { Meta, StoryObj } from '@storybook/react';
Expand Down Expand Up @@ -229,6 +240,8 @@ import withUseArgsVideo from './assets/2024-07-09-controlled-components-in-story

そのため、`useArgs`フックを使わないパターンの実装も紹介しておく。次のようになる。

`ToggleButton.stories.tsx`:

```tsx
import type { Meta, StoryObj } from '@storybook/react';
import { fn } from '@storybook/test';
Expand Down Expand Up @@ -284,6 +297,8 @@ export const SelectedByDefault = {

[React Spectrum](https://react-spectrum.adobe.com/)で利用されている[@react-stately/utils](https://github.com/adobe/react-spectrum/tree/%40react-stately/utils%403.10.1/packages/%40react-stately/utils)では、そのための`useControlledState`フックが提供されており、次のような実装ができる。

`ToggleButton.tsx`:

```tsx
import { ReactNode, useCallback } from 'react';
import { useControlledState } from '@react-stately/utils';
Expand All @@ -292,7 +307,7 @@ export interface ToggleButtonProps {
isSelected?: boolean;
defaultSelected?: boolean;
children?: ReactNode | undefined;
onChange?: (isSelected: boolean) => void;
onChange?: ((isSelected: boolean) => void) | undefined;
}

export function ToggleButton({ children, ...props }: ToggleButtonProps) {
Expand Down

0 comments on commit bc278ed

Please sign in to comment.