Cómo hacer un componente de lista con emoción

Estuve refactorizando un poco esta semana en Sentry y noté que no teníamos un componente de Lista genérico que pudiéramos usar en todos los proyectos y funciones. Entonces, comencé uno, pero aquí está el problema: diseñamos cosas en Sentry usando Emotion, con el cual solo tengo experiencia pasajera y se describe en los documentos como...

[…] una biblioteca diseñada para escribir estilos css con JavaScript. Proporciona una composición de estilo potente y predecible, además de una excelente experiencia para el desarrollador con funciones como mapas de origen, etiquetas y utilidades de prueba. Se admiten estilos de cadena y de objeto.

Si nunca has oído hablar de Emotion, la idea general es la siguiente: cuando trabajamos en grandes bases de código con muchos componentes, queremos asegurarnos de que podemos controlar la cascada de nuestro CSS. Entonces, digamos que tiene una .activeclase en un archivo y quiere asegurarse de que no afecte los estilos de un componente completamente separado en otro archivo que también tiene una clase .active.

Emotion aborda este problema agregando cadenas personalizadas a los nombres de sus clases para que no entren en conflicto con otros componentes. Aquí hay un ejemplo del HTML que podría generar:

div/div

Bastante bonito, ¿eh? Sin embargo, existen muchas otras herramientas y flujos de trabajo que hacen algo muy similar, como los módulos CSS.

Para comenzar a crear el componente, primero debemos instalar Emotion en nuestro proyecto. No voy a explicar esas cosas porque serán diferentes dependiendo de su entorno y configuración. Pero una vez que esté completo, podemos seguir adelante y crear un nuevo componente como este:

import React from 'react';import styled from '@emotion/styled';export const List = styled('ul')`  list-style: none;  padding: 0;`;

Esto me parece bastante extraño porque no solo estamos escribiendo estilos para el ulelemento, sino que también estamos definiendo que el componente debe representar un ul. Combinar tanto el marcado como los estilos en un solo lugar parece extraño, pero me gusta lo simple que es. Simplemente interfiere con mi modelo mental y la separación de preocupaciones entre HTML, CSS y JavaScript.

En otro componente, podemos importar esto Listy usarlo así:

import List from 'components/list';ListThis is a list item./List

Los estilos que agregamos a nuestro componente de lista se convertirán en un nombre de clase, como .oefioauegy luego se agregarán al ulelemento que definimos en el componente.

¡Pero aún no hemos terminado! Con el diseño de la lista, necesitaba poder renderizar a uly an olcon el mismo componente. También necesitaba una versión que me permitiera colocar un ícono dentro de cada elemento de la lista. Así como esto:

Lo bueno (y también un poco extraño) de Emotion es que podemos usar el asatributo para seleccionar qué elemento HTML nos gustaría representar cuando importemos nuestro componente. Podemos usar este atributo para crear nuestra olvariante sin tener que crear una typepropiedad personalizada o algo así. Y resulta que se parece a esto:

ListThis will render a ul./ListList as="ol"This will render an ol./List

Eso no sólo es extraño para mí, ¿verdad? Sin embargo, es muy interesante porque significa que no tenemos que aplicar ninguna lógica extraña en el componente en sí solo para cambiar el marcado.

Fue en este punto que comencé a anotar cómo podría ser la API perfecta para este componente porque entonces podemos trabajar desde allí. Esto es lo que me imaginaba:

List  ListItemItem 1/ListItem  ListItemItem 2/ListItem  ListItemItem 3/ListItem/ListList  ListItem icon={IconBusiness color="orange400" size="sm" /}Item 1/ListItem  ListItem icon={IconBusiness color="orange400" size="sm" /}Item 2/ListItem  ListItem icon={IconBusiness color="orange400" size="sm" /}Item 3/ListItem/ListList as="ol"  ListItemItem 1/ListItem  ListItemItem 2/ListItem  ListItemItem 3/ListItem/List

Entonces, después de hacer este boceto, supe que necesitaríamos dos componentes, junto con la capacidad de anidar subcomponentes de íconos dentro del archivo ListItem. Podemos empezar así:

import React from 'react';import styled from '@emotion/styled';export const List = styled('ul')`  list-style: none;  padding: 0;  margin-bottom: 20px;  ol {    counter-reset: numberedList;  }`;

Esa olsintaxis peculiar es la forma en que le decimos a la emoción que estos estilos solo se aplican a un elemento cuando se representa como un archivo ol. A menudo es una buena idea simplemente agregar un background: red;a este elemento para asegurarse de que su componente esté procesando las cosas correctamente.

El siguiente es nuestro subcomponente, el ListItem. Es importante tener en cuenta que en Sentry también usamos TypeScript, por lo que antes de definir nuestro ListItemcomponente, primero necesitaremos configurar nuestros accesorios:

type ListItemProps = {  icon?: React.ReactNode;  children?: string | React.ReactNode;  className?: string;};

Ahora podemos agregar nuestro IconWrappercomponente que dimensionará un Iconcomponente dentro del archivo ListItem. Si recuerdas el ejemplo anterior, quería que se viera así:

List  ListItem icon={IconBusiness color="orange400" size="sm" /}Item 1/ListItem  ListItem icon={IconBusiness color="orange400" size="sm" /}Item 2/ListItem  ListItem icon={IconBusiness color="orange400" size="sm" /}Item 3/ListItem/List

Ese IconBusinesscomponente es un componente preexistente y queremos envolverlo en un lapso para poder diseñarlo. Afortunadamente, necesitaremos solo un poquito de CSS para alinear el ícono correctamente con el texto y podremos IconWrappermanejar todo eso por nosotros:

type ListItemProps = {  icon?: React.ReactNode;  children?: string | React.ReactNode;  className?: string;};const IconWrapper = styled('span')`  display: flex;  margin-right: 15px;  height: 16px;  align-items: center;`;

Una vez que hayamos hecho esto finalmente podemos agregar nuestro ListItemcomponente debajo de estos dos, aunque es considerablemente más complejo. Necesitaremos agregar los accesorios, luego podremos renderizar lo IconWrapperanterior cuando el iconaccesorio exista y también renderizar el componente de icono que se le pasó. También agregué todos los estilos a continuación para que puedas ver cómo le doy estilo a cada una de estas variantes:

export const ListItem = styled(({icon, className, children}: ListItemProps) = (  li className={className}    {icon  (      IconWrapper        {icon}      /IconWrapper    )}    {children}  /li))ListItemProps`  display: flex;  align-items: center;  position: relative;  padding-left: 34px;  margin-bottom: 20px;  /* Tiny circle and icon positioning */  :before,  ${IconWrapper} {    position: absolute;    left: 0;  }  ul  {    color: #aaa;    /* This pseudo is the tiny circle for ul items */     :before {      content: '';      width: 6px;      height: 6px;      border-radius: 50%;      margin-right: 15px;      border: 1px solid #aaa;      background-color: transparent;      left: 5px;      top: 10px;    }    /* Icon styles */    ${p =      p.icon       `      span {        top: 4px;      }      /* Removes tiny circle pseudo if icon is present */      :before {        content: none;      }    `}  }  /* When the list is rendered as an ol */  ol  {    :before {      counter-increment: numberedList;      content: counter(numberedList);      top: 3px;      display: flex;      align-items: center;      justify-content: center;      text-align: center;      width: 18px;      height: 18px;      font-size: 10px;      font-weight: 600;      border: 1px solid #aaa;      border-radius: 50%;      background-color: transparent;      margin-right: 20px;    }  }`;

¡Y ahí lo tienes! Un componente relativamente simple Listconstruido con Emotion. Aunque, después de realizar este ejercicio, todavía no estoy seguro de que me guste la sintaxis. Creo que hace que las cosas simples sean realmente simples, pero los componentes de tamaño mediano son mucho más complicados de lo que deberían ser. Además, podría resultar bastante confuso para un recién llegado y eso me preocupa un poco.

Pero supongo que todo es una experiencia de aprendizaje. De cualquier manera, me alegro de haber tenido la oportunidad de trabajar en este pequeño componente porque me enseñó algunas cosas buenas sobre TypeScript, React y cómo tratar de hacer que nuestros estilos sean algo legibles.

SUSCRÍBETE A NUESTRO BOLETÍN 
No te pierdas de nuestro contenido ni de ninguna de nuestras guías para que puedas avanzar en los juegos que más te gustan.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Subir

Este sitio web utiliza cookies para mejorar tu experiencia mientras navegas por él. Este sitio web utiliza cookies para mejorar tu experiencia de usuario. Al continuar navegando, aceptas su uso. Mas informacion