Agregar un componente base
Se explica el flujo de creación de un nuevo tipo de componente base a la librería.
Prerequisitos
Clonar el repositorio de Inlaze-ui-react perteneciente a la organización de github sport-enlace-sas
Instalar dependencias con npm
Ejecutar el comando npm run libs:storybook para ir visualizando y documentando el componente a agregar.
Casos de uso
Agregar un nuevo componente base en la librería que esté disponible para todos los templates
Paso a paso para el ciclo de vida completa en la adición de un componente nuevo
Paso 1: Agregar un nuevo componente base
Suponiendo que la libreria actualmente no tiene el componente button, para agregarlo se requiere:
Determinar el {componentType} si el componente es UIkit o CompoundComponents, para saber si se guarda en common/UiKit o en common/CompundComponents
Determinar si el componente tiene variantes, para determinar si en common/{compundComponents} se guarda en un folder en plural cuando tiene variantes, o solo un componente en singular
Ejecutar el comando del CLI de NX para crear componente teniendo en cuenta los dos factores anteriores:
// npx nx g @nx/react:component libs/src/lib/common/{ComponentType}/{SingularComponentName}/{SingularComponentName}
// npx nx g @nx/react:component libs/src/lib/common/{ComponentType}/{PluralComponentName}/{VariantComponentName}/{VariantComponentName}
Una vez creado el componente se crea en la historia para que se pueda visualizar en el storybook ejecutando el comando:
npm run libs:stories
Step 2: Desarrollar el componente Base
Al momento de desarrollar el componente base de forma que sea fácilmente escalable, mantenible y con la flexibilidad suficiente para ser reutilizado en distintos templates, se han venido implementando las siguientes prácticas en el proceso de implementación:
Usar el mismo tipo al definir las props del componente base
Las props de todo componente base usa el tipo BaseComponentProps
import styles from "./styles.module.css"
import type { HTMLAttributes, ReactNode } from "react";
export interface DefaultProps<CssModuleBaseType> {
customCssModule?: Partial<CssModuleBaseType>;
}
export type BaseComponentProps<
ComponentProps,
CssModuleProps = object,
HTMLAttributesType = HTMLAttributes<HTMLElement>,
> = ComponentProps & HTMLAttributesType & DefaultProps<CssModuleProps>;
export type example1ComponentBaseProps = BaseComponentProps<{ children: ReactNode }>;
export type example2ComponentBaseProps = BaseComponentProps<{}, type of styles>;
export type example3ComponentBaseProps = BaseComponentProps<{ isActive: boolean }, type of styles, HTMLAnchorElement<HTMLAnchorElement>>;
Este tipo espera como genéricos:
ComponentProps: Las props propias de la funcionalidad del componente
HTMLAttributesType (opcional): Añade las props del HTML contenedor para que el cliente pueda usar los mismos atributos nativos, y no se tengan que ir adicionando en la medida que se vallan necesitando debido a que es imprevisible determinar que atributos nativos del contenedor pueda llegar a necesitar el cliente.
CssModuleProps (opcional): Cuando se crea un css module un plugin de vite de la librería se encarga de construir un {styleName}.module.css.d.ts, de forma que se pueda obtener la interface de las clases con el type of del css module importado en el componente. Lo que tiene la ventaja que el componente base en la prop customCssModule comunique a su cliente las clases que esta usando y que puede sobreescribir, proceso el cual debe ser intuitivo debido a que el nombramiento de clases se ha hecho bajo el sistema BEM.
Técnica para separar el HTML de la funcionalidad
Definir el children container o content como una Prop tipo callback que es invocado en el return del componente funcional.
function PorEjemplo({children, props}:PropsType):{children:({...props, ...hooks}:CallbackArgs)=> ReactNode, args:unknown}{
const hooks = customHook()
return children({...props, ...hooks})
}
La ventaja de definir un children de esta manera es que cualquier variable con la que sea invocado el callback va a ser accesible desde el cliente, por tanto el cliente puede tanto definir cualquier estructura html, y acceder con los argumentos del callback que fueron expuestos en el componente base, a cualquier variable usada dentro del componente, bien sea las mismas props, estados y elementos importados en el componente.
function EjemploDeUso1(){
return (
<PorEjemplo>[
(args)=>(
<div>
<ul onClick={args.ClickHandler(args.state)}>
{args.state}
</ul>
</div>
)
]</PorEjemplo>
)
}
function EjemploDeUso2(){
return (
<PorEjemplo>[
(args)=>(
<div onClick={args.ClickHandler(args.state)}>
<h1>{args.state}</h1>
<section>{args.prop1}</section>
</div>
)
]</PorEjemplo>
)
Como se observa en el ejemplo anterior se tiene acceso a las mismas funcionalidades para que el cliente tenga la libertad en que etiquetas poner los triggers y los valores usar los argumentos del callback.
Paso 3: Desarrollo de tests unitarios
Dado que esta librería tiene como propósito ser usado en los múltiples proyectos de la organización, especialmente los componentes base deben ser testeados dado que al momento de hacerles mantenimiento se debe garantizar que las nuevas features no afecten el desarrollo existente o de hacerlo sea intencionalmente y se reporte en el breaking change en la version a publicar.
Entonces los test unitarios y no las pruebas manuales del desarrollador deben ser las que identifiquen el impacto quepuede llegar a tener una actualización en todos los clientes que lo consumen.
Como factor a tener en cuenta en la librería es importante que los tests prueben que:
Si el children es cerrado se debe poder probar que el cliente tiene acceso a los atributos nativos HTML de la etiqueta contenedora.
Se debe poder probar que el cliente puede poner las variables y triggers expuestos de la forma que quiera en el children.
Probar el comportamiento de las funciones específicas propias del componente como hooks y utilidades.
Paso 4: Validar que el storybook halla documentado claramente como un cliente puede usar el componente.
El storybook debe tener un control por cada una de las props y el ejemplo de como el componente se ve afectado al modificar interactivamente esos controles.
Entorno a como configurar los controles del storybook véase: Storybook argtypes
Conclusión
Luego de conocer la receta de como crear un componete base, el paso siguiente es ver como crear un sus variantes.
Last updated