From 5dd11adee3e5ae88f55f7d01f08980f0f3241109 Mon Sep 17 00:00:00 2001 From: Sush1271 Date: Tue, 3 Nov 2026 07:22:43 +0530 Subject: [PATCH] fix: prevent stale hover highlight when reopening the toolbox --- .../popover-item-default.ts | 28 +++++++++-- .../utils/popover/popover-desktop.ts | 2 + test/cypress/tests/ui/toolbox.cy.ts | 47 +++++++++++++++++++ 3 files changed, 73 insertions(+), 4 deletions(-) diff --git a/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.ts b/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.ts index 7e9889ac8..1c44ceaec 100644 --- a/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.ts +++ b/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.ts @@ -139,6 +139,20 @@ export class PopoverItemDefault extends PopoverItem { this.disableSpecialHoverAndFocusBehavior(); } + /** + * Prevents stale hover state until the pointer enters the item again + */ + public suppressHoverUntilPointerEnters(): void { + const root = this.nodes.root; + + if (root === null || root.classList.contains(css.noHover)) { + return; + } + + root.classList.add(css.noHover); + root.addEventListener('mouseenter', this.removeSpecialHoverBehavior, { once: true }); + } + /** * Constructs HTML element corresponding to popover item params * @@ -256,8 +270,6 @@ export class PopoverItemDefault extends PopoverItem { private disableSpecialHoverAndFocusBehavior(): void { this.removeSpecialFocusBehavior(); this.removeSpecialHoverBehavior(); - - this.nodes.root?.removeEventListener('mouseleave', this.removeSpecialHoverBehavior); } /** @@ -271,7 +283,12 @@ export class PopoverItemDefault extends PopoverItem { * Removes class responsible for special hover behavior on an item */ private removeSpecialHoverBehavior = (): void => { - this.nodes.root?.classList.remove(css.noHover); + const root = this.nodes.root; + + root?.classList.remove(css.noHover); + root?.removeEventListener('mouseleave', this.removeSpecialHoverBehavior); + root?.removeEventListener('mouseenter', this.removeSpecialHoverBehavior); + root?.removeEventListener('mousemove', this.removeSpecialHoverBehavior); }; /** @@ -283,7 +300,10 @@ export class PopoverItemDefault extends PopoverItem { if (!('confirmation' in item) || item.confirmation === undefined) { try { item.onActivate?.(item); - this.disableConfirmationMode(); + + if (this.isConfirmationStateEnabled) { + this.disableConfirmationMode(); + } } catch { this.animateError(); } diff --git a/src/components/utils/popover/popover-desktop.ts b/src/components/utils/popover/popover-desktop.ts index 1eb6ae6c0..acf4e3d63 100644 --- a/src/components/utils/popover/popover-desktop.ts +++ b/src/components/utils/popover/popover-desktop.ts @@ -156,6 +156,8 @@ export class PopoverDesktop extends PopoverAbstract { public hide = (): void => { super.hide(); + this.itemsDefault.forEach(item => item.suppressHoverUntilPointerEnters()); + this.destroyNestedPopoverIfExists(); this.flipper?.deactivate(); diff --git a/test/cypress/tests/ui/toolbox.cy.ts b/test/cypress/tests/ui/toolbox.cy.ts index 127b50907..1a6f4fdb4 100644 --- a/test/cypress/tests/ui/toolbox.cy.ts +++ b/test/cypress/tests/ui/toolbox.cy.ts @@ -215,4 +215,51 @@ describe('Toolbox', function () { .should('exist'); }); }); + + it('should not keep a toolbox item hovered after closing and reopening the toolbox', function () { + /** Tool with one toolbox item for the hover regression. */ + class HoverTool extends ToolMock { + /** Returns the toolbox entry displayed by this tool. */ + public static get toolbox(): ToolboxConfig { + return { + icon: '', + title: 'Hover tool', + }; + } + } + + cy.createEditor({ + tools: { + hoverTool: HoverTool, + }, + }).as('editorInstance'); + + cy.get('[data-cy=editorjs]') + .find('.ce-paragraph') + .click(); + + cy.get('@editorInstance').then((editor) => { + editor.toolbar.toggleToolbox(true); + }); + cy.get('[data-item-name="hoverTool"]') + .should('be.visible') + .click(); + + cy.get('[data-item-name="hoverTool"]') + .should('have.class', 'ce-popover-item--no-hover'); + + cy.get('@editorInstance').then((editor) => { + editor.toolbar.toggleToolbox(true); + }); + cy.get('[data-item-name="hoverTool"]') + .should('be.visible') + .should('have.class', 'ce-popover-item--no-hover') + .should(($item) => { + expect($item[0].matches(':hover')).to.be.false; + }); + + cy.get('[data-item-name="hoverTool"]') + .trigger('mouseenter') + .should('not.have.class', 'ce-popover-item--no-hover'); + }); });