| Numéro du ticket | Titre du ticket | |------------------|-----------------| | | | ## Description de la PR ## Modification du .env ## Check list - [ ] Pas de régression - [ ] TU/TI/TF rédigée - [ ] TU/TI/TF OK - [ ] CHANGELOG modifié Reviewed-on: #20 Co-authored-by: tristan <tristan@yuno.malio.fr> Co-committed-by: tristan <tristan@yuno.malio.fr>
8.1 KiB
name, description
| name | description |
|---|---|
| creating-malio-component | Use when creating a new UI component in the @malio/layer-ui Nuxt layer — covers component, tests, playground page, and Histoire story |
Creating a Malio Component
Overview
Step-by-step process for creating a component in @malio/layer-ui. Each component requires 6 deliverables : le .vue, les tests, la page playground, la story Histoire, la mise à jour du CHANGELOG, et la mise à jour du COMPONENTS.md.
When to Use
- Création d'un nouveau composant dans
app/components/malio/ - Ajout d'une variante d'un composant existant (ex: InputPassword basé sur InputText)
Workflow
digraph create_component {
rankdir=TB;
"1. Lire les fichiers de référence" -> "2. Créer le composant .vue";
"2. Créer le composant .vue" -> "3. Créer les tests .test.ts";
"3. Créer les tests .test.ts" -> "4. npm run test + npm run lint";
"4. npm run test + npm run lint" -> "Tests OK?" [shape=diamond];
"Tests OK?" -> "5. Créer la page playground" [label="oui"];
"Tests OK?" -> "3. Créer les tests .test.ts" [label="non, corriger"];
"5. Créer la page playground" -> "6. Créer la story Histoire";
"6. Créer la story Histoire" -> "7. Mettre à jour CHANGELOG.md";
"7. Mettre à jour CHANGELOG.md" -> "8. Mettre à jour COMPONENTS.md";
}
Étapes
1. Lire les fichiers de référence
Identifier le composant le plus proche comme base (ex: InputText.vue pour InputPassword.vue). Lire :
- Le composant de référence :
app/components/malio/<Ref>.vue - Ses tests :
app/components/malio/<Ref>.test.ts
2. Créer le composant .vue
Fichier : app/components/malio/<NomComposant>.vue
Checklist obligatoire :
| Élément | Pattern |
|---|---|
defineOptions |
{ name: 'Malio<Nom>', inheritAttrs: false } |
| Props | defineProps<T>() + withDefaults() — props communes : id, label, modelValue, inputClass, labelClass, groupClass, disabled, readonly, hint, error, success |
| Contrôlé / non-contrôlé | isControlled = computed(() => props.modelValue !== undefined) + localValue en fallback |
| Classes CSS | Fusionnées via twMerge() pour permettre l'override consommateur |
| Accessibilité | aria-invalid, aria-describedby, label[for] lié à input[id] |
| Icônes | Icon as IconifyIcon depuis @iconify/vue (pas @nuxt/icon) |
| ID généré | useId() + prefix unique (ex: malio-input-password-${generatedId}) |
3. Créer les tests .test.ts
Fichier : app/components/malio/<NomComposant>.test.ts (colocalisé)
Pattern de montage :
import { mount } from '@vue/test-utils'
import type { DefineComponent } from 'vue'
import MonComposant from './MonComposant.vue'
const ComposantForTest = MonComposant as DefineComponent<MonComposantProps>
const mountComponent = (props: MonComposantProps = {}) =>
mount(ComposantForTest, {
props,
global: {
stubs: {
IconifyIcon: {
template: '<span data-test="icon" v-bind="$attrs" />',
},
},
},
})
Tests minimum à couvrir :
- Rendu initial avec valeur
- Rendu du label
- Emit
update:modelValue - Props
disabled,readonly - États
error,success,hint(messages + classes CSS) - Accessibilité (
aria-invalid,label[for]/input[id]) - Comportements spécifiques au composant
Attention stub IconifyIcon : Le stub basé sur le nom IconifyIcon ne remplace pas toujours le vrai composant @iconify/vue. Pour tester les props du composant Icon (ex: icon), utiliser findComponent avec l'import réel :
import { Icon as IconifyIcon } from '@iconify/vue'
// ...
const iconComponent = wrapper.findComponent(IconifyIcon)
expect(iconComponent.props('icon')).toBe('mdi:eye-outline')
4. Vérification
npm run test # Tous les tests passent
npm run lint # Pas d'erreurs
5. Créer la page playground
Fichier : .playground/pages/composant/<nomComposant>.vue (camelCase)
La page est auto-détectée par index.vue via import.meta.glob. Inclure des variantes représentatives dans une grille :
<div class="grid grid-cols-1 items-start gap-6 md:grid-cols-2">
<div class="rounded-lg border p-4">
<h2 class="mb-4 text-xl font-bold">Titre variante</h2>
<MalioMonComposant ... />
</div>
</div>
Variantes typiques : simple, avec label, désactivé, readonly, hint, erreur, succès, validation dynamique.
6. Créer la story Histoire
Fichier : app/story/<nomComposant>.story.vue (camelCase)
Structure :
<template>
<Story title="Category/Name">
<!-- Variantes avec v-model et valeurs initiales -->
</Story>
</template>
<docs lang="md">
# MalioNomComposant
Description courte.
## Props détaillées
<!-- Documenter chaque prop : type, description, défaut, comportement -->
## Comportement
## Accessibilité
## Events
</docs>
<script setup lang="ts">
import { ref } from 'vue'
import MalioMonComposant from '../components/malio/MonComposant.vue'
// refs pour chaque variante avec valeurs initiales
</script>
Important : initial state avec variantes. La story doit contenir des exemples visuels directement visibles (pas un composant vide). Chaque variante a un v-model avec une ref initialisée. Variantes typiques à inclure :
- Simple (avec label)
- Sans icône (
display-icon="false") si applicable - Avec hint
- Désactivé (avec valeur pré-remplie)
- Readonly (avec valeur pré-remplie)
- Erreur (avec valeur + message d'erreur)
- Succès (avec valeur + message de succès)
7. Mettre à jour le CHANGELOG
Fichier : CHANGELOG.md à la racine du projet.
Ajouter une ligne dans la section ### Added de la version courante. Le numéro de ticket se trouve dans le nom de la branche Git (ex: branche feat/MUI-8-composant-password → ticket MUI-8).
Format :
- Avec numéro de ticket :
* [#MUI-8] Création d'un composant mot de passe - Sans numéro de ticket :
* Création d'un composant textarea
Pour extraire le numéro de ticket depuis la branche courante :
git branch --show-current | grep -oP '(MUI-\d+|\d{3,})' | head -1
8. Mettre à jour COMPONENTS.md
Fichier : COMPONENTS.md à la racine du projet.
Ce fichier sert de documentation de référence pour les projets qui consomment @malio/layer-ui. Il est lu par Claude dans les projets consommateurs pour connaître les composants disponibles et leurs props.
Ajouter une section pour le nouveau composant en suivant le format existant :
## MalioNomComposant
Description courte du composant.
| Prop | Type | Défaut | Description |
|------|------|--------|-------------|
| ... | ... | ... | ... |
**Events :** `update:modelValue(value: string)`
\`\`\`vue
<MalioNomComposant v-model="val" label="Exemple" />
\`\`\`
Checklist :
- Toutes les props documentées avec type, défaut et description
- Events listés
- Slots listés si applicable
- 2-5 exemples d'utilisation couvrant les cas courants (simple, avec options, disabled, erreur)
- Section placée par ordre logique (inputs ensemble, boutons ensemble, etc.)
Common Mistakes
Cette section est alimentée au fur et à mesure des retours utilisateur et des problèmes rencontrés. Si un retour ou un bug est identifié lors de la création d'un composant, ajouter une ligne dans ce tableau.
| Erreur | Solution |
|---|---|
| Stub IconifyIcon ne fonctionne pas dans les tests | Utiliser findComponent(IconifyIcon) avec l'import réel pour tester les props |
Oubli de inheritAttrs: false |
Toujours dans defineOptions — sinon les attrs se dupliquent |
| Page playground non détectée | Vérifier le nom du fichier en camelCase dans .playground/pages/composant/ |
| Padding input pas ajusté avec icône | Ajouter !pr-10 (ou équivalent) quand une icône est présente à droite |
| Story sans initial state | Toujours initialiser les ref avec des valeurs pour que les variantes soient visibles dès le chargement |
| CHANGELOG oublié | Toujours ajouter la ligne dans ### Added avant de commit |
| COMPONENTS.md pas mis à jour | Ajouter la doc du composant dans COMPONENTS.md — c'est la référence pour les projets consommateurs |