Template.bind({ })는 함수의 복사본을 만드는 표준 JavaScript의 기법입니다. 이 기법을 사용하여 각각의 스토리가 고유한 속성(properties)을 갖지만, 동시에 동일한 구현을 사용하도록 할 수 있습니다.
인자(arguments)는 줄여서 args를 사용하며, Storybook을 다시 시작하지 않고도 Controls addon으로 컴포넌트를 실시간으로 수정할 수 있습니다. 값이 변하면 컴포넌트도 실시간 업데이트 됩니다.
Primary.args에서 의문이 들 수 있습니다. args는 JavaScript 함수의 표준 속성이 아니기 때문이죠. 이는 Storybook이 컴포넌트를 렌더링 하는 과정에서 Primary에 args를 전달하므로 정상 작동하는 것입니다.
아래 코드는 Story 작성 예시입니다.
// Story를 구성할 컴포넌트 파일 불러오기import StoryInput from'./StoryInput'/* ------------------------------------------------------------------- */exportdefault {// 컴포넌트 설명을 입력하면 Storybook에 카테고리 되어 표시됩니다. title:'FormControl/StoryInput',// 컴포넌트 설정 component: StoryInput,// 전달인자 공통 설정 args: { label:'이메일', type:'email', placeholder:'yamoo9@euid.dev', },// 전달 인자 유형 설정 argTypes: { backgroundColor: { control:'color' }, disabled: { control:'boolean' }, },}// 컴포넌트 템플릿// 함수의 복사본을 만드는 표준 JavaScript 기법constTemplate= (args) => <StoryInput {...args} />// sm 사이즈 컴포넌트exportconstSmSize=Template.bind({})SmSize.storyName ='Small'SmSize.args = { id:'sm-size-kwdj1', size:'sm',}SmSize.parameters = { viewport: { defaultViewport:'iphonex', },}// md 사이즈 컴포넌트exportconstMdSize=Template.bind({})MdSize.storyName ='Medium'MdSize.args = { id:'md-size-kwdj5', size:'md',}MdSize.parameters = { viewport: { defaultViewport:'iphonexr', },}// lg 사이즈 컴포넌트exportconstLgSize=Template.bind({})LgSize.storyName ='Large'LgSize.args = { id:'lg-size-kwdj8', size:'lg',}
import React from'react'import PropTypes from'prop-types'import'./StoryInput.scss'// Story를 구성할 컴포넌트를 작성합니다.constStoryInput= ({ id, label, className, size,...restProps }) => ( <divclassName={`storyInput ${className}${size}`.trim()}> <labelhtmlFor={id}>{label}</label> <inputid={id} type="text" {...restProps} /> </div>)exportdefault StoryInput// 컴포넌트 속성 검사를 설정하면 Story 문서에 반영됩니다.// 컴포넌트에 필요한 데이터 형태를 명시하려면 React에서 propTypes를 사용하는 것이 가장 좋습니다.// 이는 자체적 문서화일 뿐만 아니라, 문제를 조기에 발견하는 데 도움이 됩니다.StoryInput.propTypes = {/** label 요소와 input 요소를 연결하는 key */ id:PropTypes.string.isRequired,/** UI에 표시되는 레이블 */ label:PropTypes.string.isRequired,/** 레이블을 UI에서 감춤 (스크린 리더 사용자에게는 읽힘) */ labelHidden:PropTypes.bool,/** 플레이스홀더 */ placeholder:PropTypes.string,/** 커스텀 클래스 이름 */ className:PropTypes.string,/** 설정 가능한 인풋 타입 */ type:PropTypes.oneOf(['text','email','password','search']),/** 인풋 크기 */ size:PropTypes.oneOf(['sm','md','lg']),}// 컴포넌트 기본 속성을 설정하면 Story 문서에 반영됩니다.StoryInput.defaultProps = { type:'text', className:'', size:'md', labelHidden:false,}
import $1 from'./$1'conststoryConfig= {// 컴포넌트 설명 title:'${2:시스템}/${3:그룹}/$1',// 컴포넌트 설정 component: $1,// 전달 인자 설정// args: {// 설정 예시 // type: 'email',// },// 전달 인자 유형 설정// argTypes: {// 컬러 피커 컨트롤 설정 예시// backgroundColor: { control: 'color' },// }}exportdefault storyConfig// 컴포넌트 템플릿constTemplate= (args) => <$1 {...args} />// 예제 컴포넌트 생성exportconstExample=Template.bind({})// 예제 컴포넌트 props 설정Example.args = {// prop 속성 설정}
매개변수 활용
매개변수는 Story에 대한 정적 메타 데이터를 정의하는 Storybook의 방법입니다. Story의 매개변수를 사용하여 Story 또는 Story 그룹 레벨에서 다양한 애드온에 구성을 제공 할 수 있습니다. 예를 들어 앱의 다른 컴포넌트와 다른 배경에서 컴포넌트를 테스트 하고 싶다면? 다음과 같이 CSF를 설정합니다. (컴포넌트 레의 매개변수 설정)
exportdefault { title:'Button', component: Button,//👇 Story 매개 변수 설정 parameters: { backgrounds: { values: [ { name:'darkred', value:'#340000' }, { name:'storypink', value:'#fb6597' } ], }, },};
데코레이터 활용
데코레이터는 Story를 렌더링 할 때 임의의 마크업으로 컴포넌트를 감싸는 메커니즘입니다. 예를 들어 테마 또는 레이아웃 래퍼(wrapper)가 필요할 수 있습니다. 또는 UI에 특정 컨텍스트 혹은 데이터 공급자(provider)가 필요할 수 있습니다.
간단한 예는 컴포넌트의 Story에 스타일 래퍼를 추가하는 것입니다. 다음과 같이 Story를 감싸는 데코레이터를 작성하면 Storybook 뷰포트에 스타일이 반영된 래퍼 요소가 렌더링 됩니다.
디자인 시스템 또는 컴포넌트 라이브러리를 빌드 할 때 함께 작동하도록 디자인 된 2개 이상 컴포너트의 Story를 작성하려면 상위 컴포넌트와 하위 컴포넌트 모두 불러와 사용하고, 컴포넌트 작동 상황 별 Story를 렌더링 하는 것이 좋습니다.
import List from'./List';import ListItem from'./ListItem';exportdefault { component: List, title:'List',};// 하위 컴포넌트를 포함하지 않은 상위 컴포넌트 StoryexportconstEmpty= (args) => <List {...args} />;// 1개의 하위 컴포넌트를 포함한 상위 컴포넌트 StoryexportconstOneItem= (args) => ( <List {...args}> <ListItem /> </List>);// 1개 이상 하위 컴포넌트를 포함한 상위 컴포넌트 StoryexportconstManyItems= (args) => ( <List {...args}> <ListItem /> <ListItem /> <ListItem /> </List>);