Construyendo una biblioteca de componentes con React y Emotion

 

 

 

  • ¡Registro!
  • Diseño de arquitectura de componentes de interfaz de usuario y tokens, con Nathan Curtis

  • Índice
    1. Empezando #
  • Botones #
  • Componente de la caja #
  • Componente de columnas #
  • Componente de pila #
    1. Conclusión #
    2. Referencias #
  • Una biblioteca de componentes ayuda a mantener un diseño coherente en múltiples proyectos. Garantiza la coherencia porque cualquier cambio realizado se propagará a través de los proyectos que lo utilicen. En este tutorial, aprenderemos cómo construir una biblioteca de componentes usando Emotion en React para resolver inconsistencias.

     

    Según Clearleft, una biblioteca de componentes es:

    "Una colección de componentes, organizados de manera significativa y que a menudo (pero no necesariamente) brindan alguna forma de explorar y obtener una vista previa de esos componentes y sus activos asociados".

    — “ Sobre la construcción de bibliotecas de componentes ”, Clearleft

    Aprenderemos cómo construir una biblioteca de componentes creando una que consta de cuatro componentes:

    1. Button
      Un contenedor alrededor del botón HTML predeterminado
    2. Box
      Un contenedor (HTML div) con propiedades personalizadas
    3. Columns
      Un contenedor cuyos hijos están espaciados uniformemente a lo largo del eje x.
    4. Stack
      Un contenedor cuyos hijos están espaciados uniformemente a lo largo del eje y

    Estos componentes podrían luego usarse en cualquier aplicación en la que estemos trabajando. Construiremos la biblioteca de componentes usando React y Emotion.

    Al final de este artículo, debería poder crear una biblioteca de componentes que se ajuste a cualquier caso de uso que tenga en mente. Este conocimiento será útil cuando trabaje con un equipo que necesite utilizar componentes reutilizables.

    Primero, comencemos por establecer qué es la biblioteca Emotion. La documentación explica:

    “Emotion es 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”.

    — “ Introducción ”, Documentos de emociones

    En esencia, Emotion es una biblioteca de CSS en JavaScript, y una cosa interesante de las bibliotecas de CSS en JavaScript es que le permiten colocar componentes con estilos. Poder unirlos en un alcance garantiza que algunos estilos de componentes no interfieran con otros, lo cual es crucial para nuestra biblioteca de componentes.

     

    Emotion expone dos API para React:

    • @emotion/core
    • @emotion/styled

    Antes de profundizar en cómo funcionan estas API, tenga en cuenta que ambas admiten el estilo de componentes con cadenas de plantilla y objetos.

    La API principal es en realidad como la propiedad normal styleque usamos actualmente cuando creamos aplicaciones con React, con la adición de prefijos de proveedores, selectores anidados, consultas de medios y más.

    El uso del enfoque de objetos con la API principal normalmente se vería así:

    import { jsx } from '@emotion/core'let Box = props = { return ( div css={{ backgroundColor: 'grey' }} {...props} / )}

    Este es un ejemplo bastante artificial que muestra cómo podríamos diseñar un Boxcomponente con Emotion. Es como cambiar una stylepropiedad por otra cssy luego estamos listos.

    Ahora, veamos cómo podríamos usar el enfoque de cadena de plantilla con la misma API principal:

    import { jsx, css } from '@emotion/core'let Box = props = { return ( div css={css` background-color: grey `} {...props} / )}

    Todo lo que hicimos fue envolver la cadena de la plantilla con la cssfunción de etiqueta y Emotion se encarga del resto.

    La API con estilo , que se basa en la API principal, adopta un enfoque ligeramente diferente para diseñar componentes. Esta API se llama con un elemento HTML o componente React particular, y ese elemento se llama con un objeto o una cadena de plantilla que contiene los estilos para ese elemento.

    Veamos cómo podríamos usar el enfoque de objetos con la API con estilo:

    import styled from '@emotion/styled'const Box = styled.div({ backgroundColor: 'grey'});

    A continuación se muestra una forma de utilizar la API con estilo, que es una alternativa al uso de la API principal. Las salidas renderizadas son las mismas.

    • Aproveche la sólida recuperación de datos y el tamaño de paquete optimizado con KendoReact Server Data Grid Probar ahora

    Ahora, veamos cómo podríamos usar el enfoque de cadena de plantilla usando la API con estilo:

    import styled from '@emotion/styled'const Box = styled.div` background-color: grey`

    Esto logra lo mismo que el enfoque de objetos, solo que esta vez con una cadena de plantilla.

    Podríamos utilizar la API principal o la API con estilo al crear componentes o una aplicación. Prefiero el enfoque con estilo para una biblioteca de componentes por un par de razones:

     

    • Consigue mucho con unas pocas pulsaciones de teclas.
    • Se necesita un asaccesorio, que ayuda a cambiar dinámicamente el elemento HTML desde el sitio de la llamada. Digamos que por defecto usamos un elemento de párrafo y necesitamos un elemento de encabezado debido a la semántica; podemos pasar el elemento de encabezado como un valor a la aspropiedad.

    Empezando #

    Para comenzar, clonemos los scripts de configuración en GitHub, lo cual podemos hacer en la línea de comando:

    git clone [email protected]:smashingmagazine/component-library.git

    Este comando copia el código en ese repositorio a la component-librarycarpeta. Contiene el código necesario para configurar una biblioteca de componentes, que incluye Rollup para ayudar a agrupar nuestra biblioteca.

    Actualmente tenemos una componentscarpeta con un index.jsarchivo, que no hace nada. Crearemos nuevas carpetas debajo de la componentscarpeta para cada componente que construyamos en nuestra biblioteca. La carpeta de cada componente expondrá los siguientes archivos:

    • Component.js
      Este es el componente que estamos construyendo.
    • index.js
      Esto exporta el componente Component.jsy facilita la referencia a componentes desde una ubicación diferente.
    • Component.story.js
      Básicamente, esto representa nuestro componente en sus múltiples estados usando Storybook.

    También viene con una utilscarpeta que define ciertas propiedades que se usarían en nuestros componentes. La carpeta contiene varios archivos:

    • helpers.js
      Contiene funciones auxiliares que usaremos en nuestra aplicación.
    • units.js
      Esto define las unidades de espaciado y tamaño de fuente, que usaremos más adelante.
    • theme.js
      Esto define la paleta, las sombras, la tipografía y la forma de nuestra biblioteca de componentes.

    Veamos lo que hemos definido en el units.jsarchivo:

    export const spacing = { none: 0, xxsmall: '4px', xsmall: '8px', small: '12px', medium: '20px', gutter: '24px', large: '32px', xlarge: '48px', xxlarge: '96px',};export const fontSizes = { xsmall: '0.79rem', small: '0.889rem', medium: '1rem', large: '1.125rem', xlarge: '1.266rem', xxlarge: '1.424rem',};

    Esto define las reglas spacingy fontSizes. La regla de espaciado se inspiró en el sistema de diseño Braid , que se basa en múltiplos de cuatro. Se fontSizesderivan de la escala de tipo segundo mayor (1,125), que es una buena escala para sitios web de productos. Si tiene curiosidad por saber más sobre la escala de tipos, “ Exploración de las escalas de tipos responsivos ” explica el valor de conocer las escalas apropiadas para diferentes sitios web.

    A continuación, ¡repasemos el theme.jsarchivo!

    import { spacing } from './units';const white = '#fff';const black = '#111';const palette = { common: { black, white, }, primary: { main: '#0070F3', light: '#146DD6', contrastText: white, }, error: { main: '#A51C30', light: '#A7333F', contrastText: white, }, grey: { 100: '#EAEAEA', 200: '#C9C5C5', 300: '#888', 400: '#666', },};const shadows = { 0: 'none', 1: '0px 5px 10px rgba(0, 0, 0, 0.12)', 2: '0px 8px 30px rgba(0, 0, 0, 0.24)',};const typography = { fontFamily: "Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Ubuntu, 'Helvetica Neue', sans-serif",};const shape = { borderRadius: spacing['xxsmall'],};export const theme = { palette, shadows, typography, shape,};

    En el archivo de tema, hemos definido nuestro palette, que son esencialmente los colores que usaremos en todos los componentes de nuestra biblioteca. También tenemos un shadowsobjeto, donde definimos nuestros box-shadowvalores. También está el typographyobjeto, que actualmente solo define nuestro fontFamily. Finalmente, shapese utiliza para propiedades como border-radius. La estructura de este tema está inspirada en Material-UI .

     

    ¡A continuación, nuestro helpers.jsarchivo!

    export const isObjectEmpty = (obj) = { return Object.keys(obj).length === 0;};

    Aquí, solo exponemos la isObjectEmptyfunción, que toma un objeto y regresa truesi el objeto está vacío. Devuelve falsesi tiene algún valor. Haremos uso de esta función más adelante.

    Ahora que hemos revisado todos los archivos de la utilscarpeta, ¡es hora de comenzar a construir nuestros componentes!

    Botones #

    Los botones son uno de los componentes más utilizados en la web. Se utilizan en todas partes y pueden adoptar diferentes formas, tamaños y más.

    Estos son los botones que vamos a construir en Figma .

    Diseño de componentes de botones de Figma ( vista previa grande )

    Estas sutiles variaciones se aplicarán como propiedades de nuestro botón. Nos gustaría que los botones de nuestra biblioteca de componentes acepten propiedades como variant, size, enableElevation(es decir box-shadow, ) y color.

    Comenzando con el componente de botón, creemos una Buttoncarpeta, donde definiremos todo lo relacionado con los botones, como se discutió anteriormente.

    Creemos nuestro componente de botón:

    import styled from '@emotion/styled';import isPropValid from '@emotion/is-prop-valid';const StyledButton = () = {};const IGNORED_PROPS = ['color'];const buttonConfig = { shouldForwardProp: (prop) = isPropValid(prop) !IGNORED_PROPS.includes(prop),};export const Button = styled('button', buttonConfig)(StyledButton);

    Aquí, comenzamos configurando nuestro componente de botón con un archivo buttonConfig. Contiene , que se utiliza para controlar las propiedades buttonConfigque shouldForwardPropdeben reenviarse al DOM, porque propiedades como se colormuestran en el elemento renderizado de forma predeterminada.

    A continuación, definamos los tamaños de nuestros botones, que usaremos en el componente del botón.

    const buttonSizeProps = { small: { fontSize: fontSizes['xsmall'], padding: `${spacing['xsmall']} ${spacing['small']}`, }, medium: { fontSize: fontSizes['small'], padding: `${spacing['small']} ${spacing['medium']}`, }, large: { fontSize: fontSizes['medium'], padding: `${spacing['medium']} ${spacing['large']}`, },};

    buttonSizePropses un mapa de nuestros valores de tamaño ( small, mediumy large), y devuelve fontSizevalores paddingbasados ​​en los tamaños. Para un botón pequeño, necesitaríamos una fuente pequeña con un relleno pequeño. Lo mismo ocurre con los tamaños medianos y grandes para escalarlos adecuadamente.

     

    A continuación, definamos una función que proporcione propiedades CSS válidas basadas en la variante pasada:

    const getPropsByVariant = ({ variant, color, theme }) = { const colorInPalette = theme.palette[color]; const variants = { outline: colorInPalette ? outlineVariantPropsByPalette : defaultOutlineVariantProps, solid: colorInPalette ? solidVariantPropsByPalette : defaultSolidVariantProps, }; return variants[variant] || variants.solid;};

    Aquí, la getPropsByVariantfunción toma las propiedades variant, colory themey devuelve las propiedades de la variante especificada; si no se especifica ninguna variante, el valor predeterminado es solid. colorInPaletterecupera la paleta asignada al color especificado si se encuentra y undefinedsi no se encuentra en nuestro themeobjeto.

    En cada variante comprobamos si realmente existe una paleta para el color indicado; si no lo hacemos, usamos colores de los objetos commony greyde nuestro tema, que aplicaremos en defaultOutlineVariantPropsy defaultSolidVariantProps. Quick news about games, health, travel, tv, movies

    A continuación, ¡definamos nuestras propiedades variantes!

    const defaultSolidVariantProps = { main: { border: `1px solid ${theme.palette.grey[100]}`, backgroundColor: theme.palette.grey[100], color: theme.palette.common.black, }, hover: { border: `1px solid ${theme.palette.grey[200]}`, backgroundColor: theme.palette.grey[200], },};const defaultOutlineVariantProps = { main: { border: `1px solid ${theme.palette.common.black}`, backgroundColor: theme.palette.common.white, color: theme.palette.common.black, }, hover: { border: `1px solid ${theme.palette.common.black}`, backgroundColor: theme.palette.common.white, color: theme.palette.common.black, },};const solidVariantPropsByPalette = colorInPalette { main: { border: `1px solid ${colorInPalette.main}`, backgroundColor: colorInPalette.main, color: colorInPalette.contrastText, }, hover: { border: `1px solid ${colorInPalette.light}`, backgroundColor: colorInPalette.light, },};const outlineVariantPropsByPalette = colorInPalette { main: { border: `1px solid ${colorInPalette.main}`, backgroundColor: theme.palette.common.white, color: colorInPalette.main, }, hover: { border: `1px solid ${colorInPalette.light}`, backgroundColor: theme.palette.common.white, color: colorInPalette.light, },};

    Aquí definimos las propiedades que se aplicarán a nuestro botón en función de las variantes seleccionadas. Y, como se mencionó anteriormente, defaultSolidVariantPropsusar defaultOutlineVariantPropscolores de nuestros objetos commony greycomo respaldo para cuando el color especificado no esté en nuestra paleta o cuando no se especifique ningún color para lo que implementamos.

     

    Por cierto, los objetos solidVariantPropsByPalettey outlineVariantPropsByPaletteusan el color de nuestra paleta como se especifica en el botón. Ambos tienen mainpropiedades hoverque diferencian los estilos predeterminado y de desplazamiento del botón, respectivamente.

    El diseño del botón que hemos utilizado tiene dos variantes, que podemos consultar en el diseño de nuestra biblioteca de componentes .

    A continuación, creemos nuestra StyledButtonfunción, que combina todo lo que hemos hecho hasta ahora.

    const StyledButton = ({ color, size, variant, enableElevation, disabled, theme,}) = { if (isObjectEmpty(theme)) { theme = defaultTheme; } const fontSizeBySize = buttonSizeProps[size]?.fontSize; const paddingBySize = buttonSizeProps[size]?.padding; const propsByVariant = getPropsByVariant({ variant, theme, color }); return { fontWeight: 500, cursor: 'pointer', opacity: disabled 0.7, transition: 'all 0.3s linear', padding: buttonSizeProps.medium.padding, fontSize: buttonSizeProps.medium.fontSize, borderRadius: theme.shape.borderRadius, fontFamily: theme.typography.fontFamily, boxShadow: enableElevation theme.shadows[1], ...(propsByVariant propsByVariant.main), ...(paddingBySize { padding: paddingBySize }), ...(fontSizeBySize { fontSize: fontSizeBySize }), ':hover': !disabled { boxShadow: enableElevation theme.shadows[2], ...(propsByVariant propsByVariant.hover), }, };};

    En la StyledButtonfunción, asignamos defaultThemeel tema si el themeobjeto está vacío, lo que hace que sea opcional para los consumidores de nuestra biblioteca usar ThemeProvider de Emotion para poder hacer uso de la biblioteca. Asignamos fontSizey paddingbasándonos en el buttonSizePropsobjeto. Definimos varias propiedades de botón predeterminadas, como fontWeighty cursor, que no están vinculadas a ninguna propiedad, y también derivamos valores color, backgroundColory borderbasados ​​en el resultado de propsByVariant.

    Ahora que hemos creado nuestro Buttoncomponente, veamos cómo podemos usarlo:

    Button variant="solid" color="primary" size="small" enableElevation disabled Small Outline Elevated Button/Button

    Podemos comprobar cómo se ve en CodeSandbox :

    That’s how to use the Button component. We define the following properties:

    • We define a variant with a solid value. We could have specified outline instead. If the variant prop isn’t provided, we would also default to solid.
    • We define color, with a value of primary. We also support error as a color value or a color from a theme object. If the color property isn’t specified, we would fall back to our default color state.
    • We define size, with a value of small. It could be medium (the default) or large.
    • We define EnableElevation because we want some box-shadow on our button. We could have chosen not to use it.
    • Finally, we define disabled because we want our button to be disabled. The additional thing we do to a disabled button is reduce its opacity.

    The button doesn’t need to take any property. It defaults to a solid medium-sized button.

     

    Componente de la caja #

    A box component is a container that can hold any component or HTML element. It accepts but is not limited to properties such as padding, margin, display, and width. It can also be used as a base component for some of the other components we’ll get into later.

    Here’s what it looks like on Figma:

    Box component design from Figma (Large preview)

    Before diving into the code, let’s not forget to create a new folder for this component.

    Now, let’s create our Box component:

    import styled from '@emotion/styled';import isPropValid from '@emotion/is-prop-valid';import { spacing, theme as defaultTheme } from '../../utils';const StyledBox = ({ paddingX, paddingY, marginX, marginY, width, display, theme, ...props}) = { if (isObjectEmpty(theme)) { theme = defaultTheme; } const padding = spacing[props.padding]; let paddingTop = spacing[props.paddingTop]; let paddingRight = spacing[props.paddingRight]; let paddingBottom = spacing[props.paddingBottom]; let paddingLeft = spacing[props.paddingLeft]; if (paddingX) { paddingLeft = spacing[paddingX]; paddingRight = spacing[paddingX]; } if (paddingY) { paddingTop = spacing[paddingY]; paddingBottom = spacing[paddingY]; } let margin = spacing[props.margin]; let marginTop = spacing[props.marginTop]; let marginRight = spacing[props.marginRight]; let marginBottom = spacing[props.marginBottom]; let marginLeft = spacing[props.marginLeft]; if (marginX) { marginLeft = spacing[marginX]; marginRight = spacing[marginX]; } if (marginY) { marginTop = spacing[marginY]; marginBottom = spacing[marginY]; } return { padding, paddingTop, paddingRight, paddingBottom, paddingLeft, margin, marginTop, marginRight, marginBottom, marginLeft, width, display, fontFamily: theme.typography.fontFamily, };};const IGNORED_PROPS = ['display', 'width'];const boxConfig = { shouldForwardProp: (prop) = isPropValid(prop) !IGNORED_PROPS.includes(prop),};export const Box = styled('div', boxConfig)(StyledBox);

    The spacing rule we defined earlier is being applied to both padding and margin, as we can see in the Box component. We receive contextual values for padding and margin, and we look up their actual values from the spacing object.

    We accept paddingX and paddingY props to update padding across the horizontal and vertical axis, respectively. We do the same for marginX and marginY as well.

    Also, we don’t want the display and width props to get forwarded to the DOM because we only need them in CSS. So, we add them to our list of props to ignore, and pass that on to our config.

     

    Here’s how we could use the Box component:

    Box padding="small" paddingTop="medium" paddingBottom="medium" Simple Box Component/Box

    We can see what this looks like on CodeSandbox.

    In this Box component, we’ve assigned small as a value to our padding property, and medium to the paddingTop and paddingBottom properties. When rendered, the Box component will have its padding-left and padding-right properties set to 12px each, and its padding-top and padding-bottom properties set to 20px. We could have replaced paddingTop and paddingBottom with paddingY and gotten the same result.

    Componente de columnas #

    The Columns component is a variation of our Box component, with a display type of flex and with children spaced evenly across the x-axis.

    Here is a representation of the Columns component in Figma:

    Columns component design from Figma (Large preview)

    Let’s build our Columns component!

    import React from 'react';import { Box } from '../Box';export const Columns = ({ children, space, ...props }) = { return ( Box display="flex" {...props} {React.Children.map(children, (child, index) = { if (child.type !== Box) { console.warn( 'Each child in a Columns component should be a Box component' ); } if (index 0) { return React.cloneElement(child, { marginLeft: space, width: '100%', }); } return React.cloneElement(child, { width: '100%' }); })} /Box );};

    We’re using React.Children to map over the Columns component’s children. And we’re adding marginLeft and width properties to each of the children, except the first child, which doesn’t need a marginLeft property because it’s the leftmost child in the column. We expect each child to be a Box element to ensure that the necessary styles are applied to it.

    Here’s how we could use the Columns component:

    Columns space="small" Box Item 1/Box Box Item 2/Box Box Item 3/Box/Columns

    We can see what that looks like on CodeSandbox.

    The Columns children here are spaced evenly across the x-axis by 12 pixels because that’s what the value of small resolves to, as we’ve defined earlier. Because the Columns component is literally a Box component, it can take in other Box component properties, and we can customize it as much as we want.

    Componente de pila #

    This is also a variation of our Box component that takes the full width of the parent element and whose children are spaced evenly across the y-axis.

    Here is a representation of the Stack component in Figma:

    Stack component design from Figma (Large preview)

    Let’s build our Stack component:

     

    import React from 'react';import { Box } from '../Box';import { Columns } from '../Columns';const StackChildrenTypes = [Box, Columns];const UnsupportedChildTypeWarning = 'Each child in a Stack component should be one of the types: Box, Columns';export const Stack = ({ children, space, ...props }) = { return ( Box {...props} {React.Children.map(children, (child, index) = { if (!StackChildrenTypes.includes(child.type)) { console.warn(UnsupportedChildTypeWarning); } if (index 0) { return React.cloneElement(child, { marginTop: space }); } return child; })} /Box );};

    Here, we map over each child with React.Children and apply a paddingTop property to it with the value of the space argument. As for the first child, we need it to take its original position, so we skip adding a marginTop property to it. We also accept each child to be a Box so that we can apply the necessary properties to it.

    Here’s how we could use the Stack component:

    Stack space="small" Box marginTop="medium" Item 1/Box Box Item 2/Box Box Item 3/Box/Stack

    We can see what that looks like on CodeSandbox.

    Here, the Box elements are spaced evenly with the small unit, and the first Box takes a separate marginTop property. This shows that you can customize components however you wish.

    Conclusión #

    We’ve gone through the basics of using Emotion to create components in React using the APIs that it provides. This is just one of many ways to go about building a component library. There are some nuances to building it for a brand because you might not have to take theming and some other things into consideration. But if you plan to release the library to the public one day, then you’ll have to deal with requests for those missing pieces, so consider that possibility and make the library a little flexible ahead of time.

    If you have any questions, feel free to drop them as comments.

    The repository for this article is on GitHub, and the button designs we’ve used are on Figma.

    Referencias #

    • “On Building Component Libraries”, Mark Perkins, Clearleft
    • “Exploring Responsive Type Scales”, Joseph Mueller
    • “ Diseñar sistemas con React y Storybook ”, Emma Bostian, Frontend Masters
    • Documentación oficial de emoción.

    Útiles bits de front-end y UX, entregados una vez por semana.

    Con herramientas que le ayudarán a realizar mejor su trabajo. Suscríbase y obtenga el PDF de las listas de verificación de diseño de interfaz inteligente de Vitaly por correo electrónico.

    En front-end y UX . Con la confianza de más de 207.000 personas.

    (ks, ra, al, il)Explora más en

    • API
    • Reaccionar
    • javascript





    Tal vez te puede interesar:

    1. 40 bibliotecas útiles de JavaScript
    2. Bibliotecas prácticas de JavaScript y complementos de jQuery
    3. Bibliotecas de JavaScript útiles y complementos de jQuery: parte 2
    4. Bibliotecas JavaScript útiles y complementos jQuery

    Construyendo una biblioteca de componentes con React y Emotion

    Construyendo una biblioteca de componentes con React y Emotion

    ¡Registro! Diseño de arquitectura de componentes de interfaz de usuario y tokens, con Nathan Curtis Índice

    programar

    es

    https://pseint.es/static/images/programar-construyendo-una-biblioteca-de-componentes-con-react-y-emotion-1057-0.jpg

    2024-04-04

     

    Construyendo una biblioteca de componentes con React y Emotion
    Construyendo una biblioteca de componentes con React y Emotion

    Si crees que alguno de los contenidos (texto, imagenes o multimedia) en esta página infringe tus derechos relativos a propiedad intelectual, marcas registradas o cualquier otro de tus derechos, por favor ponte en contacto con nosotros en el mail [email protected] y retiraremos este contenido inmediatamente

     

     

    Top 20