Cuando vas a desarrollar, se necesita tener a mano buena documentación para tirar de ella en algún momento cuando desconoces sobre el tema o tienes dudas. Esa documentación suele estar redactada formalmente con ejemplos de código en algunos casos para basarse o clarificar el concepto que se explica. Son básicamente los llamados manuales, tutoriales, guías, …

Pero también hay otra manera de explicar el comportamiento de cualquier funcionalidad como si fuera un «patio de juegos» o playground donde se interactúa con el código ejecutándose, y eso es lo que proporciona Storybook como su gran propuesta de valor. Además también se pueden generar documentos escritos o incluso poder hacer tests sobre dichos componentes.

Es por eso que realizaremos una serie de artículos sobre Storybook (en su versión 7.6 a día de escribir este artículo) dentro de un monorepo Nx para Angular porque no hay mucha información y ejemplos para este caso.

Arquitecturas

Según la documentación de Nx, hay varias maneras de configurar storybook en el monorepo según como lo queramos construir (teniendo en cuenta la limitación de storybook de que solo puede haber una instancia por tipo de framework). Aquí te enumero algunas estrategias o casos de uso muy comunes dentro de las mejores prácticas:

  1. Una única instancia de Storybook para todos los proyectos: Solamente existirá un proyecto que englobe todas las stories. Para espacios de trabajo con múltiples aplicaciones y bibliotecas, todas usando un único framework.
  2. Una única instancia de Storybook por scope: Cuándo aplicar esta estrategia:
    1. Para espacios de trabajo con múltiples aplicaciones y librerías usando más de un framework.
    2. Para espacios de trabajo que tienen diferentes scopes y siguen una estructura de carpetas definida.
    3. Espacios de trabajo que tengan múltiples aplicaciones y bibliotecas divididas por tema y framework, a los que no les importe tener más de un Storybook.
  3. Una única instancia de storybook usando Storybook composition: Un Storybook hospeda a varias instancias de storybooks. Básicamente se usa para espacios de trabajo con múltiples aplicaciones y librerías usando más de un framework.

Si bien tener un Storybook por proyecto es factible, no estaría dentro de las mejores prácticas.

Para nuestro caso, el caso de este artículo y siguientes de la serie de Storybook, llevaremos a cabo la estrategia de una única instancia de Storybook para todos los proyectos, dado que solo tenemos Angular como framework.

Instalación

Seguiremos la arquitectura basada en una instancia única para todas las librerías. Al final obtendremos una librería host que será el punto inicial, la instancia de storybook que contendrá básicamente su configuración.

Para esta serie de artículos, usaremos nuestro monorepo. Dependiendo de la versión de Nx, los comandos pueden variar un poco. 

Pero siempre hay que hacer los siguientes pasos:

  • Generar/Crear una librería de Angular vacía.
  • Instalar Storybook sobre esa librería creada.

Para Nx 15 o inferior (usando @nrwl):

Partiendo de una consola, ejecutamos la siguiente línea:

npx nx g @nrwl/angular:library storybook-host --unitTestRunner=none

Esto nos generará una librería vacía con los siguientes cambios más importantes:

UPDATE angular.json
CREATE libs/storybook-host/README.md
CREATE libs/storybook-host/tsconfig.lib.json
CREATE libs/storybook-host/src/index.ts
CREATE libs/storybook-host/src/lib/storybook-host.module.ts
UPDATE tsconfig.base.json
CREATE libs/storybook-host/tsconfig.json
CREATE libs/storybook-host/.eslintrc.json

Hay que chequear que tienes instalada la librería @nrwl/storybook, alineada con la versión del monorepo, así por ejemplo si la versión es la 15,6.3, hay que ejecutar el comando:

npm install --save-dev @nrwl/storybook@15.6.3

Ahora toca configurar esa librería para usar Storybook usando el siguiente comando:

npx nx g @nrwl/storybook:configuration storybook-host --interactionTests=true --uiFramework=@storybook/angular

Ejecutando ese comando nos instalará la versión 6.5.15 de Storybook, aunque después podemos actualizar a la versión 7.6, actualizando el package.json.

Es posible que nos haga preguntas como si deseamos configurar Cypress en cuyo caso no será necesario. Al igual si nos pregunta por el builder, podemos escoger por ejemplo webpack. Al terminar, nos configurará la librería con los siguientes cambios más importantes:

UPDATE package.json
UPDATE nx.json
CREATE .storybook/main.js
CREATE libs/storybook-host/.storybook/main.js
CREATE libs/storybook-host/.storybook/preview.js
CREATE libs/storybook-host/.storybook/tsconfig.json
UPDATE libs/storybook-host/tsconfig.lib.json
UPDATE libs/storybook-host/tsconfig.json
UPDATE angular.json

Ahora podemos ejecutar y ver como queda el Storybook de la siguiente manera:

npx nx build-storybook storybook-host

Con esto podremos abrir un navegador para ver el Storybook, habitualmente en el puerto localhost:4400, pero nos avisará de que no tenemos ninguna story.

Para Nx 16 (usando @nx):

Partimos de la versión de Nx 16.0.3. Abrimos una consola y empezamos la instalación:

npx nx g @nx/angular:library storybook-host --unitTestRunner=none

Nos genera los siguientes cambios:

UPDATE nx.json
CREATE .prettierrc
CREATE .prettierignore
UPDATE package.json
CREATE libs/storybook-host/project.json
CREATE libs/storybook-host/README.md
CREATE libs/storybook-host/tsconfig.json
CREATE libs/storybook-host/tsconfig.lib.json
CREATE libs/storybook-host/src/index.ts
CREATE libs/storybook-host/src/lib/storybook-host.module.ts
CREATE libs/storybook-host/.eslintrc.json
UPDATE tsconfig.base.json

Finalmente instalamos y configuramos Storybook en Nx mediante el comando:

npx nx g @nx/angular:storybook-configuration storybook-host

Nos hará unas preguntas que deberemos responder, pero básicamente debemos elegir que el framework de trabajo será @storybook/angular. Al finalizar vemos que nos instala la versión 7.0.2 de Storybook.

Instalar la versión 7.6 de storybook:

A día de escribir este artículo es la versión que vamos a instalar (aunque están trabajando en la versión 8)

Podemos hacer varias cosas, entre ellas:

  • Actualizar el package.json para obtener las últimas librerías de @storybook (y sus dependencias).
  • Actualizar el monorepo que tenga asociada la versión 7.6 de Storybook. Hay que tener en cuenta que con esto también podemos actualizar angular (o no). Podéis ver el artículo de actualizaciones del monorepo.

Para Nx 18:

En este caso, actualizaremos todo el monorepo llevándolo a su versión latest (v.18.0.7 a día de escribir este artículo), ejecutando en una consola:

npx nx migrate latest
npx nx migrate --run-migrations

Ya tenemos la versión de Nx que queríamos. Vamos ahora a proceder a la instalación de Storybook, muy parecida a las anteriores. Creamos la librería host vacía en la carpeta libs:

npx nx g @nx/angular:library storybook-host --directory=libs/storybook-host --unitTestRunner=none --projectNameAndRootFormat=as-provided

Instalamos y configuramos el Storybook con:

npx nx g @nx/angular:storybook-configuration storybook-host

Respondemos a las preguntas que nos hace:

  1. Do you want set up Storybook interaction tests? –> Yes
  2. Automatically generate *.stories.ts files for components declared in this project? –> No
  3. Configure a static file server for the Storybook instance? –> Yes

En la segunda pregunta contestamos que no porque no necesitamos generar stories para esta librería. Ya tenemos instalado Storybook, podemos ejecutarlo de la siguiente manera:

npx nx run storybook-host:storybook

y como colofón, añadiremos en el package.json un script para que lo levante:

"storybook": "npx nx run storybook-host:storybook"

Resumen

Hemos visto como instalar Storybook para Angular en diferentes versiones del monorepo Nx. Prácticamente son parecidas aunque cambian las versiones como es lógico.
Durante las diferentes instalaciones Nx nos ha puesto los packages necesarios en el package.json así como la configuración de Storybook en la carpeta «.storybook» de la librería «storybook-host». No obstante a partir de una versión ha cambiado lor archivos de javascript a typescript (por ejemplo de main.js a main.ts y de preview.js a preview.ts) para aprovechar las ventajas de typescript.

Más adelante en otros artículos iremos viendo los diferentes tips para afinar la configuración.