From 32608c8f7174e8ab4530d64800f04b6b4929e913 Mon Sep 17 00:00:00 2001 From: tristan Date: Tue, 7 Apr 2026 14:30:06 +0200 Subject: [PATCH] fix : suppression du doublon du composant Checkbox --- .playground/pages/composant/checkbox.vue | 101 -------- app/components/malio/Checkbox.test.ts | 142 ----------- app/components/malio/Checkbox.vue | 227 ------------------ .../malio/checkbox/Checkbox.test.ts | 4 +- app/components/malio/checkbox/Checkbox.vue | 16 +- app/story/inputCheckbox.story.vue | 114 --------- 6 files changed, 10 insertions(+), 594 deletions(-) delete mode 100644 .playground/pages/composant/checkbox.vue delete mode 100644 app/components/malio/Checkbox.test.ts delete mode 100644 app/components/malio/Checkbox.vue delete mode 100644 app/story/inputCheckbox.story.vue diff --git a/.playground/pages/composant/checkbox.vue b/.playground/pages/composant/checkbox.vue deleted file mode 100644 index 211328c..0000000 --- a/.playground/pages/composant/checkbox.vue +++ /dev/null @@ -1,101 +0,0 @@ - - - diff --git a/app/components/malio/Checkbox.test.ts b/app/components/malio/Checkbox.test.ts deleted file mode 100644 index 6fccf55..0000000 --- a/app/components/malio/Checkbox.test.ts +++ /dev/null @@ -1,142 +0,0 @@ -import {describe, expect, it} from 'vitest' -import {mount} from '@vue/test-utils' -import type {DefineComponent} from 'vue' -import Checkbox from './Checkbox.vue' - -type CheckboxProps = { - id?: string - label?: string - name?: string - modelValue?: boolean | null - inputClass?: string - labelClass?: string - groupClass?: string - required?: boolean - disabled?: boolean - readonly?: boolean - hint?: string - error?: string - success?: string -} - -const CheckboxForTest = Checkbox as DefineComponent - -const mountCheckbox = (props: CheckboxProps = {}) => - mount(CheckboxForTest, {props}) - -describe('MalioCheckbox', () => { - it('renders a checkbox input', () => { - const wrapper = mountCheckbox() - - expect(wrapper.get('input').attributes('type')).toBe('checkbox') - }) - - it('renders the label text', () => { - const wrapper = mountCheckbox({label: 'Accept terms'}) - - expect(wrapper.get('label').text()).toContain('Accept terms') - }) - - it('uses a provided id on input and label', () => { - const wrapper = mountCheckbox({ - id: 'checkbox-id', - label: 'Accept terms', - }) - - expect(wrapper.get('input').attributes('id')).toBe('checkbox-id') - expect(wrapper.get('label').attributes('for')).toBe('checkbox-id') - }) - - it('generates an id when none is provided', () => { - const wrapper = mountCheckbox({label: 'Accept terms'}) - const inputId = wrapper.get('input').attributes('id') - - expect(inputId?.startsWith('malio-checkbox-')).toBe(true) - expect(wrapper.get('label').attributes('for')).toBe(inputId) - }) - - it('applies the name attribute', () => { - const wrapper = mountCheckbox({name: 'terms'}) - - expect(wrapper.get('input').attributes('name')).toBe('terms') - }) - - it('reflects the checked state from modelValue', () => { - const wrapper = mountCheckbox({modelValue: true}) - - expect((wrapper.get('input').element as HTMLInputElement).checked).toBe(true) - }) - - it('emits update:modelValue when toggled', async () => { - const wrapper = mountCheckbox({modelValue: false}) - const input = wrapper.get('input') - - await input.setValue(true) - - expect(wrapper.emitted('update:modelValue')?.[0]).toEqual([true]) - }) - - it('does not emit when readonly', async () => { - const wrapper = mountCheckbox({ - modelValue: true, - readonly: true, - }) - const input = wrapper.get('input') - - await input.setValue(false) - - expect(wrapper.emitted('update:modelValue')).toBeUndefined() - expect((input.element as HTMLInputElement).checked).toBe(true) - }) - - it('sets disabled and required attributes', () => { - const wrapper = mountCheckbox({ - disabled: true, - required: true, - }) - - expect(wrapper.get('input').attributes('disabled')).toBeDefined() - expect(wrapper.get('input').attributes('required')).toBeDefined() - }) - - it('shows a hint message and links it with aria-describedby', () => { - const wrapper = mountCheckbox({hint: 'Required field'}) - const inputId = wrapper.get('input').attributes('id') - - expect(wrapper.get('p').text()).toBe('Required field') - expect(wrapper.get('input').attributes('aria-describedby')).toBe(`${inputId}-describedby`) - }) - - it('shows an error state and message', () => { - const wrapper = mountCheckbox({ - label: 'Accept terms', - error: 'You must accept', - }) - - expect(wrapper.get('input').attributes('aria-invalid')).toBe('true') - expect(wrapper.get('label').classes()).toContain('text-m-error') - expect(wrapper.get('p').text()).toBe('You must accept') - }) - - it('shows success only when there is no error', () => { - const wrapper = mountCheckbox({ - success: 'Valid', - error: 'Invalid', - }) - - expect(wrapper.get('p').text()).toBe('Invalid') - expect(wrapper.get('p').classes()).toContain('text-m-error') - }) - - it('shows success styles and message when there is no error', () => { - const wrapper = mountCheckbox({ - label: 'Accept terms', - success: 'Valid', - modelValue: true, - }) - - expect(wrapper.get('label').classes()).toContain('text-m-success') - expect(wrapper.get('p').text()).toBe('Valid') - expect(wrapper.get('p').classes()).toContain('text-m-success') - }) -}) diff --git a/app/components/malio/Checkbox.vue b/app/components/malio/Checkbox.vue deleted file mode 100644 index dac4e76..0000000 --- a/app/components/malio/Checkbox.vue +++ /dev/null @@ -1,227 +0,0 @@ - - - - - diff --git a/app/components/malio/checkbox/Checkbox.test.ts b/app/components/malio/checkbox/Checkbox.test.ts index 9915c01..6fccf55 100644 --- a/app/components/malio/checkbox/Checkbox.test.ts +++ b/app/components/malio/checkbox/Checkbox.test.ts @@ -114,7 +114,7 @@ describe('MalioCheckbox', () => { }) expect(wrapper.get('input').attributes('aria-invalid')).toBe('true') - expect(wrapper.get('label').classes()).toContain('text-m-danger') + expect(wrapper.get('label').classes()).toContain('text-m-error') expect(wrapper.get('p').text()).toBe('You must accept') }) @@ -125,7 +125,7 @@ describe('MalioCheckbox', () => { }) expect(wrapper.get('p').text()).toBe('Invalid') - expect(wrapper.get('p').classes()).toContain('text-m-danger') + expect(wrapper.get('p').classes()).toContain('text-m-error') }) it('shows success styles and message when there is no error', () => { diff --git a/app/components/malio/checkbox/Checkbox.vue b/app/components/malio/checkbox/Checkbox.vue index f4a3a43..dac4e76 100644 --- a/app/components/malio/checkbox/Checkbox.vue +++ b/app/components/malio/checkbox/Checkbox.vue @@ -110,7 +110,7 @@ const mergedLabelClass = computed(() => twMerge( 'cbx text-black', disabled.value ? 'cursor-not-allowed text-black/60' : '', - hasError.value ? 'text-m-danger' : '', + hasError.value ? 'text-m-error' : '', hasSuccess.value ? 'text-m-success' : '', props.labelClass, ), @@ -120,7 +120,7 @@ const mergedMessageClass = computed(() => twMerge( 'text-xs', hasError.value - ? 'text-m-danger' + ? 'text-m-error' : hasSuccess.value ? 'text-m-success' : 'text-m-muted', @@ -200,14 +200,14 @@ const onChange = (event: Event) => { stroke-dashoffset: 0; } -.inp-cbx + .cbx.text-m-danger span:first-child { - border-color: rgb(var(--m-danger) / 1); +.inp-cbx + .cbx.text-m-error span:first-child { + border-color: rgb(var(--m-error) / 1); } -.cbx.text-m-danger span:first-child svg { - stroke: rgb(var(--m-danger) / 1); +.cbx.text-m-error span:first-child svg { + stroke: rgb(var(--m-error) / 1); } -.inp-cbx:checked + .cbx.text-m-danger span:first-child { - border-color: rgb(var(--m-danger) / 1); +.inp-cbx:checked + .cbx.text-m-error span:first-child { + border-color: rgb(var(--m-error) / 1); } .inp-cbx + .cbx.text-m-success span:first-child { diff --git a/app/story/inputCheckbox.story.vue b/app/story/inputCheckbox.story.vue deleted file mode 100644 index 10d3667..0000000 --- a/app/story/inputCheckbox.story.vue +++ /dev/null @@ -1,114 +0,0 @@ - - - -# MalioCheckbox - -Composant checkbox custom avec `v-model`, message d'aide, et états visuels -`error` / `success`. - ------------------------------------------------------------------------- - -## Props - -### id - -- Type: `string` -- Description: Identifiant HTML du checkbox. -- Comportement: si absent, un id unique est généré automatiquement. - -### label - -- Type: `string` -- Description: Texte affiche a cote de la case. - -### name - -- Type: `string` -- Description: Attribut `name` du champ. - -### modelValue - -- Type: `boolean | null | undefined` -- Description: État coche du composant. - -### inputClass - -- Type: `string` -- Description: Classes supplémentaires appliquées a l'input natif. - -### labelClass - -- Type: `string` -- Description: Classes supplémentaires appliquées au label. - -### groupClass - -- Type: `string` -- Description: Classes supplémentaires appliquées au conteneur. - -### required - -- Type: `boolean` -- Description: Ajoute l'attribut HTML `required`. - -### disabled - -- Type: `boolean` -- Description: Désactive le composant. - -### readonly - -- Type: `boolean` -- Description: Empêche la mise a jour du `v-model` tout en gardant - l'affichage courant. - -### hint - -- Type: `string` -- Description: Message d'aide affiche sous le checkbox. - -### error - -- Type: `string` -- Description: Message d'erreur. -- Effet: prioritaire sur `success`, applique `aria-invalid` et la couleur - d'erreur au texte et a la case. - -### success - -- Type: `string` -- Description: Message de succès. -- Effet: applique la couleur de succès au texte et a la case si `error` - est absent. - ------------------------------------------------------------------------- - -## Accessibilité - -- `aria-invalid` est active si `error` existe. -- `aria-describedby` pointe vers le message affiche. -- L'input natif reste present pour conserver le comportement formulaire. - ------------------------------------------------------------------------- - -## Event - -### update:modelValue - -- Émis a chaque changement de l'état coche. -- Retourne un booléen `true` ou `false`. - - -