Skip to content

Commit f89c6db

Browse files
jelbournandrewseguin
authored andcommitted
build: add most components to kitchen-sink (#4836)
* build: add most components to kitchen-sink * debug
1 parent 549d622 commit f89c6db

File tree

14 files changed

+391
-122
lines changed

14 files changed

+391
-122
lines changed

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

Lines changed: 5 additions & 5 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,13 @@ 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
90-
lastItem.focus();
89+
// Focus the last item by fake updating the _hasFocus state for unit tests.
90+
lastItem._hasFocus = true;
9191

9292
// Destroy the last item
9393
testComponent.remove = lastIndex;

src/lib/chips/chip-list.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ export class MdChipList implements AfterContentInit, OnDestroy {
195195
chip.destroy.subscribe(() => {
196196
let chipIndex: number = this.chips.toArray().indexOf(chip);
197197

198-
if (this._isValidIndex(chipIndex)) {
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: 36 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import {
2-
Component,
2+
Directive,
33
ElementRef,
44
EventEmitter,
55
Input,
66
OnDestroy,
7-
OnInit,
87
Output,
98
Renderer2,
109
} from '@angular/core';
@@ -24,34 +23,52 @@ export class MdChipBase {
2423
export const _MdChipMixinBase = mixinColor(MdChipBase, 'primary');
2524

2625

26+
/**
27+
* Dummy directive to add CSS class to basic chips.
28+
* @docs-private
29+
*/
30+
@Directive({
31+
selector: `md-basic-chip, [md-basic-chip], mat-basic-chip, [mat-basic-chip]`,
32+
host: {'class': 'mat-basic-chip'}
33+
})
34+
export class MdBasicChip { }
35+
2736
/**
2837
* Material design styled Chip component. Used inside the MdChipList component.
2938
*/
30-
@Component({
39+
@Directive({
3140
selector: `md-basic-chip, [md-basic-chip], md-chip, [md-chip],
3241
mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]`,
33-
template: `<ng-content></ng-content>`,
3442
inputs: ['color'],
3543
host: {
36-
'[class.mat-chip]': 'true',
44+
'class': 'mat-chip',
3745
'tabindex': '-1',
3846
'role': 'option',
39-
4047
'[class.mat-chip-selected]': 'selected',
41-
'[attr.disabled]': 'disabled',
42-
'[attr.aria-disabled]': '_isAriaDisabled',
43-
44-
'(click)': '_handleClick($event)'
48+
'[attr.disabled]': 'disabled || null',
49+
'[attr.aria-disabled]': '_isAriaDisabled()',
50+
'(click)': '_handleClick($event)',
51+
'(focus)': '_hasFocus = true',
52+
'(blur)': '_hasFocus = false',
4553
}
4654
})
47-
export class MdChip extends _MdChipMixinBase implements Focusable, OnInit, OnDestroy, CanColor {
48-
49-
/** Whether or not the chip is disabled. Disabled chips cannot be focused. */
55+
export class MdChip extends _MdChipMixinBase implements Focusable, OnDestroy, CanColor {
56+
/** Whether or not the chip is disabled. */
57+
@Input() get disabled(): boolean { return this._disabled; }
58+
set disabled(value: boolean) { this._disabled = coerceBooleanProperty(value); }
5059
protected _disabled: boolean = null;
5160

52-
/** Whether or not the chip is selected. */
61+
/** Whether the chip is selected. */
62+
@Input() get selected(): boolean { return this._selected; }
63+
set selected(value: boolean) {
64+
this._selected = coerceBooleanProperty(value);
65+
(this.selected ? this.select : this.deselect).emit({chip: this});
66+
}
5367
protected _selected: boolean = false;
5468

69+
/** Whether the chip has focus. */
70+
_hasFocus: boolean = false;
71+
5572
/** Emitted when the chip is focused. */
5673
onFocus = new EventEmitter<MdChipEvent>();
5774

@@ -68,44 +85,10 @@ export class MdChip extends _MdChipMixinBase implements Focusable, OnInit, OnDes
6885
super(renderer, elementRef);
6986
}
7087

71-
ngOnInit(): void {
72-
this._addDefaultCSSClass();
73-
}
74-
7588
ngOnDestroy(): void {
7689
this.destroy.emit({chip: this});
7790
}
7891

79-
/** Whether or not the chip is disabled. */
80-
@Input() get disabled(): boolean {
81-
return this._disabled;
82-
}
83-
84-
/** Sets the disabled state of the chip. */
85-
set disabled(value: boolean) {
86-
this._disabled = coerceBooleanProperty(value) ? true : null;
87-
}
88-
89-
/** A String representation of the current disabled state. */
90-
get _isAriaDisabled(): string {
91-
return String(coerceBooleanProperty(this.disabled));
92-
}
93-
94-
/** Whether or not this chip is selected. */
95-
@Input() get selected(): boolean {
96-
return this._selected;
97-
}
98-
99-
set selected(value: boolean) {
100-
this._selected = coerceBooleanProperty(value);
101-
102-
if (this._selected) {
103-
this.select.emit({chip: this});
104-
} else {
105-
this.deselect.emit({chip: this});
106-
}
107-
}
108-
10992
/**
11093
* Toggles the current selected state of this chip.
11194
* @return Whether the chip is selected.
@@ -121,6 +104,11 @@ export class MdChip extends _MdChipMixinBase implements Focusable, OnInit, OnDes
121104
this.onFocus.emit({chip: this});
122105
}
123106

107+
/** The aria-disabled state for the chip */
108+
_isAriaDisabled(): string {
109+
return String(this.disabled);
110+
}
111+
124112
/** Ensures events fire properly upon click. */
125113
_handleClick(event: Event) {
126114
// Check disabled
@@ -131,18 +119,4 @@ export class MdChip extends _MdChipMixinBase implements Focusable, OnInit, OnDes
131119
this.focus();
132120
}
133121
}
134-
135-
/** Initializes the appropriate CSS classes based on the chip type (basic or standard). */
136-
private _addDefaultCSSClass() {
137-
let el: HTMLElement = this._elementRef.nativeElement;
138-
139-
// Always add the `mat-chip` class
140-
this._renderer.addClass(el, 'mat-chip');
141-
142-
// If we are a basic chip, also add the `mat-basic-chip` class for :not() targeting
143-
if (el.nodeName.toLowerCase() == 'mat-basic-chip' || el.hasAttribute('mat-basic-chip') ||
144-
el.nodeName.toLowerCase() == 'md-basic-chip' || el.hasAttribute('md-basic-chip')) {
145-
this._renderer.addClass(el, 'mat-basic-chip');
146-
}
147-
}
148122
}

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
}
@@ -223,10 +231,13 @@ export class FocusTrap {
223231
/** Factory that allows easy instantiation of focus traps. */
224232
@Injectable()
225233
export class FocusTrapFactory {
226-
constructor(private _checker: InteractivityChecker, private _ngZone: NgZone) { }
234+
constructor(
235+
private _checker: InteractivityChecker,
236+
private _platform: Platform,
237+
private _ngZone: NgZone) { }
227238

228239
create(element: HTMLElement, deferAnchors = false): FocusTrap {
229-
return new FocusTrap(element, this._checker, this._ngZone, deferAnchors);
240+
return new FocusTrap(element, this._platform, this._checker, this._ngZone, deferAnchors);
230241
}
231242
}
232243

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
}

src/lib/datepicker/datepicker.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ export class MdDatepicker<D> implements OnDestroy {
234234
config.viewContainerRef = this._viewContainerRef;
235235

236236
this._dialogRef = this._dialog.open(MdDatepickerContent, config);
237-
this._dialogRef.afterClosed().first().subscribe(() => this.close());
237+
this._dialogRef.afterClosed().subscribe(() => this.close());
238238
this._dialogRef.componentInstance.datepicker = this;
239239
}
240240

@@ -257,7 +257,7 @@ export class MdDatepicker<D> implements OnDestroy {
257257
this._ngZone.onStable.first().subscribe(() => this._popupRef.updatePosition());
258258
}
259259

260-
this._popupRef.backdropClick().first().subscribe(() => this.close());
260+
this._popupRef.backdropClick().subscribe(() => this.close());
261261
}
262262

263263
/** Create the popup. */

0 commit comments

Comments
 (0)