diff --git a/libs/components/popovers/src/lib/modules/popover/popover.directive.spec.ts b/libs/components/popovers/src/lib/modules/popover/popover.directive.spec.ts index 8db6aeaf61..11b6c44e86 100644 --- a/libs/components/popovers/src/lib/modules/popover/popover.directive.spec.ts +++ b/libs/components/popovers/src/lib/modules/popover/popover.directive.spec.ts @@ -430,6 +430,65 @@ describe('Popover directive', () => { expect(popover).toBeNull(); })); + it('should open and close popover via focus when a hover trigger is used', fakeAsync(() => { + fixture.componentInstance.trigger = 'mouseenter'; + + detectChangesFakeAsync(); + + const button = getCallerElement(); + + button?.focus(); + SkyAppTestUtility.fireDomEvent(button, 'focusin'); + + detectChangesFakeAsync(); + + let popover = getPopoverElement(); + + expect(isElementVisible(popover)).toEqual(true); + + // Simulate moving the mouse to the popover. + SkyAppTestUtility.fireDomEvent(popover, 'mouseenter'); + + detectChangesFakeAsync(); + + popover = getPopoverElement(); + + // Confirm popover is still open. + expect(isElementVisible(popover)).toEqual(true); + + // Simulate moving the mouse from the popover to the trigger button. + SkyAppTestUtility.fireDomEvent(popover, 'mouseleave'); + tick(); + fixture.detectChanges(); + SkyAppTestUtility.fireDomEvent(button, 'mouseenter'); + tick(); + fixture.detectChanges(); + + popover = getPopoverElement(); + + // Confirm popover is still open. + expect(isElementVisible(popover)).toEqual(true); + + // Simulate mouse leaving the trigger button. + SkyAppTestUtility.fireDomEvent(button, 'mouseleave'); + + detectChangesFakeAsync(); + + popover = getPopoverElement(); + + // Confirm popover is still open. + expect(isElementVisible(popover)).toEqual(true); + + SkyAppTestUtility.fireDomEvent(button, 'focusout'); + + detectChangesFakeAsync(); + + popover = getPopoverElement(); + + // Menu should now be closed. + expect(popover).toBeNull(); + })); + it('should close popover when clicking outside', fakeAsync(() => { detectChangesFakeAsync(); diff --git a/libs/components/popovers/src/lib/modules/popover/popover.directive.ts b/libs/components/popovers/src/lib/modules/popover/popover.directive.ts index f23532ea59..8c4a496b84 100644 --- a/libs/components/popovers/src/lib/modules/popover/popover.directive.ts +++ b/libs/components/popovers/src/lib/modules/popover/popover.directive.ts @@ -215,11 +215,39 @@ export class SkyPopoverDirective implements OnInit, OnDestroy { this.skyPopover.isActive && this.skyPopoverTrigger === 'mouseenter' ) { - // Give the popover a chance to set its isMouseEnter flag before checking to see - // if it should be closed. - setTimeout(() => { - this.#closePopoverOrMarkForClose(); - }); + if (document.activeElement !== element) { + // Give the popover a chance to set its isMouseEnter flag before checking to see + // if it should be closed. + setTimeout(() => { + this.#closePopoverOrMarkForClose(); + }); + } + } + } + }); + + observableFromEvent(element, 'focusin') + .pipe(takeUntil(this.#ngUnsubscribe)) + .subscribe(() => { + if (this.skyPopover) { + if ( + !this.skyPopover.isActive && + this.skyPopoverTrigger === 'mouseenter' + ) { + this.#sendMessage(SkyPopoverMessageType.Open); + } + } + }); + + observableFromEvent(element, 'focusout') + .pipe(takeUntil(this.#ngUnsubscribe)) + .subscribe(() => { + if (this.skyPopover) { + if ( + this.skyPopover.isActive && + this.skyPopoverTrigger === 'mouseenter' + ) { + this.#sendMessage(SkyPopoverMessageType.Close); } } });