Skip to content

Commit 90a55fa

Browse files
crisbetoandrewseguin
authored andcommitted
fix(tooltip): don't open from programmatic focus (#7258)
Prevents the tooltip from opening when its trigger is focused programmatically. Fixes #7245.
1 parent 3e3ef2a commit 90a55fa

File tree

2 files changed

+27
-4
lines changed

2 files changed

+27
-4
lines changed

src/lib/tooltip/tooltip.spec.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,8 +485,22 @@ describe('MatTooltip', () => {
485485
dispatchKeyboardEvent(buttonElement, 'keydown', ESCAPE);
486486
fixture.detectChanges();
487487
}).not.toThrow();
488+
489+
tick(0);
488490
}));
489491

492+
it('should not show the tooltip on progammatic focus', fakeAsync(() => {
493+
expect(tooltipDirective._tooltipInstance).toBeUndefined();
494+
495+
buttonElement.focus();
496+
tick(0);
497+
fixture.detectChanges();
498+
tick(500);
499+
500+
expect(overlayContainerElement.querySelector('.mat-tooltip')).toBeNull();
501+
}));
502+
503+
490504
});
491505

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

src/lib/tooltip/tooltip.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.io/license
77
*/
8-
98
import {animate, AnimationEvent, state, style, transition, trigger} from '@angular/animations';
10-
import {AriaDescriber} from '@angular/cdk/a11y';
9+
import {AriaDescriber, FocusMonitor} from '@angular/cdk/a11y';
1110
import {Directionality} from '@angular/cdk/bidi';
1211
import {coerceBooleanProperty} from '@angular/cdk/coercion';
1312
import {ESCAPE} from '@angular/cdk/keycodes';
@@ -90,8 +89,6 @@ export const MAT_TOOLTIP_SCROLL_STRATEGY_PROVIDER = {
9089
exportAs: 'matTooltip',
9190
host: {
9291
'(longpress)': 'show()',
93-
'(focus)': 'show()',
94-
'(blur)': 'hide(0)',
9592
'(keydown)': '_handleKeydown($event)',
9693
'(touchend)': 'hide(' + TOUCHEND_HIDE_DELAY + ')',
9794
},
@@ -177,6 +174,7 @@ export class MatTooltip implements OnDestroy {
177174
private _ngZone: NgZone,
178175
private _platform: Platform,
179176
private _ariaDescriber: AriaDescriber,
177+
private _focusMonitor: FocusMonitor,
180178
@Inject(MAT_TOOLTIP_SCROLL_STRATEGY) private _scrollStrategy,
181179
@Optional() private _dir: Directionality) {
182180

@@ -188,6 +186,15 @@ export class MatTooltip implements OnDestroy {
188186
this._leaveListener =
189187
renderer.listen(_elementRef.nativeElement, 'mouseleave', () => this.hide());
190188
}
189+
190+
_focusMonitor.monitor(_elementRef.nativeElement, renderer, false).subscribe(origin => {
191+
// Note that the focus monitor runs outside the Angular zone.
192+
if (!origin) {
193+
_ngZone.run(() => this.hide(0));
194+
} else if (origin !== 'program') {
195+
_ngZone.run(() => this.show());
196+
}
197+
});
191198
}
192199

193200
/**
@@ -197,13 +204,15 @@ export class MatTooltip implements OnDestroy {
197204
if (this._tooltipInstance) {
198205
this._disposeTooltip();
199206
}
207+
200208
// Clean up the event listeners set in the constructor
201209
if (!this._platform.IOS) {
202210
this._enterListener();
203211
this._leaveListener();
204212
}
205213

206214
this._ariaDescriber.removeDescription(this._elementRef.nativeElement, this.message);
215+
this._focusMonitor.stopMonitoring(this._elementRef.nativeElement);
207216
}
208217

209218
/** Shows the tooltip after the delay in ms, defaults to tooltip-delay-show or 0ms if no input */

0 commit comments

Comments
 (0)