fix: readonly component style + TabList + required component (#61)
Release / release (push) Successful in 1m24s

| 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é

---------

Co-authored-by: THOLOT DECHENE Matthieu <matthieu@yuno.malio.fr>
Co-authored-by: matthieu <matthieu@yuno.malio.fr>
Reviewed-on: #61
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
This commit was merged in pull request #61.
This commit is contained in:
2026-06-04 06:45:24 +00:00
committed by Autin
parent b55050b2ad
commit 9ff3e83c03
58 changed files with 3192 additions and 167 deletions
@@ -1,5 +1,5 @@
import {describe, expect, it} from 'vitest'
import {mount} from '@vue/test-utils'
import {mount, renderToString} from '@vue/test-utils'
import type {DefineComponent} from 'vue'
import SelectCheckbox from './SelectCheckbox.vue'
@@ -9,7 +9,7 @@ type Option = {
}
type SelectCheckboxProps = {
modelValue: Array<string | number>
modelValue?: Array<string | number>
options?: Option[]
emptyOptionLabel?: string
label?: string
@@ -24,7 +24,10 @@ type SelectCheckboxProps = {
displaySelectAll?: boolean
selectAllLabel?: string
disabled?: boolean
readonly?: boolean
groupClass?: string
required?: boolean
reserveMessageSpace?: boolean
}
const SelectCheckboxForTest = SelectCheckbox as DefineComponent<SelectCheckboxProps>
@@ -36,6 +39,18 @@ const options: Option[] = [
]
describe('MalioSelectCheckbox', () => {
it('rend sans planter quand modelValue nest pas fourni (non contrôlé)', () => {
expect(() =>
mount(SelectCheckboxForTest, {props: {label: 'Catégories', options}}),
).not.toThrow()
})
it('rend en SSR sans planter quand modelValue est absent (cause du crash playground)', async () => {
await expect(
renderToString(SelectCheckboxForTest, {props: {label: 'Catégories', readonly: true, options}}),
).resolves.toBeTruthy()
})
it('renders checkbox inputs for options', async () => {
const wrapper = mount(SelectCheckboxForTest, {
props: {modelValue: [], options},
@@ -235,6 +250,38 @@ describe('MalioSelectCheckbox', () => {
expect(wrapper.get('[data-test="chevron"]').classes()).toContain('text-m-success')
})
it('affiche l\'astérisque quand required est vrai', () => {
const wrapper = mount(SelectCheckboxForTest, {
props: {modelValue: [], label: 'Champ', required: true},
})
expect(wrapper.find('[data-test="required-mark"]').exists()).toBe(true)
})
it('n\'affiche pas l\'astérisque par défaut', () => {
const wrapper = mount(SelectCheckboxForTest, {
props: {modelValue: [], label: 'Champ'},
})
expect(wrapper.find('[data-test="required-mark"]').exists()).toBe(false)
})
it('expose aria-required quand required est vrai', () => {
const wrapper = mount(SelectCheckboxForTest, {
props: {modelValue: [], options, required: true},
})
expect(wrapper.find('[aria-required="true"]').exists()).toBe(true)
})
it('n\'expose pas aria-required par défaut', () => {
const wrapper = mount(SelectCheckboxForTest, {
props: {modelValue: [], options},
})
expect(wrapper.find('[aria-required="true"]').exists()).toBe(false)
})
it('keeps the bottom border allocation when open downward (transparent, not zero)', async () => {
const wrapper = mount(SelectCheckboxForTest, {
props: {modelValue: [], options},
@@ -248,4 +295,75 @@ describe('MalioSelectCheckbox', () => {
expect(buttonClasses).not.toContain('!border-b-0')
expect(buttonClasses).toContain('!border-b-transparent')
})
it('readonly : bordure noire même sans sélection, pas de grow/bleu', () => {
const wrapper = mount(SelectCheckboxForTest, {
props: {label: 'Champ', readonly: true, modelValue: [], options: [{label: 'A', value: 'a'}]},
})
const trigger = wrapper.get('button')
expect(trigger.classes()).toContain('border-black')
expect(trigger.classes()).not.toContain('border-m-muted')
expect(trigger.classes()).not.toContain('grow-height')
expect(trigger.classes()).not.toContain('focus-visible:border-m-primary')
})
it('readonly vide : label gris, pas de bleu', () => {
const wrapper = mount(SelectCheckboxForTest, {
props: {label: 'Champ', readonly: true, modelValue: [], options: [{label: 'A', value: 'a'}]},
})
const label = wrapper.get('label')
expect(label.classes()).not.toContain('text-m-primary')
expect(label.classes()).toContain('text-m-muted')
})
it('readonly sélectionné : label noir + chevron noir', () => {
const wrapper = mount(SelectCheckboxForTest, {
props: {label: 'Champ', readonly: true, modelValue: ['a'], options: [{label: 'A', value: 'a'}]},
})
expect(wrapper.get('label').classes()).toContain('text-black')
expect(wrapper.get('[data-test="chevron"]').classes()).toContain('text-black')
})
it('readonly empêche louverture du dropdown', async () => {
const wrapper = mount(SelectCheckboxForTest, {
props: {label: 'Champ', readonly: true, modelValue: [], options: [{label: 'A', value: 'a'}]},
})
await wrapper.get('button').trigger('click')
expect(wrapper.find('[role="listbox"]').exists()).toBe(false)
})
it('readonly expose aria-readonly et reste focusable (pas disabled)', () => {
const wrapper = mount(SelectCheckboxForTest, {
props: {label: 'Champ', readonly: true, modelValue: [], options},
})
const trigger = wrapper.get('button')
expect(trigger.attributes('aria-readonly')).toBe('true')
expect(trigger.attributes('disabled')).toBeUndefined()
})
it('disabled + readonly : pas daria-readonly (disabled prime)', () => {
const wrapper = mount(SelectCheckboxForTest, {props: {modelValue: [], label: 'Champ', disabled: true, readonly: true, options: [{label: 'A', value: 'a'}]}})
const trigger = wrapper.get('button')
expect(trigger.attributes('aria-readonly')).toBeUndefined()
expect(trigger.attributes('disabled')).toBeDefined()
})
it('réserve lespace 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]')
})
})