Skip to content

Commit ba85883

Browse files
topherfangiojelbourn
authored andcommitted
feat(chips): add keyboard interaction. (#2046)
Add basic focus/keyboard support for chips. - Up/down arrows navigate chips. - Clicking a chip properly focuses it for subsequent keyboard navigation. - More demos. Confirmed AoT compatibility. References #120.
1 parent bb1f006 commit ba85883

File tree

13 files changed

+553
-54
lines changed

13 files changed

+553
-54
lines changed

src/demo-app/chips/chips-demo.html

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,67 @@
22
<section>
33
<h3>Static Chips</h3>
44

5+
<h5>Simple</h5>
6+
7+
<md-chip-list>
8+
<md-chip>Chip 1</md-chip>
9+
<md-chip>Chip 2</md-chip>
10+
<md-chip>Chip 3</md-chip>
11+
</md-chip-list>
12+
13+
<h5>Advanced</h5>
14+
15+
<md-chip-list>
16+
<md-chip class="md-accent selected">Selected/Colored</md-chip>
17+
<md-chip class="md-warn" *ngIf="visible"
18+
(destroy)="alert('chip destroyed')" (click)="toggleVisible()">
19+
With Events
20+
</md-chip>
21+
</md-chip-list>
22+
23+
<h5>Unstyled</h5>
24+
25+
<md-chip-list>
26+
<md-basic-chip>Basic Chip 1</md-basic-chip>
27+
<md-basic-chip>Basic Chip 2</md-basic-chip>
28+
<md-basic-chip>Basic Chip 3</md-basic-chip>
29+
</md-chip-list>
30+
31+
<h3>Material Contributors</h3>
32+
533
<md-chip-list>
6-
<md-chip>Basic Chip</md-chip>
7-
<md-chip class="selected md-primary">Primary</md-chip>
8-
<md-chip class="selected md-accent">Accent</md-chip>
9-
<md-chip class="selected md-warn">Warn</md-chip>
34+
<md-chip *ngFor="let person of people; let even = even" [ngClass]="[color, even ? 'selected' : '' ]">
35+
{{person.name}}
36+
</md-chip>
37+
</md-chip-list>
38+
39+
<br />
40+
41+
<md-input #input (keyup.enter)="add(input)" (blur)="add(input)" placeholder="New Contributor...">
42+
</md-input>
43+
44+
<h3>Stacked Chips</h3>
45+
46+
<p>
47+
You can also stack the chips if you want them on top of each other.
48+
</p>
49+
50+
<md-chip-list class="md-chip-list-stacked">
51+
<md-chip (focus)="color = ''" class="selected">
52+
None
53+
</md-chip>
54+
55+
<md-chip (focus)="color = 'md-primary'" class="selected md-primary">
56+
Primary
57+
</md-chip>
58+
59+
<md-chip (focus)="color = 'md-accent'" class="selected md-accent">
60+
Accent
61+
</md-chip>
62+
63+
<md-chip (focus)="color = 'md-warn'" class="selected md-warn">
64+
Warn
65+
</md-chip>
1066
</md-chip-list>
1167
</section>
1268
</div>

src/demo-app/chips/chips-demo.scss

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,10 @@
11
.chips-demo {
2+
.md-chip-list-stacked {
3+
display: block;
4+
max-width: 200px;
5+
}
6+
7+
md-basic-chip {
8+
margin: auto 10px;
9+
}
210
}

src/demo-app/chips/chips-demo.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
import {Component} from '@angular/core';
2+
import {MdInput} from '@angular/material';
3+
4+
export interface Person {
5+
name: string;
6+
}
27

38
@Component({
49
moduleId: module.id,
@@ -7,4 +12,30 @@ import {Component} from '@angular/core';
712
styleUrls: ['chips-demo.css']
813
})
914
export class ChipsDemo {
15+
visible: boolean = true;
16+
color: string = '';
17+
18+
people: Person[] = [
19+
{ name: 'Kara' },
20+
{ name: 'Jeremy' },
21+
{ name: 'Topher' },
22+
{ name: 'Elad' },
23+
{ name: 'Kristiyan' },
24+
{ name: 'Paul' }
25+
];
26+
27+
alert(message: string): void {
28+
alert(message);
29+
}
30+
31+
add(input: MdInput): void {
32+
if (input.value && input.value.trim() != '') {
33+
this.people.push({ name: input.value.trim() });
34+
input.value = '';
35+
}
36+
}
37+
38+
toggleVisible(): void {
39+
this.visible = false;
40+
}
1041
}

src/lib/chips/_chips-theme.scss

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@
1313
}
1414

1515
.md-chip.selected {
16+
// There is no dark theme in the current spec, so this applies to both
17+
background-color: #808080;
18+
19+
// Use a contrast color for a grey very close to the background color
20+
color: md-contrast($md-grey, 600);
21+
1622
&.md-primary {
1723
background-color: md-color($primary, 500);
1824
color: md-contrast($primary, 500);
@@ -26,4 +32,4 @@
2632
color: md-contrast($warn, 500);
2733
}
2834
}
29-
}
35+
}

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

Lines changed: 72 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
2-
import {Component, DebugElement} from '@angular/core';
2+
import {Component, DebugElement, QueryList} from '@angular/core';
33
import {By} from '@angular/platform-browser';
4-
import {MdChipList, MdChipsModule} from './index';
4+
import {MdChip, MdChipList, MdChipsModule} from './index';
5+
import {ListKeyManager} from '../core/a11y/list-key-manager';
56

6-
describe('MdChip', () => {
7+
describe('MdChipList', () => {
78
let fixture: ComponentFixture<any>;
9+
let chipListDebugElement: DebugElement;
10+
let chipListNativeElement: HTMLElement;
11+
let chipListInstance: MdChipList;
12+
let testComponent: StaticChipList;
13+
let items: QueryList<MdChip>;
14+
let manager: ListKeyManager;
815

916
beforeEach(async(() => {
1017
TestBed.configureTestingModule({
@@ -15,39 +22,87 @@ describe('MdChip', () => {
1522
});
1623

1724
TestBed.compileComponents();
25+
26+
fixture = TestBed.createComponent(StaticChipList);
27+
fixture.detectChanges();
28+
29+
chipListDebugElement = fixture.debugElement.query(By.directive(MdChipList));
30+
chipListNativeElement = chipListDebugElement.nativeElement;
31+
chipListInstance = chipListDebugElement.componentInstance;
32+
testComponent = fixture.debugElement.componentInstance;
1833
}));
1934

2035
describe('basic behaviors', () => {
21-
let chipListDebugElement: DebugElement;
22-
let chipListNativeElement: HTMLElement;
23-
let chipListInstance: MdChipList;
24-
let testComponent: StaticChipList;
36+
it('adds the `md-chip-list` class', () => {
37+
expect(chipListNativeElement.classList).toContain('md-chip-list');
38+
});
39+
});
2540

41+
describe('focus behaviors', () => {
2642
beforeEach(() => {
27-
fixture = TestBed.createComponent(StaticChipList);
43+
items = chipListInstance.chips;
44+
manager = chipListInstance._keyManager;
45+
});
46+
47+
it('watches for chip focus', () => {
48+
let array = items.toArray();
49+
let lastIndex = array.length - 1;
50+
let lastItem = array[lastIndex];
51+
52+
lastItem.focus();
2853
fixture.detectChanges();
2954

30-
chipListDebugElement = fixture.debugElement.query(By.directive(MdChipList));
31-
chipListNativeElement = chipListDebugElement.nativeElement;
32-
chipListInstance = chipListDebugElement.componentInstance;
33-
testComponent = fixture.debugElement.componentInstance;
55+
expect(manager.focusedItemIndex).toBe(lastIndex);
3456
});
3557

36-
it('adds the `md-chip-list` class', () => {
37-
expect(chipListNativeElement.classList).toContain('md-chip-list');
58+
describe('on chip destroy', () => {
59+
it('focuses the next item', () => {
60+
let array = items.toArray();
61+
let midItem = array[2];
62+
63+
// Focus the middle item
64+
midItem.focus();
65+
66+
// Destroy the middle item
67+
testComponent.remove = 2;
68+
fixture.detectChanges();
69+
70+
// It focuses the 4th item (now at index 2)
71+
expect(manager.focusedItemIndex).toEqual(2);
72+
});
73+
74+
it('focuses the previous item', () => {
75+
let array = items.toArray();
76+
let lastIndex = array.length - 1;
77+
let lastItem = array[lastIndex];
78+
79+
// Focus the last item
80+
lastItem.focus();
81+
82+
// Destroy the last item
83+
testComponent.remove = lastIndex;
84+
fixture.detectChanges();
85+
86+
// It focuses the next-to-last item
87+
expect(manager.focusedItemIndex).toEqual(lastIndex - 1);
88+
});
3889
});
3990
});
91+
4092
});
4193

4294
@Component({
4395
template: `
4496
<md-chip-list>
45-
<md-chip>{{name}} 1</md-chip>
46-
<md-chip>{{name}} 2</md-chip>
47-
<md-chip>{{name}} 3</md-chip>
97+
<div *ngIf="remove != 0"><md-chip>{{name}} 1</md-chip></div>
98+
<div *ngIf="remove != 1"><md-chip>{{name}} 2</md-chip></div>
99+
<div *ngIf="remove != 2"><md-chip>{{name}} 3</md-chip></div>
100+
<div *ngIf="remove != 3"><md-chip>{{name}} 4</md-chip></div>
101+
<div *ngIf="remove != 4"><md-chip>{{name}} 5</md-chip></div>
48102
</md-chip-list>
49103
`
50104
})
51105
class StaticChipList {
52106
name: 'Test';
107+
remove: Number;
53108
}

0 commit comments

Comments
 (0)