Skip to content

Commit 0bd3890

Browse files
crisbetojelbourn
authored andcommitted
fix(stepper): don't handle enter/space when modifier key is pressed (#13827)
Along the same lines as #13790. Doesn't handle the prevent the default action for ENTER and SPACE when one of the modifier keys is pressed, in order to avoid interefering with the native keyboard shortcuts.
1 parent 4217171 commit 0bd3890

File tree

2 files changed

+53
-6
lines changed

2 files changed

+53
-6
lines changed

src/cdk/stepper/stepper.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -434,19 +434,24 @@ export class CdkStepper implements AfterViewInit, OnDestroy {
434434
}
435435

436436
_onKeydown(event: KeyboardEvent) {
437+
// TODO(crisbeto): move into a CDK utility once
438+
// the similar PRs for other components are merged in.
439+
const hasModifier = event.altKey || event.shiftKey || event.ctrlKey || event.metaKey;
437440
const keyCode = event.keyCode;
441+
const manager = this._keyManager;
438442

439-
if (this._keyManager.activeItemIndex != null && (keyCode === SPACE || keyCode === ENTER)) {
440-
this.selectedIndex = this._keyManager.activeItemIndex;
443+
if (manager.activeItemIndex != null && !hasModifier &&
444+
(keyCode === SPACE || keyCode === ENTER)) {
445+
this.selectedIndex = manager.activeItemIndex;
441446
event.preventDefault();
442447
} else if (keyCode === HOME) {
443-
this._keyManager.setFirstItemActive();
448+
manager.setFirstItemActive();
444449
event.preventDefault();
445450
} else if (keyCode === END) {
446-
this._keyManager.setLastItemActive();
451+
manager.setLastItemActive();
447452
event.preventDefault();
448453
} else {
449-
this._keyManager.onKeydown(event);
454+
manager.onKeydown(event);
450455
}
451456
}
452457

src/lib/stepper/stepper.spec.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
UP_ARROW,
1111
} from '@angular/cdk/keycodes';
1212
import {StepperOrientation, MAT_STEPPER_GLOBAL_OPTIONS, STEP_STATE} from '@angular/cdk/stepper';
13-
import {dispatchKeyboardEvent} from '@angular/cdk/testing';
13+
import {dispatchKeyboardEvent, createKeyboardEvent, dispatchEvent} from '@angular/cdk/testing';
1414
import {Component, DebugElement, EventEmitter, OnInit, Type, Provider} from '@angular/core';
1515
import {ComponentFixture, fakeAsync, flush, inject, TestBed} from '@angular/core/testing';
1616
import {
@@ -345,6 +345,16 @@ describe('MatStepper', () => {
345345

346346
expect(stepperComponent.selectedIndex).toBe(1);
347347
});
348+
349+
it('should not do anything when pressing the ENTER key with a modifier', () => {
350+
const stepHeaders = fixture.debugElement.queryAll(By.css('.mat-vertical-stepper-header'));
351+
assertSelectKeyWithModifierInteraction(fixture, stepHeaders, 'vertical', ENTER);
352+
});
353+
354+
it('should not do anything when pressing the SPACE key with a modifier', () => {
355+
const stepHeaders = fixture.debugElement.queryAll(By.css('.mat-vertical-stepper-header'));
356+
assertSelectKeyWithModifierInteraction(fixture, stepHeaders, 'vertical', SPACE);
357+
});
348358
});
349359

350360
describe('basic stepper when attempting to set the selected step too early', () => {
@@ -1063,6 +1073,38 @@ function assertArrowKeyInteractionInRtl(fixture: ComponentFixture<any>,
10631073
expect(stepperComponent._getFocusIndex()).toBe(0);
10641074
}
10651075

1076+
/** Asserts that keyboard interaction works correctly when the user is pressing a modifier key. */
1077+
function assertSelectKeyWithModifierInteraction(fixture: ComponentFixture<any>,
1078+
stepHeaders: DebugElement[],
1079+
orientation: StepperOrientation,
1080+
selectionKey: number) {
1081+
const stepperComponent = fixture.debugElement.query(By.directive(MatStepper)).componentInstance;
1082+
const modifiers = ['altKey', 'shiftKey', 'ctrlKey', 'metaKey'];
1083+
1084+
expect(stepperComponent._getFocusIndex()).toBe(0);
1085+
expect(stepperComponent.selectedIndex).toBe(0);
1086+
1087+
dispatchKeyboardEvent(stepHeaders[0].nativeElement, 'keydown',
1088+
orientation === 'vertical' ? DOWN_ARROW : RIGHT_ARROW);
1089+
fixture.detectChanges();
1090+
1091+
expect(stepperComponent._getFocusIndex())
1092+
.toBe(1, 'Expected index of focused step to increase by 1 after pressing the next key.');
1093+
expect(stepperComponent.selectedIndex)
1094+
.toBe(0, 'Expected index of selected step to remain unchanged after pressing the next key.');
1095+
1096+
modifiers.forEach(modifier => {
1097+
const event: KeyboardEvent = createKeyboardEvent('keydown', selectionKey);
1098+
Object.defineProperty(event, modifier, {get: () => true});
1099+
dispatchEvent(stepHeaders[1].nativeElement, event);
1100+
fixture.detectChanges();
1101+
1102+
expect(stepperComponent.selectedIndex).toBe(0, `Expected selected index to remain unchanged ` +
1103+
`when pressing the selection key with ${modifier} modifier.`);
1104+
expect(event.defaultPrevented).toBe(false);
1105+
});
1106+
}
1107+
10661108
function asyncValidator(minLength: number, validationTrigger: Subject<void>): AsyncValidatorFn {
10671109
return (control: AbstractControl): Observable<ValidationErrors | null> => {
10681110
return validationTrigger.pipe(

0 commit comments

Comments
 (0)