Installation de Storybook sur une application React TypeScript

Article rédigé par

Alex_Avatar
Alex Pawlonski

Software Application Developer

Dans cet article, nous allons voir comment installer Storybook dans une application React utilisant TypeScript et Tailwind css. Nous verrons également comment utiliser Tailwind css sur les composants générés par les stories, mais aussi utiliser les contexts de React entre les composants à l’intérieur d’une story. 

Si vous n’êtes intéressés que par une partie du tutoriel comme l’installation de Tailwind css sur Storybook ou l’utilisation du contexte, vous pouvez réaliser ces parties indépendamment les unes des autres. 

Enfin, si vous n’avez pas d’application React déjà construite, vous pourrez retrouver des informations sur la création d’une application React sous TypeScript, ainsi que l’installation de Tailwinds css dans React. Pour cela, rendez-vous dans la partie Compléments en fin d’article. 

1 – Prérequis

Si vous avez déjà une application React sur laquelle vous voulez ajouter Storybook, c’est par ici que ça se passe ! 👇

Nous allons commencer par passer en revue l’application qui servira d’exemple pour ce tutoriel. 

L’application de base a été générée avec cette commande :  

npx create-react-app my-app –template typescript

Voici la liste des dépendances de base du projet : 

 "dependencies": { 
    "@testing-library/jest-dom": "^5.11.4", 
    "@testing-library/react": "^11.1.0", 
    "@testing-library/user-event": "^12.1.10", 
    "@types/jest": "^26.0.15", 
    "@types/node": "^12.0.0", 
    "@types/react": "^17.0.0", 
    "@types/react-dom": "^17.0.0", 
    "react": "^17.0.2", 
    "react-dom": "^17.0.2", 
    "react-scripts": "4.0.3", 
    "typescript": "^4.1.2", 
    "web-vitals": "^1.0.1" 
  }, 

💡 Pour plus d’infos sur l’utilisation de TypeScript dans React, n’hésitez pas à vous rendre sur la doc : https://reactjs.org/docs/static-type-checking.html#typescript

La version de Tailwind css installée est la 2.2.16.

💡 Pour plus d’infos sur l’utilisation de Tailwind css n’hésitez pas à vous rendre sur la doc : https://v2.tailwindcss.com/docs/guides/create-react-app

devDependencies": { 
    "autoprefixer": "^9", 
    "postcss": "^7", 
    "tailwindcss": "npm:@tailwindcss/postcss7-compat", 
  } 

La structure de l’application de base : 

Notre application a été construite avec un dossier components qui regroupe les composants qui construisent ces deux pages. 

Il y a également un dossier context qui contient le contexte React utilisé par l’application si votre application utilise plusieurs contexts ou un context différent pour Storybook. 

Le contexte de l’application : 

Pour l’exemple, le contexte mis en place dans cette application est d’une grande simplicité Il permet de stocker un nom et un prénom dans un objet User contenu dans un State. 

Pour la partie sur la mise en place du contexte dans Storybook, le contexte qui sera utilisé sera le même pour la partie Storybook et la partie production.

Comment se présente notre exemple ?  

L’application se présente sous la forme de 2 petites fenêtres accessibles via une barre de navigation.  

💡 React router est mis en place sur cette application, mais il ne sera pas détaillé dans ce tuto. Voir la documentation pour plus d’informations : https://reactrouter.com/

La première fenêtre permet d’envoyer via un bouton et un formulaire le nom et le prénom de l’utilisateur dans le context de l’application. 

À l’intérieur de cette page, il y a deux autres composants, un input et un composant bouton qui sont placés dans les dossiers atoms de l’application. 

const PageUser = (): ReactElement => { 
  const { setUser } = useContext(GlobalContext); 
  const [name, setName] = useState<string>(""); 
  const [surName, setSurName] = useState<string>(""); 
  return ( 
    <div> 
      <h2 className="text-white text-2xl">PageUser</h2> 
      <div> 
        <Input label="Name" data={name} fCallBack={(data) => setName(data)} /> 
        <Input 
          label="Surname" 
          data={surName} 
          fCallBack={(data) => setSurName(data)} 
        /> 
        <Button 
          label={"Submit"} 
          fCallBack={() => { 
            setUser({ name: name, surname: surName }); 
            setName(""); 
            setSurName(""); 
          }} 
        /> 
      </div> 
    </div> 
  ); 
}; 
export default PageUser; 

La 2 ème fenêtre permet de consulter le nom et le prénom qui sont enregistrés dans le contexte de l’application.

S’il n’y a pas d’information dans le context, les champs sont simplement laissés vides.

import { ReactElement, useContext } from "react"; 
import { GlobalContext } from "../../context/context"; 

const PageInfoUser = (): ReactElement => { 
  const { user } = useContext(GlobalContext); 
  return ( 
    <div className="text-white"> 
      <h2 className="text-2xl ">User Info</h2> 
      <div className=" w-60"> 
        <div className="flex"> 
          <p className="mr-2">Name : </p> 
          <p>{user?.name}</p> 
        </div> 
        <div className="flex"> 
          <p className="mr-2">SurName : </p> 
          <p>{user?.surname}</p> 
        </div> 
      </div> 
    </div> 
  ); 
}; 
export default PageInfoUser; 

2 - Installation de Storybook

Nous rentrons (enfin 😁) dans le vif du sujet ; nous allons procéder à l’installation de Storybook sur notre application. Pour cela, il faut taper la commande dans la console ciblant la racine de notre application :

npx sb init

Il s’agit de la commande pour pouvoir installer Storybook. Il n’y a pas besoin d’action supplémentaire pour l’installation de base, car Storybook va automatiquement détecter les paramètres de notre application React. Par exemple, si TypeScript est installé, il va générer des fichiers d’exemple et de configuration directement en TypeScript.

💡 Pour plus d’informations sur l’installation de Storybook, ou si vous rencontrez des difficultés, je vous invite à consulter la documentation. https://storybook.js.org/docs/react/get-started/install

Après l’installation de Storybook, vous allez pouvoir observer l’apparition, dans l’arborescence de votre application, de 2 fichiers supplémentaires.

Tout d’abord à la racine de l’application, vous allez trouver le fichier de config de Storybook  « .storybook ». Ce dossier va contenir plusieurs fichiers qui vont vous permettre de configurer l’environnement autour de vos stories, mais également Storybook en lui-même. Notamment pour lui rajouter des plugins, ou dans notre cas, mettre en place le contexte et pouvoir utiliser Tailwind css dans nos stories.

Ensuite, il y a un dossier dans le répertoire src de votre application : ce dossier est automatiquement généré par Storybook avec des composant React accompagnés de leurs stories et de leurs fichiers de style.

Le dossier Story n’est pas nécessaire pour le fonctionnement de Storybook. Il va inclure à sa liste de composants tous les dossiers qui vont avoir une extension .stories, peu importe où il se trouve dans le fichier src.

Extrait du fichier main.js, cette ligne permet de gérer la configuration :

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

Il y a plusieurs façons de construire une application utilisant Storybook au niveau de sa structure.

Tout d’abord, il est possible de se passer du dossier stories et construire sa bibliothèque de composants dans le dossier components en créant les composants dans des dossiers portant leurs noms.

Fichier du dossier du composant Button :

Ce dossier est construit avec un fichier .tsx et un .css , dans lequel on va construire le composant et son style.

De plus, on va y mettre le fichier stories et aussi rajouter un fichier index dans lequel on va importer les autres fichiers du dossier.

L’autre structure, que l’on va privilégier dans cet exemple, est de séparer la bibliothèque de composants et les fichiers .stories et les placer dans le dossier stories, pour une meilleure clarté au moment de la consultation du dossier stories. Il est conseillé de construire le dossier de la même manière que le dossier components a été construit.

💡 Vous pouvez trouver un exemple dans la capture d’écran présente plus haut.

Nous allons maintenant détailler la construction d’une stories pour le composant Input dans Storybook.

import { ComponentStory, Meta } from "@storybook/react"; 
import { useState } from "react"; 
 
// We import the component that will be used in the storie.  
import Input from "../../components/atoms/input";  
// This export allows you to configure the display name in the Storybook list
export default {  
  title: "Atoms/Input", 
  component: Input, 
  
// Using the Meta package simplifies the installation of stories 
} as Meta;  
const Template: ComponentStory<typeof Input> = (args) => { 

// The template is written in the same way as a React component, which allows adding logic. 
  const [data, setData] = useState<string>(""); 
  return ( 
    <div className="bg-gray-600 p-2"> // we can also add a decorator.
      <Input {...args} data={data} fCallBack={(data) => setData(data)} /> 
      
// The component that is generated by Storybook 
    </div> 
  ); 
}; 
// For a simple component, you could write the line this way:  
const Template: ComponentStory<typeof Input> = (args) => <Input />;  

Une fois qu’on a construit le template de la story, pour qu’elle soit utilisée par Storybook, il faut lui donner des états. Pour cela, il faut exporter une constante (ici Default) et la lier au template avec : 

Le nom de la constante sera utilisé par Storybook pour nommer l’état dans la liste des états du composant.

export const Default = Template.bind({}); 

Une fois que la constante a été liée au template, il faut définir des arguments à cette constante avec Default.args

Default.args = { 
  label: "label input", 
}; 

Cette zone permet de définir les Props d’entrée d’un composant dans Storybook, cette zone est aussi interprétée pour construire le panneau de contrôle en bas de la fenêtre dans Storybook. Elle permet alors de configurer à la volée les Props, pour effectuer des tests directement sans quitter l’application.

Bien sûr si le composant n’a pas de Props, on peut définir un champ argument vide.

3 - Installation de Tailwind CSS dans Storybook

Une fois que vous avez réalisé les étages au-dessus, vous pourrez lancer l’application Storybook, mais vous pourrez remarquer qu’il manque le style dans vos composants Storybook. C’est normal, car Storybook ne gère pas nativement les classes Tailwind css que vous pourrez mettre dans vos composants.

Cela est dû au fait que la génération du CSS par Storybook ne prend pas encore en charge Tailwind css. Pour pouvoir remédier à ça, il faut se rendre dans le fichier main.js.

module.exports = { 
  stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"], 
  addons: [ 
    "@storybook/addon-links", 
    "@storybook/addon-essentials", 
    "@storybook/preset-create-react-app", 
  ], 
  framework: "@storybook/react", 
  
  //here to add config 
}; 

Il faut rajouter en haut du fichier, une ligne en plus pour d’abord configurer une constante qui sera utilisée plus bas.

const path = require("path"); 

Puis ajouter une configuration webpack, pour préciser à Storybook de générer le CSS de Tailwind CSS.

webpackFinal: async (config) => { 
    config.module.rules.push({ 
      test: /\,css&/, 
      use: [ 
        { 
          loader: "postcss-loader", 
          options: { 
            ident: "postcss", 
            plugins: [require("tailwindcss"), require("autoprefixer")], 
          }, 
        }, 
      ], 
      include: path.resolve(__dirname, "../"), 
    }); 
    return config; 
  }, 

Une fois que c’est fait, il faut se rendre dans le fichier preview.js dans le même dossier que main.js, et y ajouter l’import du fichier .css qui contient les imports des classes Tailwind CSS.

import "../src/index.css"; 

export const parameters = { 
  actions: { argTypesRegex: "^on[A-Z].*" }, 
  controls: { 
    matchers: { 
      color: /(background|color)$/i, 
      date: /Date$/, 
    }, 
  }, 
}; 

Ici, il s’agit du fichier index.css 

@tailwind base; 
@tailwind components; 
@tailwind utilities; 

Une fois ces actions faites, vous pourrez admirer le résultat après avoir relancé votre application Storybook.

4 - Mise en place du Context React sur Storybook

Si vous avez besoin de faire communiquer des informations entre plusieurs composants à l’intérieur d’une même story, il peut être intéressant de mettre en place le contexte de React directement dans Storybook, par exemple pour éviter des erreurs dans le cas où certains composants auraient besoin de faire un appel à un contexte qui n’existerait pas dans le cadre de Storybook.

La mise en place du contexte dans Storybook est très simple. Il suffit de vous rendre dans le fichier preview.js et d’y ajouter un import pour référencer le contexte de votre application.

import GlobalContextProvider from "../src/context/context"; 

Si vous utilisez un autre contexte spécial pour Storybook, vous pouvez l’importer à cet endroit aussi.

Ensuite, il vous suffira de créer une constante nommée decorators comme ceci :

export const decorators = [ 
  (Story) => ( 
    <GlobalContextProvider> 
      <Story /> 
    </GlobalContextProvider> 
  ), 
]; 

Cette constante peut servir à une multitude de choses. Dans notre exemple, nous l’utilisons pour englober toutes les stories générées par Storybook avec le GlobalContextProvider, ce qui permet de donner accès au contexte à tous les composants enfants qui seront déclarés dans les stories.

Bien sûr, vous pouvez rajouter d’autres éléments parents à la balise . Ils seront alors interprétés de la même façon que du JSX dans un composant React. Vous pouvez par exemple y ajouter une div avec du style pour donner un contexte visuel à toutes vos stories.

export const decorators = [ 
  (Story) => ( 
    <GlobalContextProvider> 
      <div className="bg-red-800 text-3xl rounded-sm"> 
        <Story /> 
      </div>  
    </GlobalContextProvider> 
  ), 

La mise en place du contexte permet aussi la réalisation des stories pouvant représenter des pages entières ou plusieurs pages à la fois. 

Dans cet exemple, nous avons les deux pages de l’application qui sont réunies dans une seule Story.
Le contexte permet la communication entre ces deux pages de la même manière que si elles étaient directement générées par l’application.

Vous pouvez voir dans la capture ci-dessous que ces deux pages sont alors générées côte à côte et qu’elles fonctionnent de la même manière que sur l’application. 👇

Dans cet exemple, cette utilisation reste assez rudimentaire. Mais ça peut être très pratique dans le cadre d’une démonstration d’une partie de l’application directement dans Storybook. Cela peut également s’avérer utile dans le cas où vous voudriez partager publiquement votre Storybook, ce qui vous permettra de donner des exemples concrets de mise en place de vos composants dans une page.

Vous pouvez retrouver plusieurs exemples de cette utilisation directement sur Internet, car certaines entreprises mettent à disposition leur Storybook. D’ailleurs Storybook vous propose plusieurs de ces exemples via la documentation : https://storybook.js.org/docs/react/get-started/examples

5 - Ressources

Articles de blog similaires

Suivez-nous sur les réseaux sociaux

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.