Storybook.js untuk Web Components: Membangun, Mendokumentasikan, dan Menguji Komponen UI Mandiri
1. Pendahuluan
Sebagai seorang developer web, kita sering dihadapkan pada tantangan untuk membangun komponen UI yang reusable, maintainable, dan konsisten di berbagai proyek. Terlebih lagi, ketika kita bekerja dalam tim atau di lingkungan design system, dokumentasi yang jelas dan cara untuk menguji setiap state komponen menjadi krusial.
Di sinilah Web Components dan Storybook.js datang sebagai pasangan yang serasi. Web Components menawarkan cara standar untuk membangun komponen UI yang mandiri dan framework-agnostic, sementara Storybook menyediakan isolated development environment untuk mengembangkan, mendokumentasikan, dan menguji komponen-komponen tersebut.
Bayangkan Anda sedang membangun sebuah tombol (<my-button>). Tombol ini mungkin memiliki berbagai state: aktif, nonaktif, loading, dengan ikon, tanpa ikon, dll. Tanpa Storybook, Anda harus menjalankan seluruh aplikasi, menavigasi ke halaman yang relevan, dan memanipulasi state aplikasi hanya untuk melihat bagaimana tombol tersebut berperilaku. Ini tidak efisien dan rentan kesalahan.
Dengan Storybook, Anda bisa melihat semua state tombol Anda dalam satu tempat, berinteraksi dengannya, dan bahkan membagikannya dengan desainer atau product manager tanpa perlu menjalankan aplikasi utama. Artikel ini akan memandu Anda cara mengintegrasikan Storybook.js dengan Web Components Anda, sehingga alur kerja pengembangan UI Anda menjadi jauh lebih efisien dan menyenangkan.
2. Mengapa Web Components dan Storybook Adalah Pasangan Ideal?
Sebelum kita masuk ke praktik, mari kita pahami mengapa kombinasi ini sangat powerful:
- Web Components: Ini adalah standar web native yang memungkinkan Anda membuat elemen HTML kustom yang dienkapsulasi. Artinya, style dan perilaku komponen Anda tidak akan bocor ke luar atau terpengaruh oleh bagian lain dari aplikasi. Web Components bersifat framework-agnostic, jadi Anda bisa menggunakannya di React, Vue, Angular, atau bahkan Vanilla JS.
- Keunggulan: Reusabilitas tinggi, isolasi yang kuat, tidak terikat framework.
- Storybook.js: Sebuah tool UI development environment yang memungkinkan Anda membangun komponen UI secara terisolasi. Anda bisa mendefinisikan “stories” yang merepresentasikan berbagai state visual komponen Anda.
- Keunggulan: Pengembangan terisolasi (faster iteration), dokumentasi interaktif, visual testing, snapshot testing, dan kolaborasi lintas tim yang lebih baik.
📌 Analogi: Jika Web Components adalah bata lego individual yang bisa Anda buat sendiri dengan instruksi perakitan yang jelas, maka Storybook adalah meja kerja khusus dengan lampu sorot dan katalog lengkap di mana Anda bisa merakit dan memamerkan setiap bata lego Anda dalam berbagai bentuk dan warna, tanpa harus membangun seluruh istana lego terlebih dahulu.
3. Setup Awal Storybook untuk Web Components
Mari kita mulai dengan menyiapkan proyek sederhana. Kita akan menggunakan Lit, sebuah library ringan untuk membangun Web Components, karena Lit membuat prosesnya lebih mudah dan menyenangkan.
Pertama, buat proyek baru:
# Buat direktori proyek
mkdir my-web-component-project
cd my-web-component-project
# Inisialisasi npm
npm init -y
# Install Lit
npm install lit
Sekarang, mari kita instal Storybook. Storybook memiliki preset untuk Web Components, yang akan mempermudah konfigurasi awal.
npx storybook@latest init --type web-components
Perintah di atas akan:
- Mendeteksi jenis proyek Anda (berdasarkan
package.jsondan dependensi). - Menginstal semua dependensi Storybook yang diperlukan.
- Membuat file konfigurasi Storybook (
.storybook/main.js,.storybook/preview.js). - Menambahkan script
storybookkepackage.jsonAnda. - Membuat contoh komponen dan stories (
src/stories).
Setelah instalasi selesai, Anda bisa menjalankan Storybook dengan:
npm run storybook
Ini akan membuka Storybook di browser Anda (biasanya http://localhost:6006). Anda akan melihat contoh komponen yang sudah dibuatkan oleh Storybook.
4. Menulis Stories Pertama Anda untuk Komponen Kustom
Sekarang, mari kita buat Web Component kita sendiri dan tulis stories untuknya. Kita akan membuat komponen tombol sederhana.
src/components/my-button.js:
import { LitElement, html, css } from 'lit';
export class MyButton extends LitElement {
static properties = {
label: { type: String },
variant: { type: String }, // primary, secondary, danger
disabled: { type: Boolean },
};
static styles = css`
:host {
display: inline-block;
}
button {
padding: 10px 20px;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
transition: background-color 0.2s ease;
}
button:hover {
opacity: 0.9;
}
button:disabled {
opacity: 0.6;
cursor: not-allowed;
}
/* Variants */
.primary {
background-color: #007bff;
color: white;
}
.secondary {
background-color: #6c757d;
color: white;
}
.danger {
background-color: #dc3545;
color: white;
}
`;
constructor() {
super();
this.label = 'Click Me';
this.variant = 'primary';
this.disabled = false;
}
render() {
return html`
<button class="${this.variant}" ?disabled="${this.disabled}">
${this.label}
</button>
`;
}
}
customElements.define('my-button', MyButton);
Sekarang, mari kita buat story untuk komponen <my-button> ini.
src/components/my-button.stories.js:
import { html } from 'lit';
import '../components/my-button.js'; // Import komponen kita
// Metadata tentang komponen kita
export default {
title: 'Components/MyButton', // Nama di Storybook UI
component: 'my-button', // Nama tag Web Component kita
parameters: {
layout: 'centered', // Untuk menempatkan komponen di tengah kanvas Storybook
},
// Definisi argumen (props) yang bisa diubah di Storybook UI
argTypes: {
label: { control: 'text', description: 'Teks yang ditampilkan pada tombol.' },
variant: {
control: 'select',
options: ['primary', 'secondary', 'danger'],
description: 'Gaya visual tombol.',
},
disabled: { control: 'boolean', description: 'Menentukan apakah tombol dinonaktifkan.' },
onClick: { action: 'clicked', description: 'Event handler saat tombol diklik.' },
},
};
// Template dasar untuk Story kita
const Template = ({ label, variant, disabled, onClick }) =>
html`<my-button
.label="${label}"
.variant="${variant}"
?disabled="${disabled}"
@click="${onClick}"
></my-button>`;
// Definisi Stories
export const Primary = Template.bind({});
Primary.args = {
label: 'Primary Button',
variant: 'primary',
};
export const Secondary = Template.bind({});
Secondary.args = {
label: 'Secondary Button',
variant: 'secondary',
};
export const Danger = Template.bind({});
Danger.args = {
label: 'Danger Button',
variant: 'danger',
};
export const Disabled = Template.bind({});
Disabled.args = {
label: 'Disabled Button',
disabled: true,
};
export const WithCustomLabel = Template.bind({});
WithCustomLabel.args = {
label: 'Kirim Data',
variant: 'primary',
};
💡 Tips Penting:
title: Strukturkan dengan baik (misal:Components/NamaKomponen) agar mudah dinavigasi.component: Pastikan ini sesuai dengan nama tag kustom Web Component Anda (my-button).argTypes: Ini adalah kunci untuk membuat kontrol interaktif di Storybook UI. Anda bisa mengubahlabel,variant, ataudisabledsecara langsung dan melihat perubahannya secara real-time..label="${label}"dan?disabled="${disabled}": Ini adalah cara Lit mengikat properti dan atribut Boolean. Pastikan sesuai dengan bagaimana Web Component Anda menerima properti.
Setelah Anda menyimpan file ini dan Storybook berjalan, Anda akan melihat MyButton di sidebar. Klik di sana, dan Anda akan melihat semua stories yang telah Anda definisikan, lengkap dengan kontrol interaktif!
5. Dokumentasi Otomatis dan Interaktif
Salah satu fitur terbaik Storybook adalah kemampuannya untuk menghasilkan dokumentasi komponen secara otomatis. Dengan addon Docs, setiap story Anda tidak hanya menampilkan komponen, tetapi juga cuplikan kode, tabel properti (berdasarkan argTypes), dan ruang untuk menambahkan deskripsi Markdown.
Anda tidak perlu melakukan konfigurasi tambahan jika Anda menggunakan npx storybook@latest init. Addon Docs sudah terinstal dan terkonfigurasi secara default.
✅ Manfaat Dokumentasi Otomatis:
- Konsistensi: Semua komponen didokumentasikan dengan format yang sama.
- Up-to-date: Dokumentasi selalu sesuai dengan kode karena dihasilkan dari stories.
- Kolaborasi: Desainer, product manager, dan developer lain bisa dengan mudah memahami dan menguji komponen tanpa perlu melihat kode implementasi.
- Contoh Live: Setiap story adalah contoh live yang bisa diinteraksikan.
Untuk melihat dokumentasi, di Storybook UI, navigasikan ke komponen MyButton, lalu klik tab “Docs”. Anda akan melihat detail setiap story, properti yang tersedia, dan contoh penggunaan.
6. Menguji Komponen dengan Play Function
Storybook tidak hanya untuk pengembangan dan dokumentasi, tetapi juga untuk pengujian! Dengan play function dan addon @storybook/addon-interactions, Anda bisa menulis skenario pengujian interaksi langsung di Storybook. Ini memungkinkan Anda untuk memverifikasi perilaku komponen secara otomatis di berbagai state.
Mari kita tambahkan play function ke salah satu story tombol kita.
Pertama, pastikan Anda memiliki @storybook/addon-interactions dan @storybook/test terinstal. Jika Anda menggunakan npx storybook@latest init, ini seharusnya sudah ada.
// src/components/my-button.stories.js (lanjutan)
import { html } from 'lit';
import { userEvent, within } from '@storybook/test'; // Import untuk pengujian interaksi
import '../components/my-button.js';
// ... (metadata dan Template yang sudah ada) ...
export const InteractivePrimary = Template.bind({});
InteractivePrimary.args = {
label: 'Interactive Button',
variant: 'primary',
};
InteractivePrimary.play = async ({ canvasElement, args }) => {
const canvas = within(canvasElement);
const button = canvas.getByRole('button');
// Simulasi klik tombol
await userEvent.click(button);
// Verifikasi bahwa event onClick telah dipanggil
// args.onClick adalah action mock yang didefinisikan di argTypes
await expect(args.onClick).toHaveBeenCalled();
// Verifikasi bahwa tombol tidak disabled
await expect(button).not.toBeDisabled();
};
export const DisabledInteraction = Template.bind({});
DisabledInteraction.args = {
label: 'Disabled Button',
disabled: true,
variant: 'secondary',
};
DisabledInteraction.play = async ({ canvasElement, args }) => {
const canvas = within(canvasElement);
const button = canvas.getByRole('button');
// Coba klik tombol yang disabled
await userEvent.click(button);
// Verifikasi bahwa event onClick TIDAK dipanggil
await expect(args.onClick).not.toHaveBeenCalled();
// Verifikasi bahwa tombol memang disabled
await expect(button).toBeDisabled();
};
Ketika Anda melihat story InteractivePrimary atau DisabledInteraction di Storybook, Anda akan melihat indikator “Play” dan proses pengujian akan dijalankan secara otomatis. Jika ada kegagalan, Anda akan melihat pesan error. Ini sangat berguna untuk:
- Visual Debugging: Melihat secara langsung langkah-langkah interaksi dan hasilnya.
- Regression Testing: Memastikan interaksi komponen tidak rusak saat ada perubahan kode.
- Dokumentasi Perilaku: Menunjukkan bagaimana komponen seharusnya berinteraksi.
🎯 Target: Memastikan bahwa komponen UI Anda tidak hanya terlihat benar, tetapi juga berperilaku sesuai harapan di berbagai skenario interaksi.
7. Tips dan Best Practices untuk Alur Kerja yang Optimal
Untuk memaksimalkan penggunaan Storybook dengan Web Components Anda, pertimbangkan tips berikut:
- Organisasi Stories:
- Kelompokkan stories berdasarkan jenis komponen (misal:
Components/Buttons,Layout/Cards). - Gunakan
Metauntuk story utama danexport const NamaStoryuntuk varian individual.
- Kelompokkan stories berdasarkan jenis komponen (misal:
- CSS Global/Theming:
- Jika Web Components Anda bergantung pada CSS global atau variabel CSS untuk theming, Anda bisa mengimpornya di
.storybook/preview.js. - Contoh:
// .storybook/preview.js import '../src/styles/global.css'; // Import global styles Anda export const parameters = { // ... };
- Jika Web Components Anda bergantung pada CSS global atau variabel CSS untuk theming, Anda bisa mengimpornya di
- Addons Storybook:
- Jelajahi addon lain seperti
Viewport(untuk menguji responsivitas),Backgrounds(mengubah warna latar belakang kanvas),Accessibility(menguji aksesibilitas komponen). - Instal dan konfigurasikan addon di
.storybook/main.jsatau.storybook/preview.js.
- Jelajahi addon lain seperti
- CI/CD Integration:
- Integrasikan Storybook ke dalam pipeline CI/CD Anda. Anda bisa menggunakannya untuk visual regression testing (memastikan tidak ada perubahan visual yang tidak diinginkan) atau menjalankan
playfunctions untuk interaction testing. - Storybook juga bisa di-deploy sebagai situs statis (
build-storybook) yang bisa dibagikan sebagai living documentation untuk tim Anda.
- Integrasikan Storybook ke dalam pipeline CI/CD Anda. Anda bisa menggunakannya untuk visual regression testing (memastikan tidak ada perubahan visual yang tidak diinginkan) atau menjalankan
Kesimpulan
Mengintegrasikan Storybook.js dengan Web Components adalah strategi yang sangat efektif untuk membangun, mendokumentasikan, dan menguji komponen UI yang robust dan reusable. Anda tidak hanya akan mempercepat proses pengembangan, tetapi juga meningkatkan kualitas kode, konsistensi UI, dan kolaborasi dalam tim. Dengan isolasi yang ditawarkan Storybook, Anda bisa fokus pada satu komponen pada satu waktu, memastikan setiap state dan interaksi berfungsi dengan sempurna sebelum diintegrasikan ke dalam aplikasi yang lebih besar.
Jadi, tunggu apa lagi? Mulailah petualangan Anda dengan Storybook dan Web Components, dan rasakan sendiri perbedaannya dalam alur kerja pengembangan UI Anda!
🔗 Baca Juga
- Storybook.js: Membangun dan Mendokumentasikan Komponen UI Secara Efisien
- Membuat Komponen Web yang Fleksibel dan Mudah Ditemakan: Menguasai CSS Custom Properties dan ::part Pseudo-element
- Menguasai Lifecycle Custom Elements Vanilla: Membangun Web Components yang Robust dan Prediktif
- CSS Custom Highlight API: Membangun Fitur Penanda Teks Kustom yang Powerful dan Efisien di Web