Introduction
Dans l’article 1 de la série consacrée au Design System + NPM Workspaces, nous avons vu comment créer le monorepo contenant les sources de notre premier composant.
Dans cet article, nous allons voir comment ajouter d’autres composants et comment publier nos composants sur un serveur gestionnaire de packages NPM.
Création d’un second composant
Le second composant de notre Design System sera un Toaster (https://codeseven.github.io/toastr/demo.html). Ce composant sera utilisé pour afficher des messages aux utilisateurs.
Comme pour le premier composant ilb-button, nous utiliserons le template Lit + TypeScript légèrement modifié. Vous pouvez donc dupliquer le premier composant et renommer le dossier ilb-button copy en ilb-toaster.
Modifier le fichier dev/index.html
avec le code suivant :
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Toaster Component</title> <script type="module" src="./index.js"></script> </head> <body> <button id="btn">Click Me to show toast !!</button> <ilb-toaster id="toaster" text="My custom message"></ilb-toaster> <script> const toaster = document.getElementById('toaster'); const button = document.getElementById('btn'); button.addEventListener('click', (e) => { toaster.setAttribute('showToast', true); }); </script> </body> </html>
Supprimer le dossier node_modules
, nous ferons une nouvelle installation des dépendances une fois le composant prêt.
Modifier le fichier src/index.scss
avec le code suivant :
@import url(https://fonts.googleapis.com/css?family=Roboto:400,100,900); //colors $red: #E1332D; $white: #fff; $black: #000; .toaster { position: fixed; right: 1rem; top: 1rem; background: $red; padding: 20px; border-radius: 8px; opacity: 0.8; box-shadow: 1px 1px 6px rgba(0, 0, 0, 0.6); color: $black; &:hover { opacity: 1; } }
Ainsi que le fichier src/index.ts
, les commentaires vous renseignent sur les propriétés et la méthode de rendu :
import {LitElement, html, PropertyValues} from 'lit'; import {customElement, property} from 'lit/decorators.js'; import styles from './index.scss'; @customElement('ilb-toaster') export class IlbToaster extends LitElement { @property() text: String = ''; // Message à afficher dans le toast @property() timer: number = 3000; // Durée d'affichage du toast @property() showToast: boolean = false; // Affichage du toast static get styles(): any { return [styles]; } // Lance un timeout pour afficher le toast sur la durée souhaitée une fois la propriété showToast modifiée protected updated(changedProperties: PropertyValues): void { if (changedProperties.has('showToast')) { this.toasterTimer(); } } toasterTimer() { setTimeout(() => { this.showToast = false }, this.timer); } // La méthode de rendu du composant Toaster render() { return this.showToast ? html` <div class="toaster"> ${this.text} </div> `: ''; } }
Pour des raisons de simplicité, nous passons le message en propriété du composant, mais nous aurions également pu le passer via le slot pour laisser plus de possibilités de personnalisations à l’utilisateur de notre composant.
À la racine du package ilb-toaster, modifier les fichiers suivants :
src/customElement.json
{ "version": "experimental", "tags": [ { "name": "ilb-toaster", "path": "./src/index.ts", "description": "Toaster Design System", "properties": [ { "name": "styles", "type": "CSSResult", "default": "\"css`\\n .btn {\\n background: red\\n }\\n `\"" } ], "slots": [ { "name": "", "description": "This element has a slot" } ] } ] }
package.json
{ "name": "ilb-toaster", "version": "0.0.0", "description": "Toaster webComponent", "keywords": [ "toaster", "web-components", "lit-element", "typescript" ], "license": "ISC", "main": "dist/index.js", "unpkg": "dist/index.js", "type": "module", "types": "dist/index.d.ts", "files": [ "dist" ], "scripts": { "build": "rollup -c ../../rollup.config.js", "serve": "rollup -w -c ../../rollup.config.dev.js", "checksize": "rollup -c ../../rollup.config.js ; cat dist/index.js | gzip -9 | wc -c ; rm dist/index.js", "prepublish": "rollup -c ../../rollup.config.js", "test": "web-test-runner ./src/**/*.spec.ts --node-resolve", "test:watch": "web-test-runner ./src/**/*.spec.ts --node-resolve --watch", "lint": "eslint ./src --ext ts" }, "devDependencies": { "node-sass": "^7.0.1", "rollup": "^2.74.1", "sass": "^1.52.1" }, "dependencies": { "lit": "^2.0.2" } }
Les fichiers tsconfig.json
, web-dev-server.config.js
et web-test-runner.config.js
restent identiques.
Nous pouvons maintenant exécuter l’installation des dépendances via la commande :
npm i --workspace=ilb-toaster // permet de spécifier le package ou s'éxecute la commande d'installation // ou npm i --workspaces // pour éxecuter la commande sur tous les packages
Et enfin lancer le serveur de développement avec la commande :
npm run --workspace=ilb-toaster serve
Le second composant fonctionne :
Maintenant que notre Design System comporte deux composants, il faut publier ces derniers sur un gestionnaire de packages afin de pouvoir les consommer dans une application ou dans un autre composant du Design System.
Publication des composants
Dans un premier temps nous allons utiliser une image Docker (https://hub.docker.com/r/verdaccio/verdaccio/) pour installer une registry npm localement.
Pour cela, lancer votre client Docker et télécharger l’image via la commande :
docker pull verdaccio/verdaccio
Une fois le téléchargement terminé, lancer l’image via la commande :
docker run -it --rm --name verdaccio -p 4873:4873 verdaccio/verdaccio
Il faut maintenant créer un utilisateur dans un autre terminal grâce à la commande :
npm adduser --registry http://0.0.0.0:4873/
Il suffit alors de suivre le prompt et d’entrer les informations souhaitées, dans notre cas :
Username: admin Password: admin
Il faut maintenant mettre à jour la registry sur nos packages du Design System en ajoutant la clé sur chaque fichier package.json
pour ajouter l’URL par default de la registry locale
... "publishConfig": { "registry": "http://0.0.0.0:4873/" }, ...
Pour rendre la publication plus facile, nous allons créer un script qui va prendre en paramètres les nom du package que nous souhaitons publier : à la racine du projet, créer un dossier cli, ce dossier nous servira plus tard pour créer une CLI afin de faciliter la création de nouveaux composants.
Dans le dossier cli
, créer un nouveau fichier publish.js
:
const { execSync } = require('child_process'); const minimist = require('minimist'); const argv = minimist(process.argv.slice(2)); // Utilisation de minimist pour récupérer les arguments passés dans la ligne de commande const { version, component } = argv; // Récupération de la version et du composant à publier console.log('version', version); // Log pour avoir la version dans la console console.log('component', component); // Log pour avoir le composant dans la console execSync(`npm version ${version} --workspace=${component}`); // Met a jour la version dans le fichier package.json du package execSync(`npm publish --workspace=${component} --registry http://localhost:4873/`); // Publie le package sur la registry locale
Pour ce script, nous avons besoin de la librairie minimist que l’on va installer en dépendance de développement au moyen de la commande :
npm install -D minimist
Une fois le script créé, il faut builder le composant et le publier au moyen des commandes suivantes :
npm run --workspace=ilb-button build
Modifier l’attribut “scripts”, à la racine du projet dans votre fichier package.json
:
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "publish:major": "node ./cli/publish --version=major", "publish:minor": "node ./cli/publish --version=minor", "publish:patch": "node ./cli/publish --version=patch" }
Puis :
npm run publish:major -- --component=ilb-button
En sortie de console, nous devons avoir :
On vérifie maintenant que le package NPM est bien publié sur la registry :
Et voilà, vous pouvez faire de même pour notre second package “toaster”.
Conclusion
Nous avons maintenant notre monorepo qui contient deux composants que nous sommes capables de publier sur une registry NPM. Dans la suite de cette série d’articles, nous verrons comment :
- Comment créer un CLI pour faciliter la vie vos développeurs lors de la création de composants
- Comment un composant peut avoir un autre composant en dépendance.
- Comment consommer nos composants dans une application Angular / React / JavaScript.
- Comment documenter notre Design System.
Série d’articles Design System :
- Partie 1 : Installation du monorepo et création du premier composant
- Partie 2 : Création et publication de composants