Introdução ao Storybook em React

Você já tentou colocar todos os seus componentes de interface do usuário em um local no React?

Se você é novo no mundo do React, provavelmente não o fará.

O que significa isso?

Veja o reagir-belo-dnd exemplos.

O que você viu nos exemplos são chamados de histórias. E a ferramenta usada para criar histórias se chama Storybook.

Agora você já entendeu o que vamos falar neste artigo. Sem delongas, vamos explorar.

O que é o livro de histórias?

O Storybook é um ambiente de desenvolvimento isolado da interface do usuário que fornece um playground para seus componentes. Podemos brincar com nossos componentes de diferentes maneiras sem executar nosso aplicativo principal. Podemos executar o livro de histórias em sua porta com a configuração.

Não se limita ao React. Podemos usar storybook com a maioria dos frameworks frontend como Vue, Angular, Mithril, Marko, Svelte, etc.,

Você pode encontrar mais sobre o livro de histórias aqui.

O que é uma história?

Uma história define o estado renderizado do seu componente. Se pegarmos um componente comum, podemos usá-lo de maneiras diferentes com props. Podemos escrever uma história para cada um desses estados.

Digamos que temos um componente Button.

Um botão pode existir em diferentes estados como desativado, carregando, primário, secundário, pequeno, grande, médio, etc. Se listarmos todos os estados, será muito difícil avançar no tutorial. Acho que você entende. Você obterá mais quando começar a trabalhar com o livro de histórias.

Você pode ver as histórias do botão em diferentes casos (Grande, Médio, Pequeno).

Configurando o Storybook em um projeto

Vamos configurar um livro de histórias em um projeto de reação.

Vamos lá.

  • Crie um projeto de reação com o seguinte comando. Você pode nomear o que quiser.
npx create-react-app storybook-demo
  • Agora, instale o livro de histórias em seu projeto com o seguinte comando.
npx sb init

Concluímos a configuração do livro de histórias.

O livro de histórias fornece um servidor separado para nós.

Como iniciá-lo?

O livro de histórias adiciona automaticamente um comando em nosso arquivo de script. Você pode verificá-lo no arquivo package.json dentro da seção de scripts. Por enquanto, execute o seguinte comando para iniciar o servidor storybook.

npm run storybook

O Storybook iniciará um novo servidor com a porta fornecida na seção de scripts do arquivo package.json. Ele abrirá automaticamente o livro de histórias em nosso navegador padrão (igual ao servidor react).

Você verá histórias diferentes nele por padrão. Você pode removê-los se não quiser ou guardá-los para referência. Como discutimos na seção anterior, um botão pode ter vários estados, você pode vê-los no storybook (nem todos os estados mencionados). Vamos escrever um grande conjunto de histórias para o botão na última seção deste tutorial.

Explore diferentes seções do livro de histórias e familiarize-se com as diferentes seções. Vamos cobrir alguns deles no tutorial.

Vamos escrever nossa primeira história.

Testando o livro de histórias

Vimos o livro de histórias em execução e alguns exemplos nele.

  • Crie uma pasta chamada Button dentro da pasta src.
  • Crie arquivos chamados Button.jsx, Button.css e constants.js
  • Coloque o respectivo código dos trechos abaixo nos arquivos.

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

Qual é esse código?

  Problemas de segurança da Internet das Coisas (IoT) e como evitá-los

Escrevemos um componente comum para Button que pode ser usado de diferentes maneiras. Agora, temos um componente que pode ter diferentes estados.

Vamos escrever nossa primeira história seguindo os passos abaixo.

  • Crie um arquivo chamado Button.stories.jsx
  • Importe React e nosso componente Button para o arquivo.
  • Agora, defina um título ou caminho para nossas histórias componentes. Vamos defini-lo usando o seguinte código.
export default {
   title: ‘common/Button’,
}

O código acima colocará todas as histórias que estão no arquivo atual dentro do diretório common/Button/.

  • Exporte um botão com adereços obrigatórios da seguinte maneira.
export const defaultButton = () => (
    <Button text=”Default Button” onClick={() => {}} />
);

Concluímos nossa primeira história. Execute o livro de histórias com o seguinte comando e veja a saída.

npm run storybook

Escreveremos mais histórias, no final, não se preocupe.

Como é útil no desenvolvimento Frontend?

Qual é a principal vantagem de usar um livro de histórias?

Digamos que estamos trabalhando em uma equipe de 10 membros. E precisamos verificar os componentes comuns que todos escreveram para o projeto de trabalho atual.

Como podemos fazer isso?

Temos que ir a todos os componentes comuns para verificá-los. Mas, é demorado e não é uma maneira preferida para nós. Aí vem nosso novo livro de histórias de convidados.

Como utilizá-lo para superar nosso problema?

Podemos escrever histórias para os componentes comuns (qualquer componente de interface do usuário) usando o livro de histórias. E sempre que seu colega de equipe quiser verificar os componentes comuns de outras pessoas, ele simplesmente executará o servidor do livro de histórias e verá todos os componentes da interface do usuário, como vimos acima.

Podemos fazer muito mais com os componentes renderizados no storybook. O Storybook tem um conceito chamado Addons que dá superpoderes às nossas histórias.

Digamos que temos que verificar a capacidade de resposta dos componentes da interface do usuário no próprio livro de histórias, podemos usar um complemento chamado Viewport no livro de histórias. Aprenderemos mais sobre os complementos nas próximas seções.

Trabalhando com livro de histórias

Nesta seção, escreveremos diferentes histórias definindo diferentes estados de nosso componente comum Button.

Escrever histórias não é tão difícil. Uma história define um estado de um componente. Se você vir as props de um componente, entenderá facilmente os diferentes casos de uso do componente.

Vamos escrever algumas histórias dando adereços opcionais.

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

As três histórias acima definem diferentes casos de uso do nosso componente Button. Agora, é sua vez de adicionar alguns outros casos de histórias para nosso componente comum. Tente adicionar disabledSamllRectangularButton, hazardButton, successDisabledButton, etc.,

  Como as soluções de RH e folha de pagamento da ADP podem ajudar seu negócio em crescimento?

Não fornecerei código para os casos acima. Você tem que escrevê-lo sozinho para entendê-lo. Você pode ver o código completo das histórias que escrevemos até agora.

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

Agora, você tem domínio total sobre como escrever histórias para um componente.

Vamos pular para a próxima seção, onde aprenderemos sobre complementos e como eles impulsionam nossas histórias.

Complementos do livro de histórias

Teremos vários complementos disponíveis por padrão. Na seção, exploraremos os addons mais úteis para o nosso desenvolvimento.

Vamos impulsionar nossas histórias de Button.

Controles

Os controles adicionam um recurso para fornecer acessórios personalizados ao componente no próprio livro de histórias. Para nosso componente Button, podemos adicionar controles para alterar os diferentes adereços no livro de histórias.

Digamos que temos que descobrir a melhor cor para a cor de fundo do botão. Será demorado se testarmos para verificar a cor de fundo, dando um por um ao componente. Em vez disso, podemos adicionar um controle que nos permite escolher a cor diferente no livro de histórias. Podemos testar a cor de fundo no próprio livro de histórias.

Vamos ver como adicionar controles às nossas histórias de botão.

Primeiro, temos que definir todos os adereços abaixo do título da seguinte forma.

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

Em seguida, separe os props do componente e forneça-os como argumentos a seguir.

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

Você pode ver os controles na parte inferior da janela de visualização do componente.

Você pode ver a guia de controles na parte inferior da janela de visualização do componente. Brinque com isso.

Atualize todas as histórias como acima. Isso é mais como conhecer a sintaxe dos complementos do livro de histórias. Nos argTypes, usamos diferentes tipos de controles. Você pode encontrar todos os controles que estão presentes no livro de histórias aqui.

As histórias de botão atualizadas terão a seguinte aparência.

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

Ações

Ações são eventos em JavaScript. Podemos clicar em um botão que é um evento em JavaScript. Podemos fazer algumas ações ao clicar no botão usando o complemento de ações.

  Guia completo para hospedar seu WordPress no SiteGround

Com ações, podemos testar se os eventos estão funcionando corretamente ou não. O botão desativado não pode ser clicado e o botão ativado deve ser clicável. Podemos garantir isso usando as ações.

Vamos ver como adicionar ação ao clique do botão.

Demos funções anônimas para as props onClick anteriormente. Agora, temos que atualizá-lo.

  • Importe a ação do complemento storybook usando a seguinte instrução.
import { action } from "@storybook/addon-actions";
  • Substitua todos os () => {} pela instrução a seguir.
action("Button is clicked!")

Agora, vá para o livro de histórias e clique em um botão. Você verá a mensagem impressa na guia de ações ao lado da guia de controles. A mensagem não será impressa se você clicar no botão desativado, pois ele está desativado.

Podemos usar a ação para diferentes eventos como onChange, onMouseOver, onMouseOut, etc., para garantir que eles estejam funcionando corretamente. Tente implementar o mesmo para onChange para um elemento de entrada.

Veja a documentação para ações aqui.

Fundo

Podemos alterar o plano de fundo da janela de visualização usando o complemento de plano de fundo. Não precisamos escrever nenhum código. Basta alterá-lo dentro do livro de histórias. Você pode ver o gif abaixo.

Janela de exibição

Também podemos testar a capacidade de resposta de nossos componentes no livro de histórias. Veja o gif abaixo para saber mais sobre as opções de viewport.

Documentos

Podemos documentar nossos componentes no livro de histórias usando o addon docs. É mais útil quando estamos trabalhando em equipe. Eles vão ler o componente e entendê-lo diretamente. Isso economiza muito tempo para os desenvolvedores.

Na janela de visualização dos componentes do livro de histórias, você pode ver os documentos no canto superior direito da guia Tela. Ele conterá todos os documentos de todas as histórias de um componente. Temos que usar Button.stories.mdx se quisermos documentar o componente que inclui markdown e renderização do componente. Nós apenas escrevemos algum código de markdown extra dentro dele junto com as histórias dos componentes.

Estamos escrevendo um documento para nossas histórias. O código inclui remarcação e renderização de componentes. É tudo apenas aprender a sintaxe. Você vai obtê-lo à primeira vista.

Vamos ver o código do documento 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>

Saiba mais sobre os componentes de documentação aqui.

Você pode descobrir mais sobre complementos aqui.

Conclusão

Espero que você tenha gostado do tutorial e aprendido sobre o livro de histórias. E use-o de forma eficaz em sua equipe para tornar seu trabalho produtivo.

Novo no React? Confira esses recursos de aprendizagem.

Codificação feliz 🙂