Skip to content

Commit

Permalink
fix(select): close panel on alt + arrow key presses (#9250)
Browse files Browse the repository at this point in the history
* Closes the select panel when pressing alt + down/up arrow, based on the native select.
* Does some minor cleanup around the select tests.
  • Loading branch information
crisbeto authored and jelbourn committed Jan 29, 2018
1 parent 58b665e commit c3ec94d
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 35 deletions.
77 changes: 52 additions & 25 deletions src/lib/select/select.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,34 +62,13 @@ import {
/** The debounce interval when typing letters to select an option. */
const LETTER_KEY_DEBOUNCE_INTERVAL = 200;

const platform = new Platform();


describe('MatSelect', () => {
let overlayContainer: OverlayContainer;
let overlayContainerElement: HTMLElement;
let dir: {value: 'ltr'|'rtl'};
let scrolledSubject = new Subject();
let viewportRuler: ViewportRuler;

// Providers used for all mat-select tests
const commonProviders = [
{provide: Directionality, useFactory: () => dir = {value: 'ltr'}},
{
provide: ScrollDispatcher, useFactory: () => ({
scrolled: () => scrolledSubject.asObservable(),
}),
},
];

// NgModule imports used for all mat-select tests.
const commonModuleImports = [
MatFormFieldModule,
MatSelectModule,
ReactiveFormsModule,
FormsModule,
NoopAnimationsModule,
];
let platform: Platform;

/**
* Configures the test module for MatSelect with the given declarations. This is broken out so
Expand All @@ -99,14 +78,28 @@ describe('MatSelect', () => {
*/
function configureMatSelectTestingModule(declarations) {
TestBed.configureTestingModule({
imports: commonModuleImports,
imports: [
MatFormFieldModule,
MatSelectModule,
ReactiveFormsModule,
FormsModule,
NoopAnimationsModule,
],
declarations: declarations,
providers: commonProviders,
providers: [
{provide: Directionality, useFactory: () => dir = {value: 'ltr'}},
{
provide: ScrollDispatcher, useFactory: () => ({
scrolled: () => scrolledSubject.asObservable(),
}),
},
],
}).compileComponents();

inject([OverlayContainer], (oc: OverlayContainer) => {
inject([OverlayContainer, Platform], (oc: OverlayContainer, p: Platform) => {
overlayContainer = oc;
overlayContainerElement = oc.getContainerElement();
platform = p;
})();
}

Expand Down Expand Up @@ -283,6 +276,40 @@ describe('MatSelect', () => {
expect(formControl.value).toBeFalsy('Expected value not to have changed.');
}));

it('should should close when pressing ALT + DOWN_ARROW', fakeAsync(() => {
const {select: selectInstance} = fixture.componentInstance;

selectInstance.open();
fixture.detectChanges();

expect(selectInstance.panelOpen).toBe(true, 'Expected select to be open.');

const event = createKeyboardEvent('keydown', DOWN_ARROW);
Object.defineProperty(event, 'altKey', {get: () => true});

dispatchEvent(select, event);

expect(selectInstance.panelOpen).toBe(false, 'Expected select to be closed.');
expect(event.defaultPrevented).toBe(true, 'Expected default action to be prevented.');
}));

it('should should close when pressing ALT + UP_ARROW', fakeAsync(() => {
const {select: selectInstance} = fixture.componentInstance;

selectInstance.open();
fixture.detectChanges();

expect(selectInstance.panelOpen).toBe(true, 'Expected select to be open.');

const event = createKeyboardEvent('keydown', UP_ARROW);
Object.defineProperty(event, 'altKey', {get: () => true});

dispatchEvent(select, event);

expect(selectInstance.panelOpen).toBe(false, 'Expected select to be closed.');
expect(event.defaultPrevented).toBe(true, 'Expected default action to be prevented.');
}));

it('should be able to select options by typing on a closed select', fakeAsync(() => {
const formControl = fixture.componentInstance.control;
const options = fixture.componentInstance.options.toArray();
Expand Down
25 changes: 15 additions & 10 deletions src/lib/select/select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,7 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
const isArrowKey = keyCode === DOWN_ARROW || keyCode === UP_ARROW;
const isOpenKey = keyCode === ENTER || keyCode === SPACE;

// Open the select on ALT + arrow key to match the native <select>
if (isOpenKey || ((this.multiple || event.altKey) && isArrowKey)) {
event.preventDefault(); // prevents the page from scrolling down when pressing space
this.open();
Expand All @@ -658,23 +659,27 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
/** Handles keyboard events when the selected is open. */
private _handleOpenKeydown(event: KeyboardEvent): void {
const keyCode = event.keyCode;
const isArrowKey = keyCode === DOWN_ARROW || keyCode === UP_ARROW;
const manager = this._keyManager;

if (keyCode === HOME || keyCode === END) {
event.preventDefault();
keyCode === HOME ? this._keyManager.setFirstItemActive() :
this._keyManager.setLastItemActive();
} else if ((keyCode === ENTER || keyCode === SPACE) && this._keyManager.activeItem) {
keyCode === HOME ? manager.setFirstItemActive() : manager.setLastItemActive();
} else if (isArrowKey && event.altKey) {
// Close the select on ALT + arrow key to match the native <select>
event.preventDefault();
this.close();
} else if ((keyCode === ENTER || keyCode === SPACE) && manager.activeItem) {
event.preventDefault();
this._keyManager.activeItem._selectViaInteraction();
manager.activeItem._selectViaInteraction();
} else {
const isArrowKey = keyCode === DOWN_ARROW || keyCode === UP_ARROW;
const previouslyFocusedIndex = this._keyManager.activeItemIndex;
const previouslyFocusedIndex = manager.activeItemIndex;

this._keyManager.onKeydown(event);
manager.onKeydown(event);

if (this._multiple && isArrowKey && event.shiftKey && this._keyManager.activeItem &&
this._keyManager.activeItemIndex !== previouslyFocusedIndex) {
this._keyManager.activeItem._selectViaInteraction();
if (this._multiple && isArrowKey && event.shiftKey && manager.activeItem &&
manager.activeItemIndex !== previouslyFocusedIndex) {
manager.activeItem._selectViaInteraction();
}
}
}
Expand Down

0 comments on commit c3ec94d

Please sign in to comment.