수업에서 배운 storybook을 이용해 todo 컴포넌트를 만들어보자!
공식문서에 친절하게 todo 컴포넌트를 만들어보는 튜토리얼이 제공된다. 그러나 jsx 파일이 기준이고, 이전 버전이라 최신버전과 tsx를 사용하고 있는 내 작업 환경에서는 수정해야하는 부분이 몇 가지 있었다,,
튜토리얼 참고
https://storybook.js.org/tutorials/intro-to-storybook/react/ko/simple-component/
1. Task.tsx 와 Task.type.tsx 파일 생성
// 공식문서 튜토리얼에서 알려주는 Task.jsx
import React from 'react';
export default function Task({ task: { id, title, state }, onArchiveTask, onPinTask }) {
return (
<div className={`list-item ${state}`}>
<label className="checkbox">
<input
type="checkbox"
defaultChecked={state === 'TASK_ARCHIVED'}
disabled={true}
name="checked"
/>
<span
className="checkbox-custom"
onClick={() => onArchiveTask(id)}
id={`archiveTask-${id}`}
aria-label={`archiveTask-${id}`}
/>
</label>
<div className="title">
<input type="text" value={title} readOnly={true} placeholder="Input title" />
</div>
<div className="actions" onClick={event => event.stopPropagation()}>
{state !== 'TASK_ARCHIVED' && (
// eslint-disable-next-line jsx-a11y/anchor-is-valid
<a onClick={() => onPinTask(id)}>
<span className={`icon-star`} id={`pinTask-${id}`} aria-label={`pinTask-${id}`} />
</a>
)}
</div>
</div>
);
}
공식문서에는 이렇게 Task.jsx 를 작성하라고 알려준다. 그러나 내가 사용 할 tsx에서는 타입을 지정해줘야하기 때문에,
Task.type.tsx 파일을 생성해 타입을 지정해준다!
// Task.type.tsx
export type TaskProps = {
task: {
id: string;
title: string;
state: string;
};
onArchiveTask: (id: string) => void;
onPinTask: (id: string) => void;
};
2. Task.tsx 수정 및 Task.stories.tsx 생성
1번에서 튜토리얼에 나와있던
export default function Task({ task: { id, title, state }, onArchiveTask, onPinTask })
이 부분을 작성한 타입 프롭스를 받아와
export default function Task({task: { id, title, state }, onArchiveTask, onPinTask,}: TaskProps)
이렇게 타입을 명시해주는 형태로 바꿔주고,
튜토리얼 대로 스토리파일을 생성해 여기에도 타입 프롭스를 적용시켜준다.
💡스토리파일이란? Storybook에서 컴포넌트를 시각적으로 테스트하고 문서화하기 위한 파일이다. (필수!!)
Storybook에서는 각 컴포넌트에 대해 여러 가지 "스토리"를 정의할 수 있고, 각 스토리는 해당 컴포넌트가 어떻게 보이는지를 보여주는 하나의 상태를 의미한다.
// Task.stories.tsx
import Task from "./Task";
import { TaskProps } from "./Task.type";
import { Meta, StoryFn } from "@storybook/react";
export default {
component: Task,
title: "안녕하세요",
} as Meta;
const Template: StoryFn<TaskProps> = (args) => <Task {...args} />;
export const Default = Template.bind({});
Default.args = {
task: {
id: "1",
title: "오늘의 할 일",
state: "TASK_INBOX",
},
};
export const Pinned = Template.bind({});
Pinned.args = {
task: {
...Default.args.task,
state: "TASK_PINNED",
},
};
export const Archived = Template.bind({});
Archived.args = {
task: {
...Default.args.task,
state: "TASK_ARCHIVED",
},
};
공식문서에서는 위 코드의 component와 title을
이렇게 설명해주는데 여기서 title에 대한 설명이 좀 어렵게 느껴진다.
그냥 쉽게 얘기하자면 스토리북을 실행 시켰을 때 사이드바에서 보이는 이름이다 😂
실제로 여기다 안녕하세요를 입력하면 그대로 변경되는 걸 볼 수 있다,,ㅎㅎ
이외에 다른 코드를 사용한 이유와 설명에 대한 링크는 공식문서에 잘 나와있으니 참고하면 된다!
3. Preview.tsx 수정
튜토리얼에서는 스토리북 폴더안의 preview 파일에 이 부분을 추가해주라고 나와있다.
actions: { argTypesRegex: "^on[A-Z].*" },
근데 최신버전에서는 그대로 추가해줬을 때 터미널에서 에러가 발생한다.
We recommend removing the argTypesRegex and assigning explicit action with the fn function from @storybook/test instead: https://storybook.js.org/docs/essentials/actions#via-storybooktest-fn-spy-function
argTypesRegex 지우고 다른 거(fn 함수) 쓰길 추천 한다고 함,,
사실 그대로 놔둬도 앱 실행에는 문제가 없지만 거슬리기 때문에 링크를 타고 들어가서 알려주는 대로 바꿔줬다.
actions: { argTypesRegex: '^on.*' },
4. Task.styled.tsx 생성 및 Task.tsx 수정
storybook에서 제공하는 css를 적용하지 않았기때문에 적용할 styled 파일을 생성해주자!
import styled from "styled-components";
export const ListItem = styled.div<{ state: string }>`
display: flex;
align-items: center;
`;
export const CheckboxLabel = styled.label`
display: flex;
align-items: center;
cursor: pointer;
`;
export const CheckboxInput = styled.input`
display: none;
`;
export const CheckboxCustom = styled.span`
width: 20px;
height: 20px;
border: 1px solid #ccc;
display: inline-block;
margin-right: 10px;
cursor: pointer;
&:hover {
background-color: #f0f0f0;
}
`;
export const Title = styled.div<{ state: string }>`
flex-grow: 1;
cursor: default;
text-decoration: ${({ state }) =>
state === "TASK_ARCHIVED" ? "line-through" : "none"};
`;
export const Actions = styled.div`
display: flex;
align-items: center;
`;
export const PinIcon = styled.span`
cursor: pointer;
`;
원래 Task 파일에서는 storybook에서 제공하는 css에 맞춰 텍스트 부분이 input으로 되어있다.
이걸 그냥 text 형태로 바꾸고, 할 일의 상태가 "TASK_ARCHIVED" 되면 글에 취소선이 생기도록 조금 수정해줬다.
그리고 Task.tsx에도 style 적용해주기,,
import React from "react";
import {
ListItem,
CheckboxLabel,
CheckboxInput,
CheckboxCustom,
Title,
Actions,
PinIcon,
} from "./Task.styled";
import { TaskProps } from "src/components/Task/Task.type";
export default function Task({
task: { id, title, state },
onArchiveTask,
onPinTask,
}: TaskProps) {
return (
<ListItem state={state}>
<CheckboxLabel>
<CheckboxInput
type="checkbox"
defaultChecked={state === "TASK_ARCHIVED"}
disabled={true}
name="checked"
/>
<CheckboxCustom onClick={() => onArchiveTask(id)} />
</CheckboxLabel>
<Title state={state}>
{title} {/* TitleInput 대신 Title 사용 */}
</Title>
<Actions onClick={(event) => event.stopPropagation()}>
{state !== "TASK_ARCHIVED" && (
<a onClick={() => onPinTask(id)}>
<PinIcon id={`pinTask-${id}`} aria-label={`pinTask-${id}`} />
</a>
)}
</Actions>
</ListItem>
);
}
npm run story 으로 실행시켜보면
이렇게 적용된 걸 확인 할 수 있었다.
원래라면 저 체크박스가 체크도 되고, 또 Pinned 되면 옆에 별 아이콘도 표시되도록 해야하는데 그건 다음 기회에,,, 🍀
'TIL archiving ···.ᐟ > React' 카테고리의 다른 글
TanStack Query(React Query) (0) | 2024.10.21 |
---|---|
[React 연습] 간단한 카운터 & Todo 만들기 (1) | 2024.10.16 |
상태 관리 라이브러리 Recoil (2) | 2024.10.15 |
Context API (0) | 2024.10.14 |
React Router - useNavigate() (0) | 2024.10.10 |