diff --git a/app/components/malio/drawer/Drawer.test.ts b/app/components/malio/drawer/Drawer.test.ts
index 8d05cf5..5be7684 100644
--- a/app/components/malio/drawer/Drawer.test.ts
+++ b/app/components/malio/drawer/Drawer.test.ts
@@ -74,4 +74,75 @@ describe('MalioDrawer', () => {
const wrapper = mountComponent({ modelValue: true, drawerClass: 'max-w-2xl' })
expect(wrapper.find('[data-test="panel"]').classes()).toContain('max-w-2xl')
})
+
+ it('renders the #header slot inside the header bar', () => {
+ const wrapper = mountComponent(
+ { modelValue: true },
+ { header: '
Titre
' },
+ )
+ expect(wrapper.find('[data-test="header"] [data-test="title"]').text()).toBe('Titre')
+ })
+
+ it('renders the header bar when showClose is true even without #header', () => {
+ const wrapper = mountComponent({ modelValue: true })
+ expect(wrapper.find('[data-test="header"]').exists()).toBe(true)
+ })
+
+ it('does not render the header bar when no #header and showClose is false', () => {
+ const wrapper = mountComponent({ modelValue: true, showClose: false })
+ expect(wrapper.find('[data-test="header"]').exists()).toBe(false)
+ })
+
+ it('shows the close button by default', () => {
+ const wrapper = mountComponent({ modelValue: true })
+ expect(wrapper.find('[data-test="close-button"]').exists()).toBe(true)
+ })
+
+ it('hides the close button when showClose is false', () => {
+ const wrapper = mountComponent(
+ { modelValue: true, showClose: false },
+ { header: 'Titre
' },
+ )
+ expect(wrapper.find('[data-test="close-button"]').exists()).toBe(false)
+ })
+
+ it('close button renders mdi:cancel-bold icon', () => {
+ const wrapper = mountComponent({ modelValue: true })
+ const icon = wrapper.findComponent(IconifyIcon)
+ expect(icon.props('icon')).toBe('mdi:cancel-bold')
+ })
+
+ it('close button has aria-label "Fermer"', () => {
+ const wrapper = mountComponent({ modelValue: true })
+ expect(wrapper.find('[data-test="close-button"]').attributes('aria-label')).toBe('Fermer')
+ })
+
+ it('emits update:modelValue false and close on close button click', async () => {
+ const wrapper = mountComponent({ modelValue: true })
+ await wrapper.find('[data-test="close-button"]').trigger('click')
+ expect(wrapper.emitted('update:modelValue')?.[0]).toEqual([false])
+ expect(wrapper.emitted('close')).toHaveLength(1)
+ })
+
+ it('sets aria-labelledby to the header id when #header is provided', () => {
+ const wrapper = mountComponent(
+ { modelValue: true, id: 'test-drawer' },
+ { header: 'Titre
' },
+ )
+ const panel = wrapper.find('[data-test="panel"]')
+ expect(panel.attributes('aria-labelledby')).toBe('test-drawer-header')
+ expect(wrapper.find('[data-test="header-content"]').attributes('id')).toBe('test-drawer-header')
+ })
+
+ it('sets aria-label from ariaLabel when no #header is provided', () => {
+ const wrapper = mountComponent({ modelValue: true, ariaLabel: 'Panneau latéral' })
+ const panel = wrapper.find('[data-test="panel"]')
+ expect(panel.attributes('aria-label')).toBe('Panneau latéral')
+ expect(panel.attributes('aria-labelledby')).toBeUndefined()
+ })
+
+ it('applies headerClass to the header bar', () => {
+ const wrapper = mountComponent({ modelValue: true, headerClass: 'bg-m-primary' })
+ expect(wrapper.find('[data-test="header"]').classes()).toContain('bg-m-primary')
+ })
})
diff --git a/app/components/malio/drawer/Drawer.vue b/app/components/malio/drawer/Drawer.vue
index cc4870c..2a712af 100644
--- a/app/components/malio/drawer/Drawer.vue
+++ b/app/components/malio/drawer/Drawer.vue
@@ -25,9 +25,38 @@
)"
role="dialog"
aria-modal="true"
+ :aria-labelledby="hasHeader ? headerId : undefined"
+ :aria-label="hasHeader ? undefined : (ariaLabel || undefined)"
tabindex="-1"
data-test="panel"
>
+