Skip to content

Commit

Permalink
fix(stepper): don't handle enter/space when modifier key is pressed (#…
Browse files Browse the repository at this point in the history
…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.
  • Loading branch information
crisbeto authored and jelbourn committed Nov 6, 2018
1 parent 4217171 commit 0bd3890
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 6 deletions.
15 changes: 10 additions & 5 deletions src/cdk/stepper/stepper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -434,19 +434,24 @@ export class CdkStepper implements AfterViewInit, OnDestroy {
}

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

if (this._keyManager.activeItemIndex != null && (keyCode === SPACE || keyCode === ENTER)) {
this.selectedIndex = this._keyManager.activeItemIndex;
if (manager.activeItemIndex != null && !hasModifier &&
(keyCode === SPACE || keyCode === ENTER)) {
this.selectedIndex = manager.activeItemIndex;
event.preventDefault();
} else if (keyCode === HOME) {
this._keyManager.setFirstItemActive();
manager.setFirstItemActive();
event.preventDefault();
} else if (keyCode === END) {
this._keyManager.setLastItemActive();
manager.setLastItemActive();
event.preventDefault();
} else {
this._keyManager.onKeydown(event);
manager.onKeydown(event);
}
}

Expand Down
44 changes: 43 additions & 1 deletion src/lib/stepper/stepper.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
UP_ARROW,
} from '@angular/cdk/keycodes';
import {StepperOrientation, MAT_STEPPER_GLOBAL_OPTIONS, STEP_STATE} from '@angular/cdk/stepper';
import {dispatchKeyboardEvent} from '@angular/cdk/testing';
import {dispatchKeyboardEvent, createKeyboardEvent, dispatchEvent} from '@angular/cdk/testing';
import {Component, DebugElement, EventEmitter, OnInit, Type, Provider} from '@angular/core';
import {ComponentFixture, fakeAsync, flush, inject, TestBed} from '@angular/core/testing';
import {
Expand Down Expand Up @@ -345,6 +345,16 @@ describe('MatStepper', () => {

expect(stepperComponent.selectedIndex).toBe(1);
});

it('should not do anything when pressing the ENTER key with a modifier', () => {
const stepHeaders = fixture.debugElement.queryAll(By.css('.mat-vertical-stepper-header'));
assertSelectKeyWithModifierInteraction(fixture, stepHeaders, 'vertical', ENTER);
});

it('should not do anything when pressing the SPACE key with a modifier', () => {
const stepHeaders = fixture.debugElement.queryAll(By.css('.mat-vertical-stepper-header'));
assertSelectKeyWithModifierInteraction(fixture, stepHeaders, 'vertical', SPACE);
});
});

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

/** Asserts that keyboard interaction works correctly when the user is pressing a modifier key. */
function assertSelectKeyWithModifierInteraction(fixture: ComponentFixture<any>,
stepHeaders: DebugElement[],
orientation: StepperOrientation,
selectionKey: number) {
const stepperComponent = fixture.debugElement.query(By.directive(MatStepper)).componentInstance;
const modifiers = ['altKey', 'shiftKey', 'ctrlKey', 'metaKey'];

expect(stepperComponent._getFocusIndex()).toBe(0);
expect(stepperComponent.selectedIndex).toBe(0);

dispatchKeyboardEvent(stepHeaders[0].nativeElement, 'keydown',
orientation === 'vertical' ? DOWN_ARROW : RIGHT_ARROW);
fixture.detectChanges();

expect(stepperComponent._getFocusIndex())
.toBe(1, 'Expected index of focused step to increase by 1 after pressing the next key.');
expect(stepperComponent.selectedIndex)
.toBe(0, 'Expected index of selected step to remain unchanged after pressing the next key.');

modifiers.forEach(modifier => {
const event: KeyboardEvent = createKeyboardEvent('keydown', selectionKey);
Object.defineProperty(event, modifier, {get: () => true});
dispatchEvent(stepHeaders[1].nativeElement, event);
fixture.detectChanges();

expect(stepperComponent.selectedIndex).toBe(0, `Expected selected index to remain unchanged ` +
`when pressing the selection key with ${modifier} modifier.`);
expect(event.defaultPrevented).toBe(false);
});
}

function asyncValidator(minLength: number, validationTrigger: Subject<void>): AsyncValidatorFn {
return (control: AbstractControl): Observable<ValidationErrors | null> => {
return validationTrigger.pipe(
Expand Down

0 comments on commit 0bd3890

Please sign in to comment.