Skip to content

Commit

Permalink
feat(chips): add keyboard interaction. (angular#2046)
Browse files Browse the repository at this point in the history
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 angular#120.
  • Loading branch information
topherfangio authored and jelbourn committed Dec 14, 2016
1 parent bb1f006 commit ba85883
Show file tree
Hide file tree
Showing 13 changed files with 553 additions and 54 deletions.
64 changes: 60 additions & 4 deletions src/demo-app/chips/chips-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,67 @@
<section>
<h3>Static Chips</h3>

<h5>Simple</h5>

<md-chip-list>
<md-chip>Chip 1</md-chip>
<md-chip>Chip 2</md-chip>
<md-chip>Chip 3</md-chip>
</md-chip-list>

<h5>Advanced</h5>

<md-chip-list>
<md-chip class="md-accent selected">Selected/Colored</md-chip>
<md-chip class="md-warn" *ngIf="visible"
(destroy)="alert('chip destroyed')" (click)="toggleVisible()">
With Events
</md-chip>
</md-chip-list>

<h5>Unstyled</h5>

<md-chip-list>
<md-basic-chip>Basic Chip 1</md-basic-chip>
<md-basic-chip>Basic Chip 2</md-basic-chip>
<md-basic-chip>Basic Chip 3</md-basic-chip>
</md-chip-list>

<h3>Material Contributors</h3>

<md-chip-list>
<md-chip>Basic Chip</md-chip>
<md-chip class="selected md-primary">Primary</md-chip>
<md-chip class="selected md-accent">Accent</md-chip>
<md-chip class="selected md-warn">Warn</md-chip>
<md-chip *ngFor="let person of people; let even = even" [ngClass]="[color, even ? 'selected' : '' ]">
{{person.name}}
</md-chip>
</md-chip-list>

<br />

<md-input #input (keyup.enter)="add(input)" (blur)="add(input)" placeholder="New Contributor...">
</md-input>

<h3>Stacked Chips</h3>

<p>
You can also stack the chips if you want them on top of each other.
</p>

<md-chip-list class="md-chip-list-stacked">
<md-chip (focus)="color = ''" class="selected">
None
</md-chip>

<md-chip (focus)="color = 'md-primary'" class="selected md-primary">
Primary
</md-chip>

<md-chip (focus)="color = 'md-accent'" class="selected md-accent">
Accent
</md-chip>

<md-chip (focus)="color = 'md-warn'" class="selected md-warn">
Warn
</md-chip>
</md-chip-list>
</section>
</div>
8 changes: 8 additions & 0 deletions src/demo-app/chips/chips-demo.scss
Original file line number Diff line number Diff line change
@@ -1,2 +1,10 @@
.chips-demo {
.md-chip-list-stacked {
display: block;
max-width: 200px;
}

md-basic-chip {
margin: auto 10px;
}
}
31 changes: 31 additions & 0 deletions src/demo-app/chips/chips-demo.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import {Component} from '@angular/core';
import {MdInput} from '@angular/material';

export interface Person {
name: string;
}

@Component({
moduleId: module.id,
Expand All @@ -7,4 +12,30 @@ import {Component} from '@angular/core';
styleUrls: ['chips-demo.css']
})
export class ChipsDemo {
visible: boolean = true;
color: string = '';

people: Person[] = [
{ name: 'Kara' },
{ name: 'Jeremy' },
{ name: 'Topher' },
{ name: 'Elad' },
{ name: 'Kristiyan' },
{ name: 'Paul' }
];

alert(message: string): void {
alert(message);
}

add(input: MdInput): void {
if (input.value && input.value.trim() != '') {
this.people.push({ name: input.value.trim() });
input.value = '';
}
}

toggleVisible(): void {
this.visible = false;
}
}
8 changes: 7 additions & 1 deletion src/lib/chips/_chips-theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@
}

.md-chip.selected {
// There is no dark theme in the current spec, so this applies to both
background-color: #808080;

// Use a contrast color for a grey very close to the background color
color: md-contrast($md-grey, 600);

&.md-primary {
background-color: md-color($primary, 500);
color: md-contrast($primary, 500);
Expand All @@ -26,4 +32,4 @@
color: md-contrast($warn, 500);
}
}
}
}
89 changes: 72 additions & 17 deletions src/lib/chips/chip-list.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {Component, DebugElement} from '@angular/core';
import {Component, DebugElement, QueryList} from '@angular/core';
import {By} from '@angular/platform-browser';
import {MdChipList, MdChipsModule} from './index';
import {MdChip, MdChipList, MdChipsModule} from './index';
import {ListKeyManager} from '../core/a11y/list-key-manager';

describe('MdChip', () => {
describe('MdChipList', () => {
let fixture: ComponentFixture<any>;
let chipListDebugElement: DebugElement;
let chipListNativeElement: HTMLElement;
let chipListInstance: MdChipList;
let testComponent: StaticChipList;
let items: QueryList<MdChip>;
let manager: ListKeyManager;

beforeEach(async(() => {
TestBed.configureTestingModule({
Expand All @@ -15,39 +22,87 @@ describe('MdChip', () => {
});

TestBed.compileComponents();

fixture = TestBed.createComponent(StaticChipList);
fixture.detectChanges();

chipListDebugElement = fixture.debugElement.query(By.directive(MdChipList));
chipListNativeElement = chipListDebugElement.nativeElement;
chipListInstance = chipListDebugElement.componentInstance;
testComponent = fixture.debugElement.componentInstance;
}));

describe('basic behaviors', () => {
let chipListDebugElement: DebugElement;
let chipListNativeElement: HTMLElement;
let chipListInstance: MdChipList;
let testComponent: StaticChipList;
it('adds the `md-chip-list` class', () => {
expect(chipListNativeElement.classList).toContain('md-chip-list');
});
});

describe('focus behaviors', () => {
beforeEach(() => {
fixture = TestBed.createComponent(StaticChipList);
items = chipListInstance.chips;
manager = chipListInstance._keyManager;
});

it('watches for chip focus', () => {
let array = items.toArray();
let lastIndex = array.length - 1;
let lastItem = array[lastIndex];

lastItem.focus();
fixture.detectChanges();

chipListDebugElement = fixture.debugElement.query(By.directive(MdChipList));
chipListNativeElement = chipListDebugElement.nativeElement;
chipListInstance = chipListDebugElement.componentInstance;
testComponent = fixture.debugElement.componentInstance;
expect(manager.focusedItemIndex).toBe(lastIndex);
});

it('adds the `md-chip-list` class', () => {
expect(chipListNativeElement.classList).toContain('md-chip-list');
describe('on chip destroy', () => {
it('focuses the next item', () => {
let array = items.toArray();
let midItem = array[2];

// Focus the middle item
midItem.focus();

// Destroy the middle item
testComponent.remove = 2;
fixture.detectChanges();

// It focuses the 4th item (now at index 2)
expect(manager.focusedItemIndex).toEqual(2);
});

it('focuses the previous item', () => {
let array = items.toArray();
let lastIndex = array.length - 1;
let lastItem = array[lastIndex];

// Focus the last item
lastItem.focus();

// Destroy the last item
testComponent.remove = lastIndex;
fixture.detectChanges();

// It focuses the next-to-last item
expect(manager.focusedItemIndex).toEqual(lastIndex - 1);
});
});
});

});

@Component({
template: `
<md-chip-list>
<md-chip>{{name}} 1</md-chip>
<md-chip>{{name}} 2</md-chip>
<md-chip>{{name}} 3</md-chip>
<div *ngIf="remove != 0"><md-chip>{{name}} 1</md-chip></div>
<div *ngIf="remove != 1"><md-chip>{{name}} 2</md-chip></div>
<div *ngIf="remove != 2"><md-chip>{{name}} 3</md-chip></div>
<div *ngIf="remove != 3"><md-chip>{{name}} 4</md-chip></div>
<div *ngIf="remove != 4"><md-chip>{{name}} 5</md-chip></div>
</md-chip-list>
`
})
class StaticChipList {
name: 'Test';
remove: Number;
}
Loading

0 comments on commit ba85883

Please sign in to comment.