StoryBook for React #2 간단한 컴포넌트 만들기

TaejoonPark·2022년 4월 13일
0

Storybook

목록 보기
2/5

간단한 컴포넌트와 스토리

아래 Task 컴포넌트가 있다고 상상하고 story를 만들어보자.

src/components/Task.jsx

import React from 'react';

export default function Task({ task: { id, title, state }, onArchiveTask, onPinTask }) {
  return (
    <div className='list-item'>
      <input type='text' value={title} readOnly={true} />
    </div>
  );
}

src/components/Task.stories.js

import React from 'react';
import Task from './Task';

// #1
export default {
  component: Task,
  title: 'Task'
}

// #2
const Template = args => <Task {...args} />;

// #3
import React from 'react';
import Task from './Task';

export default {
  component: Task,
  title: 'Task',
};

const Template = args => <Task {...args} />;

// #3
export const Default = Template.bind({});
Default.args = {
  task: {
    id: 1,
    title: 'Default Task',
    state: 'TASK_INBOX',
    updatedAt: new Date(2018, 0, 1, 9, 0),
  },
};

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',
  },
};

#1 export default로 객체를 내보내는데 내보낼 component를 지정하고 title같은 속성을 지정할 수 있다. 여기서 title은 스토리북을 실행했을 때 사이드바에 나타나는 그룹 제목이라고 생각하면 된다.

#2 컴포넌트를 재활용하기 위해 컴포넌트를 리턴하는 Template 함수를 만든다. 이 Template함수를 복사해서 사용할 것이다.

#3 Template함수를 복사하기 위해 Template.bind({})를 실행하고 복사된 함수를 변수에 담는다.
담은 함수.args에다가 객체 형태로 넣어줄건데 그 객체 안에는 props를 설정할 수 있다.

구성

.storybook/main.js

스토리위치를 지정하고 addon을 추가할 수 있다.
storybook 경로만 수정해주었다.

module.exports = {
  stories: ['../src/components/**/*.stories.js'],
  addons: [
    '@storybook/addon-links',
    '@storybook/addon-essentials',
    '@storybook/addon-interactions',
    '@storybook/preset-create-react-app',
  ],
  framework: '@storybook/react',
  core: {
    builder: '@storybook/builder-webpack5',
  },
};

.storybook/preview.js

css 위치를 지정할 수 있다.
그리고 parameter를 통해 storybook의 기능과 애드온을 제어할 수 있다. 이를 이용하여 parameter내부에 actions를 이용해 버튼 클릭 같은 것을 테스트해 볼 것이다.

import '../src/index.css';
export const parameters = {
  actions: { argTypesRegex: '^on[A-Z].*' },
  controls: {
    matchers: {
      color: /(background|color)$/i,
      date: /Date$/,
    },
  },
};

State주기

src/components/Task.jsx

storybook 스크립트를 실행하면 story파일에서 props를 주는 템플릿들을 확인할 수 있다.

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)} />
      </label>
      <div className='title'>
        <input type='text' value={title} readOnly={true} />
      </div>
      <div className='actions' onClick={event => event.stopPropagation()}>
        {state !== 'TASK_ARCHIVED' && (
          <a
            onClick={() => {
              onPinTask(id);
            }}

            <span className='icon-star'></span>
          </a>
        )}
      </div>
    </div>
  );
}

Type 명시하기

공식문서에서는 propTypes를 안내하고 있지만 트렌드에 따라 Typescript를 사용해보았다. 잘된다. 👏

components/Task.tsx

import React from 'react';

type TaskBoxProps = {
  task: {
    id: number;
    title: string;
    state: string;
  };
  onArchiveTask: (id: number) => void;
  onPinTask: (id: number) => void;
};

export default function Task({
  task: { id, title, state },
  onArchiveTask,
  onPinTask,
}: TaskBoxProps) {
  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)} />
      </label>

      <div className='title'>
        <input type='text' value={title} readOnly={true} />
      </div>

      <div className='actions' onClick={event => event.stopPropagation()}>
        {state !== 'TASK_ARCHIVED' && (
          <a
            onClick={() => {
              onPinTask(id);
            }}
          >
            <span className='icon-star'></span>
          </a>
        )}
      </div>
    </div>
  );
}

snaphost

해당 스토리를 완성하고 저장해놓고 다음에 업데이트 되었을 때 바뀐 부분을 빠르게 캐치하고 테스트해볼 수 있는 방법을 실험해보려고 했으나 react18버전에서 구동이 되지 않는 것 같다. 해당 이슈를 어렵지 않게 찾을 수 있었다.
https://github.com/storybookjs/storybook/issues/17985

profile
공유하는 것을 좋아하는 프론트엔드 개발자

0개의 댓글

관련 채용 정보