Skip to content

Commit a9e550e

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 a22a9fa commit a9e550e

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
@@ -442,19 +442,24 @@ export class CdkStepper implements AfterViewInit, OnDestroy {
442442
}
443443

444444
_onKeydown(event: KeyboardEvent) {
445+
// TODO(crisbeto): move into a CDK utility once
446+
// the similar PRs for other components are merged in.
447+
const hasModifier = event.altKey || event.shiftKey || event.ctrlKey || event.metaKey;
445448
const keyCode = event.keyCode;
449+
const manager = this._keyManager;
446450

447-
if (this._keyManager.activeItemIndex != null && (keyCode === SPACE || keyCode === ENTER)) {
448-
this.selectedIndex = this._keyManager.activeItemIndex;
451+
if (manager.activeItemIndex != null && !hasModifier &&
452+
(keyCode === SPACE || keyCode === ENTER)) {
453+
this.selectedIndex = manager.activeItemIndex;
449454
event.preventDefault();
450455
} else if (keyCode === HOME) {
451-
this._keyManager.setFirstItemActive();
456+
manager.setFirstItemActive();
452457
event.preventDefault();
453458
} else if (keyCode === END) {
454-
this._keyManager.setLastItemActive();
459+
manager.setLastItemActive();
455460
event.preventDefault();
456461
} else {
457-
this._keyManager.onKeydown(event);
462+
manager.onKeydown(event);
458463
}
459464
}
460465

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)