Skip to content

Commit a29c58e

Browse files
committed
build: add most components to kitchen-sink
1 parent 508b141 commit a29c58e

File tree

13 files changed

+393
-127
lines changed

13 files changed

+393
-127
lines changed

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,14 @@ describe('MdChipList', () => {
4747
manager = chipListInstance._keyManager;
4848
});
4949

50-
it('focuses the first chip on focus', () => {
50+
it('should focus the first chip on focus', () => {
5151
chipListInstance.focus();
5252
fixture.detectChanges();
5353

5454
expect(manager.activeItemIndex).toBe(0);
5555
});
5656

57-
it('watches for chip focus', () => {
57+
it('should watch for chip focus', () => {
5858
let array = chips.toArray();
5959
let lastIndex = array.length - 1;
6060
let lastItem = array[lastIndex];
@@ -81,13 +81,14 @@ describe('MdChipList', () => {
8181
expect(manager.activeItemIndex).toEqual(2);
8282
});
8383

84-
it('focuses the previous item', () => {
84+
it('should focus the previous item', () => {
8585
let array = chips.toArray();
8686
let lastIndex = array.length - 1;
8787
let lastItem = array[lastIndex];
8888

89-
// Focus the last item
89+
// Focus the last item. Fake updating the _hasFocus state for unit tests.
9090
lastItem.focus();
91+
lastItem._hasFocus = true;
9192

9293
// Destroy the last item
9394
testComponent.remove = lastIndex;

src/lib/chips/chip-list.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,8 @@ export class MdChipList implements AfterContentInit, OnDestroy {
194194
// On destroy, remove the item from our list, and check focus
195195
chip.destroy.subscribe(() => {
196196
let chipIndex: number = this.chips.toArray().indexOf(chip);
197-
198-
if (this._isValidIndex(chipIndex)) {
197+
198+
if (this._isValidIndex(chipIndex) && chip._hasFocus) {
199199
// Check whether the chip is the last item
200200
if (chipIndex < this.chips.length - 1) {
201201
this._keyManager.setActiveItem(chipIndex);

src/lib/chips/chip.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ describe('Chips', () => {
2929

3030
chipDebugElement = fixture.debugElement.query(By.directive(MdChip));
3131
chipNativeElement = chipDebugElement.nativeElement;
32-
chipInstance = chipDebugElement.componentInstance;
32+
chipInstance = chipDebugElement.injector.get(MdChip);
3333

3434
document.body.appendChild(chipNativeElement);
3535
});
@@ -56,7 +56,7 @@ describe('Chips', () => {
5656
chipDebugElement = fixture.debugElement.query(By.directive(MdChip));
5757
chipListNativeElement = fixture.debugElement.query(By.directive(MdChipList)).nativeElement;
5858
chipNativeElement = chipDebugElement.nativeElement;
59-
chipInstance = chipDebugElement.componentInstance;
59+
chipInstance = chipDebugElement.injector.get(MdChip);
6060
testComponent = fixture.debugElement.componentInstance;
6161

6262
document.body.appendChild(chipNativeElement);

src/lib/chips/chip.ts

Lines changed: 39 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {
2-
Component,
2+
Directive,
33
ElementRef,
44
EventEmitter,
55
Input,
@@ -16,36 +16,56 @@ export interface MdChipEvent {
1616
chip: MdChip;
1717
}
1818

19+
/**
20+
* Dummy directive to add CSS class to basic chips.
21+
* @docs-private
22+
*/
23+
@Directive({
24+
selector: `md-basic-chip, [md-basic-chip], mat-basic-chip, [mat-basic-chip]`,
25+
host: {'class': 'mat-basic-chip'}
26+
})
27+
export class MdBasicChip { }
28+
1929
/**
2030
* Material design styled Chip component. Used inside the MdChipList component.
2131
*/
22-
@Component({
32+
@Directive({
2333
selector: `md-basic-chip, [md-basic-chip], md-chip, [md-chip],
2434
mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]`,
25-
template: `<ng-content></ng-content>`,
2635
host: {
27-
'[class.mat-chip]': 'true',
36+
'class': 'mat-chip',
2837
'tabindex': '-1',
2938
'role': 'option',
30-
3139
'[class.mat-chip-selected]': 'selected',
32-
'[attr.disabled]': 'disabled',
33-
'[attr.aria-disabled]': '_isAriaDisabled',
34-
35-
'(click)': '_handleClick($event)'
40+
'[attr.disabled]': 'disabled || null',
41+
'[attr.aria-disabled]': '_isAriaDisabled()',
42+
'(click)': '_handleClick($event)',
43+
'(focus)': '_hasFocus = true',
44+
'(blur)': '_hasFocus = false',
3645
}
3746
})
3847
export class MdChip implements Focusable, OnInit, OnDestroy {
39-
40-
/** Whether or not the chip is disabled. Disabled chips cannot be focused. */
48+
/** Whether or not the chip is disabled. */
49+
@Input() get disabled(): boolean { return this._disabled; }
50+
set disabled(value: boolean) { this._disabled = coerceBooleanProperty(value); }
4151
protected _disabled: boolean = null;
4252

43-
/** Whether or not the chip is selected. */
53+
/** Whether the chip is selected. */
54+
@Input() get selected(): boolean { return this._selected; }
55+
set selected(value: boolean) {
56+
this._selected = coerceBooleanProperty(value);
57+
(this.selected ? this.select : this.deselect).emit({chip: this});
58+
}
4459
protected _selected: boolean = false;
4560

46-
/** The palette color of selected chips. */
61+
/** Color of the chip. Can be `primary`, `accent`, or `warn`. */
62+
@Input() get color(): string { return this._color; }
63+
set color(value: string) { this._updateColor(value); }
4764
protected _color: string = 'primary';
4865

66+
/** Whether the chip has focus. */
67+
_hasFocus: boolean = false;
68+
4969
/** Emitted when the chip is focused. */
5070
onFocus = new EventEmitter<MdChipEvent>();
5171

@@ -61,60 +81,19 @@ export class MdChip implements Focusable, OnInit, OnDestroy {
6181
constructor(protected _renderer: Renderer2, protected _elementRef: ElementRef) { }
6282

6383
ngOnInit(): void {
64-
this._addDefaultCSSClass();
6584
this._updateColor(this._color);
6685
}
6786

6887
ngOnDestroy(): void {
6988
this.destroy.emit({chip: this});
7089
}
7190

72-
/** Whether or not the chip is disabled. */
73-
@Input() get disabled(): boolean {
74-
return this._disabled;
75-
}
76-
77-
/** Sets the disabled state of the chip. */
78-
set disabled(value: boolean) {
79-
this._disabled = coerceBooleanProperty(value) ? true : null;
80-
}
81-
82-
/** A String representation of the current disabled state. */
83-
get _isAriaDisabled(): string {
84-
return String(coerceBooleanProperty(this.disabled));
85-
}
86-
87-
/** Whether or not this chip is selected. */
88-
@Input() get selected(): boolean {
89-
return this._selected;
90-
}
91-
92-
set selected(value: boolean) {
93-
this._selected = coerceBooleanProperty(value);
94-
95-
if (this._selected) {
96-
this.select.emit({chip: this});
97-
} else {
98-
this.deselect.emit({chip: this});
99-
}
100-
}
101-
10291
/**
10392
* Toggles the current selected state of this chip.
10493
* @return Whether the chip is selected.
10594
*/
10695
toggleSelected(): boolean {
107-
this.selected = !this.selected;
108-
return this.selected;
109-
}
110-
111-
/** The color of the chip. Can be `primary`, `accent`, or `warn`. */
112-
@Input() get color(): string {
113-
return this._color;
114-
}
115-
116-
set color(value: string) {
117-
this._updateColor(value);
96+
return this.selected = !this.selected;
11897
}
11998

12099
/** Allows for programmatic focusing of the chip. */
@@ -123,6 +102,11 @@ export class MdChip implements Focusable, OnInit, OnDestroy {
123102
this.onFocus.emit({chip: this});
124103
}
125104

105+
/** The aria-disabled state for the chip */
106+
_isAriaDisabled(): string {
107+
return String(this.disabled);
108+
}
109+
126110
/** Ensures events fire properly upon click. */
127111
_handleClick(event: Event) {
128112
// Check disabled
@@ -134,20 +118,6 @@ export class MdChip implements Focusable, OnInit, OnDestroy {
134118
}
135119
}
136120

137-
/** Initializes the appropriate CSS classes based on the chip type (basic or standard). */
138-
private _addDefaultCSSClass() {
139-
let el: HTMLElement = this._elementRef.nativeElement;
140-
141-
// Always add the `mat-chip` class
142-
this._renderer.addClass(el, 'mat-chip');
143-
144-
// If we are a basic chip, also add the `mat-basic-chip` class for :not() targeting
145-
if (el.nodeName.toLowerCase() == 'mat-basic-chip' || el.hasAttribute('mat-basic-chip') ||
146-
el.nodeName.toLowerCase() == 'md-basic-chip' || el.hasAttribute('md-basic-chip')) {
147-
this._renderer.addClass(el, 'mat-basic-chip');
148-
}
149-
}
150-
151121
/** Updates the private _color variable and the native element. */
152122
private _updateColor(newColor: string) {
153123
this._setElementColor(this._color, false);

src/lib/chips/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import {NgModule} from '@angular/core';
22
import {MdChipList} from './chip-list';
3-
import {MdChip} from './chip';
3+
import {MdChip, MdBasicChip} from './chip';
44

55

66
@NgModule({
77
imports: [],
8-
exports: [MdChipList, MdChip],
9-
declarations: [MdChipList, MdChip]
8+
exports: [MdChipList, MdChip, MdBasicChip],
9+
declarations: [MdChipList, MdChip, MdBasicChip]
1010
})
1111
export class MdChipsModule {}
1212

src/lib/core/a11y/focus-trap.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ import {
88
Injectable,
99
} from '@angular/core';
1010
import {InteractivityChecker} from './interactivity-checker';
11+
import {Platform} from '../platform/platform';
1112
import {coerceBooleanProperty} from '../coercion/boolean-property';
1213

1314
import 'rxjs/add/operator/first';
1415

16+
1517
/**
1618
* Class that allows for trapping focus within a DOM element.
1719
*
@@ -37,6 +39,7 @@ export class FocusTrap {
3739

3840
constructor(
3941
private _element: HTMLElement,
42+
private _platform: Platform,
4043
private _checker: InteractivityChecker,
4144
private _ngZone: NgZone,
4245
deferAnchors = false) {
@@ -64,6 +67,11 @@ export class FocusTrap {
6467
* in the constructor, but can be deferred for cases like directives with `*ngIf`.
6568
*/
6669
attachAnchors(): void {
70+
// If we're not on the browser, there can be no focus to trap.
71+
if (!this._platform.isBrowser) {
72+
return;
73+
}
74+
6775
if (!this._startAnchor) {
6876
this._startAnchor = this._createAnchor();
6977
}
@@ -212,10 +220,13 @@ export class FocusTrap {
212220
/** Factory that allows easy instantiation of focus traps. */
213221
@Injectable()
214222
export class FocusTrapFactory {
215-
constructor(private _checker: InteractivityChecker, private _ngZone: NgZone) { }
223+
constructor(
224+
private _checker: InteractivityChecker,
225+
private _platform: Platform,
226+
private _ngZone: NgZone) { }
216227

217228
create(element: HTMLElement, deferAnchors = false): FocusTrap {
218-
return new FocusTrap(element, this._checker, this._ngZone, deferAnchors);
229+
return new FocusTrap(element, this._platform, this._checker, this._ngZone, deferAnchors);
219230
}
220231
}
221232

src/lib/core/a11y/interactivity-checker.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ export class InteractivityChecker {
4848
* @returns Whether the element is tabbable.
4949
*/
5050
isTabbable(element: HTMLElement): boolean {
51+
// Nothing is tabbable on the the server 😎
52+
if (!this._platform.isBrowser) {
53+
return false;
54+
}
5155

5256
let frameElement = getWindow(element).frameElement as HTMLElement;
5357

src/lib/core/platform/features.ts

Lines changed: 49 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,56 @@
1+
/** Cached result Set of input types support by the current browser. */
12
let supportedInputTypes: Set<string>;
23

4+
/** Types of <input> that *might* be supported. */
5+
const candidateInputTypes = [
6+
// `color` must come first. Chrome 56 shows a warning if we change the type to `color` after
7+
// first changing it to something else:
8+
// The specified value "" does not conform to the required format.
9+
// The format is "#rrggbb" where rr, gg, bb are two-digit hexadecimal numbers.
10+
'color',
11+
'button',
12+
'checkbox',
13+
'date',
14+
'datetime-local',
15+
'email',
16+
'file',
17+
'hidden',
18+
'image',
19+
'month',
20+
'number',
21+
'password',
22+
'radio',
23+
'range',
24+
'reset',
25+
'search',
26+
'submit',
27+
'tel',
28+
'text',
29+
'time',
30+
'url',
31+
'week',
32+
];
33+
334
/** @returns The input types supported by this browser. */
435
export function getSupportedInputTypes(): Set<string> {
5-
if (!supportedInputTypes) {
6-
let featureTestInput = document.createElement('input');
7-
supportedInputTypes = new Set([
8-
// `color` must come first. Chrome 56 shows a warning if we change the type to `color` after
9-
// first changing it to something else:
10-
// The specified value "" does not conform to the required format.
11-
// The format is "#rrggbb" where rr, gg, bb are two-digit hexadecimal numbers.
12-
'color',
13-
'button',
14-
'checkbox',
15-
'date',
16-
'datetime-local',
17-
'email',
18-
'file',
19-
'hidden',
20-
'image',
21-
'month',
22-
'number',
23-
'password',
24-
'radio',
25-
'range',
26-
'reset',
27-
'search',
28-
'submit',
29-
'tel',
30-
'text',
31-
'time',
32-
'url',
33-
'week',
34-
].filter(value => {
35-
featureTestInput.setAttribute('type', value);
36-
return featureTestInput.type === value;
37-
}));
36+
// Result is cached.
37+
if (supportedInputTypes) {
38+
return supportedInputTypes;
39+
}
40+
41+
// We can't check if an input type is not supported until we're on the browser, so say that
42+
// everything is supported when not on the browser. We don't use `Platform` here since it's
43+
// just a helper function and can't inject it.
44+
if (typeof document !== 'object' || !!document) {
45+
supportedInputTypes = new Set(candidateInputTypes);
46+
return supportedInputTypes;
3847
}
48+
49+
let featureTestInput = document.createElement('input');
50+
supportedInputTypes = new Set(candidateInputTypes.filter(value => {
51+
featureTestInput.setAttribute('type', value);
52+
return featureTestInput.type === value;
53+
}));
54+
3955
return supportedInputTypes;
4056
}

0 commit comments

Comments
 (0)