DEV Community

Cover image for Utilizando o Storybook para componentes
Lucas Lafèré
Lucas Lafèré

Posted on

Utilizando o Storybook para componentes

Para que serve o Storybook?

O Storybook é uma ferramenta bastante útil para desenvolvedores de aplicações web baseadas em componentes. Ele permite a criação de componentes de forma isolada, tornando mais fácil testar diferentes estados, cores e propriedades.

Sabe aquele botãozinho que ao clicar, deve ter seu texto mudado, e de acordo com isso, também deve mudar sua cor? Com o Storybook, a tarefa de testar essa funcionalidade se torna muito mais fácil.

Além disso, o Storybook ajuda a criar uma documentação para os componentes, o que facilita o trabalho em equipe.

O Storybook é amplamente utilizado para desenvolvimentos baseados em Component Driven UI. Neste tipo de abordagem, os componentes são criados primeiro e depois as páginas são construídas a partir deles, seguindo uma estratégia "from the bottom-up".

Aqui vai um exemplo do Storybook em funcionamento:

Image description

Instalando o Storybook

Para instalar o Storybook na sua aplicação React, seja com CRA (Create React App) ou Vite, basta executar o seguinte comando no terminal:

npx sb init

Enter fullscreen mode Exit fullscreen mode

Com isso, serão instalados alguns arquivos e pastas novas em seu projeto. A pasta .storybook contém as configurações padrão do Storybook, enquanto a pasta stories é destinada a armazenar os seus stories.

Para inicializar o Storybook após a instalação, execute o seguinte comando:

npm run storybook

Enter fullscreen mode Exit fullscreen mode

Nota: É recomendável que você exclua todos os arquivos na pasta /stories/, pois eles não serão utilizados e podem causar confusão para alguém novo no Storybook.

Criando um Story

Para adicionar um story para um componente em React, crie um arquivo com o nome [nome_do_componente].stories.jsx, por exemplo, Button.stories.jsx para um componente chamado Button.

Exemplo de um componente Button:

import PropTypes from 'prop-types';

function Button({ label, backgroundColor = 'red', size = 'md', handleClick }) {
  let scale = 1;
  if (size === 'sm') scale = 0.75;
  if (size === 'lg') scale = 1.5;

  const style = {
    backgroundColor,
    padding: `${scale * 0.5}rem ${scale * 1}rem`,
    border: 'none',
  };

  return (
    <button onClick={handleClick} style={style}>
      {label}
    </button>
  );
}

Button.propTypes = {
  label: PropTypes.string,
  backgroundColor: PropTypes.string,
  size: PropTypes.oneOf(['sm', 'md', 'lg']),
  handleClick: PropTypes.func,
};

export default Button;
Enter fullscreen mode Exit fullscreen mode

Em seguida, você deve criar um objeto em Button.stories.jsx:

// Em Button.stories.jsx:

import Button from '../components/Button';

export default {
  title: 'My New Button',
// O componente que você deseja renderizar, testar e adicionar um story sobre:
  component: Button, 
};

Enter fullscreen mode Exit fullscreen mode

Agora, você pode criar uma função que retorne o seu componente e dar o nome "Red" ao seu story:

// Em Button.stories.jsx:

import Button from '../components/Button';

export default {
  title: 'My New Button',
  component: Button, 
};

export const Red = () => <Button label="Press Me" backgroundColor="red" />;

Enter fullscreen mode Exit fullscreen mode

Se você estiver usando TypeScript ou o recurso "propTypes" para tipar as suas props, o StoryBook será capaz de ler, inferir e documentar essa tipagem, sabendo, por exemplo, que label: string e backgroundColor: string.

Primeiro componente storybook, um quadrado vermelho com o texto "press me"

Com isso, você já tem o seu primeiro story. Ele ainda não é interativo, mas já existe com documentação e já mostra como seria renderizado em uma página. Agora você pode torná-lo interativo para alternar as props, receber ações e assim por diante.

Tornando nosso Story interativo

Primeiro, precisamos voltar ao nosso arquivo Button.stories.jsx e adicionar a seguinte linha:

//Em Button.stories.jsx:

import Button from '../components/Button';

export default {
  title: 'My new Button',
  component: Button 
}

// nova função aqui:

const Template = args => <Button {...args} />export const Red = () => <Button label="Press me" backgroundColor="red" />

Enter fullscreen mode Exit fullscreen mode

Com isso, estamos criando na variável Template uma função que recebe alguns argumentos (args) - como as props do Componente - e retorna o Button, passando todos os args (props) para o Button.

Agora, para cada novo “estilo” que desejamos testar do nosso componente Button, basta utilizarmos o Template que criamos.

Por exemplo:

//Em Button.stories.jsx:

import Button from '../components/Button';

export default {
  title: 'My new Button',
  component: Button 
}
// mudando o Red para utilizar a Template:

const Template = args => <Button {...args} />
export const Red = Template.bind({}) 
Red.args = { 
  backgroundColor = "red",
  label: "Press me",
  size: "md",
}

Enter fullscreen mode Exit fullscreen mode

Voltando para o Storybook, conseguimos manipular esse Story:

Quadrado verde

Mantendo track de “Actions” dos componentes

Quando criamos uma função chamada onClick como uma prop do nosso componente, o Storybook é capaz de reconhecer, por assinatura, que há algum tipo de evento acontecendo ali, e isso é mostrado na seção Actions da biblioteca.

Mas e quando temos uma função diferente, como handleClick, que utilizamos acima? Para tornar essa função uma Action dentro do Storybook, precisamos usar argTypes dentro do arquivo Button.stories.jsx:

import Button from '../components/Button';

export default {
  title: "'My new Button',"
  component: Button,
  argTypes: {
    handleClick: {
      action: "handleClick"
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

Estamos dizendo para o Storybook que essa prop handleClick é uma Action, chamada handleClick. Essa string pode ser o que você quiser, é o nome utilizado dentro do Storybook para essa action.

const Template = args => <Button {...args} />export const Red = Template.bind({})
Red.args = {
  backgroundColor: "red",
  label: "Press me",
  size: "md",
}

Enter fullscreen mode Exit fullscreen mode

Agora no Storybook → Actions, podemos encontrar a função que foi utilizada, com todas as informações de evento dela, como quando utilizamos console.log em um evento:

Log de eventos do handleClick

Criando múltiplos Stories para um mesmo componente

E agora, para finalizar (o básico) da magia do Storybook, podemos criar múltiplos stories para um mesmo componente, utilizando o mesmo Template de antes:

//Em Button.stories.jsx:

import Button from '../components/Button';

export default {
title: "'My new Button',"
component: Button
argTypes: { handleClick: { action: "handleClick"} }
}

const Template = args => <Button {...args} />

export const Red = Template.bind({})
Red.args = {
backgroundColor = "red",
label: "Press me",
size: "md",
}
// criando um botão verde:
export const Green = Template.bind({})
Green.args = {
backgroundColor = "green",
label: "Press me",
size: "md",
}
// criando um botão pequeno:
export const Small = Template.bind({})
Small.args = {
backgroundColor = "red",
label: "Press me",
size: "sm",
}
// criando um botão grande:
export const Large = Template.bind({})
Large.args = {
backgroundColor = "red",
label: "Press me",
size: "lg",
}
// criando um botão com muito texto:
export const LongLabel = Template.bind({})
LongLabel.args = {
backgroundColor = "red",
label: "Press me hptrhpokophpoj gkpdofk pogdfpo kpgodfk pgdkpfo",
size: "md",
}

Enter fullscreen mode Exit fullscreen mode

Ou seja, basta utilizarmos a mesma Template, mudando os argumentos conforme necessidade, baseado no que desejamos testar.

export const NomeDoStory = Template.bind({})
NomeDoStory.args = {
backgroundColor = "red",
label: "Press me",
size: "md",
}

Enter fullscreen mode Exit fullscreen mode

No Storybook:

Imagem com diferentes componentes para testar, criados em nosso código, como mostrado acima


Com isso, você agora já é capaz de criar novos componentes de maneira prática, utilizando os Templates, e testar os diferentes estados que aquele componente precisa ter.

Top comments (0)