일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- react-hook-form
- Spring
- RTK
- backend
- component
- JavaSpring
- javascript
- tanstackquery
- go
- test
- React
- Redux
- ReactHooks
- JPA
- Chakra
- storybook
- frontend
- golang
- satisfiles
- 웹애플리케이션서버
- hook
- Gin
- typescript
- designpatterns
- java
- css
- springboot
- 티스토리챌린지
- 오블완
- Today
- Total
bkdragon's log
Storybook Interactions 본문
storybook 의 play 함수를 사용하면 스토리가 렌더링 된 이후 사용자 상호작용을 시뮬레이션할 수 있다.
설정
play 함수를 사용하기 위해서는 스토리북 설정 파일에서 interactions 플러그인울 추가해야한다.
npm install @storybook/test @storybook/addon-interactions --save-dev
// .storybook/main.js
module.exports = {
stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
addons: ["@storybook/addon-interactions"],
};
방법
사용자 상호작용 시뮬레이션의 전반적인 과정을 먼저 살펴보고 예제 코드로 넘어가자.
스토리 내에서 상호작용을 할 요소를 찾는다.
- play 함수의 파라미터 중 canvasElement 스토리에 렌더링되는 DOM 요소이다, within 을 사용해서 canvasElement 안의 영역으로 범위를 지정하고 찾을 수 있다.
상호작용을 발생시킨다.
- userEvent, fireEvent 등의 함수를 사용해서 클릭, 입력, 스크롤 등의 상호작용을 시뮬레이션할 수 있다.
상호작용의 결과를 확인한다.
- assert , matchers 를 사용해서 상호작용의 결과를 확인할 수 있다.
코드
이제 예제 코드를 살펴보자.
Menu 목록과 각 메뉴를 클릭하는 함수를 인자로 제공받는 MenuList 컴포넌트가 있다. 이 컴포넌트가 렌더링 되고 다른 메뉴를 클릭하는 상호작용을 시뮬레이션 해보자,
import { within, expect, waitFor, fireEvent, fn } from "@storybook/test";
const menus = [
{ name: "Home", path: "/" },
{ name: "Profile", path: "/profile" },
{ name: "Settings", path: "/settings", children: [{ name: "Sub Settings", path: "/sub-settings" }] },
];
export const WithInteractions: Story = {
name: "상호작용 테스트",
parameters: {
docs: {
description: {
story: "이 스토리는 메뉴 리스트의 상호작용을 테스트합니다. 'Profile' 메뉴와 'Home' 메뉴를 순서대로 클릭하여 onClickMenu 함수가 호출되는지 확인합니다.",
},
},
},
args: {
menus: menus,
currentPath: "/",
onClickMenu: fn(),
},
play: async ({ args, canvasElement, step }) => {
const canvas = within(canvasElement);
await waitFor(() => {
expect(canvas.getByText("Profile")).toBeInTheDocument();
expect(canvas.getByText("Settings")).toBeInTheDocument();
});
await new Promise((resolve) => setTimeout(resolve, 1000));
const profileMenu = canvas.getByText("Profile");
await step("Profile 메뉴 클릭", () => {
fireEvent.click(profileMenu);
});
await new Promise((resolve) => setTimeout(resolve, 1000));
const settingsMenu = canvas.getByText("Settings");
await step("Settings 메뉴 클릭", () => {
fireEvent.click(settingsMenu);
});
await waitFor(() => {
expect(canvas.getByText("Sub Settings")).toBeInTheDocument();
});
await new Promise((resolve) => setTimeout(resolve, 1000));
const subSettingsMenu = canvas.getByText("Sub Settings");
await step("Sub Settings 메뉴 클릭", () => {
fireEvent.click(subSettingsMenu);
});
expect(args.onClickMenu).toHaveBeenCalledTimes(2);
},
render: (args) => {
const [currentPath, setCurrentPath] = useState(args.currentPath);
const handleClickMenu: ComponentProps<typeof MenuList>["onClickMenu"] = (menu) => {
args?.onClickMenu?.(menu); // Mock 함수 호출 추적
setCurrentPath(menu.path); // 실제 UI 상태 변경
};
return <MenuList {...args} currentPath={currentPath} onClickMenu={handleClickMenu} />;
},
};
스토리가 렌더링 된 이후 play 함수가 실행된다.
waitFor은 특정 동작을 기다리는 함수이다. 여기서는 메뉴 요소가 렌더링 될 때까지 기다린다. (사실 렌더링 다 되고 실행되는거라 필요없기 하다.)
우선 Profile 버튼을 찾아서 클릭한다. Profile 버튼은 chidren 이 없기 때문에, onClickMenu 함수가 호출될 것이다. (children 이 있는 경우 chidren 목록이 나타나는 내부적인 로직이 실행된다.)
그 다음은 Settings 버튼을 찾아서 클릭한다. 이번에는 children 이 있기 때문에, 내부적인 로직이 실행되어 Sub Settings 버튼이 나타난다.
마지막으로 Sub Settings 버튼을 찾아서 클릭한다. Profile과 마찬가지로 onClickMenu 함수가 호출될 것이다.
그럼 결과적으로 onClickMenu 함수가 두 번 호출될 것이다.
정리
상호작용을 직접 시뮬레이션 해볼 수도 있지만, play 함수를 사용하니 자동화할 수 있었다. 스토리북은 정말 막강한 툴인 것 같다.
'React' 카테고리의 다른 글
forwardRef (0) | 2024.11.20 |
---|---|
Storybook Interactions Portal Component (0) | 2024.11.17 |
children 을 함수해서 동적으로 스타일을 지정하기 (0) | 2024.11.14 |
드래그 가능한 모달창 만들기 (0) | 2024.11.11 |
Storybook Decorator 로 전역 상태 사용하기 (0) | 2024.11.07 |