En el artículo anterior vimos la instalación de Storybook en Nx Angular monorepo. Esa instalación venía con una configuración mínima por defecto que ahora vamos a ampliar un poco más para conocer un poco más su estructura y de paso tener alguna característica más.

Antes que nada vamos a ver un poco la estructura que nos ha dejado la instalación en la librería  «storybook-host» creada. Básicamente se genera una carpeta llamada «.storybook» que contiene diferentes tipos de archivos:

  1. main.[js|ts]: Configuración principal de Storybook, donde se define el comportamiento, donde están las stories, qué add-ons (complementos) queremos incorporar, con qué framework trabajaremos, etc.
  2. preview,[js|ts]: Configuración para controlar la forma en cómo se presentan las stories donde se pueden añadir decoradoresparámetros y tipos globales. Como es de uso global, podemos incluir hojas de estilos css, código javascript,… que serán accesibles para todas las stories.
  3. manager.[js|ts]: Configuración de la UI de Storybook. Aquí podemos definir como personalizar la presentación: tipo de letra, colores, imágenes, … incluso podemos crear temas. Cuidado, no confundir con el poder incluir un css en el archivo preview, aquí cada css tiene su rol o interés (separation of concerns).
  4. tsconfig.json: Archivo de configuración de typescript para el Storybook.

main

Se exporta un objeto con la configuración principal de tipo ‘StorybookConfig’ con diferentes propiedades.

Framework

Al estar en un monorepo de Angular, la instalación de Storybook ya nos ha puesto que el framework de trabajo es «@storybook/angular»’ por defecto (lo seleccionamos al instalar).

framework: {
  name: '@storybook/angular',
  options: {}
}

Ubicación de stories y documentación

Las stories están en archivos que normalmente tienen una misma nomenclatura que los ficheros de angular más el sufijo «stories». Así pues, si tenemos un componente llamado «button.component.ts», sus stories estarán en el archivo «button.component.stories.ts».

Al tener un monorepo, pueden existir librerías (libs) y aplicaciones (apps). Normalmente las stories se concentran en las librerías, porque estamos creando componentes para ser usados por terceros o dentro de la propia aplicación (o aplicaciones) y queremos mostrar cómo se usan. Sería raro tener stories dentro de aplicaciones pero no imposible.

La documentación son archivos con extensión .mdx que es un mix entre markdown y javascript/JSX. Habitualmente siguen la misma nomenclatura que los archivos de stories, pero su extensión en vez de ser «.ts», es «.mdx» (siguiendo el ejemplo anterior  «buttons.component.stories.mdx»). No obstante puedes generar archivos mdx que no tienen nada que ver con las stories como por ejemplo «how-to.mdx».

Finalmente tenemos que decirle a Storybook donde están todos esos archivos, así que en su propiedad stories podemos poner: 

stories: [
  '../../../libs/**/*.mdx',
  '../../../libs/**/*.stories.@(js|jsx|ts|tsx)'
]

También hay que decirle donde están las stories al tsconfig:

"include": [
  "../../../libs/**/*.stories.ts",
  "*.js",  
  "*.ts"
]

Para finalizar, en el archivo «nx.json», chequeamos en el apartado «namedInputs” tanto los archivos de stories y mdx están correctamente:

  "!{projectRoot}/**/*.stories.@(js|jsx|ts|tsx)",
  "!{projectRoot}/**/*.mdx",
  "!{projectRoot}/.storybook/**/*",
  "!{projectRoot}/tsconfig.storybook.json"

Extendiendo funcionalidad: add-ons

Los add-ons son complementos para enriquecer el uso de Storybook. Inicialmente incluye un par de add-ons: 

  1. @storybook/addon-essentials‘: Extiende la UI y su comportamiento.
  2. @storybook/addon-interactions‘: Al instalar Storybook nos hizo una pregunta sobre si queríamos instalar interacciones al que respondimos que sí. Con este add-on, podemos crear stories que tengan un flujo de acciones pre-establecidas. Por ejemplo hacer automáticamente clic en un botón.

A esta lista podemos añadir los siguientes complementos teniendo que hacer la instalación de su package alineado con la versión Storybook (si la versión es ^7.5.3, hay que instalar la misma).

@storybook/addon-docs: Con él podremos generar la documentación.

npm install –-save-dev @storybook/addon-docs

@storybook/addon-a11y: Con él podremos ver si el componente cumple con los estándares de accesibilidad.

npm install –-save-dev @storybook/addon-a11y

@storybook/addon-links: Para poder programar una navegación entre stories.

npm install –-save-dev @storybook/addon-links

Lo que quedaría:

addons: [
  '@storybook/addon-essentials',
  '@storybook/addon-docs',
  '@storybook/addon-a11y',
  '@storybook/addon-interactions',
  '@storybook/addon-links'
]

Hay muchos más, así que te invito a que veas su galería y escojas según tus necesidades.

Documentación

Antes hemos hablado sobre la creación de la documentación mediante los archivos .mdx. Si bien la podemos escribir nosotros con cierta estructura, Storybook tiene la autodocumentación.

Una manera de configurar la documentación es escribirla nosotros pero podemos hacer que nos ayude de forma automática. Podemos activarla o desactivarla a nivel general, pero dejaremos a nuestro criterio si esa story tendrá o no documentación configurando que el autodocs se active mediante tags (y asociando el tag «autodocs» en la story) así como el nombre del documento será «Documentation» (si no se le asigna otro nombre).

Así que no activaremos ni desactivaremos la autodocumentación en general, si no más bien podremos generarla cuando la story tenga el tag:

docs: {
  autodocs: 'tag',
  defaultName: 'Documentation'
}

main.ts

import type { StorybookConfig } from '@storybook/angular';

const config: StorybookConfig = {
  stories: [
    '../../../libs/**/*.mdx',
    '../../../libs/**/*.stories.@(js|jsx|ts|tsx)'
  ],
  addons: [
    '@storybook/addon-essentials',
    '@storybook/addon-docs',
    '@storybook/addon-a11y',
    '@storybook/addon-interactions',
    '@storybook/addon-links'
  ],
  framework: {
    name: '@storybook/angular',
    options: {},
  },
  docs: {
    autodocs: 'tag',
    defaultName: 'Documentation'
  }
};

export default config;

preview

Exportamos un objeto de tipo ‘Preview’ con la propiedad parameters.

Ordenación

La ordenación de stories y documentación dentro del ‘sidebar’ la haríamos de forma simple, dando prioridad a la documentación y luego a las stories quedando de la siguiente manera:

storySort: {
  order: ['Documentation', 'Components']
}

Expandir toda la información de las propiedades

Con esta característica, en la manipulación de los datos (inputs) de un componente dentro de la story podremos ver todas sus características: nombre, descripción, tipo, valor, … en vez de solo ver el nombre y valor. De esta forma damos al usuario más información respecto a cada propiedad.

controls: { expanded: true }

preview.ts

import type { Preview } from '@storybook/angular';

const preview: Preview = {
  parameters: {
    options: {
      storySort: {
        order: ['Documentation', 'Components'],
      },
    },
    controls: { expanded: true },
  },
};

export default preview;

manager

Si no existe este archivo, lo creamos (manaager.ts) e inicializamos mediante el objeto de addons con su método «setConfig» pasándole una configuración.

Filtrado de stories en el sidebar

Quizás nos interese que ciertas stories no se muestren en el sidebar, es decir, podemos filtrarlas. Así pues, las marcamos con un tag específico, por ejemplo «hidden» de manera que en el preview solo tengamos que realizar el filtrado por ese tag de la siguiente forma:

filters: {
  patterns: (item) => !item.tags?.includes('hidden')
}

manager.ts

import { addons } from '@storybook/manager-api';

addons.setConfig({
  sidebar: {
    filters: {
      patterns: (item) => !item.tags?.includes('hidden')
    }
  }
});

Existe una manera más core de ocultar/mostrar stories sea donde sea (sidebar, autodocs, …) y es simplemente asignar una lista con los nombres de cada story (o una expresión regular) en la propiedad «excludeStories» del meta componente. También se puede jugar con la propiedad «includeStories» poniendo solo aquellas que queremos que se visualicen.

const meta: Meta = {
  component: TextButtonComponent,
  excludeStories: ['Basic'],
  includeStories: ['Primary', 'Secondary']
};

Resumen

Hemos hecho un recorrido imprescindible por los diferentes archivos de configuración de Storybook para darle algo más de visibilidad.

Pantalla de storybook
Ejemplo de visualización de storybook

Queda mucho camino por recorrer que iremos viendo en siguientes artículos como saber escribir bien las stories o los mdx (linting), escribir una story y documentación, refrescar el componente ante cualquier cambio, añadir multicultura, etc.