Bài viết được sự cho phép của tác giả Phạm Minh Khoa
Trong bài viết trước đây mình đã giới thiệu về Storybook, các bạn chưa đọc có thể xem lại ở link dưới đây:
Giới thiệu về StoryBook cho dự án FrontEnd
Hôm nay mình tiếp tục hướng dẫn các bước để cài đặt và sử dụng Storybook trong dự án ReactJS.
Cài đặt Storybook
Storybook có thể cài đặt dễ dàng bằng cách chạy dòng lệnh dưới đây ở thư mục root của project (lưu ý là sử dụng với project đã có, lệnh dưới sẽ không tạo ra 1 project mới)
# Add Storybook:
npx sb init
Storybook sẽ check các dependencies sẵn có trong project của bạn (file package.json) và thực hiện cài đặt để cung cấp cho bạn 1 cấu hình tốt nhất có thể. Dòng lệnh trên sẽ thực hiện các công việc sau:
- Cài đặt các dependencies cần thiết
- Cài đặt những mã (scripts) cần thiết cho việc build và chạy Storybook
- Thêm cấu hình mặc định cho Storybook
- Tạo ra 1 số stories mẫu để bạn có thể xem và chạy thử
Sau khi chạy xong thì 1 thư mục stories chứa các stories mẫu sẽ được tạo ra nằm trong thư mục src trong project của bạn.


Chạy Storybook
# Starts Storybook in development mode
npm run storybook
Chạy storybook cho project của bạn bằng lệnh trên, kết quả nhận được sẽ như hình dưới đây nếu mọi thứ làm việc ok.


Setup Storybook
Giờ chúng ta sẽ tìm cách hiển thị component trong project của mình lên storybook. Ví dụ chúng ta có 1 component là YourComponent, tạo các file .stories.js
, .stories.mdx
dùng để hiển thị component đó lên storybook và hướng dẫn cách sử dụng chúng.
// YourComponent.stories.js|jsx
import { YourComponent } from './YourComponent';
//
<img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" class="emoji" role="img" draggable="false" src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" alt="" /> This default export determines where your story goes in the story list
export default {
/* 
<img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" class="emoji" role="img" draggable="false" src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" alt="" /> 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: 'YourComponent',
component: YourComponent,
};
//
<img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" class="emoji" role="img" draggable="false" src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" alt="" /> We create a “template” of how args map to rendering
const Template = (args) => <YourComponent {...args} />;
export const FirstStory = {
args: {
//
<img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" class="emoji" role="img" draggable="false" src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" alt="" /> The args you need here will depend on your component
},
};
<!-- YourComponent.stories.mdx -->
import { Meta, Story } from '@storybook/addon-docs';
import { YourComponent } from './YourComponent';
<!--
<img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" class="emoji" role="img" draggable="false" src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" alt="" /> The title prop determines where your story goes in the story list -->
<Meta title="YourComponent" component={YourComponent} />
<!--
<img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" class="emoji" role="img" draggable="false" src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" alt="" /> We create a “template” of how args map to rendering -->
export const Template = (args) => <YourComponent {...args} />;
<!-- 
<img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" role="img" draggable="false" alt="" data-src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" class="emoji lazyload" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" /><noscript><img decoding="async" class="emoji" role="img" draggable="false" src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f447.svg" alt="" /> The args you need here will depend on your component -->
<Story
name="FirstStory"
args={{}}>
{Template.bind({})}
</Story>
Lưu lại và xem kết quả trên storybook của chúng ta (storybook hỗ trợ hot reload nên không cần chạy lại nhé các bạn)


Các bạn có thể xem full source code demo storybook ở đây nhé.
https://github.com/Akishina/reactjs-storybook
Cảm ơn mọi người đã đọc bài.
Bài viết gốc được đăng tải tại: anywayblogs.com
Theo dõi những bài viết mới nhất của TopDev nhé:
Xây dựng một ứng dụng trò chuyện bằng ReactJS trong 10 PHÚT (Phần 1)
Hướng dẫn sử dụng ReactJS Props và State
Xem thêm các việc làm React, việc làm ReactJS hấp dẫn tại TopDev