Skip to content

Commit b5001f6

Browse files
crisbetojelbourn
authored andcommitted
fix(tooltip): hiding and reopening for consecutive show calls (#13326)
If the tooltip's `show` method is called twice in a row (e.g. if the user hovered over the trigger and then focused it), it'll be closed and quickly reopened which is a little wasteful and looks glitchy. These changes switch it up so nothing is done, if the tooltip is open and there are no in-progress timeouts.
1 parent 39b5577 commit b5001f6

File tree

2 files changed

+30
-3
lines changed

2 files changed

+30
-3
lines changed

src/lib/tooltip/tooltip.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,26 @@ describe('MatTooltip', () => {
625625
expect(overlayContainerElement.querySelector('.mat-tooltip')).toBeNull();
626626
}));
627627

628+
it('should not hide the tooltip when calling `show` twice in a row', fakeAsync(() => {
629+
tooltipDirective.show();
630+
tick(0);
631+
expect(tooltipDirective._isTooltipVisible()).toBe(true);
632+
fixture.detectChanges();
633+
tick(500);
634+
635+
const overlayRef = tooltipDirective._overlayRef!;
636+
637+
spyOn(overlayRef, 'detach').and.callThrough();
638+
639+
tooltipDirective.show();
640+
tick(0);
641+
expect(tooltipDirective._isTooltipVisible()).toBe(true);
642+
fixture.detectChanges();
643+
tick(500);
644+
645+
expect(overlayRef.detach).not.toHaveBeenCalled();
646+
}));
647+
628648
});
629649

630650
describe('fallback positions', () => {

src/lib/tooltip/tooltip.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,10 @@ export class MatTooltip implements OnDestroy {
271271

272272
/** Shows the tooltip after the delay in ms, defaults to tooltip-delay-show or 0ms if no input */
273273
show(delay: number = this.showDelay): void {
274-
if (this.disabled || !this.message) { return; }
274+
if (this.disabled || !this.message || (this._isTooltipVisible() &&
275+
!this._tooltipInstance!._showTimeoutId && !this._tooltipInstance!._hideTimeoutId)) {
276+
return;
277+
}
275278

276279
const overlayRef = this._createOverlay();
277280

@@ -524,10 +527,10 @@ export class TooltipComponent {
524527
tooltipClass: string|string[]|Set<string>|{[key: string]: any};
525528

526529
/** The timeout ID of any current timer set to show the tooltip */
527-
_showTimeoutId: number;
530+
_showTimeoutId: number | null;
528531

529532
/** The timeout ID of any current timer set to hide the tooltip */
530-
_hideTimeoutId: number;
533+
_hideTimeoutId: number | null;
531534

532535
/** Property watched by the animation framework to show or hide the tooltip */
533536
_visibility: TooltipVisibility = 'initial';
@@ -553,12 +556,14 @@ export class TooltipComponent {
553556
// Cancel the delayed hide if it is scheduled
554557
if (this._hideTimeoutId) {
555558
clearTimeout(this._hideTimeoutId);
559+
this._hideTimeoutId = null;
556560
}
557561

558562
// Body interactions should cancel the tooltip if there is a delay in showing.
559563
this._closeOnInteraction = true;
560564
this._showTimeoutId = setTimeout(() => {
561565
this._visibility = 'visible';
566+
this._showTimeoutId = null;
562567

563568
// Mark for check so if any parent component has set the
564569
// ChangeDetectionStrategy to OnPush it will be checked anyways
@@ -574,10 +579,12 @@ export class TooltipComponent {
574579
// Cancel the delayed show if it is scheduled
575580
if (this._showTimeoutId) {
576581
clearTimeout(this._showTimeoutId);
582+
this._showTimeoutId = null;
577583
}
578584

579585
this._hideTimeoutId = setTimeout(() => {
580586
this._visibility = 'hidden';
587+
this._hideTimeoutId = null;
581588

582589
// Mark for check so if any parent component has set the
583590
// ChangeDetectionStrategy to OnPush it will be checked anyways

0 commit comments

Comments
 (0)