Story 작성하기
Storybook은 Story를 모아놓은 집합체가 되고, 각각의 Story는 저마다의 Vision State를 가지고 있는 Component를 의미한다. ex) primary button, secondary button, large button, small button , ...
하나의 Story를 만들기 위해 필요한 파일은 다음과 같다.
- component가 정의되어 있는 파일. ex) button.js(x)
- style가 정의되어 있는 파일. ex) button.css
- story가 정의되어 있는 파일. ex) button.stories.js
위 파일들은 하나의 폴더에 모여있어야한다.
다음과 같이 Button component와 style이 정의되어 있다고 가정하자
import React from 'react'
import './Button.css'
function Button(props) {
const {variant = 'primary', children, ...rest} = props;
return (
<button className={`button ${variant}} `} {...rest}>{children}</button>
)
}
export default Button
.button{
border: none;
color: white;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
border-radius: 4px;
cursor: pointer;
}
.primary {background-color: #008CBA;}
.secondary {background-color: #e7e7e7; color: black};
.success { background-color: #4CAF50;}
.danger {background-color: #f44336;}
총 4개의 디자인의 Button Story가 생성될 수 있다.
import React from 'react';
import Button from './Button';
export default{
title: 'Button',// 필수, 전체 프로젝트에서 Unique해야한다.
component: Button,
}
export const Primary = () => <Button variant='primary'>Primary</Button>
export const Secondary = () => <Button variant='secondary'>Secondary</Button>
export const Success = () => <Button variant='success'>Success</Button>
export const Danger = () => <Button variant='danger'>Danger</Button>
//Primary.storyName = 'Primary Button' // story를 rename 할 수 있다.
(npm run storybook을 하면 .storybook/main.js의 stories경로를 보고 해당하는 story 파일들을 읽어 보여준다.)
이같이 독립적으로 component의 디자인이 어떻게 될지 알 수 있다.
협업에서 굉장히 유용하다.
Story 안에 Story 넣기
다음과 같이 Input component도 추가 되었다고 가정하자.
import React from 'react'
import './Input.css';
function Input(props) {
const {size='medium',...rest} = props;
return (
<input className={`input ${size}`} {...rest} />
)
}
export default Input
.input{
display: block;
width:400px;
padding-left:1rem;
padding-right:1rem;
border-radius: 0.25rem;
border: 1px solid;
border-color: inherit;
background-color: #fff;
}
.small{
height:2rem;
font-size:0.875rem;
}
.medium{
height:2.5rem;
font-size:1rem;
}
.large{
height:3rem;
font-size:1.25rem;
}
primary button과 large input을 조합한 component를 subscription이란 이름의 story로 만들고 싶다. 이 때 다음과 같이 Subscription.stories.js를 작성하면된다.
import React from 'react';
import {Primary} from '../Button/Button.stories';
import {Large } from '../Button/Input.stories';
export default{
title:'Form/Subcription' // Form directory 안에 생성됨
}
export const PrimarySubscription = () =>{
<>
<Large/>
<Primary/>
</>
}
args를 이용하여 story 만들기
지금까지 우리는 story를 만들때 개별적으로 import 한 component에 props를 넣어주면서 만들었다. 하지만 이 방법보다 storybook의 args속성을 이용하여 props를 설정하는 것이 좀 더 좋은 방법이다.
그 이유는 다음과 같다.
- props를 object로 표현하여 넘겨주는 것이 jsx element형식보다 더 적절하다
- 복잡한 component가 만들어 지는 상황에서 써야할 코드의 양을 줄일 수 있다.
- args는 다른 story에 재사용이 가능하다.
import React from 'react';
import Button from './Button';
export default{
title: 'Form/Button',
component: Button,
// args:{
// children:'Button' // default 설정 가능
// }
}
// export const Primary = () => <Button variant='primary'>Primary</Button>
// export const Secondary = () => <Button variant='secondary'>Secondary</Button>
// export const Success = () => <Button variant='success'>Success</Button>
// export const Danger = () => <Button variant='danger'>Danger</Button>
const Template = args => <Button {...args}/>
export const Primary = Template.bind({})
Primary.args={
variant:'primary',
children:'Primary Args'
}
export const LongPrimary = Template.bind({})
LongPrimary.args={
...Primary.args,
children:'Long Primary Args'
}
Decorator
storybook web에서 보여지는 일괄적인 style을 적용하고 싶을 수 있다. 이때 사용하는 것이 Decorator이다.
Button을 가운데 정열하는 Decorator 파일은 다음과 같이 작성할 수 있다.
// utility component -> decorator
import React from 'react'
import './Center.css';
function Center(props) {
return (
<div className="center">{
props.children
}</div>
)
}
export default Center
.center{
display:flex;
justify-content: center;
}
이후 Button story에 일괄적인 Center style를 적용하면 된다. 이 때 사용하는 것이 decorator 설정이다
import React from 'react';
import Button from './Button';
import Center from '../Center/Center'
export default{
title: 'Form/Button',
component: Button,
decorators:[story => <Center>{story()}</Center>]
}
const Template = args => <Button {...args}/>
export const Primary = Template.bind({})
Primary.args={
variant:'primary',
children:'Primary Args'
}
export const LongPrimary = Template.bind({})
LongPrimary.args={
...Primary.args,
children:'Long Primary Args'
}
결과는 다음과 같이 가운데 정렬이 된다.
모든 storybook에 일괄적인 decorator을 적용하고 싶다면 global decorator을 사용한다. preview.js에서 설정할 수 있다.
import React from 'react';
import {addDecorator} from '@storybook/react';
import Center from '../src/components/Center/Center'
addDecorator(story=><Center>{story()}</Center>)