From b9eb24ed3dd66f2daf51efc8594900213934d4c7 Mon Sep 17 00:00:00 2001 From: Stephane Comeau Date: Fri, 4 Mar 2022 13:46:08 -0800 Subject: [PATCH] fix: tooltips remain visible when hovered (#5701) * tooltip hover * Change files * feedback tweak --- ...-e25eb67c-a6bb-4476-b996-5049eb18f26a.json | 7 ++ ...-dd0eb23b-8eef-49ad-b97d-9b367f71c782.json | 7 ++ .../src/tooltip/fixtures/base.html | 32 +++-- .../src/tooltip/tooltip.styles.ts | 1 - .../fast-foundation/src/tooltip/tooltip.ts | 117 ++++++++++++++---- 5 files changed, 134 insertions(+), 30 deletions(-) create mode 100644 change/@microsoft-fast-components-e25eb67c-a6bb-4476-b996-5049eb18f26a.json create mode 100644 change/@microsoft-fast-foundation-dd0eb23b-8eef-49ad-b97d-9b367f71c782.json diff --git a/change/@microsoft-fast-components-e25eb67c-a6bb-4476-b996-5049eb18f26a.json b/change/@microsoft-fast-components-e25eb67c-a6bb-4476-b996-5049eb18f26a.json new file mode 100644 index 00000000000..c48866e76d7 --- /dev/null +++ b/change/@microsoft-fast-components-e25eb67c-a6bb-4476-b996-5049eb18f26a.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "tooltip hover", + "packageName": "@microsoft/fast-components", + "email": "scomea@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@microsoft-fast-foundation-dd0eb23b-8eef-49ad-b97d-9b367f71c782.json b/change/@microsoft-fast-foundation-dd0eb23b-8eef-49ad-b97d-9b367f71c782.json new file mode 100644 index 00000000000..b447b2e3824 --- /dev/null +++ b/change/@microsoft-fast-foundation-dd0eb23b-8eef-49ad-b97d-9b367f71c782.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "tooltip hover", + "packageName": "@microsoft/fast-foundation", + "email": "scomea@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/packages/web-components/fast-components/src/tooltip/fixtures/base.html b/packages/web-components/fast-components/src/tooltip/fixtures/base.html index fefd69eed34..a8103fd02dd 100644 --- a/packages/web-components/fast-components/src/tooltip/fixtures/base.html +++ b/packages/web-components/fast-components/src/tooltip/fixtures/base.html @@ -106,35 +106,51 @@

Show/Hide with auto updating and lock to viewport for Corner Positions

Fixed positions

- + Top - + Right - + Bottom - + Left - + Top Left - + Top Right - + Bottom Left - + Bottom Right diff --git a/packages/web-components/fast-components/src/tooltip/tooltip.styles.ts b/packages/web-components/fast-components/src/tooltip/tooltip.styles.ts index f0a8c69b5d2..1723b14c95f 100644 --- a/packages/web-components/fast-components/src/tooltip/tooltip.styles.ts +++ b/packages/web-components/fast-components/src/tooltip/tooltip.styles.ts @@ -61,7 +61,6 @@ export const tooltipStyles: ( align-items: center; overflow: visible; flex-direction: row; - pointer-events: none; } ${anchoredRegionTag}.right, diff --git a/packages/web-components/fast-foundation/src/tooltip/tooltip.ts b/packages/web-components/fast-foundation/src/tooltip/tooltip.ts index d89df6e76fe..8362e0a0bbb 100644 --- a/packages/web-components/fast-foundation/src/tooltip/tooltip.ts +++ b/packages/web-components/fast-foundation/src/tooltip/tooltip.ts @@ -149,7 +149,7 @@ export class Tooltip extends FoundationElement { .querySelectorAll(":hover") .forEach(element => { if (element.id === anchorId) { - this.startHoverTimer(); + this.startShowDelayTimer(); } }); } @@ -256,13 +256,23 @@ export class Tooltip extends FoundationElement { /** * The timer that tracks delay time before the tooltip is shown on hover */ - private delayTimer: number | null = null; + private showDelayTimer: number | null = null; /** - * Indicates whether the anchor is currently being hovered + * The timer that tracks delay time before the tooltip is hidden + */ + private hideDelayTimer: number | null = null; + + /** + * Indicates whether the anchor is currently being hovered or has focus */ private isAnchorHoveredFocused: boolean = false; + /** + * Indicates whether the region is currently being hovered + */ + private isRegionHovered: boolean = false; + public connectedCallback(): void { super.connectedCallback(); this.anchorElement = this.getAnchor(); @@ -271,7 +281,8 @@ export class Tooltip extends FoundationElement { public disconnectedCallback(): void { this.hideTooltip(); - this.clearDelayTimer(); + this.clearShowDelayTimer(); + this.clearHideDelayTimer(); super.disconnectedCallback(); } @@ -309,11 +320,31 @@ export class Tooltip extends FoundationElement { ); }; + /** + * mouse enters region + */ + private handleRegionMouseOver = (ev: Event): void => { + this.isRegionHovered = true; + }; + + /** + * mouse leaves region + */ + private handleRegionMouseOut = (ev: Event): void => { + this.isRegionHovered = false; + this.startHideDelayTimer(); + }; + /** * mouse enters anchor */ private handleAnchorMouseOver = (ev: Event): void => { - this.startHoverTimer(); + if (this.tooltipVisible) { + // tooltip is already visible, just set the anchor hover flag + this.isAnchorHoveredFocused = true; + return; + } + this.startShowDelayTimer(); }; /** @@ -321,31 +352,64 @@ export class Tooltip extends FoundationElement { */ private handleAnchorMouseOut = (ev: Event): void => { this.isAnchorHoveredFocused = false; - this.updateTooltipVisibility(); - this.clearDelayTimer(); + this.clearShowDelayTimer(); + this.startHideDelayTimer(); }; + /** + * anchor gets focus + */ private handleAnchorFocusIn = (ev: Event): void => { - this.startHoverTimer(); + this.startShowDelayTimer(); }; + /** + * anchor loses focus + */ private handleAnchorFocusOut = (ev: Event): void => { this.isAnchorHoveredFocused = false; - this.updateTooltipVisibility(); - this.clearDelayTimer(); + this.clearShowDelayTimer(); + this.startHideDelayTimer(); + }; + + /** + * starts the hide timer + */ + private startHideDelayTimer = (): void => { + this.clearHideDelayTimer(); + + if (!this.tooltipVisible) { + return; + } + + // allow 60 ms for account for pointer to move between anchor/tooltip + // without hiding tooltip + this.hideDelayTimer = window.setTimeout((): void => { + this.updateTooltipVisibility(); + }, 60); + }; + + /** + * clears the hide delay + */ + private clearHideDelayTimer = (): void => { + if (this.hideDelayTimer !== null) { + clearTimeout(this.hideDelayTimer); + this.hideDelayTimer = null; + } }; /** - * starts the hover timer if not currently running + * starts the show timer if not currently running */ - private startHoverTimer = (): void => { + private startShowDelayTimer = (): void => { if (this.isAnchorHoveredFocused) { return; } if (this.delay > 1) { - if (this.delayTimer === null) - this.delayTimer = window.setTimeout((): void => { + if (this.showDelayTimer === null) + this.showDelayTimer = window.setTimeout((): void => { this.startHover(); }, this.delay); return; @@ -355,7 +419,7 @@ export class Tooltip extends FoundationElement { }; /** - * starts the hover delay timer + * start hover */ private startHover = (): void => { this.isAnchorHoveredFocused = true; @@ -363,12 +427,12 @@ export class Tooltip extends FoundationElement { }; /** - * clears the hover delay + * clears the show delay */ - private clearDelayTimer = (): void => { - if (this.delayTimer !== null) { - clearTimeout(this.delayTimer); - this.delayTimer = null; + private clearShowDelayTimer = (): void => { + if (this.showDelayTimer !== null) { + clearTimeout(this.showDelayTimer); + this.showDelayTimer = null; } }; @@ -481,7 +545,7 @@ export class Tooltip extends FoundationElement { this.showTooltip(); return; } else { - if (this.isAnchorHoveredFocused) { + if (this.isAnchorHoveredFocused || this.isRegionHovered) { this.showTooltip(); return; } @@ -509,6 +573,7 @@ export class Tooltip extends FoundationElement { if (!this.tooltipVisible) { return; } + this.clearHideDelayTimer(); if (this.region !== null && this.region !== undefined) { (this.region as any).removeEventListener( "positionchange", @@ -516,6 +581,9 @@ export class Tooltip extends FoundationElement { ); this.region.viewportElement = null; this.region.anchorElement = null; + + this.region.removeEventListener("mouseover", this.handleRegionMouseOver); + this.region.removeEventListener("mouseout", this.handleRegionMouseOut); } document.removeEventListener("keydown", this.handleDocumentKeydown); this.tooltipVisible = false; @@ -535,5 +603,12 @@ export class Tooltip extends FoundationElement { "positionchange", this.handlePositionChange ); + + this.region.addEventListener("mouseover", this.handleRegionMouseOver, { + passive: true, + }); + this.region.addEventListener("mouseout", this.handleRegionMouseOut, { + passive: true, + }); }; }