From 90a55fa3456fd2e69edcd69d3d0892eedc3e3dd3 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Tue, 10 Oct 2017 00:50:52 +0200 Subject: [PATCH] fix(tooltip): don't open from programmatic focus (#7258) Prevents the tooltip from opening when its trigger is focused programmatically. Fixes #7245. --- src/lib/tooltip/tooltip.spec.ts | 14 ++++++++++++++ src/lib/tooltip/tooltip.ts | 17 +++++++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/lib/tooltip/tooltip.spec.ts b/src/lib/tooltip/tooltip.spec.ts index 1e27a1432590..61446059f112 100644 --- a/src/lib/tooltip/tooltip.spec.ts +++ b/src/lib/tooltip/tooltip.spec.ts @@ -485,8 +485,22 @@ describe('MatTooltip', () => { dispatchKeyboardEvent(buttonElement, 'keydown', ESCAPE); fixture.detectChanges(); }).not.toThrow(); + + tick(0); })); + it('should not show the tooltip on progammatic focus', fakeAsync(() => { + expect(tooltipDirective._tooltipInstance).toBeUndefined(); + + buttonElement.focus(); + tick(0); + fixture.detectChanges(); + tick(500); + + expect(overlayContainerElement.querySelector('.mat-tooltip')).toBeNull(); + })); + + }); describe('fallback positions', () => { diff --git a/src/lib/tooltip/tooltip.ts b/src/lib/tooltip/tooltip.ts index b65f5a75c51a..d368952ca511 100644 --- a/src/lib/tooltip/tooltip.ts +++ b/src/lib/tooltip/tooltip.ts @@ -5,9 +5,8 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ - import {animate, AnimationEvent, state, style, transition, trigger} from '@angular/animations'; -import {AriaDescriber} from '@angular/cdk/a11y'; +import {AriaDescriber, FocusMonitor} from '@angular/cdk/a11y'; import {Directionality} from '@angular/cdk/bidi'; import {coerceBooleanProperty} from '@angular/cdk/coercion'; import {ESCAPE} from '@angular/cdk/keycodes'; @@ -90,8 +89,6 @@ export const MAT_TOOLTIP_SCROLL_STRATEGY_PROVIDER = { exportAs: 'matTooltip', host: { '(longpress)': 'show()', - '(focus)': 'show()', - '(blur)': 'hide(0)', '(keydown)': '_handleKeydown($event)', '(touchend)': 'hide(' + TOUCHEND_HIDE_DELAY + ')', }, @@ -177,6 +174,7 @@ export class MatTooltip implements OnDestroy { private _ngZone: NgZone, private _platform: Platform, private _ariaDescriber: AriaDescriber, + private _focusMonitor: FocusMonitor, @Inject(MAT_TOOLTIP_SCROLL_STRATEGY) private _scrollStrategy, @Optional() private _dir: Directionality) { @@ -188,6 +186,15 @@ export class MatTooltip implements OnDestroy { this._leaveListener = renderer.listen(_elementRef.nativeElement, 'mouseleave', () => this.hide()); } + + _focusMonitor.monitor(_elementRef.nativeElement, renderer, false).subscribe(origin => { + // Note that the focus monitor runs outside the Angular zone. + if (!origin) { + _ngZone.run(() => this.hide(0)); + } else if (origin !== 'program') { + _ngZone.run(() => this.show()); + } + }); } /** @@ -197,6 +204,7 @@ export class MatTooltip implements OnDestroy { if (this._tooltipInstance) { this._disposeTooltip(); } + // Clean up the event listeners set in the constructor if (!this._platform.IOS) { this._enterListener(); @@ -204,6 +212,7 @@ export class MatTooltip implements OnDestroy { } this._ariaDescriber.removeDescription(this._elementRef.nativeElement, this.message); + this._focusMonitor.stopMonitoring(this._elementRef.nativeElement); } /** Shows the tooltip after the delay in ms, defaults to tooltip-delay-show or 0ms if no input */