Catégories
Astuces et Design

Comment créer un composant de liste avec Emotion

J'ai fait un peu de refactoring cette semaine chez Sentry et j'ai remarqué que nous n'avions pas de composant de liste générique que nous pouvions utiliser entre les projets et les fonctionnalités. Donc, j'en ai commencé une, mais voici le hic: nous stylisons les choses chez Sentry en utilisant Emotion, avec laquelle je n'ai qu'une expérience de passage et qui est décrit dans les documents comme…

(…) Une bibliothèque conçue pour écrire des styles CSS avec JavaScript. Il offre une composition de style puissante et prévisible en plus d'une excellente expérience de développeur avec des fonctionnalités telles que les cartes sources, les étiquettes et les utilitaires de test. Les styles de chaîne et d'objet sont pris en charge.

Si vous n'avez jamais entendu parler d'Emotion, l'idée générale est la suivante: lorsque nous travaillons sur de grandes bases de code avec beaucoup de composants, nous voulons nous assurer que nous pouvons contrôler la cascade de notre CSS. Disons que vous avez un .active dans un fichier et vous voulez vous assurer que cela n'affecte pas les styles d'un composant complètement séparé dans un autre fichier qui a également une classe de.active.

Emotion résout ce problème en ajoutant des chaînes personnalisées à vos noms de classe afin qu'elles n'entrent pas en conflit avec d'autres composants. Voici un exemple du code HTML qu'il pourrait générer:

Assez bien, hein? Il existe de nombreux autres outils et workflows qui font quelque chose de très similaire, tels que les modules CSS.

Pour commencer à créer le composant, nous devons d'abord installer Emotion dans notre projet. Je ne vais pas vous expliquer ce genre de choses, car ce sera différent selon votre environnement et votre configuration. Mais une fois cela terminé, nous pouvons continuer et créer un nouveau composant comme celui-ci:

import React from 'react';
import styled from '@emotion/styled';

export const List = styled('ul')`
  list-style: none;
  padding: 0;
`;

Cela me semble assez bizarre parce que non seulement nous écrivons des styles pour

    élément, mais nous définissons que le composant doit rendre un

      , aussi. La combinaison du balisage et des styles en un seul endroit semble étrange, mais j'aime sa simplicité. Cela dérange en quelque sorte mon modèle mental et la séparation des préoccupations entre HTML, CSS et JavaScript.

      Dans un autre composant, nous pouvons importer ce et utilisez-le comme ceci:

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

      Les styles que nous avons ajoutés à notre composant de liste seront ensuite transformés en nom de classe, comme .oefioaueg, puis ajouté au

        élément que nous avons défini dans le composant.

        Mais nous n'avons pas encore fini! Avec la conception de la liste, j'avais besoin de pouvoir rendre un

          Et un

            avec le même composant. J'avais également besoin d'une version qui me permette de placer une icône dans chaque élément de la liste. Juste comme ça:

            Le cool (et aussi le genre de bizarre) chose à propos de l'émotion est que nous pouvons utiliser le as attribut pour sélectionner quel élément HTML nous aimerions rendre lorsque nous importons notre composant. Nous pouvons utiliser cet attribut pour créer notre

              variante sans avoir à faire une coutume type propriété ou quelque chose. Et cela ressemble à ceci:

              This will render a ul.
              This will render an ol.

              Ce n'est pas juste bizarre pour moi, non? C'est super bien, cependant, car cela signifie que nous n'avons pas à faire de logique bizarro dans le composant lui-même juste pour changer le balisage.

              C'est à ce stade que j'ai commencé à noter à quoi pourrait ressembler l'API parfaite pour ce composant, car nous pourrons ensuite revenir en arrière. Voici ce que j'imaginais:

              
                Item 1
                Item 2
                Item 3
              
              
              
                }>Item 1
                }>Item 2
                }>Item 3
              
              
              
                Item 1
                Item 2
                Item 3
              

              Donc, après avoir fait ce croquis, je savais que nous aurions besoin de deux composants, ainsi que de la possibilité d'imbriquer des sous-composants d'icônes dans le . On peut commencer comme ça:

              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;
                }
              `;

              Cette particularité ol& la syntaxe est la façon dont nous disons à l'émotion que ces styles s'appliquent uniquement à un élément lorsqu'il est rendu en tant que

                . C’est souvent une bonne idée d’ajouter background: red; à cet élément pour vous assurer que votre composant rend les choses correctement.

                Ensuite, notre sous-composant, le . Il est important de noter que chez Sentry, nous utilisons également TypeScript, donc avant de définir notre composant, nous devons d'abord configurer nos accessoires:

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

                Maintenant, nous pouvons ajouter notre composant qui dimensionnera un composant dans le ListItem. Si vous vous souvenez de l'exemple ci-dessus, je voulais qu'il ressemble à ceci:

                
                  }>Item 1
                  }>Item 2
                  }>Item 3
                

                Cette IconBusiness est un composant préexistant et nous voulons l'envelopper dans un intervalle afin de pouvoir le styliser. Heureusement, nous aurons besoin d'un tout petit peu de CSS pour aligner correctement l'icône avec le texte et le peut gérer tout cela pour nous:

                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;
                `;

                Une fois que nous avons fait cela, nous pouvons enfin ajouter notre composante sous ces deux, bien qu'il soit considérablement plus complexe. Nous devons ajouter les accessoires, puis nous pouvons rendre le ci-dessus lorsque le icon prop existe et rend également le composant icône qui lui est passé. J'ai également ajouté tous les styles ci-dessous afin que vous puissiez voir comment je stylise chacune de ces variantes:

                export const ListItem = styled(({icon, className, children}: ListItemProps) => (
                  
              1. {icon && ( {icon} )} {children}
              2. ))` 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 & { &: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; } } `;

                Et voila! Un relativement simple composant construit avec Emotion. Bien que, après avoir effectué cet exercice, je ne suis toujours pas sûr d’aimer la syntaxe. Je pense que ça rend les choses simples vraiment des composants simples mais de taille moyenne beaucoup plus compliqué qu'ils ne devraient l'être. De plus, cela pourrait être sacrément déroutant pour un nouveau venu et cela m'inquiète un peu.

                Mais tout est une expérience d'apprentissage, je suppose. Quoi qu'il en soit, je suis heureux d'avoir eu l'occasion de travailler sur ce minuscule composant, car il m'a appris quelques bonnes choses sur TypeScript, React et essayer de rendre nos styles quelque peu lisibles.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *