Trong quá trình phát triển giao diện người dùng (UI) cho các dự án, việc quản lý và kiểm thử các thành phần UI một cách độc lập là một thách thức lớn. Trong các dự án lớn, sẽ phải xây dựng rất nhiều base component, khi đó bạn sẽ rất cần đến một công cụ trực quan để quản lý và thử nghiệm các thành phần một cách dễ dàng. Đây chính là lúc Storybook phát huy tác dụng.
Bài viết này mình sẽ hướng dẫn bạn chi tiết về cách cài đặt và sử dụng Storybook với React và TypeScript (các bạn hoàn toàn có thể sử dụng Storybook cho framework khác như: Vue, Angular,...), giúp bạn có thể xây dựng và quản lý các thành phần UI một cách hiệu quả.
1. Storybook là gì?
Storybook là công cụ mã nguồn mở, hỗ trợ quá trình thiết kế và phát triển các UI Component cho ứng dụng trong một môi trường biệt lập, cho phép bạn kiểm thử và xem trước các components một cách độc lập mà không cần tích hợp vào ứng dụng chính.
Mỗi component được gán một "stories", mô tả các biến thể khác nhau của component, bao gồm tất cả các props và state có thể. Các stories giúp hiển thị trực quan cách component hoạt động trong các tình huống khác nhau, từ đó giúp quá trình phát triển dễ dàng hơn.
Bạn có thể thấy hình phía trên là cấu trúc của Storybook:
- Category (Danh mục): đây là mục lớn nhất, đại diện cho một hệ thống hoặc tập hợp lớn các thành phần UI có cùng một mục đích hoặc được phân nhóm chung. Ví dụ "Design System" là một danh mục.
- Folder (Thư mục): thường chứa các nhóm thành phần UI nhỏ hơn. Ví dụ, trong hình, "Atoms" là một thư mục, có thể chứa các thành phần UI đơn giản, nhỏ gọn.
- Component (Thành phần): thành phần UI cụ thể, như là một button. Trong ví dụ này, "Button" là một thành phần cụ thể nằm trong thư mục "Atoms".
- Docs (Tài liệu): tài liệu mô tả hoặc giải thích về thành phần UI, cung cấp thông tin chi tiết về cách sử dụng thành phần đó. Tài liệu thường đi kèm với ví dụ về code và cách sử dụng của component.
- Stories (Câu chuyện): mỗi thành phần UI có thể có nhiều trạng thái khác nhau. Một stories trong Storybook là một cách để mô tả biến thể cụ thể của thành phần UI. Ví dụ, "Primary" là một stories mô tả nút ở trạng thái mặc định trong ứng dụng.
2. Tại sao nên sử dụng Storybook?
- Storybook giúp bạn tập trung phát triển base component một cách độc lập mà không bị ảnh hưởng bởi những thành phần khác trong ứng dụng.
- Storybook tự động tạo ra tài liệu về components của bạn. Bạn không cần phải viết thêm tài liệu thủ công, mà mọi thông tin như kiểu dữ liệu, cách sử dụng sẽ được Storybook tự động hiển thị
- Storybook cho phép bạn xem components của mình ở nhiều trạng thái khác nhau. Ví dụ, bạn có thể xem trước button ở trạng thái bình thường, khi hover, khi disabled, hay khi loading.
3. Ưu, nhược điểm của Storybook
3.1 Ưu điểm của Storybook
- Storybook cho phép bạn phát triển và kiểm thử các component UI một cách độc lập mà không cần phải chạy toàn bộ ứng dụng. Điều này giúp tiết kiệm rất nhiều thời gian, đặc biệt khi bạn đang phát triển các thành phần nhỏ và cần kiểm tra chúng trong nhiều trường hợp khác nhau (như các trạng thái active, hover, disabled, hoặc với các dữ liệu khác nhau).
- Storybook có một hệ sinh thái addon mạnh mẽ, cho phép bạn mở rộng chức năng của nó với các tính năng như tạo tài liệu tự động (Docs addon), mô phỏng sự kiện người dùng (Actions addon), hoặc thay đổi props trực tiếp từ giao diện (Knobs addon).
- Storybook giúp bạn tạo ra một môi trường trực quan, nơi mà các team có thể xem trước các thành phần giao diện mà không cần phải cài đặt toàn bộ ứng dụng.
- Storybook tự động tạo ra một tài liệu cho các component của bạn. Team design và team frontend có thể làm việc cùng nhau hiệu quả hơn mà không phải mất thời gian tạo tài liệu thủ công.
3.2 Nhược điểm của Storybook
- Mặc dù Storybook rất mạnh trong việc phát triển các component độc lập, nhưng khi bạn có những thành phần phức tạp có sự tương tác sâu với nhau (như việc sử dụng nhiều state chia sẻ), Storybook có thể không phải là giải pháp lý tưởng.
- Storybook giúp kiểm tra các component UI một cách độc lập, nhưng nó không thể thay thế hoàn toàn việc kiểm thử UI trong ứng dụng thực tế.
Components có thể hoạt động tốt trong Storybook nhưng vẫn có thể gặp vấn đề khi được tích hợp vào ứng dụng do sự khác biệt về môi trường, API, hoặc các trạng thái của ứng dụng. - Storybook có thể bị ảnh hưởng về hiệu suất khi bạn làm việc với các dự án lớn hoặc có quá nhiều component. Khi có quá nhiều câu chuyện và component, giao diện Storybook có thể bị chậm và việc khởi động Storybook có thể mất nhiều thời gian hơn.
4. Hướng dẫn sử dụng Storybook với dự án Typescript
Trước khi cài đặt Storybook, bạn cần có một project React. Nếu bạn đã có project rồi, bạn có thể bỏ qua bước này. Nếu chưa, bạn có thể tham khảo tại đây.
B1: Cài đặt Storybook
Storybook có thể được cài đặt dễ dàng bằng cách sử dụng lệnh sau:
npx storybook init
Lệnh này sẽ tự động cấu hình Storybook cho project của bạn.
Khi quá trình cài đặt hoàn tất, bạn sẽ thấy một số thay đổi trong thư mục của project:
.storybook/
: đây là thư mục chứa các file cấu hình của Storybook, bao gồm main.ts và preview.tssrc/stories/
: Đây là thư mục chứa các câu chuyện (stories) mẫu. Bạn có thể sử dụng những câu chuyện này để tham khảo hoặc viết lại cho riêng mình.- Trong file
package.json
sẽ thêm 2 câu lệnh:
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
B2: Bây giờ, bạn có thể khởi động Storybook để kiểm tra xem quá trình cài đặt đã thành công hay chưa
npm run storybook
Lệnh này sẽ khởi động Storybook trên http://localhost:6006
. Khi mở trình duyệt, bạn sẽ thấy giao diện Storybook và các component mẫu.
B3: Cấu hình Typescript cho Storybook
Storybook đã hỗ trợ TypeScript ngay khi cài đặt. Tuy nhiên, để chắc chắn rằng TypeScript được cấu hình đúng, bạn có thể kiểm tra và chỉnh sửa file .storybook/main.ts
như sau:
import type { StorybookConfig } from '@storybook/react-vite'
const config: StorybookConfig = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
'@storybook/addon-onboarding',
'@storybook/addon-links',
'@storybook/addon-essentials',
'@chromatic-com/storybook',
'@storybook/addon-interactions'
],
framework: {
name: '@storybook/react-vite',
options: {}
}
}
export default config
Mình sẽ lấy một ví dụ mà storybook mẫu cho các bạn hiểu nhé. Đây là button component
import React from 'react';
import './button.css';
export interface ButtonProps {
/** Is this the principal call to action on the page? */
primary?: boolean;
/** What background color to use */
backgroundColor?: string;
/** How large should the button be? */
size?: 'small' | 'medium' | 'large';
/** Button contents */
label: string;
/** Optional click handler */
onClick?: () => void;
}
/** Primary UI component for user interaction */
export const Button = ({
primary = false,
size = 'medium',
backgroundColor,
label,
...props
}: ButtonProps) => {
const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary';
return (
<button
type="button"
className={['storybook-button', `storybook-button--${size}`, mode].join(' ')}
style={{ backgroundColor }}
{...props}
>
{label}
</button>
);
};
- primary: xác định button này có phải là button chính trên giao diện hay không. Nếu là primary, sẽ sử dụng một lớp CSS đặc biệt (được định nghĩa trong file
button.css
). - backgroundColor: thuộc tính này xác định màu nền của button, nhận giá trị kiểu string và có thể được đặt trực tiếp bằng cách sử dụng thuộc tính style.
- size: thuộc tính này xác định kích thước của button. Các giá trị có thể là 'small', 'medium', hoặc 'large', và mỗi kích thước sẽ có một style tương ứng.
- label: nội dung hiển thị trên button. Đây là giá trị bắt buộc (required), khác với các thuộc tính khác là tùy chọn (optional).
- onClick: sự kiện sẽ được gọi khi người dùng nhấn vào button
Tiếp theo, khi có button component rồi, mình sẽ đi xây dựng stories cho component đó, cụ thể:
import type { Meta, StoryObj } from '@storybook/react'
import { fn } from '@storybook/test'
import { Button } from './Button'
// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
const meta = {
title: 'Example/Button',
component: Button,
parameters: {
// Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
layout: 'centered'
},
// This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
tags: ['autodocs'],
// More on argTypes: https://storybook.js.org/docs/api/argtypes
argTypes: {
backgroundColor: { control: 'color' }
},
// Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
args: { onClick: fn() }
} satisfies Meta<typeof Button>
export default meta
type Story = StoryObj<typeof meta>
// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
export const Primary: Story = {
args: {
primary: true,
label: 'Button'
}
}
export const Secondary: Story = {
args: {
label: 'Button'
}
}
export const Large: Story = {
args: {
size: 'large',
label: 'Button'
}
}
export const Small: Story = {
args: {
size: 'small',
label: 'Button'
}
}
- title: là tên của component trong Storybook UI, cho phép bạn nhóm và tổ chức các stories một cách hợp lý. Ví dụ, component Button sẽ nằm trong danh mục Example và được gọi là Button.
- component: xác định component Button mà chúng ta muốn hiển thị trong các stories.
- parameters.layout: cấu hình này đặt button ở giữa màn hình trong giao diện Storybook (Canvas). Bạn có thể sử dụng các layout khác như fullscreen hoặc padded.
- tags: ['autodocs']: là tính năng tự động tạo tài liệu (Autodocs) cho các component trong Storybook. Khi sử dụng tag này, Storybook sẽ tự động tạo ra tài liệu cho component dựa trên các props và các câu chuyện mà bạn định nghĩa.
- argTypes: là nơi bạn định nghĩa loại dữ liệu cho các props của component. Ví dụ, bạn có thể thay đổi backgroundColor từ giao diện Storybook mà không cần thay đổi code. Ở đây,
control: 'color'
cho phép bạn chọn màu trực tiếp từ giao diện. - args: là các giá trị mặc định cho props của component. Trong ví dụ này, onClick là một spy function (fn()), giúp ghi lại các sự kiện khi button được nhấn và hiển thị trong bảng Actions.
- satisfies Meta<typeof Button>: là cú pháp TypeScript giúp đảm bảo meta phù hợp với kiểu dữ liệu của component Button. Điều này giúp tránh các lỗi type khi bạn định nghĩa các stories.
Tại sao bạn nên sử dụng args?
- args cho phép bạn cung cấp giá trị cho các props của component ngay trong stories, giúp bạn dễ dàng thay đổi trạng thái của component.
- Khi bạn chỉnh sửa các args từ giao diện Storybook, nó sẽ cập nhật các giá trị props trực tiếp mà không cần khởi động lại ứng dụng.
5. Kết luận
Qua quá trình tìm hiểu và triển khai Storybook với component Button, mình tin rằng các bạn đã nắm được những lợi ích mà storybook mang lại, cách triển khai storybook cho dự án React của bạn.
Các bài viết liên quan:
Bài viết liên quan
React Toastify là gì? Hướng dẫn sử dụng Toast Notification với React Toastify
Nov 21, 2024 • 7 min read
Hướng dẫn sử dụng Zustand trong NextJS
Nov 21, 2024 • 8 min read
Lazy Loading: Kỹ thuật Tối ưu Hiệu suất Website
Nov 17, 2024 • 14 min read
Hướng dẫn sử dụng Redux Toolkit và Redux Saga trong dự án React
Nov 15, 2024 • 10 min read
WebGL là gì? Hướng dẫn tạo đồ họa đơn giản với WebGL
Nov 13, 2024 • 7 min read
Test-Driven Development (TDD) là gì? Hướng dẫn thực hành TDD
Nov 13, 2024 • 6 min read