ما هو Storybook وكيف تبني مكتبة مكونات React احترافية به؟
مقدمة إلى Storybook ومكتبات المكونات
تساعد أطر العمل الحديثة مثل React وVue وAngular المطورين على بناء أنظمة معيارية باستخدام المكونات (components). ومع ذلك، غالبًا ما تفتقر هذه الأطر إلى طريقة واضحة لعرض جميع المكونات من منظور شامل. هنا يأتي دور Storybook، الأداة القوية التي تتيح لنا بناء مكتبات مكونات وأنظمة تصميم توثق نفسها ذاتيًا أثناء عملية التطوير.
ما هو Storybook؟
Storybook هو أداة مبنية على JavaScript تمكن المطورين من إنشاء أنظمة واجهة مستخدم (UI systems) منظمة، مما يجعل عملية البناء والتوثيق أكثر كفاءة وسهولة في الاستخدام. إنه يوفر بيئة معزولة لتطوير واختبار المكونات بشكل مستقل عن التطبيق الرئيسي.

بمجرد بناء المكون، يسمح لك Storybook بإنشاء ملف “قصة” (story file) حيث يمكنك استيراد المكون وإنشاء أمثلة مختلفة لحالات الاستخدام ضمن بيئة معزولة (iFramed sandbox). يوفر هذا بيئة منظمة ومركزة لبناء مكونات جديدة والعمل على المكونات الموجودة، مما يضمن الاتساق وسهولة الصيانة.
ماذا سنبني في هذا الدليل؟
سنقوم بتهيئة تطبيق React JS جديد باستخدام أداة Create React App. داخل هذا التطبيق، سنقوم بتثبيت Storybook وإنشاء بعض المكونات الجديدة. سيساعدنا هذا على تعلم كيفية بناء مكونات يمكننا العمل عليها في “قصة” (story) ثم استخدامها في تطبيق React.

الخطوة 0: تهيئة تطبيق React
للبدء، سنبدأ من الصفر باستخدام Create React App. سيساعدنا هذا على التركيز على الإنتاجية مع Storybook بدلاً من استعراض كيفية دمجه في تطبيق موجود. ومع ذلك، إذا كنت تعمل بالفعل مع تطبيق تم إنشاؤه باستخدام Create React App ولم يتم “إخراجه” (ejected)، فيجب أن تكون قادرًا على المتابعة من الخطوة 1 وما بعدها بنفس الطريقة!
لنبدأ بالانتقال إلى المكان الذي نريد إنشاء تطبيقنا الجديد فيه وتشغيل أمر Create React App:
npx create-react-app my-storybook
ملاحظة: لا تتردد في استبدال my-storybook باسم الدليل الذي تختاره.

بمجرد الانتهاء من التشغيل، يمكنك الانتقال إلى الدليل:
cd my-storybook
والآن أصبحنا جاهزين للانطلاق!
الخطوة 1: تثبيت Storybook
يجعل Storybook عملية البدء سهلة للغاية مع تثبيت React القياسي. خاصة مع Create React App، يكتشف Storybook تلقائيًا أننا نستخدم تطبيقًا تم إنشاؤه بواسطة CRA ويقوم بتثبيت التبعيات وتهيئة كل شيء لنا.
تهيئة Storybook
للبدء في تثبيت Storybook، قم بتشغيل الأمر التالي:
npx -p @storybook/cli sb init

إذا لم تكن تستخدم Create React App أو لم ينجح الأمر، يمكنك مراجعة الأدلة المتاحة في وثائقهم. بعد الانتهاء من ذلك، يجب أن تكون جميع تبعيات Storybook مثبتة.

تشغيل Storybook
الآن نحن جاهزون للعمل! أخيرًا، قم بتشغيل:
yarn storybook
# أو
npm run storybook
وبمجرد الانتهاء من تحميل كل شيء، سيفتح Storybook علامة تبويب جديدة في متصفحك ويجب أن ترى الآن رسالة ترحيب داخل لوحة تحكم Storybook الجديدة!

الخطوة 2: إنشاء زر جديد
إذا ألقيت نظرة سريعة على لوحة التحكم، فربما لاحظت أنها تأتي محملة مسبقًا بزر (Button) متاح كعرض توضيحي.

يجب أن تلاحظ أيضًا أنه إذا نقرت على الزر، فسترى فعلاً إجراءً يظهر في علامة التبويب Actions في الأسفل. يوضح هذا الحدث الذي تم التقاطه من نقرة الزر. إنه بسيط، لكنه رائع للحصول على شعور جيد بما تتوقعه في Storybook. المشكلة الوحيدة هي أن هذا مخصص لأغراض العرض التوضيحي فقط، لذا دعنا نبني زرنا الخاص لاستبداله.
إنشاء مكون زر (Button) جديد
للبدء، دعنا أولاً ننشئ بعض الأدلة:
- ضمن
src، أنشئ مجلدًا جديدًا يسمىcomponents. - ضمن
components، أنشئ مجلدًا جديدًا يسمىButton.
بمجرد إنشاء هذه المجلدات، أنشئ ملفًا جديدًا يسمى index.js داخل مجلد src/components/Button، وأضف بداخله:
// داخل src/components/Button/index.js
export { default } from './Button';
سيؤدي هذا إلى استيراد الملف التالي الذي أنشأناه والذي يسمى Button.js مما سيتيح لنا استيراد ملفاتنا بسهولة أكبر باستخدام src/components/Button بدلاً من /src/components/Button/Button.
بعد ذلك، دعنا ننشئ Button.js بجوار ملف index.js الخاص بنا بالمحتوى التالي:
// داخل src/components/Button/Button.js
import React from 'react';
const Button = ({ children, ...rest }) => {
return (
<button className="button" { ...rest }>
{ children }
</button>
)
}
export default Button;
هنا، نقوم بإنشاء مكون جديد يسمى Button يضيف فئة (class) باسم button إلى العنصر ويمرر الـ children. بالإضافة إلى ذلك، نقوم بتفكيك بقية الـ props إلى المتغير rest وننشر هذه القيمة في عنصر <button>.
إذا اتبعت الخطوات، يجب أن تبدو ملفاتك الآن كما يلي:

استخدام مكون الزر (Button) الجديد
الآن بعد أن أصبح لدينا مكون Button، دعنا نستخدمه! افتح الملف src/stories/1-Button.stories.js واستبدل السطر الذي يستورد Button بـ:
import Button from '../components/Button';

وبمجرد حفظ الملف، يمكنك فتح علامة تبويب المتصفح الخاصة بلوحة تحكم Storybook، وسترى الآن زرًا يبدو متشابهًا إلى حد كبير، لكنه يستخدم أنماط المتصفح الافتراضية لعنصر <button>. ستلاحظ حتى أنه إذا نقرت عليه، فسيتم تسجيل الحدث ضمن علامة التبويب Actions.
تنسيق مكون الزر (Button) الخاص بنا
أخيرًا، ربما لا نريد استخدام أنماط المتصفح الافتراضية، لذا دعنا نجعلها تبدو جميلة. في دليل src/components/Button الخاص بنا، أضف ملفًا جديدًا Button.css وأضف المحتوى التالي:
/* داخل src/components/Button/Button.css */
.button {
color: white;
font-weight: bold;
background-color: blueviolet;
border: none;
padding: .8em 1em;
border-radius: .2rem;
}
يطبق هذا بعض الأنماط على فئة .button الخاصة بنا مثل إضافة لون خلفية وتغيير لون الخط إلى الأبيض. ولكن إذا فتحت Storybook، ستلاحظ أنه لم يفعل شيئًا. لاستخدامه، نحتاج إلى استيراده إلى مكوننا. داخل src/components/Button/Button.js أضف ما يلي في الأعلى تحت استيراد React:
import './Button.css';
وبمجرد حفظ ذلك وفتح متصفحك، يجب أن ترى الآن زرنا الجديد بأنماطنا المحدثة!

الخطوة 3: استخدام مكون الزر (Button) الجديد في التطبيق
الهدف النهائي من مكوننا هو استخدامه في التطبيق، أليس كذلك؟ لذا دعنا نضيفه إلى تطبيقنا.
التبديل إلى تطبيق React
أولاً، سنحتاج إما إلى بدء تطبيق React الخاص بنا في علامة تبويب طرفية جديدة أو إنهاء عملية Storybook وبدء عملية React هناك. لبدء تطبيق React باستخدام Create React App، قم بتشغيل:
yarn start
# أو
npm run start
بمجرد تحميله، يجب أن يكون لدينا تطبيق Create React App القياسي إذا كنت تتابع معي:

استيراد واستخدام الزر الجديد
بعد ذلك، داخل src/App.js، دعنا نستورد زرنا الجديد في أعلى الصفحة:
import Button from './components/Button';
مع استيراد Button، يمكننا استخدامه. هنا، يمكننا ببساطة إضافته في أي مكان نريده في الصفحة. سأقوم باستبدال رابط Learn React بـ:
<p>
<Button>Hello, Storybook!</Button>
</p>
وإذا قمنا بالحفظ وإعادة تحميل الصفحة، يجب أن نرى الآن زرنا على الصفحة!

تكرار العملية: إنشاء مكون رأس (Header) جديد
الشيء الرائع في Storybook وReact (أو أي من أطر العمل المدعومة) هو أن هذه العملية تتسع لعدد المكونات التي تريدها. لذا دعنا نبني مكونًا آخر!
إنشاء مكون الرأس (Header) الخاص بنا
على غرار زرنا (Button)، دعنا نبدأ بإنشاء مجموعة الأدلة والملفات التي تمنحنا مكوننا. نظرًا لأننا فعلنا ذلك مرة واحدة بالفعل، سأقدم الكود دون استعراض ما يحدث. دعنا نبدأ بتشغيل خادم Storybook الخاص بنا مرة أخرى بـ:
yarn storybook
# أو
npm run storybook
- أنشئ دليل
Headerداخل دليلsrc/components. - أنشئ ملف
index.jsداخلsrc/components/Headerبالمحتوى التالي:
// في src/components/Header/index.js
export { default } from './Header';
- أنشئ ملف
Header.jsداخلsrc/components/Headerبالمحتوى التالي:
// في src/components/Header/Header.js
import React from 'react';
import './Header.css';
const Header = ({ children }) => {
return (
<h2 className="header">
{ children }
</h2>
)
}
export default Header;
- أنشئ ملف
Header.cssداخلsrc/components/Headerبالمحتوى التالي:
/* في src/components/Header/Header.css */
.header {
font-family: sans-serif;
font-size: 2.5em;
color: blueviolet;
border-bottom: solid 5px aqua;
padding-bottom: .2em;
box-shadow: 0 5px 0 blueviolet;
}
الآن إذا لاحظت، إذا حاولت فتح Storybook، مرة أخرى، لن يحدث شيء. هذه المرة نحتاج إلى إنشاء ملف قصة جديد.
إنشاء ملف قصة (Story) جديد
داخل src/stories، أضف ملفًا جديدًا يسمى 2-Header.stories.js:
// داخل src/stories/2-Header.stories.js
import React from 'react';
import Header from '../components/Header';
export default {
title: 'Header',
component: Header,
};
export const Text = () => <Header>Hello Header</Header>;
إليك تفصيل لملف القصة الخاص بنا:
- أولاً، نقوم باستيراد مكوننا – وهذا أمر قياسي في أي وقت نريد استخدامه.
- أول شيء نقوم بتصديره هو كائن
default. معStorybook، يتوقع أن يكون التصدير الافتراضي هو تهيئة قصتنا، لذلك هنا نقدم له عنوانًا (title) ونمرر المكون (component) الذي نستخدمه لهذه القصة. - الشيء الثاني والأخير الذي نقوم بتصديره هو الثابت
Text. معStorybook، يعتبر أي تصدير غير افتراضي بمثابة اختلاف سيتم تداخله تحت العنوان الذي تقدمه في التصدير الافتراضي.
وإذا حفظت هذا الملف وفتحت لوحة تحكم Storybook في المتصفح، يجب أن ترى الآن الرأس الجديد!

استخدام مكون الرأس (Header)
استخدام مكوننا هو نفس استخدام مكون الزر (Button) الخاص بنا، لذا داخل src/App.js، دعنا نضيف رأسنا. بعد بدء خادم React الخاص بك، استورد رأسنا الجديد أولاً:
// في src/App.js
import Header from './components/Header';
ثم أضفه إلى أعلى الصفحة:
// في src/App.js
<Header>تطبيقي</Header>
وإذا فتحت الصفحة، فسنرى رأسنا الجديد!

إضافة المزيد من المكونات
كما لاحظت مع خطوة التكرار الثانية – إضافة مكون جديد هي نفس العملية تقريبًا لأي نوع من المكونات التي نريد إضافتها. بمجرد أن يكون لدينا في مكتبتنا، يمكننا تطويره في بيئة مركزة ثم استيراده إلى تطبيقنا لاستخدامه. يمكنك الآن استخدام هذا لإدارة مكتبة المكونات الخاصة بك والحفاظ بشكل أفضل على نظام كامل لمشروعك!
ميزات Storybook إضافية
لا يتوقف Storybook عند مجرد إضافة المكونات، بل يوفر القدرة على تهيئة الإضافات (Addons) التي تعزز القدرات الأساسية وتفتح الكثير من الاحتمالات. إليك بعض من المفضلات لدي:
Story Source
عند بناء نظام مكونات، يكون الأمل هو أن يتمكن الأشخاص من استخدام هذه المكونات بسهولة. ولكن إذا لم يكن لديك توثيق، فسيتعين على شخص ما فتح الملف أو محاولة العثور على مثال استخدام آخر. بدلاً من ذلك، يعرض Story Source الكود المصدري لملف القصة الذي أنشأته مما يسمح لأي شخص يتصفح لوحة تحكم Storybook بالحصول على مثال مباشر جنبًا إلى جنب مع مخرجات المكون!

Storyshots
إذا كنت من محبي الاختبارات الآلية، فربما سمعت عن استخدام Jest أو أداة أخرى لإضافة اختبار اللقطات (snapshot testing) إلى تطبيقك. Storyshots هو طريقة سهلة لإضافة اختبار لقطات Jest إلى نظام المكونات الخاص بك. يقوم بإنشاء لقطات بناءً على القصص التي تنشئها حتى تتمكن من التأكد من أن مكوناتك لا تتغير بشكل جوهري (أو تتعطل) أثناء التطوير.

الخلاصة التقنية
يُعد Storybook أداة لا غنى عنها في عالم تطوير واجهات المستخدم الحديث، خاصة عند العمل مع أطر عمل مثل React. إنه لا يقتصر على تسريع عملية بناء المكونات فحسب، بل يعزز أيضًا جودة التوثيق والتعاون بين فرق التصميم والتطوير. من خلال توفير بيئة معزولة ومركزة، يضمن Storybook أن كل مكون يعمل كما هو متوقع عبر حالات استخدام متعددة، مما يقلل من الأخطاء ويزيد من اتساق التصميم. ميزاته المتقدمة مثل Story Source وStoryshots تضيف طبقات قيمة من الشفافية والاختبار الآلي، مما يجعله حجر الزاوية في بناء أنظمة تصميم قوية ومستدامة.