feat: angular 4 compatibility (#3608)

* Bumps the required Angular version to 4 and TypeScript to 2.1.6.
* Fixes deprecation warnings for `<template>` usages.
* Fixes any unit and e2e test failures.
* Includes the new animations module and switches any components that use animations to it.
* Fixes issues with the various test apps.

Fixes #3357.
Fixes #3336.
FIxes #3301.
This commit is contained in:
Kristiyan Kostadinov
2017-03-23 22:46:47 +01:00
committed by Jeremy Elbourn
parent c524438b70
commit cd55082a48
53 changed files with 373 additions and 267 deletions
+61 -22
View File
@@ -1,20 +1,55 @@
For help getting started with a new Angular app, check out the [Angular CLI](https://cli.angular.io/).
For help getting started with a new Angular app, check out the
[Angular CLI](https://cli.angular.io/).
For existing apps, follow these steps to begin using Angular Material.
## Step 1: Install Angular Material
## Step 1: Install Angular Material
```bash
npm install --save @angular/material
```
## Step 2: Import the Module
Add MaterialModule as an import in your app's root NgModule.
## Step 2: Animations
Some Material components depend on the Angular animations module in order to be able to do
more advanced transitions. If you want these animations to work in your app, you have to
install the `@angular/animations` module and include the `BrowserAnimationsModule` in your app.
```bash
npm install --save @angular/animations
```
```ts
import { MaterialModule } from '@angular/material';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
@NgModule({
...
imports: [BrowserAnimationsModule],
...
})
export class PizzaPartyAppModule { }
```
If you don't want to add another dependency to your project, you can use the `NoopAnimationsModule`.
```ts
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
@NgModule({
...
imports: [NoopAnimationsModule],
...
})
export class PizzaPartyAppModule { }
```
## Step 3: Import the Module
Add MaterialModule as an import in your app's root NgModule.
```ts
import {MaterialModule} from '@angular/material';
@NgModule({
...
imports: [MaterialModule],
@@ -23,9 +58,9 @@ import { MaterialModule } from '@angular/material';
export class PizzaPartyAppModule { }
```
## Step 3: Include Theming
## Step 4: Include Theming
Including a theme is **required** to apply all of the core and theme styles to your application.
Including a theme is **required** to apply all of the core and theme styles to your application.
To get started with a prebuilt theme, include the following in your app's index.html:
@@ -35,16 +70,17 @@ To get started with a prebuilt theme, include the following in your app's index.
Note that your app's project structure may have a different relative location for your node_modules.
For more information on theming and instructions on how to create a custom theme, see the [theming guide](./theming.md).
For more information on theming and instructions on how to create a custom theme, see the
[theming guide](./theming.md).
## Step 4: Gesture Support
## Step 5: Gesture Support
Some components (`md-slide-toggle`, `md-slider`, `mdTooltip`) rely on
Some components (`md-slide-toggle`, `md-slider`, `mdTooltip`) rely on
[HammerJS](http://hammerjs.github.io/) for gestures. In order to get the full feature-set of these
components, HammerJS must be loaded into the application.
You can add HammerJS to your application via [npm](https://www.npmjs.com/package/hammerjs), a CDN
(such as the [Google CDN](https://developers.google.com/speed/libraries/#hammerjs)), or served
You can add HammerJS to your application via [npm](https://www.npmjs.com/package/hammerjs), a CDN
(such as the [Google CDN](https://developers.google.com/speed/libraries/#hammerjs)), or served
directly from your app.
To install via npm, use the following command:
@@ -57,22 +93,25 @@ After installing, import it on your app's root module.
import 'hammerjs';
```
## Step 5 (Optional): Add Material Icons
## Step 6 (Optional): Add Material Icons
If you want your `md-icon` components to use [Material Icons](https://material.io/icons/),
load the font in your `index.html`.
If you want your `md-icon` components to use [Material Icons](https://material.io/icons/), load the font in your `index.html`.
```html
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
```
For more information on using Material Icons, check out the [Material Icons Guide](https://google.github.io/material-design-icons/).
For more information on using Material Icons, check out the
[Material Icons Guide](https://google.github.io/material-design-icons/).
Note that `md-icon` has support for any font or svg icons, so using Material Icons is
just one option.
Note that `md-icon` has support for any font or svg icons, so using Material Icons is just one option.
## Configuring SystemJS
If your project is using SystemJS for module loading, you will need to add `@angular/material`
If your project is using SystemJS for module loading, you will need to add `@angular/material`
to the SystemJS configuration:
```js
+13 -12
View File
@@ -25,22 +25,23 @@
"node": ">= 5.4.1 < 7"
},
"dependencies": {
"@angular/common": "^2.3.0",
"@angular/compiler": "^2.3.0",
"@angular/core": "^2.3.0",
"@angular/forms": "^2.3.0",
"@angular/http": "^2.3.0",
"@angular/platform-browser": "^2.3.0",
"@angular/animations": "^4.0.0-rc.5",
"@angular/common": "^4.0.0-rc.5",
"@angular/compiler": "^4.0.0-rc.5",
"@angular/core": "^4.0.0-rc.5",
"@angular/forms": "^4.0.0-rc.5",
"@angular/http": "^4.0.0-rc.5",
"@angular/platform-browser": "^4.0.0-rc.5",
"core-js": "^2.4.1",
"rxjs": "^5.0.1",
"systemjs": "0.19.43",
"zone.js": "^0.7.2"
"zone.js": "^0.8.4"
},
"devDependencies": {
"@angular/compiler-cli": "^2.3.0",
"@angular/platform-browser-dynamic": "^2.3.0",
"@angular/platform-server": "^2.3.0",
"@angular/router": "^3.3.0",
"@angular/compiler-cli": "^4.0.0-rc.5",
"@angular/platform-browser-dynamic": "^4.0.0-rc.5",
"@angular/platform-server": "^4.0.0-rc.5",
"@angular/router": "^4.0.0-rc.5",
"@types/chalk": "^0.4.31",
"@types/fs-extra": "0.0.37",
"@types/glob": "^5.0.30",
@@ -106,7 +107,7 @@
"ts-node": "^2.1.0",
"tslint": "^4.4.2",
"tslint-no-unused-var": "0.0.6",
"typescript": "~2.0.10",
"typescript": "~2.1.6",
"uglify-js": "^2.8.7",
"web-animations-js": "^2.2.2"
}
+4 -1
View File
@@ -2,8 +2,9 @@ import {NgModule, ApplicationRef} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {HttpModule} from '@angular/http';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import {DemoApp, Home} from './demo-app/demo-app';
import {RouterModule} from '@angular/router';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {DemoApp, Home} from './demo-app/demo-app';
import {
MaterialModule,
OverlayContainer,
@@ -43,9 +44,11 @@ import {AutocompleteDemo} from './autocomplete/autocomplete-demo';
import {InputDemo} from './input/input-demo';
import {StyleDemo} from './style/style-demo';
@NgModule({
imports: [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
HttpModule,
ReactiveFormsModule,
+2 -2
View File
@@ -67,6 +67,6 @@
<p>Last close result: {{lastCloseResult}}</p>
<template>
<ng-template>
I'm a template dialog. I've been opened {{numTemplateOpens}} times!
</template>
</ng-template>
+4 -4
View File
@@ -15,16 +15,16 @@
Open menu
</button>
<template cdk-connected-overlay [origin]="trigger" [width]="500" hasBackdrop [open]="isMenuOpen"
<ng-template cdk-connected-overlay [origin]="trigger" [width]="500" hasBackdrop [open]="isMenuOpen"
(backdropClick)="isMenuOpen=false">
<div style="background-color: mediumpurple" >
This is the menu panel.
</div>
</template>
</ng-template>
<!-- Template to load into an overlay. -->
<template cdk-portal>
<ng-template cdk-portal>
<p class="demo-fusilli"> Fusilli </p>
</template>
</ng-template>
<button (click)="openPanelWithBackdrop()">Backdrop panel</button>
+4 -4
View File
@@ -1,6 +1,6 @@
<h2> The portal host is here: </h2>
<div class="demo-portal-host">
<template [cdkPortalHost]="selectedPortal"></template>
<ng-template [cdkPortalHost]="selectedPortal"></ng-template>
</div>
<button type="button" (click)="selectedPortal = programmingJoke">
@@ -15,15 +15,15 @@
Science joke
</button>
<!-- Template vars on <template> elements can't be accessed _in_ the template because Angular
<!-- Template vars on <ng-template> elements can't be accessed _in_ the template because Angular
doesn't support grabbing the instance / TemplateRef this way because the variable may be
referring to something *in* the template (such as #item in ngFor). As such, the component
has to use @ViewChild / @ViewChildren to get these references.
See https://github.com/angular/angular/issues/7158 -->
<template cdk-portal>
<ng-template cdk-portal>
<p> - Why don't jokes work in octal?</p>
<p> - Because 7 10 11.</p>
</template>
</ng-template>
<div *cdk-portal>
<p> - Did you hear about this year's Fibonacci Conference? </p>
+4
View File
@@ -14,7 +14,11 @@ System.config({
'@angular/http': 'vendor/@angular/http/bundles/http.umd.js',
'@angular/forms': 'vendor/@angular/forms/bundles/forms.umd.js',
'@angular/router': 'vendor/@angular/router/bundles/router.umd.js',
'@angular/animations': 'vendor/@angular/animations/bundles/animations.umd.js',
'@angular/animations/browser': 'vendor/@angular/animations/bundles/animations-browser.umd.js',
'@angular/platform-browser': 'vendor/@angular/platform-browser/bundles/platform-browser.umd.js',
'@angular/platform-browser/animations':
'vendor/@angular/platform-browser/bundles/platform-browser-animations.umd.js',
'@angular/platform-browser-dynamic':
'vendor/@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
'@angular/material': '@angular/material/bundles/material.umd.js'
+5 -5
View File
@@ -48,7 +48,7 @@
<md-tab-group class="demo-tab-group" dynamicHeight [(selectedIndex)]="activeTabIndex">
<md-tab *ngFor="let tab of dynamicTabs" [disabled]="tab.disabled">
<template md-tab-label>{{tab.label}}</template>
<ng-template md-tab-label>{{tab.label}}</ng-template>
<div class="tab-content">
{{tab.content}}
<br>
@@ -100,7 +100,7 @@
<md-tab-group class="demo-tab-group" dynamicHeight>
<md-tab *ngFor="let tab of tabs" [disabled]="tab.disabled">
<template md-tab-label>{{tab.label}}</template>
<ng-template md-tab-label>{{tab.label}}</ng-template>
<div class="tab-content">
{{tab.content}}
<br>
@@ -146,7 +146,7 @@
<md-tab-group class="demo-tab-group" style="height: 220px">
<md-tab *ngFor="let tab of tabs" [disabled]="tab.disabled">
<template md-tab-label>{{tab.label}}</template>
<ng-template md-tab-label>{{tab.label}}</ng-template>
<div class="tab-content">
{{tab.content}}
<br>
@@ -191,7 +191,7 @@
<md-tab-group class="demo-tab-group" style="height: 200px" md-stretch-tabs>
<md-tab *ngFor="let tab of tabs" [disabled]="tab.disabled">
<template md-tab-label>{{tab.label}}</template>
<ng-template md-tab-label>{{tab.label}}</ng-template>
<div class="tab-content">
{{tab.content}}
</div>
@@ -203,7 +203,7 @@
<md-tab-group class="demo-tab-group">
<md-tab *ngFor="let tab of asyncTabs | async; let i = index" [disabled]="i == 1">
<template md-tab-label>{{tab.label}}</template>
<ng-template md-tab-label>{{tab.label}}</ng-template>
<div class="tab-content">
{{tab.content}}
+1 -1
View File
@@ -2,4 +2,4 @@
<button id="disabled" (click)="openDisabled()">DISABLED</button>
<button id="template" (click)="openTemplate()">TEMPLATE</button>
<template><div class="my-template-dialog">my template dialog</div></template>
<ng-template><div class="my-template-dialog">my template dialog</div></ng-template>
+3 -2
View File
@@ -1,5 +1,6 @@
import {NgModule} from '@angular/core';
import {BrowserModule, AnimationDriver} from '@angular/platform-browser';
import {BrowserModule} from '@angular/platform-browser';
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
import {RouterModule} from '@angular/router';
import {SimpleCheckboxes} from './checkbox/checkbox-e2e';
import {E2EApp, Home} from './e2e-app/e2e-app';
@@ -23,6 +24,7 @@ import {SlideToggleE2E} from './slide-toggle/slide-toggle-e2e';
BrowserModule,
RouterModule.forRoot(E2E_APP_ROUTES),
MaterialModule.forRoot(),
NoopAnimationsModule,
],
declarations: [
E2EApp,
@@ -45,7 +47,6 @@ import {SlideToggleE2E} from './slide-toggle/slide-toggle-e2e';
],
bootstrap: [E2EApp],
providers: [
{provide: AnimationDriver, useValue: AnimationDriver.NOOP},
{provide: OverlayContainer, useClass: FullscreenOverlayContainer}
],
entryComponents: [TestDialog, TestDialogFullScreen]
+4
View File
@@ -14,6 +14,10 @@ System.config({
'@angular/http': 'vendor/@angular/http/bundles/http.umd.js',
'@angular/forms': 'vendor/@angular/forms/bundles/forms.umd.js',
'@angular/router': 'vendor/@angular/router/bundles/router.umd.js',
'@angular/animations': 'vendor/@angular/animations/bundles/animations.umd.js',
'@angular/animations/browser': 'vendor/@angular/animations/bundles/animations-browser.umd.js',
'@angular/platform-browser/animations':
'vendor/@angular/platform-browser/bundles/platform-browser-animations.umd.js',
'@angular/platform-browser': 'vendor/@angular/platform-browser/bundles/platform-browser.umd.js',
'@angular/platform-browser/testing':
'vendor/@angular/platform-browser/bundles/platform-browser-testing.umd.js',
+3 -3
View File
@@ -1,15 +1,15 @@
<section>
<md-tab-group>
<md-tab>
<template md-tab-label>One</template>
<ng-template md-tab-label>One</ng-template>
First tab's content
</md-tab>
<md-tab>
<template md-tab-label>Two</template>
<ng-template md-tab-label>Two</ng-template>
Second tab's content
</md-tab>
<md-tab>
<template md-tab-label>Three</template>
<ng-template md-tab-label>Three</ng-template>
Third tab's content
</md-tab>
</md-tab-group>
+2 -2
View File
@@ -1,5 +1,5 @@
<template>
<ng-template>
<div class="mat-autocomplete-panel" role="listbox" [id]="id" [ngClass]="_getClassList()" #panel>
<ng-content></ng-content>
</div>
</template>
</ng-template>
+41 -30
View File
@@ -113,7 +113,7 @@ describe('MdCheckbox', () => {
expect(inputElement.indeterminate).toBe(false);
});
it('should set indeterminate to false when set checked', () => {
it('should set indeterminate to false when set checked', async(() => {
testComponent.isIndeterminate = true;
fixture.detectChanges();
@@ -124,27 +124,34 @@ describe('MdCheckbox', () => {
testComponent.isChecked = true;
fixture.detectChanges();
expect(checkboxInstance.checked).toBe(true);
expect(inputElement.indeterminate).toBe(false);
expect(inputElement.checked).toBe(true);
expect(testComponent.isIndeterminate).toBe(false);
fixture.whenStable().then(() => {
fixture.detectChanges();
expect(checkboxInstance.checked).toBe(true);
expect(inputElement.indeterminate).toBe(false);
expect(inputElement.checked).toBe(true);
expect(testComponent.isIndeterminate).toBe(false);
testComponent.isIndeterminate = true;
fixture.detectChanges();
testComponent.isIndeterminate = true;
fixture.detectChanges();
expect(checkboxInstance.indeterminate).toBe(true);
expect(inputElement.indeterminate).toBe(true);
expect(inputElement.checked).toBe(true);
expect(testComponent.isIndeterminate).toBe(true);
expect(checkboxInstance.indeterminate).toBe(true);
expect(inputElement.indeterminate).toBe(true);
expect(inputElement.checked).toBe(true);
expect(testComponent.isIndeterminate).toBe(true);
testComponent.isChecked = false;
fixture.detectChanges();
testComponent.isChecked = false;
fixture.detectChanges();
expect(checkboxInstance.checked).toBe(false);
expect(inputElement.indeterminate).toBe(false);
expect(inputElement.checked).toBe(false);
expect(testComponent.isIndeterminate).toBe(false);
});
fixture.whenStable().then(() => {
fixture.detectChanges();
expect(checkboxInstance.checked).toBe(false);
expect(inputElement.indeterminate).toBe(false);
expect(inputElement.checked).toBe(false);
expect(testComponent.isIndeterminate).toBe(false);
});
});
}));
it('should change native element checked when check programmatically', () => {
expect(inputElement.checked).toBe(false);
@@ -169,7 +176,7 @@ describe('MdCheckbox', () => {
expect(checkboxInstance.checked).toBe(false);
});
it('should change from indeterminate to checked on click', () => {
it('should change from indeterminate to checked on click', async(() => {
testComponent.isChecked = false;
testComponent.isIndeterminate = true;
fixture.detectChanges();
@@ -179,15 +186,17 @@ describe('MdCheckbox', () => {
checkboxInstance._onInputClick(<Event>{stopPropagation: () => {}});
expect(checkboxInstance.checked).toBe(true);
expect(checkboxInstance.indeterminate).toBe(false);
fixture.whenStable().then(() => {
expect(checkboxInstance.checked).toBe(true);
expect(checkboxInstance.indeterminate).toBe(false);
checkboxInstance._onInputClick(<Event>{stopPropagation: () => {}});
fixture.detectChanges();
checkboxInstance._onInputClick(<Event>{stopPropagation: () => {}});
fixture.detectChanges();
expect(checkboxInstance.checked).toBe(false);
expect(checkboxInstance.indeterminate).toBe(false);
});
expect(checkboxInstance.checked).toBe(false);
expect(checkboxInstance.indeterminate).toBe(false);
});
}));
it('should add and remove disabled state', () => {
expect(checkboxInstance.disabled).toBe(false);
@@ -219,16 +228,18 @@ describe('MdCheckbox', () => {
expect(checkboxInstance.checked).toBe(false);
});
it('should overwrite indeterminate state when checked is re-set', () => {
it('should overwrite indeterminate state when checked is re-set', async(() => {
testComponent.isIndeterminate = true;
fixture.detectChanges();
testComponent.isChecked = true;
fixture.detectChanges();
expect(checkboxInstance.checked).toBe(true);
expect(checkboxInstance.indeterminate).toBe(false);
});
fixture.whenStable().then(() => {
expect(checkboxInstance.checked).toBe(true);
expect(checkboxInstance.indeterminate).toBe(false);
});
}));
it('should preserve the user-provided id', () => {
expect(checkboxNativeElement.id).toBe('simple-check');
+4 -2
View File
@@ -225,8 +225,10 @@ export class MdCheckbox implements ControlValueAccessor, AfterViewInit, OnDestro
set checked(checked: boolean) {
if (checked != this.checked) {
if (this._indeterminate) {
this._indeterminate = false;
this.indeterminateChange.emit(this._indeterminate);
Promise.resolve().then(() => {
this._indeterminate = false;
this.indeterminateChange.emit(this._indeterminate);
});
}
this._checked = checked;
this._changeDetectorRef.markForCheck();
@@ -268,14 +268,14 @@ describe('Overlay directives', () => {
@Component({
template: `
<button cdk-overlay-origin #trigger="cdkOverlayOrigin">Toggle menu</button>
<template cdk-connected-overlay [open]="isOpen" [width]="width" [height]="height"
<ng-template cdk-connected-overlay [open]="isOpen" [width]="width" [height]="height"
[origin]="trigger"
[hasBackdrop]="hasBackdrop" backdropClass="mat-test-class"
(backdropClick)="backdropClicked=true" [offsetX]="offsetX" [offsetY]="offsetY"
(positionChange)="positionChangeHandler($event)" (attach)="attachHandler()"
(detach)="detachHandler()" [minWidth]="minWidth" [minHeight]="minHeight">
<p>Menu content</p>
</template>`,
</ng-template>`,
})
class ConnectedOverlayDirectiveTest {
isOpen = false;
+1 -1
View File
@@ -307,7 +307,7 @@ class PizzaMsg { }
/** Test-bed component that contains a TempatePortal and an ElementRef. */
@Component({template: `<template cdk-portal>Cake</template>`})
@Component({template: `<ng-template cdk-portal>Cake</ng-template>`})
class TestComponentWithTemplatePortals {
@ViewChild(TemplatePortalDirective) templatePortal: TemplatePortalDirective;
+4 -4
View File
@@ -34,13 +34,13 @@ be built upon.
##### `TemplatePortalDirective`
Used to get a portal from a `<template>`. `TemplatePortalDirectives` *is* a `Portal`.
Used to get a portal from a `<ng-template>`. `TemplatePortalDirectives` *is* a `Portal`.
Usage:
```html
<template cdk-portal>
<ng-template cdk-portal>
<p>The content of this template is captured by the portal.</p>
</template>
</ng-template>
<!-- OR -->
@@ -68,5 +68,5 @@ Used to add a portal host to a template. `PortalHostDirective` *is* a `PortalHos
Usage:
```html
<!-- Attaches the `userSettingsPortal` from the previous example. -->
<template [cdk-portalHost]="userSettingsPortal"></template>
<ng-template [cdk-portalHost]="userSettingsPortal"></ng-template>
```
+3 -3
View File
@@ -17,9 +17,9 @@ import {Portal, TemplatePortal, ComponentPortal, BasePortalHost} from './portal'
* the directive instance itself can be attached to a host, enabling declarative use of portals.
*
* Usage:
* <template portal #greeting>
* <ng-template portal #greeting>
* <p> Hello {{name}} </p>
* </template>
* </ng-template>
*/
@Directive({
selector: '[cdk-portal], [portal]',
@@ -37,7 +37,7 @@ export class TemplatePortalDirective extends TemplatePortal {
* directly attached to it, enabling declarative use.
*
* Usage:
* <template [cdkPortalHost]="greeting"></template>
* <ng-template [cdkPortalHost]="greeting"></ng-template>
*/
@Directive({
selector: '[cdkPortalHost], [portalHost]',
+6 -6
View File
@@ -71,7 +71,7 @@ describe('Portals', () => {
expect(hostContainer.textContent).toContain('Chocolate');
});
it('should load a <template> portal', () => {
it('should load a <ng-template> portal', () => {
let testAppComponent = fixture.debugElement.componentInstance;
// Detect changes initially so that the component's ViewChildren are resolved.
@@ -86,7 +86,7 @@ describe('Portals', () => {
expect(hostContainer.textContent).toContain('Cake');
});
it('should load a <template> portal with the `*` sugar', () => {
it('should load a <ng-template> portal with the `*` sugar', () => {
let testAppComponent = fixture.debugElement.componentInstance;
// Detect changes initially so that the component's ViewChildren are resolved.
@@ -101,7 +101,7 @@ describe('Portals', () => {
expect(hostContainer.textContent).toContain('Pie');
});
it('should load a <template> portal with a binding', () => {
it('should load a <ng-template> portal with a binding', () => {
let testAppComponent = fixture.debugElement.componentInstance;
// Detect changes initially so that the component's ViewChildren are resolved.
@@ -378,14 +378,14 @@ class ArbitraryViewContainerRefComponent {
selector: 'portal-test',
template: `
<div class="portal-container">
<template [cdkPortalHost]="selectedPortal"></template>
<ng-template [cdkPortalHost]="selectedPortal"></ng-template>
</div>
<template cdk-portal>Cake</template>
<ng-template cdk-portal>Cake</ng-template>
<div *cdk-portal>Pie</div>
<template cdk-portal> {{fruit}} </template>`,
<ng-template cdk-portal> {{fruit}} </ng-template>`,
})
class PortalTestApp {
@ViewChildren(TemplatePortalDirective) portals: QueryList<TemplatePortalDirective>;
@@ -0,0 +1,8 @@
/**
* Gets a RegExp used to detect an angular wrapped error message.
* See https://github.com/angular/angular/issues/8348
*/
export function wrappedErrorMessage(e: Error) {
const escapedMessage = e.message.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&');
return new RegExp(escapedMessage);
};
+1 -1
View File
@@ -1 +1 @@
<template cdkPortalHost></template>
<ng-template cdkPortalHost></ng-template>
+7 -5
View File
@@ -7,14 +7,16 @@ import {
OnDestroy,
Renderer,
ElementRef,
EventEmitter,
} from '@angular/core';
import {
animate,
trigger,
state,
style,
transition,
trigger,
AnimationTransitionEvent,
EventEmitter,
} from '@angular/core';
AnimationEvent,
} from '@angular/animations';
import {BasePortalHost, ComponentPortal, PortalHostDirective, TemplatePortal} from '../core';
import {MdDialogConfig} from './dialog-config';
import {MdDialogContentAlreadyAttachedError} from './dialog-errors';
@@ -139,7 +141,7 @@ export class MdDialogContainer extends BasePortalHost implements OnDestroy {
* Callback, invoked whenever an animation on the host completes.
* @docs-private
*/
_onAnimationDone(event: AnimationTransitionEvent) {
_onAnimationDone(event: AnimationEvent) {
this._onAnimationStateChange.emit(event.toState as MdDialogContainerAnimationState);
}
+5 -2
View File
@@ -7,7 +7,8 @@ import {
TestBed,
tick,
} from '@angular/core/testing';
import {NgModule,
import {
NgModule,
Component,
Directive,
ViewChild,
@@ -16,6 +17,7 @@ import {NgModule,
Inject,
} from '@angular/core';
import {By} from '@angular/platform-browser';
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
import {MdDialogModule} from './index';
import {MdDialog} from './dialog';
import {MdDialogContainer} from './dialog-container';
@@ -450,6 +452,7 @@ describe('MdDialog', () => {
expect(overlayContainerElement.querySelectorAll('.mat-dialog-container').length).toBe(1);
(overlayContainerElement.querySelector('button[md-dialog-close]') as HTMLElement).click();
viewContainerFixture.detectChanges();
viewContainerFixture.whenStable().then(() => {
expect(overlayContainerElement.querySelectorAll('.mat-dialog-container').length).toBe(0);
@@ -616,7 +619,7 @@ const TEST_DIRECTIVES = [
];
@NgModule({
imports: [MdDialogModule],
imports: [MdDialogModule, NoopAnimationsModule],
exports: TEST_DIRECTIVES,
declarations: TEST_DIRECTIVES,
entryComponents: [
+5 -17
View File
@@ -6,6 +6,7 @@ import {MdInputModule} from './index';
import {MdInputContainer, MdInputDirective} from './input-container';
import {Platform} from '../core/platform/platform';
import {PlatformModule} from '../core/platform/index';
import {wrappedErrorMessage} from '../core/testing/wrapped-error-message';
import {
MdInputContainerMissingMdInputError,
MdInputContainerPlaceholderConflictError,
@@ -227,28 +228,28 @@ describe('MdInputContainer', function () {
let fixture = TestBed.createComponent(MdInputContainerInvalidHintTestController);
expect(() => fixture.detectChanges()).toThrowError(
angularWrappedErrorMessage(new MdInputContainerDuplicatedHintError('start')));
wrappedErrorMessage(new MdInputContainerDuplicatedHintError('start')));
});
it('validates there\'s only one hint label per side (attribute)', () => {
let fixture = TestBed.createComponent(MdInputContainerInvalidHint2TestController);
expect(() => fixture.detectChanges()).toThrowError(
angularWrappedErrorMessage(new MdInputContainerDuplicatedHintError('start')));
wrappedErrorMessage(new MdInputContainerDuplicatedHintError('start')));
});
it('validates there\'s only one placeholder', () => {
let fixture = TestBed.createComponent(MdInputContainerInvalidPlaceholderTestController);
expect(() => fixture.detectChanges()).toThrowError(
angularWrappedErrorMessage(new MdInputContainerPlaceholderConflictError()));
wrappedErrorMessage(new MdInputContainerPlaceholderConflictError()));
});
it('validates that mdInput child is present', () => {
let fixture = TestBed.createComponent(MdInputContainerMissingMdInputTestController);
expect(() => fixture.detectChanges()).toThrowError(
angularWrappedErrorMessage(new MdInputContainerMissingMdInputError()));
wrappedErrorMessage(new MdInputContainerMissingMdInputError()));
});
it('validates the type', () => {
@@ -774,16 +775,3 @@ class MdTextareaWithBindings {
template: `<md-input-container><input></md-input-container>`
})
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, '\\$&');
+6 -1
View File
@@ -8,7 +8,12 @@ import {MdRippleModule} from '../core/ripple/index';
@NgModule({
imports: [OverlayModule, CommonModule, MdRippleModule, CompatibilityModule],
imports: [
OverlayModule,
CommonModule,
MdRippleModule,
CompatibilityModule,
],
exports: [MdMenu, MdMenuItem, MdMenuTrigger, CompatibilityModule],
declarations: [MdMenu, MdMenuItem, MdMenuTrigger],
})
+5 -5
View File
@@ -1,11 +1,11 @@
import{
AnimationEntryMetadata,
trigger,
state,
style,
animate,
transition
} from '@angular/core';
transition,
AnimationTriggerMetadata,
} from '@angular/animations';
/**
* Below are all the animations for the md-menu component.
@@ -23,7 +23,7 @@ import{
*/
// TODO(kara): switch to :enter and :leave once Mobile Safari is sorted out.
export const transformMenu: AnimationEntryMetadata = trigger('transformMenu', [
export const transformMenu: AnimationTriggerMetadata = trigger('transformMenu', [
state('showing', style({
opacity: 1,
transform: `scale(1)`
@@ -44,7 +44,7 @@ export const transformMenu: AnimationEntryMetadata = trigger('transformMenu', [
* This animation fades in the background color and content of the menu panel
* after its containing element is scaled in.
*/
export const fadeInItems: AnimationEntryMetadata = trigger('fadeInItems', [
export const fadeInItems: AnimationTriggerMetadata = trigger('fadeInItems', [
state('showing', style({opacity: 1})),
transition('void => *', [
style({opacity: 0}),
+2 -2
View File
@@ -1,9 +1,9 @@
<template>
<ng-template>
<div class="mat-menu-panel" [ngClass]="_classList" (keydown)="_keyManager.onKeydown($event)"
(click)="_emitCloseEvent()" [@transformMenu]="'showing'">
<div class="mat-menu-content" [@fadeInItems]="'showing'">
<ng-content></ng-content>
</div>
</div>
</template>
</ng-template>
+7 -8
View File
@@ -1,5 +1,6 @@
import {TestBed, async, ComponentFixture} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
import {
Component,
ElementRef,
@@ -28,7 +29,7 @@ describe('MdMenu', () => {
beforeEach(async(() => {
dir = 'ltr';
TestBed.configureTestingModule({
imports: [MdMenuModule.forRoot()],
imports: [MdMenuModule.forRoot(), NoopAnimationsModule],
declarations: [SimpleMenu, PositionedMenu, OverlapMenu, CustomMenuPanel, CustomMenu],
providers: [
{provide: OverlayContainer, useFactory: () => {
@@ -70,7 +71,7 @@ describe('MdMenu', () => {
}).not.toThrowError();
});
it('should close the menu when a click occurs outside the menu', async(() => {
it('should close the menu when a click occurs outside the menu', () => {
const fixture = TestBed.createComponent(SimpleMenu);
fixture.detectChanges();
fixture.componentInstance.trigger.openMenu();
@@ -79,10 +80,8 @@ describe('MdMenu', () => {
backdrop.click();
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(overlayContainerElement.textContent).toBe('');
});
}));
expect(overlayContainerElement.textContent).toBe('');
});
it('should open a custom menu', () => {
const fixture = TestBed.createComponent(CustomMenu);
@@ -469,10 +468,10 @@ class OverlapMenu implements TestableMenu {
@Component({
selector: 'custom-menu',
template: `
<template>
<ng-template>
Custom Menu header
<ng-content></ng-content>
</template>
</ng-template>
`,
exportAs: 'mdCustomMenu'
})
+3 -3
View File
@@ -21,8 +21,8 @@
},
"homepage": "https://github.com/angular/material2#readme",
"peerDependencies": {
"@angular/core": "^2.3.0",
"@angular/common": "^2.3.0",
"@angular/http": "^2.3.0"
"@angular/core": "^4.0.0-rc.5",
"@angular/common": "^4.0.0-rc.5",
"@angular/http": "^4.0.0-rc.5"
}
}
+6 -1
View File
@@ -6,7 +6,12 @@ import {CompatibilityModule, OverlayModule} from '../core';
@NgModule({
imports: [CommonModule, OverlayModule, MdOptionModule, CompatibilityModule],
imports: [
CommonModule,
OverlayModule,
MdOptionModule,
CompatibilityModule,
],
exports: [MdSelect, MdOptionModule, CompatibilityModule],
declarations: [MdSelect],
})
+5 -5
View File
@@ -1,11 +1,11 @@
import {
animate,
AnimationEntryMetadata,
AnimationTriggerMetadata,
state,
style,
transition,
trigger,
} from '@angular/core';
} from '@angular/animations';
/**
* The following are all the animations for the md-select component, with each
@@ -19,7 +19,7 @@ import {
* it to either the top left corner (ltr) or top right corner (rtl) of the trigger,
* depending on the text direction of the application.
*/
export const transformPlaceholder: AnimationEntryMetadata = trigger('transformPlaceholder', [
export const transformPlaceholder: AnimationTriggerMetadata = trigger('transformPlaceholder', [
state('floating-ltr', style({
top: '-22px',
left: '-2px',
@@ -42,7 +42,7 @@ export const transformPlaceholder: AnimationEntryMetadata = trigger('transformPl
*
* When the panel is removed from the DOM, it simply fades out linearly.
*/
export const transformPanel: AnimationEntryMetadata = trigger('transformPanel', [
export const transformPanel: AnimationTriggerMetadata = trigger('transformPanel', [
state('showing', style({
opacity: 1,
minWidth: 'calc(100% + 32px)',
@@ -66,7 +66,7 @@ export const transformPanel: AnimationEntryMetadata = trigger('transformPanel',
* select's options. It is time delayed to occur 100ms after the overlay
* panel has transformed in.
*/
export const fadeInContent: AnimationEntryMetadata = trigger('fadeInContent', [
export const fadeInContent: AnimationTriggerMetadata = trigger('fadeInContent', [
state('showing', style({opacity: 1})),
transition('void => showing', [
style({opacity: 0}),
+2 -2
View File
@@ -13,7 +13,7 @@
<span class="mat-select-underline"></span>
</div>
<template cdk-connected-overlay [origin]="origin" [open]="panelOpen" hasBackdrop (backdropClick)="close()"
<ng-template cdk-connected-overlay [origin]="origin" [open]="panelOpen" hasBackdrop (backdropClick)="close()"
backdropClass="cdk-overlay-transparent-backdrop" [positions]="_positions" [minWidth]="_triggerWidth"
[offsetY]="_offsetY" [offsetX]="_offsetX" (attach)="_setScrollTop()">
<div class="mat-select-panel" [@transformPanel]="'showing'" (@transformPanel.done)="_onPanelDone()"
@@ -23,4 +23,4 @@
<ng-content></ng-content>
</div>
</div>
</template>
</ng-template>
+51 -48
View File
@@ -9,6 +9,7 @@ import {
ChangeDetectionStrategy,
OnInit,
} from '@angular/core';
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
import {MdSelectModule} from './index';
import {OverlayContainer} from '../core/overlay/overlay-container';
import {MdSelect, MdSelectFloatPlaceholderType} from './select';
@@ -20,6 +21,8 @@ import {
} from '@angular/forms';
import {ViewportRuler} from '../core/overlay/position/viewport-ruler';
import {dispatchFakeEvent} from '../core/testing/dispatch-events';
import {wrappedErrorMessage} from '../core/testing/wrapped-error-message';
describe('MdSelect', () => {
let overlayContainerElement: HTMLElement;
@@ -27,7 +30,7 @@ describe('MdSelect', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [MdSelectModule.forRoot(), ReactiveFormsModule, FormsModule],
imports: [MdSelectModule.forRoot(), ReactiveFormsModule, FormsModule, NoopAnimationsModule],
declarations: [
BasicSelect,
NgModelSelect,
@@ -589,67 +592,67 @@ describe('MdSelect', () => {
let fixture: ComponentFixture<BasicSelect>;
let trigger: HTMLElement;
beforeEach(() => {
beforeEach(fakeAsync(() => {
fixture = TestBed.createComponent(BasicSelect);
fixture.detectChanges();
trigger = fixture.debugElement.query(By.css('.mat-select-trigger')).nativeElement;
}));
it('should float the placeholder when the panel is open and unselected', () => {
expect(fixture.componentInstance.select._getPlaceholderAnimationState())
.toEqual('', 'Expected placeholder to initially have a normal position.');
trigger.click();
fixture.detectChanges();
expect(fixture.componentInstance.select._getPlaceholderAnimationState())
.toEqual('floating-ltr', 'Expected placeholder to animate up to floating position.');
const backdrop =
overlayContainerElement.querySelector('.cdk-overlay-backdrop') as HTMLElement;
backdrop.click();
fixture.detectChanges();
expect(fixture.componentInstance.select._getPlaceholderAnimationState())
.toEqual('', 'Expected placeholder to animate back down to normal position.');
});
it('should float the placeholder when the panel is open and unselected', () => {
expect(fixture.componentInstance.select._getPlaceholderAnimationState())
.toEqual('', 'Expected placeholder to initially have a normal position.');
it('should float the placeholder without animation when value is set', () => {
fixture.componentInstance.control.setValue('pizza-1');
fixture.detectChanges();
trigger.click();
fixture.detectChanges();
expect(fixture.componentInstance.select._getPlaceholderAnimationState())
.toEqual('floating-ltr', 'Expected placeholder to animate up to floating position.');
const placeholderEl =
fixture.debugElement.query(By.css('.mat-select-placeholder')).nativeElement;
const backdrop =
overlayContainerElement.querySelector('.cdk-overlay-backdrop') as HTMLElement;
backdrop.click();
fixture.detectChanges();
expect(placeholderEl.classList)
.toContain('mat-floating-placeholder', 'Expected placeholder to display as floating.');
expect(fixture.componentInstance.select._getPlaceholderAnimationState())
.toEqual('', 'Expected animation state to be empty to avoid animation.');
});
expect(fixture.componentInstance.select._getPlaceholderAnimationState())
.toEqual('', 'Expected placeholder to animate back down to normal position.');
});
it('should use the floating-rtl state when the dir is rtl', () => {
dir.value = 'rtl';
it('should float the placeholder without animation when value is set', () => {
fixture.componentInstance.control.setValue('pizza-1');
fixture.detectChanges();
trigger.click();
fixture.detectChanges();
expect(fixture.componentInstance.select._getPlaceholderAnimationState())
.toEqual('floating-rtl');
});
const placeholderEl =
fixture.debugElement.query(By.css('.mat-select-placeholder')).nativeElement;
it('should add a class to the panel when the menu is done animating', fakeAsync(() => {
trigger.click();
fixture.detectChanges();
expect(placeholderEl.classList)
.toContain('mat-floating-placeholder', 'Expected placeholder to display as floating.');
expect(fixture.componentInstance.select._getPlaceholderAnimationState())
.toEqual('', 'Expected animation state to be empty to avoid animation.');
});
const panel = overlayContainerElement.querySelector('.mat-select-panel');
it('should use the floating-rtl state when the dir is rtl', () => {
dir.value = 'rtl';
expect(panel.classList).not.toContain('mat-select-panel-done-animating');
trigger.click();
fixture.detectChanges();
expect(fixture.componentInstance.select._getPlaceholderAnimationState())
.toEqual('floating-rtl');
});
tick(250);
fixture.detectChanges();
expect(panel.classList).toContain('mat-select-panel-done-animating');
}));
it('should add a class to the panel when the menu is done animating', fakeAsync(() => {
trigger.click();
fixture.detectChanges();
const panel = overlayContainerElement.querySelector('.mat-select-panel');
expect(panel.classList).not.toContain('mat-select-panel-done-animating');
tick(250);
fixture.detectChanges();
expect(panel.classList).toContain('mat-select-panel-done-animating');
}));
});
describe('positioning', () => {
@@ -1566,13 +1569,13 @@ describe('MdSelect', () => {
it('should throw an exception when trying to set a non-array value', () => {
expect(() => {
testInstance.control.setValue('not-an-array');
}).toThrowError(MdSelectNonArrayValueError);
}).toThrowError(wrappedErrorMessage(new MdSelectNonArrayValueError()));
});
it('should throw an exception when trying to change multiple mode after init', () => {
expect(() => {
testInstance.select.multiple = false;
}).toThrowError(MdSelectDynamicMultipleError);
}).toThrowError(wrappedErrorMessage(new MdSelectDynamicMultipleError()));
});
it('should pass the `multiple` value to all of the option instances', async(() => {
+1
View File
@@ -447,6 +447,7 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
this.onOpen.emit();
} else {
this.onClose.emit();
this._panelDoneAnimating = false;
}
}
+1 -1
View File
@@ -680,7 +680,7 @@ class SlideToggleTestApp {
@Component({
selector: 'slide-toggle-forms-test-app',
template: `
<form (ngSubmit)="isSubmitted = true">
<form ngNativeValidate (ngSubmit)="isSubmitted = true">
<md-slide-toggle name="slide" ngModel [required]="isRequired">Required</md-slide-toggle>
<button type="submit"></button>
</form>`
+6 -1
View File
@@ -7,7 +7,12 @@ import {SimpleSnackBar} from './simple-snack-bar';
@NgModule({
imports: [OverlayModule, PortalModule, CommonModule, CompatibilityModule],
imports: [
OverlayModule,
PortalModule,
CommonModule,
CompatibilityModule,
],
exports: [MdSnackBarContainer, CompatibilityModule],
declarations: [MdSnackBarContainer, SimpleSnackBar],
entryComponents: [MdSnackBarContainer, SimpleSnackBar],
+1 -1
View File
@@ -1 +1 @@
<template cdkPortalHost></template>
<ng-template cdkPortalHost></ng-template>
+9 -7
View File
@@ -2,17 +2,19 @@ import {
Component,
ComponentRef,
ViewChild,
trigger,
state,
style,
transition,
animate,
AnimationTransitionEvent,
NgZone,
OnDestroy,
Renderer,
ElementRef,
} from '@angular/core';
import {
trigger,
state,
style,
transition,
animate,
AnimationEvent,
} from '@angular/animations';
import {
BasePortalHost,
ComponentPortal,
@@ -103,7 +105,7 @@ export class MdSnackBarContainer extends BasePortalHost implements OnDestroy {
}
/** Handle end of animations, updating the state of the snackbar. */
onAnimationEnd(event: AnimationTransitionEvent) {
onAnimationEnd(event: AnimationEvent) {
if (event.toState === 'void' || event.toState === 'complete') {
this._completeExit();
}
+3 -2
View File
@@ -9,6 +9,7 @@ import {
} from '@angular/core/testing';
import {NgModule, Component, Directive, ViewChild, ViewContainerRef} from '@angular/core';
import {CommonModule} from '@angular/common';
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
import {MdSnackBarModule, MdSnackBar, MdSnackBarConfig, SimpleSnackBar} from './index';
import {OverlayContainer, LiveAnnouncer} from '../core';
@@ -28,7 +29,7 @@ describe('MdSnackBar', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [MdSnackBarModule.forRoot(), SnackBarTestModule],
imports: [MdSnackBarModule.forRoot(), SnackBarTestModule, NoopAnimationsModule],
providers: [
{provide: OverlayContainer, useFactory: () => {
overlayContainerElement = document.createElement('div');
@@ -353,7 +354,7 @@ describe('MdSnackBar with parent MdSnackBar', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [MdSnackBarModule.forRoot(), SnackBarTestModule],
imports: [MdSnackBarModule.forRoot(), SnackBarTestModule, NoopAnimationsModule],
declarations: [ComponentThatProvidesMdSnackBar],
providers: [
{provide: OverlayContainer, useFactory: () => {
+4
View File
@@ -18,6 +18,10 @@ System.config({
'@angular/http/testing': 'vendor/@angular/http/bundles/http-testing.umd.js',
'@angular/forms': 'vendor/@angular/forms/bundles/forms.umd.js',
'@angular/forms/testing': 'vendor/@angular/forms/bundles/forms-testing.umd.js',
'@angular/animations': 'vendor/@angular/animations/bundles/animations.umd.js',
'@angular/animations/browser': 'vendor/@angular/animations/bundles/animations-browser.umd.js',
'@angular/platform-browser/animations':
'vendor/@angular/platform-browser/bundles/platform-browser-animations.umd.js',
'@angular/platform-browser': 'vendor/@angular/platform-browser/bundles/platform-browser.umd.js',
'@angular/platform-browser/testing':
'vendor/@angular/platform-browser/bundles/platform-browser-testing.umd.js',
+6 -1
View File
@@ -16,7 +16,12 @@ import {SCROLL_DISPATCHER_PROVIDER} from '../core/overlay/scroll/scroll-dispatch
@NgModule({
imports: [CommonModule, PortalModule, MdRippleModule, ObserveContentModule],
imports: [
CommonModule,
PortalModule,
MdRippleModule,
ObserveContentModule,
],
// Don't export all components because some are only to be used internally.
exports: [
MdTabGroup,
+1 -1
View File
@@ -2,5 +2,5 @@
[@translateTab]="_canBeAnimated ? _position : null"
(@translateTab.start)="_onTranslateTabStarted($event)"
(@translateTab.done)="_onTranslateTabComplete($event)">
<template cdkPortalHost></template>
<ng-template cdkPortalHost></ng-template>
</div>
+5 -4
View File
@@ -1,5 +1,6 @@
import {async, ComponentFixture, TestBed, flushMicrotasks, fakeAsync} from '@angular/core/testing';
import {Component, ViewChild, TemplateRef, ViewContainerRef} from '@angular/core';
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
import {LayoutDirection, Dir} from '../core/rtl/dir';
import {TemplatePortal} from '../core/portal/portal';
import {MdTabBody} from './tab-body';
@@ -14,7 +15,7 @@ describe('MdTabBody', () => {
beforeEach(async(() => {
dir = 'ltr';
TestBed.configureTestingModule({
imports: [CommonModule, PortalModule, MdRippleModule],
imports: [CommonModule, PortalModule, MdRippleModule, NoopAnimationsModule],
declarations: [
MdTabBody,
SimpleTabBodyApp,
@@ -149,9 +150,9 @@ describe('MdTabBody', () => {
describe('on centered', () => {
let fixture: ComponentFixture<SimpleTabBodyApp>;
beforeEach(() => {
beforeEach(fakeAsync(() => {
fixture = TestBed.createComponent(SimpleTabBodyApp);
});
}));
it('should attach the content when centered and detach when not', fakeAsync(() => {
fixture.componentInstance.position = 1;
@@ -187,7 +188,7 @@ describe('MdTabBody', () => {
@Component({
template: `
<template>Tab Body Content</template>
<ng-template>Tab Body Content</ng-template>
<md-tab-body [content]="content" [position]="position" [origin]="origin"></md-tab-body>
`
})
+10 -8
View File
@@ -5,18 +5,20 @@ import {
Output,
EventEmitter,
OnInit,
trigger,
state,
style,
animate,
transition,
AnimationTransitionEvent,
ElementRef,
Optional,
ChangeDetectorRef,
AfterViewChecked,
AfterContentChecked,
} from '@angular/core';
import {
trigger,
state,
style,
animate,
transition,
AnimationEvent,
} from '@angular/animations';
import {TemplatePortal, PortalHostDirective, Dir, LayoutDirection} from '../core';
import 'rxjs/add/operator/map';
@@ -165,13 +167,13 @@ export class MdTabBody implements OnInit, AfterViewChecked, AfterContentChecked
}
}
_onTranslateTabStarted(e: AnimationTransitionEvent) {
_onTranslateTabStarted(e: AnimationEvent) {
if (this._isCenterPosition(e.toState)) {
this.onCentering.emit(this._elementRef.nativeElement.clientHeight);
}
}
_onTranslateTabComplete(e: AnimationTransitionEvent) {
_onTranslateTabComplete(e: AnimationEvent) {
// If the end state is that the tab is not centered, then detach the content.
if (!this._isCenterPosition(e.toState) && !this._isCenterPosition(this._position)) {
this._portalHost.detach();
+4 -4
View File
@@ -12,12 +12,12 @@
(click)="tabHeader.focusIndex = selectedIndex = i">
<!-- If there is a label template, use it. -->
<template [ngIf]="tab.templateLabel">
<template [cdkPortalHost]="tab.templateLabel"></template>
</template>
<ng-template [ngIf]="tab.templateLabel">
<ng-template [cdkPortalHost]="tab.templateLabel"></ng-template>
</ng-template>
<!-- If there is not a label template, fall back to the text label. -->
<template [ngIf]="!tab.templateLabel">{{tab.textLabel}}</template>
<ng-template [ngIf]="!tab.templateLabel">{{tab.textLabel}}</ng-template>
</div>
</md-tab-header>
+10 -9
View File
@@ -3,6 +3,7 @@ import {
} from '@angular/core/testing';
import {MdTabGroup, MdTabsModule, MdTabHeaderPosition} from './index';
import {Component, ViewChild} from '@angular/core';
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
import {By} from '@angular/platform-browser';
import {Observable} from 'rxjs/Observable';
import {MdTab} from './tab';
@@ -13,7 +14,7 @@ import {FakeViewportRuler} from '../core/overlay/position/fake-viewport-ruler';
describe('MdTabGroup', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [MdTabsModule.forRoot()],
imports: [MdTabsModule.forRoot(), NoopAnimationsModule],
declarations: [
SimpleTabsTestApp,
SimpleDynamicTabsTestApp,
@@ -297,15 +298,15 @@ describe('MdTabGroup', () => {
(focusChange)="handleFocus($event)"
(selectChange)="handleSelection($event)">
<md-tab>
<template md-tab-label>Tab One</template>
<ng-template md-tab-label>Tab One</ng-template>
Tab one content
</md-tab>
<md-tab>
<template md-tab-label>Tab Two</template>
<ng-template md-tab-label>Tab Two</ng-template>
Tab two content
</md-tab>
<md-tab>
<template md-tab-label>Tab Three</template>
<ng-template md-tab-label>Tab Three</ng-template>
Tab three content
</md-tab>
</md-tab-group>
@@ -331,7 +332,7 @@ class SimpleTabsTestApp {
(focusChange)="handleFocus($event)"
(selectChange)="handleSelection($event)">
<md-tab *ngFor="let tab of tabs">
<template md-tab-label>{{tab.label}}</template>
<ng-template md-tab-label>{{tab.label}}</ng-template>
{{tab.content}}
</md-tab>
</md-tab-group>
@@ -384,15 +385,15 @@ class BindedTabsTestApp {
template: `
<md-tab-group class="tab-group">
<md-tab>
<template md-tab-label>Tab One</template>
<ng-template md-tab-label>Tab One</ng-template>
Tab one content
</md-tab>
<md-tab disabled>
<template md-tab-label>Tab Two</template>
<ng-template md-tab-label>Tab Two</ng-template>
Tab two content
</md-tab>
<md-tab>
<template md-tab-label>Tab Three</template>
<ng-template md-tab-label>Tab Three</ng-template>
Tab three content
</md-tab>
</md-tab-group>
@@ -404,7 +405,7 @@ class DisabledTabsTestApp {}
template: `
<md-tab-group class="tab-group">
<md-tab *ngFor="let tab of tabs | async">
<template md-tab-label>{{ tab.label }}</template>
<ng-template md-tab-label>{{ tab.label }}</ng-template>
{{ tab.content }}
</md-tab>
</md-tab-group>
+1 -1
View File
@@ -1,4 +1,4 @@
<!-- Create a template for the content of the <md-tab> so that we can grab a reference to this
TemplateRef and use it in a Portal to render the tab content in the appropriate place in the
tab-group. -->
<template><ng-content></ng-content></template>
<ng-template><ng-content></ng-content></ng-template>
+1 -1
View File
@@ -13,7 +13,7 @@ import {MdTabLabel} from './tab-label';
templateUrl: 'tab.html',
})
export class MdTab implements OnInit {
/** Content for the tab label given by <template md-tab-label>. */
/** Content for the tab label given by <ng-template md-tab-label>. */
@ContentChild(MdTabLabel) templateLabel: MdTabLabel;
/** Template inside the MdTab view that contains an <ng-content>. */
+9 -9
View File
@@ -10,10 +10,10 @@ tab labels in the header.
### Events
The `selectChange` output event is emitted when the active tab changes.
The `selectChange` output event is emitted when the active tab changes.
The `focusChange` output event is emitted when the user puts focus on any of the tab labels in
the header, usually through keyboard navigation.
the header, usually through keyboard navigation.
### Labels
@@ -37,16 +37,16 @@ For more complex labels, add a template with the `md-tab-label` directive inside
```html
<md-tab-group>
<md-tab>
<template md-tab-label>
<ng-template md-tab-label>
The <em>best</em> pasta
</template>
</ng-template>
<h1>Best pasta restaurants</h1>
<p>...</p>
</md-tab>
<md-tab>
<template md-tab-label>
<ng-template md-tab-label>
<md-icon>thumb_down</md-icon> The worst sushi
</template>
</ng-template>
<h1>Terrible sushi restaurants</h1>
<p>...</p>
</md-tab>
@@ -58,7 +58,7 @@ For more complex labels, add a template with the `md-tab-label` directive inside
By default, the tab group will not change its height to the height of the currently active tab. To
change this, set the `dynamicHeight` input to true. The tab body will animate its height according
to the height of the active tab.
### Tabs and navigation
While `<md-tab-group>` is used to switch between views within a single route, `<nav md-tab-nav-bar>`
provides a tab-like UI for navigating between routes.
@@ -77,5 +77,5 @@ provides a tab-like UI for navigating between routes.
```
The tab-nav-bar is not tied to any particular router; it works with normal `<a>` elements and uses
the `active` property to determine which tab is currently active. The corresponding
`<router-outlet>` can be placed anywhere in the view.
the `active` property to determine which tab is currently active. The corresponding
`<router-outlet>` can be placed anywhere in the view.
+5 -4
View File
@@ -9,11 +9,12 @@ import {
import {
Component,
DebugElement,
AnimationTransitionEvent,
ViewChild,
ChangeDetectionStrategy
} from '@angular/core';
import {AnimationEvent} from '@angular/animations';
import {By} from '@angular/platform-browser';
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
import {TooltipPosition, MdTooltip, MdTooltipModule, SCROLL_THROTTLE_MS} from './index';
import {OverlayContainer} from '../core';
import {Dir, LayoutDirection} from '../core/rtl/dir';
@@ -31,7 +32,7 @@ describe('MdTooltip', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [MdTooltipModule.forRoot(), OverlayModule],
imports: [MdTooltipModule.forRoot(), OverlayModule, NoopAnimationsModule],
declarations: [BasicTooltipDemo, ScrollableTooltipDemo, OnPushTooltipDemo],
providers: [
Platform,
@@ -263,12 +264,12 @@ describe('MdTooltip', () => {
// _afterVisibilityAnimation function, but for unknown reasons in the test infrastructure,
// this does not occur. Manually call this and verify that doing so does not
// throw an error.
tooltipInstance._afterVisibilityAnimation(new AnimationTransitionEvent({
tooltipInstance._afterVisibilityAnimation({
fromState: 'visible',
toState: 'hidden',
totalTime: 150,
phaseName: '',
}));
} as AnimationEvent);
}));
it('should consistently position before and after overlay origin in ltr and rtl dir', () => {
+9 -7
View File
@@ -4,12 +4,6 @@ import {
Input,
ElementRef,
ViewContainerRef,
style,
trigger,
state,
transition,
animate,
AnimationTransitionEvent,
NgZone,
Optional,
OnDestroy,
@@ -17,6 +11,14 @@ import {
OnInit,
ChangeDetectorRef
} from '@angular/core';
import {
style,
trigger,
state,
transition,
animate,
AnimationEvent,
} from '@angular/animations';
import {
Overlay,
OverlayState,
@@ -445,7 +447,7 @@ export class TooltipComponent {
}
}
_afterVisibilityAnimation(e: AnimationTransitionEvent): void {
_afterVisibilityAnimation(e: AnimationEvent): void {
if (e.toState === 'hidden' && !this.isVisible()) {
this._onHide.next();
}
+3
View File
@@ -106,7 +106,10 @@ const ROLLUP_GLOBALS = {
'@angular/common': 'ng.common',
'@angular/forms': 'ng.forms',
'@angular/http': 'ng.http',
'@angular/animations': 'ng.animations',
'@angular/animations/browser': 'ng.animations.browser',
'@angular/platform-browser': 'ng.platformBrowser',
'@angular/platform-browser/animations': 'ng.platformBrowser.animations',
'@angular/platform-browser-dynamic': 'ng.platformBrowserDynamic',
// Rxjs dependencies