Alura > Cursos de Front-end > Cursos de React > Conteúdos de React > Primeiras aulas do curso React: eleve o nível da sua documentação no Storybook

React: eleve o nível da sua documentação no Storybook

Finalizando o Design System - Apresentação

Olá! Tudo bem? Eu sou Neilton, mas você pode me chamar de Nei.

Audiodescrição: Neilton se autodescreve como um homem cisgênero de pele negra, usa óculos de grau com armação retangular, possui cabelos encaracolados e está vestindo uma camisa de manga azul marinho. Ao fundo, há uma luz degradê nas cores azul e rosa.

O que aprenderemos?

Neste curso, vamos aprender a aprimorar a qualidade da nossa documentação no Storybook, utilizando recursos do próprio Storybook! Então, o que vamos aprender?

Estamos no navegador, onde vamos, finalmente, completar nossa biblioteca de componentes. Nos cursos anteriores, aprendemos como criar cada um deles, inclusive tornando os componentes o mais acessíveis possível, e agora vamos concluir, criando os últimos componentes que faltam: Modal e Drop-Down, que são um pouco mais complexos.

Depois disso, vamos nos aprofundar no Storybook e aprenderemos a utilizar recursos que melhorarão nossa documentação. Também estudaremos um pouco mais sobre uma ferramenta de testes, o Chromatic, desenvolvida pelo próprio Storybook.

Esta ferramenta realiza testes visuais e de regressão, fundamentais para quando estivermos trabalhando com nossa equipe e necessitarmos de feedback sobre as modificações no código.

O resultado final está sendo exibido na tela: criamos 12 componentes com 60 stories, e agora estamos no ponto de base dos nossos testes visuais de regressão. Vamos aprender a usar o Chromatic, configurá-lo, instalá-lo e implementá-lo em nosso projeto.

Outro ponto que também abordaremos é como organizar nossa documentação, categorizando cada componente em uma pasta específica, seja ele um átomo ou uma molécula. Também vamos separá-los por categorias, destinguindo Design System e Design Tokens, usando, por exemplo, cores.

Vamos aprender muitos conteúdos interessantes, que agregarão à sua carreira.

Pré-requisitos

Para um melhor aproveitamento do curso, você precisa ter concluído os dois cursos anteriores desta formação, e conhecer o básico de Next e Tailwind CSS.

Últimos recados

Estou muito empolgado para iniciar este curso com você! Mas antes, tenho um recado! Gostaria que você se juntasse à nossa comunidade no Discord. Todos os dias ocorrem eventos, por isso é fundamental que você participe, assim estará imerso em um ambiente de aprendizado, além de ter a oportunidade de trocar ideias, informações e conhecimentos conosco, instrutores, e com outras pessoas estudantes.

Sem mais delongas, vamos começar?!

Finalizando o Design System - Criação do componente Modal

Dando continuidade à criação do nosso design system, vamos desenvolver um componente de modal. Esse componente é bastante comum em muitas aplicações web, quando queremos, por exemplo:

Então, precisamos desenvolver esse componente de modal em nosso design system.

No Figma, vamos localizar "Pages" na lateral esquerda da tela e acessar a aba "Moléculas". Vamos procurar o componente de modal. É possível notar que ele apresenta quatro variantes, o que já demonstra sua versatilidade, o que pode conter.

Há uma variante do modal que tem um título, um texto e dois botões: um de confirmação e outro de deleção, que serve para excluirmos algum item de algum lugar.

Precisamos criar um componente de modal acessível, pois quando clicamos no botão para abri-lo, ele sobrepõe vários elementos da nossa tela. Então, esse componente modal precisa ser acessível para que mais pessoas, incluindo as que têm um pouco de dificuldade de navegar na nossa página, consigam acessá-lo. Nós já sabemos como fazer isso! Utilizamos a biblioteca Headless UI.

Vamos acessar a página Headless ui e escolher o elemento "Dialog (Modal)". Se trata de um componente de modal, uma caixa de diálogo, personalizável e acessível. Podemos interagir com a documentação, fechar, abrir o modal, visualizar a transição/animação, e conferir o código de exemplo (Em "Basic example").

import { useState } from 'react'
import { Dialog } from '@headlessui/react'

function MyDialog() {
  let [isOpen, setIsOpen] = useState(true)

  return (
    <Dialog open={isOpen} onClose={() => setIsOpen(false)}>
      <Dialog.Panel>
        <Dialog.Title>Deactivate account</Dialog.Title>
        <Dialog.Description>
          This will permanently deactivate your account
        </Dialog.Description>

        <p>
          Are you sure you want to deactivate your account? All of your data
          will be permanently removed. This action cannot be undone.
        </p>

        <button onClick={() => setIsOpen(false)}>Deactivate</button>
        <button onClick={() => setIsOpen(false)}>Cancel</button>
      </Dialog.Panel>
    </Dialog>
  )
}

Vamos nos basear nele para a criação do componente modal!

No Visual Studio Code acessaremos a pasta de componentes e criaremos uma nova pasta chamada "Modal", e, dentro dela, criaremos o arquivo Modal.tsx.

Agora, vamos criar a estrutura base do componente de modal. Começaremos importando o Reacte exportando uma função chamada const Modal, que é uma arrow function. Dentro dela, retornaremos algo. Por enquanto, apenas uma div.

import React from 'react'

const Modal = () => { 
  return <div></div>
}

Mais abaixo, exportaremos, por padrão, o componente de Modal que estamos criando.

import React from 'react'

const Modal = () => { 
  return <div></div>
}

export default Modal; 

Com isso, criamos a estrutura básica do componente de modal. Agora, precisamos dizer para essa modal o que ele vai receber de propriedade. Por isso, dentro dos parênteses da função, entre chaves, indicaremos que essa modal vai receber:

import React from 'react'

const Modal = (children, isOpen, onClose, title) => { 
  return <div></div>
}

export default Modal; 

O Visual Studio Code está apontando que as props ainda não foram tipadas. Vamos fazer isso agora!

Então, acima, na linha 3, vamos inserir export type e nomear como ModalProps. Isso será igual a um objeto, onde tiparemos cada uma das propriedades:

import React from 'react'
  
export type ModalProps = {
  children: string;
    isOpen: boolean;
    title: string;
    onClose: () => void;
};

const Modal = (children, isOpen, onClose, title) => { 
  return <div></div>
};

export default Modal; 

Escrevemos todos os tipos da modal e o próximo passo é atribuir o ModalProps às propriedaades dentro dos parênteses da função modal.

import React from 'react'
  
export type ModalProps = {
  children: string;
    isOpen: boolean;
    title: string;
    onClose: () => void;
};

const Modal = (children, isOpen, onClose, title): ModalProps => { 
  return <div></div>
};

export default Modal; 

Com isso, estamos tipando o componente de modal. E para garantir que tudo esteja correto, dado que precisamos que o componente modal tenha também o tipo JSX, na linha oito, depois das chaves, vamos inserir um & ("e" comercial), e colar o tipo React.HTMLAttributes<HTMLDivElements>, considerando que o <HTMLDivElement> serve apenas para identificação da modal.

import React from 'react'
  
export type ModalProps = {
  children: string;
    isOpen: boolean;
    title: string;
    onClose: () => void;
} & React.HTMLAttributes<HTMLDivElements>;;

const Modal = (children, isOpen, onClose, title): ModalProps => { 
  return <div></div>
};

export default Modal; 

Agora, temos que criar toda a estrutura do modal. Estamos retornando uma div, mas precisamos retornar a estrutura do modal conforme a documentação do Headless UI. Então, voltando ao navegador e conferindo o código de exemplo, percebemos uma estrutura composta por: Dialog; Dialog.Pane; Dialog.Title; e Dialog.Description.

import { useState } from 'react'
import { Dialog } from '@headlessui/react'

function MyDialog() {
  let [isOpen, setIsOpen] = useState(true)

  return (
    <Dialog open={isOpen} onClose={() => setIsOpen(false)}>
      <Dialog.Panel>
        <Dialog.Title>Deactivate account</Dialog.Title>
        <Dialog.Description>
          This will permanently deactivate your account
        </Dialog.Description>

        <p>
          Are you sure you want to deactivate your account? All of your data
          will be permanently removed. This action cannot be undone.
        </p>

        <button onClick={() => setIsOpen(false)}>Deactivate</button>
        <button onClick={() => setIsOpen(false)}>Cancel</button>
      </Dialog.Panel>
    </Dialog>
  )
}

Não utilizaremos todos, mas vamos aproveitá-los na criação da estrutura do nosso modal.

Para economizar tempo, já temos o código pronto e vamos apenas copiá-lo e colá-lo no Visual Studio Code.

import React from "react";

export type ModalProps = {
  children: string;
  isOpen: boolean;
  title: string;
  onClose: () => void;
} & React.HTMLAttributes <HTMLDivElement>;;

const Modal = ({ children, isOpen, onClose, title }: ModalProps) => {
  return <div></div>;
};

export default Modal;

No lugar da <div></div>, para termos uma animação de transição no nosso modal abri-la e fechá-la, vamos adicionar um Transition, que será importado do "@headlessui/react".

import { Transition } from "headlessui/react";
import React from "react";

export type ModalProps = {
  children: string;
  isOpen: boolean;
  title: string;
  onClose: () => void;
} & React.HTMLAttributes <HTMLDivElement>;;

const Modal = ({ children, isOpen, onClose, title }: ModalProps) => {
  return <Transition></Transition>;
};

export default Modal;

O Transition recebe algumas propriedades:

import { Transition } from "headlessui/react";
import React from "react";

export type ModalProps = {
  children: string;
  isOpen: boolean;
  title: string;
  onClose: () => void;
} & React.HTMLAttributes <HTMLDivElement>;;

const Modal = ({ children, isOpen, onClose, title }: ModalProps) => {
  return <Transition appear show={isOpen} as></Transition>;
};

export default Modal;

Já está quase tudo pronto, só falta dizer que esse Transition será um Fragment. O Fragment é uma estrutura do React que permite o envelopamento dos elementos. O React precisa disso: ter um elemento pai para todos os elementos que precisamos renderizar.

import { Transition } from "headlessui/react";
import React from "react";

export type ModalProps = {
  children: string;
  isOpen: boolean;
  title: string;
  onClose: () => void;
} & React.HTMLAttributes <HTMLDivElement>;;

const Modal = ({ children, isOpen, onClose, title }: ModalProps) => {
  return <Transition appear show={isOpen} as={Fragment}></Transition>;
};

export default Modal;

Dentro do Transition vamos colar o código que acabamos de copiar.

import { Transition } from "headlessui/react";
import React from "react";

export type ModalProps = {
  children: string;
  isOpen: boolean;
  title: string;
  onClose: () => void;
} & React.HTMLAttributes <HTMLDivElement>;;

const Modal = ({ children, isOpen, onClose, title }: ModalProps) => {
  return 
      <Transition appear show={isOpen} as={Fragment}>
           <Dialog as="div" className="" onClose=(onClose}>
          <Transition.Child
            as={Fragment}
            enter="ease-out duration-300"
            enterFrom="opacity=0"
            enterTo="opacity-100"
            leave-"ease-in duration-200"
            leaveFrom="opacity-100"
            leaveTo="opacity-0"
          >
<div className="" />
</Transition.Child>

// Código omitido. 

Estamos utilizando um Dialog e o Visual Studio Code está reclamando que não importamos esse Dialog. Então, vamos importá-lo. No meio da nossa Modal, temos um ícone também, que serve para fechá-la.

  <Dialog.Panel className="">
     <div className="">
       <Dialog. Title as="h3" className=">
        {title}
       </Dialog.Title>
       <button onClick={onClose}>
         <XMarkIcon qlassName="" />
       </button>
      </div>
       {children)
     </Dialog.Panel> </Transition.Child>
    </div>
   </div>
  </Dialog>
 </Transition>
  );
};

Vamos importar esse ícone também, da biblioteca @heroicons/react/24/outline".

import { Dialog, Transition } from "@headlessui/react";
import { XMarkIcon } from "@herocoins/react/24/outline";
import React, { Fragment } from "react";

// Código omitido. 

Então, temos o Transition. Dentro dele, estamos utilizando o Dialog, que funciona como um container, é onde todo o conteúdo da nossa modal ficará armazenado.

Além disso, temos o Transition.Child que vai lidar com as animações. Em suma, o Transition é usado para inserirmos uma animação em nosso componente e o Transition.Child, para controlá-la.

// Código omitido. 

const Modal = (( children, isOpen, onClose, title }; ModalProps) => {
 return
<Transition appear show={isopen} as={Fragment}> 
   <Dialog as="div" className="" onClose={onClose}>
     <Transition.Child
       as={Fragment}
       enter="ease-out duration-300"
       enterFrom="opacity-0"
       enterTo="opacity-100"
       leave="ease-in duration-200"
       leaveFrom="opacity-100"
       leaveTo="opacity-0"
         >

// Código omitido.

Nós passamos um elemento para o Transition.Child, por exemplo, o as={Fragment}, que também é um fragmento e não possui função semântica em nosso código. No entanto, ele possui algumas outras propriedades, como enter e enterFrom, usadas para controlar a animação.

Portanto, o enter="ease-out duration-300" se refere ao valor de 300 milissegundos, que é o tempo de duração em que ele vai começar. Ele começará em uma opacidade zero, ou seja, não estará visível na tela, e atingirá opacidade 100, tornando-se totalmente visível.

Já a propriedade leave é para onde ele vai, ou seja, vai da opacidade 100 para a zero. Isso significa que a animação desaparecerá de nossa tela.

Construímos uma estrutura para o conteúdo da nossa modal, que inclui uma <div className. Novamente, usamos o Transition. Child para controlar a nossa animação e o Dialog.Panel, onde colocaremos nosso conteúdo.

No Dialog.title, estou designando ele como tipo "h3" e passando o {title}, que recebemos como propriedade. Também teremos um botão de fechar.

     <Dialog.Panel className=""> 
       <div className="">
         <Dialog.Title as="h3" className="">
           {title}
         </Dialog.Title>
         <button onClick={onClose)>
           <XMarkIcon className="" />
         </button>
        </div>
        {children}
       </Dialog.Panel> 
         </Transition. Child>
     </div>
    </div>
   </Dialog>
  </Transition>
  );
};

Por fim, temos o {children}, que é o conteúdo que receberemos e renderizaremos em nossa modal. Com isso, conseguimos criar toda a estrutura da nossa modal, mas ainda não temos os estilos do Tailwind, componente do Headless UI. Ele não vem com os estilos padrões. Precisamos adicionar estilos nesse nosso componente de modal, mas faremos isso na sequência.

Então, permaneça conosco e até lá!

Finalizando o Design System - Adicionando estilos básicos da Modal

Já criamos a estrutura do nosso componente de Modal, porém não adicionamos nenhum estilo, pois a biblioteca Headless UI não oferece componentes estilizados, eles são completamente sem estilo para podermos personalizá-los como quisermos.

Eles são acessíveis, e é por isso que estamos utilizando essa biblioteca no nosso componente de modal.

No Visual Studio Code, precisamos fazer algumas correções no nosso código Modal.tsx.

O primeiro passo é importar os tipos do componente Dialog da Headless UI. Então, na linha 1, onde estamos importando o Dialog e o Transition, vamos adicionar vírgula e escrever DialogProps para importar esses tipos também.

import { Dialog, Transition, DialogProps } from "@headlessui/react"; 
import { XMarkIcon } from "@heroicons/react/24/outline"; 
import React, { Fragment } from "react";

// Código omitido. 

Então, para utilizar esses tipos, antes de React.HTMLAttributes, vamos adicionar um espaço e colocar DialogProps<>. Dentro dos sinais de "menor que" e "maior que", vamos passar any, ou seja, pode ser qualquer tipo. Também adicionaremos um & para incluir todos os tipos.

export type ModalProps = {
  children: string;
  isopen: boolean;
  title: string;
  onClose: () => void;
} & DialogProps<any> & React.HTMLAttributes <HTMLDivElement>;

const Modal = ({ children, isOpen, onClose, title }: ModalProps) => {
  return ( 
      <Transition appear show={isopen} as={Fragment}>
      <Dialog as="div" className="" onClose=(onClose}>
         <Transition.Child
           as={Fragment}
           enter "ease-out duration-300"
           enterFrom="opacity-0"
           enterTo="opacity-100"
           leave="ease-in duration-200"
           leaveFrom="opacity-100"
           leaveTo="opacity-8"

// Código omitido. 

Portanto, agora temos os tipos do Modal, que são os tipos das propriedades que estamos recebendo. Uma delas é o children que, por sinal, não é uma string, mas, sim um React.ReactNode. Definindo esse tipo, poderemos passar algum elemento HTML como children para o componente de Modal.

export type ModalProps = {
  children: React.ReactNode;
  isopen: boolean;
  title: string;
  onClose: () => void;
} & DialogProps<any> & React.HTMLAttributes <HTMLDivElement>;

const Modal = ({ children, isOpen, onClose, title }: ModalProps) => {
  return ( 
      <Transition appear show={isopen} as={Fragment}>
      <Dialog as="div" className="" onClose=(onClose}>
         <Transition.Child
           as={Fragment}
           enter "ease-out duration-300"
           enterFrom="opacity-0"
           enterTo="opacity-100"
           leave="ease-in duration-200"
           leaveFrom="opacity-100"
           leaveTo="opacity-8"

// Código omitido. 

Se bem que o componente Modal não é fixo, não é um componente que já vem com os elementos que precisa renderizar. Receberemos esses elementos via Props, precisamente através do children.

Então, o tipo da Modal tem os tipos recebidos via Props: children; isOpen; onClose; title. E também tem os tipos do DialogProps e o React.HTMLAttributes, que estão mais relacionados ao JSX.

Neste vídeo, vamos adicionar os estilos que contém o índice CSS em nosso componente Modal. Então, na linha onde está o Dialog as="div", temos um className vazio. Vamos adicionar alguns estilos!

// Código omitido. 

const Modal = ({ children, isOpen, onClose, title }: ModalProps) => {
  return ( 
      <Transition appear show={isopen} as={Fragment}>
      <Dialog as="div" className="" onClose=(onClose}>
         <Transition.Child
           as={Fragment}
           enter "ease-out duration-300"
           enterFrom="opacity-0"
           enterTo="opacity-100"
           leave="ease-in duration-200"
           leaveFrom="opacity-100"
           leaveTo="opacity-8"

// Código omitido. 

Vamos adicionar o estilo "relative" para definirmos a div como relativa. Usamos muito relative e position:absolute entre dois elementos, nos quais queremos controlar qual será posicionado em relação ao outro. No Tailwind CSS, usamos a classe utilitária relative.

Então, escreveremos relative e passaremos Z-10. O "z" se refere a "z-index". A própria extensão que estamos utilizando, que é o Tailwind CSS Lens, já mostra o significado dessa classe utilitária do Tailwind. No caso, ela está aplicando o "z-index: 10". Logo, essa div estará bem acima dos outros elementos da nossa aplicação.

// Código omitido. 

const Modal = ({ children, isOpen, onClose, title }: ModalProps) => {
  return ( 
      <Transition appear show={isopen} as={Fragment}>
      <Dialog as="div" className="relative z-10" onClose=(onClose}>
         <Transition.Child
           as={Fragment}
           enter "ease-out duration-300"
           enterFrom="opacity-0"
           enterTo="opacity-100"
           leave="ease-in duration-200"
           leaveFrom="opacity-100"
           leaveTo="opacity-8"

// Código omitido. 

Na linha 25, temos outro className. Ele vai receber uma classe maior, que será do tipo "fixed inset-0, o background será bg-black, que é a cor que definimos. Também aplicaremos uma opacidade, bg-opacity-25.

// Código omitido. 

  <div className="fixed inset-0 bg-black bg-opacity-25" /> </Transition.Child>

// Código omitido. 

Esse background é o que está atrás do nosso Modal. É como se fosse um overlay (sobreposição). Logo, ele terá uma opacidade um pouco escura, de modo a ocultar o que está abaixo. Isso é apenas estilo, não se preocupe.

Na linha 28, temos uma div e precisamos indicar as classes. Também será uma classe fixed, com inset-0, overflow-hidden e overflow-y-auto. O overflow-y-auto serve para ajustarmos o conteúdo dentro dessa div.

// Código omitido. 

  <div className="fixed inset-0 bg-black bg-opacity-25" /> </Transition.Child>

<div className="fixed inset-0 overflow-y-auto"> 
  <div className=""> 
      <Transition.Child

// Código omitido. 

Na div logo abaixo, vamos simplesmente copiar, pois são muitas classes, e explicar o que significa cada uma delas.

// Código omitido. 

  <div className="fixed inset-0 bg-black bg-opacity-25" /> </Transition.Child>

<div className="flex min-h-full items-center justify-center p-4"> 
  <div className=""> 
      <Transition.Child

// Código omitido. 

Começamos determinando o tamanho total da tela com flex min-h-full. Posicionamos os itens no centro com items-center. E alinhamos com justify-center. Isso é Flexbox, mas no CSS, são essas classes que representam as propriedades. Por fim, com p-4, estamos dando um padding de 20px.

Vamos seguir estilizando outra classe, que está na linha 39: <Dialog.Panel className="". Também temos várias classes, por isso vamos copiá-las e colá-las.

// Código omitido. 

<Dialog.Panel className="flex flex-col gap-5 w-full max-w-md transform overflow-hidden rounded-lg bg-white p-7 transition-all">

// Código omitido. 

Estamos utilizando o Flexbox com flex flex-col, indicando que o alinhamento é em coluna, e não em linha. Adicionamos um gap-5 para o espaçamento.

Na linha 41, adicionarei as classes em outra div className que, na verdade, são destinadas ao posicionamento: "flex item-center justify-between".

// Código omitido. 

<Dialog.Panel className="flex flex-col gap-5 w-full max-w-md transform overflow-hidden rounded-lg bg-white p-7 transition-all">
  <div className="flex item-center justify-between"

// Código omitido. 

E na linha 42, do Dialog.Title, também adicionaremos algumas classes. Elas são mais simples, pois se tratam apenas do tamanho do texto e da fonte.

// Código omitido. 

<Dialog.Panel className="flex flex-col gap-5 w-full max-w-md transform overflow-hidden rounded-lg bg-white p-7 transition-all">
  <div className="flex item-center justify-between"
     <Dialog.Title as="h3" className="text-lg font-semibold">

// Código omitido. 

No ícone, também adicionaremos uma classe, apenas para definir o tamanho dele: "w-5 h-5". Assim, o ícone terá um tamanho de 4px, seja para altura ou largura.

// Código omitido. 

<Dialog.Panel className="flex flex-col gap-5 w-full max-w-md transform overflow-hidden rounded-lg bg-white p-7 transition-all">
  <div className="flex item-center justify-between"
     <Dialog.Title as="h3" className="text-lg font-semibold">
       {title}
     </Dialog.Title>
     <button onClick={onClose}>
       <XMarkIcon className="w-5 h-5" />
     </button>

// Código omitido. 

Além disso, determinaremos a cor do texto dele, escrevendo text-disabled. Essa cor foi definida nos nossos ajustes do tailwind-config.

// Código omitido. 

<Dialog.Panel className="flex flex-col gap-5 w-full max-w-md transform overflow-hidden rounded-lg bg-white p-7 transition-all">
  <div className="flex item-center justify-between"
     <Dialog.Title as="h3" className="text-lg font-semibold">
       {title}
     </Dialog.Title>
     <button onClick={onClose}>
       <XMarkIcon className="w-5 h-5 text-disabled" />
     </button>

// Código omitido. 

Completamos todas as classes que precisávamos adicionar. Agora só precisamos salvar esses dados, compilá-los e visualizá-los no Storybook. Mas, antes, precisamos escrever uma história, que contarei no próximo vídeo. Então, não saia daí! Até mais!

Sobre o curso React: eleve o nível da sua documentação no Storybook

O curso React: eleve o nível da sua documentação no Storybook possui 119 minutos de vídeos, em um total de 46 atividades. Gostou? Conheça nossos outros cursos de React em Front-end, ou leia nossos artigos de Front-end.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda React acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas