Skip to content

Commit cd7ec58

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 473d4c6 commit cd7ec58

File tree

3 files changed

+35
-19
lines changed

3 files changed

+35
-19
lines changed

src/material/tooltip/tooltip.spec.ts

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

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

@@ -655,7 +674,7 @@ describe('MatTooltip', () => {
655674
tick(0);
656675
fixture.detectChanges();
657676

658-
const event = dispatchKeyboardEvent(buttonElement, 'keydown', ESCAPE);
677+
const event = dispatchKeyboardEvent(document.body, 'keydown', ESCAPE);
659678
fixture.detectChanges();
660679
flush();
661680

@@ -669,7 +688,7 @@ describe('MatTooltip', () => {
669688

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

src/material/tooltip/tooltip.ts

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,6 @@ export function MAT_TOOLTIP_DEFAULT_OPTIONS_FACTORY(): MatTooltipDefaultOptions
131131
@Directive({
132132
selector: '[matTooltip]',
133133
exportAs: 'matTooltip',
134-
host: {
135-
'(keydown)': '_handleKeydown($event)',
136-
},
137134
})
138135
export class MatTooltip implements OnDestroy, OnInit {
139136
_overlayRef: OverlayRef | null;
@@ -356,15 +353,6 @@ export class MatTooltip implements OnDestroy, OnInit {
356353
return !!this._tooltipInstance && this._tooltipInstance.isVisible();
357354
}
358355

359-
/** Handles the keydown events on the host element. */
360-
_handleKeydown(e: KeyboardEvent) {
361-
if (this._isTooltipVisible() && e.keyCode === ESCAPE && !hasModifierKey(e)) {
362-
e.preventDefault();
363-
e.stopPropagation();
364-
this.hide(0);
365-
}
366-
}
367-
368356
/** Create the overlay config and position strategy */
369357
private _createOverlay(): OverlayRef {
370358
if (this._overlayRef) {
@@ -392,7 +380,7 @@ export class MatTooltip implements OnDestroy, OnInit {
392380
}
393381
});
394382

395-
this._overlayRef = this._overlay.create({
383+
const overlayRef = this._overlayRef = this._overlay.create({
396384
direction: this._dir,
397385
positionStrategy: strategy,
398386
panelClass: TOOLTIP_PANEL_CLASS,
@@ -401,11 +389,21 @@ export class MatTooltip implements OnDestroy, OnInit {
401389

402390
this._updatePosition();
403391

404-
this._overlayRef.detachments()
392+
overlayRef.keydownEvents()
393+
.pipe(takeUntil(this._destroyed))
394+
.subscribe(event => {
395+
if (this._isTooltipVisible() && event.keyCode === ESCAPE && !hasModifierKey(event)) {
396+
event.preventDefault();
397+
event.stopPropagation();
398+
this.hide(0);
399+
}
400+
});
401+
402+
overlayRef.detachments()
405403
.pipe(takeUntil(this._destroyed))
406404
.subscribe(() => this._detach());
407405

408-
return this._overlayRef;
406+
return overlayRef;
409407
}
410408

411409
/** 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
@@ -36,7 +36,6 @@ export declare class MatTooltip implements OnDestroy, OnInit {
3636
main: OverlayConnectionPosition;
3737
fallback: OverlayConnectionPosition;
3838
};
39-
_handleKeydown(e: KeyboardEvent): void;
4039
_isTooltipVisible(): boolean;
4140
hide(delay?: number): void;
4241
ngOnDestroy(): void;

0 commit comments

Comments
 (0)