Mundarija (39)
- Bu darsda
- 1. Nega bu kerak?
- 2. Story nima: CSF3
- 2.1 Fayl tuzilmasi
- 2.2 Story — oddiy obyekt: o'zimiz chizib ko'ramiz
- 2.3 Args birlashishi qoidasi
- 3. O'rnatish: npm create storybook yoki qo'lda
- 3.1 Rasmiy buyruq
- 3.2 Qo'lda: ikki paket
- 3.3 .storybook/main.ts
- 3.4 Tuzoq: Storybook ilovaning plaginlarini ham oladi
- 3.5 Skriptlar va .gitignore
- 4. Dekoratorlar: komponentga kerakli muhit
- 4.1 Ikkinchi tuzoq: router yo'q
- 4.2 preview.tsx: hamma story uchun
- 4.3 Fayl darajasidagi dekorator
- 5. Controls va actions
- 5.1 Controls: props'ni panelda o'zgartirish
- 5.2 Actions: fn() — josus funksiya
- 6. Hujjat va soxta server: autodocs, MSW addon
- 6.1 Autodocs
- 6.2 MSW addon
- 7. TypeScript va npm tuzoqlari
- 7.1 Kutubxona turlari va TypeScript 7
- 7.2 npm 11 va allowScripts
- 8. Vazifalar qadami: Storybook 10 (holat 11)
- 8.1 O'lchab qaror
- 8.2 Fayllar
- 8.3 Dialog story'si
- 8.4 Natija va commit
- 9. Ko'p uchraydigan xatolar
- 10. Mashqlar
- 1-mashq (oson): bo'sh joylarni to'ldiring
- 2-mashq (o'rta): narx yorlig'i story'lari
- 3-mashq (qiyin): dekoratorlar zanjiri
- 4-mashq: Vazifalar qadami
- 11. Real ishda
- Xulosa
- Manbalar
Storybook I: komponentlarni izolyatsiyada ishlab chiqish — story, args, controls va dekoratorlar
Qisqacha: Storybook — komponentlar ustaxonasi: har komponentni ilovadan alohida, har bir holati bilan (oddiy, bo'sh, xato, uzun matn) ko'rsatadi. Holat — story:
Komponent.stories.tsxfaylidagi oddiy obyekt, uningargsi — komponentga beriladigan props. Default eksport (meta) komponentni va umumiy args'ni aytadi, har nomli eksport — bitta story. Komponentga router, tema yoki provider kerak bo'lsa —preview.tsxdagi dekorator uni o'rab beradi.vazifalarda Storybook 10.6.1: 4 fayl, 9 story,node_modulesga +53 MB, ilova bundle'i o'zgarmaydi.
Bu darsda
- Storybook qaysi muammoni hal qilishini va qachon kerak emasligini tushuntira olasiz.
- Storybook 10 ni React + Vite loyihasiga qo'lda o'rnatasiz:
.storybook/main.tsvapreview.tsx. - CSF3 formatida story yozasiz:
meta,args, story'ning o'zargsi,fn()bilan actions. - Controls (
argTypes), dekoratorlar, autodocs va MSW addon nima qilishini bilasiz. vazifalarga Storybook qo'shib, ikki haqiqiy tuzoqni (sw.jsva router) tuzatasiz.
Oldin bilishingiz kerak: RTL II: foydalanuvchi harakatlari, Dizayn tizimi: tokenlar va variantlar, Komponent API'sini loyihalash, Playwright II.
1. Nega bu kerak?
Sardor OchirishDialog ning matnini o'zgartirdi. Natijani ko'rish uchun u nima qiladi? Ilovani ochadi, kiradi, vazifa qo'shadi, qator menyusidan «O'chirish» ni bosadi. Besh qadam — bitta dialogni ko'rish uchun. Ro'yxatning bo'sh holatini ko'rish uchun esa hamma vazifani o'chirish kerak. Uzun nomli vazifa qanday chiqishini bilish uchun uzun nom yozish kerak. Har o'zgarishdan keyin yana boshidan.
Yana bir muammo — holatlarni hech kim eslab qolmaydi. Dizayner "xavfli tugma qanday ko'rinadi?" deb so'rasa, Sardor uni ilovaning qaysi burchagidan topishini bilmaydi.
Storybook — komponentlarni ilovadan alohida, izolyatsiyada (isolation) chizadigan ustaxona. Har komponentning har bir holati — alohida sahifa: chap tomonda ro'yxat, o'rtada komponent, pastda uning props'ini o'zgartirish paneli. Ilova kerak emas, server kerak emas, kirish kerak emas.
Hayotiy o'xshatish: oshxonada oshpaz yangi sousni zalga chiqarmasdan, alohida likopchada tatib ko'radi — sho'r, achchiq, quyuq variantlarini yonma-yon. Storybook — komponentlar uchun shunday tatib ko'rish stoli.
flowchart LR
subgraph I["Ilovada"]
A1["kirish"] --> A2["vazifa qo'shish"] --> A3["menyu"] --> A4["dialog"]
end
subgraph S["Storybook'da"]
B1["OchirishDialog / OchiqDialog"]
endRasmda: ilovada dialogga to'rt qadam bilan yetasiz, Storybook'da — ro'yxatdagi bitta bosish bilan.
Storybook bilan nima keladi:
| Imkoniyat | Nima beradi |
|---|---|
| Story ro'yxati | har komponentning hamma holati bir joyda |
| Controls | props'ni panelda o'zgartirib ko'rish |
| Actions | onClick kabi hodisalar chaqirilganini ko'rish |
| Docs | komponent hujjati (props jadvali, misollar) |
| Testlar | story'lar testga aylanadi (Storybook II) |
Tekshirib ko'ring: Sardorning jamoasida bitta sahifali kichik landing bor, unda 5 ta komponent va bitta dasturchi. Storybook kerakmi?
Javob
Ehtimol yo'q. Storybook ko'p komponentli, ko'p holatli loyihada o'zini oqlaydi: dizayn tizimi, bir nechta dasturchi, dizayner bilan ishlash. Narxi bor — bu darsda o'lchaymiz (+53 MB, alohida sozlash, alohida build). 5 ta komponent uchun RTL testlari (RTL I) yetarli.
2. Story nima: CSF3
2.1 Fayl tuzilmasi
Story'lar CSF (Component Story Format — komponent story formati) da yoziladi. CSF — oddiy ES modul: maxsus sintaksis yo'q. Joriy versiyasi — CSF3. vazifalar dagi Tugma story fayli to'liq:
import type { Meta, StoryObj } from "@storybook/react-vite";
import { fn } from "storybook/test";
import { Tugma } from "../src/ui/Tugma.tsx";
const meta = {
component: Tugma,
args: { children: "Saqlash", onClick: fn() },
} satisfies Meta<typeof Tugma>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Oddiy: Story = {};
export const Asosiy: Story = { args: { variant: "asosiy" } };
export const Xavfli: Story = {
args: { variant: "xavfli", children: "O'chirish" },
};Qatorma-qator:
| Qism | Ma'nosi |
|---|---|
export default meta |
fayl haqida: qaysi komponent, umumiy args |
component: Tugma |
shu komponent chiziladi |
args (meta'da) |
hamma story uchun umumiy props |
export const Oddiy |
bitta story; nomi ro'yxatda «Oddiy» bo'lib chiqadi |
args (story'da) |
faqat shu story uchun props — umumiysining ustiga |
satisfies Meta<…> |
TypeScript props nomlarini tekshiradi (satisfies) |
Args (argumentlar) — story komponentga beradigan props. Xavfli story'sida args birlashadi: children — story'dan («O'chirish»), onClick — meta'dan. Story'niki ustun.
title yozilmagan — Storybook nomni fayl yo'lidan oladi (stories/Tugma.stories.tsx → «Tugma»). Kerak bo'lsa title: "UI/Tugma" bilan guruhlash mumkin.
2.2 Story — oddiy obyekt: o'zimiz chizib ko'ramiz
Natija oynasida Storybook yo'q — u alohida dastur. Lekin CSF oddiy JavaScript bo'lgani uchun g'oyani RTL bilan sinab ko'rish mumkin: meta va story'larni olamiz, args'ni birlashtiramiz va har story'ni chizamiz. Bu — o'quv modeli; Storybook ichida ham mohiyatan shu ish bo'ladi.
import { render, screen } from "@testing-library/react";
import { expect, test, vi } from "vitest";
function DishCard({ name, price, soldOut = false, onAdd }) {
return (
<article aria-label={name}>
<h3>{name}</h3>
<p>{price} so'm</p>
<button disabled={soldOut} onClick={onAdd}>
{soldOut ? "Tugadi" : "Qo'shish"}
</button>
</article>
);
}
// DishCard.stories.jsx — xuddi shu shaklda yoziladi
const meta = {
component: DishCard,
args: { name: "Osh", price: "35 000", onAdd: vi.fn() },
};
const Default = {};
const SoldOut = { args: { name: "Manti", soldOut: true } };
const LongName = {
args: { name: "Qovurma lag'mon, katta porsiya" },
};
const stories = { Default, SoldOut, LongName };
for (const [title, story] of Object.entries(stories)) {
test(title, () => {
const args = { ...meta.args, ...story.args };
const Component = meta.component;
render(<Component {...args} />);
const card = screen.getByRole("article");
console.log(card.textContent);
expect(screen.getByRole("button")).toBeInTheDocument();
});
}Konsolda:
Osh35 000 so'mQo'shish
✓ Default
Manti35 000 so'mTugadi
✓ SoldOut
Qovurma lag'mon, katta porsiya35 000 so'mQo'shish
✓ LongName
Testlar: 3 o'tdi (jami 3)textContent — maqoladagi hamma matn yopishgan holda: nom (<h3>), narx (<p>), tugma. Qatorlarni solishtiring. SoldOut faqat ikki arg yozdi (name, soldOut), narx esa meta'dan keldi — «35 000 so'm» uchala story'da ham bor. Tugma matni soldOut ga qarab almashdi. Uchta story — uchta holat, va ularni yozish uchun komponentga bitta qator ham qo'shilmadi.
Storybook bundan ko'proq qiladi: har story'ni brauzerda alohida sahifa qilib ko'rsatadi, onAdd bosilganini panelda yozadi. Lekin asosi — shu ikki qator: { ...meta.args, ...story.args } va <Component {...args} />.
2.3 Args birlashishi qoidasi
Storybook args'ni uch qavatdan yig'adi, keyingisi oldingisining ustiga yoziladi:
flowchart LR
P["preview.tsx: args"] --> M["meta.args (fayl)"] --> S["story.args"] --> C["komponent props"]Rasmda: eng umumiysi — butun loyiha (preview.tsx), keyin fayl, keyin story. Shuning uchun umumiy narsani (onClick: fn(), standart matn) meta'ga qo'ying, story'ga faqat farqni yozing. Story fayli shunda "holatlar ro'yxati" bo'lib o'qiladi: Xavfli — "oddiyning o'zi, faqat variant boshqa".
Story'ning args dan boshqa maydonlari ham bor:
| Maydon | Nima uchun |
|---|---|
render: (args) => <…/> |
komponentni o'zingiz chizish (o'ram, bir nechta nusxa) |
decorators |
story atrofiga o'ram (keyingi bo'limlar) |
parameters |
addon'lar sozlamasi (a11y, layout, msw) |
tags |
belgilar: autodocs, !test |
play |
story chizilgach harakat va tekshiruv (Storybook II) |
Storybook 10 da yangi yozuv ham bor — CSF Factories (preview.meta(…), meta.story(…)): turlar avtomatik chiqadi. vazifalar CSF3 bilan qoldi — hujjatdagi asosiy misollar hali CSF3 da.
Tekshirib ko'ring:
meta.argsdavariant: "asosiy", story'daargs: { variant: undefined }. Komponentga qaysi qiymat boradi?
Javob
undefined — story'niki ustun, undefined ham qiymat ({ ...a, ...b } dagi kabi: kalit bor bo'lsa, eskisini bosadi). Komponent o'z standartini oladi (Tugma da "oddiy"). Umumiy args'ni "o'chirish" uchun shu yo'l ishlaydi.
3. O'rnatish: npm create storybook yoki qo'lda
3.1 Rasmiy buyruq
Hujjatdagi yo'l — bitta buyruq:
npm create storybook@latestU loyihani tanib (React + Vite), paketlarni o'rnatadi, .storybook/ papkasini, namuna story'larni va skriptlarni yozadi. Qulay — lekin savollar beradi (interaktiv), qo'shimcha addon'lar va namuna fayllar qo'shadi. vazifalar da qo'lda o'rnatdik: har fayl nima uchun borligini bilish uchun.
Storybook 10 talablari: faqat ESM (main.ts ham ES modul), Node 20.19+ yoki 22.12+. Bizda Node 24.21.0.
3.2 Qo'lda: ikki paket
git switch -c chore/storybook
npm i -D -E storybook@10.6.1 @storybook/react-vite@10.6.1storybook— yadro: CLI (storybook dev,storybook build), interfeys, controls, actions, viewport.@storybook/react-vite— framework paketi: React'ni Vite bilan chizadi va loyihaningvite.config.tsini oladi.
10.6.1 ni oldik — 10.6.2 o'rnatish kunida 7 kundan yangi edi (zanjir hujumi xavfi uchun yangi relizni bir hafta kutish qoidasi, paket xavfsizligi). Storybook 9 dan beri controls, actions va viewport yadroning ichida — alohida "essentials" paketi yo'q.
3.3 .storybook/main.ts
import type { StorybookConfig } from "@storybook/react-vite";
const ILOVA_PLAGINLARI = /^(vazifalar-|spa-zaxira$|blocking-render$)/;
function ilovaPlaginimi(plagin: unknown): boolean {
return (
typeof plagin === "object" &&
plagin !== null &&
"name" in plagin &&
typeof plagin.name === "string" &&
ILOVA_PLAGINLARI.test(plagin.name)
);
}
const config: StorybookConfig = {
stories: ["../stories/**/*.stories.tsx"],
addons: [],
framework: "@storybook/react-vite",
core: { disableTelemetry: true },
viteFinal: (sozlama) => ({
...sozlama,
plugins: (sozlama.plugins ?? [])
.flat()
.filter((p) => !ilovaPlaginimi(p)),
}),
};
export default config;| Kalit | Nima qiladi |
|---|---|
stories |
story fayllari qayerda (glob) |
addons |
qo'shimcha imkoniyatlar — hozircha bo'sh, Storybook II da ikkita |
framework |
React + Vite |
core.disableTelemetry |
Storybook'ga foydalanish statistikasini yubormaslik |
viteFinal |
Vite sozlamasini Storybook'ga berishdan oldin o'zgartirish |
viteFinal nega kerak — bu birinchi haqiqiy tuzoq.
3.4 Tuzoq: Storybook ilovaning plaginlarini ham oladi
Birinchi npm run build-storybook (viteFinal'siz) muvaffaqiyatli tugadi, lekin storybook-static/ ichida ikki g'alati fayl bor edi: sw.js va 404.html. Build logida: sw.js: QOBIQ — 52 ta. Bu — ilovamizning Service Worker plagini (21-qism): u Storybook build'ini ham "ilova" deb, uning 52 ta faylini keshga yozadigan sw.js yaratdi. Storybook'ni bir marta ochgan dasturchi brauzerida keyin ilova emas, eski Storybook keshi ochilishi mumkin edi.
Sabab: framework loyihaning vite.config.ts ini to'liq oladi — React plagini va Tailwind kerak, lekin SW, 404 nusxasi, dev serverlari kerak emas. viteFinal ularni nomi bo'yicha olib tashlaydi. Natija — o'lchab ko'rdik:
- ilova plaginlari bilan7.8 MB, ichida sw.js22,8 s
- viteFinal bilan7.0 MB10 s
Manba: O'lchandi: vazifalar kanoni (holat 11), i5-12500H, Windows 11, Node 24.21.0, Storybook 10.6.1, 2026-10-20
Ikki baravar tezroq va keraksiz sw.js yo'q. public/sw.js shabloni baribir nusxalanadi (Vite public/ papkasini o'zi ko'chiradi), lekin hech kim uni ro'yxatdan o'tkazmaydi — xavfsiz.
3.5 Skriptlar va .gitignore
{
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
}npm run storybook — dev server (http://localhost:6006), fayl o'zgarsa sahifa o'zi yangilanadi. npm run build-storybook — statik sayt storybook-static/ ga: uni istalgan hostingga qo'yib, jamoaga havola berish mumkin. Bu papka .gitignore, ESLint ignores va .prettierignore ga qo'shildi — u build natijasi, manba emas.
O'lchov: storybook dev tayyor bo'lishi 9.5 s (sovuq) va 4.9 s (issiq).
4. Dekoratorlar: komponentga kerakli muhit
4.1 Ikkinchi tuzoq: router yo'q
OddiyQator story'si ochilganda Storybook qizil quti ko'rsatdi:
TypeError: Cannot destructure property 'basename' of 'React.useContext(...)' as it is null.Tarjima: "useContext(...) natijasidan basename xususiyatini ajratib bo'lmaydi — u null". Qatorda Link bor (React Router), Link esa router kontekstini qidiradi. Ilovada router App atrofida turadi. Story esa komponentni yolg'iz chizadi — atrofda hech narsa yo'q. Natija oynasida ham xuddi shunday:
import { render, screen } from "@testing-library/react";
import type { ComponentType, ReactElement } from "react";
import { Link, MemoryRouter } from "react-router";
import { expect, test } from "vitest";
function TaskRow({ text }: { text: string }) {
return (
<li>
<Link to="/vazifa/1">{text}</Link>
</li>
);
}
// Dekorator: story'ni oladi, o'ralgan holda qaytaradi
type Decorator = (Story: ComponentType) => ReactElement;
const withRouter: Decorator = (Story) => (
<MemoryRouter>
<Story />
</MemoryRouter>
);
test("router'siz — xato", () => {
try {
render(<TaskRow text="Non olish" />);
} catch (e) {
console.log(String(e));
}
});
test("dekorator bilan", () => {
const Story = () => <TaskRow text="Non olish" />;
render(withRouter(Story));
const link = screen.getByRole("link", { name: "Non olish" });
expect(link).toHaveAttribute("href", "/vazifa/1");
});Konsolda:
TypeError: Cannot destructure property 'basename' of 'React$111.useContext(...)' as it is null.
✓ router'siz — xato
✓ dekorator bilan
Testlar: 2 o'tdi (jami 2)React$111 — natija oynasidagi to'plam ichidagi nom; Storybook'da va loyihangizda boshqacha chiqadi, ma'nosi bir xil.
Dekorator (decorator) — story'ni qabul qilib, uni biror narsaga o'rab qaytaradigan funksiya. 15-qismdagi dekoratorlar bilan nomi bir, g'oyasi o'xshash: asl narsani o'zgartirmay, atrofiga qo'shimcha qo'shadi. O'xshatish — sovg'a qog'ozi: ichidagi narsa o'sha, faqat endi u yetkazib berishga tayyor.
4.2 preview.tsx: hamma story uchun
import type { Preview } from "@storybook/react-vite";
import "../src/vazifalar.css";
import "../src/i18n/index.ts";
import { MemoryRouter } from "react-router";
import { TemaProvider } from "../src/kontekst/TemaKonteksti.tsx";
const preview: Preview = {
decorators: [
(Story) => (
<MemoryRouter>
<TemaProvider>
<Story />
</TemaProvider>
</MemoryRouter>
),
],
};
export default preview;preview.tsx — har story'dan oldin ishlaydigan fayl. Unda uch narsa bor:
import "../src/vazifalar.css"— ilovaning CSS'i (Tailwind ham). Busiz komponentlar "yalang'och" chiqadi.import "../src/i18n/index.ts"— tarjimalar (i18n): komponentlart("…")bilan matn oladi.- dekorator —
MemoryRouter(xotiradagi router, manzil satri kerak emas) vaTemaProvider.
Dekorator uch darajada yoziladi — args kabi: preview.tsx (hamma story), meta.decorators (fayl), story.decorators (bitta story).
4.3 Fayl darajasidagi dekorator
OddiyQator ga router'dan tashqari amallar konteksti ham kerak (QatorAmallariKonteksti — qator tugmalari shu orqali onOchir, onTahrirla ni oladi) va <ul>:
const amallar = {
onAlmashtir: fn(),
onOchir: fn(),
onTahrirla: fn(),
onSaqla: fn(() => null),
onBekor: fn(),
tugmaRef: () => () => undefined,
};
const meta = {
component: OddiyQator,
args: {
vazifa: { id: 1, matn: "Non olish #bozor", bajarildi: false },
sorov: "",
},
decorators: [
(Story) => (
<QatorAmallariKonteksti value={amallar}>
<ul>
<Story />
</ul>
</QatorAmallariKonteksti>
),
],
} satisfies Meta<typeof OddiyQator>;Story'lar: Faol, Bajarilgan (bajarildi: true), QidiruvBilan (sorov: "non" — qidiruv so'zi ajratib ko'rsatiladi). <ul> nega kerakligini Storybook II da a11y tekshiruvi aytadi — u yerda ishlatilmagan holatda bu xato edi.
Tekshirib ko'ring: Hamma story'ga qorong'i tema kerak emas — faqat bittasiga. Dekoratorni qayerga yozasiz?
Javob
O'sha story'ning o'ziga: export const Qorongi: Story = { decorators: [(Story) => <div data-tema="qorongi"><Story /></div>] }. preview.tsx dagisi hammaga, meta dagisi butun faylga ta'sir qiladi.
5. Controls va actions
5.1 Controls: props'ni panelda o'zgartirish
Storybook'da story ostida Controls paneli bor: har arg uchun maydon. children — matn maydoni, disabled — katakcha. Turini Storybook TypeScript turlaridan o'zi chiqaradi. variant kabi cheklangan qiymatlar uchun ro'yxatni aniq berish mumkin — argTypes:
const meta = {
component: Tugma,
args: { children: "Saqlash", onClick: fn() },
argTypes: {
variant: {
control: "radio",
options: ["oddiy", "asosiy", "xavfli"],
},
onClick: { table: { disable: true } },
},
} satisfies Meta<typeof Tugma>;Bu qism vazifalar da yo'q — Storybook turlardan o'zi to'g'ri tanladi. Boshqaruv turlari: text, boolean, number, range, color, date, select, radio, check, object, file.
Controls'da o'zgartirish faqat sizning ekraningizda — fayl o'zgarmaydi. Qiziq holat topsangiz (masalan, juda uzun matn tugmani buzdi), uni yangi story qilib yozing: shunda u hamma uchun va testlar uchun ham saqlanadi.
5.2 Actions: fn() — josus funksiya
onClick: fn() — storybook/test dan. Tugma bosilganda Actions panelida yozuv chiqadi: onClick va uning argumentlari. fn() — aslida Vitest'ning vi.fn() i (Vitest II), Storybook uni o'z paneliga ulaydi. Shuning uchun keyingi darsda expect(args.onClick).toHaveBeenCalled() deb tekshira olamiz.
Natija oynasida o'sha g'oya vi.fn() bilan:
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { expect, test, vi } from "vitest";
function DeleteButton({ onDelete }) {
return (
<button onClick={() => onDelete({ id: 7 })}>O'chirish</button>
);
}
test("action — kim, nima bilan chaqirildi", async () => {
const args = { onDelete: vi.fn() };
const user = userEvent.setup();
render(<DeleteButton {...args} />);
await user.click(screen.getByRole("button", { name: "O'chirish" }));
console.log(JSON.stringify(args.onDelete.mock.calls));
expect(args.onDelete).toHaveBeenCalledWith({ id: 7 });
});Konsolda:
[[{"id":7}]]
✓ action — kim, nima bilan chaqirildi
Testlar: 1 o'tdi (jami 1)mock.calls — har chaqiruv argumentlari massivi: bitta chaqiruv, unda bitta argument ({ id: 7 }). Storybook'ning Actions paneli shu ro'yxatni chiroyli ko'rsatadi.
6. Hujjat va soxta server: autodocs, MSW addon
6.1 Autodocs
Meta'ga tags: ["autodocs"] qo'shsangiz, Storybook komponent uchun hujjat sahifasi yasaydi: tavsif (JSDoc izohidan), props jadvali (turlardan), hamma story'lar bitta sahifada. Dizayn tizimi uchun bu "jonli hujjat": kod o'zgarsa, hujjat ham o'zgaradi. Storybook 10 da buning uchun alohida paket kerak — @storybook/addon-docs (npx storybook add @storybook/addon-docs). vazifalar da u o'rnatilmagan: bizda dizayn tizimi kutubxonasi emas, bitta ilova — story ro'yxati yetarli.
6.2 MSW addon
Ba'zi komponent ma'lumotni o'zi oladi (fetch, TanStack Query). Story'da server yo'q — nima qilish kerak? MSW darsidagi handlerlarni story'ga ulash mumkin: msw-storybook-addon (3.0.3, 2026-10 holatiga; MSW 2+ va Storybook 10 bilan mos deb e'lon qilingan). Hujjat bo'yicha: preview.tsx da initialize() va loaders: [mswLoader], story'da parameters: { msw: { handlers: [...] } }.
vazifalar da bu addon yo'q va sinalmagan. Sabab — tuzilma: story'li komponentlar (Tugma, OddiyQator, OchirishDialog, VazifaRoyxati) ma'lumotni props orqali oladi, fetch qilmaydi. Bu ataylab: "ko'rinish" komponentlari ma'lumot qayerdan kelishini bilmaydi (komponent API). Butun sahifani (ma'lumot bilan) ko'rish — e2e va visual testlar ishi (Visual regression).
7. TypeScript va npm tuzoqlari
7.1 Kutubxona turlari va TypeScript 7
npm run tip (tsc -b) story fayllarini tekshirganda node_modules ichida 4 ta xato chiqdi:
TS2578: Unused '@ts-expect-error' directive.
TS2344: Type '…' does not satisfy the constraint '…'.Tarjima: "ishlatilmagan @ts-expect-error ko'rsatmasi" va "tur cheklovni qanoatlantirmaydi". Sabab: @storybook/react 10.6.1 ning tur fayli bir joyda "bu yerda xato bo'ladi, e'tibor berma" (@ts-expect-error) deb yozilgan. TypeScript 6 u yerda xato beradi, TypeScript 7 (tsgo) esa bermaydi — "kutilgan xato yo'q" degan yangi xato chiqadi. Bizning kodda xato yo'q, kutubxona turlari TS 7 ga hali moslashmagan.
Yechim — alohida tsconfig.storybook.json: ilova sozlamasidan meros, faqat skipLibCheck: true (kutubxona .d.ts lari tekshirilmaydi). Ilova va testlar sozlamalarida skipLibCheck yo'q — o'z kodimiz to'liq strict. Bu jest-dom turlari dagidan farqli qaror: u yerda xatoni o'zimiz tuzatdik, bu yerda tuzatadigan narsa kutubxona ichida.
7.2 npm 11 va allowScripts
O'rnatishda npm ogohlantirdi:
install-scripts ... esbuild@0.28.2 (postinstall) not yet covered by allowScriptsnpm 11 o'rnatish skriptlarini sukut bo'yicha ishga tushirmaydi (paket xavfsizligi). esbuild'ning postinstall i o'tkazib yuborildi — Storybook baribir ishladi: esbuild binary'si @esbuild/win32-x64 paketi orqali keldi. Ruxsat berish shart bo'lmadi.
8. Vazifalar qadami: Storybook 10 (holat 11)
8.1 O'lchab qaror
Storybook kanonga kiradimi — avval narxini o'lchadik:
| O'lchov | Qiymat |
|---|---|
| O'rnatish | 12 s |
node_modules |
+53.3 MB (keyingi dars addon'lari bilan jami +58 MB: 375 → 433 MB) |
storybook dev |
9.5 s sovuq / 4.9 s issiq |
storybook build |
10.0 s, 7.0 MB |
| Ilova JS (gzip) | 206.7 KB — o'zgarmadi |
Narx sezilarli. Kirdi, chunki keyingi darsda story'lar Vitest testiga aylanadi (alohida test vositasi kerak emas) va a11y tekshiruvi o'sha yerda ishlaydi. Ilova foydalanuvchisi hech narsa to'lamaydi: Storybook — faqat devDependencies.
Paket o'rnatilgach birinchi npm test 70.8 s oldi (odatda 9–13 s): Vite keshi qaytadan quriladi, vaqtning 67 % i muhitni tayyorlashga ketdi. Bu bir martalik sovuq start — keyingi ishga tushirish odatdagidek.
8.2 Fayllar
.storybook/
main.ts stories, framework, viteFinal (ilova plaginlarisiz)
preview.tsx CSS, i18n, MemoryRouter + TemaProvider
stories/
Tugma.stories.tsx Oddiy, Asosiy, Xavfli
OddiyQator.stories.tsx Faol, Bajarilgan, QidiruvBilan
OchirishDialog.stories.tsx OchiqDialog
VazifaRoyxati.stories.tsx BoshHolat, UchVazifa
tsconfig.storybook.json skipLibCheck faqat shu yerda4 fayl, 9 story. Story'lar src/ da emas, alohida stories/ da — ilova kodi va ustaxona aralashmasin. Ko'p loyihalarda story komponent yonida turadi (Tugma.tsx + Tugma.stories.tsx) — ikkalasi ham to'g'ri, jamoada bitta qoida bo'lsin.
8.3 Dialog story'si
OchirishDialog ref orqali showModal() bilan ochiladi — o'zi ochiq turmaydi. Story uni ochib qo'yadigan kichik o'ram komponent yozadi:
function Ochiq(props: { matn: string; onJavob: (t: boolean) => void }) {
const ref = useRef<HTMLDialogElement>(null);
useEffect(() => ref.current?.showModal(), []);
return <OchirishDialog ref={ref} {...props} />;
}
const meta = {
component: Ochiq,
args: { matn: "Non olish #bozor", onJavob: fn() },
} satisfies Meta<typeof Ochiq>;Storybook haqiqiy brauzerda chizadi — showModal ishlaydi (jsdom'da yo'q, RTL I).
8.4 Natija va commit
Testlar o'zgarmadi: Vitest 348, brauzer 6, e2e 10. Lint, format:check, tip, knip — toza. JS 206.7 KB.
git add -A
git commitCommit (kanon): chore: Storybook 10 (react-vite) — Tugma, vazifa qatori, dialog, ro'yxat story'lari.
9. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
Cannot destructure property 'basename'… |
Link router'siz |
preview.tsx dekoratori: MemoryRouter |
| komponent uslubsiz | CSS import qilinmagan | preview.tsx da ilova CSS'i |
storybook-static da sw.js |
ilova Vite plaginlari | viteFinal da olib tashlash |
useX must be used within Provider |
kontekst yo'q | fayl yoki loyiha dekoratori |
TS2578 Unused '@ts-expect-error' |
kutubxona turlari, TS 7 | skipLibCheck faqat story tsconfig'da |
| story ro'yxatda yo'q | stories glob mos emas |
main.ts dagi yo'l, fayl nomi *.stories.tsx |
| har story'da bir xil props | hammasi story'ga yozilgan | umumiysini meta.args ga |
10. Mashqlar
Mashqlar kurs/mashqlar/22/11-storybook-story/ da. Jonli yechimlarni «Ishga tushir» bilan sinang.
1-mashq (oson): bo'sh joylarni to'ldiring
- Fayl haqidagi obyekt —
export meta - Story komponentga beradigan props —
- Hamma story'ni o'raydigan funksiyalar turadigan fayl —
.storybook/.tsx - Panelda cheklangan qiymatlar ro'yxati —
- Actions panelida ko'rinadigan josus —
()
Yechim
- default —
export default meta: komponent va umumiy args. - args — story'niki meta'nikidan ustun.
- preview —
preview.tsxdagidecoratorshamma story'ga ta'sir qiladi. - argTypes —
control: "radio",options: [...]. - fn —
storybook/testdan; aslidavi.fn().
2-mashq (o'rta): narx yorlig'i story'lari
PriceTag komponenti: price (son), discount (foiz, ixtiyoriy). Chegirma bo'lsa — eski narx <s> ichida va yangisi <strong> da; bo'lmasa — faqat <strong>. Meta (price: 35000) va uch story yozing: Default, WithDiscount (discount: 10), Free (price: 0 — matn «Bepul»). Keyin «Story — oddiy obyekt» bo'limidagidek har story'ni chizib, <strong> matnini konsolga chiqaring. Narxni String(n) bilan chiqaring (toLocaleString Chrome va Node'da farq qiladi).
Ishora: args birlashishi — { ...meta.args, ...story.args }; <strong> ni container.querySelector("strong") bilan emas, matn bilan tekshiring.
Yechim
import { render, screen } from "@testing-library/react";
import { expect, test } from "vitest";
function PriceTag({ price, discount }) {
if (price === 0) return <strong>Bepul</strong>;
if (!discount) return <strong>{String(price)} so'm</strong>;
const final = price - (price * discount) / 100;
return (
<p>
<s>{String(price)} so'm</s>{" "}
<strong>{String(final)} so'm</strong>
</p>
);
}
const meta = { component: PriceTag, args: { price: 35000 } };
const stories = {
Default: {},
WithDiscount: { args: { discount: 10 } },
Free: { args: { price: 0 } },
};
for (const [title, story] of Object.entries(stories)) {
test(title, () => {
const Component = meta.component;
render(<Component {...meta.args} {...story.args} />);
const strong = screen.getByText(/so'm|Bepul/, {
selector: "strong",
});
console.log(strong.textContent);
expect(strong).toBeVisible();
});
}Konsolda:
35000 so'm
✓ Default
31500 so'm
✓ WithDiscount
Bepul
✓ Free
Testlar: 3 o'tdi (jami 3){ ...meta.args, ...story.args } o'rniga ikki marta yoyish ({...meta.args} {...story.args}) ham bir xil: keyingisi ustun. selector: "strong" — getByText ni bitta tegga toraytiradi: <s> dagi eski narx ham «so'm» bilan tugaydi.
3-mashq (qiyin): dekoratorlar zanjiri
Storybook bir nechta dekoratorni zanjir qilib qo'llaydi: decorators: [a, b] — b tashqarida, a ichkarida (ro'yxatdagi birinchisi story'ga eng yaqin). applyDecorators(Story, decorators) funksiyasini yozing: u har dekoratorni navbat bilan qo'llab, yakuniy komponent qaytarsin. withTheme (<div data-theme="dark">) va withFrame (<section aria-label="Ramka">) bilan sinang: tema section ichida bo'lishi kerak. Greeting komponenti useContext orqali ismni oladi — uni withUser dekoratori UserContext.Provider bilan bersin.
Ishora: reduce (massiv metodlari); har qadamda const Inner = acc; return () => decorator(Inner);.
Yechim
import { render, screen, within } from "@testing-library/react";
import { createContext, useContext } from "react";
import { expect, test } from "vitest";
const UserContext = createContext(null);
function Greeting() {
const user = useContext(UserContext);
if (!user) throw new Error("UserContext topilmadi");
return <p>Salom, {user}!</p>;
}
function applyDecorators(Story, decorators) {
return decorators.reduce((Acc, decorator) => {
const Inner = Acc;
return () => decorator(Inner);
}, Story);
}
const withUser = (Story) => (
<UserContext.Provider value="Malika">
<Story />
</UserContext.Provider>
);
const withTheme = (Story) => (
<div data-theme="dark">
<Story />
</div>
);
const withFrame = (Story) => (
<section aria-label="Ramka">
<Story />
</section>
);
test("zanjir: birinchi dekorator — eng ichkarida", () => {
const Final = applyDecorators(Greeting, [
withUser,
withTheme,
withFrame,
]);
render(<Final />);
const frame = screen.getByRole("region", { name: "Ramka" });
const theme = frame.firstElementChild;
console.log(theme.getAttribute("data-theme"));
expect(within(frame).getByText("Salom, Malika!")).toBeVisible();
});Konsolda:
dark
✓ zanjir: birinchi dekorator — eng ichkarida
Testlar: 1 o'tdi (jami 1)reduce birinchi qadamda withUser(Greeting) ni, keyin uning ustiga withTheme, oxirida withFrame ni o'raydi — shuning uchun section eng tashqarida. withUser ni ro'yxatdan olib tashlasangiz, Greeting "UserContext topilmadi" xatosini tashlaydi — router misolidagi kabi.
4-mashq: Vazifalar qadami
chore/storybook branch'ida «Vazifalar qadami» dagi o'zgarishlarni qiling: ikki paket, main.ts, preview.tsx, 4 story fayli, skriptlar, tsconfig.storybook.json.
Yechim
Tekshiruv ro'yxati:
- Avval
viteFinalsiznpm run build-storybook—storybook-static/sw.jsbormi? KeyinviteFinalbilan — yo'qligiga ishonch hosil qiling. preview.tsxdagiMemoryRouterni vaqtincha olib tashlang —OddiyQatorstory'sibasenamexatosini beradi. Qaytaring.npm run storybook— chap ro'yxatda 4 komponent, 9 story;Tugma/Xavflida Controls'danchildrenni o'zgartirib ko'ring.npm run tip— toza (skipLibCheckfaqattsconfig.storybook.jsonda).npm test— 348 test, ilovanpm run build— JS 206.7 KB.
11. Real ishda
- Dizayn tizimlari va UI kutubxonalari Storybook'ni hujjat sifatida chiqaradi — komponent "do'koni": dizayner, menejer va dasturchi bir joyga qaraydi.
- Kod ko'rib chiqishda (pull request) Storybook build'i havola bilan qo'shiladi: ko'rib chiquvchi o'zgargan komponentni ilovani ishga tushirmasdan ko'radi.
- Yangi komponent ko'pincha avval story'da quriladi (ilovaga ulanmasdan) — "komponentdan boshlab ishlab chiqish" (component-driven development).
- Muqobillar: Ladle (Vite'ga asoslangan, yengil, CSF bilan mos), Histoire (Vue/Svelte). React ekotizimida eng keng tarqalgani — Storybook.
- Intervyu savollari: "story nima?", "args va argTypes farqi?", "dekorator qachon kerak?", "Storybook'ning narxi nima?".
Xulosa
- Storybook — komponentlarni ilovasiz, har holati bilan ko'rsatadigan ustaxona; holat — story.
- CSF3:
export default meta(komponent, umumiy args) + nomli eksportlar (story'lar); args birlashadi — story'niki ustun. - Dekorator story'ni kerakli muhitga o'raydi: router, tema, provider;
preview.tsx— hamma story uchun. - Controls (
argTypes) — panelda props, actions —fn()(Vitest josusi); autodocs —@storybook/addon-docs, MSW addon — ma'lumot oladigan komponentlar uchun. vazifalar: 4 fayl, 9 story, +53 MB;viteFinalilova plaginlarini olib tashlaydi, bundle o'zgarmaydi.
Keyingi dars: Storybook II: interaction, a11y va Vitest integratsiyasi — story'larga play funksiyasi, axe bilan a11y tekshiruvi va har story'ning Vitest testiga aylanishi.
Manbalar
- Storybook: Install, Writing stories (CSF3), Args, ArgTypes, Decorators, Controls, Actions (storybook.js.org/docs)
- Storybook 10 migration guide — ESM, Node 20.19+ / 22.12+; MIGRATION.md — essentials yadroga ko'chgani, addon-docs alohida
- msw-storybook-addon — npm (3.0.3, peer: msw ≥ 2, storybook ^10)
- Haqiqiy o'lchovlar: Storybook 10.6.1, Node 24.21.0, npm 11.19.0, Windows 11, i5-12500H (2026-10-20); natija oynasi — Chrome 155
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!