Skip to content

Commit a34feed

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 5b3e846 commit a34feed

File tree

2 files changed

+35
-16
lines changed

2 files changed

+35
-16
lines changed

src/material/tooltip/tooltip.spec.ts

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

630+
it('should hide when pressing escape', fakeAsync(() => {
631+
tooltipDirective.show();
632+
tick(0);
633+
fixture.detectChanges();
634+
tick(500);
635+
636+
expect(tooltipDirective._isTooltipVisible()).toBe(true);
637+
expect(overlayContainerElement.textContent).toContain(initialTooltipMessage);
638+
639+
dispatchKeyboardEvent(document.body, 'keydown', ESCAPE);
640+
tick(0);
641+
fixture.detectChanges();
642+
tick(500);
643+
fixture.detectChanges();
644+
645+
expect(tooltipDirective._isTooltipVisible()).toBe(false);
646+
expect(overlayContainerElement.textContent).toBe('');
647+
}));
648+
630649
it('should not throw when pressing ESCAPE', fakeAsync(() => {
631650
expect(() => {
632-
dispatchKeyboardEvent(buttonElement, 'keydown', ESCAPE);
651+
dispatchKeyboardEvent(document.body, 'keydown', ESCAPE);
633652
fixture.detectChanges();
634653
}).not.toThrow();
635654

@@ -642,7 +661,7 @@ describe('MatTooltip', () => {
642661
tick(0);
643662
fixture.detectChanges();
644663

645-
const event = dispatchKeyboardEvent(buttonElement, 'keydown', ESCAPE);
664+
const event = dispatchKeyboardEvent(document.body, 'keydown', ESCAPE);
646665
fixture.detectChanges();
647666
flush();
648667

@@ -656,7 +675,7 @@ describe('MatTooltip', () => {
656675

657676
const event = createKeyboardEvent('keydown', ESCAPE);
658677
Object.defineProperty(event, 'altKey', {get: () => true});
659-
dispatchEvent(buttonElement, event);
678+
dispatchEvent(document.body, event);
660679
fixture.detectChanges();
661680
flush();
662681

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
})
@@ -329,15 +328,6 @@ export class MatTooltip implements OnDestroy, OnInit {
329328
return !!this._tooltipInstance && this._tooltipInstance.isVisible();
330329
}
331330

332-
/** Handles the keydown events on the host element. */
333-
_handleKeydown(e: KeyboardEvent) {
334-
if (this._isTooltipVisible() && e.keyCode === ESCAPE && !hasModifierKey(e)) {
335-
e.preventDefault();
336-
e.stopPropagation();
337-
this.hide(0);
338-
}
339-
}
340-
341331
/** Handles the touchend events on the host element. */
342332
_handleTouchend() {
343333
this.hide(this._defaultOptions.touchendHideDelay);
@@ -370,7 +360,7 @@ export class MatTooltip implements OnDestroy, OnInit {
370360
}
371361
});
372362

373-
this._overlayRef = this._overlay.create({
363+
const overlayRef = this._overlayRef = this._overlay.create({
374364
direction: this._dir,
375365
positionStrategy: strategy,
376366
panelClass: TOOLTIP_PANEL_CLASS,
@@ -379,11 +369,21 @@ export class MatTooltip implements OnDestroy, OnInit {
379369

380370
this._updatePosition();
381371

382-
this._overlayRef.detachments()
372+
overlayRef.keydownEvents()
373+
.pipe(takeUntil(this._destroyed))
374+
.subscribe(event => {
375+
if (this._isTooltipVisible() && event.keyCode === ESCAPE && !hasModifierKey(event)) {
376+
event.preventDefault();
377+
event.stopPropagation();
378+
this.hide(0);
379+
}
380+
});
381+
382+
overlayRef.detachments()
383383
.pipe(takeUntil(this._destroyed))
384384
.subscribe(() => this._detach());
385385

386-
return this._overlayRef;
386+
return overlayRef;
387387
}
388388

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

0 commit comments

Comments
 (0)