Har du någon gång funderat på att samla alla dina UI-komponenter på ett och samma ställe i React?
Om du precis har börjat med React är det kanske inte det första du tänker på.
Vad innebär det egentligen?
Ta en titt på react-beautiful-dnd som exempel.
Det du ser i exemplen är så kallade ”stories” (berättelser). Verktyget som används för att skapa dessa berättelser heter Storybook.
Nu vet du vad den här artikeln ska handla om. Låt oss dyka in direkt!
Vad är Storybook?
Storybook är en isolerad utvecklingsmiljö för användargränssnitt, som fungerar som en lekplats för dina komponenter. Du kan utforska dina komponenter på olika sätt utan att behöva köra hela huvudapplikationen. Storybook körs i sin egen port efter installationen.
Det är inte begränsat till React. Du kan använda Storybook med de flesta frontend-ramverk som Vue, Angular, Mithril, Marko och Svelte.
Du kan läsa mer om Storybook här.
Vad är en ”story”?
En ”story” definierar hur en komponent ser ut i ett visst tillstånd. Om vi tar en vanlig komponent som exempel, kan vi använda den på många sätt genom att ändra dess props. För varje sådant tillstånd kan vi skapa en separat ”story”.
Låt oss säga att vi har en knappkomponent.
En knapp kan ha flera olika tillstånd som inaktiverad, laddar, primär, sekundär, liten, stor, medium, etc. Om vi ska lista alla tillstånd blir det svårt att hålla artikeln kort. Men jag tror du förstår poängen. Det kommer att klarna när du börjar använda Storybook.
Du kan se ”stories” för en knapp i olika storlekar (Stor, Medium, Liten).
Konfigurera Storybook i ett projekt
Nu ska vi konfigurera Storybook i ett React-projekt.
Här går vi:
- Skapa ett nytt React-projekt med följande kommando. Du kan namnge det som du vill.
npx create-react-app storybook-demo
- Installera Storybook med det här kommandot.
npx sb init
Installationen är nu klar.
Storybook använder en egen server.
Hur startar man den?
Storybook lägger automatiskt till ett kommando i din ”package.json”-fil. Du kan kontrollera det i ”scripts”-delen. Kör följande kommando för att starta Storybook-servern.
npm run storybook
Storybook startar en ny server på den port som definieras i ”package.json”. Det kommer automatiskt att öppna Storybook i din standardwebbläsare (samma som React-servern).
Du kommer att se några standardberättelser. Du kan ta bort dem eller behålla dem som referens. Som vi pratade om tidigare kan en knapp ha flera tillstånd, och du kan se några exempel på dem i Storybook (alla tillstånd är inte med). Vi kommer att skriva en hel del ”stories” för knappen i slutet av den här artikeln.
Utforska de olika delarna av Storybook för att bekanta dig med dem. Vi kommer att gå igenom några av dem.
Låt oss skriva vår första ”story”.
Testa Storybook
Vi har sett hur Storybook startas och några exempel i den.
- Skapa en ny mapp som heter ”Button” i ”src”-mappen.
- Skapa filerna ”Button.jsx”, ”Button.css” och ”constants.js” i den mappen.
- Kopiera koden från exemplen nedan till respektive fil.
Button.jsx
import React, { Component } from "react"; import PropTypes from "prop-types"; import "./Button.css"; import { buttonTypes, buttonVariants, buttonSizes } from "./constants"; class Button extends Component { static defaultProps = { isDisabled: false, type: "filled", variant: "oval", size: "medium", backgroundColor: "#1ea7fd", textColor: "#ffffff", }; static buttonTypes = buttonTypes; static buttonVariants = buttonVariants; static buttonSizes = buttonSizes; renderButton = () => { const { text, isDisabled, type, variant, size, backgroundColor, textColor, onClick, } = this.props; return ( <button onClick={onClick} className={`default ${variant} ${size} ${ isDisabled ? "disabled" : "" }`} style={ type === buttonTypes.outline ? { border: `1px solid ${backgroundColor}`, color: "#000000", backgroundColor: "transparent", } : { backgroundColor: `${backgroundColor}`, border: `1px solid ${backgroundColor}`, color: textColor, } } disabled={isDisabled} > {text} </button> ); }; render() { return this.renderButton(); } } Button.propTypes = { text: PropTypes.string, isDisabled: PropTypes.bool, type: PropTypes.oneOf([buttonTypes.outline, buttonTypes.filled]), variant: PropTypes.oneOf([buttonVariants.oval, buttonVariants.rectangular]), size: PropTypes.oneOf([ buttonSizes.small, buttonSizes.medium, buttonSizes.large, ]), backgroundColor: PropTypes.string, textColor: PropTypes.string, onClick: PropTypes.func, }; export { Button };
Button.css
.default { border: none; cursor: pointer; background-color: transparent; } .default:focus { outline: none; } .disabled { opacity: 0.75; cursor: not-allowed; } .small { font-size: 12px; padding: 4px 8px; } .medium { font-size: 14px; padding: 8px 12px; } .large { font-size: 16px; padding: 12px 16px; } .oval { border-radius: 4px; } .rectangular { border-radius: 0; }
constants.js
export const buttonTypes = { outline: "outline", filled: "filled", }; export const buttonVariants = { oval: "oval", rectangular: "rectangular", }; export const buttonSizes = { small: "small", medium: "medium", large: "large", };
Vad gör den här koden?
Vi har skapat en återanvändbar knappkomponent som kan anpassas på olika sätt. Vi har nu en komponent som kan ha flera olika tillstånd.
Låt oss nu skriva vår första ”story” genom att följa stegen nedan.
- Skapa en ny fil som heter ”Button.stories.jsx”
- Importera React och vår ”Button”-komponent i filen.
- Definiera en titel, eller en sökväg, för våra komponentberättelser. Vi gör det med hjälp av följande kod.
export default { title: 'common/Button', }
Ovanstående kod placerar alla ”stories” i den aktuella filen i mappen ”common/Button/”.
- Exportera en knapp med de nödvändiga props, enligt nedan.
export const defaultButton = () => ( <Button text="Default Button" onClick={() => {}} /> );
Vår första ”story” är klar. Kör Storybook med följande kommando för att se resultatet.
npm run storybook
Vi kommer att skriva fler ”stories”, oroa dig inte.
Varför är det användbart inom frontend-utveckling?
Vilken är den största fördelen med att använda Storybook?
Låt oss säga att du jobbar i ett team på 10 personer. Ni behöver kontrollera de gemensamma komponenterna som alla har skrivit för det aktuella projektet.
Hur gör ni det?
Ni måste gå igenom alla vanliga komponenter för att granska dem. Det tar mycket tid och är inte särskilt praktiskt. Här kommer Storybook in.
Hur kan den lösa problemet?
Vi kan skriva ”stories” för de gemensamma komponenterna (alla UI-komponenter) med hjälp av Storybook. När en teammedlem vill se de andras komponenter, startar de bara Storybook-servern och ser alla UI-komponenter där, precis som vi har sett tidigare.
Vi kan göra mycket mer med de renderade komponenterna i Storybook. Storybook har något som kallas ”Addons” som ger våra ”stories” extrafunktioner.
Låt oss säga att vi behöver kontrollera responsiviteten hos våra UI-komponenter i Storybook, vi kan använda ett tillägg som heter Viewport. Vi kommer att lära oss mer om tillägg i kommande avsnitt.
Arbeta med Storybook
I det här avsnittet ska vi skriva flera olika ”stories” som definierar olika tillstånd för vår knappkomponent.
Att skriva ”stories” är inte så svårt. En ”story” definierar ett tillstånd för en komponent. Om du tittar på en komponents props, kommer du enkelt förstå hur komponenten kan användas på olika sätt.
Låt oss skriva några ”stories” genom att ge valfria props.
export const largeButton = () => ( <Button text="Large Button" onClick={() => {}} size="large" /> ); export const outlineSmallButton = () => ( <Button text="Outline Small Button" onClick={() => {}} size="small" type="outline" /> ); export const rectangularLargeButton = () => ( <Button text="Rectangular Large Button" onClick={() => {}} size="large" variant="rectangular" /> ); export const disabledButton = () => ( <Button text="Disabled Button" onClick={() => {}} isDisabled={true} /> ); export const warningButton = () => ( <Button text="Warning Button" onClick={() => {}} backgroundColor="orange" /> );
De ovanstående ”stories” definierar olika användningsområden för vår knappkomponent. Nu är det din tur att lägga till några andra ”stories” för den. Försök att lägga till ”disabledSamllRectangularButton”, ”dangerButton”, ”successDisabledButton” osv.
Jag kommer inte att ge koden för de fallen. Du måste skriva dem själv för att förstå. Här kommer hela koden för de ”stories” vi har skrivit hittills.
import React from "react"; import { Button } from "./Button"; export default { title: "src/common/Button", }; export const defaultButton = () => ( <Button text="Default Button" onClick={() => {}} /> ); export const largeButton = () => ( <Button text="Large Button" onClick={() => {}} size="large" /> ); export const outlineSmallButton = () => ( <Button text="Outline Small Button" onClick={() => {}} size="small" type="outline" /> ); export const rectangularLargeButton = () => ( <Button text="Rectangular Large Button" onClick={() => {}} size="large" variant="rectangular" /> ); export const disabledButton = () => ( <Button text="Disabled Button" onClick={() => {}} isDisabled={true} /> ); export const warningButton = () => ( <Button text="Disabled Button" onClick={() => {}} backgroundColor="orange" /> );
Nu har du koll på hur man skriver ”stories” för en komponent.
Låt oss gå vidare till nästa avsnitt där vi ska lära oss om tillägg och hur de förbättrar våra ”stories”.
Storybook-tillägg
Det finns många tillägg som är tillgängliga som standard. I det här avsnittet kommer vi att utforska de mest användbara tilläggen för vår utveckling.
Låt oss förbättra våra knapp-”stories”.
Controls
”Controls” lägger till en funktion för att anpassa props till komponenten direkt i Storybook. För vår knappkomponent kan vi lägga till ”controls” för att ändra de olika props i Storybook.
Låt oss säga att vi vill hitta den bästa färgen för knappen. Det tar tid att testa alla bakgrundsfärger genom att ändra dem manuellt. Istället kan vi lägga till en ”control” som låter oss välja olika färger i Storybook. Vi kan testa bakgrundsfärgen direkt i Storybook.
Låt oss se hur vi lägger till ”controls” i våra knapp-”stories”.
Först måste vi definiera alla props under ”title”, enligt nedan.
export default { title: "src/common/Button", argTypes: { text: { control: "text" }, backgroundColor: { control: "color" }, isDisabled: { control: "boolean" }, size: { control: { type: "select", options: ["small", "medium", "large"] }, }, type: { control: { type: "select", options: ["filled", "outline"] }, }, variant: { control: { type: "select", options: ["oval", "rectangular"] }, }, }, };
Separera sedan props från komponenten och ge dem som argument enligt nedan.
export const outlineSmallButton = (args) => ( <Button {...args} onClick={() => {}} /> ); outlineSmallButton.args = { text: "Outline Small Button", size: "small", type: "outline", };
Du kan se ”controls” längst ned i komponentförhandsgranskningsfönstret.
Du hittar ”controls”-fliken längst ned i förhandsgranskningsfönstret. Prova den!
Uppdatera alla ”stories” enligt exemplet ovan. Det handlar mest om att lära sig syntaxen för Storybook-tillägg. I ”argTypes” har vi använt olika typer av ”controls”. Du kan hitta alla tillgängliga ”controls” i Storybook här.
De uppdaterade knapp-”stories” ser ut så här:
import React from "react"; import { Button } from "./Button"; export default { title: "src/common/Button", argTypes: { text: { control: "text" }, backgroundColor: { control: "color" }, isDisabled: { control: "boolean" }, size: { control: { type: "select", options: ["small", "medium", "large"] }, }, type: { control: { type: "select", options: ["filled", "outline"] }, }, variant: { control: { type: "select", options: ["oval", "rectangular"] }, }, }, }; export const defaultButton = (args) => <Button {...args} onClick={() => {}} />; defaultButton.args = { text: "Default Button", }; export const largeButton = (args) => ( <Button {...args} onClick={() => {}} size="large" /> ); largeButton.args = { text: "Large Button", }; export const outlineSmallButton = (args) => ( <Button {...args} onClick={() => {}} /> ); outlineSmallButton.args = { text: "Outline Small Button", size: "small", type: "outline", }; export const rectangularLargeButton = (args) => ( <Button {...args} onClick={() => {}} /> ); rectangularLargeButton.args = { text: "Rectangular Large Button", size: "large", variant: "rectangular", }; export const disabledButton = (args) => <Button {...args} onClick={() => {}} />; disabledButton.args = { text: "Disabled Button", isDisabled: true, }; export const warningButton = (args) => <Button {...args} onClick={() => {}} />; warningButton.args = { text: "Warning Button", backgroundColor: "orange", };
Actions
”Actions” representerar händelser i JavaScript. Ett knapptryck är en JavaScript-händelse. Med hjälp av ”actions”-tillägget kan vi utföra åtgärder när knappen klickas.
Med ”actions” kan vi testa om händelser fungerar som de ska. En inaktiverad knapp ska inte kunna klickas, och en aktiverad knapp ska kunna klickas. Vi kan kontrollera det med hjälp av ”actions”.
Låt oss se hur vi lägger till ”actions” för knapptryck.
Vi har använt en anonym funktion för ”onClick” prop tidigare. Nu måste vi uppdatera den.
- Importera ”action” från Storybook-tillägget med följande kod.
import { action } from "@storybook/addon-actions";
- Ersätt alla ”() => {}” med följande.
action("Button is clicked!")
Gå nu till Storybook och klicka på en knapp. Du kommer att se meddelandet visas under fliken ”actions”, bredvid fliken ”controls”. Meddelandet kommer inte att visas om du klickar på en inaktiverad knapp, eftersom den är just inaktiverad.
Vi kan använda ”action” för flera händelser som ”onChange”, ”onMouseOver”, ”onMouseOut” osv., för att se till att de fungerar korrekt. Prova att implementera samma sak för ”onChange” för ett inmatningsfält.
Se dokumentationen för ”actions” här.
Backgrounds
Med ”backgrounds”-tillägget kan vi ändra bakgrunden i förhandsgranskningsfönstret. Vi behöver inte skriva någon kod. Vi kan ändra det direkt i Storybook. Se gif:en nedan.
Viewport
Vi kan även testa responsiviteten hos våra komponenter i Storybook. Se gif-filen nedan för att lära dig mer om visningsportsalternativen.
Docs
Vi kan dokumentera våra komponenter i Storybook med hjälp av tillägget ”docs”. Det är särskilt användbart när man jobbar i team. Teammedlemmarna kan läsa dokumentationen för komponenten och snabbt förstå den. Det sparar utvecklarnas tid.
I Storybooks komponentförhandsgranskningsfönster, kan du se ”Docs”-fliken längst upp till höger, bredvid fliken ”Canvas”. Den innehåller dokumentationen för alla ”stories” för en viss komponent. Om vi vill dokumentera komponenten, och inkludera både Markdown och komponentrendering, behöver vi använda ”Button.stories.mdx”. Vi lägger till lite extra Markdown-kod tillsammans med våra komponent-”stories”.
Vi ska nu skriva en dokumentation för våra ”stories”. Koden innehåller både Markdown och komponentrendering. Du behöver bara lära dig syntaxen. Du kommer att förstå den snabbt.
Här kommer koden för ”Button.stories.mdx”:
<!--- Button.stories.mdx --> import { Meta, Story, Preview, ArgsTable } from '@storybook/addon-docs/blocks'; import { Button } from './Button'; <Meta title="MDX/Button" component={Button} /> # Button Documentation With `MDX` we can define a story for `Button` right in the middle of our Markdown documentation. <ArgsTable of={Button} /> export const Template = (args) => <Button {...args} /> ## Default Button We can write the documentation related to the Default Button <Preview> <Story name="Default Button" args={{ text: 'Default Button' }}> {Template.bind({})} </Story> </Preview> ## Large Button We are writing sample docs for two stories, you can write rest of them <Preview> <Story name="Large Button" args={{ text: "Large Button", }}> {Template.bind({})} </Story> </Preview>
Läs mer om dokumentationskomponenterna här.
Du kan läsa mer om tillägg här.
Slutsats
Jag hoppas att du gillade den här artikeln och lärde dig något om Storybook. Använd det i ditt team för att effektivisera ditt arbete.
Är du ny i React? Kolla in de här resurserna.
Lycka till med kodningen! 🙂