-
-
+
{{postalCode.characterCount}} / 5
@@ -57,7 +57,7 @@ Textarea
Input
-
{{characterCountHintExample.characterCount}} / 100
@@ -66,7 +66,7 @@ Input
Textarea
-
{{characterCountHintExampleTextarea.characterCount}} / 100
@@ -89,7 +89,7 @@ Textarea
-
{{input.characterCount}} / 100
From bb1f00629b4621b4abaedc5f5f1414950f417fdc Mon Sep 17 00:00:00 2001
From: Elad Bezalel
Date: Wed, 14 Dec 2016 03:55:37 +0200
Subject: [PATCH 28/91] chore: correct coersion to coercion (#2160)
---
src/lib/checkbox/checkbox.ts | 2 +-
src/lib/core/a11y/focus-trap.ts | 2 +-
src/lib/core/{coersion => coercion}/boolean-property.spec.ts | 0
src/lib/core/{coersion => coercion}/boolean-property.ts | 0
src/lib/core/{coersion => coercion}/number-property.spec.ts | 0
src/lib/core/{coersion => coercion}/number-property.ts | 0
src/lib/core/core.ts | 4 ++--
src/lib/radio/radio.ts | 2 +-
src/lib/select/option.ts | 2 +-
src/lib/select/select.ts | 2 +-
src/lib/tabs/tab-label-wrapper.ts | 2 +-
src/lib/tabs/tab.ts | 2 +-
12 files changed, 9 insertions(+), 9 deletions(-)
rename src/lib/core/{coersion => coercion}/boolean-property.spec.ts (100%)
rename src/lib/core/{coersion => coercion}/boolean-property.ts (100%)
rename src/lib/core/{coersion => coercion}/number-property.spec.ts (100%)
rename src/lib/core/{coersion => coercion}/number-property.ts (100%)
diff --git a/src/lib/checkbox/checkbox.ts b/src/lib/checkbox/checkbox.ts
index 4d440785709f..89d8d01b36ac 100644
--- a/src/lib/checkbox/checkbox.ts
+++ b/src/lib/checkbox/checkbox.ts
@@ -15,7 +15,7 @@ import {
} from '@angular/core';
import {CommonModule} from '@angular/common';
import {NG_VALUE_ACCESSOR, ControlValueAccessor} from '@angular/forms';
-import {coerceBooleanProperty} from '../core/coersion/boolean-property';
+import {coerceBooleanProperty} from '../core/coercion/boolean-property';
import {MdRippleModule, DefaultStyleCompatibilityModeModule} from '../core';
import {ViewportRuler} from '../core/overlay/position/viewport-ruler';
diff --git a/src/lib/core/a11y/focus-trap.ts b/src/lib/core/a11y/focus-trap.ts
index bda63179dcbe..cc52a1a8fcf8 100644
--- a/src/lib/core/a11y/focus-trap.ts
+++ b/src/lib/core/a11y/focus-trap.ts
@@ -1,6 +1,6 @@
import {Component, ViewEncapsulation, ViewChild, ElementRef, Input, NgZone} from '@angular/core';
import {InteractivityChecker} from './interactivity-checker';
-import {coerceBooleanProperty} from '../coersion/boolean-property';
+import {coerceBooleanProperty} from '../coercion/boolean-property';
/**
diff --git a/src/lib/core/coersion/boolean-property.spec.ts b/src/lib/core/coercion/boolean-property.spec.ts
similarity index 100%
rename from src/lib/core/coersion/boolean-property.spec.ts
rename to src/lib/core/coercion/boolean-property.spec.ts
diff --git a/src/lib/core/coersion/boolean-property.ts b/src/lib/core/coercion/boolean-property.ts
similarity index 100%
rename from src/lib/core/coersion/boolean-property.ts
rename to src/lib/core/coercion/boolean-property.ts
diff --git a/src/lib/core/coersion/number-property.spec.ts b/src/lib/core/coercion/number-property.spec.ts
similarity index 100%
rename from src/lib/core/coersion/number-property.spec.ts
rename to src/lib/core/coercion/number-property.spec.ts
diff --git a/src/lib/core/coersion/number-property.ts b/src/lib/core/coercion/number-property.ts
similarity index 100%
rename from src/lib/core/coersion/number-property.ts
rename to src/lib/core/coercion/number-property.ts
diff --git a/src/lib/core/core.ts b/src/lib/core/core.ts
index 0791f8842195..0cda89a4ccc4 100644
--- a/src/lib/core/core.ts
+++ b/src/lib/core/core.ts
@@ -90,8 +90,8 @@ export * from './compatibility/default-mode';
export * from './animation/animation';
// Coersion
-export {coerceBooleanProperty} from './coersion/boolean-property';
-export {coerceNumberProperty} from './coersion/number-property';
+export {coerceBooleanProperty} from './coercion/boolean-property';
+export {coerceNumberProperty} from './coercion/number-property';
// Compatibility
export {DefaultStyleCompatibilityModeModule} from './compatibility/default-mode';
diff --git a/src/lib/radio/radio.ts b/src/lib/radio/radio.ts
index 5b16ec5cf987..f455a24f6b3a 100644
--- a/src/lib/radio/radio.ts
+++ b/src/lib/radio/radio.ts
@@ -25,7 +25,7 @@ import {
MdUniqueSelectionDispatcher,
DefaultStyleCompatibilityModeModule,
} from '../core';
-import {coerceBooleanProperty} from '../core/coersion/boolean-property';
+import {coerceBooleanProperty} from '../core/coercion/boolean-property';
import {ViewportRuler} from '../core/overlay/position/viewport-ruler';
diff --git a/src/lib/select/option.ts b/src/lib/select/option.ts
index f45f6da0fc57..7fb475b1f490 100644
--- a/src/lib/select/option.ts
+++ b/src/lib/select/option.ts
@@ -8,7 +8,7 @@ import {
ViewEncapsulation
} from '@angular/core';
import {ENTER, SPACE} from '../core/keyboard/keycodes';
-import {coerceBooleanProperty} from '../core/coersion/boolean-property';
+import {coerceBooleanProperty} from '../core/coercion/boolean-property';
/**
* Option IDs need to be unique across components, so this counter exists outside of
diff --git a/src/lib/select/select.ts b/src/lib/select/select.ts
index e4b2eb936a4c..520df0bbf059 100644
--- a/src/lib/select/select.ts
+++ b/src/lib/select/select.ts
@@ -20,7 +20,7 @@ import {Dir} from '../core/rtl/dir';
import {Subscription} from 'rxjs/Subscription';
import {transformPlaceholder, transformPanel, fadeInContent} from './select-animations';
import {ControlValueAccessor, NgControl} from '@angular/forms';
-import {coerceBooleanProperty} from '../core/coersion/boolean-property';
+import {coerceBooleanProperty} from '../core/coercion/boolean-property';
import {ConnectedOverlayDirective} from '../core/overlay/overlay-directives';
import {ViewportRuler} from '../core/overlay/position/viewport-ruler';
diff --git a/src/lib/tabs/tab-label-wrapper.ts b/src/lib/tabs/tab-label-wrapper.ts
index 3177939a077d..8c1cbdf50b81 100644
--- a/src/lib/tabs/tab-label-wrapper.ts
+++ b/src/lib/tabs/tab-label-wrapper.ts
@@ -1,5 +1,5 @@
import {Directive, ElementRef, Renderer, Input} from '@angular/core';
-import {coerceBooleanProperty} from '../core/coersion/boolean-property';
+import {coerceBooleanProperty} from '../core/coercion/boolean-property';
/** Used in the `md-tab-group` view to display tab labels */
diff --git a/src/lib/tabs/tab.ts b/src/lib/tabs/tab.ts
index b11bdbc3e547..8e8363c648a0 100644
--- a/src/lib/tabs/tab.ts
+++ b/src/lib/tabs/tab.ts
@@ -3,7 +3,7 @@ import {
ViewContainerRef, Input, TemplateRef, ViewChild, OnInit, ContentChild,
Component
} from '@angular/core';
-import {coerceBooleanProperty} from '../core/coersion/boolean-property';
+import {coerceBooleanProperty} from '../core/coercion/boolean-property';
import {MdTabLabel} from './tab-label';
From ba85883e64eeb6cbac496389945460e71f5f3cbf Mon Sep 17 00:00:00 2001
From: Topher Fangio
Date: Tue, 13 Dec 2016 20:55:44 -0600
Subject: [PATCH 29/91] 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.
---
src/demo-app/chips/chips-demo.html | 64 ++++++++++-
src/demo-app/chips/chips-demo.scss | 8 ++
src/demo-app/chips/chips-demo.ts | 31 ++++++
src/lib/chips/_chips-theme.scss | 8 +-
src/lib/chips/chip-list.spec.ts | 89 +++++++++++++---
src/lib/chips/chip-list.ts | 118 ++++++++++++++++++++-
src/lib/chips/chip.spec.ts | 99 ++++++++++++++---
src/lib/chips/chip.ts | 92 ++++++++++++++--
src/lib/chips/chips.scss | 72 ++++++++++++-
src/lib/core/a11y/list-key-manager.spec.ts | 11 +-
src/lib/core/a11y/list-key-manager.ts | 11 +-
src/lib/core/keyboard/keycodes.ts | 2 +
test/karma.config.ts | 2 +-
13 files changed, 553 insertions(+), 54 deletions(-)
diff --git a/src/demo-app/chips/chips-demo.html b/src/demo-app/chips/chips-demo.html
index 99f8b4ea45a3..5100ef5f31fc 100644
--- a/src/demo-app/chips/chips-demo.html
+++ b/src/demo-app/chips/chips-demo.html
@@ -2,11 +2,67 @@
Static Chips
+ Simple
+
+
+ Chip 1
+ Chip 2
+ Chip 3
+
+
+ Advanced
+
+
+ Selected/Colored
+
+ With Events
+
+
+
+ Unstyled
+
+
+ Basic Chip 1
+ Basic Chip 2
+ Basic Chip 3
+
+
+ Material Contributors
+
- Basic Chip
- Primary
- Accent
- Warn
+
+ {{person.name}}
+
+
+
+
+
+
+
+
+ Stacked Chips
+
+
+ You can also stack the chips if you want them on top of each other.
+
+
+
+
+ None
+
+
+
+ Primary
+
+
+
+ Accent
+
+
+
+ Warn
+
\ No newline at end of file
diff --git a/src/demo-app/chips/chips-demo.scss b/src/demo-app/chips/chips-demo.scss
index 996d6cfd641f..46e1d249941e 100644
--- a/src/demo-app/chips/chips-demo.scss
+++ b/src/demo-app/chips/chips-demo.scss
@@ -1,2 +1,10 @@
.chips-demo {
+ .md-chip-list-stacked {
+ display: block;
+ max-width: 200px;
+ }
+
+ md-basic-chip {
+ margin: auto 10px;
+ }
}
\ No newline at end of file
diff --git a/src/demo-app/chips/chips-demo.ts b/src/demo-app/chips/chips-demo.ts
index ea2693f24cf2..45ac721ff490 100644
--- a/src/demo-app/chips/chips-demo.ts
+++ b/src/demo-app/chips/chips-demo.ts
@@ -1,4 +1,9 @@
import {Component} from '@angular/core';
+import {MdInput} from '@angular/material';
+
+export interface Person {
+ name: string;
+}
@Component({
moduleId: module.id,
@@ -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;
+ }
}
diff --git a/src/lib/chips/_chips-theme.scss b/src/lib/chips/_chips-theme.scss
index 5f0826096c76..9fa1b60b2240 100644
--- a/src/lib/chips/_chips-theme.scss
+++ b/src/lib/chips/_chips-theme.scss
@@ -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);
@@ -26,4 +32,4 @@
color: md-contrast($warn, 500);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/lib/chips/chip-list.spec.ts b/src/lib/chips/chip-list.spec.ts
index 14c1b69fa674..cd77b9c234d3 100644
--- a/src/lib/chips/chip-list.spec.ts
+++ b/src/lib/chips/chip-list.spec.ts
@@ -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;
+ let chipListDebugElement: DebugElement;
+ let chipListNativeElement: HTMLElement;
+ let chipListInstance: MdChipList;
+ let testComponent: StaticChipList;
+ let items: QueryList;
+ let manager: ListKeyManager;
beforeEach(async(() => {
TestBed.configureTestingModule({
@@ -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: `
- {{name}} 1
- {{name}} 2
- {{name}} 3
+ {{name}} 1
+ {{name}} 2
+ {{name}} 3
+ {{name}} 4
+ {{name}} 5
`
})
class StaticChipList {
name: 'Test';
+ remove: Number;
}
diff --git a/src/lib/chips/chip-list.ts b/src/lib/chips/chip-list.ts
index 1d7a2c778e27..aebd396ac829 100644
--- a/src/lib/chips/chip-list.ts
+++ b/src/lib/chips/chip-list.ts
@@ -1,32 +1,142 @@
import {
+ AfterContentInit,
ChangeDetectionStrategy,
Component,
+ ContentChildren,
ElementRef,
ModuleWithProviders,
NgModule,
+ QueryList,
ViewEncapsulation
} from '@angular/core';
import {MdChip} from './chip';
+import {ListKeyManager} from '../core/a11y/list-key-manager';
+/**
+ * A material design chips component (named ChipList for it's similarity to the List component).
+ *
+ * Example:
+ *
+ *
+ * Chip 1
+ * Chip 2
+ *
+ */
@Component({
moduleId: module.id,
selector: 'md-chip-list',
- template: ` `,
+ template: `
`,
host: {
// Properties
'tabindex': '0',
'role': 'listbox',
- 'class': 'md-chip-list'
+ 'class': 'md-chip-list',
+
+ // Events
+ '(focus)': '_keyManager.focusFirstItem()',
+ '(keydown)': 'keydown($event)'
+ },
+ queries: {
+ chips: new ContentChildren(MdChip)
},
styleUrls: ['chips.css'],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush
})
-export class MdChipList {
+export class MdChipList implements AfterContentInit {
+
+ /** Track which chips we're listening to for focus/destruction. */
+ private _subscribed: WeakMap = new WeakMap();
+
+ /** The ListKeyManager which handles focus. */
+ _keyManager: ListKeyManager;
+
+ /** The chip components contained within this chip list. */
+ chips: QueryList;
+
constructor(private _elementRef: ElementRef) {}
- ngAfterContentInit(): void {}
+ ngAfterContentInit(): void {
+ this._keyManager = new ListKeyManager(this.chips).withFocusWrap();
+
+ // Go ahead and subscribe all of the initial chips
+ this.subscribeChips(this.chips);
+
+ // When the list changes, re-subscribe
+ this.chips.changes.subscribe((chips: QueryList) => {
+ this.subscribeChips(chips);
+ });
+ }
+
+ /** Pass relevant key presses to our key manager. */
+ keydown(event: KeyboardEvent) {
+ this._keyManager.onKeydown(event);
+ }
+
+ /**
+ * Iterate through the list of chips and add them to our list of
+ * subscribed chips.
+ *
+ * @param chips The list of chips to be subscribed.
+ */
+ protected subscribeChips(chips: QueryList): void {
+ chips.forEach(chip => this.addChip(chip));
+ }
+
+ /**
+ * Add a specific chip to our subscribed list. If the chip has
+ * already been subscribed, this ensures it is only subscribed
+ * once.
+ *
+ * @param chip The chip to be subscribed (or checked for existing
+ * subscription).
+ */
+ protected addChip(chip: MdChip) {
+ // If we've already been subscribed to a parent, do nothing
+ if (this._subscribed.has(chip)) {
+ return;
+ }
+
+ // Watch for focus events outside of the keyboard navigation
+ chip.onFocus.subscribe(() => {
+ let chipIndex: number = this.chips.toArray().indexOf(chip);
+
+ if (this.isValidIndex(chipIndex)) {
+ this._keyManager.updateFocusedItemIndex(chipIndex);
+ }
+ });
+
+ // On destroy, remove the item from our list, and check focus
+ chip.destroy.subscribe(() => {
+ let chipIndex: number = this.chips.toArray().indexOf(chip);
+
+ if (this.isValidIndex(chipIndex)) {
+ // Check whether the chip is the last item
+ if (chipIndex < this.chips.length - 1) {
+ this._keyManager.setFocus(chipIndex);
+ } else if (chipIndex - 1 >= 0) {
+ this._keyManager.setFocus(chipIndex - 1);
+ }
+ }
+
+ this._subscribed.delete(chip);
+ chip.destroy.unsubscribe();
+ });
+
+ this._subscribed.set(chip, true);
+ }
+
+ /**
+ * Utility to ensure all indexes are valid.
+ *
+ * @param index The index to be checked.
+ * @returns {boolean} True if the index is valid for our list of chips.
+ */
+ private isValidIndex(index: number): boolean {
+ return index >= 0 && index < this.chips.length;
+ }
+
}
@NgModule({
diff --git a/src/lib/chips/chip.spec.ts b/src/lib/chips/chip.spec.ts
index 15b0157012d6..0b623e3bb3ce 100644
--- a/src/lib/chips/chip.spec.ts
+++ b/src/lib/chips/chip.spec.ts
@@ -1,47 +1,118 @@
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {Component, DebugElement} from '@angular/core';
import {By} from '@angular/platform-browser';
-import {MdChip, MdChipsModule} from './index';
+import {MdChipList, MdChip, MdChipsModule} from './index';
-describe('MdChip', () => {
+describe('Chips', () => {
let fixture: ComponentFixture;
+ let chipDebugElement: DebugElement;
+ let chipListNativeElement: HTMLElement;
+ let chipNativeElement: HTMLElement;
+ let chipInstance: MdChip;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [MdChipsModule.forRoot()],
declarations: [
- SingleChip
+ BasicChip, SingleChip
]
});
TestBed.compileComponents();
}));
- describe('basic behaviors', () => {
- let chipDebugElement: DebugElement;
- let chipNativeElement: HTMLElement;
- let chipInstance: MdChip;
- let testComponent: SingleChip;
+ describe('MdBasicChip', () => {
beforeEach(() => {
- fixture = TestBed.createComponent(SingleChip);
+ fixture = TestBed.createComponent(BasicChip);
fixture.detectChanges();
chipDebugElement = fixture.debugElement.query(By.directive(MdChip));
chipNativeElement = chipDebugElement.nativeElement;
chipInstance = chipDebugElement.componentInstance;
- testComponent = fixture.debugElement.componentInstance;
+
+ document.body.appendChild(chipNativeElement);
+ });
+
+ afterEach(() => {
+ document.body.removeChild(chipNativeElement);
});
- it('adds the `md-chip` class', () => {
- expect(chipNativeElement.classList).toContain('md-chip');
+ it('does not add the `md-chip` class', () => {
+ expect(chipNativeElement.classList).not.toContain('md-chip');
+ });
+ });
+
+ describe('MdChip', () => {
+ let testComponent: SingleChip;
+
+ describe('basic behaviors', () => {
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(SingleChip);
+ fixture.detectChanges();
+
+ chipDebugElement = fixture.debugElement.query(By.directive(MdChip));
+ chipListNativeElement = fixture.debugElement.query(By.directive(MdChipList)).nativeElement;
+ chipNativeElement = chipDebugElement.nativeElement;
+ chipInstance = chipDebugElement.componentInstance;
+ testComponent = fixture.debugElement.componentInstance;
+
+ document.body.appendChild(chipNativeElement);
+ });
+
+ afterEach(() => {
+ document.body.removeChild(chipNativeElement);
+ });
+
+ it('adds the `md-chip` class', () => {
+ expect(chipNativeElement.classList).toContain('md-chip');
+ });
+
+ it('emits focus on click', () => {
+ spyOn(chipInstance, 'focus').and.callThrough();
+
+ chipNativeElement.click();
+
+ expect(chipInstance.focus).toHaveBeenCalledTimes(1);
+ });
+
+ it('emits destroy on destruction', () => {
+ spyOn(testComponent, 'chipDestroy').and.callThrough();
+
+ // Force a destroy callback
+ testComponent.shouldShow = false;
+ fixture.detectChanges();
+
+ expect(testComponent.chipDestroy).toHaveBeenCalledTimes(1);
+ });
});
});
});
@Component({
- template: `{{name}} `
+ template: `
+
+
+
+ {{name}}
+
+
+ `
})
class SingleChip {
- name: 'Test';
+ name: String = 'Test';
+ shouldShow: Boolean = true;
+
+ chipFocus() {
+ }
+
+ chipDestroy() {
+ }
+}
+
+@Component({
+ template: `{{name}} `
+})
+class BasicChip {
}
diff --git a/src/lib/chips/chip.ts b/src/lib/chips/chip.ts
index 461834f05266..9b9fadb5a052 100644
--- a/src/lib/chips/chip.ts
+++ b/src/lib/chips/chip.ts
@@ -1,17 +1,95 @@
-import { Component, ElementRef, Renderer } from '@angular/core';
+import {
+ Component,
+ ElementRef,
+ EventEmitter,
+ Input,
+ OnDestroy,
+ OnInit,
+ Output,
+ Renderer
+} from '@angular/core';
+import {MdFocusable} from '../core/a11y/list-key-manager';
+import {coerceBooleanProperty} from '../core/coersion/boolean-property';
+
+export interface MdChipEvent {
+ chip: MdChip;
+}
+
+/**
+ * A material design styled Chip component. Used inside the ChipList component.
+ */
@Component({
- selector: 'md-chip, [md-chip]',
+ selector: 'md-basic-chip, [md-basic-chip], md-chip, [md-chip]',
template: ` `,
host: {
- // Properties
- 'class': 'md-chip',
'tabindex': '-1',
- 'role': 'option'
+ 'role': 'option',
+
+ '[attr.disabled]': 'disabled',
+ '[attr.aria-disabled]': '_isAriaDisabled',
+
+ '(click)': '_handleClick($event)'
}
})
-export class MdChip {
+export class MdChip implements MdFocusable, OnInit, OnDestroy {
+
+ /* Whether or not the chip is disabled. */
+ protected _disabled: boolean = null;
+
+ /**
+ * Emitted when the chip is focused.
+ */
+ onFocus = new EventEmitter();
+
+ /**
+ * Emitted when the chip is destroyed.
+ */
+ @Output() destroy = new EventEmitter();
+
constructor(protected _renderer: Renderer, protected _elementRef: ElementRef) {}
- ngAfterContentInit(): void {}
+ ngOnInit(): void {
+ let el: HTMLElement = this._elementRef.nativeElement;
+
+ if (el.nodeName.toLowerCase() == 'md-chip' || el.hasAttribute('md-chip')) {
+ el.classList.add('md-chip');
+ }
+ }
+
+ ngOnDestroy(): void {
+ this.destroy.emit({ chip: this });
+ }
+
+ /** Whether or not the chip is disabled. */
+ @Input() get disabled(): boolean {
+ return this._disabled;
+ }
+
+ /** Sets the disabled state of the chip. */
+ set disabled(value: boolean) {
+ this._disabled = coerceBooleanProperty(value) ? true : null;
+ }
+
+ /** A String representation of the current disabled state. */
+ get _isAriaDisabled(): string {
+ return String(coerceBooleanProperty(this.disabled));
+ }
+
+ /** Allows for programmatic focusing of the chip. */
+ focus(): void {
+ this._renderer.invokeElementMethod(this._elementRef.nativeElement, 'focus');
+ this.onFocus.emit({ chip: this });
+ }
+
+ /** Ensures events fire properly upon click. */
+ _handleClick(event: Event) {
+ // Check disabled
+ if (this.disabled) {
+ event.preventDefault();
+ event.stopPropagation();
+ } else {
+ this.focus();
+ }
+ }
}
diff --git a/src/lib/chips/chips.scss b/src/lib/chips/chips.scss
index 7fa57fd207f8..379277e9daab 100644
--- a/src/lib/chips/chips.scss
+++ b/src/lib/chips/chips.scss
@@ -1,8 +1,53 @@
$md-chip-vertical-padding: 8px;
$md-chip-horizontal-padding: 12px;
+$md-chip-font-size: 13px;
+$md-chip-line-height: 16px;
-.md-chip-list {
- padding: $md-chip-horizontal-padding;
+$md-chips-chip-margin: $md-chip-horizontal-padding / 4;
+
+.md-chip-list-wrapper {
+
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ align-items: flex-start;
+
+ /*
+ * Only apply the margins to chips
+ */
+ .md-chip {
+ margin: 0 $md-chips-chip-margin 0 $md-chips-chip-margin;
+
+ // Remove the margin from the first element (in both LTR and RTL)
+ &:first-child {
+ margin: {
+ left: 0;
+ right: $md-chips-chip-margin;
+ }
+
+ [dir='rtl'] & {
+ margin: {
+ left: $md-chips-chip-margin;
+ right: 0;
+ }
+ }
+ }
+
+ // Remove the margin from the last element (in both LTR and RTL)
+ &:last-child {
+ margin: {
+ left: $md-chips-chip-margin;
+ right: 0;
+ }
+
+ [dir='rtl'] & {
+ margin: {
+ left: 0;
+ right: $md-chips-chip-margin;
+ }
+ }
+ }
+ }
}
.md-chip {
@@ -11,6 +56,25 @@ $md-chip-horizontal-padding: 12px;
$md-chip-vertical-padding $md-chip-horizontal-padding;
border-radius: $md-chip-horizontal-padding * 2;
- font-size: 13px;
- line-height: 16px;
+ font-size: $md-chip-font-size;
+ line-height: $md-chip-line-height;
+}
+
+.md-chip-list-stacked .md-chip-list-wrapper {
+ display: block;
+
+ .md-chip {
+ display: block;
+ margin: 0;
+ margin-bottom: $md-chip-vertical-padding;
+
+ [dir='rtl'] & {
+ margin: 0;
+ margin-bottom: $md-chip-vertical-padding;
+ }
+
+ &:last-child, [dir='rtl'] &:last-child {
+ margin-bottom: 0;
+ }
+ }
}
diff --git a/src/lib/core/a11y/list-key-manager.spec.ts b/src/lib/core/a11y/list-key-manager.spec.ts
index a8381aa2a72b..2d2e14eefa53 100644
--- a/src/lib/core/a11y/list-key-manager.spec.ts
+++ b/src/lib/core/a11y/list-key-manager.spec.ts
@@ -23,7 +23,6 @@ class FakeEvent {
}
}
-
describe('ListKeyManager', () => {
let keyManager: ListKeyManager;
let itemList: FakeQueryList;
@@ -210,6 +209,16 @@ describe('ListKeyManager', () => {
expect(itemList.items[1].focus).toHaveBeenCalledTimes(1);
});
+ it('should allow setting the focused item without calling focus', () => {
+ expect(keyManager.focusedItemIndex)
+ .toBe(0, `Expected focus to be on the first item of the list.`);
+
+ keyManager.updateFocusedItemIndex(1);
+ expect(keyManager.focusedItemIndex)
+ .toBe(1, `Expected focusedItemIndex to be updated after calling updateFocusedItemIndex().`);
+ expect(itemList.items[1].focus).not.toHaveBeenCalledTimes(1);
+ });
+
it('should focus the first item when focusFirstItem() is called', () => {
keyManager.onKeydown(DOWN_ARROW_EVENT);
keyManager.onKeydown(DOWN_ARROW_EVENT);
diff --git a/src/lib/core/a11y/list-key-manager.ts b/src/lib/core/a11y/list-key-manager.ts
index 3be6819a31c2..9cca50160fa3 100644
--- a/src/lib/core/a11y/list-key-manager.ts
+++ b/src/lib/core/a11y/list-key-manager.ts
@@ -32,7 +32,11 @@ export class ListKeyManager {
return this;
}
- /** Sets the focus of the list to the item at the index specified. */
+ /**
+ * Sets the focus of the list to the item at the index specified.
+ *
+ * @param index The index of the item to be focused.
+ */
setFocus(index: number): void {
this._focusedItemIndex = index;
this._items.toArray()[index].focus();
@@ -89,6 +93,11 @@ export class ListKeyManager {
return this._focusedItemIndex;
}
+ /** Allows setting of the focusedItemIndex without focusing the item. */
+ updateFocusedItemIndex(index: number) {
+ this._focusedItemIndex = index;
+ }
+
/**
* Observable that emits any time the TAB key is pressed, so components can react
* when focus is shifted off of the list.
diff --git a/src/lib/core/keyboard/keycodes.ts b/src/lib/core/keyboard/keycodes.ts
index af4c4b702f9a..bff519c5d38f 100644
--- a/src/lib/core/keyboard/keycodes.ts
+++ b/src/lib/core/keyboard/keycodes.ts
@@ -20,3 +20,5 @@ export const SPACE = 32;
export const TAB = 9;
export const ESCAPE = 27;
+export const BACKSPACE = 8;
+export const DELETE = 46;
diff --git a/test/karma.config.ts b/test/karma.config.ts
index 144746c27ca3..e5fb72704ed0 100644
--- a/test/karma.config.ts
+++ b/test/karma.config.ts
@@ -16,7 +16,7 @@ export function config(config) {
require('karma-browserstack-launcher'),
require('karma-sauce-launcher'),
require('karma-chrome-launcher'),
- require('karma-firefox-launcher'),
+ require('karma-firefox-launcher')
],
files: [
{pattern: 'dist/vendor/core-js/client/core.js', included: true, watched: false},
From bac0388ca323efcf860b3b28cd40267c15a9a85a Mon Sep 17 00:00:00 2001
From: Jeremy Elbourn
Date: Tue, 13 Dec 2016 19:23:03 -0800
Subject: [PATCH 30/91] chore: fix bad chips merge and broken tests (#2207)
---
src/lib/chips/chip-list.spec.ts | 4 +++-
src/lib/chips/chip.ts | 2 +-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/lib/chips/chip-list.spec.ts b/src/lib/chips/chip-list.spec.ts
index cd77b9c234d3..01daa7be7bd9 100644
--- a/src/lib/chips/chip-list.spec.ts
+++ b/src/lib/chips/chip-list.spec.ts
@@ -22,7 +22,9 @@ describe('MdChipList', () => {
});
TestBed.compileComponents();
+ }));
+ beforeEach(() => {
fixture = TestBed.createComponent(StaticChipList);
fixture.detectChanges();
@@ -30,7 +32,7 @@ describe('MdChipList', () => {
chipListNativeElement = chipListDebugElement.nativeElement;
chipListInstance = chipListDebugElement.componentInstance;
testComponent = fixture.debugElement.componentInstance;
- }));
+ });
describe('basic behaviors', () => {
it('adds the `md-chip-list` class', () => {
diff --git a/src/lib/chips/chip.ts b/src/lib/chips/chip.ts
index 9b9fadb5a052..81cd0f48914d 100644
--- a/src/lib/chips/chip.ts
+++ b/src/lib/chips/chip.ts
@@ -10,7 +10,7 @@ import {
} from '@angular/core';
import {MdFocusable} from '../core/a11y/list-key-manager';
-import {coerceBooleanProperty} from '../core/coersion/boolean-property';
+import {coerceBooleanProperty} from '../core/coercion/boolean-property';
export interface MdChipEvent {
chip: MdChip;
From b3e21b8aee69a3171c7dcec9243815a17a5ba522 Mon Sep 17 00:00:00 2001
From: Kristiyan Kostadinov
Date: Wed, 14 Dec 2016 21:54:32 +0100
Subject: [PATCH 31/91] test(ripple): refactor long-running unit test (#2155)
Refactors a unit test that used to take 1.6s, due to it using a native `setTimeout`, by replacing the native timer with a flushable one.
Fixes #2097.
---
src/lib/core/ripple/ripple.spec.ts | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/src/lib/core/ripple/ripple.spec.ts b/src/lib/core/ripple/ripple.spec.ts
index bcbda05e9a0c..91b86f436bf9 100644
--- a/src/lib/core/ripple/ripple.spec.ts
+++ b/src/lib/core/ripple/ripple.spec.ts
@@ -1,4 +1,4 @@
-import {TestBed, ComponentFixture, async} from '@angular/core/testing';
+import {TestBed, ComponentFixture, fakeAsync, tick} from '@angular/core/testing';
import {Component, ViewChild} from '@angular/core';
import {MdRipple, MdRippleModule} from './ripple';
@@ -132,14 +132,13 @@ describe('MdRipple', () => {
expect(rippleElement.querySelectorAll('.md-ripple-foreground').length).toBe(0);
});
- it('removes foreground ripples after timeout', async(() => {
+ it('removes foreground ripples after timeout', fakeAsync(() => {
rippleElement.click();
expect(rippleElement.querySelectorAll('.md-ripple-foreground').length).toBe(1);
- // Use a real timeout because the ripple's timeout runs outside of the angular zone.
- setTimeout(() => {
- expect(rippleElement.querySelectorAll('.md-ripple-foreground').length).toBe(0);
- }, 1600);
+ tick(1600);
+
+ expect(rippleElement.querySelectorAll('.md-ripple-foreground').length).toBe(0);
}));
it('creates ripples when manually triggered', () => {
From 9e3c59c78c1f537c99a9966de030300b2a9975d1 Mon Sep 17 00:00:00 2001
From: Martin Belev
Date: Wed, 14 Dec 2016 22:55:42 +0200
Subject: [PATCH 32/91] test(interactivity-checker): remove unncessary
`setTimeout` (#2159)
---
.../core/a11y/interactivity-checker.spec.ts | 18 +++++-------------
1 file changed, 5 insertions(+), 13 deletions(-)
diff --git a/src/lib/core/a11y/interactivity-checker.spec.ts b/src/lib/core/a11y/interactivity-checker.spec.ts
index ab707d665152..a722ec8bdd5e 100644
--- a/src/lib/core/a11y/interactivity-checker.spec.ts
+++ b/src/lib/core/a11y/interactivity-checker.spec.ts
@@ -1,6 +1,5 @@
import {InteractivityChecker} from './interactivity-checker';
import {MdPlatform} from '../platform/platform';
-import {async} from '@angular/core/testing';
describe('InteractivityChecker', () => {
let testContainerElement: HTMLElement;
@@ -337,22 +336,15 @@ describe('InteractivityChecker', () => {
expect(checker.isTabbable(button)).toBe(true);
});
- it('should mark elements which are contentEditable as tabbable', async(() => {
+ it('should mark elements which are contentEditable as tabbable', () => {
let editableEl = createFromTemplate('', true);
- // Wait one tick, because the browser takes some time to update the tabIndex
- // according to the contentEditable attribute.
- setTimeout(() => {
+ expect(checker.isTabbable(editableEl)).toBe(true);
- expect(checker.isTabbable(editableEl)).toBe(true);
+ editableEl.tabIndex = -1;
- editableEl.tabIndex = -1;
-
- expect(checker.isTabbable(editableEl)).toBe(false);
-
- }, 0);
-
- }));
+ expect(checker.isTabbable(editableEl)).toBe(false);
+ });
it('should never mark iframe elements as tabbable', () => {
let iframe = createFromTemplate('
@@ -30,6 +31,7 @@
SET VALUE
TOGGLE REQUIRED
TOGGLE DISABLED
+ RESET
diff --git a/src/lib/select/select.spec.ts b/src/lib/select/select.spec.ts
index ae7302c34901..856524937535 100644
--- a/src/lib/select/select.spec.ts
+++ b/src/lib/select/select.spec.ts
@@ -193,19 +193,19 @@ describe('MdSelect', () => {
});
it('should focus the selected option if an option is selected', async(() => {
- trigger.click();
- fixture.detectChanges();
-
- const options =
- overlayContainerElement.querySelectorAll('md-option') as NodeListOf;
- options[1].click();
- fixture.detectChanges();
+ // must wait for initial writeValue promise to finish
+ fixture.whenStable().then(() => {
+ fixture.componentInstance.control.setValue('pizza-1');
+ fixture.detectChanges();
- trigger.click();
- fixture.detectChanges();
+ trigger.click();
+ fixture.detectChanges();
- fixture.whenStable().then(() => {
- expect(fixture.componentInstance.select._keyManager.focusedItemIndex).toEqual(1);
+ // must wait for animation to finish
+ fixture.whenStable().then(() => {
+ fixture.detectChanges();
+ expect(fixture.componentInstance.select._keyManager.focusedItemIndex).toEqual(1);
+ });
});
}));
@@ -307,6 +307,49 @@ describe('MdSelect', () => {
.toEqual('steak-0', `Expected control's value to be set to the new option.`);
});
+ it('should clear the selection when a nonexistent option value is selected', () => {
+ fixture.componentInstance.control.setValue('pizza-1');
+ fixture.detectChanges();
+
+ fixture.componentInstance.control.setValue('gibberish');
+ fixture.detectChanges();
+
+ const value = fixture.debugElement.query(By.css('.md-select-value'));
+ expect(value).toBe(null, `Expected trigger to be cleared when option value is not found.`);
+ expect(trigger.textContent)
+ .not.toContain('Pizza', `Expected trigger to be cleared when option value is not found.`);
+
+ trigger.click();
+ fixture.detectChanges();
+
+ const options =
+ overlayContainerElement.querySelectorAll('md-option') as NodeListOf;
+ expect(options[1].classList)
+ .not.toContain('md-selected', `Expected option with the old value not to be selected.`);
+ });
+
+
+ it('should clear the selection when the control is reset', () => {
+ fixture.componentInstance.control.setValue('pizza-1');
+ fixture.detectChanges();
+
+ fixture.componentInstance.control.reset();
+ fixture.detectChanges();
+
+ const value = fixture.debugElement.query(By.css('.md-select-value'));
+ expect(value).toBe(null, `Expected trigger to be cleared when option value is not found.`);
+ expect(trigger.textContent)
+ .not.toContain('Pizza', `Expected trigger to be cleared when option value is not found.`);
+
+ trigger.click();
+ fixture.detectChanges();
+
+ const options =
+ overlayContainerElement.querySelectorAll('md-option') as NodeListOf;
+ expect(options[1].classList)
+ .not.toContain('md-selected', `Expected option with the old value not to be selected.`);
+ });
+
it('should set the control to touched when the select is touched', () => {
expect(fixture.componentInstance.control.touched)
.toEqual(false, `Expected the control to start off as untouched.`);
diff --git a/src/lib/select/select.ts b/src/lib/select/select.ts
index 43455e495c3b..8f2189af1fad 100644
--- a/src/lib/select/select.ts
+++ b/src/lib/select/select.ts
@@ -277,11 +277,7 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
return;
}
- this.options.forEach((option: MdOption) => {
- if (option.value === value) {
- option.select();
- }
- });
+ this._setSelectionByValue(value);
}
/**
@@ -378,6 +374,30 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
scrollContainer.scrollTop = this._scrollTop;
}
+ /**
+ * Sets the selected option based on a value. If no option can be
+ * found with the designated value, the select trigger is cleared.
+ */
+ private _setSelectionByValue(value: any): void {
+ const options = this.options.toArray();
+
+ for (let i = 0; i < this.options.length; i++) {
+ if (options[i].value === value) {
+ options[i].select();
+ return;
+ }
+ }
+
+ // Clear selection if no item was selected.
+ this._clearSelection();
+ }
+
+ /** Clears the select trigger and deselects every option in the list. */
+ private _clearSelection(): void {
+ this._selected = null;
+ this._updateOptions();
+ }
+
private _getTriggerRect(): ClientRect {
return this.trigger.nativeElement.getBoundingClientRect();
}
@@ -426,6 +446,7 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
this._selected = option;
this._updateOptions();
this._setValueWidth();
+ this._placeholderState = '';
if (this.panelOpen) {
this.close();
}
From 21221ff29d5d888b910b8b34286c2fea26bb7d3d Mon Sep 17 00:00:00 2001
From: Jeremy Elbourn
Date: Fri, 16 Dec 2016 14:33:13 -0800
Subject: [PATCH 46/91] chore(doc): initial version of dgeni setup for api docs
(#2257)
---
package.json | 2 +
tools/dgeni/index.js | 148 ++++++++++++++++++
tools/dgeni/processors/categorizer.js | 112 +++++++++++++
tools/dgeni/processors/component-grouper.js | 60 +++++++
tools/dgeni/processors/docs-private-filter.js | 41 +++++
tools/dgeni/templates/common.template.html | 1 +
.../templates/componentGroup.template.html | 91 +++++++++++
tools/gulp/tasks/docs.ts | 7 +-
8 files changed, 461 insertions(+), 1 deletion(-)
create mode 100644 tools/dgeni/index.js
create mode 100644 tools/dgeni/processors/categorizer.js
create mode 100644 tools/dgeni/processors/component-grouper.js
create mode 100644 tools/dgeni/processors/docs-private-filter.js
create mode 100644 tools/dgeni/templates/common.template.html
create mode 100644 tools/dgeni/templates/componentGroup.template.html
diff --git a/package.json b/package.json
index 5248ca21b24c..55f7f90f7b61 100644
--- a/package.json
+++ b/package.json
@@ -54,6 +54,8 @@
"axe-core": "^2.0.7",
"axe-webdriverjs": "^0.4.0",
"conventional-changelog": "^1.1.0",
+ "dgeni": "^0.4.2",
+ "dgeni-packages": "^0.16.2",
"express": "^4.14.0",
"firebase-tools": "^2.2.1",
"fs-extra": "^0.26.5",
diff --git a/tools/dgeni/index.js b/tools/dgeni/index.js
new file mode 100644
index 000000000000..4153eecc91eb
--- /dev/null
+++ b/tools/dgeni/index.js
@@ -0,0 +1,148 @@
+const path = require('path');
+const fs = require('fs');
+const Dgeni = require('dgeni');
+const DgeniPackage = Dgeni.Package;
+
+// dgeni packages
+const jsdocPackage = require('dgeni-packages/jsdoc');
+const nunjucksPackage = require('dgeni-packages/nunjucks');
+const typescriptPackage = require('dgeni-packages/typescript');
+
+
+// Project configuration.
+const projectRootDir = path.resolve(__dirname, '../..');
+const sourceDir = path.resolve(projectRootDir, 'src/lib');
+const outputDir = path.resolve(projectRootDir, 'dist/docs');
+const templateDir = path.resolve(__dirname, './templates');
+
+// Package definition for material2 api docs. This only *defines* the package- it does not yet
+// actually *run* anything.
+//
+// A dgeni package is very similar to an Angular 1 module. Modules contain:
+// "services" (injectables)
+// "processors" (injectables that conform to a specific interface)
+// "templates": nunjucks templates that can be used to render content
+//
+// A dgeni package also has a `config` method, similar to an Angular 1 module.
+// A config block can inject any services/processors and configure them before
+// docs processing begins.
+
+const dgeniPackageDeps = [
+ jsdocPackage,
+ nunjucksPackage,
+ typescriptPackage,
+];
+
+let apiDocsPackage = new DgeniPackage('material2-api-docs', dgeniPackageDeps)
+
+// Processor that filters out symbols that should not be shown in the docs.
+.processor(require('./processors/docs-private-filter'))
+
+// Processor that appends categorization flags to the docs, e.g. `isDirective`, `isNgModule`, etc.
+.processor(require('./processors/categorizer'))
+
+// Processor to group components into top-level groups such as "Tabs", "Sidenav", etc.
+.processor(require('./processors/component-grouper'))
+
+.config(function(log) {
+ log.level = 'info';
+})
+
+// Configure the processor for reading files from the file system.
+.config(function(readFilesProcessor, writeFilesProcessor) {
+ readFilesProcessor.basePath = sourceDir;
+ readFilesProcessor.$enabled = false; // disable for now as we are using readTypeScriptModules
+
+ writeFilesProcessor.outputFolder = outputDir;
+})
+
+// Configure the output path for written files (i.e., file names).
+.config(function(computePathsProcessor) {
+ computePathsProcessor.pathTemplates = [{
+ docTypes: ['componentGroup'],
+ pathTemplate: '${name}',
+ outputPathTemplate: '${name}.html',
+ }];
+})
+
+// Configure custom JsDoc tags.
+.config(function(parseTagsProcessor) {
+ parseTagsProcessor.tagDefinitions = parseTagsProcessor.tagDefinitions.concat([
+ {name: 'docs-private'}
+ ]);
+})
+
+// Configure the processor for understanding TypeScript.
+.config(function(readTypeScriptModules) {
+ console.log(sourceDir);
+ readTypeScriptModules.basePath = sourceDir;
+ readTypeScriptModules.ignoreExportsMatching = [/^_/];
+ readTypeScriptModules.hidePrivateMembers = true;
+
+ // Entry points for docs generation. All publically exported symbols found through these
+ // files will have docs generated.
+ readTypeScriptModules.sourceFiles = [
+ 'autocomplete/index.ts',
+ 'button/index.ts',
+ 'button-toggle/index.ts',
+ 'card/index.ts',
+ 'checkbox/index.ts',
+ 'chips/index.ts',
+ 'core/index.ts',
+ 'dialog/index.ts',
+ 'grid-list/index.ts',
+ 'icon/index.ts',
+ 'input/index.ts',
+ 'list/index.ts',
+ 'menu/index.ts',
+ 'progress-bar/index.ts',
+ 'progress-circle/index.ts',
+ 'radio/index.ts',
+ 'select/index.ts',
+ 'sidenav/index.ts',
+ 'slide-toggle/index.ts',
+ 'slider/index.ts',
+ 'snack-bar/index.ts',
+ 'tabs/index.ts',
+ 'toolbar/index.ts',
+ 'tooltip/index.ts',
+ ];
+})
+
+
+// Configure processor for finding nunjucks templates.
+.config(function(templateFinder, templateEngine) {
+ // Where to find the templates for the doc rendering
+ templateFinder.templateFolders = [templateDir];
+
+ // Standard patterns for matching docs to templates
+ templateFinder.templatePatterns = [
+ '${ doc.template }',
+ '${ doc.id }.${ doc.docType }.template.html',
+ '${ doc.id }.template.html',
+ '${ doc.docType }.template.html',
+ '${ doc.id }.${ doc.docType }.template.js',
+ '${ doc.id }.template.js',
+ '${ doc.docType }.template.js',
+ '${ doc.id }.${ doc.docType }.template.json',
+ '${ doc.id }.template.json',
+ '${ doc.docType }.template.json',
+ 'common.template.html'
+ ];
+
+ // Nunjucks and Angular conflict in their template bindings so change Nunjucks
+ templateEngine.config.tags = {
+ variableStart: '{$',
+ variableEnd: '$}'
+ };
+});
+
+
+module.exports = apiDocsPackage;
+
+// Run the dgeni pipeline, generating documentation.
+// TODO(jelbourn): remove this once the process is more final in favor of gulp.
+let dgeni = new Dgeni([apiDocsPackage]);
+dgeni.generate().then(docs => {
+ console.log(docs);
+});
diff --git a/tools/dgeni/processors/categorizer.js b/tools/dgeni/processors/categorizer.js
new file mode 100644
index 000000000000..2a90b33811f2
--- /dev/null
+++ b/tools/dgeni/processors/categorizer.js
@@ -0,0 +1,112 @@
+/**
+ * Processor to add properties to docs objects.
+ *
+ * isMethod | Whether the doc is for a method on a class.
+ * isDirective | Whether the doc is for a @Component or a @Directive
+ * isService | Whether the doc is for an @Injectable
+ * isNgModule | Whether the doc is for an NgModule
+ */
+module.exports = function categorizer() {
+ return {
+ $runBefore: ['docs-processed'],
+ $process: function(docs) {
+ docs.forEach(doc => {
+ // The typescriptPackage groups both methods and parameters into "members".
+ // Use the presence of `parameters` as a proxy to determine if this is a method.
+ if (doc.classDoc && doc.hasOwnProperty('parameters')) {
+ doc.isMethod = true;
+
+ // Mark methods with a `void` return type so we can omit show the return type in the docs.
+ doc.showReturns = doc.returnType && doc.returnType != 'void';
+
+ normalizeMethodParameters(doc);
+
+ // Maintain a list of methods on the associated class so we can
+ // iterate through them while rendering.
+ doc.classDoc.methods ?
+ doc.classDoc.methods.push(doc) :
+ doc.classDoc.methods = [doc];
+ } else if (isDirective(doc)) {
+ doc.isDirective = true;
+ } else if (isService(doc)) {
+ doc.isService = true;
+ } else if (isNgModule(doc)) {
+ doc.isNgModule = true;
+ } else if (doc.docType == 'member') {
+ doc.isDirectiveInput = isDirectiveInput(doc);
+ doc.isDirectiveOutput = isDirectiveOutput(doc);
+
+ doc.classDoc.properties ?
+ doc.classDoc.properties.push(doc) :
+ doc.classDoc.properties = [doc];
+ }
+ });
+ }
+ };
+};
+
+
+/**
+ * The `parameters` property are the parameters extracted from TypeScript and are strings
+ * of the form "propertyName: propertyType" (literally what's written in the source).
+ *
+ * The `params` property is pulled from the `@param` JsDoc tag. We need to merge
+ * the information of these to get name + type + description.
+ *
+ * We will use the `params` property to store the final normalized form since it is already
+ * an object.
+ */
+function normalizeMethodParameters(method) {
+ if (method.parameters) {
+ method.parameters.forEach(parameter => {
+ let [parameterName, parameterType] = parameter.split(':');
+
+ if (!method.params) {
+ method.params = [];
+ }
+
+ let jsDocParam = method.params.find(p => p.name == parameterName);
+
+ if (!jsDocParam) {
+ jsDocParam = {name: parameterName};
+ method.params.push(jsDocParam);
+ }
+
+ jsDocParam.type = parameterType.trim();
+ });
+ }
+}
+
+function isDirective(doc) {
+ return hasClassDecorator(doc, 'Component') || hasClassDecorator(doc, 'Directive');
+}
+
+function isService(doc) {
+ return hasClassDecorator(doc, 'Injectable')
+}
+
+function isNgModule(doc) {
+ return hasClassDecorator(doc, 'NgModule');
+}
+
+function isDirectiveOutput(doc) {
+ return hasMemberDecorator(doc, 'Output');
+}
+
+function isDirectiveInput(doc) {
+ return hasMemberDecorator(doc, 'Input');
+}
+
+function hasMemberDecorator(doc, decoratorName) {
+ return doc.docType == 'member' && hasDecorator(doc, decoratorName);
+}
+
+function hasClassDecorator(doc, decoratorName) {
+ return doc.docType == 'class' && hasDecorator(doc, decoratorName);
+}
+
+function hasDecorator(doc, decoratorName) {
+ return doc.decorators &&
+ doc.decorators.length &&
+ doc.decorators.some(d => d.name == decoratorName);
+}
diff --git a/tools/dgeni/processors/component-grouper.js b/tools/dgeni/processors/component-grouper.js
new file mode 100644
index 000000000000..8bab3d52506c
--- /dev/null
+++ b/tools/dgeni/processors/component-grouper.js
@@ -0,0 +1,60 @@
+const path = require('path');
+
+/**
+ * Processor to group docs into top-level "Components" WRT material design, e.g., "Button", "Tabs",
+ * where each group may conists of several directives and services.
+ */
+
+
+/** Component group data structure. */
+class ComponentGroup {
+ constructor(name) {
+ this.name = name;
+ this.id = `component-group-${name}`;
+ this.docType = 'componentGroup';
+ this.directives = [];
+ this.services = [];
+ this.ngModule = null;
+ }
+}
+
+module.exports = function componentGrouper() {
+ return {
+ $runBefore: ['docs-processed'],
+ $process: function(docs) {
+ // Map of group name to group instance.
+ let groups = new Map();
+
+ docs.forEach(doc => {
+ // Full path to the file for this doc.
+ let basePath = doc.fileInfo.basePath;
+ let filePath = doc.fileInfo.filePath;
+
+ // All of the component documentation is under `src/lib`, which will be the basePath.
+ // We group the docs up by the directory immediately under `src/lib` (e.g., "button").
+ let groupName = path.relative(basePath, filePath).split(path.sep)[0];
+
+ // Get the group for this doc, or, if one does not exist, create it.
+ let group;
+ if (groups.has(groupName)) {
+ group = groups.get(groupName);
+ } else {
+ group = new ComponentGroup(groupName);
+ groups.set(groupName, group);
+ }
+
+ // Put this doc into the appropriate list in this group.
+ if (doc.isDirective) {
+ group.directives.push(doc);
+ } else if (doc.isService) {
+ group.services.push(doc);
+ } else if (doc.isNgModule) {
+ group.ngModule = doc;
+ }
+ });
+
+ return Array.from(groups.values());
+ }
+ };
+};
+
diff --git a/tools/dgeni/processors/docs-private-filter.js b/tools/dgeni/processors/docs-private-filter.js
new file mode 100644
index 000000000000..7507e58c1c1b
--- /dev/null
+++ b/tools/dgeni/processors/docs-private-filter.js
@@ -0,0 +1,41 @@
+/**
+ * Processor to filter out symbols that should not be shown in the Material docs.
+ */
+
+const INTERNAL_METHODS = [
+ // Lifecycle methods
+ 'ngOnInit',
+ 'ngOnChanges',
+ 'ngDoCheck',
+ 'ngAfterContentInit',
+ 'ngAfterContentChecked',
+ 'ngAfterViewInit',
+ 'ngAfterViewChecked',
+ 'ngOnDestroy',
+
+ // ControlValueAccessor methods
+ 'writeValue',
+ 'registerOnChange',
+ 'registerOnTouched',
+ 'setDisabledState',
+
+ // Don't ever need to document constructors
+ 'constructor',
+
+ // tabIndex exists on all elements, no need to document it
+ 'tabIndex',
+];
+
+module.exports = function docsPrivateFilter() {
+ return {
+ $runBefore: ['docs-processed'],
+ $process: function(docs) {
+ return docs.filter(d => !(hasDocsPrivateTag(d) || INTERNAL_METHODS.includes(d.name)));
+ }
+ };
+};
+
+function hasDocsPrivateTag(doc) {
+ let tags = doc.tags && doc.tags.tags;
+ return tags ? tags.find(d => d.tagName == 'docs-private') : false;
+}
diff --git a/tools/dgeni/templates/common.template.html b/tools/dgeni/templates/common.template.html
new file mode 100644
index 000000000000..2ca6024237ca
--- /dev/null
+++ b/tools/dgeni/templates/common.template.html
@@ -0,0 +1 @@
+## {$ doc.name $} ({$ doc.docType $})
\ No newline at end of file
diff --git a/tools/dgeni/templates/componentGroup.template.html b/tools/dgeni/templates/componentGroup.template.html
new file mode 100644
index 000000000000..e39cdf5bd587
--- /dev/null
+++ b/tools/dgeni/templates/componentGroup.template.html
@@ -0,0 +1,91 @@
+
+
+{$ doc.name $}
+ Module: {$ doc.ngModule.name $}
+
+Directives
+
+{% for directive in doc.directives %}
+ {$ directive.name $}
+ {$ directive.description $}
+
+ {%- if directive.properties.length -%}
+ Properties
+
+
+ Name
+ Description
+
+ {% for property in directive.properties %}
+
+
+ {%- if property.isDirectiveInput -%}
+ @Input
+ {%- endif -%}
+
+ {$ property.name $}
+ {$ property.type $}
+
+ {$ property.description $}
+
+ {% endfor %}
+
+ {%- endif -%}
+
+ {%- if directive.methods.length -%}
+ Methods
+ {% for method in directive.methods %}
+
+
+ {$ method.name $}
+
+
+ {$ method.description $}
+
+
+ {%- if method.params.length -%}
+
+ Parameters
+
+ {% for parameter in method.params %}
+
+
+ {$ parameter.name $}
+ {$ parameter.type $}
+
+
+ {$ parameter.description $}
+
+
+ {% endfor %}
+ {%- endif -%}
+
+ {%- if method.showReturns -%}
+
+ Returns
+
+
+ {$ method.returnType $}
+ {$ method.returns.description $}
+
+ {%- endif -%}
+
+ {% endfor %}
+ {%- endif -%}
+
+{% endfor %}
+
+
+
+
diff --git a/tools/gulp/tasks/docs.ts b/tools/gulp/tasks/docs.ts
index 6c73abb774c5..e368809585e8 100644
--- a/tools/gulp/tasks/docs.ts
+++ b/tools/gulp/tasks/docs.ts
@@ -26,4 +26,9 @@ gulp.task('docs', () => {
.pipe(gulp.dest('dist/docs'));
});
-task('api', execNodeTask('typedoc', ['--options', typedocPath, './src/lib']));
+task('api', () => {
+ const Dgeni = require('dgeni');
+ const docsPackage = require(path.resolve(__dirname, '../../dgeni'));
+ const dgeni = new Dgeni([docsPackage]);
+ return dgeni.generate();
+});
From b2df2a2a5fe46c39a22d3e1da342d07fef5113a3 Mon Sep 17 00:00:00 2001
From: mmalerba
Date: Fri, 16 Dec 2016 15:56:01 -0800
Subject: [PATCH 47/91] chore(input): improve error when md-input-container has
no md-input (#2253)
* fix cryptic error when md-input-container has no md-input
* fix typo
---
src/lib/input/input-container-errors.ts | 8 +++++
src/lib/input/input-container.spec.ts | 46 ++++++++++++++++++++-----
src/lib/input/input-container.ts | 7 +++-
3 files changed, 51 insertions(+), 10 deletions(-)
diff --git a/src/lib/input/input-container-errors.ts b/src/lib/input/input-container-errors.ts
index 214997f60f64..55485ac1b14e 100644
--- a/src/lib/input/input-container-errors.ts
+++ b/src/lib/input/input-container-errors.ts
@@ -20,3 +20,11 @@ export class MdInputContainerDuplicatedHintError extends MdError {
super(`A hint was already declared for 'align="${align}"'.`);
}
}
+
+
+export class MdInputContainerMissingMdInputError extends MdError {
+ constructor() {
+ super('md-input-container must contain an md-input directive. Did you forget to add md-input ' +
+ 'to the native input or textarea element?');
+ }
+}
diff --git a/src/lib/input/input-container.spec.ts b/src/lib/input/input-container.spec.ts
index c0c2bd1985c6..defb792f5a9f 100644
--- a/src/lib/input/input-container.spec.ts
+++ b/src/lib/input/input-container.spec.ts
@@ -6,6 +6,11 @@ import {MdInputModule} from './input';
import {MdInputContainer} from './input-container';
import {MdPlatform} from '../core/platform/platform';
import {PlatformModule} from '../core/platform/index';
+import {
+ MdInputContainerMissingMdInputError,
+ MdInputContainerPlaceholderConflictError,
+ MdInputContainerDuplicatedHintError
+} from './input-container-errors';
describe('MdInputContainer', function () {
@@ -35,6 +40,7 @@ describe('MdInputContainer', function () {
MdInputContainerNumberTestController,
MdTextareaWithBindings,
MdInputContainerWithDisabled,
+ MdInputContainerMissingMdInputTestController
],
});
@@ -150,25 +156,29 @@ describe('MdInputContainer', function () {
it('validates there\'s only one hint label per side', () => {
let fixture = TestBed.createComponent(MdInputContainerInvalidHintTestController);
- expect(() => fixture.detectChanges()).toThrow();
- // TODO(jelbourn): .toThrow(new MdInputContainerDuplicatedHintError('start'));
- // See https://github.com/angular/angular/issues/8348
+ expect(() => fixture.detectChanges()).toThrowError(
+ angularWrappedErrorMessage(new MdInputContainerDuplicatedHintError('start')));
});
it('validates there\'s only one hint label per side (attribute)', () => {
let fixture = TestBed.createComponent(MdInputContainerInvalidHint2TestController);
- expect(() => fixture.detectChanges()).toThrow();
- // TODO(jelbourn): .toThrow(new MdInputContainerDuplicatedHintError('start'));
- // See https://github.com/angular/angular/issues/8348
+ expect(() => fixture.detectChanges()).toThrowError(
+ angularWrappedErrorMessage(new MdInputContainerDuplicatedHintError('start')));
});
it('validates there\'s only one placeholder', () => {
let fixture = TestBed.createComponent(MdInputContainerInvalidPlaceholderTestController);
- expect(() => fixture.detectChanges()).toThrow();
- // TODO(jelbourn): .toThrow(new MdInputContainerPlaceholderConflictError());
- // See https://github.com/angular/angular/issues/8348
+ expect(() => fixture.detectChanges()).toThrowError(
+ angularWrappedErrorMessage(new MdInputContainerPlaceholderConflictError()));
+ });
+
+ it('validates that md-input child is present', () => {
+ let fixture = TestBed.createComponent(MdInputContainerMissingMdInputTestController);
+
+ expect(() => fixture.detectChanges()).toThrowError(
+ angularWrappedErrorMessage(new MdInputContainerMissingMdInputError()));
});
it('validates the type', () => {
@@ -406,3 +416,21 @@ class MdTextareaWithBindings {
cols: number = 8;
wrap: string = 'hard';
}
+
+@Component({
+ template: ` `
+})
+class MdInputContainerMissingMdInputTestController {}
+
+/**
+ * Gets a RegExp used to detect an angular wrapped error message.
+ * See https://github.com/angular/angular/issues/8348
+ */
+const angularWrappedErrorMessage = (e: Error) =>
+ new RegExp(`.*caused by: ${regexpEscape(e.message)}$`);
+
+/**
+ * Escape a string for use inside a RegExp.
+ * Based on https://github.com/sindresorhus/escape-string-regex
+ */
+const regexpEscape = (s: string) => s.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&');
diff --git a/src/lib/input/input-container.ts b/src/lib/input/input-container.ts
index 4fedf96e7cd7..241a6738633e 100644
--- a/src/lib/input/input-container.ts
+++ b/src/lib/input/input-container.ts
@@ -19,7 +19,8 @@ import {getSupportedInputTypes} from '../core/platform/features';
import {
MdInputContainerUnsupportedTypeError,
MdInputContainerPlaceholderConflictError,
- MdInputContainerDuplicatedHintError
+ MdInputContainerDuplicatedHintError,
+ MdInputContainerMissingMdInputError
} from './input-container-errors';
@@ -218,6 +219,10 @@ export class MdInputContainer implements AfterContentInit {
@ContentChildren(MdHint) _hintChildren: QueryList;
ngAfterContentInit() {
+ if (!this._mdInputChild) {
+ throw new MdInputContainerMissingMdInputError();
+ }
+
this._validateHints();
this._validatePlaceholders();
From 34642ea4ef50beb1b75df430f3f05503e0bb7ce7 Mon Sep 17 00:00:00 2001
From: Jeremy Elbourn
Date: Fri, 16 Dec 2016 15:56:14 -0800
Subject: [PATCH 48/91] docs: fill out jsdoc for button, checkbox, and radio
(#2252)
* docs: fill out jsdoc for button, checkbox, and radio
* Fix bad rename, re-add isAriaDisabled
---
src/lib/button-toggle/button-toggle.ts | 3 +-
src/lib/button/button.html | 6 +--
src/lib/button/button.ts | 26 +++++------
src/lib/checkbox/checkbox.html | 2 +-
src/lib/checkbox/checkbox.ts | 62 +++++++++++---------------
src/lib/radio/radio.html | 4 +-
src/lib/radio/radio.ts | 61 ++++++++++++++++---------
7 files changed, 86 insertions(+), 78 deletions(-)
diff --git a/src/lib/button-toggle/button-toggle.ts b/src/lib/button-toggle/button-toggle.ts
index a88b510f2e44..b74de147c58f 100644
--- a/src/lib/button-toggle/button-toggle.ts
+++ b/src/lib/button-toggle/button-toggle.ts
@@ -34,6 +34,7 @@ export type ToggleType = 'checkbox' | 'radio';
/**
* Provider Expression that allows md-button-toggle-group to register as a ControlValueAccessor.
* This allows it to support [(ngModel)].
+ * @docs-private
*/
export const MD_BUTTON_TOGGLE_GROUP_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
@@ -43,7 +44,7 @@ export const MD_BUTTON_TOGGLE_GROUP_VALUE_ACCESSOR: any = {
var _uniqueIdCounter = 0;
-/** A simple change event emitted by either MdButtonToggle or MdButtonToggleGroup. */
+/** Change event object emitted by MdButtonToggle. */
export class MdButtonToggleChange {
source: MdButtonToggle;
value: any;
diff --git a/src/lib/button/button.html b/src/lib/button/button.html
index a82eb530bbfd..281f5f5101e1 100644
--- a/src/lib/button/button.html
+++ b/src/lib/button/button.html
@@ -1,8 +1,8 @@
diff --git a/src/lib/button/button.ts b/src/lib/button/button.ts
index 890ea7df3565..d3129c2752d0 100644
--- a/src/lib/button/button.ts
+++ b/src/lib/button/button.ts
@@ -47,24 +47,22 @@ export class MdButton {
private _disableRipple: boolean = false;
private _disabled: boolean = null;
+ /** Whether the ripple effect for this button is disabled. */
@Input()
get disableRipple() { return this._disableRipple; }
set disableRipple(v) { this._disableRipple = coerceBooleanProperty(v); }
+ /** Whether the button is disabled. */
@Input()
get disabled() { return this._disabled; }
set disabled(value: boolean) { this._disabled = coerceBooleanProperty(value) ? true : null; }
constructor(private _elementRef: ElementRef, private _renderer: Renderer) { }
+ /** The color of the button. Can be `primary`, `accent`, or `warn`. */
@Input()
- get color(): string {
- return this._color;
- }
-
- set color(value: string) {
- this._updateColor(value);
- }
+ get color(): string { return this._color; }
+ set color(value: string) { this._updateColor(value); }
_setMousedown() {
// We only *show* the focus style when focus has come to the button via the keyboard.
@@ -95,16 +93,16 @@ export class MdButton {
this._isKeyboardFocused = false;
}
- /** TODO(hansl): e2e test this function. */
- focus() {
+ /** Focuses the button. */
+ focus(): void {
this._renderer.invokeElementMethod(this._elementRef.nativeElement, 'focus');
}
- getHostElement() {
+ _getHostElement() {
return this._elementRef.nativeElement;
}
- isRoundButton() {
+ _isRoundButton() {
const el = this._elementRef.nativeElement;
return el.hasAttribute('md-icon-button') ||
el.hasAttribute('md-fab') ||
@@ -122,6 +120,7 @@ export class MdButton {
inputs: ['color', 'disabled', 'disableRipple'],
host: {
'[attr.disabled]': 'disabled',
+ '[attr.aria-disabled]': '_isAriaDisabled',
'[class.md-button-focus]': '_isKeyboardFocused',
'(mousedown)': '_setMousedown()',
'(focus)': '_setKeyboardFocus()',
@@ -137,14 +136,13 @@ export class MdAnchor extends MdButton {
super(elementRef, renderer);
}
+ /** @docs-private */
@HostBinding('tabIndex')
get tabIndex(): number {
return this.disabled ? -1 : 0;
}
- /** Gets the aria-disabled value for the component, which must be a string for Dart. */
- @HostBinding('attr.aria-disabled')
- get isAriaDisabled(): string {
+ get _isAriaDisabled(): string {
return this.disabled ? 'true' : 'false';
}
diff --git a/src/lib/checkbox/checkbox.html b/src/lib/checkbox/checkbox.html
index fba084ce10a5..b67f3aae244a 100644
--- a/src/lib/checkbox/checkbox.html
+++ b/src/lib/checkbox/checkbox.html
@@ -16,7 +16,7 @@
(change)="_onInteractionEvent($event)"
(click)="_onInputClick($event)">
diff --git a/src/lib/checkbox/checkbox.ts b/src/lib/checkbox/checkbox.ts
index 89d8d01b36ac..ad1332117327 100644
--- a/src/lib/checkbox/checkbox.ts
+++ b/src/lib/checkbox/checkbox.ts
@@ -20,14 +20,13 @@ import {MdRippleModule, DefaultStyleCompatibilityModeModule} from '../core';
import {ViewportRuler} from '../core/overlay/position/viewport-ruler';
-/**
- * Monotonically increasing integer used to auto-generate unique ids for checkbox components.
- */
+/** Monotonically increasing integer used to auto-generate unique ids for checkbox components. */
let nextId = 0;
/**
- * Provider Expression that allows md-checkbox to register as a ControlValueAccessor. This allows it
- * to support [(ngModel)].
+ * Provider Expression that allows md-checkbox to register as a ControlValueAccessor.
+ * This allows it to support [(ngModel)].
+ * @docs-private
*/
export const MD_CHECKBOX_CONTROL_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
@@ -37,6 +36,7 @@ export const MD_CHECKBOX_CONTROL_VALUE_ACCESSOR: any = {
/**
* Represents the different states that require custom transitions between them.
+ * @docs-private
*/
export enum TransitionCheckState {
/** The initial state of the component before any user interaction. */
@@ -49,7 +49,7 @@ export enum TransitionCheckState {
Indeterminate
}
-// A simple change event emitted by the MdCheckbox component.
+/** Change event object emitted by MdCheckbox. */
export class MdCheckboxChange {
source: MdCheckbox;
checked: boolean;
@@ -73,7 +73,7 @@ export class MdCheckboxChange {
'[class.md-checkbox-checked]': 'checked',
'[class.md-checkbox-disabled]': 'disabled',
'[class.md-checkbox-align-end]': 'align == "end"',
- '[class.md-checkbox-focused]': 'hasFocus',
+ '[class.md-checkbox-focused]': '_hasFocus',
},
providers: [MD_CHECKBOX_CONTROL_VALUE_ACCESSOR],
encapsulation: ViewEncapsulation.None,
@@ -97,18 +97,19 @@ export class MdCheckbox implements ControlValueAccessor {
/** Whether the ripple effect on click should be disabled. */
private _disableRipple: boolean;
+ /** Whether the ripple effect for this checkbox is disabled. */
@Input()
get disableRipple(): boolean { return this._disableRipple; }
set disableRipple(value) { this._disableRipple = coerceBooleanProperty(value); }
- /** ID to be applied to the `input` element */
+ /** ID of the native input element inside `` */
get inputId(): string {
return `input-${this.id}`;
}
private _required: boolean;
- /** Whether the checkbox is required or not. */
+ /** Whether the checkbox is required. */
@Input()
get required(): boolean { return this._required; }
set required(value) { this._required = coerceBooleanProperty(value); }
@@ -118,18 +119,12 @@ export class MdCheckbox implements ControlValueAccessor {
private _disabled: boolean = false;
- /**
- * Whether the checkbox is disabled. When the checkbox is disabled it cannot be interacted with.
- * The correct ARIA attributes are applied to denote this to assistive technology.
- */
+ /** Whether the checkbox is disabled. */
@Input()
get disabled(): boolean { return this._disabled; }
set disabled(value) { this._disabled = coerceBooleanProperty(value); }
- /**
- * The tabindex attribute for the checkbox. Note that when the checkbox is disabled, the attribute
- * on the host element will be removed. It will be placed back when the checkbox is re-enabled.
- */
+ /** @docs-private */
@Input() tabindex: number = 0;
/** Name value will be applied to the input element if present */
@@ -141,7 +136,10 @@ export class MdCheckbox implements ControlValueAccessor {
/** The native ` element */
@ViewChild('input') _inputElement: ElementRef;
- /** Called when the checkbox is blurred. Needed to properly implement ControlValueAccessor. */
+ /**
+ * Called when the checkbox is blurred. Needed to properly implement ControlValueAccessor.
+ * @docs-private
+ */
onTouched: () => any = () => {};
private _currentAnimationClass: string = '';
@@ -156,7 +154,7 @@ export class MdCheckbox implements ControlValueAccessor {
private _controlValueAccessorChangeFn: (value: any) => void = (value) => {};
- hasFocus: boolean = false;
+ _hasFocus: boolean = false;
constructor(private _renderer: Renderer,
private _elementRef: ElementRef,
@@ -205,15 +203,10 @@ export class MdCheckbox implements ControlValueAccessor {
}
}
- /** Sets the color of the checkbox */
+ /** The color of the button. Can be `primary`, `accent`, or `warn`. */
@Input()
- get color(): string {
- return this._color;
- }
-
- set color(value: string) {
- this._updateColor(value);
- }
+ get color(): string { return this._color; }
+ set color(value: string) { this._updateColor(value); }
_updateColor(newColor: string) {
this._setElementColor(this._color, false);
@@ -283,19 +276,17 @@ export class MdCheckbox implements ControlValueAccessor {
/** Informs the component when the input has focus so that we can style accordingly */
_onInputFocus() {
- this.hasFocus = true;
+ this._hasFocus = true;
}
/** Informs the component when we lose focus in order to style accordingly */
_onInputBlur() {
- this.hasFocus = false;
+ this._hasFocus = false;
this.onTouched();
}
- /**
- * Toggles the `checked` value between true and false
- */
- toggle() {
+ /** Toggles the `checked` state of the checkbox. */
+ toggle(): void {
this.checked = !this.checked;
}
@@ -320,7 +311,8 @@ export class MdCheckbox implements ControlValueAccessor {
}
}
- focus() {
+ /** Focuses the checkbox. */
+ focus(): void {
this._renderer.invokeElementMethod(this._inputElement.nativeElement, 'focus');
this._onInputFocus();
}
@@ -366,7 +358,7 @@ export class MdCheckbox implements ControlValueAccessor {
return `md-checkbox-anim-${animSuffix}`;
}
- getHostElement() {
+ _getHostElement() {
return this._elementRef.nativeElement;
}
}
diff --git a/src/lib/radio/radio.html b/src/lib/radio/radio.html
index 9fdf1080dc68..567896528cc2 100644
--- a/src/lib/radio/radio.html
+++ b/src/lib/radio/radio.html
@@ -6,7 +6,7 @@
@@ -25,7 +25,7 @@
(click)="_onInputClick($event)">
-
+
diff --git a/src/lib/radio/radio.ts b/src/lib/radio/radio.ts
index f455a24f6b3a..b710d7765976 100644
--- a/src/lib/radio/radio.ts
+++ b/src/lib/radio/radio.ts
@@ -32,6 +32,7 @@ import {ViewportRuler} from '../core/overlay/position/viewport-ruler';
/**
* Provider Expression that allows md-radio-group to register as a ControlValueAccessor. This
* allows it to support [(ngModel)] and ngControl.
+ * @docs-private
*/
export const MD_RADIO_GROUP_CONTROL_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
@@ -39,22 +40,17 @@ export const MD_RADIO_GROUP_CONTROL_VALUE_ACCESSOR: any = {
multi: true
};
-// TODO(mtlin):
-// Ink ripple is currently placeholder.
-// Determine motion spec for button transitions.
-// Design review.
-// RTL
-// Support forms API.
-// Use ChangeDetectionStrategy.OnPush
-
var _uniqueIdCounter = 0;
-/** A simple change event emitted by either MdRadioButton or MdRadioGroup. */
+/** Change event object emitted by MdRadio and MdRadioGroup. */
export class MdRadioChange {
source: MdRadioButton;
value: any;
}
+/**
+ * A group of radio buttons. May contain one or more `
` elements.
+ */
@Directive({
selector: 'md-radio-group, mat-radio-group',
providers: [MD_RADIO_GROUP_CONTROL_VALUE_ACCESSOR],
@@ -86,10 +82,17 @@ export class MdRadioGroup implements AfterContentInit, ControlValueAccessor {
/** The method to be called in order to update ngModel */
_controlValueAccessorChangeFn: (value: any) => void = (value) => {};
- /** onTouch function registered via registerOnTouch (ControlValueAccessor). */
+ /**
+ * onTouch function registered via registerOnTouch (ControlValueAccessor).
+ * @docs-private
+ */
onTouched: () => any = () => {};
- /** Event emitted when the group value changes. */
+ /**
+ * Event emitted when the group value changes.
+ * Change events are only emitted when the value changes due to user interaction with
+ * a radio button (the same behavior as ` `).
+ */
@Output()
change: EventEmitter = new EventEmitter();
@@ -97,6 +100,7 @@ export class MdRadioGroup implements AfterContentInit, ControlValueAccessor {
@ContentChildren(forwardRef(() => MdRadioButton))
_radios: QueryList = null;
+ /** Name of the radio button group. All radio buttons inside this group will use this name. */
@Input()
get name(): string {
return this._name;
@@ -107,7 +111,8 @@ export class MdRadioGroup implements AfterContentInit, ControlValueAccessor {
this._updateRadioButtonNames();
}
- @Input() align: 'start' | 'end';
+ /** Alignment of the radio-buttons relative to their labels. Can be 'before' or 'after'. */
+ @Input() align: 'before' | 'after';
@Input()
get disabled(): boolean {
@@ -228,7 +233,9 @@ export class MdRadioGroup implements AfterContentInit, ControlValueAccessor {
}
}
-
+/**
+ * A radio-button. May be inside of
+ */
@Component({
moduleId: module.id,
selector: 'md-radio-button, mat-radio-button',
@@ -271,15 +278,20 @@ export class MdRadioButton implements OnInit {
/** The parent radio group. May or may not be present. */
radioGroup: MdRadioGroup;
+ /** Whether the ripple effect for this radio button is disabled. */
@Input()
get disableRipple(): boolean { return this._disableRipple; }
set disableRipple(value) { this._disableRipple = coerceBooleanProperty(value); }
- /** Event emitted when the group value changes. */
+ /**
+ * Event emitted when the checked state of this radio button changes.
+ * Change events are only emitted when the value changes due to user interaction with
+ * the radio button (the same behavior as ` `).
+ */
@Output()
change: EventEmitter = new EventEmitter();
- /** The native ` element */
+ /** The native ` ` element */
@ViewChild('input') _inputElement: ElementRef;
constructor(@Optional() radioGroup: MdRadioGroup,
@@ -298,10 +310,12 @@ export class MdRadioButton implements OnInit {
});
}
+ /** ID of the native input element inside `` */
get inputId(): string {
return `${this.id}-input`;
}
+ /** Whether this radio button is checked. */
@HostBinding('class.md-radio-checked')
@Input()
get checked(): boolean {
@@ -327,7 +341,7 @@ export class MdRadioButton implements OnInit {
}
}
- /** MdRadioGroup reads this to assign its own value. */
+ /** The value of this radio button. */
@Input()
get value(): any {
return this._value;
@@ -349,17 +363,19 @@ export class MdRadioButton implements OnInit {
}
}
- private _align: 'start' | 'end';
+ private _align: 'before' | 'after';
+ /** Alignment of the radio-button relative to their labels. Can be 'before' or 'after'. */
@Input()
- get align(): 'start' | 'end' {
- return this._align || (this.radioGroup != null && this.radioGroup.align) || 'start';
+ get align(): 'before' | 'after' {
+ return this._align || (this.radioGroup != null && this.radioGroup.align) || 'before';
}
- set align(value: 'start' | 'end') {
+ set align(value: 'before' | 'after') {
this._align = value;
}
+ /** Whether the radio button is disabled. */
@HostBinding('class.md-radio-disabled')
@Input()
get disabled(): boolean {
@@ -401,7 +417,8 @@ export class MdRadioButton implements OnInit {
this._isFocused = true;
}
- focus() {
+ /** Focuses the radio button. */
+ focus(): void {
this._renderer.invokeElementMethod(this._inputElement.nativeElement, 'focus');
this._onInputFocus();
}
@@ -448,7 +465,7 @@ export class MdRadioButton implements OnInit {
}
}
- getHostElement() {
+ _getHostElement() {
return this._elementRef.nativeElement;
}
}
From a8185794f426206d3210ee6878a81bf713599b43 Mon Sep 17 00:00:00 2001
From: mmalerba
Date: Fri, 16 Dec 2016 15:56:38 -0800
Subject: [PATCH 49/91] fix(input): treat number 0 as non-empty (#2245)
---
src/lib/input/input-container.spec.ts | 24 ++++++++++++++++++++++++
src/lib/input/input-container.ts | 2 +-
2 files changed, 25 insertions(+), 1 deletion(-)
diff --git a/src/lib/input/input-container.spec.ts b/src/lib/input/input-container.spec.ts
index defb792f5a9f..df092c0074d2 100644
--- a/src/lib/input/input-container.spec.ts
+++ b/src/lib/input/input-container.spec.ts
@@ -38,6 +38,7 @@ describe('MdInputContainer', function () {
MdInputContainerTextTestController,
MdInputContainerPasswordTestController,
MdInputContainerNumberTestController,
+ MdInputContainerZeroTestController,
MdTextareaWithBindings,
MdInputContainerWithDisabled,
MdInputContainerMissingMdInputTestController
@@ -127,6 +128,19 @@ describe('MdInputContainer', function () {
expect(el.classList.contains('md-empty')).toBe(false, 'should not be empty');
}));
+ it('should not treat the number 0 as empty', async(() => {
+ let fixture = TestBed.createComponent(MdInputContainerZeroTestController);
+ fixture.detectChanges();
+
+ fixture.whenStable().then(() => {
+ fixture.detectChanges();
+
+ let el = fixture.debugElement.query(By.css('label')).nativeElement;
+ expect(el).not.toBeNull();
+ expect(el.classList.contains('md-empty')).toBe(false);
+ });
+ }));
+
it('should add id', () => {
let fixture = TestBed.createComponent(MdInputContainerTextTestController);
fixture.detectChanges();
@@ -405,6 +419,16 @@ class MdInputContainerPasswordTestController {}
})
class MdInputContainerNumberTestController {}
+@Component({
+ template: `
+
+
+ `
+})
+class MdInputContainerZeroTestController {
+ value = 0;
+}
+
@Component({
template: `
diff --git a/src/lib/input/input-container.ts b/src/lib/input/input-container.ts
index 241a6738633e..4402aeb00e9b 100644
--- a/src/lib/input/input-container.ts
+++ b/src/lib/input/input-container.ts
@@ -118,7 +118,7 @@ export class MdInputDirective implements AfterContentInit {
*/
@Output() _placeholderChange = new EventEmitter();
- get empty() { return (this.value == null || this.value == '') && !this._isNeverEmpty(); }
+ get empty() { return (this.value == null || this.value === '') && !this._isNeverEmpty(); }
focused = false;
From e43677525dd83355c4115367121b296d39ec271d Mon Sep 17 00:00:00 2001
From: Tim van Dalen
Date: Sat, 17 Dec 2016 01:02:28 +0100
Subject: [PATCH 50/91] fix(platform): Remove assumption of `window' in
MdPlatform (#2221)
Makes it work when loaded in node
---
src/lib/core/platform/platform.ts | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
mode change 100644 => 100755 src/lib/core/platform/platform.ts
diff --git a/src/lib/core/platform/platform.ts b/src/lib/core/platform/platform.ts
old mode 100644
new mode 100755
index ade29058e1de..7e90e3cccf7e
--- a/src/lib/core/platform/platform.ts
+++ b/src/lib/core/platform/platform.ts
@@ -4,7 +4,9 @@ declare const window: any;
// Whether the current platform supports the V8 Break Iterator. The V8 check
// is necessary to detect all Blink based browsers.
-const hasV8BreakIterator = (window.Intl && (window.Intl as any).v8BreakIterator);
+const hasV8BreakIterator = typeof(window) !== 'undefined' ?
+ (window.Intl && (window.Intl as any).v8BreakIterator) :
+ (typeof(Intl) !== 'undefined' && (Intl as any).v8BreakIterator);
/**
* Service to detect the current platform by comparing the userAgent strings and
From 3637b93a24fbb50ba157f34773d241d69d3b072d Mon Sep 17 00:00:00 2001
From: Kristiyan Kostadinov
Date: Sat, 17 Dec 2016 02:53:38 +0100
Subject: [PATCH 51/91] refactor: convert dash-case inputs to camelCase (#2244)
* refactor: convert dash-case inputs to camelCase
Converts any dash-cased `@Input` properties into camelCase and adds a deprecated proxy property with the old naming. The following properties have been renamed:
* `md-ripple-trigger`
* `md-ripple-centered`
* `md-ripple-disabled`
* `md-ripple-max-radius`
* `md-ripple-speed-factor`
* `md-ripple-color`
* `md-ripple-background-color`
* `md-ripple-focused`
* `md-ripple-unbounded`
* `thumb-label`
* `tick-interval`
* `md-dynamic-height`
* `tooltip-position`
The following properties were skipped:
* `md-menu-trigger-for` - Is currently used also as a selector for the menu trigger.
* Any properties that a native equivalent (e.g. `aria-label`, `aria-labeledby`).
* Fix tabs CSS issue and switch mdDynamicHeight to dynamicHeight.
* Rename [md-tooltip] to [mdTooltip].
* Rename md-menu-trigger-for to mdMenuTriggerFor.
* Use public property for dynamic height binding.
* Proper method name.
* Fix isRoundButton naming.
* make menu-trigger and md-tooltip backwards compatible
---
src/demo-app/menu/menu-demo.html | 8 +--
src/demo-app/ripple/ripple-demo.html | 14 +++---
src/demo-app/slider/slider-demo.html | 14 +++---
src/demo-app/tabs/tabs-demo.html | 6 +--
src/demo-app/tooltip/tooltip-demo.html | 8 +--
src/e2e-app/menu/menu-e2e.html | 10 ++--
src/lib/button/button.html | 6 +--
src/lib/button/button.spec.ts | 2 +-
src/lib/checkbox/checkbox.html | 8 +--
src/lib/checkbox/checkbox.spec.ts | 2 +-
src/lib/core/ripple/README.md | 18 +++----
src/lib/core/ripple/_ripple.scss | 12 ++---
src/lib/core/ripple/ripple.spec.ts | 12 ++---
src/lib/core/ripple/ripple.ts | 70 ++++++++++++++++++++++----
src/lib/grid-list/grid-list.ts | 20 ++------
src/lib/menu/README.md | 50 +++++++++---------
src/lib/menu/menu-errors.ts | 2 +-
src/lib/menu/menu-item.html | 4 +-
src/lib/menu/menu-trigger.ts | 11 ++--
src/lib/menu/menu.spec.ts | 6 +--
src/lib/radio/radio.html | 8 +--
src/lib/radio/radio.spec.ts | 2 +-
src/lib/select/option.html | 4 +-
src/lib/slider/README.md | 24 ++++-----
src/lib/slider/slider.spec.ts | 8 +--
src/lib/slider/slider.ts | 14 +++++-
src/lib/tabs/tab-group.scss | 10 ++--
src/lib/tabs/tab-group.ts | 12 +++--
src/lib/tabs/tab-header.html | 6 +--
src/lib/tooltip/README.md | 8 +--
src/lib/tooltip/tooltip.spec.ts | 4 +-
src/lib/tooltip/tooltip.ts | 20 ++++++--
32 files changed, 238 insertions(+), 165 deletions(-)
diff --git a/src/demo-app/menu/menu-demo.html b/src/demo-app/menu/menu-demo.html
index 423b31d086ec..1d818adf3e7f 100644
--- a/src/demo-app/menu/menu-demo.html
+++ b/src/demo-app/menu/menu-demo.html
@@ -3,7 +3,7 @@
You clicked on: {{ selected }}
-
+
more_vert
@@ -17,7 +17,7 @@
diff --git a/src/demo-app/slider/slider-demo.html b/src/demo-app/slider/slider-demo.html
index aba8619d3c68..160231e67072 100644
--- a/src/demo-app/slider/slider-demo.html
+++ b/src/demo-app/slider/slider-demo.html
@@ -4,7 +4,7 @@ Default Slider
Slider with Min and Max
-
+
{{slider2.value}}
@@ -20,11 +20,11 @@ Slider with step defined
{{slider5.value}}
Slider with set tick interval
-
-
+
+
Slider with Thumb Label
-
+
Slider with one-way binding
@@ -35,13 +35,13 @@ Slider with two-way binding
Inverted slider
-
+
Vertical slider
-
+
Inverted vertical slider
-
+
diff --git a/src/demo-app/tabs/tabs-demo.html b/src/demo-app/tabs/tabs-demo.html
index fb9826927a7f..26f9455ab1a1 100644
--- a/src/demo-app/tabs/tabs-demo.html
+++ b/src/demo-app/tabs/tabs-demo.html
@@ -42,9 +42,7 @@ Tab Group Demo - Dynamic Tabs
-
+
{{tab.label}}
{{tab.content}}
@@ -92,7 +90,7 @@ Tab Group Demo - Dynamic Tabs
Tab Group Demo - Dynamic Height
-
+
{{tab.label}}
{{tab.content}}
diff --git a/src/demo-app/tooltip/tooltip-demo.html b/src/demo-app/tooltip/tooltip-demo.html
index 82858ca89835..13031ea41301 100644
--- a/src/demo-app/tooltip/tooltip-demo.html
+++ b/src/demo-app/tooltip/tooltip-demo.html
@@ -5,10 +5,10 @@ Tooltip Demo
+ [mdTooltip]="message"
+ [mdTooltipPosition]="position"
+ [mdTooltipShowDelay]="showDelay"
+ [mdTooltipHideDelay]="hideDelay">
Mouse over to see the tooltip
diff --git a/src/e2e-app/menu/menu-e2e.html b/src/e2e-app/menu/menu-e2e.html
index ad1c2f86c817..d9c8040161d8 100644
--- a/src/e2e-app/menu/menu-e2e.html
+++ b/src/e2e-app/menu/menu-e2e.html
@@ -2,8 +2,8 @@
{{ selected }}
START
-
TRIGGER
-
TRIGGER 2
+
TRIGGER
+
TRIGGER 2
One
@@ -12,7 +12,7 @@
Four
-
+
BEFORE
@@ -20,12 +20,12 @@
-
ABOVE
+
ABOVE
Item
-
+
BOTH
diff --git a/src/lib/button/button.html b/src/lib/button/button.html
index 281f5f5101e1..a182e2ebc366 100644
--- a/src/lib/button/button.html
+++ b/src/lib/button/button.html
@@ -1,8 +1,8 @@
+ [mdRippleTrigger]="_getHostElement()"
+ [mdRippleColor]="_isRoundButton() ? 'rgba(255, 255, 255, 0.2)' : ''"
+ mdRippleBackgroundColor="rgba(0, 0, 0, 0)">
diff --git a/src/lib/button/button.spec.ts b/src/lib/button/button.spec.ts
index a0556c2f3781..b0bfaf3c7697 100644
--- a/src/lib/button/button.spec.ts
+++ b/src/lib/button/button.spec.ts
@@ -168,7 +168,7 @@ describe('MdButton', () => {
anchorElement = fixture.nativeElement.querySelector('a[md-button]');
});
- it('should remove ripple if md-ripple-disabled input is set', () => {
+ it('should remove ripple if mdRippleDisabled input is set', () => {
expect(buttonElement.querySelectorAll('[md-ripple]').length).toBe(1);
testComponent.rippleDisabled = true;
diff --git a/src/lib/checkbox/checkbox.html b/src/lib/checkbox/checkbox.html
index b67f3aae244a..5fcbe07bd9cf 100644
--- a/src/lib/checkbox/checkbox.html
+++ b/src/lib/checkbox/checkbox.html
@@ -16,10 +16,10 @@
(change)="_onInteractionEvent($event)"
(click)="_onInputClick($event)">
+ [mdRippleTrigger]="_getHostElement()"
+ [mdRippleCentered]="true"
+ [mdRippleSpeedFactor]="0.3"
+ mdRippleBackgroundColor="rgba(0, 0, 0, 0)">
{
expect(inputElement.tabIndex).toBe(13);
});
- it('should remove ripple if md-ripple-disabled input is set', async(() => {
+ it('should remove ripple if mdRippleDisabled input is set', async(() => {
testComponent.disableRipple = true;
fixture.detectChanges();
diff --git a/src/lib/core/ripple/README.md b/src/lib/core/ripple/README.md
index 48f6dd628268..1f964a9b252b 100644
--- a/src/lib/core/ripple/README.md
+++ b/src/lib/core/ripple/README.md
@@ -1,6 +1,6 @@
# md-ripple
-`md-ripple` defines an area in which a ripple animates, usually in response to user action. It is used as an attribute directive, for example `...
`.
+`md-ripple` defines an area in which a ripple animates, usually in response to user action. It is used as an attribute directive, for example `...
`.
By default, a ripple is activated when the host element of the `md-ripple` directive receives mouse or touch events. On a mousedown or touch start, the ripple background fades in. When the click event completes, a circular foreground ripple fades in and expands from the event location to cover the host element bounds.
@@ -17,11 +17,11 @@ Properties:
| Name | Type | Description |
| --- | --- | --- |
-| `md-ripple-trigger` | Element | The DOM element that triggers the ripple when clicked. Defaults to the parent of the `md-ripple`.
-| `md-ripple-color` | string | Custom color for foreground ripples
-| `md-ripple-background-color` | string | Custom color for the ripple background
-| `md-ripple-centered` | boolean | If true, the ripple animation originates from the center of the `md-ripple` bounds rather than from the location of the click event.
-| `md-ripple-max-radius` | number | Optional fixed radius of foreground ripples when fully expanded. Mainly used in conjunction with `unbounded` attribute. If not set, ripples will expand from their origin to the most distant corner of the component's bounding rectangle.
-| `md-ripple-unbounded` | boolean | If true, foreground ripples will be visible outside the component's bounds.
-| `md-ripple-focused` | boolean | If true, the background ripple is shown using the current theme's accent color to indicate focus.
-| `md-ripple-disabled` | boolean | If true, click events on the trigger element will not activate ripples. The `start` and `end` methods can still be called to programmatically create ripples.
+| `mdRippleTrigger` | Element | The DOM element that triggers the ripple when clicked. Defaults to the parent of the `md-ripple`.
+| `mdRippleColor` | string | Custom color for foreground ripples
+| `mdRippleBackgroundColor` | string | Custom color for the ripple background
+| `mdRippleCentered` | boolean | If true, the ripple animation originates from the center of the `md-ripple` bounds rather than from the location of the click event.
+| `mdRippleMaxRadius` | number | Optional fixed radius of foreground ripples when fully expanded. Mainly used in conjunction with `unbounded` attribute. If not set, ripples will expand from their origin to the most distant corner of the component's bounding rectangle.
+| `mdRippleUnbounded` | boolean | If true, foreground ripples will be visible outside the component's bounds.
+| `mdRippleFocused` | boolean | If true, the background ripple is shown using the current theme's accent color to indicate focus.
+| `mdRippleDisabled` | boolean | If true, click events on the trigger element will not activate ripples. The `start` and `end` methods can still be called to programmatically create ripples.
diff --git a/src/lib/core/ripple/_ripple.scss b/src/lib/core/ripple/_ripple.scss
index d5f520d222b9..4f4c77d078c0 100644
--- a/src/lib/core/ripple/_ripple.scss
+++ b/src/lib/core/ripple/_ripple.scss
@@ -1,7 +1,7 @@
@import '../theming/theming';
-$md-ripple-focused-opacity: 0.1;
+$mdRippleFocused-opacity: 0.1;
$md-ripple-background-fade-duration: 300ms;
$md-ripple-background-default-color: rgba(0, 0, 0, 0.0588);
$md-ripple-foreground-initial-opacity: 0.25;
@@ -15,7 +15,7 @@ $md-ripple-foreground-default-color: rgba(0, 0, 0, 0.0588);
overflow: hidden;
}
- [md-ripple].md-ripple-unbounded {
+ [md-ripple].mdRippleUnbounded {
overflow: visible;
}
@@ -30,7 +30,7 @@ $md-ripple-foreground-default-color: rgba(0, 0, 0, 0.0588);
bottom: 0;
}
- .md-ripple-unbounded .md-ripple-background {
+ .mdRippleUnbounded .md-ripple-background {
display: none;
}
@@ -38,7 +38,7 @@ $md-ripple-foreground-default-color: rgba(0, 0, 0, 0.0588);
opacity: 1;
}
- .md-ripple-focused .md-ripple-background {
+ .mdRippleFocused .md-ripple-background {
opacity: 1;
}
@@ -64,8 +64,8 @@ $md-ripple-foreground-default-color: rgba(0, 0, 0, 0.0588);
@mixin md-ripple-theme($theme) {
$accent: map-get($theme, accent);
- .md-ripple-focused .md-ripple-background {
- background-color: md-color($accent, $md-ripple-focused-opacity);
+ .mdRippleFocused .md-ripple-background {
+ background-color: md-color($accent, $mdRippleFocused-opacity);
}
}
diff --git a/src/lib/core/ripple/ripple.spec.ts b/src/lib/core/ripple/ripple.spec.ts
index 91b86f436bf9..df174107ebbf 100644
--- a/src/lib/core/ripple/ripple.spec.ts
+++ b/src/lib/core/ripple/ripple.spec.ts
@@ -438,12 +438,12 @@ class BasicRippleContainer {
template: `
+ [mdRippleTrigger]="trigger"
+ [mdRippleCentered]="centered"
+ [mdRippleMaxRadius]="maxRadius"
+ [mdRippleDisabled]="disabled"
+ [mdRippleColor]="color"
+ [mdRippleBackgroundColor]="backgroundColor">
`,
diff --git a/src/lib/core/ripple/ripple.ts b/src/lib/core/ripple/ripple.ts
index d542f3522c85..f85a4cab4fb9 100644
--- a/src/lib/core/ripple/ripple.ts
+++ b/src/lib/core/ripple/ripple.ts
@@ -30,37 +30,89 @@ export class MdRipple implements OnInit, OnDestroy, OnChanges {
*/
// Prevent TS metadata emit from referencing HTMLElement in ripple.js
// That breaks tests running in node that load material components.
- @Input('md-ripple-trigger') trigger: HTMLElement|HTMLElement;
+ @Input('mdRippleTrigger') trigger: HTMLElement|HTMLElement;
+
+ /** @deprecated */
+ @Input('md-ripple-trigger')
+ get _triggerDeprecated() { return this.trigger; }
+ set _triggerDeprecated(value: HTMLElement|HTMLElement) { this.trigger = value; };
+
/**
* Whether the ripple always originates from the center of the host element's bounds, rather
* than originating from the location of the click event.
*/
- @Input('md-ripple-centered') centered: boolean;
+ @Input('mdRippleCentered') centered: boolean;
+
+ /** @deprecated */
+ @Input('md-ripple-centered')
+ get _centeredDeprecated() { return this.centered; }
+ set _centeredDeprecated(value: boolean) { this.centered = value; };
+
/**
* Whether click events will not trigger the ripple. It can still be triggered by manually
* calling start() and end().
*/
- @Input('md-ripple-disabled') disabled: boolean;
+ @Input('mdRippleDisabled') disabled: boolean;
+
+ /** @deprecated */
+ @Input('md-ripple-disabled')
+ get _disabledDeprecated() { return this.disabled; }
+ set _disabledDeprecated(value: boolean) { this.disabled = value; };
+
/**
* If set, the radius in pixels of foreground ripples when fully expanded. If unset, the radius
* will be the distance from the center of the ripple to the furthest corner of the host element's
* bounding rectangle.
*/
- @Input('md-ripple-max-radius') maxRadius: number = 0;
+ @Input('mdRippleMaxRadius') maxRadius: number = 0;
+
+ /** @deprecated */
+ @Input('md-ripple-max-radius')
+ get _maxRadiusDeprecated() { return this.maxRadius; }
+ set _maxRadiusDeprecated(value: number) { this.maxRadius = value; };
+
/**
* If set, the normal duration of ripple animations is divided by this value. For example,
* setting it to 0.5 will cause the animations to take twice as long.
*/
- @Input('md-ripple-speed-factor') speedFactor: number = 1;
+ @Input('mdRippleSpeedFactor') speedFactor: number = 1;
+
+ /** @deprecated */
+ @Input('md-ripple-speed-factor')
+ get _speedFactorDeprecated() { return this.speedFactor; }
+ set _speedFactorDeprecated(value: number) { this.speedFactor = value; };
+
/** Custom color for ripples. */
- @Input('md-ripple-color') color: string;
+ @Input('mdRippleColor') color: string;
+
+ /** @deprecated */
+ @Input('md-ripple-color')
+ get _colorDeprecated() { return this.color; }
+ set _colorDeprecated(value: string) { this.color = value; };
+
/** Custom color for the ripple background. */
- @Input('md-ripple-background-color') backgroundColor: string;
+ @Input('mdRippleBackgroundColor') backgroundColor: string;
+
+ /** @deprecated */
+ @Input('md-ripple-background-color')
+ get _backgroundColorDeprecated() { return this.backgroundColor; }
+ set _backgroundColorDeprecated(value: string) { this.backgroundColor = value; };
/** Whether the ripple background will be highlighted to indicated a focused state. */
- @HostBinding('class.md-ripple-focused') @Input('md-ripple-focused') focused: boolean;
+ @HostBinding('class.md-ripple-focused') @Input('mdRippleFocused') focused: boolean;
+
+ /** @deprecated */
+ @Input('md-ripple-focused')
+ get _focusedDeprecated(): boolean { return this.focused; }
+ set _focusedDeprecated(value: boolean) { this.focused = value; };
+
/** Whether foreground ripples should be visible outside the component's bounds. */
- @HostBinding('class.md-ripple-unbounded') @Input('md-ripple-unbounded') unbounded: boolean;
+ @HostBinding('class.md-ripple-unbounded') @Input('mdRippleUnbounded') unbounded: boolean;
+
+ /** @deprecated */
+ @Input('md-ripple-unbounded')
+ get _unboundedDeprecated(): boolean { return this.unbounded; }
+ set _unboundedDeprecated(value: boolean) { this.unbounded = value; };
private _rippleRenderer: RippleRenderer;
_ruler: ViewportRuler;
diff --git a/src/lib/grid-list/grid-list.ts b/src/lib/grid-list/grid-list.ts
index 34d6bc9d6f55..b1b0fd5ccff4 100644
--- a/src/lib/grid-list/grid-list.ts
+++ b/src/lib/grid-list/grid-list.ts
@@ -66,22 +66,12 @@ export class MdGridList implements OnInit, AfterContentChecked {
@Optional() private _dir: Dir) {}
@Input()
- get cols() {
- return this._cols;
- }
-
- set cols(value: any) {
- this._cols = coerceToNumber(value);
- }
+ get cols() { return this._cols; }
+ set cols(value: any) { this._cols = coerceToNumber(value); }
- @Input('gutterSize')
- get gutterSize() {
- return this._gutter;
- }
-
- set gutterSize(value: any) {
- this._gutter = coerceToString(value);
- }
+ @Input()
+ get gutterSize() { return this._gutter; }
+ set gutterSize(value: any) { this._gutter = coerceToString(value); }
/** Set internal representation of row height from the user-provided value. */
@Input()
diff --git a/src/lib/menu/README.md b/src/lib/menu/README.md
index b3fe5cef0b6c..f6d8f210395e 100644
--- a/src/lib/menu/README.md
+++ b/src/lib/menu/README.md
@@ -1,6 +1,6 @@
# md-menu
-`md-menu` is a list of options that displays when triggered. You can read more about menus in the
+`md-menu` is a list of options that displays when triggered. You can read more about menus in the
[Material Design spec](https://material.google.com/components/menus.html).
### Not yet implemented
@@ -13,8 +13,8 @@
### Simple menu
-In your template, create an `md-menu` element. You can use either `` or `` tags for
-your menu items, as long as each is tagged with an `md-menu-item` attribute. Note that you can
+In your template, create an `md-menu` element. You can use either `` or `` tags for
+your menu items, as long as each is tagged with an `md-menu-item` attribute. Note that you can
disable items by adding the `disabled` boolean attribute or binding to it.
*my-comp.html*
@@ -28,15 +28,15 @@ disable items by adding the `disabled` boolean attribute or binding to it.
```
-Menus are hidden by default, so you'll want to connect up a menu trigger that can open your menu.
-You can do so by adding a button tag with an `md-menu-trigger-for` attribute and passing in the menu
-instance. You can create a local reference to your menu instance by adding `#menu="mdMenu"` to
+Menus are hidden by default, so you'll want to connect up a menu trigger that can open your menu.
+You can do so by adding a button tag with an `mdMenuTriggerFor` attribute and passing in the menu
+instance. You can create a local reference to your menu instance by adding `#menu="mdMenu"` to
your menu element.
*my-comp.html*
```html
-
+
more_vert
@@ -55,10 +55,10 @@ Output:
### Toggling the menu programmatically
-You can also use the menu's API to open or close the menu programmatically from your class. Please
-note that in this case, an `md-menu-trigger-for` attribute is still necessary to connect
+You can also use the menu's API to open or close the menu programmatically from your class. Please
+note that in this case, an `mdMenuTriggerFor` attribute is still necessary to connect
the menu to its trigger element in the DOM.
-
+
*my-comp.component.ts*
```ts
class MyComp {
@@ -72,7 +72,7 @@ class MyComp {
*my-comp.html*
```html
-
+
more_vert
@@ -91,15 +91,15 @@ Menus also support displaying `md-icon` elements before the menu item text.
*my-comp.html*
```html
-
+
dialpad
Redial
-
+
voicemail
Check voicemail
-
+
notifications_off
Disable alerts
@@ -114,8 +114,8 @@ Output:
### Customizing menu position
-By default, the menu will display after and below its trigger. You can change this display position
-using the `x-position` (`before | after`) and `y-position` (`above | below`) attributes.
+By default, the menu will display after and below its trigger. You can change this display position
+using the `x-position` (`before | after`) and `y-position` (`above | below`) attributes.
*my-comp.html*
```html
@@ -134,7 +134,7 @@ Output:
### Accessibility
-The menu adds `role="menu"` to the main menu element and `role="menuitem"` to each menu item. It
+The menu adds `role="menu"` to the main menu element and `role="menuitem"` to each menu item. It
also adds `aria-hasPopup="true"` to the trigger element.
#### Keyboard events:
@@ -146,18 +146,18 @@ also adds `aria-hasPopup="true"` to the trigger element.
| Signature | Values | Description |
| --- | --- | --- |
-| `x-position` | `before | after` | The horizontal position of the menu in relation to the trigger. Defaults to `after`. |
+| `x-position` | `before | after` | The horizontal position of the menu in relation to the trigger. Defaults to `after`. |
| `y-position` | `above | below` | The vertical position of the menu in relation to the trigger. Defaults to `below`. |
-
+
### Trigger Programmatic API
**Properties**
| Name | Type | Description |
| --- | --- | --- |
-| `menuOpen` | `Boolean` | Property that is true when the menu is open. It is not settable (use methods below). |
-| `onMenuOpen` | `Observable` | Observable that emits when the menu opens. |
-| `onMenuClose` | `Observable` | Observable that emits when the menu closes. |
+| `menuOpen` | `Boolean` | Property that is true when the menu is open. It is not settable (use methods below). |
+| `onMenuOpen` | `Observable` | Observable that emits when the menu opens. |
+| `onMenuClose` | `Observable` | Observable that emits when the menu closes. |
**Methods**
@@ -165,7 +165,7 @@ also adds `aria-hasPopup="true"` to the trigger element.
| --- | --- | --- |
| `openMenu()` | `Promise` | Opens the menu. Returns a promise that will resolve when the menu has opened. |
| `closeMenu()` | `Promise` | Closes the menu. Returns a promise that will resolve when the menu has closed. |
-| `toggleMenu()` | `Promise` | Toggles the menu. Returns a promise that will resolve when the menu has completed opening or closing. |
-| `destroyMenu()` | `Promise` | Destroys the menu overlay completely.
-
+| `toggleMenu()` | `Promise` | Toggles the menu. Returns a promise that will resolve when the menu has completed opening or closing. |
+| `destroyMenu()` | `Promise` | Destroys the menu overlay completely.
+
diff --git a/src/lib/menu/menu-errors.ts b/src/lib/menu/menu-errors.ts
index ae634699a661..069dfabd5333 100644
--- a/src/lib/menu/menu-errors.ts
+++ b/src/lib/menu/menu-errors.ts
@@ -10,7 +10,7 @@ export class MdMenuMissingError extends MdError {
Example:
-
+
`);
}
}
diff --git a/src/lib/menu/menu-item.html b/src/lib/menu/menu-item.html
index 864f582d74c1..6b8fd4445968 100644
--- a/src/lib/menu/menu-item.html
+++ b/src/lib/menu/menu-item.html
@@ -1,4 +1,4 @@
-
{
expect(rippleElement).toBeFalsy('Expected a disabled radio button not to have a ripple');
});
- it('should remove ripple if md-ripple-disabled input is set', async(() => {
+ it('should remove ripple if mdRippleDisabled input is set', async(() => {
fixture.detectChanges();
for (let radioNativeElement of radioNativeElements)
{
diff --git a/src/lib/select/option.html b/src/lib/select/option.html
index 90659d08fbd9..0ced3b05a018 100644
--- a/src/lib/select/option.html
+++ b/src/lib/select/option.html
@@ -1,3 +1,3 @@
-
\ No newline at end of file
+
diff --git a/src/lib/slider/README.md b/src/lib/slider/README.md
index 87e422e90396..781d9d0613a3 100644
--- a/src/lib/slider/README.md
+++ b/src/lib/slider/README.md
@@ -43,40 +43,40 @@ value on bottom.
### Adding a thumb label
By default the exact selected value of a slider is not visible to the user. However, this value can
-be added to the thumb by adding the `thumb-label` attribute.
+be added to the thumb by adding the `thumbLabel` attribute.
The [material design spec](https://material.google.com/components/sliders.html) recommends using the
-`thumb-label` attribute (along with `tick-interval="1"`) only for sliders that are used to display a
+`thumbLabel` attribute (along with `tickInterval="1"`) only for sliders that are used to display a
discrete value (such as a 1-5 rating).
```html
-
+
```
### Adding ticks
By default a sliders do not show tick marks along the thumb track. They can be enabled using the
-`tick-interval` attribute. The value of `tick-interval` should be a number representing the number
-of steps between between ticks. For example a `tick-interval` of `3` with a `step` of `4` will draw
+`tickInterval` attribute. The value of `tickInterval` should be a number representing the number
+of steps between between ticks. For example a `tickInterval` of `3` with a `step` of `4` will draw
tick marks at every `3` steps, which is the same as every `12` values.
```html
-
+
```
-The `tick-interval` can also be set to `auto` which will automatically choose the number of steps
+The `tickInterval` can also be set to `auto` which will automatically choose the number of steps
such that there is at least `30px` of space between ticks.
```html
-
+
```
-
+
The slider will always show a tick at the beginning and end of the track. If the remaining space
doesn't add up perfectly the last interval will be shortened or lengthened so that the tick can be
shown at the end of the track.
The [material design spec](https://material.google.com/components/sliders.html) recommends using the
-`tick-interval` attribute (set to `1` along with the `thumb-label` attribute) only for sliders that
+`tickInterval` attribute (set to `1` along with the `thumbLabel` attribute) only for sliders that
are used to display a discrete value (such as a 1-5 rating).
### Disabling the slider
@@ -91,7 +91,7 @@ value.
### Value binding
`md-slider` supports both 1-way binding and 2-way binding via `ngModel`. It also emits a `change`
-event when the value changes due to user interaction.
+event when the value changes due to user interaction.
```html
@@ -132,7 +132,7 @@ right-to-left languages.
| `max` | number | Optional, the maximum number for the slider. Default = `100`. |
| `step` | number | Optional, declares where the thumb will snap to. Default = `1`. |
| `value` | number | Optional, the value to start the slider at. |
-| `tick-interval` | `"auto"` \| number | Optional, how many steps between tick marks. |
+| `tickInterval` | `"auto"` \| number | Optional, how many steps between tick marks. |
| `invert` | boolean | Optional, whether to invert the axis the thumb moves along. |
| `vertical` | boolean | Optional, whether the slider should be oriented vertically. |
| `disabled` | boolean | Optional, whether or not the slider is disabled. Default = `false`. |
diff --git a/src/lib/slider/slider.spec.ts b/src/lib/slider/slider.spec.ts
index 951562e71017..46367d801331 100644
--- a/src/lib/slider/slider.spec.ts
+++ b/src/lib/slider/slider.spec.ts
@@ -1041,7 +1041,7 @@ class StandardSlider { }
class DisabledSlider { }
@Component({
- template: ` `,
+ template: ` `,
styles: [styles],
})
class SliderWithMinAndMax {
@@ -1062,19 +1062,19 @@ class SliderWithValue { }
class SliderWithStep { }
@Component({
- template: ` `,
+ template: ` `,
styles: [styles],
})
class SliderWithAutoTickInterval { }
@Component({
- template: ` `,
+ template: ` `,
styles: [styles],
})
class SliderWithSetTickInterval { }
@Component({
- template: ` `,
+ template: ` `,
styles: [styles],
})
class SliderWithThumbLabel { }
diff --git a/src/lib/slider/slider.ts b/src/lib/slider/slider.ts
index 5be17ccd817a..f9906be8f78f 100644
--- a/src/lib/slider/slider.ts
+++ b/src/lib/slider/slider.ts
@@ -103,10 +103,15 @@ export class MdSlider implements ControlValueAccessor {
/** Whether or not to show the thumb label. */
private _thumbLabel: boolean = false;
- @Input('thumb-label')
+ @Input('thumbLabel')
get thumbLabel(): boolean { return this._thumbLabel; }
set thumbLabel(value) { this._thumbLabel = coerceBooleanProperty(value); }
+ /** @deprecated */
+ @Input('thumb-label')
+ get _thumbLabelDeprecated(): boolean { return this._thumbLabel; }
+ set _thumbLabelDeprecated(value) { this._thumbLabel = value; }
+
private _controlValueAccessorChangeFn: (value: any) => void = () => {};
/** The last value for which a change event was emitted. */
@@ -140,12 +145,17 @@ export class MdSlider implements ControlValueAccessor {
*/
private _tickInterval: 'auto' | number = 0;
- @Input('tick-interval')
+ @Input()
get tickInterval() { return this._tickInterval; }
set tickInterval(v) {
this._tickInterval = (v == 'auto') ? v : coerceNumberProperty(v, this._tickInterval);
}
+ /** @deprecated */
+ @Input('tick-interval')
+ get _tickIntervalDeprecated() { return this.tickInterval; }
+ set _tickIntervalDeprecated(v) { this.tickInterval = v; }
+
/** The size of a tick interval as a percentage of the size of the track. */
private _tickIntervalPercent: number = 0;
diff --git a/src/lib/tabs/tab-group.scss b/src/lib/tabs/tab-group.scss
index 62a89f2e8f6e..0cc31e4050b6 100644
--- a/src/lib/tabs/tab-group.scss
+++ b/src/lib/tabs/tab-group.scss
@@ -25,7 +25,7 @@
flex-grow: 1;
}
-// The bottom section of the view; contains the tab bodies
+// The bottom section of the view; contains the tab bodies
.md-tab-body-wrapper {
position: relative;
overflow: hidden;
@@ -33,11 +33,12 @@
transition: height $md-tab-animation-duration $ease-in-out-curve-function;
}
-// Wraps each tab body
+// Wraps each tab body
md-tab-body {
@include md-fill;
display: block;
overflow: hidden;
+
&.md-tab-body-active {
position: relative;
overflow-x: hidden;
@@ -45,7 +46,8 @@ md-tab-body {
z-index: 1;
flex-grow: 1;
}
- :host[md-dynamic-height] &.md-tab-body-active {
+
+ :host.md-tab-group-dynamic-height &.md-tab-body-active {
overflow-y: hidden;
}
}
@@ -54,4 +56,4 @@ md-tab-body {
.md-tab-disabled {
cursor: default;
pointer-events: none;
-}
\ No newline at end of file
+}
diff --git a/src/lib/tabs/tab-group.ts b/src/lib/tabs/tab-group.ts
index 0f46849eeb90..91f919b4450d 100644
--- a/src/lib/tabs/tab-group.ts
+++ b/src/lib/tabs/tab-group.ts
@@ -48,6 +48,7 @@ export class MdTabChangeEvent {
selector: 'md-tab-group',
templateUrl: 'tab-group.html',
styleUrls: ['tab-group.css'],
+ host: { '[class.md-tab-group-dynamic-height]': 'dynamicHeight' }
})
export class MdTabGroup {
@ContentChildren(MdTab) _tabs: QueryList;
@@ -65,9 +66,14 @@ export class MdTabGroup {
/** Whether the tab group should grow to the size of the active tab */
private _dynamicHeight: boolean = false;
- @Input('md-dynamic-height') set dynamicHeight(value: boolean) {
- this._dynamicHeight = coerceBooleanProperty(value);
- }
+ @Input()
+ get dynamicHeight(): boolean { return this._dynamicHeight; }
+ set dynamicHeight(value: boolean) { this._dynamicHeight = coerceBooleanProperty(value); }
+
+ /** @deprecated */
+ @Input('md-dynamic-height')
+ get _dynamicHeightDeprecated(): boolean { return this._dynamicHeight; }
+ set _dynamicHeightDeprecated(value: boolean) { this._dynamicHeight = value; }
/** The index of the active tab. */
private _selectedIndex: number = null;
diff --git a/src/lib/tabs/tab-header.html b/src/lib/tabs/tab-header.html
index a73c284021f2..238c98f7d8e1 100644
--- a/src/lib/tabs/tab-header.html
+++ b/src/lib/tabs/tab-header.html
@@ -1,6 +1,6 @@
diff --git a/src/lib/tooltip/README.md b/src/lib/tooltip/README.md
index 65a7857d4d01..7d79fa18d2f4 100644
--- a/src/lib/tooltip/README.md
+++ b/src/lib/tooltip/README.md
@@ -6,18 +6,18 @@ The positions `before` and `after` should be used instead of `left` and `right`
### Examples
A button with a tooltip
```html
-Button
+Button
```
-## `[md-tooltip]`
+## `[mdTooltip]`
### Properties
| Name | Type | Description |
| --- | --- | --- |
-| `md-tooltip` | `string` | The message to be displayed. |
-| `tooltip-position` | `"before"|"after"|"above"|"below"|"left"|"right"` | The position of the tooltip. |
+| `mdTooltip` | `string` | The message to be displayed. |
+| `mdTooltipPosition` | `"before"|"after"|"above"|"below"|"left"|"right"` | The position of the tooltip. |
### Methods
diff --git a/src/lib/tooltip/tooltip.spec.ts b/src/lib/tooltip/tooltip.spec.ts
index 97481e8bbf80..83c9ac5a1657 100644
--- a/src/lib/tooltip/tooltip.spec.ts
+++ b/src/lib/tooltip/tooltip.spec.ts
@@ -302,8 +302,8 @@ describe('MdTooltip', () => {
selector: 'app',
template: `
+ [mdTooltip]="message"
+ [mdTooltipPosition]="position">
Button
`
})
diff --git a/src/lib/tooltip/tooltip.ts b/src/lib/tooltip/tooltip.ts
index c14536da593d..b8c1f9e003bd 100644
--- a/src/lib/tooltip/tooltip.ts
+++ b/src/lib/tooltip/tooltip.ts
@@ -43,7 +43,7 @@ export const TOUCHEND_HIDE_DELAY = 1500;
* https://material.google.com/components/tooltips.html
*/
@Directive({
- selector: '[md-tooltip], [mat-tooltip]',
+ selector: '[md-tooltip], [mat-tooltip], [mdTooltip]',
host: {
'(longpress)': 'show()',
'(touchend)': 'hide(' + TOUCHEND_HIDE_DELAY + ')',
@@ -58,10 +58,15 @@ export class MdTooltip {
/** Allows the user to define the position of the tooltip relative to the parent element */
private _position: TooltipPosition = 'below';
- @Input('tooltip-position') get position(): TooltipPosition {
+ @Input('mdTooltipPosition') get position(): TooltipPosition {
return this._position;
}
+ /** @deprecated */
+ @Input('tooltip-position')
+ get _positionDeprecated(): TooltipPosition { return this._position; }
+ set _positionDeprecated(value: TooltipPosition) { this._position = value; }
+
set position(value: TooltipPosition) {
if (value !== this._position) {
this._position = value;
@@ -75,14 +80,14 @@ export class MdTooltip {
}
/** The default delay in ms before showing the tooltip after show is called */
- @Input('tooltipShowDelay') showDelay = 0;
+ @Input('mdTooltipShowDelay') showDelay = 0;
/** The default delay in ms before hiding the tooltip after hide is called */
- @Input('tooltipHideDelay') hideDelay = 0;
+ @Input('mdTooltipHideDelay') hideDelay = 0;
/** The message to be displayed in the tooltip */
private _message: string;
- @Input('md-tooltip') get message() {
+ @Input('mdTooltip') get message() {
return this._message;
}
set message(value: string) {
@@ -92,6 +97,11 @@ export class MdTooltip {
}
}
+ /** @deprecated */
+ @Input('md-tooltip')
+ get _deprecatedMessage(): string { return this.message; }
+ set _deprecatedMessage(v: string) { this.message = v; }
+
constructor(private _overlay: Overlay, private _elementRef: ElementRef,
private _viewContainerRef: ViewContainerRef, private _ngZone: NgZone,
@Optional() private _dir: Dir) {}
From 2168d7cf6947be8305df5dfa4b32b55e2ada656c Mon Sep 17 00:00:00 2001
From: Jeremy Elbourn
Date: Fri, 16 Dec 2016 18:24:56 -0800
Subject: [PATCH 52/91] chore: add PlatformModule export to core.ts
---
src/lib/core/core.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/lib/core/core.ts b/src/lib/core/core.ts
index 0cda89a4ccc4..7b99bb54bfde 100644
--- a/src/lib/core/core.ts
+++ b/src/lib/core/core.ts
@@ -32,6 +32,7 @@ export * from './projection/projection';
// Platform
export * from './platform/platform';
export * from './platform/features';
+export {PlatformModule} from './platform/index';
// Overlay
export {Overlay, OVERLAY_PROVIDERS} from './overlay/overlay';
From 2be9ec4ee1488945f281fc0ab17de3c4c534ae01 Mon Sep 17 00:00:00 2001
From: Martin Belev
Date: Mon, 19 Dec 2016 00:14:51 +0200
Subject: [PATCH 53/91] chore: update stylelint version (#2268)
Update the version of stylelint to remove "Can not parse selector".
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 55f7f90f7b61..ecbe1e9303b3 100644
--- a/package.json
+++ b/package.json
@@ -88,7 +88,7 @@
"run-sequence": "^1.2.2",
"sass": "^0.5.0",
"strip-ansi": "^3.0.0",
- "stylelint": "^7.5.0",
+ "stylelint": "^7.7.0",
"symlink-or-copy": "^1.0.1",
"travis-after-modes": "0.0.6-2",
"ts-node": "^0.7.3",
From 28f1f803ba4825630eee0014f2ffb442d5280806 Mon Sep 17 00:00:00 2001
From: Paul Gschwendtner
Date: Mon, 19 Dec 2016 19:16:28 +0100
Subject: [PATCH 54/91] chore: minify html and css for releases (#2262)
* chore: minify html and css for releases
* For releases, the HTML and CSS assets will be minified before being inlined into the built JS
* Remove comments in production html
---
package.json | 3 +++
tools/gulp/constants.ts | 5 +++++
tools/gulp/tasks/components.ts | 38 ++++++++++++++++++++++++++-------
tools/gulp/tasks/development.ts | 2 +-
4 files changed, 39 insertions(+), 9 deletions(-)
diff --git a/package.json b/package.json
index ecbe1e9303b3..8cd801703d20 100644
--- a/package.json
+++ b/package.json
@@ -63,7 +63,10 @@
"gulp": "^3.9.1",
"gulp-autoprefixer": "^3.1.1",
"gulp-clean": "^0.3.2",
+ "gulp-clean-css": "^2.3.0",
"gulp-cli": "^1.2.2",
+ "gulp-htmlmin": "^3.0.0",
+ "gulp-if": "^2.0.2",
"gulp-markdown": "^1.2.0",
"gulp-sass": "^2.3.2",
"gulp-server-livereload": "^1.8.2",
diff --git a/tools/gulp/constants.ts b/tools/gulp/constants.ts
index 4a7f2fed561d..773d3a4a10b0 100644
--- a/tools/gulp/constants.ts
+++ b/tools/gulp/constants.ts
@@ -15,6 +15,11 @@ export const SASS_AUTOPREFIXER_OPTIONS = {
cascade: false,
};
+export const HTML_MINIFIER_OPTIONS = {
+ collapseWhitespace: true,
+ removeComments: true
+};
+
export const NPM_VENDOR_FILES = [
'@angular', 'core-js/client', 'hammerjs', 'rxjs', 'systemjs/dist', 'zone.js/dist'
];
diff --git a/tools/gulp/tasks/components.ts b/tools/gulp/tasks/components.ts
index 41f9d5e33b89..e9ab99483d57 100644
--- a/tools/gulp/tasks/components.ts
+++ b/tools/gulp/tasks/components.ts
@@ -1,13 +1,18 @@
-import {task, watch} from 'gulp';
+import {task, watch, src, dest} from 'gulp';
import * as path from 'path';
-import {DIST_COMPONENTS_ROOT, PROJECT_ROOT, COMPONENTS_DIR} from '../constants';
+import {
+ DIST_COMPONENTS_ROOT, PROJECT_ROOT, COMPONENTS_DIR, HTML_MINIFIER_OPTIONS
+} from '../constants';
import {sassBuildTask, tsBuildTask, execNodeTask, copyTask, sequenceTask} from '../task_helpers';
import {writeFileSync} from 'fs';
// No typings for these.
const inlineResources = require('../../../scripts/release/inline-resources');
const rollup = require('rollup').rollup;
+const gulpMinifyCss = require('gulp-clean-css');
+const gulpMinifyHtml = require('gulp-htmlmin');
+const gulpIf = require('gulp-if');
// NOTE: there are two build "modes" in this file, based on which tsconfig is used.
@@ -23,9 +28,9 @@ const tsconfigPath = path.relative(PROJECT_ROOT, path.join(COMPONENTS_DIR, 'tsco
/** [Watch task] Rebuilds (ESM output) whenever ts, scss, or html sources change. */
task(':watch:components', () => {
- watch(path.join(COMPONENTS_DIR, '**/*.ts'), [':build:components:rollup']);
- watch(path.join(COMPONENTS_DIR, '**/*.scss'), [':build:components:rollup']);
- watch(path.join(COMPONENTS_DIR, '**/*.html'), [':build:components:rollup']);
+ watch(path.join(COMPONENTS_DIR, '**/*.ts'), ['build:components']);
+ watch(path.join(COMPONENTS_DIR, '**/*.scss'), ['build:components']);
+ watch(path.join(COMPONENTS_DIR, '**/*.html'), ['build:components']);
});
@@ -41,11 +46,18 @@ task(':build:components:assets', copyTask([
path.join(PROJECT_ROOT, 'README.md'),
], DIST_COMPONENTS_ROOT));
+/** Minifies the HTML and CSS assets in the distribution folder. */
+task(':build:components:assets:minify', () => {
+ return src('**/*.+(html|css)', { cwd: DIST_COMPONENTS_ROOT})
+ .pipe(gulpIf(/.css$/, gulpMinifyCss(), gulpMinifyHtml(HTML_MINIFIER_OPTIONS)))
+ .pipe(dest(DIST_COMPONENTS_ROOT));
+});
+
/** Builds scss into css. */
task(':build:components:scss', sassBuildTask(DIST_COMPONENTS_ROOT, COMPONENTS_DIR));
/** Builds the UMD bundle for all of Angular Material. */
-task(':build:components:rollup', [':build:components:inline'], () => {
+task(':build:components:rollup', () => {
const globals: {[name: string]: string} = {
// Angular dependencies
'@angular/core': 'ng.core',
@@ -102,13 +114,23 @@ task(':build:components:inline', sequenceTask(
':inline-resources',
));
+/** Builds components with minified HTML and CSS inlined into the built JS. */
+task(':build:components:inline:release', sequenceTask(
+ [':build:components:ts', ':build:components:scss', ':build:components:assets'],
+ ':build:components:assets:minify',
+ ':inline-resources'
+));
+
/** Inlines resources (html, css) into the JS output (for either ESM or CJS output). */
task(':inline-resources', () => inlineResources(DIST_COMPONENTS_ROOT));
/** Builds components to ESM output and UMD bundle. */
-task('build:components', [':build:components:rollup']);
+task('build:components', sequenceTask(':build:components:inline', ':build:components:rollup'));
+task('build:components:release', sequenceTask(
+ ':build:components:inline:release', ':build:components:rollup'
+));
/** Generates metadata.json files for all of the components. */
-task(':build:components:ngc', ['build:components'], execNodeTask(
+task(':build:components:ngc', ['build:components:release'], execNodeTask(
'@angular/compiler-cli', 'ngc', ['-p', tsconfigPath]
));
diff --git a/tools/gulp/tasks/development.ts b/tools/gulp/tasks/development.ts
index c239ccb971a2..2aa70c855753 100644
--- a/tools/gulp/tasks/development.ts
+++ b/tools/gulp/tasks/development.ts
@@ -24,7 +24,7 @@ task(':watch:devapp', () => {
task(':build:devapp:vendor', vendorTask());
-task(':build:devapp:ts', [':build:components:rollup'], tsBuildTask(appDir));
+task(':build:devapp:ts', ['build:components'], tsBuildTask(appDir));
task(':build:devapp:scss', [':build:components:scss'], sassBuildTask(outDir, appDir));
task(':build:devapp:assets', copyTask(appDir, outDir));
task('build:devapp', buildAppTask('devapp'));
From 508703a8c509ad16eaa8bf7f37d664192aca7823 Mon Sep 17 00:00:00 2001
From: Kristiyan Kostadinov
Date: Mon, 19 Dec 2016 20:17:23 +0200
Subject: [PATCH 55/91] test: use proper typings in e2e app (#2051)
* test: use proper typings in e2e app
* Switches to use the typings from @types for the e2e app.
* Adds all of the necessary imports and fixes type issues.
Relates to #2025.
Fixes #1531.
* Add missing typings to slide toggle.
* Fix another typo.
---
e2e/components/button/button.e2e.ts | 2 +
e2e/components/checkbox/checkbox.e2e.ts | 2 +
e2e/components/dialog/dialog.e2e.ts | 20 +-
e2e/components/grid-list/grid-list.e2e.ts | 2 +
e2e/components/icon/icon.e2e.ts | 2 +
e2e/components/list/list.e2e.ts | 2 +
e2e/components/menu/menu-page.ts | 17 +-
e2e/components/menu/menu.e2e.ts | 55 +-
.../progress-bar/progress-bar.e2e.ts | 2 +
.../progress-circle/progress-circle.e2e.ts | 2 +
e2e/components/radio/radio.e2e.ts | 2 +
.../slide-toggle/slide-toggle.e2e.ts | 4 +-
e2e/components/tabs/tabs.e2e.ts | 15 +-
e2e/e2e.d.ts | 4 -
e2e/index.e2e.ts | 2 +
e2e/tsconfig.json | 13 +-
e2e/typings/angular-protractor/index.d.ts | 1850 ------
e2e/typings/es6-shim/es6-shim.d.ts | 670 --
e2e/typings/jasmine/jasmine.d.ts | 498 --
e2e/typings/node/index.d.ts | 2331 -------
e2e/typings/selenium-webdriver/index.d.ts | 5395 -----------------
package.json | 3 +
22 files changed, 86 insertions(+), 10807 deletions(-)
delete mode 100644 e2e/e2e.d.ts
delete mode 100644 e2e/typings/angular-protractor/index.d.ts
delete mode 100644 e2e/typings/es6-shim/es6-shim.d.ts
delete mode 100644 e2e/typings/jasmine/jasmine.d.ts
delete mode 100644 e2e/typings/node/index.d.ts
delete mode 100644 e2e/typings/selenium-webdriver/index.d.ts
diff --git a/e2e/components/button/button.e2e.ts b/e2e/components/button/button.e2e.ts
index 6d7a263d6803..8bbab6c135b9 100644
--- a/e2e/components/button/button.e2e.ts
+++ b/e2e/components/button/button.e2e.ts
@@ -1,3 +1,5 @@
+import {browser, by, element} from 'protractor';
+
describe('button', function () {
describe('disabling behavior', function () {
beforeEach(function() {
diff --git a/e2e/components/checkbox/checkbox.e2e.ts b/e2e/components/checkbox/checkbox.e2e.ts
index d4b0008c95bf..18e2d434c348 100644
--- a/e2e/components/checkbox/checkbox.e2e.ts
+++ b/e2e/components/checkbox/checkbox.e2e.ts
@@ -1,3 +1,5 @@
+import {browser, by, element} from 'protractor';
+
describe('checkbox', function () {
describe('check behavior', function () {
beforeEach(function() {
diff --git a/e2e/components/dialog/dialog.e2e.ts b/e2e/components/dialog/dialog.e2e.ts
index 105f8e91abe6..4af19720d6cb 100644
--- a/e2e/components/dialog/dialog.e2e.ts
+++ b/e2e/components/dialog/dialog.e2e.ts
@@ -1,9 +1,11 @@
+import {browser, by, element, Key, ProtractorBy} from 'protractor';
+
describe('dialog', () => {
beforeEach(() => browser.get('/dialog'));
it('should open a dialog', () => {
element(by.id('default')).click();
- waitForDialog().then(isPresent => expect(isPresent).toBe(true));
+ waitForDialog().then((isPresent: boolean) => expect(isPresent).toBe(true));
});
it('should close by clicking on the backdrop', () => {
@@ -11,7 +13,7 @@ describe('dialog', () => {
waitForDialog().then(() => {
clickOnBackrop();
- waitForDialog().then(isPresent => expect(isPresent).toBe(false));
+ waitForDialog().then((isPresent: boolean) => expect(isPresent).toBe(false));
});
});
@@ -20,7 +22,7 @@ describe('dialog', () => {
waitForDialog().then(() => {
pressEscape();
- waitForDialog().then(isPresent => expect(isPresent).toBe(false));
+ waitForDialog().then((isPresent: boolean) => expect(isPresent).toBe(false));
});
});
@@ -29,7 +31,7 @@ describe('dialog', () => {
waitForDialog().then(() => {
element(by.id('close')).click();
- waitForDialog().then(isPresent => expect(isPresent).toBe(false));
+ waitForDialog().then((isPresent: boolean) => expect(isPresent).toBe(false));
});
});
@@ -56,7 +58,7 @@ describe('dialog', () => {
element(by.id('default')).click();
waitForDialog().then(() => {
- let tab = protractor.Key.TAB;
+ let tab = Key.TAB;
browser.actions().sendKeys(tab, tab, tab).perform();
expectFocusOn(element(by.id('close')));
@@ -68,7 +70,7 @@ describe('dialog', () => {
waitForDialog().then(() => {
clickOnBackrop();
- waitForDialog().then(isPresent => expect(isPresent).toBe(true));
+ waitForDialog().then((isPresent: boolean) => expect(isPresent).toBe(true));
});
});
@@ -77,12 +79,12 @@ describe('dialog', () => {
waitForDialog().then(() => {
pressEscape();
- waitForDialog().then(isPresent => expect(isPresent).toBe(true));
+ waitForDialog().then((isPresent: boolean) => expect(isPresent).toBe(true));
});
});
function waitForDialog() {
- return browser.isElementPresent(by.css('md-dialog-container'));
+ return browser.isElementPresent(by.css('md-dialog-container') as ProtractorBy);
}
function clickOnBackrop() {
@@ -95,7 +97,7 @@ describe('dialog', () => {
}
function pressEscape() {
- browser.actions().sendKeys(protractor.Key.ESCAPE).perform();
+ browser.actions().sendKeys(Key.ESCAPE).perform();
}
// TODO(crisbeto): should be moved to a common util. copied from the menu e2e setup.
diff --git a/e2e/components/grid-list/grid-list.e2e.ts b/e2e/components/grid-list/grid-list.e2e.ts
index b3c19563df20..64c4647fde5a 100644
--- a/e2e/components/grid-list/grid-list.e2e.ts
+++ b/e2e/components/grid-list/grid-list.e2e.ts
@@ -1,3 +1,5 @@
+import {browser, by, element} from 'protractor';
+
describe('grid-list', () => {
beforeEach(() => browser.get('/grid-list'));
diff --git a/e2e/components/icon/icon.e2e.ts b/e2e/components/icon/icon.e2e.ts
index 9894a2fd9a3b..e0cb1caa6910 100644
--- a/e2e/components/icon/icon.e2e.ts
+++ b/e2e/components/icon/icon.e2e.ts
@@ -1,3 +1,5 @@
+import {browser, by, element} from 'protractor';
+
describe('icon', () => {
describe('font icons by ligature', () => {
let testIcon: any;
diff --git a/e2e/components/list/list.e2e.ts b/e2e/components/list/list.e2e.ts
index 708ff9943ef0..329f8a8dd19c 100644
--- a/e2e/components/list/list.e2e.ts
+++ b/e2e/components/list/list.e2e.ts
@@ -1,3 +1,5 @@
+import {browser, by, element} from 'protractor';
+
describe('list', () => {
beforeEach(() => browser.get('/list'));
diff --git a/e2e/components/menu/menu-page.ts b/e2e/components/menu/menu-page.ts
index a89ce7327573..270f7900a75f 100644
--- a/e2e/components/menu/menu-page.ts
+++ b/e2e/components/menu/menu-page.ts
@@ -1,4 +1,4 @@
-import ElementFinder = protractor.ElementFinder;
+import {browser, by, element, ElementFinder, ProtractorBy} from 'protractor';
export class MenuPage {
@@ -16,9 +16,7 @@ export class MenuPage {
backdrop() { return element(by.css('.md-overlay-backdrop')); }
- items(index: number) {
- return element.all(by.css('[md-menu-item]')).get(index);
- }
+ items(index: number) { return element.all(by.css('[md-menu-item]')).get(index); }
textArea() { return element(by.id('text')); }
@@ -35,20 +33,21 @@ export class MenuPage {
combinedMenu() { return element(by.css('.md-menu-panel.combined')); }
// TODO(kara): move to common testing utility
- pressKey(key: any): void {
+ pressKey(key: string): void {
browser.actions().sendKeys(key).perform();
}
// TODO(kara): move to common testing utility
- expectFocusOn(el: ElementFinder): void {
+ expectFocusOn(el: any): void {
expect(browser.driver.switchTo().activeElement().getInnerHtml())
.toBe(el.getInnerHtml());
}
expectMenuPresent(expected: boolean) {
- return browser.isElementPresent(by.css('.md-menu-panel')).then(isPresent => {
- expect(isPresent).toBe(expected);
- });
+ return browser.isElementPresent(by.css('.md-menu-panel') as ProtractorBy)
+ .then((isPresent: boolean) => {
+ expect(isPresent).toBe(expected);
+ });
}
expectMenuLocation(el: ElementFinder, {x, y}: {x: number, y: number}) {
diff --git a/e2e/components/menu/menu.e2e.ts b/e2e/components/menu/menu.e2e.ts
index 099620972e06..79c3c394c77c 100644
--- a/e2e/components/menu/menu.e2e.ts
+++ b/e2e/components/menu/menu.e2e.ts
@@ -1,4 +1,5 @@
-import { MenuPage } from './menu-page';
+import {browser, Key, protractor} from 'protractor';
+import {MenuPage} from './menu-page';
describe('menu', () => {
let page: MenuPage;
@@ -57,7 +58,7 @@ describe('menu', () => {
it('should mirror classes on host to menu template in overlay', () => {
page.trigger().click();
- page.menu().getAttribute('class').then(classes => {
+ page.menu().getAttribute('class').then((classes: string) => {
expect(classes).toContain('md-menu-panel custom');
});
});
@@ -66,16 +67,16 @@ describe('menu', () => {
beforeEach(() => {
// click start button to avoid tabbing past navigation
page.start().click();
- page.pressKey(protractor.Key.TAB);
+ page.pressKey(Key.TAB);
});
it('should auto-focus the first item when opened with ENTER', () => {
- page.pressKey(protractor.Key.ENTER);
+ page.pressKey(Key.ENTER);
page.expectFocusOn(page.items(0));
});
it('should auto-focus the first item when opened with SPACE', () => {
- page.pressKey(protractor.Key.SPACE);
+ page.pressKey(Key.SPACE);
page.expectFocusOn(page.items(0));
});
@@ -85,60 +86,60 @@ describe('menu', () => {
});
it('should focus subsequent items when down arrow is pressed', () => {
- page.pressKey(protractor.Key.ENTER);
- page.pressKey(protractor.Key.DOWN);
+ page.pressKey(Key.ENTER);
+ page.pressKey(Key.DOWN);
page.expectFocusOn(page.items(1));
});
it('should focus previous items when up arrow is pressed', () => {
- page.pressKey(protractor.Key.ENTER);
- page.pressKey(protractor.Key.DOWN);
- page.pressKey(protractor.Key.UP);
+ page.pressKey(Key.ENTER);
+ page.pressKey(Key.DOWN);
+ page.pressKey(Key.UP);
page.expectFocusOn(page.items(0));
});
it('should skip disabled items using arrow keys', () => {
- page.pressKey(protractor.Key.ENTER);
- page.pressKey(protractor.Key.DOWN);
- page.pressKey(protractor.Key.DOWN);
+ page.pressKey(Key.ENTER);
+ page.pressKey(Key.DOWN);
+ page.pressKey(Key.DOWN);
page.expectFocusOn(page.items(3));
- page.pressKey(protractor.Key.UP);
+ page.pressKey(Key.UP);
page.expectFocusOn(page.items(1));
});
it('should close the menu when tabbing past items', () => {
- page.pressKey(protractor.Key.ENTER);
- page.pressKey(protractor.Key.TAB);
+ page.pressKey(Key.ENTER);
+ page.pressKey(Key.TAB);
page.expectMenuPresent(false);
- page.pressKey(protractor.Key.TAB);
- page.pressKey(protractor.Key.ENTER);
+ page.pressKey(Key.TAB);
+ page.pressKey(Key.ENTER);
page.expectMenuPresent(true);
- page.pressKey(protractor.Key.chord(protractor.Key.SHIFT, protractor.Key.TAB));
+ page.pressKey(protractor.Key.chord(Key.SHIFT, Key.TAB));
page.expectMenuPresent(false);
});
it('should wrap back to menu when arrow keying past items', () => {
- page.pressKey(protractor.Key.ENTER);
- page.pressKey(protractor.Key.DOWN);
- page.pressKey(protractor.Key.DOWN);
- page.pressKey(protractor.Key.DOWN);
+ page.pressKey(Key.ENTER);
+ page.pressKey(Key.DOWN);
+ page.pressKey(Key.DOWN);
+ page.pressKey(Key.DOWN);
page.expectFocusOn(page.items(0));
- page.pressKey(protractor.Key.UP);
+ page.pressKey(Key.UP);
page.expectFocusOn(page.items(3));
});
it('should focus before and after trigger when tabbing past items', () => {
- page.pressKey(protractor.Key.ENTER);
- page.pressKey(protractor.Key.TAB);
+ page.pressKey(Key.ENTER);
+ page.pressKey(Key.TAB);
page.expectFocusOn(page.triggerTwo());
// navigate back to trigger
page.pressKey(protractor.Key.chord(protractor.Key.SHIFT, protractor.Key.TAB));
- page.pressKey(protractor.Key.ENTER);
+ page.pressKey(Key.ENTER);
page.pressKey(protractor.Key.chord(protractor.Key.SHIFT, protractor.Key.TAB));
page.expectFocusOn(page.start());
diff --git a/e2e/components/progress-bar/progress-bar.e2e.ts b/e2e/components/progress-bar/progress-bar.e2e.ts
index 679849145462..33c5898400b8 100644
--- a/e2e/components/progress-bar/progress-bar.e2e.ts
+++ b/e2e/components/progress-bar/progress-bar.e2e.ts
@@ -1,3 +1,5 @@
+import {browser, by, element} from 'protractor';
+
describe('progress-bar', () => {
beforeEach(() => browser.get('/progress-bar'));
diff --git a/e2e/components/progress-circle/progress-circle.e2e.ts b/e2e/components/progress-circle/progress-circle.e2e.ts
index 9dcbdeefe0f0..29c344f8a9cb 100644
--- a/e2e/components/progress-circle/progress-circle.e2e.ts
+++ b/e2e/components/progress-circle/progress-circle.e2e.ts
@@ -1,3 +1,5 @@
+import {browser, by, element} from 'protractor';
+
describe('progress-circle', () => {
beforeEach(() => browser.get('/progress-circle'));
diff --git a/e2e/components/radio/radio.e2e.ts b/e2e/components/radio/radio.e2e.ts
index 7fccd196a68a..37c81509dc08 100644
--- a/e2e/components/radio/radio.e2e.ts
+++ b/e2e/components/radio/radio.e2e.ts
@@ -1,3 +1,5 @@
+import {browser, by, element} from 'protractor';
+
describe('radio', function () {
describe('disabling behavior', function () {
diff --git a/e2e/components/slide-toggle/slide-toggle.e2e.ts b/e2e/components/slide-toggle/slide-toggle.e2e.ts
index c818c632826e..c68610615a6a 100644
--- a/e2e/components/slide-toggle/slide-toggle.e2e.ts
+++ b/e2e/components/slide-toggle/slide-toggle.e2e.ts
@@ -1,3 +1,5 @@
+import {browser, element, by, protractor} from 'protractor';
+
describe('slide-toggle', () => {
beforeEach(() => browser.get('slide-toggle'));
@@ -40,7 +42,7 @@ describe('slide-toggle', () => {
});
it('should move the thumb on state change', () => {
- let slideToggleEl = element(By.css('#normal-slide-toggle'));
+ let slideToggleEl = element(by.css('#normal-slide-toggle'));
let thumbEl = element(by.css('#normal-slide-toggle .md-slide-toggle-thumb-container'));
let previousX = thumbEl.getLocation().then(pos => pos.x);
diff --git a/e2e/components/tabs/tabs.e2e.ts b/e2e/components/tabs/tabs.e2e.ts
index 61f8a9b9f95e..9a9fd53a3032 100644
--- a/e2e/components/tabs/tabs.e2e.ts
+++ b/e2e/components/tabs/tabs.e2e.ts
@@ -1,5 +1,4 @@
-import ElementArrayFinder = protractor.ElementArrayFinder;
-import ElementFinder = protractor.ElementFinder;
+import {browser, by, element, ElementArrayFinder, ElementFinder, Key} from 'protractor';
describe('tabs', () => {
describe('basic behavior', () => {
@@ -28,22 +27,22 @@ describe('tabs', () => {
tabLabels.get(0).click();
expect(getFocusStates(tabLabels)).toEqual([true, false, false]);
- pressKey(protractor.Key.RIGHT);
+ pressKey(Key.RIGHT);
expect(getFocusStates(tabLabels)).toEqual([false, true, false]);
- pressKey(protractor.Key.RIGHT);
+ pressKey(Key.RIGHT);
expect(getFocusStates(tabLabels)).toEqual([false, false, true]);
- pressKey(protractor.Key.RIGHT);
+ pressKey(Key.RIGHT);
expect(getFocusStates(tabLabels)).toEqual([false, false, true]);
- pressKey(protractor.Key.LEFT);
+ pressKey(Key.LEFT);
expect(getFocusStates(tabLabels)).toEqual([false, true, false]);
- pressKey(protractor.Key.LEFT);
+ pressKey(Key.LEFT);
expect(getFocusStates(tabLabels)).toEqual([true, false, false]);
- pressKey(protractor.Key.LEFT);
+ pressKey(Key.LEFT);
expect(getFocusStates(tabLabels)).toEqual([true, false, false]);
});
});
diff --git a/e2e/e2e.d.ts b/e2e/e2e.d.ts
deleted file mode 100644
index 0674ad35d6f7..000000000000
--- a/e2e/e2e.d.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-///
-///
-///
-///
diff --git a/e2e/index.e2e.ts b/e2e/index.e2e.ts
index 4bc96fb8936e..dded58a17bae 100644
--- a/e2e/index.e2e.ts
+++ b/e2e/index.e2e.ts
@@ -1,3 +1,5 @@
+import {browser} from 'protractor';
+
describe('hello, protractor', function () {
describe('index', function () {
browser.get('/');
diff --git a/e2e/tsconfig.json b/e2e/tsconfig.json
index 82440caf18f3..06f19589b388 100644
--- a/e2e/tsconfig.json
+++ b/e2e/tsconfig.json
@@ -11,9 +11,12 @@
"outDir": "../dist/e2e/",
"rootDir": ".",
"sourceMap": true,
- "target": "es5"
- },
- "files": [
- "e2e.d.ts"
- ]
+ "target": "es5",
+ "typeRoots": [
+ "../node_modules/@types/"
+ ],
+ "types": [
+ "jasmine"
+ ]
+ }
}
diff --git a/e2e/typings/angular-protractor/index.d.ts b/e2e/typings/angular-protractor/index.d.ts
deleted file mode 100644
index 66b391c52902..000000000000
--- a/e2e/typings/angular-protractor/index.d.ts
+++ /dev/null
@@ -1,1850 +0,0 @@
-// Generated by typings
-// Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/7de6c3dd94feaeb21f20054b9f30d5dabc5efabd/angular-protractor/angular-protractor.d.ts
-// Type definitions for Angular Protractor 1.5.0
-// Project: https://github.com/angular/protractor
-// Definitions by: Bill Armstrong
-// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
-
-
-declare namespace protractor {
- //region Wrapped webdriver Items
-
- class ActionSequence extends webdriver.ActionSequence {}
- class Builder extends webdriver.Builder {}
- class Capabilities extends webdriver.Capabilities {}
- class Command extends webdriver.Command {}
- class EventEmitter extends webdriver.EventEmitter {}
- class Session extends webdriver.Session {}
- class WebDriver extends webdriver.WebDriver {}
- class WebElement extends webdriver.WebElement {}
- class WebElementPromise extends webdriver.WebElementPromise { }
-
- var Browser: webdriver.IBrowser;
- var Button: webdriver.IButton;
- var Capability: webdriver.ICapability;
- var CommandName: webdriver.ICommandName;
- var Key: webdriver.IKey;
-
- namespace error {
- class Error extends webdriver.error.Error {}
- var ErrorCode: webdriver.error.IErrorCode;
- }
-
- namespace logging {
- class Preferences extends webdriver.logging.Preferences { }
- class Entry extends webdriver.logging.Entry { }
-
- var Type: webdriver.logging.IType;
- var Level: webdriver.logging.ILevelValues;
-
- function getLevel(nameOrValue: string): webdriver.logging.ILevel;
- function getLevel(nameOrValue: number): webdriver.logging.ILevel;
- }
-
- namespace promise {
- class Thenable extends webdriver.promise.Thenable { }
- class Promise extends webdriver.promise.Promise { }
- class Deferred extends webdriver.promise.Deferred { }
- class ControlFlow extends webdriver.promise.ControlFlow { }
- class CancellationError extends webdriver.promise.CancellationError { }
-
- /**
- * Given an array of promises, will return a promise that will be fulfilled
- * with the fulfillment values of the input array's values. If any of the
- * input array's promises are rejected, the returned promise will be rejected
- * with the same reason.
- *
- * @param {!Array.<(T|!webdriver.promise.Promise.)>} arr An array of
- * promises to wait on.
- * @return {!webdriver.promise.Promise.>} A promise that is
- * fulfilled with an array containing the fulfilled values of the
- * input array, or rejected with the same reason as the first
- * rejected value.
- * @template T
- */
- function all(arr: webdriver.promise.Promise[]): webdriver.promise.Promise;
-
- /**
- * Invokes the appropriate callback function as soon as a promised
- * {@code value} is resolved. This function is similar to
- * {@link webdriver.promise.when}, except it does not return a new promise.
- * @param {*} value The value to observe.
- * @param {Function} callback The function to call when the value is
- * resolved successfully.
- * @param {Function=} opt_errback The function to call when the value is
- * rejected.
- */
- function asap(value: any, callback: Function, opt_errback?: Function): void;
-
- /**
- * @return {!webdriver.promise.ControlFlow} The currently active control flow.
- */
- function controlFlow(): webdriver.promise.ControlFlow;
-
- /**
- * Creates a new control flow. The provided callback will be invoked as the
- * first task within the new flow, with the flow as its sole argument. Returns
- * a promise that resolves to the callback result.
- * @param {function(!webdriver.promise.ControlFlow)} callback The entry point
- * to the newly created flow.
- * @return {!webdriver.promise.Promise} A promise that resolves to the callback
- * result.
- */
- function createFlow(callback: (flow: webdriver.promise.ControlFlow) => R): webdriver.promise.Promise;
-
- /**
- * Determines whether a {@code value} should be treated as a promise.
- * Any object whose "then" property is a function will be considered a promise.
- *
- * @param {*} value The value to test.
- * @return {boolean} Whether the value is a promise.
- */
- function isPromise(value: any): boolean;
-
- /**
- * Tests is a function is a generator.
- * @param {!Function} fn The function to test.
- * @return {boolean} Whether the function is a generator.
- */
- function isGenerator(fn: Function): boolean;
-
- /**
- * Creates a promise that will be resolved at a set time in the future.
- * @param {number} ms The amount of time, in milliseconds, to wait before
- * resolving the promise.
- * @return {!webdriver.promise.Promise} The promise.
- */
- function delayed(ms: number): webdriver.promise.Promise;
-
- /**
- * Calls a function for each element in an array, and if the function returns
- * true adds the element to a new array.
- *
- * If the return value of the filter function is a promise, this function
- * will wait for it to be fulfilled before determining whether to insert the
- * element into the new array.
- *
- *
If the filter function throws or returns a rejected promise, the promise
- * returned by this function will be rejected with the same reason. Only the
- * first failure will be reported; all subsequent errors will be silently
- * ignored.
- *
- * @param {!(Array.|webdriver.promise.Promise.>)} arr The
- * array to iterator over, or a promise that will resolve to said array.
- * @param {function(this: SELF, TYPE, number, !Array.): (
- * boolean|webdriver.promise.Promise.)} fn The function
- * to call for each element in the array.
- * @param {SELF=} opt_self The object to be used as the value of 'this' within
- * {@code fn}.
- * @template TYPE, SELF
- */
- function filter(arr: T[], fn: (element: T, index: number, array: T[]) => any, opt_self?: any): webdriver.promise.Promise;
- function filter(arr: webdriver.promise.Promise, fn: (element: T, index: number, array: T[]) => any, opt_self?: any): webdriver.promise.Promise
-
- /**
- * Creates a new deferred object.
- * @return {!webdriver.promise.Deferred} The new deferred object.
- */
- function defer(): webdriver.promise.Deferred;
-
- /**
- * Creates a promise that has been resolved with the given value.
- * @param {*=} opt_value The resolved value.
- * @return {!webdriver.promise.Promise} The resolved promise.
- */
- function fulfilled(opt_value?: T): webdriver.promise.Promise;
-
- /**
- * Calls a function for each element in an array and inserts the result into a
- * new array, which is used as the fulfillment value of the promise returned
- * by this function.
- *
- * If the return value of the mapping function is a promise, this function
- * will wait for it to be fulfilled before inserting it into the new array.
- *
- *
If the mapping function throws or returns a rejected promise, the
- * promise returned by this function will be rejected with the same reason.
- * Only the first failure will be reported; all subsequent errors will be
- * silently ignored.
- *
- * @param {!(Array.|webdriver.promise.Promise.>)} arr The
- * array to iterator over, or a promise that will resolve to said array.
- * @param {function(this: SELF, TYPE, number, !Array.): ?} fn The
- * function to call for each element in the array. This function should
- * expect three arguments (the element, the index, and the array itself.
- * @param {SELF=} opt_self The object to be used as the value of 'this' within
- * {@code fn}.
- * @template TYPE, SELF
- */
- function map(arr: T[], fn: (element: T, index: number, array: T[]) => any, opt_self?: any): webdriver.promise.Promise
- function map(arr: webdriver.promise.Promise, fn: (element: T, index: number, array: T[]) => any, opt_self?: any): webdriver.promise.Promise
-
- /**
- * Creates a promise that has been rejected with the given reason.
- * @param {*=} opt_reason The rejection reason; may be any value, but is
- * usually an Error or a string.
- * @return {!webdriver.promise.Promise} The rejected promise.
- */
- function rejected(opt_reason?: any): webdriver.promise.Promise;
-
- /**
- * Wraps a function that is assumed to be a node-style callback as its final
- * argument. This callback takes two arguments: an error value (which will be
- * null if the call succeeded), and the success value as the second argument.
- * If the call fails, the returned promise will be rejected, otherwise it will
- * be resolved with the result.
- * @param {!Function} fn The function to wrap.
- * @return {!webdriver.promise.Promise} A promise that will be resolved with the
- * result of the provided function's callback.
- */
- function checkedNodeCall(fn: Function, ...var_args: any[]): webdriver.promise.Promise;
-
- /**
- * Consumes a {@code GeneratorFunction}. Each time the generator yields a
- * promise, this function will wait for it to be fulfilled before feeding the
- * fulfilled value back into {@code next}. Likewise, if a yielded promise is
- * rejected, the rejection error will be passed to {@code throw}.
- *
- * Example 1: the Fibonacci Sequence.
- *
- * webdriver.promise.consume(function* fibonacci() {
- * var n1 = 1, n2 = 1;
- * for (var i = 0; i < 4; ++i) {
- * var tmp = yield n1 + n2;
- * n1 = n2;
- * n2 = tmp;
- * }
- * return n1 + n2;
- * }).then(function(result) {
- * console.log(result); // 13
- * });
- *
- *
- * Example 2: a generator that throws.
- *
- * webdriver.promise.consume(function* () {
- * yield webdriver.promise.delayed(250).then(function() {
- * throw Error('boom');
- * });
- * }).thenCatch(function(e) {
- * console.log(e.toString()); // Error: boom
- * });
- *
- *
- * @param {!Function} generatorFn The generator function to execute.
- * @param {Object=} opt_self The object to use as "this" when invoking the
- * initial generator.
- * @param {...*} var_args Any arguments to pass to the initial generator.
- * @return {!webdriver.promise.Promise.>} A promise that will resolve to the
- * generator's final result.
- * @throws {TypeError} If the given function is not a generator.
- */
- function consume(generatorFn: Function, opt_self?: any, ...var_args: any[]): webdriver.promise.Promise;
-
- /**
- * Registers an observer on a promised {@code value}, returning a new promise
- * that will be resolved when the value is. If {@code value} is not a promise,
- * then the return promise will be immediately resolved.
- * @param {*} value The value to observe.
- * @param {Function=} opt_callback The function to call when the value is
- * resolved successfully.
- * @param {Function=} opt_errback The function to call when the value is
- * rejected.
- * @return {!webdriver.promise.Promise} A new promise.
- */
- function when(value: T, opt_callback?: (value: T) => any, opt_errback?: (error: any) => any): webdriver.promise.Promise;
- function when(value: webdriver.promise.Promise, opt_callback?: (value: T) => any, opt_errback?: (error: any) => any): webdriver.promise.Promise;
-
- /**
- * Returns a promise that will be resolved with the input value in a
- * fully-resolved state. If the value is an array, each element will be fully
- * resolved. Likewise, if the value is an object, all keys will be fully
- * resolved. In both cases, all nested arrays and objects will also be
- * fully resolved. All fields are resolved in place; the returned promise will
- * resolve on {@code value} and not a copy.
- *
- * Warning: This function makes no checks against objects that contain
- * cyclical references:
- *
- * var value = {};
- * value['self'] = value;
- * webdriver.promise.fullyResolved(value); // Stack overflow.
- *
- * @param {*} value The value to fully resolve.
- * @return {!webdriver.promise.Promise} A promise for a fully resolved version
- * of the input value.
- */
- function fullyResolved(value: any): webdriver.promise.Promise;
-
- /**
- * Changes the default flow to use when no others are active.
- * @param {!webdriver.promise.ControlFlow} flow The new default flow.
- * @throws {Error} If the default flow is not currently active.
- */
- function setDefaultFlow(flow: webdriver.promise.ControlFlow): void;
- }
-
- namespace stacktrace {
- class Frame extends webdriver.stacktrace.Frame { }
- class Snapshot extends webdriver.stacktrace.Snapshot { }
-
- /**
- * Formats an error's stack trace.
- * @param {!(Error|goog.testing.JsUnitException)} error The error to format.
- * @return {!(Error|goog.testing.JsUnitException)} The formatted error.
- */
- function format(error: any): any;
-
- /**
- * Gets the native stack trace if available otherwise follows the call chain.
- * The generated trace will exclude all frames up to and including the call to
- * this function.
- * @return {!Array.} The frames of the stack trace.
- */
- function get(): webdriver.stacktrace.Frame[];
-
- /**
- * Whether the current browser supports stack traces.
- *
- * @type {boolean}
- * @const
- */
- var BROWSER_SUPPORTED: boolean;
- }
-
- namespace until {
- class Condition extends webdriver.until.Condition { }
-
- /**
- * Creates a condition that will wait until the input driver is able to switch
- * to the designated frame. The target frame may be specified as:
- *
- * A numeric index into {@code window.frames} for the currently selected
- * frame.
- * A {@link webdriver.WebElement}, which must reference a FRAME or IFRAME
- * element on the current page.
- * A locator which may be used to first locate a FRAME or IFRAME on the
- * current page before attempting to switch to it.
- *
- *
- * Upon successful resolution of this condition, the driver will be left
- * focused on the new frame.
- *
- * @param {!(number|webdriver.WebElement|
- * webdriver.Locator|webdriver.By.Hash|
- * function(!webdriver.WebDriver): !webdriver.WebElement)} frame
- * The frame identifier.
- * @return {!until.Condition.} A new condition.
- */
- function ableToSwitchToFrame(frame: number): webdriver.until.Condition;
- function ableToSwitchToFrame(frame: webdriver.IWebElement): webdriver.until.Condition;
- function ableToSwitchToFrame(frame: webdriver.Locator): webdriver.until.Condition;
- function ableToSwitchToFrame(frame: (webdriver: webdriver.WebDriver) => webdriver.IWebElement): webdriver.until.Condition;
- function ableToSwitchToFrame(frame: any): webdriver.until.Condition;
-
- /**
- * Creates a condition that waits for an alert to be opened. Upon success, the
- * returned promise will be fulfilled with the handle for the opened alert.
- *
- * @return {!until.Condition.} The new condition.
- */
- function alertIsPresent(): webdriver.until.Condition;
-
- /**
- * Creates a condition that will wait for the given element to be disabled.
- *
- * @param {!webdriver.WebElement} element The element to test.
- * @return {!until.Condition.} The new condition.
- * @see webdriver.WebDriver#isEnabled
- */
- function elementIsDisabled(element: webdriver.IWebElement): webdriver.until.Condition;
-
- /**
- * Creates a condition that will wait for the given element to be enabled.
- *
- * @param {!webdriver.WebElement} element The element to test.
- * @return {!until.Condition.} The new condition.
- * @see webdriver.WebDriver#isEnabled
- */
- function elementIsEnabled(element: webdriver.IWebElement): webdriver.until.Condition;
-
- /**
- * Creates a condition that will wait for the given element to be deselected.
- *
- * @param {!webdriver.WebElement} element The element to test.
- * @return {!until.Condition.} The new condition.
- * @see webdriver.WebDriver#isSelected
- */
- function elementIsNotSelected(element: webdriver.IWebElement): webdriver.until.Condition;
-
- /**
- * Creates a condition that will wait for the given element to be in the DOM,
- * yet not visible to the user.
- *
- * @param {!webdriver.WebElement} element The element to test.
- * @return {!until.Condition.} The new condition.
- * @see webdriver.WebDriver#isDisplayed
- */
- function elementIsNotVisible(element: webdriver.IWebElement): webdriver.until.Condition;
-
- /**
- * Creates a condition that will wait for the given element to be selected.
- * @param {!webdriver.WebElement} element The element to test.
- * @return {!until.Condition.} The new condition.
- * @see webdriver.WebDriver#isSelected
- */
- function elementIsSelected(element: webdriver.IWebElement): webdriver.until.Condition;
-
- /**
- * Creates a condition that will wait for the given element to become visible.
- *
- * @param {!webdriver.WebElement} element The element to test.
- * @return {!until.Condition.} The new condition.
- * @see webdriver.WebDriver#isDisplayed
- */
- function elementIsVisible(element: webdriver.IWebElement): webdriver.until.Condition;
-
- /**
- * Creates a condition that will loop until an element is
- * {@link webdriver.WebDriver#findElement found} with the given locator.
- *
- * @param {!(webdriver.Locator|webdriver.By.Hash|Function)} locator The locator
- * to use.
- * @return {!until.Condition.} The new condition.
- */
- function elementLocated(locator: webdriver.Locator): webdriver.until.Condition;
- function elementLocated(locator: any): webdriver.until.Condition;
-
- /**
- * Creates a condition that will wait for the given element's
- * {@link webdriver.WebDriver#getText visible text} to contain the given
- * substring.
- *
- * @param {!webdriver.WebElement} element The element to test.
- * @param {string} substr The substring to search for.
- * @return {!until.Condition.} The new condition.
- * @see webdriver.WebDriver#getText
- */
- function elementTextContains(element: webdriver.IWebElement, substr: string): webdriver.until.Condition;
-
- /**
- * Creates a condition that will wait for the given element's
- * {@link webdriver.WebDriver#getText visible text} to match the given
- * {@code text} exactly.
- *
- * @param {!webdriver.WebElement} element The element to test.
- * @param {string} text The expected text.
- * @return {!until.Condition.} The new condition.
- * @see webdriver.WebDriver#getText
- */
- function elementTextIs(element: webdriver.IWebElement, text: string): webdriver.until.Condition;
-
- /**
- * Creates a condition that will wait for the given element's
- * {@link webdriver.WebDriver#getText visible text} to match a regular
- * expression.
- *
- * @param {!webdriver.WebElement} element The element to test.
- * @param {!RegExp} regex The regular expression to test against.
- * @return {!until.Condition.} The new condition.
- * @see webdriver.WebDriver#getText
- */
- function elementTextMatches(element: webdriver.IWebElement, regex: RegExp): webdriver.until.Condition;
-
- /**
- * Creates a condition that will loop until at least one element is
- * {@link webdriver.WebDriver#findElement found} with the given locator.
- *
- * @param {!(webdriver.Locator|webdriver.By.Hash|Function)} locator The locator
- * to use.
- * @return {!until.Condition.>} The new
- * condition.
- */
- function elementsLocated(locator: webdriver.Locator): webdriver.until.Condition;
- function elementsLocated(locator: any): webdriver.until.Condition;
-
- /**
- * Creates a condition that will wait for the given element to become stale. An
- * element is considered stale once it is removed from the DOM, or a new page
- * has loaded.
- *
- * @param {!webdriver.WebElement} element The element that should become stale.
- * @return {!until.Condition.} The new condition.
- */
- function stalenessOf(element: webdriver.IWebElement): webdriver.until.Condition;
-
- /**
- * Creates a condition that will wait for the current page's title to contain
- * the given substring.
- *
- * @param {string} substr The substring that should be present in the page
- * title.
- * @return {!until.Condition.} The new condition.
- */
- function titleContains(substr: string): webdriver.until.Condition;
-
- /**
- * Creates a condition that will wait for the current page's title to match the
- * given value.
- *
- * @param {string} title The expected page title.
- * @return {!until.Condition.} The new condition.
- */
- function titleIs(title: string): webdriver.until.Condition;
-
- /**
- * Creates a condition that will wait for the current page's title to match the
- * given regular expression.
- *
- * @param {!RegExp} regex The regular expression to test against.
- * @return {!until.Condition.} The new condition.
- */
- function titleMatches(regex: RegExp): webdriver.until.Condition;
- }
-
- namespace ExpectedConditions {
- /**
- * Negates the result of a promise.
- *
- * @param {webdriver.until.Condition} expectedCondition
- * @return {!webdriver.until.Condition} An expected condition that returns the negated value.
- */
- function not(expectedCondition: webdriver.until.Condition): webdriver.until.Condition;
-
- /**
- * Chain a number of expected conditions using logical_and, short circuiting at the
- * first expected condition that evaluates to false.
- *
- * @param {...webdriver.until.Condition[]} fns An array of expected conditions to 'and' together.
- * @return {!webdriver.until.Condition} An expected condition that returns a promise which evaluates
- * to the result of the logical and.
- */
- function and(...fns: webdriver.until.Condition[]): webdriver.until.Condition;
-
- /**
- * Chain a number of expected conditions using logical_or, short circuiting at the
- * first expected condition that evaluates to true.
- *
- * @param {...webdriver.until.Condition[]} fns An array of expected conditions to 'or' together.
- * @return {!webdriver.until.Condition} An expected condition that returns a promise which
- * evaluates to the result of the logical or.
- */
- function or(...fns: webdriver.until.Condition[]): webdriver.until.Condition;
-
- /**
- * Expect an alert to be present.
- *
- * @return {!webdriver.until.Condition} An expected condition that returns a promise representing
- * whether an alert is present.
- */
- function alertIsPresent(): webdriver.until.Condition;
-
- /**
- * An Expectation for checking an element is visible and enabled such that you can click it.
- *
- * @param {ElementFinder} element The element to check
- * @return {!webdriver.until.Condition} An expected condition that returns a promise representing
- * whether the element is clickable.
- */
- function elementToBeClickable(element: ElementFinder): webdriver.until.Condition;
-
- /**
- * An expectation for checking if the given text is present in the element.
- * Returns false if the elementFinder does not find an element.
- *
- * @param {ElementFinder} element The element to check
- * @param {string} text The text to verify against
- * @return {!webdriver.until.Condition} An expected condition that returns a promise representing
- * whether the text is present in the element.
- */
- function textToBePresentInElement(element: ElementFinder, text: string): webdriver.until.Condition;
-
- /**
- * An expectation for checking if the given text is present in the element’s value.
- * Returns false if the elementFinder does not find an element.
- *
- * @param {ElementFinder} element The element to check
- * @param {string} text The text to verify against
- * @return {!webdriver.until.Condition} An expected condition that returns a promise representing
- * whether the text is present in the element's value.
- */
- function textToBePresentInElementValue(
- element: ElementFinder, text: string
- ): webdriver.until.Condition;
-
- /**
- * An expectation for checking that the title contains a case-sensitive substring.
- *
- * @param {string} title The fragment of title expected
- * @return {!webdriver.until.Condition} An expected condition that returns a promise representing
- * whether the title contains the string.
- */
- function titleContains(title: string): webdriver.until.Condition;
-
- /**
- * An expectation for checking the title of a page.
- *
- * @param {string} title The expected title, which must be an exact match.
- * @return {!webdriver.until.Condition} An expected condition that returns a promise representing
- * whether the title equals the string.
- */
- function titleIs(title: string): webdriver.until.Condition;
-
- /**
- * An expectation for checking that an element is present on the DOM of a page. This does not necessarily
- * mean that the element is visible. This is the opposite of 'stalenessOf'.
- *
- * @param {ElementFinder} elementFinder The element to check
- * @return {!webdriver.until.Condition} An expected condition that returns a promise
- * representing whether the element is present.
- */
- function presenceOf(element: ElementFinder): webdriver.until.Condition;
-
- /**
- * An expectation for checking that an element is not attached to the DOM of a page.
- * This is the opposite of 'presenceOf'.
- *
- * @param {ElementFinder} elementFinder The element to check
- * @return {!webdriver.until.Condition} An expected condition that returns a promise representing
- * whether the element is stale.
- */
- function stalenessOf(element: ElementFinder): webdriver.until.Condition;
-
- /**
- * An expectation for checking that an element is present on the DOM of a page and visible.
- * Visibility means that the element is not only displayed but also has a height and width that is
- * greater than 0. This is the opposite of 'invisibilityOf'.
- *
- * @param {ElementFinder} elementFinder The element to check
- * @return {!webdriver.until.Condition} An expected condition that returns a promise representing
- * whether the element is visible.
- */
- function visibilityOf(element: ElementFinder): webdriver.until.Condition;
-
- /**
- * An expectation for checking that an element is present on the DOM of a page. This does not necessarily
- * mean that the element is visible. This is the opposite of 'stalenessOf'.
- *
- * @param {ElementFinder} elementFinder The element to check
- * @return {!webdriver.until.Condition} An expected condition that returns a promise representing
- * whether the element is invisible.
- */
- function invisibilityOf(element: ElementFinder): webdriver.until.Condition;
-
- /**
- * An expectation for checking the selection is selected.
- *
- * @param {ElementFinder} elementFinder The element to check
- * @return {!webdriver.until.Condition} An expected condition that returns a promise representing
- * whether the element is selected.
- */
- function elementToBeSelected(element: ElementFinder): webdriver.until.Condition;
- }
-
- //endregion
-
- /**
- * Use as: element(locator)
- *
- * The ElementFinder can be treated as a WebElement for most purposes, in
- * particular, you may perform actions (i.e. click, getText) on them as you
- * would a WebElement. ElementFinders extend Promise, and once an action
- * is performed on an ElementFinder, the latest result from the chain can be
- * accessed using then. Unlike a WebElement, an ElementFinder will wait for
- * angular to settle before performing finds or actions.
- *
- * ElementFinder can be used to build a chain of locators that is used to find
- * an element. An ElementFinder does not actually attempt to find the element
- * until an action is called, which means they can be set up in helper files
- * before the page is available.
- *
- * @param {webdriver.Locator} locator An element locator.
- * @return {ElementFinder}
- */
- interface Element {
- (locator: webdriver.Locator): ElementFinder;
-
- /**
- * ElementArrayFinder is used for operations on an array of elements (as opposed
- * to a single element).
- *
- * @param {webdriver.Locator} locator An element locator.
- * @return {ElementArrayFinder}
- */
- all(locator: webdriver.Locator): ElementArrayFinder;
- }
-
- interface ElementFinder extends webdriver.IWebElement, webdriver.promise.IThenable {
- /**
- * Calls to element may be chained to find elements within a parent.
- *
- * @alias element(locator).element(locator)
- * @view
- *
- *
- * Child text
- *
{{person.phone}}
- *
- *
- *
- * @example
- * // Chain 2 element calls.
- * var child = element(by.css('.parent')).
- * element(by.css('.child'));
- * expect(child.getText()).toBe('Child text\n555-123-4567');
- *
- * // Chain 3 element calls.
- * var triple = element(by.css('.parent')).
- * element(by.css('.child')).
- * element(by.binding('person.phone'));
- * expect(triple.getText()).toBe('555-123-4567');
- *
- * @param {webdriver.Locator} subLocator
- * @return {ElementFinder}
- */
- element(subLocator: webdriver.Locator): ElementFinder;
-
- /**
- * Calls to element may be chained to find an array of elements within a parent.
- *
- * @alias element(locator).all(locator)
- * @view
- *
- *
- * First
- * Second
- * Third
- *
- *
- *
- * @example
- * var items = element(by.css('.parent')).all(by.tagName('li'))
- *
- * @param {webdriver.Locator} subLocator
- * @return {ElementArrayFinder}
- */
- all(subLocator: webdriver.Locator): ElementArrayFinder;
-
- /**
- * Shortcut for querying the document directly with css.
- *
- * @alias $(cssSelector)
- * @view
- *
- * First
- * Second
- *
- *
- * @example
- * var item = $('.count .two');
- * expect(item.getText()).toBe('Second');
- *
- * @param {string} selector A css selector
- * @return {ElementFinder} which identifies the located
- * {@link webdriver.WebElement}
- */
- $(selector: string): ElementFinder;
-
- /**
- * Shortcut for querying the document directly with css.
- *
- * @alias $$(cssSelector)
- * @view
- *
- * First
- * Second
- *
- *
- * @example
- * // The following protractor expressions are equivalent.
- * var list = element.all(by.css('.count span'));
- * expect(list.count()).toBe(2);
- *
- * list = $$('.count span');
- * expect(list.count()).toBe(2);
- * expect(list.get(0).getText()).toBe('First');
- * expect(list.get(1).getText()).toBe('Second');
- *
- * @param {string} selector a css selector
- * @return {ElementArrayFinder} which identifies the
- * array of the located {@link webdriver.WebElement}s.
- */
- $$(selector: string): ElementArrayFinder;
-
- /**
- * Determine whether the element is present on the page.
- *
- * @view
- * {{person.name}}
- *
- * @example
- * // Element exists.
- * expect(element(by.binding('person.name')).isPresent()).toBe(true);
- *
- * // Element not present.
- * expect(element(by.binding('notPresent')).isPresent()).toBe(false);
- *
- * @return {ElementFinder} which resolves to whether
- * the element is present on the page.
- */
- isPresent(): webdriver.promise.Promise;
-
- /**
- * Override for WebElement.prototype.isElementPresent so that protractor waits
- * for Angular to settle before making the check.
- *
- * @see ElementFinder.isPresent
- *
- * @param {webdriver.Locator} subLocator Locator for element to look for.
- * @return {ElementFinder} which resolves to whether
- * the element is present on the page.
- */
- isElementPresent(subLocator: webdriver.Locator): webdriver.promise.Promise;
-
- /**
- * @see ElementArrayFinder.prototype.locator
- *
- * @return {webdriver.Locator}
- */
- locator(): webdriver.Locator;
-
- /**
- * Returns the WebElement represented by this ElementFinder.
- * Throws the WebDriver error if the element doesn't exist.
- *
- * @example
- * The following three expressions are equivalent.
- * element(by.css('.parent')).getWebElement();
- * browser.waitForAngular(); browser.driver.findElement(by.css('.parent'));
- * browser.findElement(by.css('.parent'));
- *
- * @alias element(locator).getWebElement()
- * @return {webdriver.WebElement}
- */
- getWebElement(): webdriver.WebElement;
-
- /**
- * Evaluates the input as if it were on the scope of the current element.
- * @see ElementArrayFinder.evaluate
- *
- * @param {string} expression
- *
- * @return {ElementFinder} which resolves to the evaluated expression.
- */
- evaluate(expression: string): ElementFinder;
-
- /**
- * @see ElementArrayFinder.prototype.allowAnimations.
- * @param {string} value
- *
- * @return {ElementFinder} which resolves to whether animation is allowed.
- */
- allowAnimations(value: string): ElementFinder;
-
- /**
- * Create a shallow copy of ElementFinder.
- *
- * @return {!ElementFinder} A shallow copy of this.
- */
- clone(): ElementFinder;
- }
-
- interface ElementArrayFinder extends webdriver.promise.IThenable {
- /**
- * Returns the elements as an array of WebElements.
- */
- getWebElements(): webdriver.WebElement[];
-
-
- /**
- * Get an element within the ElementArrayFinder by index. The index starts at 0.
- * Negative indices are wrapped (i.e. -i means ith element from last)
- * This does not actually retrieve the underlying element.
- *
- * @alias element.all(locator).get(index)
- * @view
- *
- * First
- * Second
- * Third
- *
- *
- * @example
- * var list = element.all(by.css('.items li'));
- * expect(list.get(0).getText()).toBe('First');
- * expect(list.get(1).getText()).toBe('Second');
- *
- * @param {number} index Element index.
- * @return {ElementFinder} finder representing element at the given index.
- */
- get(index: number): ElementFinder;
-
- /**
- * Get the first matching element for the ElementArrayFinder. This does not
- * actually retrieve the underlying element.
- *
- * @alias element.all(locator).first()
- * @view
- *
- * First
- * Second
- * Third
- *
- *
- * @example
- * var first = element.all(by.css('.items li')).first();
- * expect(first.getText()).toBe('First');
- *
- * @return {ElementFinder} finder representing the first matching element
- */
- first(): ElementFinder;
-
- /**
- * Get the last matching element for the ElementArrayFinder. This does not
- * actually retrieve the underlying element.
- *
- * @alias element.all(locator).last()
- * @view
- *
- * First
- * Second
- * Third
- *
- *
- * @example
- * var last = element.all(by.css('.items li')).last();
- * expect(last.getText()).toBe('Third');
- *
- * @return {ElementFinder} finder representing the last matching element
- */
- last(): ElementFinder;
-
- /**
- * Count the number of elements represented by the ElementArrayFinder.
- *
- * @alias element.all(locator).count()
- * @view
- *
- * First
- * Second
- * Third
- *
- *
- * @example
- * var list = element.all(by.css('.items li'));
- * expect(list.count()).toBe(3);
- *
- * @return {!webdriver.promise.Promise} A promise which resolves to the
- * number of elements matching the locator.
- */
- count(): webdriver.promise.Promise;
-
- /**
- * Calls the input function on each ElementFinder represented by the ElementArrayFinder.
- *
- * @alias element.all(locator).each(eachFunction)
- * @view
- *
- * First
- * Second
- * Third
- *
- *
- * @example
- * element.all(by.css('.items li')).each(function(element) {
- * // Will print First, Second, Third.
- * element.getText().then(console.log);
- * });
- *
- * @param {function(ElementFinder)} fn Input function
- */
- each(fn: (element: ElementFinder, index: number) => void): void;
-
- /**
- * Apply a map function to each element within the ElementArrayFinder. The
- * callback receives the ElementFinder as the first argument and the index as
- * a second arg.
- *
- * @alias element.all(locator).map(mapFunction)
- * @view
- *
- * First
- * Second
- * Third
- *
- *
- * @example
- * var items = element.all(by.css('.items li')).map(function(elm, index) {
- * return {
- * index: index,
- * text: elm.getText(),
- * class: elm.getAttribute('class')
- * };
- * });
- * expect(items).toEqual([
- * {index: 0, text: 'First', class: 'one'},
- * {index: 1, text: 'Second', class: 'two'},
- * {index: 2, text: 'Third', class: 'three'}
- * ]);
- *
- * @param {function(ElementFinder, number)} mapFn Map function that
- * will be applied to each element.
- * @return {!webdriver.promise.Promise} A promise that resolves to an array
- * of values returned by the map function.
- */
- map(mapFn: (element: ElementFinder, index: number) => T): webdriver.promise.Promise;
- map(mapFn: (element: ElementFinder, index: number) => T2): webdriver.promise.Promise;
-
- /**
- * Apply a filter function to each element within the ElementArrayFinder. Returns
- * a new ElementArrayFinder with all elements that pass the filter function. The
- * filter function receives the ElementFinder as the first argument
- * and the index as a second arg.
- * This does not actually retrieve the underlying list of elements, so it can
- * be used in page objects.
- *
- * @alias element.all(locator).filter(filterFn)
- * @view
- *
- * First
- * Second
- * Third
- *
- *
- * @example
- * element.all(by.css('.items li')).filter(function(elem, index) {
- * return elem.getText().then(function(text) {
- * return text === 'Third';
- * });
- * }).then(function(filteredElements) {
- * filteredElements[0].click();
- * });
- *
- * @param {function(ElementFinder, number): webdriver.WebElement.Promise} filterFn
- * Filter function that will test if an element should be returned.
- * filterFn can either return a boolean or a promise that resolves to a boolean.
- * @return {!ElementArrayFinder} A ElementArrayFinder that represents an array
- * of element that satisfy the filter function.
- */
- filter(filterFn: (element: ElementFinder, index: number) => any): ElementArrayFinder;
-
- /**
- * Apply a reduce function against an accumulator and every element found
- * using the locator (from left-to-right). The reduce function has to reduce
- * every element into a single value (the accumulator). Returns promise of
- * the accumulator. The reduce function receives the accumulator, current
- * ElementFinder, the index, and the entire array of ElementFinders,
- * respectively.
- *
- * @alias element.all(locator).reduce(reduceFn)
- * @view
- *
- * First
- * Second
- * Third
- *
- *
- * @example
- * var value = element.all(by.css('.items li')).reduce(function(acc, elem) {
- * return elem.getText().then(function(text) {
- * return acc + text + ' ';
- * });
- * });
- *
- * expect(value).toEqual('First Second Third ');
- *
- * @param {function(number, ElementFinder, number, Array.)}
- * reduceFn Reduce function that reduces every element into a single value.
- * @param {*} initialValue Initial value of the accumulator.
- * @return {!webdriver.promise.Promise} A promise that resolves to the final
- * value of the accumulator.
- */
- reduce(reduceFn: (acc: T, element: ElementFinder, index: number, arr: ElementFinder[]) => webdriver.promise.Promise, initialValue: T): webdriver.promise.Promise;
- reduce(reduceFn: (acc: T, element: ElementFinder, index: number, arr: ElementFinder[]) => T, initialValue: T): webdriver.promise.Promise;
-
- /**
- * Represents the ElementArrayFinder as an array of ElementFinders.
- *
- * @return {Array.} Return a promise, which resolves to a list
- * of ElementFinders specified by the locator.
- */
- asElementFinders_(): webdriver.promise.Promise;
-
- /**
- * Create a shallow copy of ElementArrayFinder.
- *
- * @return {!ElementArrayFinder} A shallow copy of this.
- */
- clone(): ElementArrayFinder;
-
- /**
- * Calls to ElementArrayFinder may be chained to find an array of elements
- * using the current elements in this ElementArrayFinder as the starting point.
- * This function returns a new ElementArrayFinder which would contain the
- * children elements found (and could also be empty).
- *
- * @alias element.all(locator).all(locator)
- * @view
- *
- *
- *
- * @example
- * var foo = element.all(by.css('.parent')).all(by.css('.foo'))
- * expect(foo.getText()).toEqual(['1a', '2a'])
- * var baz = element.all(by.css('.parent')).all(by.css('.baz'))
- * expect(baz.getText()).toEqual(['1b'])
- * var nonexistent = element.all(by.css('.parent')).all(by.css('.NONEXISTENT'))
- * expect(nonexistent.getText()).toEqual([''])
- *
- * @param {webdriver.Locator} subLocator
- * @return {ElementArrayFinder}
- */
- all(locator: webdriver.Locator): ElementArrayFinder;
-
- /**
- * Shorthand function for finding arrays of elements by css.
- *
- * @type {function(string): ElementArrayFinder}
- */
- $$(selector: string): ElementArrayFinder;
-
- /**
- * Returns an ElementFinder representation of ElementArrayFinder. It ensures
- * that the ElementArrayFinder resolves to one and only one underlying element.
- *
- * @return {ElementFinder} An ElementFinder representation
- * @private
- */
- toElementFinder_(): ElementFinder;
-
- /**
- * Returns the most relevant locator.
- *
- * @example
- * $('#ID1').locator() // returns by.css('#ID1')
- * $('#ID1').$('#ID2').locator() // returns by.css('#ID2')
- * $$('#ID1').filter(filterFn).get(0).click().locator() // returns by.css('#ID1')
- *
- * @return {webdriver.Locator}
- */
- locator(): webdriver.Locator;
-
- /**
- * Evaluates the input as if it were on the scope of the current underlying
- * elements.
- *
- * @view
- * {{variableInScope}}
- *
- * @example
- * var value = element(by.id('foo')).evaluate('variableInScope');
- *
- * @param {string} expression
- *
- * @return {ElementArrayFinder} which resolves to the
- * evaluated expression for each underlying element.
- * The result will be resolved as in
- * {@link webdriver.WebDriver.executeScript}. In summary - primitives will
- * be resolved as is, functions will be converted to string, and elements
- * will be returned as a WebElement.
- */
- evaluate(expression: string): ElementArrayFinder;
-
- /**
- * Determine if animation is allowed on the current underlying elements.
- * @param {string} value
- *
- * @example
- * // Turns off ng-animate animations for all elements in the
- * element(by.css('body')).allowAnimations(false);
- *
- * @return {ElementArrayFinder} which resolves to whether animation is allowed.
- */
- allowAnimations(value: boolean): ElementArrayFinder;
-
- /**
- * Schedules a command to click on this element.
- * @return {!webdriver.promise.Promise} A promise that will be resolved when
- * the click command has completed.
- */
- click(): webdriver.promise.Promise;
-
- /**
- * Schedules a command to type a sequence on the DOM element represented by this
- * instance.
- *
- * Modifier keys (SHIFT, CONTROL, ALT, META) are stateful; once a modifier is
- * processed in the keysequence, that key state is toggled until one of the
- * following occurs:
- *
- * The modifier key is encountered again in the sequence. At this point the
- * state of the key is toggled (along with the appropriate keyup/down events).
- *
- * The {@code webdriver.Key.NULL} key is encountered in the sequence. When
- * this key is encountered, all modifier keys current in the down state are
- * released (with accompanying keyup events). The NULL key can be used to
- * simulate common keyboard shortcuts:
- *
- * element.sendKeys("text was",
- * webdriver.Key.CONTROL, "a", webdriver.Key.NULL,
- * "now text is");
- * // Alternatively:
- * element.sendKeys("text was",
- * webdriver.Key.chord(webdriver.Key.CONTROL, "a"),
- * "now text is");
- *
- * The end of the keysequence is encountered. When there are no more keys
- * to type, all depressed modifier keys are released (with accompanying keyup
- * events).
- *
- *
- * Note: On browsers where native keyboard events are not yet
- * supported (e.g. Firefox on OS X), key events will be synthesized. Special
- * punctionation keys will be synthesized according to a standard QWERTY en-us
- * keyboard layout.
- *
- * @param {...string} var_args The sequence of keys to
- * type. All arguments will be joined into a single sequence (var_args is
- * permitted for convenience).
- * @return {!webdriver.promise.Promise} A promise that will be resolved when all
- * keys have been typed.
- */
- sendKeys(...var_args: string[]): webdriver.promise.Promise;
-
- /**
- * Schedules a command to query for the tag/node name of this element.
- * @return {!webdriver.promise.Promise} A promise that will be resolved with the
- * element's tag name.
- */
- getTagName(): webdriver.promise.Promise;
-
- /**
- * Schedules a command to query for the computed style of the element
- * represented by this instance. If the element inherits the named style from
- * its parent, the parent will be queried for its value. Where possible, color
- * values will be converted to their hex representation (e.g. #00ff00 instead of
- * rgb(0, 255, 0)).
- *
- * Warning: the value returned will be as the browser interprets it, so
- * it may be tricky to form a proper assertion.
- *
- * @param {string} cssStyleProperty The name of the CSS style property to look
- * up.
- * @return {!webdriver.promise.Promise} A promise that will be resolved with the
- * requested CSS value.
- */
- getCssValue(cssStyleProperty: string): webdriver.promise.Promise;
-
- /**
- * Schedules a command to query for the value of the given attribute of the
- * element. Will return the current value even if it has been modified after the
- * page has been loaded. More exactly, this method will return the value of the
- * given attribute, unless that attribute is not present, in which case the
- * value of the property with the same name is returned. If neither value is
- * set, null is returned. The "style" attribute is converted as best can be to a
- * text representation with a trailing semi-colon. The following are deemed to
- * be "boolean" attributes and will be returned as thus:
- *
- * async, autofocus, autoplay, checked, compact, complete, controls, declare,
- * defaultchecked, defaultselected, defer, disabled, draggable, ended,
- * formnovalidate, hidden, indeterminate, iscontenteditable, ismap, itemscope,
- * loop, multiple, muted, nohref, noresize, noshade, novalidate, nowrap, open,
- * paused, pubdate, readonly, required, reversed, scoped, seamless, seeking,
- * selected, spellcheck, truespeed, willvalidate
- *
- *
Finally, the following commonly mis-capitalized attribute/property names
- * are evaluated as expected:
- *
- * "class"
- * "readonly"
- *
- * @param {string} attributeName The name of the attribute to query.
- * @return {!webdriver.promise.Promise} A promise that will be resolved with the
- * attribute's value.
- */
- getAttribute(attributeName: string): webdriver.promise.Promise;
-
- /**
- * Get the visible (i.e. not hidden by CSS) innerText of this element, including
- * sub-elements, without any leading or trailing whitespace.
- * @return {!webdriver.promise.Promise} A promise that will be resolved with the
- * element's visible text.
- */
- getText(): webdriver.promise.Promise;
-
- /**
- * Schedules a command to compute the size of this element's bounding box, in
- * pixels.
- * @return {!webdriver.promise.Promise} A promise that will be resolved with the
- * element's size as a {@code {width:number, height:number}} object.
- */
- getSize(): webdriver.promise.Promise