diff --git a/projects/core/src/internal/controllers/type-native-popover.controller.test.ts b/projects/core/src/internal/controllers/type-native-popover.controller.test.ts index cd9f83f7a..ed1049f77 100644 --- a/projects/core/src/internal/controllers/type-native-popover.controller.test.ts +++ b/projects/core/src/internal/controllers/type-native-popover.controller.test.ts @@ -811,6 +811,48 @@ describe('type-popover.controller - interest invoker support', () => { await new Promise(r => setTimeout(r, 60)); expect(element.matches(':popover-open')).toBe(false); }); + + it('should cancel delayed show when the popover closes before delay completes', async () => { + element.openDelay = 50; + await elementIsStable(element); + + const open = untilEvent(element, 'open'); + element.showPopover(); + expect(await open).toBeDefined(); + + const interestEvent = new Event('interest', { cancelable: true }) as Event & { source: HTMLElement }; + interestEvent.source = button; + element.dispatchEvent(interestEvent); + await elementIsStable(element); + + const close = untilEvent(element, 'close'); + element.hidePopover(); + expect(await close).toBeDefined(); + + await new Promise(r => setTimeout(r, 60)); + expect(element.matches(':popover-open')).toBe(false); + }); + + it('should cancel delayed show when the popover receives hide-popover before delay completes', async () => { + element.openDelay = 50; + await elementIsStable(element); + + const open = untilEvent(element, 'open'); + element.showPopover(); + expect(await open).toBeDefined(); + + const interestEvent = new Event('interest', { cancelable: true }) as Event & { source: HTMLElement }; + interestEvent.source = button; + element.dispatchEvent(interestEvent); + await elementIsStable(element); + + const close = untilEvent(element, 'close'); + element.dispatchEvent(new CommandEvent('command', { command: 'hide-popover', source: button })); + expect(await close).toBeDefined(); + + await new Promise(r => setTimeout(r, 60)); + expect(element.matches(':popover-open')).toBe(false); + }); }); describe('type-popover.controller - showPopover source fallback', () => { diff --git a/projects/core/src/internal/controllers/type-native-popover.controller.ts b/projects/core/src/internal/controllers/type-native-popover.controller.ts index b6458fc54..92790b273 100644 --- a/projects/core/src/internal/controllers/type-native-popover.controller.ts +++ b/projects/core/src/internal/controllers/type-native-popover.controller.ts @@ -96,6 +96,10 @@ export class TypeNativePopoverController implements Rea setTimeout(() => this.host.hidePopover(), this.host.closeTimeout); } + if (e.newState === 'closed') { + this.#clearInterestTimeout(); + } + this.host.inert = this.host.matches(':not(:popover-open)'); if (this.host.modal) { @@ -119,6 +123,7 @@ export class TypeNativePopoverController implements Rea if (e.command === 'hide-popover') { this.host.hidePopover(); + this.#clearInterestTimeout(); } if (e.command === 'show-popover') { @@ -167,7 +172,10 @@ export class TypeNativePopoverController implements Rea hostDisconnected() { this.#observers.forEach(observer => observer.disconnect()); + this.#clearInterestTimeout(); + } + #clearInterestTimeout() { if (this.#interestTimeout) { clearTimeout(this.#interestTimeout); this.#interestTimeout = null;