Skip to content

Commit

Permalink
fix(select): announce value changes with arrow keys while closed (#14540
Browse files Browse the repository at this point in the history
)

We support the functionality from the native select where people can change the value while the dropdown is closed via the arrow keys, however for screen reader users there is no feedback that the value has changed. These changes announce the select value if it changes.
  • Loading branch information
crisbeto authored and jelbourn committed Dec 20, 2018
1 parent fe7f95e commit 3f9a125
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 2 deletions.
1 change: 1 addition & 0 deletions src/lib/select/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ ng_test_library(
"@angular//packages/platform-browser/animations",
"@rxjs",
"@rxjs//operators",
"//src/cdk/a11y",
"//src/cdk/bidi",
"//src/cdk/keycodes",
"//src/cdk/overlay",
Expand Down
24 changes: 24 additions & 0 deletions src/lib/select/select.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import {
import {MatFormFieldModule} from '@angular/material/form-field';
import {By} from '@angular/platform-browser';
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
import {LiveAnnouncer} from '@angular/cdk/a11y';
import {Subject, Subscription, EMPTY, Observable} from 'rxjs';
import {map} from 'rxjs/operators';
import {MatSelectModule} from './index';
Expand Down Expand Up @@ -291,6 +292,8 @@ describe('MatSelect', () => {
expect(options[1].selected).toBe(true, 'Expected second option to be selected.');
expect(formControl.value).toBe(options[1].value,
'Expected value from second option to have been set on the model.');

flush();
}));

it('should select first/last options via the HOME/END keys on a closed select',
Expand All @@ -314,6 +317,8 @@ describe('MatSelect', () => {
expect(firstOption.selected).toBe(true, 'Expected first option to be selected.');
expect(formControl.value).toBe(firstOption.value,
'Expected value from first option to have been set on the model.');

flush();
}));

it('should resume focus from selected item after selecting via click', fakeAsync(() => {
Expand All @@ -336,6 +341,7 @@ describe('MatSelect', () => {
fixture.detectChanges();

expect(formControl.value).toBe(options[4].value);
flush();
}));

it('should select options via LEFT/RIGHT arrow keys on a closed select', fakeAsync(() => {
Expand Down Expand Up @@ -363,8 +369,20 @@ describe('MatSelect', () => {
expect(options[1].selected).toBe(true, 'Expected second option to be selected.');
expect(formControl.value).toBe(options[1].value,
'Expected value from second option to have been set on the model.');
flush();
}));

it('should announce changes via the keyboard on a closed select',
fakeAsync(inject([LiveAnnouncer], (liveAnnouncer: LiveAnnouncer) => {
spyOn(liveAnnouncer, 'announce');

dispatchKeyboardEvent(select, 'keydown', RIGHT_ARROW);

expect(liveAnnouncer.announce).toHaveBeenCalledWith('Steak');

flush();
})));

it('should open a single-selection select using ALT + DOWN_ARROW', fakeAsync(() => {
const {control: formControl, select: selectInstance} = fixture.componentInstance;

Expand Down Expand Up @@ -534,6 +552,7 @@ describe('MatSelect', () => {

expect(formControl.value).toBe('pasta-6');
expect(fixture.componentInstance.options.toArray()[6].selected).toBe(true);
flush();
}));

it('should not shift focus when the selected options are updated programmatically ' +
Expand Down Expand Up @@ -583,6 +602,8 @@ describe('MatSelect', () => {
dispatchKeyboardEvent(select, 'keydown', DOWN_ARROW);

expect(lastOption.selected).toBe(true, 'Expected last option to stay selected.');

flush();
}));

it('should not open a multiple select when tabbing through', fakeAsync(() => {
Expand Down Expand Up @@ -694,6 +715,7 @@ describe('MatSelect', () => {
expect(spy).toHaveBeenCalledWith(true);

subscription.unsubscribe();
flush();
}));

it('should be able to focus the select trigger', fakeAsync(() => {
Expand Down Expand Up @@ -1898,6 +1920,8 @@ describe('MatSelect', () => {
dispatchKeyboardEvent(select, 'keydown', DOWN_ARROW);

expect(fixture.componentInstance.changeListener).toHaveBeenCalledTimes(1);

flush();
}));
});

Expand Down
17 changes: 15 additions & 2 deletions src/lib/select/select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {ActiveDescendantKeyManager} from '@angular/cdk/a11y';
import {ActiveDescendantKeyManager, LiveAnnouncer} from '@angular/cdk/a11y';
import {Directionality} from '@angular/cdk/bidi';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
import {SelectionModel} from '@angular/cdk/collections';
Expand Down Expand Up @@ -485,7 +485,12 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
@Optional() private _parentFormField: MatFormField,
@Self() @Optional() public ngControl: NgControl,
@Attribute('tabindex') tabIndex: string,
@Inject(MAT_SELECT_SCROLL_STRATEGY) scrollStrategyFactory: any) {
@Inject(MAT_SELECT_SCROLL_STRATEGY) scrollStrategyFactory: any,
/**
* @deprecated _liveAnnouncer to be turned into a required parameter.
* @breaking-change 8.0.0
*/
private _liveAnnouncer?: LiveAnnouncer) {
super(elementRef, _defaultErrorStateMatcher, _parentForm,
_parentFormGroup, ngControl);

Expand Down Expand Up @@ -700,12 +705,20 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
event.preventDefault(); // prevents the page from scrolling down when pressing space
this.open();
} else if (!this.multiple) {
const selectedOption = this.selected;

if (keyCode === HOME || keyCode === END) {
keyCode === HOME ? manager.setFirstItemActive() : manager.setLastItemActive();
event.preventDefault();
} else {
manager.onKeydown(event);
}

// Since the value has changed, we need to announce it ourselves.
// @breaking-change 8.0.0 remove null check for _liveAnnouncer.
if (this._liveAnnouncer && selectedOption !== this.selected) {
this._liveAnnouncer.announce((this.selected as MatOption).viewValue);
}
}
}

Expand Down

0 comments on commit 3f9a125

Please sign in to comment.