diff --git a/packages/context-menu/src/vaadin-contextmenu-items-mixin.js b/packages/context-menu/src/vaadin-contextmenu-items-mixin.js index 5e6a5c0e0d..f24f313b2f 100644 --- a/packages/context-menu/src/vaadin-contextmenu-items-mixin.js +++ b/packages/context-menu/src/vaadin-contextmenu-items-mixin.js @@ -69,7 +69,7 @@ export const ItemsMixin = (superClass) => // Overlay's outside click listener doesn't work with modeless // overlays (submenus) so we need additional logic for it this.__itemsOutsideClickListener = (e) => { - if (!e.composedPath().some((el) => el.localName === `${this._tagNamePrefix}-overlay`)) { + if (this._shouldCloseOnOutsideClick(e)) { this.dispatchEvent(new CustomEvent('items-outside-click')); } }; @@ -101,6 +101,18 @@ export const ItemsMixin = (superClass) => document.documentElement.removeEventListener('click', this.__itemsOutsideClickListener); } + /** + * Whether to close the overlay on outside click or not. + * Override this method to customize the closing logic. + * + * @param {Event} event + * @return {boolean} + * @protected + */ + _shouldCloseOnOutsideClick(event) { + return !event.composedPath().some((el) => el.localName === `${this._tagNamePrefix}-overlay`); + } + /** @protected */ __forwardFocus() { const overlay = this._overlayElement; diff --git a/packages/menu-bar/src/vaadin-menu-bar-mixin.js b/packages/menu-bar/src/vaadin-menu-bar-mixin.js index 78a7fff35c..ac03709af7 100644 --- a/packages/menu-bar/src/vaadin-menu-bar-mixin.js +++ b/packages/menu-bar/src/vaadin-menu-bar-mixin.js @@ -904,7 +904,6 @@ export const MenuBarMixin = (superClass) => /** @private */ __onButtonClick(e) { - e.stopPropagation(); const button = this._getButtonFromEvent(e); if (button) { this.__openSubMenu(button, button.__triggeredWithActiveKeys); diff --git a/packages/menu-bar/src/vaadin-menu-bar-submenu-mixin.js b/packages/menu-bar/src/vaadin-menu-bar-submenu-mixin.js index 3776e3b60c..213ecf3758 100644 --- a/packages/menu-bar/src/vaadin-menu-bar-submenu-mixin.js +++ b/packages/menu-bar/src/vaadin-menu-bar-submenu-mixin.js @@ -46,4 +46,21 @@ export const SubMenuMixin = (superClass) => this.getRootNode().host._close(); } } + + /** + * Override method from `ContextMenuMixin` to prevent closing + * sub-menu on the same click event that was used to open it. + * + * @param {Event} event + * @return {boolean} + * @protected + * @override + */ + _shouldCloseOnOutsideClick(event) { + if (this.hasAttribute('is-root') && event.composedPath().includes(this.listenOn)) { + return false; + } + + return super._shouldCloseOnOutsideClick(event); + } }; diff --git a/packages/menu-bar/test/sub-menu.common.js b/packages/menu-bar/test/sub-menu.common.js index 81f3421663..7843c3540c 100644 --- a/packages/menu-bar/test/sub-menu.common.js +++ b/packages/menu-bar/test/sub-menu.common.js @@ -94,6 +94,29 @@ describe('sub-menu', () => { expect(spy.calledOnce).to.be.true; }); + it('should set pointer events to `auto` when opened on click', async () => { + buttons[0].click(); + await nextRender(subMenu); + expect(menu.style.pointerEvents).to.equal('auto'); + }); + + it('should reset pointer events after closing on click', async () => { + buttons[0].click(); + await nextRender(subMenu); + + buttons[0].click(); + await nextRender(subMenu); + expect(menu.style.pointerEvents).to.be.empty; + }); + + it('should not stop click event from propagating when opened ', async () => { + const event = new CustomEvent('click', { bubbles: true }); + const spy = sinon.spy(event, 'stopPropagation'); + buttons[0].dispatchEvent(event); + await nextRender(subMenu); + expect(spy.called).to.be.false; + }); + it('should focus the overlay when sub-menu opened on click', async () => { const spy = sinon.spy(subMenuOverlay.$.overlay, 'focus'); buttons[0].click();