Lessons from DS for a dev - cover.png
Olga Pinheiro

Olga Pinheiro

25 Jul 2022 6 min read

Duas lições aprendidas com nosso Design System pela visão de uma dev

Aqui na Labcodes, estamos descobrindo as dores e as delícias de desenvolver nosso próprio Design System. Em um post anterior, compartilhamos lições aprendidas com foco no ponto de vista da equipe de design. Lá você também encontra um resumo do que é um Design System e de como escolhemos as ferramentas que utilizamos (a saber, Figma e Storybook com ReactJS). No post de hoje, vamos compartilhar alguns aprendizados com foco no ponto de vista da equipe de desenvolvimento.

Fiquei responsável pela implementação em ReactJS de um belíssimo input de texto. Por questões de prioridade, esse foi um dos primeiros componentes que construímos. Acontece que ele também é um dos mais complexos, por ser altamente personalizável, aceitar uma quantidade enorme de opções (jajá eu mostro a singela listinha de props) e porque decidimos lidar com a validação do input.

Animação mostrando o comportamento padrão do componente de entrada de texto Nosso neném, o componente de TextInput, em sua forma padrão, com uma mensagem de ajuda personalizável

Começar por um componente relativamente complexo resultou em uma PR aberta por vários meses, um número absurdo de commits e uns 10 momentos de “acho que terminei” seguidos por “ah, não! mais um bug aqui!”. Por outro lado, foi fonte de muitos aprendizados, tanto a nível de código quanto de processo. Mais do que isso: ajudou a fixar conhecimentos importantes, que antes não pareciam tão relevantes. O que mais me marcou foi o quanto tudo ficou mais robusto quando passamos a levar o playground e os testes em JS mais a sério.

Imagem de uma tabela de 15 linhas com as propriedades dos componentes, seus tipos e valores padrãoIsso é só pra vocês terem noção da quantidade de opções de personalização que esse componente oferece.

Playground não é só pro usuário

Existe uma grande diferença entre desenvolver componentes ReactJS para uma aplicação web e desenvolvê-los para um Design System. No primeiro caso, os componentes são utilizados em contextos específicos e conhecidos: o usuário é a pessoa que acessa a aplicação. Já no caso de um Design System, o contexto em que o componente será aplicado é desconhecido e o usuário é a pessoa desenvolvedora que utiliza o componente em seu código.

Como nossos usuários são pessoas desenvolvedoras, é bem importante oferecer uma boa documentação que possa ser acessada por eles, não somente no código. Então buscamos uma ferramenta que auxiliasse nesse processo e decidimos usar o Storybook. O Storybook, segundo seu próprio website, é um ambiente para desenvolvimento, documentação e testes visuais de componentes UI.

No começo do nosso processo de desenvolvimento, a documentação de cada componente apresentava alguns exemplos interativos de caso de uso, demonstrando como seria a interação do usuário final com o componente em situações bem definidas, e com orientações sobre seu funcionamento e aplicações.

Imagem de diferentes opções para entrada de texto: desabilitada e não desabilitada com sufixo Imagem de diferentes opções para a entrada de texto: com sufixo e erro de validação, sem sufixo e necessário com mensagem de ajuda, obrigatório com erro de validaçãoNo início, a documentação somente apresentava exemplos como esses, em que é possível interagir com o elemento mas não é possível alterar as props que ele recebe

Tudo parecia ir bem. Pensei que o componente estava pronto e ia começar a fazer os testes em JS. Até que me perguntei: e se eu quiser que o Input tenha um “suffix” e seja “required” ao mesmo tempo, será que vai funcionar bem? Testei provisoriamente num lugar qualquer do código e…

imagem de entrada de texto com sufixo ".com" e obrigatório (com uma pequena estrela no canto superior direito). Parte da estrela está escondida pelo sufixo de fundo azul claroOPA! Ao usar “suffix” e “required” simultaneamente, o símbolo de “required” ficava escondido pelo background do “suffix”, o que não é o comportamento desejável.

Nesse momento, percebi que as funcionalidades do Storybook que estávamos usando não eram suficientes para o nosso caso. Tornou-se evidente pra mim que um espaço de demo que permitisse a edição dos valores das props — simulando a utilização do componente por uma pessoa desenvolvedora — seria importante não somente para o usuário entender e explorar todas as opções de um componente, mas também como ferramenta de validação visual durante o desenvolvimento do próprio Design System.

O Storybook oferece uma ferramenta para isso, mas sua utilização não é tão intuitiva e também não era fácil de integrar à nossa identidade visual. Foi então que adicionei uma seção de “playground” customizada à documentação do componente, o que é comum em outras documentações de Design Systems, como por exemplo no Carbon.

Rapidamente, o que parecia pronto se mostrou ainda cheio de bugs:

Identificando e resolvendo os defeitos, foi possível entregar um componente mais robusto em termos visuais. Se o playground tivesse sido implementado desde o início, provavelmente haveria menos surpresas e o desenvolvimento teria sido mais rápido, já que ele trouxe cenários mais próximos ao uso real do componente. Mas antes tarde do que nunca.

Feito isso, faltava criar testes automáticos que garantissem o correto comportamento do componente em um cenário real.

Deveria ser óbvio, mas testes em JS vão muito além de Snapshots

Novamente, é importante lembrar que nosso contexto de desenvolvimento e de testes é bem particular de um Design System. Como estamos desenvolvendo uma biblioteca de componentes isolados, podemos testar o componente sem nos preocupar em que partes das aplicações ele será usado. Por exemplo, não importa para o componente de input se ele está sendo usado pegando valores do Redux ou do estado do componente pai. A única coisa que precisamos garantir é que os componentes recebam e forneçam as informações necessárias para que uma pessoa desenvolvedora consiga implementar os fluxos que deseja em seu contexto específico.

No caso do TextInput, minhas maiores preocupações foram sobre como o componente se comportaria caso a pessoa desenvolvedora passasse ou não o value por props, assim como a utilização correta do defaultValue e a validação do conteúdo inserido pelo usuário final. Como nossos componentes foram escritos usando classes, esse tipo de testes pode ser feito facilmente usando Jest e Enzyme — Jest é um framework de testes em JS, enquanto Enzyme oferece ferramentas de teste para React. Esses testes também ajudam a garantir que erros introduzidos por alterações futuras no componente sejam percebidos.

Acima de tudo, por ser uma biblioteca, entendemos que é importante cobrir uma parte significativa do código com testes unitários. Isso nos torna mais confiantes de que o código funciona, garante maior estabilidade do produto e nos ajuda a tomar decisões que levem ao desenvolvimento de um código de qualidade.

Um cenário interessante para se testar, por exemplo, é o caso de o input estar desabilitado:

it("renders as expected when passing disabled as true", async () => {
  const renderedComponent = mount(
    <AbstractTextInput
      id="testInput"
      label="Test Input"
      defaultValue="default"
      disabled
    />
  );
  expect(renderedComponent.find("input[disabled]")).toHaveLength(1);
});

Esse tipo de teste foi importante para nós por dois motivos. Primeiro, ele garante que o componente consegue se renderizar corretamente em um caso comum de uso final. Segundo, não estamos testando o componente “por dentro”; ou seja, não estamos validando que chaves internas do estado estão se comportando como devem. Pelo contrário, testamos o componente “por fora”, garantindo que futuras refatorações do componente não quebrem desnecessariamente os testes.

Mas isso não é tudo…

Esses foram meus dois maiores aprendizados ao implementar um componente complexo de um design system — mas não foram os únicos. Também foi ótimo descobrir mais sobre como empacotar os componentes, ou como usar o Storybook da maneira mais efetiva, e até como documentar decisões que não necessariamente são de desenvolvimento, aperfeiçoando o processo.

No entanto, tenho que deixar esses assuntos para um futuro post, de repente mais focado em um desses tópicos. E você, o que achou dessas nossas dicas? Quer saber mais sobre alguma parte específica do nosso processo? Deixa pra gente um comentário que a gente vai conversando. Obrigado pela atenção e nos vemos em breve!

Se quiser ler mais sobre assuntos relacionados, essas foram algumas das minhas referências: