Skip to content

Commit b8ff582

Browse files
crisbetojelbourn
authored andcommitted
refactor(datepicker): use common overlay keyboard handling (#8941)
Switches the datepicker to use the `OverlayKeyboardDispatcher`, rather than managing the keyboard logic itself. This simplifies things and handles the cases where the popup won't close if it lost focus.
1 parent c349c58 commit b8ff582

File tree

2 files changed

+13
-36
lines changed

2 files changed

+13
-36
lines changed

src/lib/datepicker/datepicker.spec.ts

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
11
import {ENTER, ESCAPE, RIGHT_ARROW} from '@angular/cdk/keycodes';
22
import {OverlayContainer, Overlay, ScrollDispatcher} from '@angular/cdk/overlay';
3-
import {
4-
createKeyboardEvent,
5-
dispatchEvent,
6-
dispatchFakeEvent,
7-
dispatchKeyboardEvent,
8-
dispatchMouseEvent,
9-
} from '@angular/cdk/testing';
3+
import {dispatchFakeEvent, dispatchKeyboardEvent, dispatchMouseEvent} from '@angular/cdk/testing';
104
import {Component, ViewChild} from '@angular/core';
115
import {fakeAsync, flush, ComponentFixture, inject, TestBed} from '@angular/core/testing';
126
import {FormControl, FormsModule, ReactiveFormsModule} from '@angular/forms';
@@ -157,26 +151,18 @@ describe('MatDatepicker', () => {
157151
expect(parseInt(getComputedStyle(popup).height as string)).toBe(0);
158152
});
159153

160-
it('should close the popup when pressing ESCAPE', () => {
154+
it('should close the popup when pressing ESCAPE', fakeAsync(() => {
161155
testComponent.datepicker.open();
162156
fixture.detectChanges();
163157

164-
let content = document.querySelector('.cdk-overlay-pane mat-datepicker-content')!;
165-
expect(content).toBeTruthy('Expected datepicker to be open.');
166-
167-
const keyboardEvent = createKeyboardEvent('keydown', ESCAPE);
168-
const stopPropagationSpy = spyOn(keyboardEvent, 'stopPropagation').and.callThrough();
158+
expect(testComponent.datepicker.opened).toBe(true, 'Expected datepicker to be open.');
169159

170-
dispatchEvent(content, keyboardEvent);
160+
dispatchKeyboardEvent(document.body, 'keydown', ESCAPE);
171161
fixture.detectChanges();
162+
flush();
172163

173-
content = document.querySelector('.cdk-overlay-pane mat-datepicker-content')!;
174-
175-
expect(content).toBeFalsy('Expected datepicker to be closed.');
176-
expect(stopPropagationSpy).toHaveBeenCalled();
177-
expect(keyboardEvent.defaultPrevented)
178-
.toBe(true, 'Expected default ESCAPE action to be prevented.');
179-
});
164+
expect(testComponent.datepicker.opened).toBe(false, 'Expected datepicker to be closed.');
165+
}));
180166

181167
it('close should close dialog', fakeAsync(() => {
182168
testComponent.touch = true;

src/lib/datepicker/datepicker.ts

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
} from '@angular/cdk/overlay';
2020
import {ComponentPortal} from '@angular/cdk/portal';
2121
import {take} from 'rxjs/operators/take';
22+
import {filter} from 'rxjs/operators/filter';
2223
import {
2324
AfterContentInit,
2425
ChangeDetectionStrategy,
@@ -83,7 +84,6 @@ export const MAT_DATEPICKER_SCROLL_STRATEGY_PROVIDER = {
8384
host: {
8485
'class': 'mat-datepicker-content',
8586
'[class.mat-datepicker-content-touch]': 'datepicker.touchUi',
86-
'(keydown)': '_handleKeydown($event)',
8787
},
8888
exportAs: 'matDatepickerContent',
8989
encapsulation: ViewEncapsulation.None,
@@ -98,18 +98,6 @@ export class MatDatepickerContent<D> implements AfterContentInit {
9898
ngAfterContentInit() {
9999
this._calendar._focusActiveCell();
100100
}
101-
102-
/**
103-
* Handles keydown event on datepicker content.
104-
* @param event The event.
105-
*/
106-
_handleKeydown(event: KeyboardEvent): void {
107-
if (event.keyCode === ESCAPE) {
108-
this.datepicker.close();
109-
event.preventDefault();
110-
event.stopPropagation();
111-
}
112-
}
113101
}
114102

115103

@@ -382,8 +370,11 @@ export class MatDatepicker<D> implements OnDestroy {
382370

383371
this._popupRef = this._overlay.create(overlayConfig);
384372

385-
merge(this._popupRef.backdropClick(), this._popupRef.detachments())
386-
.subscribe(() => this.close());
373+
merge(
374+
this._popupRef.backdropClick(),
375+
this._popupRef.detachments(),
376+
this._popupRef.keydownEvents().pipe(filter(event => event.keyCode === ESCAPE))
377+
).subscribe(() => this.close());
387378
}
388379

389380
/** Create the popup PositionStrategy. */

0 commit comments

Comments
 (0)