Erste Schritte mit Storybook in React

Haben Sie jemals darüber nachgedacht, all Ihre UI-Komponenten an einem einzigen Ort in React zu organisieren?

Wenn Sie neu in der React-Welt sind, ist dies wahrscheinlich noch nicht geschehen.

Was genau bedeutet das?

Werfen Sie einen Blick auf die Beispiele von react-beautiful-dnd.

Die dort gezeigten Beispiele sind sogenannte „Stories“. Das Tool, das verwendet wird, um diese „Stories“ zu erstellen, nennt man Storybook.

Jetzt wissen Sie, worum es in diesem Artikel geht. Lassen Sie uns also direkt eintauchen.

Was ist Storybook?

Storybook ist eine Entwicklungsumgebung, die Ihre Benutzeroberfläche isoliert und Ihnen eine Art „Spielplatz“ für Ihre Komponenten bietet. Hier können Sie auf verschiedene Arten mit Ihren Komponenten interagieren, ohne die Hauptanwendung starten zu müssen. Storybook läuft mit seinem eigenen Setup auf einem separaten Port.

Es ist nicht nur auf React beschränkt. Storybook kann mit vielen Frontend-Frameworks wie Vue, Angular, Mithril, Marko, Svelte usw. verwendet werden.

Mehr über Storybook erfahren Sie hier.

Was ist eine Story?

Eine Story definiert den gerenderten Zustand einer Komponente. Nehmen wir eine einfache Komponente, so können wir diese mit verschiedenen Props auf unterschiedliche Weisen verwenden. Für jeden dieser Zustände können wir eine eigene Story erstellen.

Nehmen wir an, wir haben eine Button-Komponente.

Eine Schaltfläche kann in unterschiedlichen Zuständen existieren: deaktiviert, geladen, primär, sekundär, klein, groß, mittel usw. Wenn wir jeden Zustand einzeln auflisten, wird es schwierig, im Tutorial voranzukommen. Ich denke, Sie verstehen. Sie werden es besser verstehen, wenn Sie mit Storybook arbeiten.

Sie können die Geschichten der Schaltfläche in verschiedenen Fällen sehen (Groß, Mittel, Klein).

Storybook in einem Projekt einrichten

Wir werden Storybook in einem React-Projekt einrichten.

Los geht’s.

  • Erstellen Sie ein React-Projekt mit folgendem Befehl. Sie können es nennen, wie Sie möchten.
npx create-react-app storybook-demo
  • Installieren Sie nun Storybook in Ihrem Projekt mit dem folgenden Befehl.
npx sb init

Damit ist die Einrichtung von Storybook abgeschlossen.

Storybook stellt uns einen eigenen Server zur Verfügung.

Wie starte ich diesen?

Storybook fügt automatisch einen Befehl zu unserer Skriptdatei hinzu. Sie können dies in der Datei package.json im Abschnitt „scripts“ überprüfen. Führen Sie vorerst den folgenden Befehl aus, um den Storybook-Server zu starten.

npm run storybook

Storybook startet einen neuen Server auf dem Port, der im Abschnitt „scripts“ der Datei package.json angegeben ist. Storybook öffnet sich automatisch in Ihrem Standardbrowser (ähnlich wie der React-Server).

Sie sehen standardmäßig verschiedene Stories darin. Sie können diese entfernen, wenn Sie es nicht möchten, oder sie als Referenz behalten. Wie wir im vorherigen Abschnitt besprochen haben, kann eine Schaltfläche mehrere Zustände haben, und einige davon sehen Sie im Storybook (aber nicht alle). Im letzten Abschnitt dieses Tutorials werden wir eine große Anzahl von Stories für die Schaltfläche schreiben.

Erkunden Sie die verschiedenen Bereiche von Storybook, um sich mit ihnen vertraut zu machen. Wir werden einige davon im Tutorial behandeln.

Lassen Sie uns unsere erste Story schreiben.

Storybook testen

Wir haben gesehen, wie Storybook läuft und wie es einige Beispiele darstellt.

  • Erstellen Sie einen Ordner namens „Button“ im Ordner „src“.
  • Erstellen Sie Dateien namens „Button.jsx“, „Button.css“ und „constants.js“.
  • Fügen Sie den jeweiligen Code aus den folgenden Snippets in die Dateien ein.

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

Was ist das für ein Code?

Wir haben eine einfache Button-Komponente geschrieben, die auf verschiedene Arten verwendet werden kann. Wir haben jetzt eine Komponente, die verschiedene Zustände annehmen kann.

Lassen Sie uns unsere erste Story schreiben, indem Sie die folgenden Schritte ausführen.

  • Erstellen Sie eine Datei namens „Button.stories.jsx“.
  • Importieren Sie React und unsere Button-Komponente in die Datei.
  • Definieren Sie einen Titel oder Pfad für unsere Komponenten-Stories. Das machen wir mit dem folgenden Code.
export default {
   title: 'common/Button',
}

Der obige Code platziert alle Stories in der aktuellen Datei im Verzeichnis „common/Button/“.

  • Exportieren Sie eine Schaltfläche mit obligatorischen Props wie folgt.
export const defaultButton = () => (
    <Button text=”Default Button” onClick={() => {}} />
);

Wir haben unsere erste Story abgeschlossen. Führen Sie Storybook mit folgendem Befehl aus und sehen Sie sich die Ausgabe an.

npm run storybook

Wir werden am Ende noch mehr Stories schreiben, keine Sorge.

Wie nützlich ist das in der Frontend-Entwicklung?

Was ist der Hauptvorteil von Storybook?

Nehmen wir an, Sie arbeiten in einem Team von 10 Mitgliedern. Und wir müssen gemeinsame Komponenten überprüfen, die jedes Teammitglied für das aktuelle Projekt geschrieben hat.

Wie können wir das machen?

Wir müssten zu jeder einzelnen gemeinsamen Komponente gehen, um sie zu überprüfen. Das ist jedoch zeitaufwändig und nicht unsere bevorzugte Vorgehensweise. Hier kommt Storybook ins Spiel.

Wie können wir es nutzen, um unser Problem zu lösen?

Wir können mit Storybook Stories für die gemeinsamen Komponenten (alle UI-Komponenten) schreiben. Wenn ein Teamkollege die Komponenten der anderen überprüfen möchte, startet er einfach den Storybook-Server und kann alle UI-Komponenten dort sehen, wie wir es oben gesehen haben.

Mit den gerenderten Komponenten im Storybook können wir noch viel mehr machen. Storybook hat ein Konzept namens „Add-ons“, das unseren Stories zusätzliche Funktionalitäten verleiht.

Nehmen wir an, wir müssen die Reaktionsfähigkeit der UI-Komponenten direkt im Storybook überprüfen. Dafür können wir das Add-on „Viewport“ verwenden. In den kommenden Abschnitten werden wir mehr über Add-ons erfahren.

Arbeiten mit Storybook

In diesem Abschnitt werden wir verschiedene Stories schreiben, die die unterschiedlichen Zustände unserer gemeinsamen „Button“-Komponente definieren.

Das Schreiben von Stories ist nicht wirklich schwierig. Eine Story definiert einen Zustand einer Komponente. Wenn Sie sich die Props einer Komponente ansehen, werden Sie die verschiedenen Anwendungsfälle der Komponente leicht verstehen.

Lassen Sie uns einige Stories schreiben, indem wir optionale Props verwenden.

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

Die obigen drei Stories definieren unterschiedliche Anwendungsfälle unserer Button-Komponente. Jetzt sind Sie an der Reihe, weitere Story-Fälle für unsere gemeinsame Komponente hinzuzufügen. Versuchen Sie es mit „disabledSmallRectangularButton“, „DangerButton“, „SuccessDisabledButton“ usw.

Ich werde keinen Code für die oben genannten Fälle bereitstellen. Sie müssen es selbst schreiben, um es zu verstehen. Hier sehen Sie den gesamten Story-Code, den wir bisher geschrieben haben.

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

Jetzt haben Sie die Grundlagen des Schreibens von Stories für eine Komponente verstanden.

Springen wir zum nächsten Abschnitt, in dem wir etwas über Add-ons erfahren und wie sie unsere Stories verbessern.

Storybook Add-ons

Wir haben standardmäßig mehrere Add-ons zur Verfügung. In diesem Abschnitt werden wir die nützlichsten Add-ons für unsere Entwicklung untersuchen.

Verbessern wir unsere Button-Stories.

Controls

Controls fügen eine Funktion hinzu, um der Komponente im Storybook benutzerdefinierte Props zu geben. Für unsere Button-Komponente können wir Controls hinzufügen, um die verschiedenen Props im Storybook zu ändern.

Nehmen wir an, wir müssen die beste Farbe für die Hintergrundfarbe der Schaltfläche finden. Das wäre zeitaufwendig, wenn wir dies manuell testen würden, indem wir die Hintergrundfarbe der Komponente einzeln ändern. Stattdessen können wir ein Steuerelement hinzufügen, mit dem wir eine andere Farbe im Storybook auswählen können. So können wir die Hintergrundfarbe direkt im Storybook testen.

Sehen wir uns an, wie Sie unseren Button-Stories Controls hinzufügen.

Zuerst müssen wir alle Props unterhalb des Titels wie folgt definieren.

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

Trennen Sie als Nächstes die Props von der Komponente und geben Sie sie als Argumente an, wie folgt.

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

Sie sehen die Controls am unteren Rand des Vorschaufensters der Komponente.

Sie finden die Registerkarte „Controls“ am unteren Rand des Vorschaufensters der Komponente. Experimentieren Sie damit.

Aktualisieren Sie alle Stories wie oben. Im Grunde geht es darum, die Syntax der Storybook-Add-ons zu erlernen. In argTypes haben wir verschiedene Arten von Controls verwendet. Alle in Storybook verfügbaren Controls finden Sie hier.

Die aktualisierten Button-Stories sehen wie folgt aus.

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

Aktionen sind Ereignisse in JavaScript. Wir können auf eine Schaltfläche klicken, was ein Ereignis in JavaScript ist. Mit dem Actions-Add-on können wir einige Aktionen bei einem Klick auf eine Schaltfläche ausführen.

Mit Aktionen können wir testen, ob die Ereignisse korrekt funktionieren oder nicht. Eine deaktivierte Schaltfläche kann nicht angeklickt werden und eine aktivierte Schaltfläche muss anklickbar sein. Das können wir mit den Aktionen überprüfen.

Sehen wir uns an, wie Sie dem Klick auf eine Schaltfläche eine Aktion hinzufügen.

Wir haben zuvor eine anonyme Funktion für den Prop „onClick“ bereitgestellt. Jetzt müssen wir es aktualisieren.

  • Importieren Sie die „action“ aus dem Storybook-Add-on mit der folgenden Anweisung.
import { action } from "@storybook/addon-actions";
  • Ersetzen Sie alle „() => {}“ durch die folgende Anweisung.
action("Button is clicked!")

Gehen Sie nun zu Storybook und klicken Sie auf eine Schaltfläche. Die Nachricht wird unter der Registerkarte „Aktionen“ ausgegeben, die sich neben der Registerkarte „Controls“ befindet. Die Nachricht wird nicht ausgegeben, wenn Sie auf die deaktivierte Schaltfläche klicken, da sie deaktiviert ist.

Wir können die Aktion für verschiedene Ereignisse wie „onChange“, „onMouseOver“, „onMouseOut“ usw. verwenden, um sicherzustellen, dass sie korrekt funktionieren. Versuchen Sie, das Gleiche für „onChange“ für ein Eingabeelement zu implementieren.

Sehen Sie sich die Dokumentation für Aktionen hier an.

Backgrounds

Mit dem Backgrounds-Add-on können wir den Hintergrund des Vorschaufensters ändern. Wir müssen keinen Code schreiben. Ändern Sie es einfach im Storybook. Sie können das GIF unten sehen.

Viewport

Wir können auch die Reaktionsfähigkeit unserer Komponenten in Storybook testen. Sehen Sie sich das GIF unten an, um mehr über die Viewport-Optionen zu erfahren.

Docs

Mit dem Docs-Add-on können wir unsere Komponenten in Storybook dokumentieren. Es ist besonders nützlich, wenn wir in einem Team arbeiten. Sie werden die Komponente lesen und direkt verstehen. Das spart den Entwicklern viel Zeit.

Im Vorschaufenster der Komponenten von Storybook können Sie oben rechts den Reiter „Canvas Docs“ sehen. Er enthält alle Dokumente aller Stories einer Komponente. Wir müssen „Button.stories.mdx“ verwenden, wenn wir die Komponente dokumentieren möchten, da es sowohl Markdown als auch das Rendern von Komponenten beinhaltet. Wir schreiben einfach zusätzlichen Markdown-Code zusammen mit den Komponenten-Stories hinein.

Wir schreiben ein Dokument für unsere Stories. Der Code enthält Markdown und das Rendern von Komponenten. Es geht nur darum, die Syntax zu erlernen. Sie werden es direkt verstehen.

Werfen wir einen Blick auf den „Button.stories.mdx“-Dokumentationscode.

<!--- 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>

Weitere Informationen über die dokumentierenden Komponenten finden Sie hier.

Mehr über Add-ons erfahren Sie hier.

Fazit

Ich hoffe, Ihnen hat dieses Tutorial gefallen und Sie haben etwas über Storybook gelernt. Nutzen Sie es effektiv in Ihrem Team, um Ihre Arbeit produktiver zu gestalten.

Neu bei React? Sehen Sie sich diese Lernressourcen an.

Viel Spaß beim Programmieren 🙂