Skip to content

Commit fe45385

Browse files
crisbetojelbourn
authored andcommitted
fix(selection-list): allow users to jump focus to a particular item by typing (#9026)
Similarly to our other listbox-based components, this allows people to move focus to a particular item by typing the first letters of its label.
1 parent fb2048b commit fe45385

File tree

3 files changed

+40
-4
lines changed

3 files changed

+40
-4
lines changed

src/lib/list/list-option.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
[matRippleTrigger]="_getHostElement()"
88
[matRippleDisabled]="_isRippleDisabled()"></div>
99

10-
<mat-pseudo-checkbox #autocheckbox
10+
<mat-pseudo-checkbox
1111
[state]="selected ? 'checked' : 'unchecked'"
1212
[disabled]="disabled"></mat-pseudo-checkbox>
1313

14-
<div class="mat-list-text"><ng-content></ng-content></div>
14+
<div class="mat-list-text" #text><ng-content></ng-content></div>
1515

1616
</div>

src/lib/list/selection-list.spec.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import {DOWN_ARROW, END, ENTER, HOME, SPACE, UP_ARROW} from '@angular/cdk/keycodes';
22
import {Platform} from '@angular/cdk/platform';
3-
import {createKeyboardEvent, dispatchFakeEvent, dispatchKeyboardEvent} from '@angular/cdk/testing';
3+
import {
4+
createKeyboardEvent,
5+
dispatchFakeEvent,
6+
dispatchEvent,
7+
dispatchKeyboardEvent,
8+
} from '@angular/cdk/testing';
49
import {Component, DebugElement} from '@angular/core';
510
import {async, ComponentFixture, fakeAsync, inject, TestBed, tick} from '@angular/core/testing';
611
import {FormControl, FormsModule, NgModel, ReactiveFormsModule} from '@angular/forms';
@@ -287,6 +292,25 @@ describe('MatSelectionList without forms', () => {
287292
expect(event.defaultPrevented).toBe(true);
288293
});
289294

295+
it('should be able to jump focus down to an item by typing', fakeAsync(() => {
296+
const listEl = selectionList.nativeElement;
297+
const manager = selectionList.componentInstance._keyManager;
298+
299+
expect(manager.activeItemIndex).toBe(-1);
300+
301+
dispatchEvent(listEl, createKeyboardEvent('keydown', 83, undefined, 's'));
302+
fixture.detectChanges();
303+
tick(200);
304+
305+
expect(manager.activeItemIndex).toBe(1);
306+
307+
dispatchEvent(listEl, createKeyboardEvent('keydown', 68, undefined, 'd'));
308+
fixture.detectChanges();
309+
tick(200);
310+
311+
expect(manager.activeItemIndex).toBe(3);
312+
}));
313+
290314
it('should be able to select all options', () => {
291315
const list: MatSelectionList = selectionList.componentInstance;
292316

src/lib/list/selection-list.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
Optional,
2828
Output,
2929
QueryList,
30+
ViewChild,
3031
ViewEncapsulation,
3132
} from '@angular/core';
3233
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
@@ -118,6 +119,9 @@ export class MatListOption extends _MatListOptionMixinBase
118119

119120
@ContentChildren(MatLine) _lines: QueryList<MatLine>;
120121

122+
/** DOM element containing the item's text. */
123+
@ViewChild('text') _text: ElementRef;
124+
121125
/** Whether the label should appear before or after the checkbox. Defaults to 'after' */
122126
@Input() checkboxPosition: 'before' | 'after' = 'after';
123127

@@ -197,6 +201,14 @@ export class MatListOption extends _MatListOptionMixinBase
197201
this._element.nativeElement.focus();
198202
}
199203

204+
/**
205+
* Returns the list item's text label. Implemented as a part of the FocusKeyManager.
206+
* @docs-private
207+
*/
208+
getLabel() {
209+
return this._text ? this._text.nativeElement.textContent : '';
210+
}
211+
200212
/** Whether this list item should show a ripple effect when clicked. */
201213
_isRippleDisabled() {
202214
return this.disabled || this.disableRipple || this.selectionList.disableRipple;
@@ -309,7 +321,7 @@ export class MatSelectionList extends _MatSelectionListMixinBase implements Focu
309321
}
310322

311323
ngAfterContentInit(): void {
312-
this._keyManager = new FocusKeyManager<MatListOption>(this.options).withWrap();
324+
this._keyManager = new FocusKeyManager<MatListOption>(this.options).withWrap().withTypeAhead();
313325

314326
if (this._tempValues) {
315327
this._setOptionsFromValues(this._tempValues);

0 commit comments

Comments
 (0)