기본 사용법
1. cli 설치
yarn add global @storybook/cli
2. storybook을 프로젝트 내에 설치
yarn getstorybook
3. storybook 실행
yarn storybook
typescript 사용법
여기까지가 바닐라 js 일 때 storybook 셋팅이고 typescript는 추가적인 setting이 필요하다.
1. .stories/0-Welcome.stories.js 를 .stories/0-Welcome.stories.tsx로 변경한다.
storybook을 다시 키면 에러가 바바밤!! 발생한다!
2. @storybook/addon-info 와 react-docgen-typescript-loader 와 babel-preset-react-app 설치
yarn add -D @storybook/addon-info react-docgen-typescript-loader babel-preset-react-app
3. .storybook/main.js를 아래와 같이 수정한다.
module.exports = {
stories: ['../src/**/*.stories.tsx'],
webpackFinal: async config => {
config.module.rules.push({
test: /\.(ts|tsx)$/,
loader: require.resolve('babel-loader'),
options: {
presets: [['react-app', { flow: false, typescript: true }]],
},
});
config.resolve.extensions.push('.ts', '.tsx');
return config;
},
};
storybook webpack은 아래와 같이 사용하면 된다.
const path = require('path');
const root = path.resolve(__dirname, '../');
const alias = {
'@modules': `${root}/src/modules`,
'@model': `${root}/src/model`,
'@hooks': `${root}/src/hooks`,
'@constants': `${root}/src/constants`,
'@config': `${root}/src/config`,
'@api': `${root}/src/api`,
'@components': `${root}/src/components`,
'@pages': `${root}/src/pages`,
'@utils': `${root}/src/utils`,
'@types': `${root}/src/utils`,
'sprite': `${root}/src/sprite/scss`,
'@public': `${root}/public`
}
const commonCssLoaderOptions = {
importLoaders: 2,
url: false
};
const cssLoaderOptions = {
...commonCssLoaderOptions,
modules: false
};
const scssLoaderOptions = {
...commonCssLoaderOptions,
modules: true,
localIdentName: '[local]--[hash:base64:5]'
};
module.exports = {
stories: ['../stories/**/*.stories.tsx'],
addons: ['@storybook/addon-actions', '@storybook/addon-links'],
webpackFinal: async config => {
config.module.rules.push({
test: /\.(ts|tsx)$/,
loader: require.resolve('babel-loader'),
options: {
presets: [['react-app', { flow: false, typescript: true }]],
},
},
{
test: /\.(css)$/,
include: path.resolve(__dirname, '../public/css'),
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader', options: cssLoaderOptions },
],
},
{
test: /\.(scss)$/,
include: path.resolve(__dirname, '../src'),
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader', options: scssLoaderOptions },
{ loader: 'sass-loader' },
],
},
{
test: /\.(scss)$/,
include: path.resolve(__dirname, '../node_modules'),
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader', options: commonCssLoaderOptions },
{ loader: 'sass-loader' },
],
}
);
config.resolve.alias = {
...config.resolve.alias,
...alias
};
config.resolve.extensions.push('.ts', '.tsx');
return config;
},
};
4. tsconfig에 "rootDir": ["src", "stories"]를 추가한다.
{
"compilerOptions": {
...
"rootDirs": [
"src",
"stories"
]
},
...
}
펄풱트.
Link를 감싸고 있는 컴포넌트를 사용할 때
TripItem.tsx
import React from 'react';
import { hot } from 'react-hot-loader/root';
import classNames from 'classnames/bind';
import { Link } from 'react-router-dom';
import { ROUTE_PATH } from '@config/routes';
import { ITrip } from '../../types/api';
import Img from '@components/Img';
const style = require('./index.scss');
const cx = classNames.bind(style);
interface IOwnProps {
tripInfo: ITrip
layoutType: string
}
const TripItem: React.FC<IOwnProps> = ({ tripInfo, layoutType }) => {
const layoutClassName = layoutType.toLowerCase();
return (
<Link to={`${ROUTE_PATH.DETAIL.url}/${tripInfo.id}`} className={cx('trip_item', layoutClassName)}>
<div className={cx('inner')}>
<Img src={tripInfo.imageUrl} useLazyLoad={true} className={cx('trip_image')}/>
<div className={cx('info')}>
<strong className={cx('title')}>{tripInfo.title}</strong>
<p className={cx('date')}>{tripInfo.startDate} ~ {tripInfo.endDate}</p>
<div>
<Img src={tripInfo.country.imgUrl} className={cx('country_image')} useLazyLoad={true} />
</div>
<p className={cx('expense')}>₩476,708</p>
</div>
</div>
</Link>
);
};
export default hot(TripItem);
stories/2-Trip.stories.tsx
export const Trip_1 = () => (
<TripItem
tripInfo={{
id:'5ed904eae5a35e8cad4b7a48',
title:'123',
memo:'testtest',
country: {
id:"390",
name:"가나",
en:"Ghana",
imgUrl:"http://www.0404.go.kr/imgsrc.mofa?atch_file_id=FILE_000000000010504&file_sn=1",
country:"가나",
currency: {
ko:"세디",
en:"GHS",
rate:"212.1682"
}
},
imageUrl:'http://static.hubzum.zumst.com/hubzum/2018/10/18/16/189e444ee9114cb9ba0271f6fc09af32_780x0c.jpg',
startDate:'Wed Jun 03 2020',
endDate:'Wed Jun 10 2020',
status:''
}}
layoutType={'type_1'}
/>
);
Trip_1.stroy = {
name: 'type1'
}
아래와 같이 Invariant failed: You should not use <Link> outside a <Router> 에러가 발생한다.
해결 방법: <MemoryRouter>로 감싸주면 된다.
import React from 'react';
import { action } from '@storybook/addon-actions';
import { Button } from '@storybook/react/demo';
import TripItem from '../src/components/TripItem';
import { MemoryRouter } from 'react-router-dom';
export default {
title: 'tripItem',
};
export const Trip_1 = () => (
<MemoryRouter>
<TripItem
tripInfo={{
id:'5ed904eae5a35e8cad4b7a48',
title:'123',
memo:'testtest',
country: {
id:"390",
name:"가나",
en:"Ghana",
imgUrl:"http://www.0404.go.kr/imgsrc.mofa?atch_file_id=FILE_000000000010504&file_sn=1",
country:"가나",
currency: {
ko:"세디",
en:"GHS",
rate:"212.1682"
}
},
imageUrl:'http://static.hubzum.zumst.com/hubzum/2018/10/18/16/189e444ee9114cb9ba0271f6fc09af32_780x0c.jpg',
startDate:'Wed Jun 03 2020',
endDate:'Wed Jun 10 2020',
status:''
}}
layoutType={'type_1'}
/>
</MemoryRouter>
);
Trip_1.stroy = {
name: 'type1'
}
끝~ 이제 사용 방법을 공부해보장
참고: https://hyunseob.github.io/2018/01/08/storybook-beginners-guide/
Storybook 입문 가이드
페이지 단위의 개발이 이루어지던 과거와 달리 요즘의 프론트엔드 개발은 주로 컴포넌트 단위로 이루어진다. 이 컴포넌트라는 개념은 사용하는 라이브러리나 프레임워크에 따라 구현 방식이 ��
hyunseob.github.io
'JavaScript > React.js' 카테고리의 다른 글
CRA + Craco + TypeScript (0) | 2020.11.16 |
---|---|
Express + React 배포 설정 (0) | 2020.07.29 |
BrowserRouter에서 path를 직접 접근할 때 발생하는 이슈 (0) | 2020.03.13 |
react에서 lottie 사용하기 (0) | 2019.10.17 |
리액트 LifeCycle (0) | 2019.05.02 |