Začínáme s Storybook v Reactu

Photo of author

By etechblogcz

Uvažovali jste někdy o možnosti umístit všechny prvky uživatelského rozhraní na jedno místo v rámci Reactu?

Pokud s Reactem teprve začínáte, možná o tom zatím nevíte.

O co se jedná?

Podívejte se na příklady v react-beautiful-dnd.

To, co jste viděli v ukázkách, se nazývá příběhy. A nástroj, který se používá k jejich vytváření, se nazývá Storybook.

Nyní už máte představu, o čem budeme v tomto článku hovořit. Bez zbytečných řečí, pojďme se na to podívat.

Co je to Storybook?

Storybook představuje izolované vývojové prostředí pro uživatelské rozhraní, které poskytuje prostor pro testování vašich komponent. Můžeme si s našimi komponentami hrát různými způsoby, aniž bychom museli spouštět celou aplikaci. Storybook můžeme spustit na samostatném portu.

Není to ale pouze pro React. Storybook lze použít s většinou frontendových frameworků, jako jsou Vue, Angular, Mithril, Marko, Svelte a další.

Více informací o Storybooku najdete zde.

Co je to příběh?

Příběh definuje, jak se daná komponenta vykreslí v určitém stavu. Vezmeme-li běžnou komponentu, můžeme ji s různými rekvizitami využívat různými způsoby. Pro každý z těchto stavů můžeme napsat samostatný příběh.

Řekněme, že máme komponentu Tlačítko.

Tlačítko může mít různé stavy, například zakázané, načítání, primární, sekundární, malé, velké, střední atd. Pokud bychom chtěli vypsat všechny stavy, bylo by velmi obtížné v tutoriálu pokročit. Myslím, že to chápete. Více vám to dá, když začnete pracovat se Storybookem.

Příběhy tlačítka můžete vidět v různých variantách (Velký, Střední, Malý).

Nastavení Storybooku v projektu

Vytvoříme Storybook v React projektu.

Pojďme na to.

  • Vytvořte React projekt pomocí tohoto příkazu. Název si můžete zvolit dle libosti.
npx create-react-app storybook-demo
  • Nyní nainstalujte Storybook do svého projektu pomocí následujícího příkazu.
npx sb init

Nastavení Storybooku je tímto dokončeno.

Storybook nám poskytuje vlastní server.

Jak ho spustit?

Storybook automaticky přidá příkaz do našeho souboru skriptů. Můžete to ověřit v souboru package.json v sekci skripty. Prozatím použijte následující příkaz pro spuštění serveru Storybook.

npm run storybook

Storybook spustí nový server na portu definovaném v sekci skriptů souboru package.json. Automaticky se otevře Storybook ve výchozím prohlížeči (stejně jako React server).

Ve výchozím nastavení v něm uvidíte různé příběhy. Pokud je nechcete, můžete je odstranit nebo si je ponechat pro referenci. Jak jsme se zmiňovali dříve, tlačítko může mít více stavů. V Storybooku je můžete vidět (ne všechny zmíněné stavy). V závěrečné části tutoriálu vytvoříme sadu příběhů pro tlačítko.

Prozkoumejte si různé části Storybooku a seznamte se s jeho funkcemi. V tomto tutoriálu se budeme zabývat některými z nich.

Pojďme napsat náš první příběh.

Testování ve Storybooku

Viděli jsme běžící Storybook a několik ukázek v něm.

  • Ve složce src vytvořte složku s názvem Button.
  • Vytvořte soubory Button.jsx, Button.css a constants.js.
  • Vložte příslušný kód z uvedených úryvků do těchto souborů.

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",
};

Co je to za kód?

Napsali jsme univerzální komponentu pro tlačítko, kterou lze použít různými způsoby. Nyní máme komponentu, která může nabývat různých stavů.

Pojďme napsat náš první příběh podle následujících kroků.

  • Vytvořte soubor s názvem Button.stories.jsx.
  • Importujte React a naši komponentu Button do souboru.
  • Nyní definujte název nebo cestu našich příběhů. Definujeme to pomocí následujícího kódu.
export default {
   title: 'common/Button',
}

Výše uvedený kód umístí všechny příběhy, které jsou v daném souboru, do adresáře common/Button/.

  • Exportujte tlačítko s požadovanými rekvizitami následovně.
export const defaultButton = () => (
    <Button text=”Default Button” onClick={() => {}} />
);

Tímto jsme dokončili náš první příběh. Spusťte Storybook pomocí tohoto příkazu a podívejte se na výstup.

npm run storybook

Nebojte se, nakonec napíšeme i další příběhy.

Jak je to užitečné při vývoji frontendu?

Jaké jsou hlavní výhody používání Storybooku?

Řekněme, že pracujeme v 10členném týmu. Musíme zkontrolovat sdílené komponenty, které každý člen týmu vytvořil pro aktuální projekt.

Jak to můžeme provést?

Museli bychom projít každou komponentu zvlášť. To je časově náročné a není to pro nás preferovaný způsob. Zde se uplatní Storybook.

Jak ho můžeme využít k vyřešení našeho problému?

Můžeme vytvářet příběhy pro sdílené komponenty (všechny komponenty uživatelského rozhraní) pomocí Storybooku. Kdykoliv bude chtít některý z kolegů zkontrolovat komponenty ostatních, jednoduše spustí server Storybooku a uvidí všechny komponenty uživatelského rozhraní, jak jsme si ukázali výše.

S komponentami zobrazenými ve Storybooku můžeme dělat mnohem více. Storybook obsahuje koncept zvaný Addons, který dává našim příběhům nadstandardní funkce.

Řekněme, že potřebujeme zkontrolovat responsivitu komponent uživatelského rozhraní přímo ve Storybooku. Můžeme použít addon s názvem Viewport. Více o addonech se dozvíte v následujících sekcích.

Práce se Storybookem

V této sekci napíšeme různé příběhy, které definují odlišné stavy naší sdílené komponenty Button.

Psaní příběhů není tak složité. Příběh definuje stav komponenty. Pokud se podíváte na rekvizity komponenty, snadno pochopíte různé případy jejího použití.

Pojďme si napsat několik příběhů s volitelnými rekvizitami.

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"
    />
);

Uvedené tři příběhy definují různé případy použití naší komponenty Button. Nyní je na vás, abyste přidali další příběhy pro naši sdílenou komponentu. Zkuste přidat disabledSamllRectangularButton, dangerButton, successDisabledButton atd.

Nebudu zde uvádět kód pro výše zmíněné případy. Musíte si to napsat sami, abyste tomu porozuměli. Níže je kompletní kód příběhů, které jsme dosud vytvořili.

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"
    />
);

Nyní máte plnou kontrolu nad vytvářením příběhů pro komponentu.

Pojďme se podívat na další sekci, kde se seznámíme s doplňky a jejich přínosem pro naše příběhy.

Storybook Addons

Ve výchozím nastavení máme k dispozici několik doplňků. V této sekci si představíme ty nejužitečnější z nich pro náš vývoj.

Pojďme vylepšit naše příběhy pro komponentu Button.

Ovládací prvky

Ovládací prvky přidávají funkci, která umožňuje úpravu rekvizit komponenty přímo ve Storybooku. Pro naši komponentu Button můžeme přidat ovládací prvky pro změnu různých rekvizit v Storybooku.

Řekněme, že musíme vybrat nejlepší barvu pozadí tlačítka. Bylo by to časově náročné, kdybychom to testovali tak, že bychom museli postupně nastavovat barvu u komponenty. Místo toho můžeme přidat ovládací prvek, který nám umožní vybírat různé barvy přímo v Storybooku. Barvu pozadí můžeme otestovat přímo ve Storybooku.

Podívejme se, jak přidat ovládací prvky do příběhů tlačítek.

Nejprve musíme definovat všechny rekvizity pod nadpisem následovně.

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"] },
        },
    },
};

Dále oddělte rekvizity od komponenty a předejte je jako argumenty následovně.

export const outlineSmallButton = (args) => (
    <Button {...args} onClick={() => {}} />
);
outlineSmallButton.args = {
    text: "Outline Small Button",
    size: "small",
    type: "outline",
};

Ovládací prvky uvidíte ve spodní části okna s náhledem komponenty.

Karta ovládacích prvků je ve spodní části okna náhledu komponenty. Můžete si s ní pohrát.

Aktualizujte všechny příběhy, jak je uvedeno výše. Jedná se spíše o znalost syntaxe doplňků Storybook. V argTypes jsme použili různé typy ovládacích prvků. Všechny dostupné ovládací prvky najdete v dokumentaci Storybooku zde.

Aktualizované příběhy tlačítek budou vypadat následovně.

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",
};

Akce

Akce jsou události v JavaScriptu. Můžeme kliknout na tlačítko, což je událost v JavaScriptu. Některé akce můžeme provést kliknutím na tlačítko pomocí doplňku Akce.

Pomocí akcí můžeme testovat, zda události fungují správně. Na zakázané tlačítko nelze kliknout a na aktivní tlačítko musí být možné kliknout. Můžeme se o tom přesvědčit pomocí akcí.

Podívejme se, jak přidat akci k kliknutí na tlačítko.

Již dříve jsme poskytli anonymní funkci rekvizitám onClick. Nyní to musíme aktualizovat.

  • Importujte akci z doplňku Storybooku pomocí tohoto příkazu.
import { action } from "@storybook/addon-actions";
  • Nahraďte všechny () => {} následujícím příkazem.
action("Button is clicked!")

Nyní přejděte do Storybooku a klikněte na tlačítko. Zprávu uvidíte pod záložkou Akce, která je vedle záložky Ovládací prvky. Pokud klepnete na deaktivované tlačítko, zpráva se nezobrazí.

Tuto akci můžeme použít pro různé události, jako jsou onChange, onMouseOver, onMouseOut atd., abychom se ujistili, že fungují správně. Zkuste to implementovat pro onChange pro vstupní prvek.

Akce najdete v dokumentaci zde.

Pozadí

Pozadí náhledového okna můžeme změnit pomocí doplňku Pozadí. Nemusíme psát žádný kód. Stačí to změnit přímo ve Storybooku. Níže se můžete podívat na GIF.

Výřez

Ve Storybooku můžeme také otestovat responsivitu našich komponent. Na obrázku níže se dozvíte více o možnostech výřezu.

Docs

Své komponenty můžeme zdokumentovat ve Storybooku pomocí doplňku Docs. Je to užitečné, když pracujeme v týmu. Kolegové si mohou přečíst dokumentaci a přímo pochopit komponentu. To vývojářům ušetří spoustu času.

V okně náhledu komponenty můžete vidět Dokumenty vpravo nahoře na kartě Plátno. Zobrazí se všechny dokumenty pro všechny příběhy komponenty. Pokud chceme dokumentovat komponentu pomocí Markdownu a vykreslování komponenty, musíme použít Button.stories.mdx. V tomto souboru napíšeme extra markdown kód společně s příběhy komponent.

Píšeme dokument pro naše příběhy. Kód zahrnuje Markdown a vykreslování komponent. Jedná se pouze o naučení syntaxe, kterou rychle pochopíte.

Podívejme se na kód dokumentu 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>

Více informací o komponentách pro dokumentaci naleznete zde.

Můžete se také podívat na více informací o doplňcích zde.

Závěr

Doufám, že se vám tutoriál líbil a že jste se něco naučili o Storybooku. Doufám, že ho efektivně využijete ve svém týmu, abyste zvýšili vaši produktivitu.

Začínáte s Reactem? Podívejte se na tyto zdroje.

Příjemné kódování 🙂