Skip to content

Commit e2ad3a0

Browse files
karammalerba
authored andcommitted
feat(list-key-manager): active descendant support (#2606)
1 parent 899f190 commit e2ad3a0

11 files changed

+532
-327
lines changed

src/lib/chips/chip-list.spec.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {async, ComponentFixture, TestBed} from '@angular/core/testing';
22
import {Component, DebugElement, QueryList} from '@angular/core';
33
import {By} from '@angular/platform-browser';
44
import {MdChip, MdChipList, MdChipsModule} from './index';
5-
import {ListKeyManager} from '../core/a11y/list-key-manager';
5+
import {FocusKeyManager} from '../core/a11y/focus-key-manager';
66
import {FakeEvent} from '../core/a11y/list-key-manager.spec';
77
import {SPACE, LEFT_ARROW, RIGHT_ARROW} from '../core/keyboard/keycodes';
88

@@ -21,7 +21,7 @@ describe('MdChipList', () => {
2121
let chipListInstance: MdChipList;
2222
let testComponent: StaticChipList;
2323
let chips: QueryList<MdChip>;
24-
let manager: ListKeyManager;
24+
let manager: FocusKeyManager;
2525

2626
beforeEach(async(() => {
2727
TestBed.configureTestingModule({
@@ -60,7 +60,7 @@ describe('MdChipList', () => {
6060
chipListInstance.focus();
6161
fixture.detectChanges();
6262

63-
expect(manager.focusedItemIndex).toBe(0);
63+
expect(manager.activeItemIndex).toBe(0);
6464
});
6565

6666
it('watches for chip focus', () => {
@@ -71,7 +71,7 @@ describe('MdChipList', () => {
7171
lastItem.focus();
7272
fixture.detectChanges();
7373

74-
expect(manager.focusedItemIndex).toBe(lastIndex);
74+
expect(manager.activeItemIndex).toBe(lastIndex);
7575
});
7676

7777
describe('on chip destroy', () => {
@@ -87,7 +87,7 @@ describe('MdChipList', () => {
8787
fixture.detectChanges();
8888

8989
// It focuses the 4th item (now at index 2)
90-
expect(manager.focusedItemIndex).toEqual(2);
90+
expect(manager.activeItemIndex).toEqual(2);
9191
});
9292

9393
it('focuses the previous item', () => {
@@ -103,7 +103,7 @@ describe('MdChipList', () => {
103103
fixture.detectChanges();
104104

105105
// It focuses the next-to-last item
106-
expect(manager.focusedItemIndex).toEqual(lastIndex - 1);
106+
expect(manager.activeItemIndex).toEqual(lastIndex - 1);
107107
});
108108
});
109109
});
@@ -124,14 +124,14 @@ describe('MdChipList', () => {
124124

125125
// Focus the last item in the array
126126
lastItem.focus();
127-
expect(manager.focusedItemIndex).toEqual(lastIndex);
127+
expect(manager.activeItemIndex).toEqual(lastIndex);
128128

129129
// Press the LEFT arrow
130130
chipListInstance._keydown(LEFT_EVENT);
131131
fixture.detectChanges();
132132

133133
// It focuses the next-to-last item
134-
expect(manager.focusedItemIndex).toEqual(lastIndex - 1);
134+
expect(manager.activeItemIndex).toEqual(lastIndex - 1);
135135
});
136136

137137
it('right arrow focuses next item', () => {
@@ -144,14 +144,14 @@ describe('MdChipList', () => {
144144

145145
// Focus the last item in the array
146146
firstItem.focus();
147-
expect(manager.focusedItemIndex).toEqual(0);
147+
expect(manager.activeItemIndex).toEqual(0);
148148

149149
// Press the RIGHT arrow
150150
chipListInstance._keydown(RIGHT_EVENT);
151151
fixture.detectChanges();
152152

153153
// It focuses the next-to-last item
154-
expect(manager.focusedItemIndex).toEqual(1);
154+
expect(manager.activeItemIndex).toEqual(1);
155155
});
156156

157157
describe('when selectable is true', () => {

src/lib/chips/chip-list.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
} from '@angular/core';
1313

1414
import {MdChip} from './chip';
15-
import {ListKeyManager} from '../core/a11y/list-key-manager';
15+
import {FocusKeyManager} from '../core/a11y/focus-key-manager';
1616
import {coerceBooleanProperty} from '../core/coercion/boolean-property';
1717
import {SPACE, LEFT_ARROW, RIGHT_ARROW} from '../core/keyboard/keycodes';
1818

@@ -55,16 +55,16 @@ export class MdChipList implements AfterContentInit {
5555
/** Whether or not the chip is selectable. */
5656
protected _selectable: boolean = true;
5757

58-
/** The ListKeyManager which handles focus. */
59-
_keyManager: ListKeyManager;
58+
/** The FocusKeyManager which handles focus. */
59+
_keyManager: FocusKeyManager;
6060

6161
/** The chip components contained within this chip list. */
6262
chips: QueryList<MdChip>;
6363

6464
constructor(private _elementRef: ElementRef) { }
6565

6666
ngAfterContentInit(): void {
67-
this._keyManager = new ListKeyManager(this.chips).withFocusWrap();
67+
this._keyManager = new FocusKeyManager(this.chips).withWrap();
6868

6969
// Go ahead and subscribe all of the initial chips
7070
this._subscribeChips(this.chips);
@@ -93,7 +93,7 @@ export class MdChipList implements AfterContentInit {
9393
*/
9494
focus() {
9595
// TODO: ARIA says this should focus the first `selected` chip.
96-
this._keyManager.focusFirstItem();
96+
this._keyManager.setFirstItemActive();
9797
}
9898

9999
/** Passes relevant key presses to our key manager. */
@@ -113,11 +113,11 @@ export class MdChipList implements AfterContentInit {
113113
event.preventDefault();
114114
break;
115115
case LEFT_ARROW:
116-
this._keyManager.focusPreviousItem();
116+
this._keyManager.setPreviousItemActive();
117117
event.preventDefault();
118118
break;
119119
case RIGHT_ARROW:
120-
this._keyManager.focusNextItem();
120+
this._keyManager.setNextItemActive();
121121
event.preventDefault();
122122
break;
123123
default:
@@ -133,7 +133,7 @@ export class MdChipList implements AfterContentInit {
133133
return;
134134
}
135135

136-
let focusedIndex = this._keyManager.focusedItemIndex;
136+
let focusedIndex = this._keyManager.activeItemIndex;
137137

138138
if (this._isValidIndex(focusedIndex)) {
139139
let focusedChip: MdChip = this.chips.toArray()[focusedIndex];
@@ -173,7 +173,7 @@ export class MdChipList implements AfterContentInit {
173173
let chipIndex: number = this.chips.toArray().indexOf(chip);
174174

175175
if (this._isValidIndex(chipIndex)) {
176-
this._keyManager.updateFocusedItemIndex(chipIndex);
176+
this._keyManager.updateActiveItemIndex(chipIndex);
177177
}
178178
});
179179

@@ -184,9 +184,9 @@ export class MdChipList implements AfterContentInit {
184184
if (this._isValidIndex(chipIndex)) {
185185
// Check whether the chip is the last item
186186
if (chipIndex < this.chips.length - 1) {
187-
this._keyManager.setFocus(chipIndex);
187+
this._keyManager.setActiveItem(chipIndex);
188188
} else if (chipIndex - 1 >= 0) {
189-
this._keyManager.setFocus(chipIndex - 1);
189+
this._keyManager.setActiveItem(chipIndex - 1);
190190
}
191191
}
192192

src/lib/chips/chip.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
Renderer
1010
} from '@angular/core';
1111

12-
import {Focusable} from '../core/a11y/list-key-manager';
12+
import {Focusable} from '../core/a11y/focus-key-manager';
1313
import {coerceBooleanProperty} from '../core/coercion/boolean-property';
1414

1515
export interface MdChipEvent {
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import {QueryList} from '@angular/core';
2+
import {ListKeyManager, CanDisable} from './list-key-manager';
3+
4+
/**
5+
* This is the interface for highlightable items (used by the ActiveDescendantKeyManager).
6+
* Each item must know how to style itself as active or inactive and whether or not it is
7+
* currently disabled.
8+
*/
9+
export interface Highlightable extends CanDisable {
10+
setActiveStyles(): void;
11+
setInactiveStyles(): void;
12+
}
13+
14+
export class ActiveDescendantKeyManager extends ListKeyManager<Highlightable> {
15+
16+
constructor(items: QueryList<Highlightable>) {
17+
super(items);
18+
}
19+
20+
/**
21+
* This method sets the active item to the item at the specified index.
22+
* It also adds active styles to the newly active item and removes active
23+
* styles from the previously active item.
24+
*/
25+
setActiveItem(index: number): void {
26+
if (this.activeItem) {
27+
this.activeItem.setInactiveStyles();
28+
}
29+
super.setActiveItem(index);
30+
if (this.activeItem) {
31+
this.activeItem.setActiveStyles();
32+
}
33+
}
34+
35+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
2+
import {QueryList} from '@angular/core';
3+
import {ListKeyManager, CanDisable} from './list-key-manager';
4+
5+
/**
6+
* This is the interface for focusable items (used by the FocusKeyManager).
7+
* Each item must know how to focus itself and whether or not it is currently disabled.
8+
*/
9+
export interface Focusable extends CanDisable {
10+
focus(): void;
11+
}
12+
13+
14+
export class FocusKeyManager extends ListKeyManager<Focusable> {
15+
16+
constructor(items: QueryList<Focusable>) {
17+
super(items);
18+
}
19+
20+
/**
21+
* This method sets the active item to the item at the specified index.
22+
* It also adds focuses the newly active item.
23+
*/
24+
setActiveItem(index: number): void {
25+
super.setActiveItem(index);
26+
this.activeItem.focus();
27+
}
28+
29+
}

0 commit comments

Comments
 (0)