From e7af92808fb81551ca564625b705124684173e27 Mon Sep 17 00:00:00 2001 From: tristan Date: Thu, 21 May 2026 16:37:19 +0200 Subject: [PATCH] feat : scroll-lock, focus-trap et restitution du focus du MalioDrawer --- app/components/malio/drawer/Drawer.test.ts | 51 +++++++++++++- app/components/malio/drawer/Drawer.vue | 78 +++++++++++++++++++++- 2 files changed, 127 insertions(+), 2 deletions(-) diff --git a/app/components/malio/drawer/Drawer.test.ts b/app/components/malio/drawer/Drawer.test.ts index ec7ca62..4b70073 100644 --- a/app/components/malio/drawer/Drawer.test.ts +++ b/app/components/malio/drawer/Drawer.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, it } from 'vitest' +import { afterEach, describe, expect, it } from 'vitest' import { mount } from '@vue/test-utils' import type { DefineComponent } from 'vue' import { Icon as IconifyIcon } from '@iconify/vue' @@ -30,6 +30,10 @@ function mountComponent(props: DrawerProps = {}, slots?: Record) } describe('MalioDrawer', () => { + afterEach(() => { + document.body.style.overflow = '' + }) + it('does not render when modelValue is false', () => { const wrapper = mountComponent({ modelValue: false }) expect(wrapper.find('[data-test="panel"]').exists()).toBe(false) @@ -214,4 +218,49 @@ describe('MalioDrawer', () => { await wrapper.find('[data-test="panel"]').trigger('keydown', { key: 'Escape' }) expect(wrapper.emitted('update:modelValue')).toBeUndefined() }) + + it('locks body scroll when opened and restores it when closed', async () => { + const wrapper = mountComponent({ modelValue: false }) + expect(document.body.style.overflow).toBe('') + await wrapper.setProps({ modelValue: true }) + expect(document.body.style.overflow).toBe('hidden') + await wrapper.setProps({ modelValue: false }) + expect(document.body.style.overflow).toBe('') + }) + + it('moves focus into the panel when opened', async () => { + const wrapper = mount(DrawerForTest, { + props: { modelValue: false, showClose: false }, + slots: { default: '' }, + attachTo: document.body, + global: { stubs: { Teleport: true } }, + }) + await wrapper.setProps({ modelValue: true }) + await wrapper.vm.$nextTick() + const first = wrapper.find('[data-test="first"]').element + expect(document.activeElement).toBe(first) + wrapper.unmount() + }) + + it('restores focus to the trigger when closed', async () => { + const trigger = document.createElement('button') + document.body.appendChild(trigger) + trigger.focus() + expect(document.activeElement).toBe(trigger) + + const wrapper = mount(DrawerForTest, { + props: { modelValue: false }, + slots: { default: '' }, + attachTo: document.body, + global: { stubs: { Teleport: true } }, + }) + await wrapper.setProps({ modelValue: true }) + await wrapper.vm.$nextTick() + await wrapper.setProps({ modelValue: false }) + await wrapper.vm.$nextTick() + expect(document.activeElement).toBe(trigger) + + wrapper.unmount() + trigger.remove() + }) }) diff --git a/app/components/malio/drawer/Drawer.vue b/app/components/malio/drawer/Drawer.vue index dbf37c2..4676d35 100644 --- a/app/components/malio/drawer/Drawer.vue +++ b/app/components/malio/drawer/Drawer.vue @@ -79,7 +79,17 @@