FRONT-END/REACTJS

StoryBook

JINGMONG 2022. 3. 5. 02:39

컴포넌트 주도 개발(Component-Driven-Development)

컴포넌트를 모듈 단위로 개발하여 사용자 인터페이스 구축에 도달하는 개발 및 설계 방법론

기본적인 컴포넌트 단위부터 시작해서 UI view를 구성하기 위해 점진적으로 조립해가는 상향적 특징을 가지고 있다.

장점

  • 디자인 체계화, 디자이너와 효율적인 협업
  • 재사용성: 다른 프로젝트에서도 쉽게 쓰고 공유 가능
  • Decoupling: CSS, JS, I18n, UI 단위 테스트 등

UI 패턴이 3회 이상 사용되는 경우. 재사용 가능한 UI 컴포넌트로 간주한다.

UI 컴포넌트가 3개 이상의 프로젝트/팀에서 사용되는 경우, 디자인 시스템에 포함시킨다.

install

npx -p @storybook/cli sb init
npm run storybook

글로벌 스타일 추가하기

컴포넌트가 제대로 보이기 위해 글로벌 스타일을 적용. Styled-components의 글로벌 스타일 태그를 이용해서 추가.

// src/shared/global.js

import { createGlobalStyle, css } from 'styled-components';
import { color, typography } from './styles';

export const fontUrl = '<https://fonts.googleapis.com/css?family=Nunito+Sans:400,700,800,900>';

export const bodyStyles = css`
  /* same as before */
`;

export const GlobalStyle = createGlobalStyle`
 body {
   ${bodyStyles}
 }
`;
// .storybook/preview.js

import React from "react";
import { GlobalStyle } from "../src/shared/global";

export const decorators = [
  (Story) => (
    <>
      <GlobalStyle />
      <Story />
    </>
  ),
];

decorator는 어떤 스토리가 선택되었든 간에 GlobalStyle이 반드시 렌더링 되도록 한다.

폰트 태그 추가하기

가장 쉬운 방법은 .storybook/preview-head.html 파일에서 <head> 태그에 직접 <link> 태그를 추가하는 것이다.

<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Nunito+Sans:400,700,800,900" />

그외 방법

GitHub - storybookjs/design-system: 🗃 Storybook Design System

 

GitHub - storybookjs/design-system: 🗃 Storybook Design System

🗃 Storybook Design System. Contribute to storybookjs/design-system development by creating an account on GitHub.

github.com

export default

export default metadata는 스토리북이 스토리를 나열하고 addons에서 사용하는 정보를 제공하는 방법을 제어한다.

// Button.stories.tsx

export default {
  title: 'Button',
  component: Button,
} as ComponentMeta<typeof Button>;

define story

CSF 파일의 명명된 export를 사용하여 컴포넌트의 스토리를 정의한다. story export에는 UpperCamelCase를 사용하는 것이 좋다.

ex) default 상태에서 primary로 button을 렌더링하는 코드를 export하는 방법

export const Primary: ComponentStory<typeof Button> = () => <Button primary>Button</Button>;

React Hooks로 작업

React Hooks를 사용하여 버튼의 상태를 변경하는 방법

export const Primary = () => {
  const [value, setValue] = useState('Secondary');
  const [isPrimary, setIsPrimary] = useState(false);

  const handleOnChange = () => {
    if(!isPrimary) {
      setIsPrimary(true);
      setValue('Primary');
    }
};
return <Button primary={isPrimary} onClick={handleOnChange} label={value} />;

story 작성 방법

// Button.stories.js|jsx

import React from 'react';

import { Button } from './Button';

export default {
  /* 👇 The title prop is optional.
  * See <https://storybook.js.org/docs/react/configure/overview#configure-story-loading>
  * to learn how to generate automatic titles
  */
  title: 'Button',
  component: Button,
};
export const Primary = () => ;
export const Secondary = () => ;
export const Tertiary = () => ;

args 사용

args를 도입하여 패턴을 개선. 유지 관리해야 하는 코드를 줄인다.

// Button.stories.ts|tsx

import React from 'react';

import { ComponentStory, ComponentMeta } from '@storybook/react';

import { Button } from './Button';

export default {
  /* 👇 The title prop is optional.
  * See <https://storybook.js.org/docs/react/configure/overview#configure-story-loading>
  * to learn how to generate automatic titles
  */
  title: 'Button',
  component: Button,
} as ComponentMeta;

//👇 We create a “template” of how args map to rendering
const Template: ComponentStory = (args) => ;

//👇 Each story then reuses that template
export const Primary = Template.bind({});
Primary.args = { backgroundColor: '#ff0', label: 'Button' };

export const Secondary = Template.bind({});
Secondary.args = { ...Primary.args, label: '😄👍😍💯' };

export const Tertiary = Template.bind({});
Tertiary.args = { ...Primary.args, label: '📚📕📈🤓' };

💡 Template.bind({})은 표준 자바스크립트 기술 함수의 복사본을 만들기 위해. Template내보낸 각 스토리가 고유한 속성을 설정할 수 있도록 복사합니다 .

story에 args를 도입하면 작성해야하는 코드의 양을 줄일 수 있고 중복도 줄일 수 있다.

또한 다른 story를 작성할 때 재사용 할 수 있다.

데코레이터 사용

데코레이터는 스토리를 렌더링할 때 컴포넌트를 임의의 마크업으로 래핑하는 매커니즘.

// Button.stories.ts|tsx

import React from 'react';

import { ComponentMeta } from '@storybook/react';

import { Button } from './Button';

export default {
  /* 👇 The title prop is optional.
  * See <https://storybook.js.org/docs/react/configure/overview#configure-story-loading>
  * to learn how to generate automatic titles
  */
  title: 'Button',
  component: Button,
  decorators: [
    (Story) => (
 
    ),
  ],
} as ComponentMeta;

데코레이터로 margin 3rem을 적용하였을 때
적용하지 않았을 때

둘 이상의 컴포넌트가 필요한 story

디자인 시스템이나 컴포넌트를 구축할 때 두개 이상의 컴포넌트가 복합되는 경우가 있을 수 있다.

예를 들어 List 컴포넌트가 ListItem 컴포넌트를 포함하는 경우

// List.stories.ts|tsx

import React from 'react';

import { ComponentStory, ComponentMeta } from '@storybook/react';

import { List } from './List';
import { ListItem } from './ListItem';

//👇 Imports a specific story from ListItem stories
import { Unchecked } from './ListItem.stories';

export default {
  /* 👇 The title prop is optional.
  * See <https://storybook.js.org/docs/react/configure/overview#configure-story-loading>
  * to learn how to generate automatic titles
  */
  title: 'List',
  component: List,
} as ComponentMeta;

const ListTemplate: ComponentStory = (args) => {
  const { items } = args;
  return (
    
      {items.map((item) => (
        
      ))}
  
)};

export const Empty = ListTemplate.bind({});
Empty.args = { items: [] };

export const OneItem = ListTemplate.bind({});
OneItem.args = {
  items: [
    {
      ...Unchecked.args,
    },
  ],
};

배포

Publish Storybook

 

Publish Storybook

Storybook is an open source tool for developing UI components in isolation for React, Vue, and Angular

storybook.js.org

 

'FRONT-END > REACTJS' 카테고리의 다른 글

Render Props pattern  (0) 2023.02.22
리덕스 라이브러리  (0) 2022.02.19
Context API  (0) 2022.02.19
react 비동기 작업  (0) 2022.02.19
react route로 SPA 개발  (0) 2022.02.19