mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
feat(chips): Add left/right arrow functionality. (#2332)
In order to maintain consistentcy with the md1 chips, add the ability to use the left/right arrow keys to navigate chips. References #120.
This commit is contained in:
committed by
Jeremy Elbourn
parent
0dd57da945
commit
3f2db2750e
@@ -17,7 +17,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
md-basic-chip {
|
||||
.md-basic-chip {
|
||||
margin: auto 10px;
|
||||
}
|
||||
}
|
||||
@@ -22,12 +22,12 @@
|
||||
$selected-background: if($is-dark-theme, md-color($background, app-bar), #808080);
|
||||
$selected-foreground: if($is-dark-theme, md-color($foreground, text), $light-selected-foreground);
|
||||
|
||||
.md-chip {
|
||||
.md-chip:not(.md-basic-chip) {
|
||||
background-color: $unselected-background;
|
||||
color: $unselected-foreground;
|
||||
}
|
||||
|
||||
.md-chip.md-chip-selected {
|
||||
.md-chip.md-chip-selected:not(.md-basic-chip) {
|
||||
background-color: $selected-background;
|
||||
color: $selected-foreground;
|
||||
|
||||
|
||||
@@ -4,7 +4,15 @@ import {By} from '@angular/platform-browser';
|
||||
import {MdChip, MdChipList, MdChipsModule} from './index';
|
||||
import {ListKeyManager} from '../core/a11y/list-key-manager';
|
||||
import {FakeEvent} from '../core/a11y/list-key-manager.spec';
|
||||
import {SPACE} from '../core/keyboard/keycodes';
|
||||
import {SPACE, LEFT_ARROW, RIGHT_ARROW} from '../core/keyboard/keycodes';
|
||||
|
||||
class FakeKeyboardEvent extends FakeEvent {
|
||||
constructor(keyCode: number, protected target: HTMLElement) {
|
||||
super(keyCode);
|
||||
|
||||
this.target = target;
|
||||
}
|
||||
}
|
||||
|
||||
describe('MdChipList', () => {
|
||||
let fixture: ComponentFixture<any>;
|
||||
@@ -101,6 +109,50 @@ describe('MdChipList', () => {
|
||||
});
|
||||
|
||||
describe('keyboard behavior', () => {
|
||||
beforeEach(() => {
|
||||
manager = chipListInstance._keyManager;
|
||||
});
|
||||
|
||||
it('left arrow focuses previous item', () => {
|
||||
let nativeChips = chipListNativeElement.querySelectorAll('md-chip');
|
||||
let lastNativeChip = nativeChips[nativeChips.length - 1] as HTMLElement;
|
||||
|
||||
let LEFT_EVENT = new FakeKeyboardEvent(LEFT_ARROW, lastNativeChip) as any;
|
||||
let array = chips.toArray();
|
||||
let lastIndex = array.length - 1;
|
||||
let lastItem = array[lastIndex];
|
||||
|
||||
// Focus the last item in the array
|
||||
lastItem.focus();
|
||||
expect(manager.focusedItemIndex).toEqual(lastIndex);
|
||||
|
||||
// Press the LEFT arrow
|
||||
chipListInstance._keydown(LEFT_EVENT);
|
||||
fixture.detectChanges();
|
||||
|
||||
// It focuses the next-to-last item
|
||||
expect(manager.focusedItemIndex).toEqual(lastIndex - 1);
|
||||
});
|
||||
|
||||
it('right arrow focuses next item', () => {
|
||||
let nativeChips = chipListNativeElement.querySelectorAll('md-chip');
|
||||
let firstNativeChip = nativeChips[0] as HTMLElement;
|
||||
|
||||
let RIGHT_EVENT: KeyboardEvent = new FakeKeyboardEvent(RIGHT_ARROW, firstNativeChip) as any;
|
||||
let array = chips.toArray();
|
||||
let firstItem = array[0];
|
||||
|
||||
// Focus the last item in the array
|
||||
firstItem.focus();
|
||||
expect(manager.focusedItemIndex).toEqual(0);
|
||||
|
||||
// Press the RIGHT arrow
|
||||
chipListInstance._keydown(RIGHT_EVENT);
|
||||
fixture.detectChanges();
|
||||
|
||||
// It focuses the next-to-last item
|
||||
expect(manager.focusedItemIndex).toEqual(1);
|
||||
});
|
||||
|
||||
describe('when selectable is true', () => {
|
||||
beforeEach(() => {
|
||||
@@ -109,7 +161,10 @@ describe('MdChipList', () => {
|
||||
});
|
||||
|
||||
it('SPACE selects/deselects the currently focused chip', () => {
|
||||
let SPACE_EVENT: KeyboardEvent = new FakeEvent(SPACE) as KeyboardEvent;
|
||||
let nativeChips = chipListNativeElement.querySelectorAll('md-chip');
|
||||
let firstNativeChip = nativeChips[0] as HTMLElement;
|
||||
|
||||
let SPACE_EVENT: KeyboardEvent = new FakeKeyboardEvent(SPACE, firstNativeChip) as any;
|
||||
let firstChip: MdChip = chips.toArray()[0];
|
||||
|
||||
spyOn(testComponent, 'chipSelect');
|
||||
@@ -181,6 +236,9 @@ class StaticChipList {
|
||||
selectable: boolean = true;
|
||||
remove: Number;
|
||||
|
||||
chipSelect(index: Number) {}
|
||||
chipDeselect(index: Number) {}
|
||||
chipSelect(index: Number) {
|
||||
}
|
||||
|
||||
chipDeselect(index: Number) {
|
||||
}
|
||||
}
|
||||
|
||||
+25
-12
@@ -14,7 +14,7 @@ import {
|
||||
import {MdChip} from './chip';
|
||||
import {ListKeyManager} from '../core/a11y/list-key-manager';
|
||||
import {coerceBooleanProperty} from '../core/coercion/boolean-property';
|
||||
import {SPACE} from '../core/keyboard/keycodes';
|
||||
import {SPACE, LEFT_ARROW, RIGHT_ARROW} from '../core/keyboard/keycodes';
|
||||
|
||||
/**
|
||||
* A material design chips component (named ChipList for it's similarity to the List component).
|
||||
@@ -98,18 +98,31 @@ export class MdChipList implements AfterContentInit {
|
||||
|
||||
/** Passes relevant key presses to our key manager. */
|
||||
_keydown(event: KeyboardEvent) {
|
||||
switch (event.keyCode) {
|
||||
case SPACE:
|
||||
// If we are selectable, toggle the focused chip
|
||||
if (this.selectable) {
|
||||
this._toggleSelectOnFocusedChip();
|
||||
}
|
||||
let target = event.target as HTMLElement;
|
||||
|
||||
// Always prevent space from scrolling the page since the list has focus
|
||||
event.preventDefault();
|
||||
break;
|
||||
default:
|
||||
this._keyManager.onKeydown(event);
|
||||
// If they are on a chip, check for space/left/right, otherwise pass to our key manager
|
||||
if (target && target.classList.contains('md-chip')) {
|
||||
switch (event.keyCode) {
|
||||
case SPACE:
|
||||
// If we are selectable, toggle the focused chip
|
||||
if (this.selectable) {
|
||||
this._toggleSelectOnFocusedChip();
|
||||
}
|
||||
|
||||
// Always prevent space from scrolling the page since the list has focus
|
||||
event.preventDefault();
|
||||
break;
|
||||
case LEFT_ARROW:
|
||||
this._keyManager.focusPreviousItem();
|
||||
event.preventDefault();
|
||||
break;
|
||||
case RIGHT_ARROW:
|
||||
this._keyManager.focusNextItem();
|
||||
event.preventDefault();
|
||||
break;
|
||||
default:
|
||||
this._keyManager.onKeydown(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,8 +38,9 @@ describe('Chips', () => {
|
||||
document.body.removeChild(chipNativeElement);
|
||||
});
|
||||
|
||||
it('does not add the `md-chip` class', () => {
|
||||
expect(chipNativeElement.classList).not.toContain('md-chip');
|
||||
it('adds the `md-basic-chip` class', () => {
|
||||
expect(chipNativeElement.classList).toContain('md-chip');
|
||||
expect(chipNativeElement.classList).toContain('md-basic-chip');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -69,6 +70,10 @@ describe('Chips', () => {
|
||||
expect(chipNativeElement.classList).toContain('md-chip');
|
||||
});
|
||||
|
||||
it('does not add the `md-basic-chip` class', () => {
|
||||
expect(chipNativeElement.classList).not.toContain('md-basic-chip');
|
||||
});
|
||||
|
||||
it('emits focus on click', () => {
|
||||
spyOn(chipInstance, 'focus').and.callThrough();
|
||||
|
||||
|
||||
@@ -136,8 +136,12 @@ export class MdChip implements Focusable, OnInit, OnDestroy {
|
||||
private _addDefaultCSSClass() {
|
||||
let el: HTMLElement = this._elementRef.nativeElement;
|
||||
|
||||
if (el.nodeName.toLowerCase() == 'md-chip' || el.hasAttribute('md-chip')) {
|
||||
el.classList.add('md-chip');
|
||||
// Always add the `md-chip` class
|
||||
el.classList.add('md-chip');
|
||||
|
||||
// If we are a basic chip, also add the `md-basic-chip` class for :not() targeting
|
||||
if (el.nodeName.toLowerCase() == 'md-basic-chip' || el.hasAttribute('md-basic-chip')) {
|
||||
el.classList.add('md-basic-chip');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ $md-chips-chip-margin: $md-chip-horizontal-padding / 4;
|
||||
/*
|
||||
* Only apply the margins to chips
|
||||
*/
|
||||
.md-chip {
|
||||
.md-chip:not(.md-basic-chip) {
|
||||
margin: 0 $md-chips-chip-margin 0 $md-chips-chip-margin;
|
||||
|
||||
// Remove the margin from the first element (in both LTR and RTL)
|
||||
@@ -50,7 +50,7 @@ $md-chips-chip-margin: $md-chip-horizontal-padding / 4;
|
||||
}
|
||||
}
|
||||
|
||||
.md-chip {
|
||||
.md-chip:not(.md-basic-chip) {
|
||||
display: inline-block;
|
||||
padding: $md-chip-vertical-padding $md-chip-horizontal-padding
|
||||
$md-chip-vertical-padding $md-chip-horizontal-padding;
|
||||
@@ -63,7 +63,7 @@ $md-chips-chip-margin: $md-chip-horizontal-padding / 4;
|
||||
.md-chip-list-stacked .md-chip-list-wrapper {
|
||||
display: block;
|
||||
|
||||
.md-chip {
|
||||
.md-chip:not(.md-basic-chip) {
|
||||
display: block;
|
||||
margin: 0;
|
||||
margin-bottom: $md-chip-vertical-padding;
|
||||
|
||||
Reference in New Issue
Block a user