diff --git a/app/components/malio/checkbox/Checkbox.test.ts b/app/components/malio/checkbox/Checkbox.test.ts index c291ced..579c88b 100644 --- a/app/components/malio/checkbox/Checkbox.test.ts +++ b/app/components/malio/checkbox/Checkbox.test.ts @@ -17,6 +17,7 @@ type CheckboxProps = { hint?: string error?: string success?: string + reserveMessageSpace?: boolean } const CheckboxForTest = Checkbox as DefineComponent @@ -171,4 +172,23 @@ describe('MalioCheckbox', () => { const wrapper = mountCheckbox({label: 'Champ'}) expect(wrapper.find('[data-test="required-mark"]').exists()).toBe(false) }) + + it('réserve l’espace message par défaut même sans message', () => { + const wrapper = mountCheckbox({label: 'Champ'}) + const msg = wrapper.find('[id$="-describedby"]') + expect(msg.exists()).toBe(true) + expect(msg.classes()).toContain('min-h-[1rem]') + }) + + it('reserveMessageSpace=false sans message : pas de ligne réservée', () => { + const wrapper = mountCheckbox({label: 'Champ', reserveMessageSpace: false}) + expect(wrapper.find('[id$="-describedby"]').exists()).toBe(false) + }) + + it('reserveMessageSpace=false avec message : ligne rendue sans min-h', () => { + const wrapper = mountCheckbox({label: 'Champ', reserveMessageSpace: false, error: 'Erreur'}) + const msg = wrapper.find('[id$="-describedby"]') + expect(msg.exists()).toBe(true) + expect(msg.classes()).not.toContain('min-h-[1rem]') + }) }) diff --git a/app/components/malio/checkbox/Checkbox.vue b/app/components/malio/checkbox/Checkbox.vue index b4de360..73ba5eb 100644 --- a/app/components/malio/checkbox/Checkbox.vue +++ b/app/components/malio/checkbox/Checkbox.vue @@ -30,6 +30,7 @@

@@ -60,6 +61,7 @@ const props = withDefaults( hint?: string error?: string success?: string + reserveMessageSpace?: boolean }>(), { id: '', @@ -75,6 +77,7 @@ const props = withDefaults( hint: '', error: '', success: '', + reserveMessageSpace: true, }, ) @@ -121,7 +124,8 @@ const mergedLabelClass = computed(() => const mergedMessageClass = computed(() => twMerge( - 'text-xs min-h-[1rem]', + 'text-xs', + props.reserveMessageSpace ? 'min-h-[1rem]' : '', hasError.value ? 'text-m-danger' : hasSuccess.value diff --git a/app/components/malio/date/Date.test.ts b/app/components/malio/date/Date.test.ts index 25c6f30..e10dbb9 100644 --- a/app/components/malio/date/Date.test.ts +++ b/app/components/malio/date/Date.test.ts @@ -21,6 +21,7 @@ type DateProps = { inputClass?: string labelClass?: string groupClass?: string + reserveMessageSpace?: boolean } const DateForTest = Date_ as DefineComponent @@ -236,4 +237,25 @@ describe('MalioDate', () => { expect(input.value).toBe('25/12/2026') }) }) + + describe('reserveMessageSpace', () => { + it('réserve l’espace message par défaut même sans message', () => { + const wrapper = mountDate({label: 'Champ'}) + const msg = wrapper.find('[id$="-describedby"]') + expect(msg.exists()).toBe(true) + expect(msg.classes()).toContain('min-h-[1rem]') + }) + + it('reserveMessageSpace=false sans message : pas de ligne réservée', () => { + const wrapper = mountDate({label: 'Champ', reserveMessageSpace: false}) + expect(wrapper.find('[id$="-describedby"]').exists()).toBe(false) + }) + + it('reserveMessageSpace=false avec message : ligne rendue sans min-h', () => { + const wrapper = mountDate({label: 'Champ', reserveMessageSpace: false, error: 'Erreur'}) + const msg = wrapper.find('[id$="-describedby"]') + expect(msg.exists()).toBe(true) + expect(msg.classes()).not.toContain('min-h-[1rem]') + }) + }) }) diff --git a/app/components/malio/date/internal/CalendarField.vue b/app/components/malio/date/internal/CalendarField.vue index 4f50dfe..35d02e1 100644 --- a/app/components/malio/date/internal/CalendarField.vue +++ b/app/components/malio/date/internal/CalendarField.vue @@ -85,10 +85,12 @@

{{ error || success || hint }} @@ -126,6 +128,7 @@ const props = withDefaults( inputClass?: string labelClass?: string groupClass?: string + reserveMessageSpace?: boolean }>(), { id: '', @@ -142,6 +145,7 @@ const props = withDefaults( inputClass: '', labelClass: '', groupClass: '', + reserveMessageSpace: true, }, ) diff --git a/app/components/malio/select/Select.test.ts b/app/components/malio/select/Select.test.ts index 387dcee..14bba66 100644 --- a/app/components/malio/select/Select.test.ts +++ b/app/components/malio/select/Select.test.ts @@ -23,6 +23,7 @@ type SelectProps = { disabled?: boolean readonly?: boolean required?: boolean + reserveMessageSpace?: boolean } const SelectForTest = Select as DefineComponent @@ -359,4 +360,23 @@ describe('MalioSelect', () => { expect(trigger.attributes('aria-readonly')).toBeUndefined() expect(trigger.attributes('disabled')).toBeDefined() }) + + it('réserve l’espace message par défaut même sans message', () => { + const wrapper = mount(SelectForTest, {props: {modelValue: null, label: 'Champ', options}}) + const msg = wrapper.find('[id$="-describedby"]') + expect(msg.exists()).toBe(true) + expect(msg.classes()).toContain('min-h-[1rem]') + }) + + it('reserveMessageSpace=false sans message : pas de ligne réservée', () => { + const wrapper = mount(SelectForTest, {props: {modelValue: null, label: 'Champ', options, reserveMessageSpace: false}}) + expect(wrapper.find('[id$="-describedby"]').exists()).toBe(false) + }) + + it('reserveMessageSpace=false avec message : ligne rendue sans min-h', () => { + const wrapper = mount(SelectForTest, {props: {modelValue: null, label: 'Champ', options, reserveMessageSpace: false, error: 'Erreur'}}) + const msg = wrapper.find('[id$="-describedby"]') + expect(msg.exists()).toBe(true) + expect(msg.classes()).not.toContain('min-h-[1rem]') + }) }) diff --git a/app/components/malio/select/Select.vue b/app/components/malio/select/Select.vue index 83d8d1c..809313d 100644 --- a/app/components/malio/select/Select.vue +++ b/app/components/malio/select/Select.vue @@ -166,6 +166,7 @@

{{ error || success || hint }} @@ -210,6 +212,7 @@ const props = withDefaults(defineProps<{ groupClass?: string noOptionsText?: string required?: boolean + reserveMessageSpace?: boolean }>(), { options: () => [], emptyOptionLabel: '', @@ -226,6 +229,7 @@ const props = withDefaults(defineProps<{ groupClass: '', noOptionsText: 'Aucune option disponible', required: false, + reserveMessageSpace: true, }) const emit = defineEmits<{ diff --git a/app/components/malio/select/SelectCheckbox.test.ts b/app/components/malio/select/SelectCheckbox.test.ts index 350b881..9b8df40 100644 --- a/app/components/malio/select/SelectCheckbox.test.ts +++ b/app/components/malio/select/SelectCheckbox.test.ts @@ -27,6 +27,7 @@ type SelectCheckboxProps = { readonly?: boolean groupClass?: string required?: boolean + reserveMessageSpace?: boolean } const SelectCheckboxForTest = SelectCheckbox as DefineComponent @@ -346,4 +347,23 @@ describe('MalioSelectCheckbox', () => { expect(trigger.attributes('aria-readonly')).toBeUndefined() expect(trigger.attributes('disabled')).toBeDefined() }) + + it('réserve l’espace message par défaut même sans message', () => { + const wrapper = mount(SelectCheckboxForTest, {props: {label: 'Champ', options}}) + const msg = wrapper.find('[id$="-describedby"]') + expect(msg.exists()).toBe(true) + expect(msg.classes()).toContain('min-h-[1rem]') + }) + + it('reserveMessageSpace=false sans message : pas de ligne réservée', () => { + const wrapper = mount(SelectCheckboxForTest, {props: {label: 'Champ', options, reserveMessageSpace: false}}) + expect(wrapper.find('[id$="-describedby"]').exists()).toBe(false) + }) + + it('reserveMessageSpace=false avec message : ligne rendue sans min-h', () => { + const wrapper = mount(SelectCheckboxForTest, {props: {label: 'Champ', options, reserveMessageSpace: false, error: 'Erreur'}}) + const msg = wrapper.find('[id$="-describedby"]') + expect(msg.exists()).toBe(true) + expect(msg.classes()).not.toContain('min-h-[1rem]') + }) }) diff --git a/app/components/malio/select/SelectCheckbox.vue b/app/components/malio/select/SelectCheckbox.vue index a0f8364..3c33262 100644 --- a/app/components/malio/select/SelectCheckbox.vue +++ b/app/components/malio/select/SelectCheckbox.vue @@ -215,6 +215,7 @@

{{ error || success || hint }} @@ -263,6 +265,7 @@ const props = withDefaults(defineProps<{ groupClass?: string noOptionsText?: string required?: boolean + reserveMessageSpace?: boolean }>(), { modelValue: () => [], options: () => [], @@ -283,6 +286,7 @@ const props = withDefaults(defineProps<{ groupClass: '', noOptionsText: 'Aucune option disponible', required: false, + reserveMessageSpace: true, }) const emit = defineEmits<{ diff --git a/app/components/malio/time/Time.test.ts b/app/components/malio/time/Time.test.ts index 39188ec..833cba2 100644 --- a/app/components/malio/time/Time.test.ts +++ b/app/components/malio/time/Time.test.ts @@ -17,6 +17,7 @@ type TimeProps = { hint?: string error?: string success?: string + reserveMessageSpace?: boolean } const TimeForTest = Time as DefineComponent @@ -86,4 +87,23 @@ describe('MalioTime', () => { const wrapper = mountTime({label: 'Champ'}) expect(wrapper.find('[data-test="required-mark"]').exists()).toBe(false) }) + + it('réserve l’espace message par défaut même sans message', () => { + const wrapper = mountTime({label: 'Champ'}) + const msg = wrapper.find('[id$="-describedby"]') + expect(msg.exists()).toBe(true) + expect(msg.classes()).toContain('min-h-[1rem]') + }) + + it('reserveMessageSpace=false sans message : pas de ligne réservée', () => { + const wrapper = mountTime({label: 'Champ', reserveMessageSpace: false}) + expect(wrapper.find('[id$="-describedby"]').exists()).toBe(false) + }) + + it('reserveMessageSpace=false avec message : ligne rendue sans min-h', () => { + const wrapper = mountTime({label: 'Champ', reserveMessageSpace: false, error: 'Erreur'}) + const msg = wrapper.find('[id$="-describedby"]') + expect(msg.exists()).toBe(true) + expect(msg.classes()).not.toContain('min-h-[1rem]') + }) }) diff --git a/app/components/malio/time/Time.vue b/app/components/malio/time/Time.vue index 3297331..2584670 100644 --- a/app/components/malio/time/Time.vue +++ b/app/components/malio/time/Time.vue @@ -58,6 +58,7 @@

{{ error || success || hint }} @@ -95,6 +97,7 @@ const props = withDefaults( hint?: string error?: string success?: string + reserveMessageSpace?: boolean }>(), { id: '', @@ -110,6 +113,7 @@ const props = withDefaults( hint: '', error: '', success: '', + reserveMessageSpace: true, }, ) diff --git a/app/components/malio/time/TimePicker.test.ts b/app/components/malio/time/TimePicker.test.ts index 3ce2926..6039f8c 100644 --- a/app/components/malio/time/TimePicker.test.ts +++ b/app/components/malio/time/TimePicker.test.ts @@ -19,6 +19,7 @@ type TimePickerProps = { inputClass?: string labelClass?: string groupClass?: string + reserveMessageSpace?: boolean } const TimePickerForTest = TimePicker as DefineComponent @@ -120,4 +121,23 @@ describe('MalioTimePicker', () => { expect(label.classes()).toContain('text-black') expect(icon.classes()).toContain('text-black') }) + + it('réserve l’espace message par défaut même sans message', () => { + const wrapper = mountPicker({label: 'Champ'}) + const msg = wrapper.find('[id$="-describedby"]') + expect(msg.exists()).toBe(true) + expect(msg.classes()).toContain('min-h-[1rem]') + }) + + it('reserveMessageSpace=false sans message : pas de ligne réservée', () => { + const wrapper = mountPicker({label: 'Champ', reserveMessageSpace: false}) + expect(wrapper.find('[id$="-describedby"]').exists()).toBe(false) + }) + + it('reserveMessageSpace=false avec message : ligne rendue sans min-h', () => { + const wrapper = mountPicker({label: 'Champ', reserveMessageSpace: false, error: 'Erreur'}) + const msg = wrapper.find('[id$="-describedby"]') + expect(msg.exists()).toBe(true) + expect(msg.classes()).not.toContain('min-h-[1rem]') + }) }) diff --git a/app/components/malio/time/TimePicker.vue b/app/components/malio/time/TimePicker.vue index c13304c..fb1209f 100644 --- a/app/components/malio/time/TimePicker.vue +++ b/app/components/malio/time/TimePicker.vue @@ -78,10 +78,12 @@

{{ error || success || hint }} @@ -116,6 +118,7 @@ const props = withDefaults( inputClass?: string labelClass?: string groupClass?: string + reserveMessageSpace?: boolean }>(), { id: '', @@ -134,6 +137,7 @@ const props = withDefaults( inputClass: '', labelClass: '', groupClass: '', + reserveMessageSpace: true, }, )