diff --git a/components/src/widgets/menu/widget.spec.js b/components/src/widgets/menu/widget.spec.js index 5d4af180..4f788d37 100644 --- a/components/src/widgets/menu/widget.spec.js +++ b/components/src/widgets/menu/widget.spec.js @@ -4,6 +4,7 @@ import Menu from './widget'; describe('Menu component', () => { describe('methods', () => { describe('#toggle', () => { + it('toggles menu to true when clicking', () => { const wrapper = mount(Menu); wrapper.vm.showMenu = false; @@ -23,26 +24,46 @@ describe('Menu component', () => { describe('#handleClickOutside', () => { - it('hides menu content when clicked outside menu', () => { - const event = { target: 'another value'}; + it('closes menu when clicked outside menu bounds', async () => { + const event = { composedPath: jest.fn().mockReturnValue(['slot', 'button']) }; const wrapper = mount(Menu); - wrapper.vm.menu = { contains: jest.fn().mockReturnValue(false) }; + wrapper.vm.menu = 'div'; wrapper.vm.showMenu = true; - wrapper.vm.handleClickOutside(event); + await wrapper.vm.handleClickOutside(event); expect(wrapper.vm.showMenu).toBe(false); }); - it('does not hide menu content when clicked inside menu', () => { - const event = { target: 'some value'}; + it('does not close menu when clicked inside menu bounds', async () => { + const event = { composedPath: jest.fn().mockReturnValue(['slot', 'button']) }; const wrapper = mount(Menu); - wrapper.vm.menu = { contains: jest.fn().mockReturnValue(true) }; + wrapper.vm.menu = 'button'; wrapper.vm.showMenu = true; - wrapper.vm.handleClickOutside(event); + await wrapper.vm.handleClickOutside(event); expect(wrapper.vm.showMenu).toBe(true); }); }); + + describe('#onClickInside', () => { + it.each([ + // expected, closeOnClickInside + [false, true], + [true, false], + ])( + 'should set showMenu.value=%s when closeOnClickInside=%s', + (expected, closeOnClickInside) => { + const wrapper = mount(Menu, { + props: { + closeOnClickInside, + }, + }); + wrapper.vm.showMenu = true; + wrapper.vm.onClickInside(); + + expect(wrapper.vm.showMenu).toEqual(expected); + }); + }); }); describe('onMounted', () => { diff --git a/components/src/widgets/menu/widget.vue b/components/src/widgets/menu/widget.vue index 2c863916..2f04e682 100644 --- a/components/src/widgets/menu/widget.vue +++ b/components/src/widgets/menu/widget.vue @@ -5,7 +5,7 @@ > @@ -15,7 +15,7 @@ v-if="showMenu" class="menu-content" :class="alignmentClass" - @click.stop + @click.stop="onClickInside" > @@ -34,6 +34,10 @@ const props = defineProps({ return ['left', 'right'].includes(value); }, }, + closeOnClickInside: { + type: Boolean, + default: true, + }, }); const showMenu = ref(false); @@ -49,11 +53,16 @@ const toggle = () => { }; const handleClickOutside = (event) => { - if (menu.value && !menu.value.contains(event.target)) { + const isClickWithinMenuBounds = event.composedPath().some(el => el === menu.value); + if (!isClickWithinMenuBounds) { showMenu.value = false; } }; +const onClickInside = () => { + if (props.closeOnClickInside) showMenu.value = false; +}; + onMounted(() => { document.addEventListener("click", handleClickOutside); });