Skip to content

Commit eea660f

Browse files
committed
fix(material/datepicker): fix date picker shortcuts
Fixes a bug in the Angular Material `datepicker` component where the datepicker should open/close only on alt+down and alt+up respectively but using shift + ctrl + alt + down or ctrl + alt + down the datepicker was opening, and same happens for closing the date picker added a condition to check if ctrl, shift or meta keys are present and if it is present restricting datepicker to not to open/close. Fixes #25868
1 parent 9a35ddf commit eea660f

File tree

4 files changed

+54
-4
lines changed

4 files changed

+54
-4
lines changed

src/cdk/keycodes/modifiers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
type ModifierKey = 'altKey' | 'shiftKey' | 'ctrlKey' | 'metaKey';
9+
export type ModifierKey = 'altKey' | 'shiftKey' | 'ctrlKey' | 'metaKey';
1010

1111
/**
1212
* Checks whether a modifier key is pressed.

src/material/datepicker/datepicker-base.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
ESCAPE,
1414
hasModifierKey,
1515
LEFT_ARROW,
16+
ModifierKey,
1617
PAGE_DOWN,
1718
PAGE_UP,
1819
RIGHT_ARROW,
@@ -815,6 +816,7 @@ export abstract class MatDatepickerBase<
815816

816817
/** Gets an observable that will emit when the overlay is supposed to be closed. */
817818
private _getCloseStream(overlayRef: OverlayRef) {
819+
const ctrlShiftMetaModifiers: ModifierKey[] = ['ctrlKey', 'shiftKey', 'metaKey'];
818820
return merge(
819821
overlayRef.backdropClick(),
820822
overlayRef.detachments(),
@@ -823,7 +825,12 @@ export abstract class MatDatepickerBase<
823825
// Closing on alt + up is only valid when there's an input associated with the datepicker.
824826
return (
825827
(event.keyCode === ESCAPE && !hasModifierKey(event)) ||
826-
(this.datepickerInput && hasModifierKey(event, 'altKey') && event.keyCode === UP_ARROW)
828+
(this.datepickerInput &&
829+
hasModifierKey(event, 'altKey') &&
830+
event.keyCode === UP_ARROW &&
831+
ctrlShiftMetaModifiers.every(
832+
(modifier: ModifierKey) => !hasModifierKey(event, modifier),
833+
))
827834
);
828835
}),
829836
),

src/material/datepicker/datepicker-input-base.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88

99
import {BooleanInput, coerceBooleanProperty} from '@angular/cdk/coercion';
10-
import {DOWN_ARROW} from '@angular/cdk/keycodes';
10+
import {DOWN_ARROW, hasModifierKey, ModifierKey} from '@angular/cdk/keycodes';
1111
import {
1212
Directive,
1313
ElementRef,
@@ -291,7 +291,11 @@ export abstract class MatDatepickerInputBase<S, D = ExtractDateTypeFromSelection
291291
}
292292

293293
_onKeydown(event: KeyboardEvent) {
294-
const isAltDownArrow = event.altKey && event.keyCode === DOWN_ARROW;
294+
const ctrlShiftMetaModifiers: ModifierKey[] = ['ctrlKey', 'shiftKey', 'metaKey'];
295+
const isAltDownArrow =
296+
hasModifierKey(event, 'altKey') &&
297+
event.keyCode === DOWN_ARROW &&
298+
ctrlShiftMetaModifiers.every((modifier: ModifierKey) => !hasModifierKey(event, modifier));
295299

296300
if (isAltDownArrow && !this._elementRef.nativeElement.readOnly) {
297301
this._openPopup();

src/material/datepicker/datepicker.spec.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,27 @@ describe('MatDatepicker', () => {
550550
expect(testComponent.datepicker.opened).toBe(false);
551551
}));
552552

553+
it('should not close the datepicker when using CTRL + SHIFT + ALT + UP_ARROW', fakeAsync(() => {
554+
testComponent.datepicker.open();
555+
fixture.detectChanges();
556+
tick();
557+
flush();
558+
559+
expect(testComponent.datepicker.opened).toBe(true);
560+
561+
const event = createKeyboardEvent('keydown', UP_ARROW, undefined, {
562+
alt: true,
563+
shift: true,
564+
control: true,
565+
});
566+
567+
dispatchEvent(document.body, event);
568+
fixture.detectChanges();
569+
flush();
570+
571+
expect(testComponent.datepicker.opened).toBe(true);
572+
}));
573+
553574
it('should open the datepicker using ALT + DOWN_ARROW', fakeAsync(() => {
554575
expect(testComponent.datepicker.opened).toBe(false);
555576

@@ -581,6 +602,24 @@ describe('MatDatepicker', () => {
581602
expect(event.defaultPrevented).toBe(false);
582603
}));
583604

605+
it('should not open the datepicker using SHIFT + CTRL + ALT + DOWN_ARROW', fakeAsync(() => {
606+
expect(testComponent.datepicker.opened).toBe(false);
607+
608+
const event = createKeyboardEvent('keydown', DOWN_ARROW, undefined, {
609+
alt: true,
610+
shift: true,
611+
control: true,
612+
});
613+
614+
dispatchEvent(fixture.nativeElement.querySelector('input'), event);
615+
fixture.detectChanges();
616+
tick();
617+
flush();
618+
619+
expect(testComponent.datepicker.opened).toBe(false);
620+
expect(event.defaultPrevented).toBe(false);
621+
}));
622+
584623
it('should show the invisible close button on focus', fakeAsync(() => {
585624
testComponent.opened = true;
586625
fixture.detectChanges();

0 commit comments

Comments
 (0)