From bc278ed07f135572e7ffe8f321db59fe9b17b3b8 Mon Sep 17 00:00:00 2001 From: Yuhei Yasuda Date: Wed, 10 Jul 2024 02:54:29 +0900 Subject: [PATCH] update 2024-07-09-controlled-components-in-storybook --- ...-09-controlled-components-in-storybook.mdx | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/content/blog/2024-07-09-controlled-components-in-storybook.mdx b/src/content/blog/2024-07-09-controlled-components-in-storybook.mdx index bb55822..88f0c5c 100644 --- a/src/content/blog/2024-07-09-controlled-components-in-storybook.mdx +++ b/src/content/blog/2024-07-09-controlled-components-in-storybook.mdx @@ -14,7 +14,7 @@ import { MouseEvent, ReactNode } from 'react'; export interface ButtonProps { children?: ReactNode | undefined; - onClick?: (event: MouseEvent) => void; + onClick?: ((event: MouseEvent) => void) | undefined; } export function Button({ children, onClick }: ButtonProps) { @@ -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'; @@ -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); @@ -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'; @@ -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'; @@ -182,6 +191,8 @@ const meta = { そんなわけで、このモックの呼び出しを有効にするには、次のように実装する。 +`ToggleButton.stories.tsx`: + ```tsx import { useArgs } from '@storybook/preview-api'; import type { Meta, StoryObj } from '@storybook/react'; @@ -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'; @@ -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'; @@ -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) {