Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React-email e componentização de emails #1606

Merged
merged 1 commit into from
Feb 10, 2024

Conversation

ErickCReis
Copy link
Contributor

Mudanças realizadas

Reimplementação do PR #1082

Esse PR adiciona a possibilidade utilização de HTML para a criação do templates de email.

*Ainda preciso corrigir os testes


React-email

A base dessa feature é o react-email.

Além de facilitar a criação dos templates utilizando a estrutura de componentes, ele também lida com as complexidades de layout necessárias para garantir a consistência entre leitores de email.

Outra vantagem do react-email é seu servidor de desenvolvimento, nele é possível ver instantaneamente as alterações no componentes.


Detalhes da implementação

Para facilitar o desenvolvimento e isolar todas as dependência criei um novo módulo, o @tabnews/mail, que fica responsável por tudo isso, a principio essa estrutura era só um teste, mas acho que funcionou bem.

Também fiz uma adição ao docker-compose para subir automaticamente o servidor do react-email, mas não sei se é algo necessário.


Resultado

Gravacao.de.Tela.2024-01-20.as.20.45.54.mov

Criação de novos templates

  1. Crie um novo componente em mail/email/
// mail/email/new-component.js

import { Text } from '@react-email/text';
import { DefaultLayout } from 'mail/components/default-layout';

export const NewComponentEmail = ({ username }) => (
  <DefaultLayout username={username} previewText="Novo template de email">
    <Text style={text}>Este é um novo componente de email</Text>
  </DefaultLayout>
);

NewComponentEmail.PreviewProps = {
  username: 'User',
};

export default NewComponentEmail;

const text = {
  color: '#333',
  fontSize: '14px',
  margin: '24px 0',
};
  1. Exporte o componente no arquivo mail/index.js
// mail/email/index.js

export { NewComponentEmail } from './emails/new-component';
  1. Utilize o template
import { ActivationEmail } from '@tabnews/mail';
import email from 'infra/email.js';

function sendNewEmail() {
  await email.send({
    from: {
      name: 'TabNews',
      address: '[email protected]',
    },
    to: '[email protected]',
    subject: 'Novo template de email',
    component: NewComponentEmail({
      username: 'username',
    }),
  });
}

Tipo de mudança

  • Nova funcionalidade

Checklist:

  • As modificações não geram novos logs de erro ou aviso (warning).
  • Eu adicionei testes que provam que a correção ou novo recurso funciona conforme esperado.
  • Tanto os novos testes quanto os antigos estão passando localmente.

Copy link

vercel bot commented Jan 21, 2024

@ErickCReis is attempting to deploy a commit to the TabNews Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Collaborator

@Rafatcb Rafatcb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parece uma adição legal, o react-email traz algumas facilidades se comparado ao desenvolvimento manual, além de estar "levemente" relacionado ao Resend (#1451).

Vi um teste que quebrou, aparentemente foi porque o Heading transforma tudo para caixa alta no modo texto. Não sei se tem uma opção para ele não fazer isso, assim não precisaria modificar os testes.

infra/docker-compose.development.yml Outdated Show resolved Hide resolved
mail/package.json Outdated Show resolved Hide resolved
mail/components/default-layout.js Outdated Show resolved Hide resolved
mail/emails/activation.js Outdated Show resolved Hide resolved
package-lock.json Outdated Show resolved Hide resolved
mail/package.json Outdated Show resolved Hide resolved
mail/emails/confirmation.js Outdated Show resolved Hide resolved
@ErickCReis
Copy link
Contributor Author

@Rafatcb obrigado pelos comentários, ao longo da semana devo continuar mexendo aqui e fazendo os ajustes.

Vi um teste que quebrou, aparentemente foi porque o Heading transforma tudo para caixa alta no modo texto. Não sei se tem uma opção para ele não fazer isso, assim não precisaria modificar os testes.

Sim, um dos erros é por causa disso, vou procurar alguma forma de resolver, mas acho que não seria um problema deixar assim e mudar os testes.

Além desse erro alguns testes também falham por conta da sanitização do texto do email, o react-mail faz essa isso internamente, ainda não pensei em como trazer essa sanitização para os testes.

Copy link
Collaborator

@aprendendofelipe aprendendofelipe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Que massa @ErickCReis! 💪💪💪

Ainda não olhei os templates, mas já tenho algumas considerações para pensarmos.

Não estou convencido sobre a necessidade de se criar um módulo separado. Ainda não consegui ver alguma vantagem, pelo menos não na forma como está no PR. Mas também não tenho nada contra. 👍

Se for o caso de deixar separado, então é interessante que ele foque em algo específico, como nos layouts das mensagens, mas sem a logo, endereço ou nada específico do TabNews. Tudo que for específico pode ser recebido via propriedades. Faz sentido?

Sobre os templates, se vai ser criado um arquivo separado para cada modelo de mensagem de email, será que não faz sentido a versão em texto simples ser gerada por uma função presente no mesmo arquivo, através dos mesmos parâmetros? Acho melhor do que converter para html e depois converter de volta para texto. E os testes devem verificar as duas versões (html e text), correto?

Deixei mais comentários e dúvidas no código 🤝

mail/package.json Outdated Show resolved Hide resolved
infra/email.js Outdated Show resolved Hide resolved
import { Preview } from '@react-email/preview';
import { Text } from '@react-email/text';

import webserver from '../../infra/webserver';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Esse import webserver from '../../infra/webserver' contradiz a ideia de isolar as dependências.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sim, adicionei esse trecho apenas para conseguir a url da logo, vou pensar uma solução melhor.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sim, adicionei esse trecho apenas para conseguir a url da logo, vou pensar uma solução melhor.

Se deixar o módulo responsável apenas pelo design, isso será passado junto com os parâmetros. Se for um módulo separado, acredito que seja a melhor forma.

mail/index.js Outdated Show resolved Hide resolved
@ErickCReis
Copy link
Contributor Author

@aprendendofelipe

Não estou convencido sobre a necessidade de se criar um módulo separado. Ainda não consegui ver alguma vantagem, pelo menos não na forma como está no PR. Mas também não tenho nada contra. 👍

A princípio foi apenas um teste, mas no final me pareceu interessante separar um pouco as dependências e os scripts e um contexto próprio.

Se for o caso de deixar separado, então é interessante que ele foque em algo específico, como nos layouts das mensagens, mas sem a logo, endereço ou nada específico do TabNews. Tudo que for específico pode ser recebido via propriedades. Faz sentido?

Não tenho certeza, por mais que seja um módulo independente ainda acho que está diretamente vinculado com o TabNews, me parece fazer sentido a logo, por exemplo, estar dentro desse módulo.

Sobre os templates, se vai ser criado um arquivo separado para cada modelo de mensagem de email, será que não faz sentido a versão em texto simples ser gerada por uma função presente no mesmo arquivo, através dos mesmos parâmetros? Acho melhor do que converter para html e depois converter de volta para texto.

Na pratica acontece isso mesmo, o template é usado para gerar os dois modos, o que muda é uma opção que a gente passa para a função de render do react-email

const html = render(template);
const text = render(template, { plainText: true });

E os testes devem verificar as duas versões (html e text), correto?

Sim, só não pensei em como fazer isso ainda.

@aprendendofelipe
Copy link
Collaborator

Se for o caso de deixar separado, então é interessante que ele foque em algo específico, como nos layouts das mensagens, mas sem a logo, endereço ou nada específico do TabNews. Tudo que for específico pode ser recebido via propriedades. Faz sentido?

Não tenho certeza, por mais que seja um módulo independente ainda acho que está diretamente vinculado com o TabNews, me parece fazer sentido a logo, por exemplo, estar dentro desse módulo.

O módulo pode até ser disponibilizado no NPM se não contiver nenhum dado específico e só definir o layout. Eu acredito que um dia a TabNewsUI também vai virar um módulo disponível no NPM, mas primeiro precisamos remover um monte de coisa específica que está fixa nos componentes. Isso pode atrair mais colaboração e também facilita compartilhar código com o curso.dev

Sobre os templates, se vai ser criado um arquivo separado para cada modelo de mensagem de email, será que não faz sentido a versão em texto simples ser gerada por uma função presente no mesmo arquivo, através dos mesmos parâmetros? Acho melhor do que converter para html e depois converter de volta para texto.

Na pratica acontece isso mesmo, o template é usado para gerar os dois modos, o que muda é uma opção que a gente passa para a função de render do react-email

const html = render(template);
const text = render(template, { plainText: true });

Sim, mas no PR o render está recebendo os componentes React e convertendo em texto. Não vejo necessidade disso já que podemos ter maior controle sobre a versão em texto, que não envolve nenhuma dificuldade para ser criada. Na verdade esse código já está pronto e só seria movido para os arquivos dos templates.

O fato dos testes terem quebrado também é um indício de que é melhor deixar a versão em texto por nossa conta.

@ErickCReis
Copy link
Contributor Author

O módulo pode até ser disponibilizado no NPM se não contiver nenhum dado específico e só definir o layout. Eu acredito que um dia a TabNewsUI também vai virar um módulo disponível no NPM, mas primeiro precisamos remover um monte de coisa específica que está fixa nos componentes. Isso pode atrair mais colaboração e também facilita compartilhar código com o curso.dev

Não imaginei esse cenário, mas com esse objetivo faz sentido deixar o mais "agnóstico" possível.

Sim, mas no PR o render está recebendo os componentes React e convertendo em texto. Não vejo necessidade disso já que podemos ter maior controle sobre a versão em texto, que não envolve nenhuma dificuldade para ser criada. Na verdade esse código já está pronto e só seria movido para os arquivos dos templates.

O fato dos testes terem quebrado também é um indício de que é melhor deixar a versão em texto por nossa conta.

Achei que o react-email fazia algumas tratativas especiais para a conversão do texto que poderiam relevantes pra gente, mas é só uma conversão simples de html para texto mesmo, então concordo com a ideia de manter o comportamento anterior para o modo de texto.

@ErickCReis
Copy link
Contributor Author

Iniciei as modificações para deixar o módulo mais genérica, mas percebi que seria um trabalho bem maior, então optei por seguir com o modelo mais simples, na pasta models/transactional, assim a gente consegue focar no react-email nesse pr e depois posso abrir um outro pr para explorar o esquema de módulos, o que acham?

*Ainda não adicionei os novos testes, acho que só vou conseguir continuar aqui no fim de semana.

infra/docker-compose.development.yml Outdated Show resolved Hide resolved
package-lock.json Outdated Show resolved Hide resolved
@ErickCReis
Copy link
Contributor Author

ErickCReis commented Jan 27, 2024

@Rafatcb fiz os ajustes que vc sugeriu.


Estou com dúvidas em relação aos testes, faz sentido validar o html nos testes de integração, ou apenas os testes unitários da model já são suficientes.

Outra coisa, preciso adicionar alguma documentação sobre o funcionamento do react-email e essa nova estrutura de componentes? Estava pensando em adicionar um README em models/transactional.

@Rafatcb Rafatcb added back Envolve modificações no backend novo recurso Nova funcionalidade/recurso labels Jan 27, 2024
@Rafatcb
Copy link
Collaborator

Rafatcb commented Jan 27, 2024

Estou com dúvidas em relação aos testes, faz sentido validar o html nos testes de integração, ou apenas os testes unitários da model já são suficientes.

Não sei se é a melhor forma, mas você pode adicionar:

  const emailHtmlResponse = await fetch(`${emailServiceUrl}/messages/${lastEmailItem.id}.html`);
  lastEmailItem.html = await emailHtmlResponse.text();

em getLastEmail, e então no teste pode verificar de forma similar ao .text:

expect(confirmationEmail.html.includes('Uma alteração de email foi solicitada.')).toBe(true);

Assim conseguimos garantir que o e-mail em HTML está enviando os textos esperados, mesmo sem conseguir validar a estilização dele.

Não sei se testes de snapshot são o suficiente. Eu nunca usei, mas os relatos que ouvi geralmente eram do tipo "sempre que muda algo, precisa gerar um snapshot novo porque o teste quebrou, e no fim ninguém vê se algo quebrou mesmo".


Edit esqueci de responder a outra pergunta.

Outra coisa, preciso adicionar alguma documentação sobre o funcionamento do react-email e essa nova estrutura de componentes? Estava pensando em adicionar um README em models/transactional.

É bom documentar como criar um e-mail ou mudar a estilização de um existente, mas como é simples, pode fazer uma documentação enxuta. Acho que pode criar um tópico novo no CONTRIBUTING.md abaixo do Criar novas Migrations.

@ErickCReis ErickCReis marked this pull request as ready for review February 10, 2024 13:51
Copy link

vercel bot commented Feb 10, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
tabnews ✅ Ready (Inspect) Visit Preview 💬 Add feedback Feb 10, 2024 5:44pm

@aprendendofelipe aprendendofelipe merged commit 61f40e7 into filipedeschamps:main Feb 10, 2024
5 checks passed
@aprendendofelipe
Copy link
Collaborator

Código rodando em produção! 🚀🚀🚀

Muito obrigado por mais essa, @ErickCReis! 💪💪💪

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
back Envolve modificações no backend novo recurso Nova funcionalidade/recurso
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants