feat(ui) : required cohérent + astérisque label + sanitisation email (MUI-41) (#60)
## Résumé (MUI-41) Harmonise l'état « obligatoire » des composants de formulaire et normalise le champ email. ### `required` + astérisque - Nouveau composant partagé `MalioRequiredMark` : astérisque rouge (`text-m-danger`, **16px**), `aria-hidden`. - Prop `required` désormais cohérente sur toute la famille formulaire ; quand vraie, l'astérisque s'affiche **dans le label**. - Prop ajoutée à `Select`, `SelectCheckbox`, `InputUpload`, `InputRichText` (les autres l'avaient déjà). - Accessibilité : `required` natif là où l'élément le supporte, sinon `aria-required` (Select/SelectCheckbox sur le `<button>`, RichText sur le wrapper éditeur, Upload sur le champ visible). - `MalioSiteSelector` **exclu** volontairement (segmented control, pas de label de champ). ### Sanitisation email (`MalioInputEmail`) - Suppression de **tous les espaces** à la saisie (pas de masque). - Nouvelle prop opt-in `lowercase` (défaut `false`) : normalise en minuscules à la frappe (cohérent RG-1.21 Starseed). - Garde défensive curseur : l'API de sélection est interdite sur `type="email"` → repositionnement best-effort sans jamais lever. - La validation de format reste à la couche `error`. ### Docs & playground - `COMPONENTS.md` (doc `required` cohérente + note famille + `lowercase`) et `CHANGELOG.md` mis à jour. - Exemples playground `required` et email `lowercase` ajoutés. ## Test plan - [x] Suite complète : 42 fichiers / 771 tests verts - [x] Lint : 0 erreur - [x] Tests `aria-required` sur Select/SelectCheckbox/RichText - [ ] Vérif visuelle playground : astérisque 16px dans le label, email qui retire les espaces / minuscule Spec & plan : `docs/superpowers/specs/` et `docs/superpowers/plans/`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Reviewed-on: #60 Co-authored-by: tristan <tristan@yuno.malio.fr> Co-committed-by: tristan <tristan@yuno.malio.fr>
This commit was merged in pull request #60.
This commit is contained in:
@@ -82,6 +82,17 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="rounded-lg border p-4">
|
||||
<h2 class="mb-4 text-xl font-bold">Sélection obligatoire</h2>
|
||||
<MalioSelect
|
||||
v-model="requiredValue"
|
||||
:options="options"
|
||||
label="Sélection obligatoire"
|
||||
:required="true"
|
||||
empty-option-label="Aucune selection"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="rounded-lg border p-4">
|
||||
<h2 class="mb-4 text-xl font-bold">Peu d'elements (2)</h2>
|
||||
<MalioSelect
|
||||
@@ -92,6 +103,28 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="rounded-lg border p-4">
|
||||
<h2 class="mb-4 text-xl font-bold">Lecture seule (vide)</h2>
|
||||
<MalioSelect
|
||||
v-model="readonlyEmptyValue"
|
||||
:options="options"
|
||||
label="Pays"
|
||||
empty-option-label="Aucune selection"
|
||||
:readonly="true"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="rounded-lg border p-4">
|
||||
<h2 class="mb-4 text-xl font-bold">Lecture seule (rempli)</h2>
|
||||
<MalioSelect
|
||||
v-model="readonlyFilledValue"
|
||||
:options="options"
|
||||
label="Pays"
|
||||
empty-option-label="Aucune selection"
|
||||
:readonly="true"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="rounded-lg border p-4 md:col-span-2">
|
||||
<h2 class="mb-4 text-xl font-bold">Liste longue</h2>
|
||||
<MalioSelect
|
||||
@@ -151,6 +184,7 @@ const longOptions = [
|
||||
{label: 'Republique tcheque', value: 'cz'},
|
||||
]
|
||||
|
||||
const requiredValue = ref<string | number | null>(null)
|
||||
const basicValue = ref<string | number | null>(null)
|
||||
const labelValue = ref<string | number | null>(null)
|
||||
const selectedValue = ref<string | number | null>('fr')
|
||||
@@ -162,4 +196,6 @@ const emptyValue = ref<string | number | null>(null)
|
||||
const shortListValue = ref<string | number | null>(null)
|
||||
const longListValue = ref<string | number | null>(null)
|
||||
const bottomValue = ref<string | number | null>(null)
|
||||
const readonlyEmptyValue = ref<string | number | null>(null)
|
||||
const readonlyFilledValue = ref<string | number | null>('fr')
|
||||
</script>
|
||||
|
||||
@@ -123,6 +123,28 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="rounded-lg border p-4">
|
||||
<h2 class="mb-4 text-xl font-bold">Lecture seule (vide)</h2>
|
||||
<MalioSelectCheckbox
|
||||
v-model="readonlyEmptyValue"
|
||||
:options="options"
|
||||
label="Pays"
|
||||
empty-option-label="Aucune selection"
|
||||
:readonly="true"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="rounded-lg border p-4">
|
||||
<h2 class="mb-4 text-xl font-bold">Lecture seule (rempli)</h2>
|
||||
<MalioSelectCheckbox
|
||||
v-model="readonlyFilledValue"
|
||||
:options="options"
|
||||
label="Pays"
|
||||
empty-option-label="Aucune selection"
|
||||
:readonly="true"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="rounded-lg border p-4 md:col-span-2">
|
||||
<h2 class="mb-4 text-xl font-bold">Liste longue</h2>
|
||||
<MalioSelectCheckbox
|
||||
@@ -145,6 +167,7 @@
|
||||
empty-option-label="Aucune selection"
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -190,4 +213,6 @@ const selectAllValue = ref<Array<string | number>>([])
|
||||
const selectAllCustomValue = ref<Array<string | number>>([])
|
||||
const longListValue = ref<Array<string | number>>([])
|
||||
const bottomValue = ref<Array<string | number>>([])
|
||||
const readonlyEmptyValue = ref<Array<string | number>>([])
|
||||
const readonlyFilledValue = ref<Array<string | number>>(['fr'])
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user