TIL archiving ···.ᐟ/React

Storybook

dayoung-archive 2024. 10. 22. 22:13

수업에서 배운 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 으로 실행시켜보면 

default 일 때
Archived 됐을 때

 

이렇게 적용된 걸 확인 할 수 있었다.

원래라면 저 체크박스가 체크도 되고, 또 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