팀단위 프로젝트 진행 시 각각의 디자인을 쉽게 볼 수 있도록 스토리북이라는 UI 컴포넌트 개발도구에 등록해 개발을 진행.
데모용 코드 작성에 도움을 주고, 공통적으로 사용될 컴포넌트를 팀원들과 편리하게 공유할 수 있다.
구성단위는 스토리(Story)
컴포넌트는 보통 하나 이상의 스토리를 가진다.
- 장점
- 로직이 복잡하지 않다(독립적 환경에서 컴포넌트 개발 가능)
- 재사용을 위한 컴포넌트들을 스토리에서 조합해 테스트 가능
컴포넌트들을 문서화 할 수 있고, 디자인 시스템에 적용해 피그마의 컴포넌트들을 동기화 할 수 있다.
npx create-react-app 프로젝트명
cd 프로젝트명
npx -p storybook sb init
npm run storybook
// Input.jsx
import React, { Component } from "react";
import PropTypes from "prop-types";
class Input extends Component {
constructor(props) {
super(props);
this.setRef = this.setRef.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
const { name, onChange } = this.props;
if (onChange) {
onChange(name, e.target.value);
}
}
componentDidMount() {
if (this.props.autoFocus) {
this.ref.focus();
}
}
componentDidUpdate() {
if (this.props.autoFocus) {
this.ref.focus();
}
}
setRef(ref) {
this.ref = ref;
}
render() {
const { errorMessage, label, name, value, type, onFocus } = this.props;
return (
<label>
{label}
<input
id={"input_${name}"}
ref={this.setRef}
onChange={this.handleChange}
onFocus={onFocus}
value={value}
type={type}
/>
{errorMessage && <span className="error">{errorMessage}</span>}
</label>
);
}
}
Input.propTypes = {
type: PropTypes.oneOf(["text", "number", "price"]),
name: PropTypes.string.isRequired,
value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
errorMessage: PropTypes.string,
label: PropTypes.string,
onChange: PropTypes.func,
autoFocus: PropTypes.bool,
};
Input.defaultProps = {
onChange: () => {},
onFocus: () => {},
autoFocus: false,
type: "text",
};
export default Input;
// Input.stories.js
import React, { Component } from 'react';
import Input from "./Input";
export default{
title : "Input",
component : Input,
};
export const label = () => <Input name="name" label="이름 : "/>;
// Text.jsx
import React, { Component } from "react";
import PropTypes from "prop-types";
export function Text({ children, color, italic, underline }) {
const style = {
color: color,
fontStyle: italic ? "italic" : "normal",
textDecoration: underline ? "underline" : "none",
};
return <span style={style}>{children}</span>;
}
Text.propTypes = {
children: PropTypes.string.isRequired,
color: PropTypes.string,
italic: PropTypes.bool,
underline: PropTypes.bool,
};
Text.defaultProps = {
color: "black",
italic: false,
underline: false,
};
// Text.stories.js
import React, { Component } from "react";
import {Text} from "./Text";
export default{
title : "Text",
component : Text,
};
const TEST_TEXT = "Story Text Test";
export const Default = () =><Text>{TEST_TEXT}</Text>;
export const Red = () =><Text color="red">{TEST_TEXT}</Text>;
export const Italic = () =><Text italic>{TEST_TEXT}</Text>;
export const Underline = () =><Text underline>{TEST_TEXT}</Text>;