Skip to content

Commit b50ab13

Browse files
committed
fix(tooltip): not closing if escape is pressed while trigger isn't focused
Currently the tooltip's `keydown` handler is on the trigger, which means that it won't fire if the trigger doesn't have focus. This could happen when a tooltip was opened by hovering over the trigger. These changes use the `OverlayKeyboardDispatcher` to handle the events instead. Fixes #14278.
1 parent 7f6972f commit b50ab13

File tree

3 files changed

+35
-17
lines changed

3 files changed

+35
-17
lines changed

src/material/tooltip/tooltip.spec.ts

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -639,9 +639,28 @@ describe('MatTooltip', () => {
639639
expect(overlayContainerElement.textContent).toContain(initialTooltipMessage);
640640
}));
641641

642+
it('should hide when pressing escape', fakeAsync(() => {
643+
tooltipDirective.show();
644+
tick(0);
645+
fixture.detectChanges();
646+
tick(500);
647+
648+
expect(tooltipDirective._isTooltipVisible()).toBe(true);
649+
expect(overlayContainerElement.textContent).toContain(initialTooltipMessage);
650+
651+
dispatchKeyboardEvent(document.body, 'keydown', ESCAPE);
652+
tick(0);
653+
fixture.detectChanges();
654+
tick(500);
655+
fixture.detectChanges();
656+
657+
expect(tooltipDirective._isTooltipVisible()).toBe(false);
658+
expect(overlayContainerElement.textContent).toBe('');
659+
}));
660+
642661
it('should not throw when pressing ESCAPE', fakeAsync(() => {
643662
expect(() => {
644-
dispatchKeyboardEvent(buttonElement, 'keydown', ESCAPE);
663+
dispatchKeyboardEvent(document.body, 'keydown', ESCAPE);
645664
fixture.detectChanges();
646665
}).not.toThrow();
647666

@@ -654,7 +673,7 @@ describe('MatTooltip', () => {
654673
tick(0);
655674
fixture.detectChanges();
656675

657-
const event = dispatchKeyboardEvent(buttonElement, 'keydown', ESCAPE);
676+
const event = dispatchKeyboardEvent(document.body, 'keydown', ESCAPE);
658677
fixture.detectChanges();
659678
flush();
660679

@@ -668,7 +687,7 @@ describe('MatTooltip', () => {
668687

669688
const event = createKeyboardEvent('keydown', ESCAPE);
670689
Object.defineProperty(event, 'altKey', {get: () => true});
671-
dispatchEvent(buttonElement, event);
690+
dispatchEvent(document.body, event);
672691
fixture.detectChanges();
673692
flush();
674693

src/material/tooltip/tooltip.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,6 @@ export function MAT_TOOLTIP_DEFAULT_OPTIONS_FACTORY(): MatTooltipDefaultOptions
114114
exportAs: 'matTooltip',
115115
host: {
116116
'(longpress)': 'show()',
117-
'(keydown)': '_handleKeydown($event)',
118117
'(touchend)': '_handleTouchend()',
119118
},
120119
})
@@ -337,15 +336,6 @@ export class MatTooltip implements OnDestroy, OnInit {
337336
return !!this._tooltipInstance && this._tooltipInstance.isVisible();
338337
}
339338

340-
/** Handles the keydown events on the host element. */
341-
_handleKeydown(e: KeyboardEvent) {
342-
if (this._isTooltipVisible() && e.keyCode === ESCAPE && !hasModifierKey(e)) {
343-
e.preventDefault();
344-
e.stopPropagation();
345-
this.hide(0);
346-
}
347-
}
348-
349339
/** Handles the touchend events on the host element. */
350340
_handleTouchend() {
351341
this.hide(this._defaultOptions.touchendHideDelay);
@@ -378,7 +368,7 @@ export class MatTooltip implements OnDestroy, OnInit {
378368
}
379369
});
380370

381-
this._overlayRef = this._overlay.create({
371+
const overlayRef = this._overlayRef = this._overlay.create({
382372
direction: this._dir,
383373
positionStrategy: strategy,
384374
panelClass: TOOLTIP_PANEL_CLASS,
@@ -387,11 +377,21 @@ export class MatTooltip implements OnDestroy, OnInit {
387377

388378
this._updatePosition();
389379

390-
this._overlayRef.detachments()
380+
overlayRef.keydownEvents()
381+
.pipe(takeUntil(this._destroyed))
382+
.subscribe(event => {
383+
if (this._isTooltipVisible() && event.keyCode === ESCAPE && !hasModifierKey(event)) {
384+
event.preventDefault();
385+
event.stopPropagation();
386+
this.hide(0);
387+
}
388+
});
389+
390+
overlayRef.detachments()
391391
.pipe(takeUntil(this._destroyed))
392392
.subscribe(() => this._detach());
393393

394-
return this._overlayRef;
394+
return overlayRef;
395395
}
396396

397397
/** Detaches the currently-attached tooltip. */

tools/public_api_guard/material/tooltip.d.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ export declare class MatTooltip implements OnDestroy, OnInit {
3434
main: OverlayConnectionPosition;
3535
fallback: OverlayConnectionPosition;
3636
};
37-
_handleKeydown(e: KeyboardEvent): void;
3837
_handleTouchend(): void;
3938
_isTooltipVisible(): boolean;
4039
hide(delay?: number): void;

0 commit comments

Comments
 (0)