From af44b9dad774b91eac61f748c76177939587e4f4 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Mon, 8 Jan 2018 20:20:07 +0100 Subject: [PATCH] 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. --- src/lib/list/list-option.html | 4 ++-- src/lib/list/selection-list.spec.ts | 26 +++++++++++++++++++++++++- src/lib/list/selection-list.ts | 14 +++++++++++++- 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/lib/list/list-option.html b/src/lib/list/list-option.html index 4b6fb2aa5df4..db84f6f66dd6 100644 --- a/src/lib/list/list-option.html +++ b/src/lib/list/list-option.html @@ -7,10 +7,10 @@ [matRippleTrigger]="_getHostElement()" [matRippleDisabled]="_isRippleDisabled()"> - -
+
diff --git a/src/lib/list/selection-list.spec.ts b/src/lib/list/selection-list.spec.ts index 876309677edb..9abb7c3416e2 100644 --- a/src/lib/list/selection-list.spec.ts +++ b/src/lib/list/selection-list.spec.ts @@ -1,6 +1,11 @@ import {DOWN_ARROW, SPACE, ENTER, UP_ARROW, HOME, END} from '@angular/cdk/keycodes'; import {Platform} from '@angular/cdk/platform'; -import {createKeyboardEvent, dispatchFakeEvent, dispatchKeyboardEvent} from '@angular/cdk/testing'; +import { + createKeyboardEvent, + dispatchFakeEvent, + dispatchEvent, + dispatchKeyboardEvent, +} from '@angular/cdk/testing'; import {Component, DebugElement} from '@angular/core'; import {async, ComponentFixture, fakeAsync, inject, TestBed, tick} from '@angular/core/testing'; import {By} from '@angular/platform-browser'; @@ -287,6 +292,25 @@ describe('MatSelectionList without forms', () => { expect(event.defaultPrevented).toBe(true); }); + it('should be able to jump focus down to an item by typing', fakeAsync(() => { + const listEl = selectionList.nativeElement; + const manager = selectionList.componentInstance._keyManager; + + expect(manager.activeItemIndex).toBe(-1); + + dispatchEvent(listEl, createKeyboardEvent('keydown', 83, undefined, 's')); + fixture.detectChanges(); + tick(200); + + expect(manager.activeItemIndex).toBe(1); + + dispatchEvent(listEl, createKeyboardEvent('keydown', 68, undefined, 'd')); + fixture.detectChanges(); + tick(200); + + expect(manager.activeItemIndex).toBe(3); + })); + it('should be able to select all options', () => { const list: MatSelectionList = selectionList.componentInstance; diff --git a/src/lib/list/selection-list.ts b/src/lib/list/selection-list.ts index bcd8cfa7767a..ce682df8abc2 100644 --- a/src/lib/list/selection-list.ts +++ b/src/lib/list/selection-list.ts @@ -27,6 +27,7 @@ import { Optional, Output, QueryList, + ViewChild, ViewEncapsulation, } from '@angular/core'; import { @@ -118,6 +119,9 @@ export class MatListOption extends _MatListOptionMixinBase @ContentChildren(MatLine) _lines: QueryList; + /** DOM element containing the item's text. */ + @ViewChild('text') _text: ElementRef; + /** Whether the label should appear before or after the checkbox. Defaults to 'after' */ @Input() checkboxPosition: 'before' | 'after' = 'after'; @@ -197,6 +201,14 @@ export class MatListOption extends _MatListOptionMixinBase this._element.nativeElement.focus(); } + /** + * Returns the list item's text label. Implemented as a part of the FocusKeyManager. + * @docs-private + */ + getLabel() { + return this._text ? this._text.nativeElement.textContent : ''; + } + /** Whether this list item should show a ripple effect when clicked. */ _isRippleDisabled() { return this.disabled || this.disableRipple || this.selectionList.disableRipple; @@ -309,7 +321,7 @@ export class MatSelectionList extends _MatSelectionListMixinBase implements Focu } ngAfterContentInit(): void { - this._keyManager = new FocusKeyManager(this.options).withWrap(); + this._keyManager = new FocusKeyManager(this.options).withWrap().withTypeAhead(); if (this._tempValues) { this._setOptionsFromValues(this._tempValues);