From a8c55eaf8a3cb46034fa229762005161449b0c63 Mon Sep 17 00:00:00 2001 From: Matthew de Nobrega Date: Sun, 17 Apr 2016 11:46:00 +0200 Subject: [PATCH 01/23] feat(input): add support for spellcheck attribute --- src/components/input/input.html | 1 + src/components/input/input.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/components/input/input.html b/src/components/input/input.html index 3a45fd18cac8..3303da2895b9 100644 --- a/src/components/input/input.html +++ b/src/components/input/input.html @@ -15,6 +15,7 @@ [id]="id" [disabled]="disabled" [required]="required" + [spellcheck]="spellcheck" [attr.maxlength]="maxLength" [type]="type" (focus)="onFocus()" diff --git a/src/components/input/input.ts b/src/components/input/input.ts index bb44e0a10c6b..ada34c9a6d97 100644 --- a/src/components/input/input.ts +++ b/src/components/input/input.ts @@ -138,6 +138,7 @@ export class MdInput implements ControlValueAccessor, AfterContentInit, OnChange @Input() maxLength: number = -1; @Input() placeholder: string; @Input() @BooleanFieldValue() required: boolean = false; + @Input() @BooleanFieldValue() spellcheck: boolean = false; @Input() type: string = 'text'; get value(): any { return this._value; }; From 1a803d277dc198120c0671777dfb0aeb78f29fd2 Mon Sep 17 00:00:00 2001 From: Matthew de Nobrega Date: Sun, 24 Apr 2016 18:13:24 +0200 Subject: [PATCH 02/23] feat(input): add support for more input attributes --- src/components/input/input.html | 15 +- src/components/input/input.spec.ts | 371 +++++++++++++++++++++++++++++ src/components/input/input.ts | 14 +- 3 files changed, 395 insertions(+), 5 deletions(-) diff --git a/src/components/input/input.html b/src/components/input/input.html index f177fd3870a5..47f44eb332e9 100644 --- a/src/components/input/input.html +++ b/src/components/input/input.html @@ -12,11 +12,20 @@ [attr.aria-disabled]="ariaDisabled" [attr.aria-required]="ariaRequired" [attr.aria-invalid]="ariaInvalid" - [id]="id" + [attr.autocomplete]="autoComplete" + [autofocus]="autoFocus" [disabled]="disabled" - [required]="required" - [spellcheck]="spellcheck" + [id]="id" + [attr.list]="list" + [attr.max]="max" [attr.maxlength]="maxLength" + [attr.min]="min" + [attr.minlength]="minLength" + [readonly]="readOnly" + [required]="required" + [spellcheck]="spellCheck" + [attr.step]="step" + [attr.tabindex]="tabIndex" [type]="type" (focus)="onFocus()" (blur)="onBlur()" diff --git a/src/components/input/input.spec.ts b/src/components/input/input.spec.ts index b70f6b7096a1..9d1fd3f873d7 100644 --- a/src/components/input/input.spec.ts +++ b/src/components/input/input.spec.ts @@ -243,6 +243,368 @@ export function main() { expect(typeof fixture.componentInstance.value).toBe('number'); }); })); + + it('supports the autoComplete attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('autocomplete')).toBeNull(); + + fixture.componentInstance.autoComplete = 'on'; + fixture.detectChanges(); + expect(el.getAttribute('autocomplete')).toEqual('on'); + })(); + }); + })); + + it('supports the autoComplete attribute as an unbound attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('autocomplete')).toEqual(''); + })(); + }); + })); + + it('supports the autoComplete attribute as an unbound value attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('autocomplete')).toEqual('name'); + })(); + }); + })); + + it('supports the autoFocus attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('autofocus')).toBeNull(); + + fixture.componentInstance.autoFocus = true; + fixture.detectChanges(); + expect(el.getAttribute('autofocus')).toEqual(''); + })(); + }); + })); + + it('supports the autoFocus attribute as an unbound attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('autofocus')).toEqual(''); + })(); + }); + })); + + it('supports the disabled attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.componentInstance.disabled = false; + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('disabled')).toEqual(null); + + fixture.componentInstance.disabled = true; + fixture.detectChanges(); + expect(el.getAttribute('disabled')).toEqual(''); + })(); + }); + })); + + it('supports the disabled attribute as an unbound attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('disabled')).toEqual(''); + })(); + }); + })); + + it('supports the list attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.componentInstance.disabled = false; + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('list')).toEqual(null); + + fixture.componentInstance.list = 'datalist-id'; + fixture.detectChanges(); + expect(el.getAttribute('list')).toEqual('datalist-id'); + })(); + }); + })); + + it('supports the max attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.componentInstance.disabled = false; + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('max')).toEqual(null); + + fixture.componentInstance.max = 10; + fixture.detectChanges(); + expect(el.getAttribute('max')).toEqual('10'); + + fixture.componentInstance.max = '2000-01-02'; + fixture.detectChanges(); + expect(el.getAttribute('max')).toEqual('2000-01-02'); + })(); + }); + })); + + it('supports the min attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.componentInstance.disabled = false; + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('min')).toEqual(null); + + fixture.componentInstance.min = 10; + fixture.detectChanges(); + expect(el.getAttribute('min')).toEqual('10'); + + fixture.componentInstance.min = '2000-01-02'; + fixture.detectChanges(); + expect(el.getAttribute('min')).toEqual('2000-01-02'); + })(); + }); + })); + + it('supports the readOnly attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('readonly')).toBeNull(); + + fixture.componentInstance.readOnly = true; + fixture.detectChanges(); + expect(el.getAttribute('readonly')).toEqual(''); + })(); + }); + })); + + it('supports the readOnly attribute as an unbound attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('readonly')).toEqual(''); + })(); + }); + })); + + it('supports the required attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('required')).toBeNull(); + + fixture.componentInstance.required = true; + fixture.detectChanges(); + expect(el.getAttribute('required')).toEqual(''); + })(); + }); + })); + + it('supports the required attribute as an unbound attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('required')).toEqual(''); + })(); + }); + })); + + it('supports the spellCheck attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('spellcheck')).toEqual('false'); + + fixture.componentInstance.spellCheck = true; + fixture.detectChanges(); + expect(el.getAttribute('spellcheck')).toEqual('true'); + })(); + }); + })); + + it('supports the spellCheck attribute as an unbound attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('spellcheck')).toEqual('true'); + })(); + }); + })); + + it('supports the step attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('step')).toEqual(null); + + fixture.componentInstance.step = 0.5; + fixture.detectChanges(); + expect(el.getAttribute('step')).toEqual('0.5'); + })(); + }); + })); + + it('supports the tabIndex attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('tabindex')).toEqual(null); + + fixture.componentInstance.tabIndex = 1; + fixture.detectChanges(); + expect(el.getAttribute('tabindex')).toEqual('1'); + })(); + }); + })); }); } @@ -391,3 +753,12 @@ class MdInputAriaTestController { ariaLabel: string = 'label'; ariaDisabled: boolean = true; } + +@Component({ + selector: 'test-input-controller', + template: ` + + `, + directives: [MdInput] +}) +class MdInputOptionalAttributeController {} diff --git a/src/components/input/input.ts b/src/components/input/input.ts index d3a1a00eff94..bf93fb393769 100644 --- a/src/components/input/input.ts +++ b/src/components/input/input.ts @@ -131,14 +131,24 @@ export class MdInput implements ControlValueAccessor, AfterContentInit, OnChange */ @Input() align: 'start' | 'end' = 'start'; @Input() dividerColor: 'primary' | 'accent' | 'warn' = 'primary'; - @Input() @BooleanFieldValue() disabled: boolean = false; @Input() @BooleanFieldValue() floatingPlaceholder: boolean = true; @Input() hintLabel: string = ''; + + @Input() autoComplete: string; + @Input() @BooleanFieldValue() autoFocus: boolean = false; + @Input() @BooleanFieldValue() disabled: boolean = false; @Input() id: string = `md-input-${nextUniqueId++}`; + @Input() list: string; + @Input() max: string; @Input() maxLength: number = -1; + @Input() min: string; + @Input() minLength: number; @Input() placeholder: string; + @Input() @BooleanFieldValue() readOnly: boolean = false; @Input() @BooleanFieldValue() required: boolean = false; - @Input() @BooleanFieldValue() spellcheck: boolean = false; + @Input() @BooleanFieldValue() spellCheck: boolean = false; + @Input() step: number; + @Input() tabIndex: number; @Input() type: string = 'text'; get value(): any { return this._value; }; From 7b3698cb39384f3e96f8783641fa43b40314bb9b Mon Sep 17 00:00:00 2001 From: Kara Erickson Date: Mon, 25 Apr 2016 10:23:04 -0700 Subject: [PATCH 03/23] feat(list): add icon support --- src/components/list/list-item.html | 2 +- src/components/list/list.scss | 8 ++++++++ src/demo-app/list/list-demo.html | 1 + src/demo-app/list/list-demo.scss | 5 +++++ src/demo-app/list/list-demo.ts | 3 ++- 5 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/components/list/list-item.html b/src/components/list/list-item.html index 07f97a500837..63f1544b67a3 100644 --- a/src/components/list/list-item.html +++ b/src/components/list/list-item.html @@ -1,5 +1,5 @@
- +
\ No newline at end of file diff --git a/src/components/list/list.scss b/src/components/list/list.scss index 035e11b988c9..3263e4c9a800 100644 --- a/src/components/list/list.scss +++ b/src/components/list/list.scss @@ -3,6 +3,7 @@ $md-list-side-padding: 16px; $md-list-avatar-size: 40px; +$md-list-icon-size: 24px; /* Normal list variables */ $md-list-top-padding: 8px; @@ -83,6 +84,13 @@ based on whether the list is in dense mode. height: $md-list-avatar-size; border-radius: 50%; } + + [md-list-icon] { + width: $md-list-icon-size; + height: $md-list-icon-size; + border-radius: 50%; + padding: 4px; + } } /* diff --git a/src/demo-app/list/list-demo.html b/src/demo-app/list/list-demo.html index 2f87affaef3e..f6de4c683557 100644 --- a/src/demo-app/list/list-demo.html +++ b/src/demo-app/list/list-demo.html @@ -87,6 +87,7 @@

Nav lists

+ folder {{ link.name }} Description diff --git a/src/demo-app/list/list-demo.scss b/src/demo-app/list/list-demo.scss index d1ac6a18f0ce..b42fc322c906 100644 --- a/src/demo-app/list/list-demo.scss +++ b/src/demo-app/list/list-demo.scss @@ -13,6 +13,11 @@ margin-top: 20px; } + [md-list-icon] { + color: white; + background: rgba(0,0,0,0.3); + } + i { color: rgba(0,0,0,0.12); } diff --git a/src/demo-app/list/list-demo.ts b/src/demo-app/list/list-demo.ts index e28e99983318..3462da4a8b20 100644 --- a/src/demo-app/list/list-demo.ts +++ b/src/demo-app/list/list-demo.ts @@ -1,12 +1,13 @@ import {Component} from 'angular2/core'; import {MdButton} from '../../components/button/button'; import {MD_LIST_DIRECTIVES} from '../../components/list/list'; +import {MdIcon} from '../../components/icon/icon'; @Component({ selector: 'list-demo', templateUrl: 'demo-app/list/list-demo.html', styleUrls: ['demo-app/list/list-demo.css'], - directives: [MD_LIST_DIRECTIVES, MdButton] + directives: [MD_LIST_DIRECTIVES, MdButton, MdIcon] }) export class ListDemo { items: string[] = [ From 2f3e9dbb073bf2cbae8b199bf05730fc551bd80c Mon Sep 17 00:00:00 2001 From: Kara Erickson Date: Mon, 25 Apr 2016 11:38:40 -0700 Subject: [PATCH 04/23] fix(theme): update default theme --- src/components/button/_button-theme.scss | 2 +- src/components/checkbox/checkbox.scss | 2 +- src/core/style/_default-theme.scss | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/button/_button-theme.scss b/src/components/button/_button-theme.scss index ca5c154530dd..008f9aefbd98 100644 --- a/src/components/button/_button-theme.scss +++ b/src/components/button/_button-theme.scss @@ -1,6 +1,6 @@ // Applies specified coloring to three supported palettes -@mixin md-button-theme ($property, $color:500, $opacity: 1) { +@mixin md-button-theme ($property, $color: 'default', $opacity: 1) { &.md-primary { #{$property}: md-color($md-primary, $color, $opacity); } diff --git a/src/components/checkbox/checkbox.scss b/src/components/checkbox/checkbox.scss index bead2ca8f351..f61897b45de1 100644 --- a/src/components/checkbox/checkbox.scss +++ b/src/components/checkbox/checkbox.scss @@ -13,7 +13,7 @@ $md-checkbox-border-color: if($md-is-dark-theme, rgba(white, 0.7), rgba(black, 0 /** The color of the checkbox's checkmark / mixedmark. */ $md-checkbox-mark-color: md-color($md-background, background); /** The color that is used as the checkbox background when it is checked. */ -$md-checkbox-background-color: md-color($md-primary, 500); +$md-checkbox-background-color: md-color($md-accent, 500); /** The base duration used for the majority of transitions for the checkbox. */ $md-checkbox-transition-duration: 90ms; /** The amount of spacing between the checkbox and its label. */ diff --git a/src/core/style/_default-theme.scss b/src/core/style/_default-theme.scss index 24624767c823..22ca178e79b7 100644 --- a/src/core/style/_default-theme.scss +++ b/src/core/style/_default-theme.scss @@ -6,8 +6,8 @@ $md-is-dark-theme: false; -$md-primary: md-palette($md-blue, 500, 100, 700, $md-contrast-palettes); -$md-accent: md-palette($md-red, A200, A100, A400, $md-contrast-palettes); -$md-warn: md-palette($md-orange, 500, 300, 800, $md-contrast-palettes); +$md-primary: md-palette($md-teal, 500, 100, 700, $md-contrast-palettes); +$md-accent: md-palette($md-purple, 500, 300,800, $md-contrast-palettes); +$md-warn: md-palette($md-red, 500, 300, 900, $md-contrast-palettes); $md-foreground: if($md-is-dark-theme, $md-dark-theme-foreground, $md-light-theme-foreground); $md-background: if($md-is-dark-theme, $md-dark-theme-background, $md-light-theme-background); From aa6c740fbd56aff6d6076815cae7c843253cb804 Mon Sep 17 00:00:00 2001 From: Kara Erickson Date: Mon, 25 Apr 2016 12:00:36 -0700 Subject: [PATCH 05/23] feat(sidenav): add fullscreen mode --- src/components/sidenav/sidenav.scss | 19 ++++++++++++++----- src/demo-app/demo-app.html | 2 +- src/demo-app/demo-app.scss | 5 ----- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/components/sidenav/sidenav.scss b/src/components/sidenav/sidenav.scss index 42843ae2f3a5..18dfeabcce08 100644 --- a/src/components/sidenav/sidenav.scss +++ b/src/components/sidenav/sidenav.scss @@ -42,6 +42,15 @@ $md-sidenav-push-background-color: md-color($md-background, dialog) !default; } } +/* This mixin ensures a sidenav element spans the whole viewport. */ +@mixin md-sidenav-fullscreen { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; +} + :host { // We need a stacking context here so that the backdrop and drawers are clipped to the @@ -52,6 +61,10 @@ $md-sidenav-push-background-color: md-color($md-background, dialog) !default; box-sizing: border-box; + &[fullscreen] { + @include md-sidenav-fullscreen(); + } + // Need this to take up space in the layout. display: block; @@ -59,11 +72,7 @@ $md-sidenav-push-background-color: md-color($md-background, dialog) !default; overflow-x: hidden; & > .md-sidenav-backdrop { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; + @include md-sidenav-fullscreen(); display: block; diff --git a/src/demo-app/demo-app.html b/src/demo-app/demo-app.html index c983bdd25be9..a3844694d52d 100644 --- a/src/demo-app/demo-app.html +++ b/src/demo-app/demo-app.html @@ -1,4 +1,4 @@ - + Button diff --git a/src/demo-app/demo-app.scss b/src/demo-app/demo-app.scss index f1ba033500e3..5b1850a9ada9 100644 --- a/src/demo-app/demo-app.scss +++ b/src/demo-app/demo-app.scss @@ -1,10 +1,5 @@ .demo-root { font-family: Roboto, 'Helvetica Neue', sans-serif; - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; // Helps fonts on OSX looks more consistent with other systems // Isn't currently in button styles due to performance concerns From e933e4e70e4ceebadac8689e2098e3e7f252f300 Mon Sep 17 00:00:00 2001 From: Kara Date: Mon, 25 Apr 2016 12:27:58 -0700 Subject: [PATCH 06/23] chore(sidenav): add todo for fullscreen (#353) --- src/components/sidenav/sidenav.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/sidenav/sidenav.scss b/src/components/sidenav/sidenav.scss index 18dfeabcce08..4d808b44ea75 100644 --- a/src/components/sidenav/sidenav.scss +++ b/src/components/sidenav/sidenav.scss @@ -42,7 +42,7 @@ $md-sidenav-push-background-color: md-color($md-background, dialog) !default; } } -/* This mixin ensures a sidenav element spans the whole viewport. */ +/* This mixin ensures a sidenav element spans the whole viewport.*/ @mixin md-sidenav-fullscreen { position: absolute; top: 0; @@ -61,6 +61,7 @@ $md-sidenav-push-background-color: md-color($md-background, dialog) !default; box-sizing: border-box; + // TODO(hansl): Update this with a more robust solution. &[fullscreen] { @include md-sidenav-fullscreen(); } From 0672356290148d27e13d0e33c0bff9b44a58cda0 Mon Sep 17 00:00:00 2001 From: Kara Erickson Date: Mon, 25 Apr 2016 14:59:20 -0700 Subject: [PATCH 07/23] fix(button): cleaned up button theming --- src/components/button/_button-base.scss | 13 +++++++++---- src/demo-app/button/button-demo.scss | 2 ++ src/demo-app/demo-app.scss | 4 ---- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/components/button/_button-base.scss b/src/components/button/_button-base.scss index d15636afb44a..cc989e29e724 100644 --- a/src/components/button/_button-base.scss +++ b/src/components/button/_button-base.scss @@ -51,7 +51,7 @@ $md-mini-fab-padding: 8px !default; font-size: $md-body-font-size-base; font-family: $md-font-family; font-weight: 500; - color: md-color($md-foreground, text); + color: currentColor; // Sizing. margin: $md-button-margin; @@ -69,7 +69,7 @@ $md-mini-fab-padding: 8px !default; %md-raised-button { @extend %md-button-base; - @include md-elevation(1); + @include md-elevation(2); @include md-button-theme('color', default-contrast); @include md-button-theme('background-color'); @@ -82,7 +82,7 @@ $md-mini-fab-padding: 8px !default; md-elevation-transition-property-value(); &:active { - @include md-elevation(2); + @include md-elevation(8); } &.md-button-focus { @@ -102,9 +102,14 @@ $md-mini-fab-padding: 8px !default; min-width: 0; border-radius: $md-fab-border-radius; background-color: md-color($md-accent); + color: md-color($md-accent, default-contrast); width: $size; height: $size; - padding: $padding; + padding: 0; + + i { + padding: $padding 0; + } &.md-button-focus { background-color: md-color($md-accent, 600); diff --git a/src/demo-app/button/button-demo.scss b/src/demo-app/button/button-demo.scss index 8e826dc48cc9..9cb6349e0ef8 100644 --- a/src/demo-app/button/button-demo.scss +++ b/src/demo-app/button/button-demo.scss @@ -6,6 +6,8 @@ } section { + display: flex; + align-items: center; background-color: #f7f7f7; margin: 8px; } diff --git a/src/demo-app/demo-app.scss b/src/demo-app/demo-app.scss index 5b1850a9ada9..bf3e1f9263a1 100644 --- a/src/demo-app/demo-app.scss +++ b/src/demo-app/demo-app.scss @@ -34,10 +34,6 @@ justify-content: space-between; width: 100%; } - - button { - color: white; - } } h1 { From 77be2df5634316d6bd8a7b78c124b03b88d66b12 Mon Sep 17 00:00:00 2001 From: Kara Erickson Date: Mon, 25 Apr 2016 16:14:07 -0700 Subject: [PATCH 08/23] feat(card): add alignment shortcuts for md-card-actions --- src/components/card/README.md | 5 +++++ src/components/card/card.scss | 5 +++++ src/demo-app/card/card-demo.html | 4 ++++ 3 files changed, 14 insertions(+) diff --git a/src/components/card/README.md b/src/components/card/README.md index ef62bd4429f6..14c28409ac0c 100644 --- a/src/components/card/README.md +++ b/src/components/card/README.md @@ -51,6 +51,11 @@ Output: +#### Preset shortcuts + +`md-card-actions` has a few layout shortcuts. You can add `align="end"` to align the buttons at the end of +the main axis (flex-end). The default is `align="start"` (flex-start). + ### Preset layouts You can also leverage preset layouts that format some of the sections together. diff --git a/src/components/card/card.scss b/src/components/card/card.scss index f99a69d1fe31..02f9c60f5c66 100644 --- a/src/components/card/card.scss +++ b/src/components/card/card.scss @@ -55,6 +55,11 @@ md-card-actions { margin-left: -16px; margin-right: -16px; padding: 8px 0; + + &[align='end'] { + display: flex; + justify-content: flex-end; + } } [md-card-image] { diff --git a/src/demo-app/card/card-demo.html b/src/demo-app/card/card-demo.html index a577da8565ba..12617c5f74d1 100644 --- a/src/demo-app/card/card-demo.html +++ b/src/demo-app/card/card-demo.html @@ -26,6 +26,10 @@

Here is some content

+ + + + From 9a6ab352f4da6431f27f2f943cff8ee23e3fc7cf Mon Sep 17 00:00:00 2001 From: Kara Erickson Date: Mon, 25 Apr 2016 15:51:01 -0700 Subject: [PATCH 09/23] chore(icon): add package.json --- src/components/icon/package.json | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/components/icon/package.json diff --git a/src/components/icon/package.json b/src/components/icon/package.json new file mode 100644 index 000000000000..a0e8b8c790f5 --- /dev/null +++ b/src/components/icon/package.json @@ -0,0 +1,27 @@ +{ + "name": "@angular2-material/icon", + "version": "2.0.0-alpha.3", + "description": "Angular 2 Material icon", + "main": "./icon.js", + "typings": "./icon.d.ts", + "repository": { + "type": "git", + "url": "https://github.com/angular/material2.git" + }, + "keywords": [ + "angular", + "material", + "material design", + "components", + "icon" + ], + "license": "MIT", + "bugs": { + "url": "https://github.com/angular/material2/issues" + }, + "homepage": "https://github.com/angular/material2#readme", + "peerDependencies": { + "angular2": "^2.0.0-beta.8", + "@angular2-material/core": "2.0.0-alpha.3" + } +} From f4a7266b094998080d45196551ff628bde456b96 Mon Sep 17 00:00:00 2001 From: Kara Erickson Date: Tue, 26 Apr 2016 10:51:51 -0700 Subject: [PATCH 10/23] fix(checkbox): fix horizontal spacing between checkboxes and radio buttons --- src/components/checkbox/checkbox.scss | 9 ++++++-- src/components/radio/radio.scss | 26 ++++++++++++++---------- src/core/style/_variables.scss | 5 +++++ src/demo-app/checkbox/checkbox-demo.html | 2 +- src/demo-app/checkbox/checkbox-demo.scss | 4 ++++ src/demo-app/radio/radio-demo.scss | 4 ++++ 6 files changed, 36 insertions(+), 14 deletions(-) diff --git a/src/components/checkbox/checkbox.scss b/src/components/checkbox/checkbox.scss index f61897b45de1..9ce568fcb0a7 100644 --- a/src/components/checkbox/checkbox.scss +++ b/src/components/checkbox/checkbox.scss @@ -3,7 +3,7 @@ @import "variables"; /** The width/height of the checkbox element. */ -$md-checkbox-size: 18px !default; +$md-checkbox-size: $md-toggle-size !default; /** The width of the line used to draw the checkmark / mixedmark. */ $md-checkbox-mark-stroke-size: 2/15 * $md-checkbox-size !default; /** The width of the checkbox border shown when the checkbox is unchecked. */ @@ -17,7 +17,7 @@ $md-checkbox-background-color: md-color($md-accent, 500); /** The base duration used for the majority of transitions for the checkbox. */ $md-checkbox-transition-duration: 90ms; /** The amount of spacing between the checkbox and its label. */ -$md-checkbox-item-spacing: 4px; +$md-checkbox-item-spacing: $md-toggle-padding; // Manual calculation done on SVG $_md-checkbox-mark-path-length: 22.910259; @@ -250,6 +250,11 @@ $_md-checkbox-indeterminate-checked-easing-function: cubic-bezier(0.14, 0, 0, 1) } } +// TODO(kara): Remove this style when fixing vertical baseline +.md-checkbox-layout label { + line-height: 24px; +} + .md-checkbox-frame { @extend %md-checkbox-outer-box; diff --git a/src/components/radio/radio.scss b/src/components/radio/radio.scss index 22913dcae1de..d1589500e1a2 100644 --- a/src/components/radio/radio.scss +++ b/src/components/radio/radio.scss @@ -1,6 +1,7 @@ @import "default-theme"; +@import "variables"; -$md-radio-width: 20px !default; +$md-radio-size: $md-toggle-size !default; // Top-level host container. md-radio-button { @@ -12,7 +13,6 @@ md-radio-button { .md-radio-label { cursor: pointer; display: block; - padding: 8px; white-space: nowrap; } @@ -20,10 +20,10 @@ md-radio-button { .md-radio-container { box-sizing: border-box; display: inline-block; - height: $md-radio-width; + height: $md-radio-size; position: relative; top: 2px; - width: $md-radio-width; + width: $md-radio-size; } // TODO(mtlin): Replace when ink ripple component is implemented. @@ -60,12 +60,12 @@ md-radio-button { border: solid 2px; border-radius: 50%; box-sizing: border-box; - height: $md-radio-width; + height: $md-radio-size; left: 0; position: absolute; top: 0; transition: border-color ease 0.28s; - width: $md-radio-width; + width: $md-radio-size; .md-radio-checked & { border-color: md-color($md-accent); @@ -81,13 +81,13 @@ md-radio-button { background-color: md-color($md-accent); border-radius: 50%; box-sizing: border-box; - height: $md-radio-width; + height: $md-radio-size; left: 0; position: absolute; top: 0; transition: transform ease 0.28s, background-color ease 0.28s; transform: scale(0); - width: $md-radio-width; + width: $md-radio-size; .md-radio-checked & { transform: scale(0.5); @@ -103,11 +103,15 @@ md-radio-button { display: inline-block; float: right; line-height: 24px; - // Equal padding on both sides, for RTL. - padding-left: 8px; - padding-right: 8px; + padding-left: $md-toggle-padding; position: relative; vertical-align: top; + + [dir='rtl'] & { + float: left; + padding-right: $md-toggle-padding; + padding-left: 0; + } } // Underlying native input element. diff --git a/src/core/style/_variables.scss b/src/core/style/_variables.scss index d4ce138117c9..00d65b5fc226 100644 --- a/src/core/style/_variables.scss +++ b/src/core/style/_variables.scss @@ -15,6 +15,11 @@ $z-index-drawer: 100 !default; // Global constants $pi: 3.14159265; +// Padding between input toggles and their labels +$md-toggle-padding: 8px !default; +// Width and height of input toggles +$md-toggle-size: 20px !default; + // Easing Curves // TODO(jelbourn): all of these need to be revisited diff --git a/src/demo-app/checkbox/checkbox-demo.html b/src/demo-app/checkbox/checkbox-demo.html index cd108fb13db9..b811f6969563 100644 --- a/src/demo-app/checkbox/checkbox-demo.html +++ b/src/demo-app/checkbox/checkbox-demo.html @@ -6,7 +6,7 @@

md-checkbox: Basic Example

[align]="alignment"> Do you want to foobar the bazquux? - {{printResult()}} -
+
Date: Tue, 26 Apr 2016 14:14:24 -0400 Subject: [PATCH 11/23] chore: use md-icon instead of in demos. (#345) --- src/components/button/README.md | 22 +++++++++---------- src/components/button/_button-base.scss | 2 +- src/components/icon/icon.scss | 2 -- src/components/list/README.md | 28 ++++++++++++------------- src/demo-app/button/button-demo.html | 24 ++++++++++----------- src/demo-app/button/button-demo.ts | 3 ++- src/demo-app/input/input-demo.html | 6 +++--- src/demo-app/input/input-demo.scss | 2 ++ src/demo-app/input/input-demo.ts | 3 ++- src/demo-app/list/list-demo.html | 4 ++-- src/demo-app/list/list-demo.scss | 8 +++---- src/demo-app/toolbar/toolbar-demo.html | 18 ++++++++-------- src/demo-app/toolbar/toolbar-demo.ts | 3 ++- 13 files changed, 64 insertions(+), 61 deletions(-) diff --git a/src/components/button/README.md b/src/components/button/README.md index df9bb47b2cc5..dbf36dc73985 100644 --- a/src/components/button/README.md +++ b/src/components/button/README.md @@ -27,23 +27,23 @@ There are five types of buttons: * Defaults background to "accent" palette defined in theme * Box shadow applied * 40 by 40 px - + Each is an attribute directive that you can add to a `button` or `a` tag. You can provide custom content to the button by inserting it between the tags, as you would with a normal button. - + Example: - + ```html ``` @@ -53,13 +53,13 @@ Output: ### Theming -All button types can be themed to match your "primary" palette, your "accent" palette, or your "warn" palette using the `color` attribute. +All button types can be themed to match your "primary" palette, your "accent" palette, or your "warn" palette using the `color` attribute. Simply pass in the palette name. -In flat buttons, the palette chosen will affect the text color, while in other buttons, it affects the background. +In flat buttons, the palette chosen will affect the text color, while in other buttons, it affects the background. Example: - + ```html @@ -90,8 +90,8 @@ Output: * In high contrast mode, a strong border is added to the button to make it easier to see. * Button focus events originating from the keyboard will retain focus styles, while button focus events from the mouse will not. * As `md-button` is added to an existing `button` or `a` tag, it enjoys all the accessibility natively built into these elements. - - + + ### Upcoming work We will also be adding ink ripples to buttons in an upcoming milestone. diff --git a/src/components/button/_button-base.scss b/src/components/button/_button-base.scss index cc989e29e724..fc591935e7d0 100644 --- a/src/components/button/_button-base.scss +++ b/src/components/button/_button-base.scss @@ -107,7 +107,7 @@ $md-mini-fab-padding: 8px !default; height: $size; padding: 0; - i { + i, md-icon { padding: $padding 0; } diff --git a/src/components/icon/icon.scss b/src/components/icon/icon.scss index 1fa23d71bc13..a8bd0cebe37d 100644 --- a/src/components/icon/icon.scss +++ b/src/components/icon/icon.scss @@ -13,7 +13,5 @@ md-icon { display: inline-block; fill: currentColor; height: $md-icon-size; - margin: auto; - vertical-align: middle; width: $md-icon-size; } diff --git a/src/components/list/README.md b/src/components/list/README.md index 4f9ced0631b2..74ea93547ed5 100644 --- a/src/components/list/README.md +++ b/src/components/list/README.md @@ -32,8 +32,8 @@ Output: ### Multi-line lists -If your list requires multiple lines per list item, annotate each line with an `md-line` attribute. -You can use whichever heading tag is appropriate for your DOM hierarchy (doesn't have to be `h3`), +If your list requires multiple lines per list item, annotate each line with an `md-line` attribute. +You can use whichever heading tag is appropriate for your DOM hierarchy (doesn't have to be `h3`), as long as the `md-line` attribute is included. ```html @@ -62,13 +62,13 @@ Two line list output: -Three line list output: +Three line list output: ### Lists with avatars -To include an avatar, add an image tag with an `md-list-avatar` attribute. +To include an avatar, add an image tag with an `md-list-avatar` attribute. ```html @@ -86,11 +86,11 @@ To include an avatar, add an image tag with an `md-list-avatar` attribute. Output: - + ### Dense lists -Lists are also available in "dense layout" mode, which shrinks the font size and height of the list -to suit UIs that may need to display more information. To enable this mode, add a `dense` attribute -to the main `md-list` tag. +Lists are also available in "dense layout" mode, which shrinks the font size and height of the list +to suit UIs that may need to display more information. To enable this mode, add a `dense` attribute +to the main `md-list` tag. ```html @@ -114,17 +114,17 @@ use `` tags.

Folders

- folder + folder

{{folder.name}}

{{folder.updated}}

Notes

- note + note

{{note.name}}

{{note.updated}}

-
+
``` @@ -151,13 +151,13 @@ If you require a more complex nav list (e.g. with more than one target per item) {{ link }} ``` ### Lists with secondary text -Secondary text styling will be part of a broader typography module to -[come later](https://github.com/angular/material2/issues/205), and won’t be implemented as part of this component +Secondary text styling will be part of a broader typography module to +[come later](https://github.com/angular/material2/issues/205), and won’t be implemented as part of this component specifically. Gray text in the examples above comes from a "demo-2" class added manually by the demo. \ No newline at end of file diff --git a/src/demo-app/button/button-demo.html b/src/demo-app/button/button-demo.html index a6e25ecb431c..3b4d4beeffa3 100644 --- a/src/demo-app/button/button-demo.html +++ b/src/demo-app/button/button-demo.html @@ -3,10 +3,10 @@ @@ -14,10 +14,10 @@ link link - check + check - check + check @@ -35,27 +35,27 @@
@@ -69,11 +69,11 @@ off
diff --git a/src/demo-app/button/button-demo.ts b/src/demo-app/button/button-demo.ts index 8304c19ac193..07aedc1ac3f7 100644 --- a/src/demo-app/button/button-demo.ts +++ b/src/demo-app/button/button-demo.ts @@ -1,11 +1,12 @@ import {Component} from 'angular2/core'; import {MdButton, MdAnchor} from '../../components/button/button'; +import {MdIcon} from '../../components/icon/icon'; @Component({ selector: 'button-demo', templateUrl: 'demo-app/button/button-demo.html', styleUrls: ['demo-app/button/button-demo.css'], - directives: [MdButton, MdAnchor] + directives: [MdButton, MdAnchor, MdIcon] }) export class ButtonDemo { isDisabled: boolean = false; diff --git a/src/demo-app/input/input-demo.html b/src/demo-app/input/input-demo.html index 0c466a9e722a..6e7b157dc93b 100644 --- a/src/demo-app/input/input-demo.html +++ b/src/demo-app/input/input-demo.html @@ -85,10 +85,10 @@

- I favorite bold placeholder + I favorite bold placeholder - I also home italic hint labels + I also home italic hint labels

@@ -122,7 +122,7 @@
Both: - email  + email   @gmail.com

diff --git a/src/demo-app/input/input-demo.scss b/src/demo-app/input/input-demo.scss index e28f5c1c93f1..4ec33a506a96 100644 --- a/src/demo-app/input/input-demo.scss +++ b/src/demo-app/input/input-demo.scss @@ -13,7 +13,9 @@ .demo-icons { font-size: 100%; + height: inherit; vertical-align: top; + width: inherit; } .demo-transform { diff --git a/src/demo-app/input/input-demo.ts b/src/demo-app/input/input-demo.ts index 65370b747648..b0c74263a981 100644 --- a/src/demo-app/input/input-demo.ts +++ b/src/demo-app/input/input-demo.ts @@ -3,6 +3,7 @@ import {MD_INPUT_DIRECTIVES} from '../../components/input/input'; import {MdButton} from '../../components/button/button'; import {MdCard} from '../../components/card/card'; import {MdCheckbox} from '../../components/checkbox/checkbox'; +import {MdIcon} from '../../components/icon/icon'; import {MdToolbar} from '../../components/toolbar/toolbar'; @@ -12,7 +13,7 @@ let max = 5; selector: 'input-demo', templateUrl: 'demo-app/input/input-demo.html', styleUrls: ['demo-app/input/input-demo.css'], - directives: [MdCard, MdCheckbox, MdButton, MdToolbar, MD_INPUT_DIRECTIVES] + directives: [MdCard, MdCheckbox, MdButton, MdIcon, MdToolbar, MD_INPUT_DIRECTIVES] }) export class InputDemo { dividerColor: boolean; diff --git a/src/demo-app/list/list-demo.html b/src/demo-app/list/list-demo.html index f6de4c683557..0982bab7d57f 100644 --- a/src/demo-app/list/list-demo.html +++ b/src/demo-app/list/list-demo.html @@ -81,7 +81,7 @@

Nav lists

{{ link.name }} @@ -96,7 +96,7 @@

Nav lists

{{ link.name }} diff --git a/src/demo-app/list/list-demo.scss b/src/demo-app/list/list-demo.scss index b42fc322c906..0aab51351a7d 100644 --- a/src/demo-app/list/list-demo.scss +++ b/src/demo-app/list/list-demo.scss @@ -13,14 +13,14 @@ margin-top: 20px; } + md-icon { + color: rgba(0,0,0,0.12); + } + [md-list-icon] { color: white; background: rgba(0,0,0,0.3); } - - i { - color: rgba(0,0,0,0.12); - } } .demo-secondary-text { diff --git a/src/demo-app/toolbar/toolbar-demo.html b/src/demo-app/toolbar/toolbar-demo.html index 2426c6113e83..e6d3f30c5172 100644 --- a/src/demo-app/toolbar/toolbar-demo.html +++ b/src/demo-app/toolbar/toolbar-demo.html @@ -2,34 +2,34 @@

- menu + menu Default Toolbar - code + code

- menu + menu Primary Toolbar - code + code

- menu + menu Accent Toolbar - code + code

@@ -51,7 +51,7 @@ - verified_user + verified_user @@ -59,8 +59,8 @@ - favorite - delete + favorite + delete

diff --git a/src/demo-app/toolbar/toolbar-demo.ts b/src/demo-app/toolbar/toolbar-demo.ts index b23f60335e84..a2bfedcea988 100644 --- a/src/demo-app/toolbar/toolbar-demo.ts +++ b/src/demo-app/toolbar/toolbar-demo.ts @@ -1,11 +1,12 @@ import {Component} from 'angular2/core'; +import {MdIcon} from '../../components/icon/icon'; import {MdToolbar} from '../../components/toolbar/toolbar'; @Component({ selector: 'toolbar-demo', templateUrl: 'demo-app/toolbar/toolbar-demo.html', styleUrls: ['demo-app/toolbar/toolbar-demo.css'], - directives: [MdToolbar] + directives: [MdToolbar, MdIcon] }) export class ToolbarDemo { From dbe3cc5ec5a835ff26bd589bf6cbef1014faa0aa Mon Sep 17 00:00:00 2001 From: Kara Erickson Date: Tue, 26 Apr 2016 11:05:04 -0700 Subject: [PATCH 12/23] fix(sidenav): add min width for empty navs --- src/components/sidenav/sidenav.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/sidenav/sidenav.scss b/src/components/sidenav/sidenav.scss index 4d808b44ea75..18e38aee1784 100644 --- a/src/components/sidenav/sidenav.scss +++ b/src/components/sidenav/sidenav.scss @@ -107,6 +107,7 @@ $md-sidenav-push-background-color: md-color($md-background, dialog) !default; top: 0; bottom: 0; z-index: 3; + min-width: 5%; background-color: $md-sidenav-background-color; From ca5339bdc9e68d9c7518d1323e8e70a2fc501ae0 Mon Sep 17 00:00:00 2001 From: Kara Erickson Date: Thu, 28 Apr 2016 09:09:45 -0700 Subject: [PATCH 13/23] chore(demo): update to hammer on google cdn --- src/index.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/index.html b/src/index.html index db5c2f8b16fb..dc88807a9014 100644 --- a/src/index.html +++ b/src/index.html @@ -24,8 +24,7 @@ - - + - - {{content-for 'head'}} + Loading... + + + + - - - - + + diff --git a/src/main.ts b/src/main.ts index ade68ee21211..babe0c1e1c58 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,14 +1,14 @@ -import {bootstrap} from 'angular2/platform/browser'; -import {HAMMER_GESTURE_CONFIG} from 'angular2/src/platform/browser_common'; +import {bootstrap} from '@angular/platform-browser-dynamic'; +import {HAMMER_GESTURE_CONFIG} from '@angular/platform-browser'; import {DemoApp} from './demo-app/demo-app'; -import {HTTP_PROVIDERS} from 'angular2/http'; -import {ROUTER_PROVIDERS} from 'angular2/router'; +import {HTTP_PROVIDERS} from '@angular/http'; +import {ROUTER_PROVIDERS} from '@angular/router'; import {MdIconRegistry} from './components/icon/icon-registry'; import {OVERLAY_CONTAINER_TOKEN} from './core/overlay/overlay'; import {MdLiveAnnouncer} from './core/live-announcer/live-announcer'; -import {provide} from 'angular2/core'; +import {provide} from '@angular/core'; import {createOverlayContainer} from './core/overlay/overlay-container'; -import {Renderer} from 'angular2/core'; +import {Renderer} from '@angular/core'; import {MdGestureConfig} from './core/gestures/MdGestureConfig'; import 'rxjs/Rx'; diff --git a/src/system-config.ts b/src/system-config.ts new file mode 100644 index 000000000000..5a7d5f604a97 --- /dev/null +++ b/src/system-config.ts @@ -0,0 +1,66 @@ +/*********************************************************************************************** + * User Configuration. + **********************************************************************************************/ +/** Map relative paths to URLs. */ +const map: any = { +}; + +/** User packages configuration. */ +const packages: any = { + 'demo-app': { + format: 'cjs', + defaultExtension: 'js' + }, + 'components': { + format: 'cjs', + defaultExtension: 'js' + }, + 'core': { + format: 'cjs', + defaultExtension: 'js' + }, +}; + +//////////////////////////////////////////////////////////////////////////////////////////////// +/*********************************************************************************************** + * Everything underneath this line is managed by the CLI. + **********************************************************************************************/ +const barrels: string[] = [ + // Angular specific barrels. + '@angular/core', + '@angular/common', + '@angular/compiler', + '@angular/http', + '@angular/router', + '@angular/platform-browser', + '@angular/platform-browser-dynamic', + + // Thirdparty barrels. + 'rxjs', + + // App specific barrels. + 'app', + 'app/shared', + /** @cli-barrel */ +]; + +const _cliSystemConfig = {}; +barrels.forEach((barrelName: string) => { + ( _cliSystemConfig)[barrelName] = { main: 'index' }; +}); + +/** Type declaration for ambient System. */ +declare var System: any; + +// Apply the CLI SystemJS configuration. +System.config({ + map: { + '@angular': 'vendor/@angular', + 'rxjs': 'vendor/rxjs', + 'main': 'main.js' + }, + packages: _cliSystemConfig +}); + +// Apply the user's configuration. +System.config({ map, packages }); diff --git a/test/karma-test-shim.js b/test/karma-test-shim.js index 63afe69caf15..e0809e59ed33 100644 --- a/test/karma-test-shim.js +++ b/test/karma-test-shim.js @@ -1,91 +1,57 @@ +/*global jasmine, __karma__, window*/ Error.stackTraceLimit = Infinity; +jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000; -jasmine.DEFAULT_TIMEOUT_INTERVAL = 4000; +__karma__.loaded = function () { +}; -__karma__.loaded = function() {}; +var distPath = '/base/dist/'; - -/** - * Gets map of module alias to location or package. - * @param dir Directory name under `src/` for create a map for. - */ -function getPathsMap(dir) { - return Object.keys(window.__karma__.files) - .filter(not(isSpecFile)) - .filter(function(x) { return new RegExp('^/base/dist/' + dir + '/.*\\.js$').test(x); }) - .reduce(function(pathsMapping, appPath) { - var pathToReplace = new RegExp('^/base/dist/' + dir + '/'); - var moduleName = appPath.replace(pathToReplace, './').replace(/\.js$/, ''); - pathsMapping[moduleName] = appPath + '?' + window.__karma__.files[appPath]; - return pathsMapping; - }, {}); +function isJsFile(path) { + return path.slice(-3) == '.js'; } -System.config({ - packages: { - 'base/dist/components': { - defaultExtension: false, - format: 'cjs', - map: getPathsMap('components') - }, - 'base/dist/core': { - defaultExtension: false, - format: 'cjs', - map: getPathsMap('core') - }, - } -}); +function isSpecFile(path) { + return path.slice(-8) == '.spec.js'; +} +function isMaterialFile(path) { + return isJsFile(path) && path.indexOf('vendor') == -1; +} -/** - * Bootstrap the browser testing providers from Angular2. The equivalent code in TypeScript - * would be: - * - * - * import {setBaseTestProviders} from 'angular2/testing'; - * import * as browser from 'angular2/platform/testing/browser'; - * - * setBaseTestProviders(browser.TEST_BROWSER_PLATFORM_PROVIDERS, - * browser.TEST_BROWSER_APPLICATION_PROVIDERS); - * - * - * See https://github.com/angular/angular/blob/master/CHANGELOG.md#200-beta2-2016-01-28 - * - * Followed by the normal import of all spec files, then bootstrap Karma. - */ -Promise.all([ - System.import('angular2/testing'), - System.import('angular2/platform/testing/browser'), -]).then(function(imports) { - var testing = imports[0]; - var browser = imports[1]; - testing.setBaseTestProviders(browser.TEST_BROWSER_PLATFORM_PROVIDERS, - browser.TEST_BROWSER_APPLICATION_PROVIDERS); +var allSpecFiles = Object.keys(window.__karma__.files) + .filter(isSpecFile) + .filter(isMaterialFile); - return Promise.all( - Object.keys(window.__karma__.files) - .filter(isSpecFile) - .map(function(moduleName) { - return System.import(moduleName).then(function(module) { - if (module.hasOwnProperty('main')) { - return module.main(); - } else { - return module; - } - }); - })); -}).then(function() { - __karma__.start(); -}, function(error) { - __karma__.error(error.stack || error); +// Load our SystemJS configuration. +System.config({ + baseURL: distPath }); -function isSpecFile(path) { - return /\.spec\.js$/.test(path); -} - -function not(fn) { - return function() { - return !fn.apply(this, arguments); - }; -} +System.import('system-config.js').then(function() { + // Load and configure the TestComponentBuilder. + return Promise.all([ + System.import('@angular/core/testing'), + System.import('@angular/platform-browser-dynamic/testing') + ]).then(function (providers) { + var testing = providers[0]; + var testingBrowser = providers[1]; + + testing.setBaseTestProviders(testingBrowser.TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS, + testingBrowser.TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS); + }); +}).then(function() { + // Finally, load all spec files. + // This will run the tests directly. + return Promise.all( + allSpecFiles.map(function (moduleName) { + return System.import(moduleName).then(function(module) { + // TODO(jelbourn): remove `main` method from tests and this check. + if (module.hasOwnProperty('main')) { + return module.main(); + } else { + return module; + } + }); + })); +}).then(__karma__.start, __karma__.error); diff --git a/test/karma.config.ts b/test/karma.config.ts index 82a5957b9ae4..f59485659c5b 100644 --- a/test/karma.config.ts +++ b/test/karma.config.ts @@ -19,22 +19,13 @@ export function config(config) { require('karma-firefox-launcher'), ], files: [ - {pattern: 'node_modules/es6-shim/es6-shim.min.js', included: true, watched: false}, - {pattern: 'node_modules/systemjs/dist/system-polyfills.js', included: true, watched: false}, - 'node_modules/angular2/es6/dev/src/testing/shims_for_IE.js', - - // Angular 2 polyfills *must* be loaded after es6-shim and system-polyfills in order to - // setup the monkey-patches for zones. - {pattern: 'node_modules/angular2/bundles/angular2-polyfills.js', included: true, watched: false}, - {pattern: 'node_modules/zone.js/dist/jasmine-patch.js', included: true, watched: false}, - {pattern: 'node_modules/zone.js/dist/async-test.js', included: true, watched: false}, - {pattern: 'node_modules/zone.js/dist/fake-async-test.js', included: true, watched: false}, - {pattern: 'node_modules/systemjs/dist/system.src.js', included: true, watched: false}, - {pattern: 'node_modules/rxjs/bundles/Rx.js', included: true, watched: false}, - 'node_modules/reflect-metadata/Reflect.js', - {pattern: 'node_modules/angular2/bundles/angular2.dev.js', included: true, watched: false}, - {pattern: 'node_modules/angular2/bundles/http.dev.js', included: true, watched: false}, - {pattern: 'node_modules/angular2/bundles/testing.dev.js', included: true, watched: false}, + {pattern: 'dist/vendor/es6-shim/es6-shim.js', included: true, watched: false}, + {pattern: 'dist/vendor/reflect-metadata/Reflect.js', included: true, watched: false}, + {pattern: 'dist/vendor/systemjs/dist/system-polyfills.js', included: true, watched: false}, + {pattern: 'dist/vendor/systemjs/dist/system.src.js', included: true, watched: false}, + {pattern: 'dist/vendor/zone.js/dist/zone.js', included: true, watched: false}, + {pattern: 'dist/vendor/zone.js/dist/async-test.js', included: true, watched: false}, + {pattern: 'dist/vendor/zone.js/dist/fake-async-test.js', included: true, watched: false}, {pattern: 'test/karma-test-shim.js', included: true, watched: true}, @@ -52,10 +43,8 @@ export function config(config) { ], proxies: { // required for component assests fetched by Angular's compiler - '/demo-app/': '/base/dist/demo-app/', '/components/': '/base/dist/components/', '/core/': '/base/dist/core/', - '/directives/': '/base/dist/directives/', }, customLaunchers: customLaunchers, From 5eab2b310a788791e3fd92e567b706a666479dad Mon Sep 17 00:00:00 2001 From: Kara Date: Tue, 3 May 2016 23:23:43 -0700 Subject: [PATCH 18/23] chore(icon): export icon registry as part of icon (#366) --- src/components/icon/icon.ts | 2 +- src/demo-app/icon/icon-demo.ts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/icon/icon.ts b/src/components/icon/icon.ts index 7d312ceea969..87c2a7171ab1 100644 --- a/src/components/icon/icon.ts +++ b/src/components/icon/icon.ts @@ -11,7 +11,7 @@ import { AfterViewChecked } from '@angular/core'; import {MdIconRegistry} from './icon-registry'; - +export {MdIconRegistry} from './icon-registry'; /** Exception thrown when an invalid icon name is passed to an md-icon component. */ diff --git a/src/demo-app/icon/icon-demo.ts b/src/demo-app/icon/icon-demo.ts index c7df43059906..d594a82f1a3b 100644 --- a/src/demo-app/icon/icon-demo.ts +++ b/src/demo-app/icon/icon-demo.ts @@ -1,6 +1,5 @@ import {Component, ViewEncapsulation} from '@angular/core'; -import {MdIcon} from '../../components/icon/icon'; -import {MdIconRegistry} from '../../components/icon/icon-registry'; +import {MdIcon, MdIconRegistry} from '../../components/icon/icon'; @Component({ selector: 'md-icon-demo', From 4f9051f5312f5e6621736c0d0017c33ebb1591e7 Mon Sep 17 00:00:00 2001 From: Jeremy Elbourn Date: Tue, 3 May 2016 23:49:24 -0700 Subject: [PATCH 19/23] chore: release alpha.4 --- CHANGELOG.md | 24 +++++++++++++++++++++ package.json | 2 +- src/components/button/package.json | 5 ++--- src/components/card/package.json | 5 ++--- src/components/checkbox/package.json | 5 ++--- src/components/grid-list/package.json | 5 ++--- src/components/icon/package.json | 6 +++--- src/components/input/package.json | 5 ++--- src/components/list/package.json | 5 ++--- src/components/progress-bar/package.json | 5 ++--- src/components/progress-circle/package.json | 5 ++--- src/components/radio/package.json | 5 ++--- src/components/sidenav/package.json | 5 ++--- src/components/toolbar/package.json | 4 ++-- src/core/package.json | 5 +++-- 15 files changed, 53 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c21de55c68ca..074b60b6de58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,27 @@ + +# [2.0.0-alpha.4 mahogany-tambourine](https://github.com/angular/material2/compare/2.0.0-alpha.3...v2.0.0-alpha.4) (2016-05-04) + + +### Bug Fixes + +* **button:** cleaned up button theming([0672356](https://github.com/angular/material2/commit/0672356)) +* **checkbox:** fix horizontal spacing between checkboxes and radio buttons([f4a7266](https://github.com/angular/material2/commit/f4a7266)) +* **sidenav:** add min width for empty navs([dbe3cc5](https://github.com/angular/material2/commit/dbe3cc5)) +* **sidenav:** prevent content from scrolling when sidenav is open([fea5923](https://github.com/angular/material2/commit/fea5923)) +* **theme:** new, more delightful default theme([2f3e9db](https://github.com/angular/material2/commit/2f3e9db)) +* update to @angular 2.0.0-rc.0 ([#384](https://github.com/angular/material2/issues/384))([04c8a1f](https://github.com/angular/material2/commit/04c8a1f)) + + +### Features + +* **icon:** initial md-icon implementation ([#281](https://github.com/angular/material2/issues/281))([a094a33](https://github.com/angular/material2/commit/a094a33)) +* **card:** add alignment shortcuts for md-card-actions([77be2df](https://github.com/angular/material2/commit/77be2df)) +* **grid-list:** basic scaffold for grid list (unreleased)([a9e1fa5](https://github.com/angular/material2/commit/a9e1fa5)) +* **list:** add icon support([7b3698c](https://github.com/angular/material2/commit/7b3698c)) +* **sidenav:** add fullscreen mode([aa6c740](https://github.com/angular/material2/commit/aa6c740)) + + + # [2.0.0-alpha.3 cotton-candelabrum](https://github.com/angular/material2/compare/2.0.0-alpha.2...v2.0.0-alpha.3) (2016-04-21) diff --git a/package.json b/package.json index f536813c2cc8..b2c0c4bc99a1 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "inline-resources": "gulp inline-resources", "deploy": "firebase deploy" }, - "version": "2.0.0-alpha.3", + "version": "2.0.0-alpha.4", "license": "MIT", "engines": { "node": ">= 4.2.1 < 5" diff --git a/src/components/button/package.json b/src/components/button/package.json index 2a3d0925a177..5b7730fcd248 100644 --- a/src/components/button/package.json +++ b/src/components/button/package.json @@ -1,6 +1,6 @@ { "name": "@angular2-material/button", - "version": "2.0.0-alpha.3", + "version": "2.0.0-alpha.4", "description": "Angular 2 Material button", "main": "./button.js", "typings": "./button.d.ts", @@ -23,7 +23,6 @@ }, "homepage": "https://github.com/angular/material2#readme", "peerDependencies": { - "angular2": "^2.0.0-beta.8", - "@angular2-material/core": "2.0.0-alpha.3" + "@angular2-material/core": "2.0.0-alpha.4" } } diff --git a/src/components/card/package.json b/src/components/card/package.json index edcffa006a20..cce49a85d3bb 100644 --- a/src/components/card/package.json +++ b/src/components/card/package.json @@ -1,6 +1,6 @@ { "name": "@angular2-material/card", - "version": "2.0.0-alpha.3", + "version": "2.0.0-alpha.4", "description": "Angular 2 Material card", "main": "./card.js", "typings": "./card.d.ts", @@ -21,7 +21,6 @@ }, "homepage": "https://github.com/angular/material2#readme", "peerDependencies": { - "angular2": "^2.0.0-beta.8", - "@angular2-material/core": "2.0.0-alpha.3" + "@angular2-material/core": "2.0.0-alpha.4" } } diff --git a/src/components/checkbox/package.json b/src/components/checkbox/package.json index bd98504fe03b..e83a9db0b210 100644 --- a/src/components/checkbox/package.json +++ b/src/components/checkbox/package.json @@ -1,6 +1,6 @@ { "name": "@angular2-material/checkbox", - "version": "2.0.0-alpha.3", + "version": "2.0.0-alpha.4", "description": "Angular 2 Material checkbox", "main": "./checkbox.js", "typings": "./checkbox.d.ts", @@ -21,7 +21,6 @@ }, "homepage": "https://github.com/angular/material2#readme", "peerDependencies": { - "angular2": "^2.0.0-beta.8", - "@angular2-material/core": "2.0.0-alpha.3" + "@angular2-material/core": "2.0.0-alpha.4" } } diff --git a/src/components/grid-list/package.json b/src/components/grid-list/package.json index dabefabd9fe7..0b0271551049 100644 --- a/src/components/grid-list/package.json +++ b/src/components/grid-list/package.json @@ -1,6 +1,6 @@ { "name": "@angular2-material/grid-list", - "version": "2.0.0-alpha.3", + "version": "2.0.0-alpha.4", "description": "Angular 2 Material grid list", "main": "./grid-list.js", "typings": "./grid-list.d.ts", @@ -23,7 +23,6 @@ }, "homepage": "https://github.com/angular/material2#readme", "peerDependencies": { - "angular2": "^2.0.0-beta.8", - "@angular2-material/core": "2.0.0-alpha.3" + "@angular2-material/core": "2.0.0-alpha.4" } } diff --git a/src/components/icon/package.json b/src/components/icon/package.json index a0e8b8c790f5..a5efd72be8f9 100644 --- a/src/components/icon/package.json +++ b/src/components/icon/package.json @@ -1,6 +1,6 @@ { "name": "@angular2-material/icon", - "version": "2.0.0-alpha.3", + "version": "2.0.0-alpha.4", "description": "Angular 2 Material icon", "main": "./icon.js", "typings": "./icon.d.ts", @@ -21,7 +21,7 @@ }, "homepage": "https://github.com/angular/material2#readme", "peerDependencies": { - "angular2": "^2.0.0-beta.8", - "@angular2-material/core": "2.0.0-alpha.3" + "@angular/http": "2.0.0-rc.0", + "@angular2-material/core": "2.0.0-alpha.4" } } diff --git a/src/components/input/package.json b/src/components/input/package.json index 6af76afaf14c..596a548f1dd7 100644 --- a/src/components/input/package.json +++ b/src/components/input/package.json @@ -1,6 +1,6 @@ { "name": "@angular2-material/input", - "version": "2.0.0-alpha.3", + "version": "2.0.0-alpha.4", "description": "Angular 2 Material input", "main": "./input.js", "typings": "./input.d.ts", @@ -23,7 +23,6 @@ }, "homepage": "https://github.com/angular/material2#readme", "peerDependencies": { - "angular2": "^2.0.0-beta.8", - "@angular2-material/core": "2.0.0-alpha.3" + "@angular2-material/core": "2.0.0-alpha.4" } } diff --git a/src/components/list/package.json b/src/components/list/package.json index dc406f2e406e..9dead14e24b7 100644 --- a/src/components/list/package.json +++ b/src/components/list/package.json @@ -1,6 +1,6 @@ { "name": "@angular2-material/list", - "version": "2.0.0-alpha.3", + "version": "2.0.0-alpha.4", "description": "Angular 2 Material list", "main": "./list.js", "typings": "./list.d.ts", @@ -21,7 +21,6 @@ }, "homepage": "https://github.com/angular/material2#readme", "peerDependencies": { - "angular2": "^2.0.0-beta.8", - "@angular2-material/core": "2.0.0-alpha.3" + "@angular2-material/core": "2.0.0-alpha.4" } } diff --git a/src/components/progress-bar/package.json b/src/components/progress-bar/package.json index 3e85e1069093..066aa9641d6e 100644 --- a/src/components/progress-bar/package.json +++ b/src/components/progress-bar/package.json @@ -1,6 +1,6 @@ { "name": "@angular2-material/progress-bar", - "version": "2.0.0-alpha.3", + "version": "2.0.0-alpha.4", "description": "Angular 2 Material progress-bar", "main": "./progress-bar.js", "typings": "./progress-bar.d.ts", @@ -23,7 +23,6 @@ }, "homepage": "https://github.com/angular/material2#readme", "peerDependencies": { - "angular2": "^2.0.0-beta.8", - "@angular2-material/core": "2.0.0-alpha.3" + "@angular2-material/core": "2.0.0-alpha.4" } } diff --git a/src/components/progress-circle/package.json b/src/components/progress-circle/package.json index 47f951ec3f51..357e05deea74 100644 --- a/src/components/progress-circle/package.json +++ b/src/components/progress-circle/package.json @@ -1,6 +1,6 @@ { "name": "@angular2-material/progress-circle", - "version": "2.0.0-alpha.3", + "version": "2.0.0-alpha.4", "description": "Angular 2 Material progress-circle", "main": "./progress-circle.js", "typings": "./progress-circle.d.ts", @@ -22,7 +22,6 @@ }, "homepage": "https://github.com/angular/material2#readme", "peerDependencies": { - "angular2": "^2.0.0-beta.8", - "@angular2-material/core": "2.0.0-alpha.3" + "@angular2-material/core": "2.0.0-alpha.4" } } diff --git a/src/components/radio/package.json b/src/components/radio/package.json index 23510f4d2129..e5215d22e9e6 100644 --- a/src/components/radio/package.json +++ b/src/components/radio/package.json @@ -1,6 +1,6 @@ { "name": "@angular2-material/radio", - "version": "2.0.0-alpha.3", + "version": "2.0.0-alpha.4", "description": "Angular 2 Material radio", "main": "./radio.js", "typings": "./radio.d.ts", @@ -21,7 +21,6 @@ }, "homepage": "https://github.com/angular/material2#readme", "peerDependencies": { - "angular2": "^2.0.0-beta.8", - "@angular2-material/core": "2.0.0-alpha.3" + "@angular2-material/core": "2.0.0-alpha.4" } } diff --git a/src/components/sidenav/package.json b/src/components/sidenav/package.json index 810e818005b5..543f20e4dfe3 100644 --- a/src/components/sidenav/package.json +++ b/src/components/sidenav/package.json @@ -1,6 +1,6 @@ { "name": "@angular2-material/sidenav", - "version": "2.0.0-alpha.3", + "version": "2.0.0-alpha.4", "description": "Angular 2 Material sidenav", "main": "./sidenav.js", "typings": "./sidenav.d.ts", @@ -24,7 +24,6 @@ }, "homepage": "https://github.com/angular/material2#readme", "peerDependencies": { - "angular2": "^2.0.0-beta.8", - "@angular2-material/core": "2.0.0-alpha.3" + "@angular2-material/core": "2.0.0-alpha.4" } } diff --git a/src/components/toolbar/package.json b/src/components/toolbar/package.json index 49d8c5ce7d39..f0c146c2b007 100644 --- a/src/components/toolbar/package.json +++ b/src/components/toolbar/package.json @@ -1,6 +1,6 @@ { "name": "@angular2-material/toolbar", - "version": "2.0.0-alpha.3", + "version": "2.0.0-alpha.4", "description": "Angular 2 Material toolbar", "main": "./toolbar.js", "typings": "./toolbar.d.ts", @@ -22,6 +22,6 @@ }, "homepage": "https://github.com/angular/material2#readme", "peerDependencies": { - "angular2": "^2.0.0-beta.8" + "@angular2-material/core": "2.0.0-alpha.4" } } diff --git a/src/core/package.json b/src/core/package.json index 6466e33b35b5..04ededf4e241 100644 --- a/src/core/package.json +++ b/src/core/package.json @@ -1,6 +1,6 @@ { "name": "@angular2-material/core", - "version": "2.0.0-alpha.3", + "version": "2.0.0-alpha.4", "description": "Angular 2 Material core", "main": "./core.js", "typings": "./core.d.ts", @@ -20,6 +20,7 @@ }, "homepage": "https://github.com/angular/material2#readme", "peerDependencies": { - "angular2": "^2.0.0-beta.8" + "@angular/core": "^2.0.0-rc.0", + "@angular/common": "^2.0.0-rc.0" } } From c56624289da6e29550e27a4e5f7d79034789ef04 Mon Sep 17 00:00:00 2001 From: Kara Erickson Date: Thu, 5 May 2016 19:59:41 -0700 Subject: [PATCH 20/23] fix(sidenav): add scrolling style to enable momentum scroll --- src/components/sidenav/sidenav.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/sidenav/sidenav.scss b/src/components/sidenav/sidenav.scss index 8e9a710faa81..ae171487333a 100644 --- a/src/components/sidenav/sidenav.scss +++ b/src/components/sidenav/sidenav.scss @@ -60,6 +60,8 @@ $md-sidenav-push-background-color: md-color($md-background, dialog) !default; @include md-stacking-context(); box-sizing: border-box; + overflow-y: scroll; + -webkit-overflow-scrolling: touch; // TODO(hansl): Update this with a more robust solution. &[fullscreen] { From 0f89b8d47ee2ffc8ed050530866626a684c8b856 Mon Sep 17 00:00:00 2001 From: Kara Erickson Date: Tue, 10 May 2016 09:38:11 -0700 Subject: [PATCH 21/23] feat(grid-list): add grid-list component --- src/components/grid-list/grid-list-errors.ts | 26 +++ src/components/grid-list/grid-list.html | 4 +- src/components/grid-list/grid-list.scss | 67 ++++++ src/components/grid-list/grid-list.ts | 154 ++++++++++++- src/components/grid-list/grid-tile.html | 4 + src/components/grid-list/grid-tile.ts | 53 +++++ src/components/grid-list/tile-coordinator.ts | 142 ++++++++++++ src/components/grid-list/tile-styler.ts | 218 +++++++++++++++++++ src/demo-app/grid-list/grid-list-demo.html | 62 +++++- src/demo-app/grid-list/grid-list-demo.scss | 15 ++ src/demo-app/grid-list/grid-list-demo.ts | 23 +- 11 files changed, 761 insertions(+), 7 deletions(-) create mode 100644 src/components/grid-list/grid-list-errors.ts create mode 100644 src/components/grid-list/grid-tile.html create mode 100644 src/components/grid-list/grid-tile.ts create mode 100644 src/components/grid-list/tile-coordinator.ts create mode 100644 src/components/grid-list/tile-styler.ts diff --git a/src/components/grid-list/grid-list-errors.ts b/src/components/grid-list/grid-list-errors.ts new file mode 100644 index 000000000000..65dc797b0283 --- /dev/null +++ b/src/components/grid-list/grid-list-errors.ts @@ -0,0 +1,26 @@ +/** + * Exception thrown when cols property is missing from grid-list + */ +export class MdGridListColsError extends Error { + constructor() { + super(`md-grid-list: must pass in number of columns. Example: `); + } +} + +/** + * Exception thrown when a tile's colspan is longer than the number of cols in list + */ +export class MdGridTileTooWideError extends Error { + constructor(cols: number, listLength: number) { + super(`Tile with colspan ${cols} is wider than grid with cols="${listLength}".`); + } +} + +/** + * Exception thrown when an invalid ratio is passed in as a rowHeight + */ +export class MdGridListBadRatioError extends Error { + constructor(value: string) { + super(`md-grid-list: invalid ratio given for row-height: "${value}"`); + } +} diff --git a/src/components/grid-list/grid-list.html b/src/components/grid-list/grid-list.html index 20ca1bf7e866..68ebf7c55c5f 100644 --- a/src/components/grid-list/grid-list.html +++ b/src/components/grid-list/grid-list.html @@ -1 +1,3 @@ -I'm a grid list! \ No newline at end of file +
+ +
\ No newline at end of file diff --git a/src/components/grid-list/grid-list.scss b/src/components/grid-list/grid-list.scss index e69de29bb2d1..d2372096a135 100644 --- a/src/components/grid-list/grid-list.scss +++ b/src/components/grid-list/grid-list.scss @@ -0,0 +1,67 @@ +// TODO(kara): Review this to see if MD spec updates are needed + +md-grid-list { + display: block; + position: relative; +} + +md-grid-tile { + display: block; + position: absolute; + + figure { + display: flex; + position: absolute; + + align-items: center; + justify-content: center; + height: 100%; + + top: 0; + right: 0; + bottom: 0; + left: 0; + + padding: 0; + margin: 0; + } + + // Headers & footers + md-grid-tile-header, + md-grid-tile-footer { + display: flex; + flex-direction: row; + align-items: center; + height: 48px; + color: #fff; + background: rgba(0, 0, 0, 0.18); + overflow: hidden; + + // Positioning + position: absolute; + left: 0; + right: 0; + + h3, + h4 { + font-weight: 400; + margin: 0 0 0 16px; + } + + h3 { + font-size: 14px; + } + + h4 { + font-size: 12px; + } + } + + md-grid-tile-header { + top: 0; + } + + md-grid-tile-footer { + bottom: 0; + } +} diff --git a/src/components/grid-list/grid-list.ts b/src/components/grid-list/grid-list.ts index 8853bbdca372..dc612c3f3d23 100644 --- a/src/components/grid-list/grid-list.ts +++ b/src/components/grid-list/grid-list.ts @@ -1,8 +1,158 @@ -import {Component} from '@angular/core'; +import { + Component, + ViewEncapsulation, + AfterContentChecked, + OnInit, + Input, + ContentChildren, + QueryList, + Renderer, + ElementRef +} from '@angular/core'; +import {MdGridTile} from './grid-tile'; +import {TileCoordinator} from './tile-coordinator'; +import { + TileStyler, + FitTileStyler, + RatioTileStyler, + FixedTileStyler +} from './tile-styler'; +import {MdGridListColsError} from './grid-list-errors'; +import {Dir} from '../../core/rtl/dir'; + +// TODO(kara): Conditional (responsive) column count / row size. +// TODO(kara): Re-layout on window resize / media change (debounced). +// TODO(kara): gridTileHeader and gridTileFooter. + +const MD_FIT_MODE = 'fit'; @Component({ selector: 'md-grid-list', + host: { 'role': 'list' }, templateUrl: './components/grid-list/grid-list.html', styleUrls: ['./components/grid-list/grid-list.css'], + encapsulation: ViewEncapsulation.None, }) -export class MdGridList {} +export class MdGridList implements OnInit, AfterContentChecked { + /** Number of columns being rendered. */ + _cols: number; + + /** Row height value passed in by user. This can be one of three types: + * - Number value (ex: "100px"): sets a fixed row height to that value + * - Ratio value (ex: "4:3"): sets the row height based on width:height ratio + * - "Fit" mode (ex: "fit"): sets the row height to total height divided by number of rows + * */ + _rowHeight: string; + + /** The amount of space between tiles. This will be something like '5px' or '2em'. */ + _gutter: string = '1px'; + + /** Sets position and size styles for a tile */ + _tileStyler: TileStyler; + + /** Query list of tiles that are being rendered. */ + @ContentChildren(MdGridTile) _tiles: QueryList; + + constructor(private _renderer: Renderer, private _element: ElementRef, + private _dir: Dir) {} + + @Input() + 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); + } + + /** Set internal representation of row height from the user-provided value. */ + @Input() + set rowHeight(value: string | number) { + this._rowHeight = coerceToString(value); + this._setTileStyler(); + } + + ngOnInit() { + this._checkCols(); + this._checkRowHeight(); + } + + /** The layout calculation is fairly cheap if nothing changes, so there's little cost + * to run it frequently. */ + ngAfterContentChecked() { + this._layoutTiles(); + } + + /** Throw a friendly error if cols property is missing */ + private _checkCols() { + if (!this.cols) { + throw new MdGridListColsError(); + } + } + + /** Default to equal width:height if rowHeight property is missing */ + private _checkRowHeight(): void { + if (!this._rowHeight) { + this._tileStyler = new RatioTileStyler('1:1'); + } + } + + /** Creates correct Tile Styler subtype based on rowHeight passed in by user */ + private _setTileStyler(): void { + if (this._rowHeight === MD_FIT_MODE) { + this._tileStyler = new FitTileStyler(); + } else if (this._rowHeight && this._rowHeight.match(/:/g)) { + this._tileStyler = new RatioTileStyler(this._rowHeight); + } else { + this._tileStyler = new FixedTileStyler(this._rowHeight); + } + } + + /** Computes and applies the size and position for all children grid tiles. */ + private _layoutTiles(): void { + let tiles = this._tiles.toArray(); + let tracker = new TileCoordinator(this.cols, tiles); + this._tileStyler.init(this.gutterSize, tracker, this.cols, this._dir); + + for (let i = 0; i < tiles.length; i++) { + let pos = tracker.positions[i]; + let tile = tiles[i]; + this._tileStyler.setStyle(tile, pos.row, pos.col); + } + this.setListStyle(this._tileStyler.getComputedHeight()); + } + + /** Sets style on the main grid-list element, given the style name and value. + * @internal + */ + setListStyle(style: [string, string]): void { + if (style) { + this._renderer.setElementStyle(this._element.nativeElement, style[0], style[1]); + } + } +} + +/** Converts values into strings. Falsy values become empty strings. + * @internal + */ +export function coerceToString(value: string | number): string { + return `${value || ''}`; +} + +/** Converts a value that might be a string into a number. + * @internal + */ +export function coerceToNumber(value: string | number): number { + return typeof value === 'string' ? parseInt(value, 10) : value; +} + +export const MD_GRID_LIST_DIRECTIVES: any[] = [MdGridList, MdGridTile]; diff --git a/src/components/grid-list/grid-tile.html b/src/components/grid-list/grid-tile.html new file mode 100644 index 000000000000..cadd734bc6ab --- /dev/null +++ b/src/components/grid-list/grid-tile.html @@ -0,0 +1,4 @@ + +
+ +
\ No newline at end of file diff --git a/src/components/grid-list/grid-tile.ts b/src/components/grid-list/grid-tile.ts new file mode 100644 index 000000000000..419c11ff1c5f --- /dev/null +++ b/src/components/grid-list/grid-tile.ts @@ -0,0 +1,53 @@ +import { + Component, + ViewEncapsulation, + Renderer, + ElementRef, + Input, +} from '@angular/core'; + +import {coerceToNumber} from './grid-list'; + +@Component({ + selector: 'md-grid-tile', + host: { 'role': 'listitem' }, + templateUrl: './components/grid-list/grid-tile.html', + styleUrls: ['./components/grid-list/grid-list.css'], + encapsulation: ViewEncapsulation.None, +}) +export class MdGridTile { + _rowspan: number = 1; + _colspan: number = 1; + _element: HTMLElement; + + constructor(private _renderer: Renderer, element: ElementRef) { + this._element = element.nativeElement; + } + + @Input() + get rowspan() { + return this._rowspan; + } + + @Input() + get colspan() { + return this._colspan; + } + + set rowspan(value) { + this._rowspan = coerceToNumber(value); + } + + set colspan(value) { + this._colspan = coerceToNumber(value); + } + + /** Sets the style of the grid-tile element. Needs to be set manually to avoid + * "Changed after checked" errors that would occur with HostBinding. + * @internal + */ + setStyle(property: string, value: string): void { + this._renderer.setElementStyle(this._element, property, value); + } + +} diff --git a/src/components/grid-list/tile-coordinator.ts b/src/components/grid-list/tile-coordinator.ts new file mode 100644 index 000000000000..0e477838893c --- /dev/null +++ b/src/components/grid-list/tile-coordinator.ts @@ -0,0 +1,142 @@ +import {MdGridTile} from './grid-tile'; +import {MdGridTileTooWideError} from './grid-list-errors'; + +/** + * Class for determining, from a list of tiles, the (row, col) position of each of those tiles + * in the grid. This is necessary (rather than just rendering the tiles in normal document flow) + * because the tiles can have a rowspan. + * + * The positioning algorithm greedily places each tile as soon as it encounters a gap in the grid + * large enough to accommodate it so that the tiles still render in the same order in which they + * are given. + * + * The basis of the algorithm is the use of an array to track the already placed tiles. Each + * element of the array corresponds to a column, and the value indicates how many cells in that + * column are already occupied; zero indicates an empty cell. Moving "down" to the next row + * decrements each value in the tracking array (indicating that the column is one cell closer to + * being free). + */ +export class TileCoordinator { + /** Tracking array (see class description). */ + tracker: number[]; + + /** Index at which the search for the next gap will start. */ + columnIndex: number = 0; + + /** The current row index. */ + rowIndex: number = 0; + + /** Gets the total number of rows occupied by tiles */ + get rowCount(): number { return this.rowIndex + 1; } + + /** Gets the total span of rows occupied by tiles. + * Ex: A list with 1 row that contains a tile with rowspan 2 will have a total rowspan of 2. */ + get rowspan() { + let lastRowMax = Math.max(...this.tracker); + // if any of the tiles has a rowspan that pushes it beyond the total row count, + // add the difference to the rowcount + return lastRowMax > 1 ? this.rowCount + lastRowMax - 1 : this.rowCount; + } + + /** The computed (row, col) position of each tile (the output). */ + positions: TilePosition[]; + + constructor(numColumns: number, tiles: MdGridTile[]) { + this.tracker = new Array(numColumns); + this.tracker.fill(0, 0, this.tracker.length); + + this.positions = tiles.map(tile => this._trackTile(tile)); + } + + /** Calculates the row and col position of a tile. */ + private _trackTile(tile: MdGridTile): TilePosition { + // Find a gap large enough for this tile. + let gapStartIndex = this._findMatchingGap(tile.colspan); + + // Place tile in the resulting gap. + this._markTilePosition(gapStartIndex, tile); + + // The next time we look for a gap, the search will start at columnIndex, which should be + // immediately after the tile that has just been placed. + this.columnIndex = gapStartIndex + tile.colspan; + + return new TilePosition(this.rowIndex, gapStartIndex); + } + + /** Finds the next available space large enough to fit the tile. */ + private _findMatchingGap(tileCols: number): number { + if (tileCols > this.tracker.length) { + throw new MdGridTileTooWideError(tileCols, this.tracker.length); + } + + // Start index is inclusive, end index is exclusive. + let gapStartIndex = -1; + let gapEndIndex = -1; + + // Look for a gap large enough to fit the given tile. Empty spaces are marked with a zero. + do { + // If we've reached the end of the row, go to the next row. + if (this.columnIndex + tileCols > this.tracker.length) { + this._nextRow(); + continue; + } + + gapStartIndex = this.tracker.indexOf(0, this.columnIndex); + + // If there are no more empty spaces in this row at all, move on to the next row. + if (gapStartIndex == -1) { + this._nextRow(); + continue; + } + + gapEndIndex = this._findGapEndIndex(gapStartIndex); + + // If a gap large enough isn't found, we want to start looking immediately after the current + // gap on the next iteration. + this.columnIndex = gapStartIndex + 1; + + // Continue iterating until we find a gap wide enough for this tile. + } while (gapEndIndex - gapStartIndex < tileCols); + return gapStartIndex; + } + + /** Move "down" to the next row. */ + private _nextRow(): void { + this.columnIndex = 0; + this.rowIndex++; + + // Decrement all spaces by one to reflect moving down one row. + for (let i = 0; i < this.tracker.length; i++) { + this.tracker[i] = Math.max(0, this.tracker[i] - 1); + } + } + + /** + * Finds the end index (exclusive) of a gap given the index from which to start looking. + * The gap ends when a non-zero value is found. + */ + private _findGapEndIndex(gapStartIndex: number): number { + for (let i = gapStartIndex + 1; i < this.tracker.length; i++) { + if (this.tracker[i] != 0) { + return i; + } + } + + // The gap ends with the end of the row. + return this.tracker.length; + } + + /** Update the tile tracker to account for the given tile in the given space. */ + private _markTilePosition(start: number, tile: MdGridTile): void { + for (let i = 0; i < tile.colspan; i++) { + this.tracker[start + i] = tile.rowspan; + } + } +} + +/** Simple data structure for tile position (row, col). + * @internal + */ +export class TilePosition { + constructor(public row: number, public col: number) {} +} diff --git a/src/components/grid-list/tile-styler.ts b/src/components/grid-list/tile-styler.ts new file mode 100644 index 000000000000..5e342a5e4e6d --- /dev/null +++ b/src/components/grid-list/tile-styler.ts @@ -0,0 +1,218 @@ +import {MdGridTile} from './grid-tile'; +import {TileCoordinator} from './tile-coordinator'; +import {MdGridListBadRatioError} from './grid-list-errors'; +import {Dir} from '../../core/rtl/dir'; + +/* Sets the style properties for an individual tile, given the position calculated by the +* Tile Coordinator. */ +export class TileStyler { + _gutterSize: string; + _rows: number = 0; + _rowspan: number = 0; + _cols: number; + _dir: Dir; + + /** Adds grid-list layout info once it is available. Cannot be processed in the constructor + * because these properties haven't been calculated by that point. + * @internal + * */ + init(_gutterSize: string, tracker: TileCoordinator, cols: number, dir: Dir): void { + this._gutterSize = normalizeUnits(_gutterSize); + this._rows = tracker.rowCount; + this._rowspan = tracker.rowspan; + this._cols = cols; + this._dir = dir; + } + + /** + * Computes the amount of space a single 1x1 tile would take up (width or height). + * Used as a basis for other calculations. + * @internal + * @param sizePercent Percent of the total grid-list space that one 1x1 tile would take up. + * @param gutterFraction Fraction of the gutter size taken up by one 1x1 tile. + * @return The size of a 1x1 tile as an expression that can be evaluated via CSS calc(). + */ + getBaseTileSize(sizePercent: number, gutterFraction: number): string { + // Take the base size percent (as would be if evenly dividing the size between cells), + // and then subtracting the size of one gutter. However, since there are no gutters on the + // edges, each tile only uses a fraction (gutterShare = numGutters / numCells) of the gutter + // size. (Imagine having one gutter per tile, and then breaking up the extra gutter on the + // edge evenly among the cells). + return `(${sizePercent}% - ( ${this._gutterSize} * ${gutterFraction} ))`; + } + + + /** + * Gets The horizontal or vertical position of a tile, e.g., the 'top' or 'left' property value. + * @internal + * @param offset Number of tiles that have already been rendered in the row/column. + * @param baseSize Base size of a 1x1 tile (as computed in getBaseTileSize). + * @return Position of the tile as a CSS calc() expression. + */ + getTilePosition(baseSize: string, offset: number): string { + // The position comes the size of a 1x1 tile plus gutter for each previous tile in the + // row/column (offset). + return calc(`(${baseSize} + ${this._gutterSize}) * ${offset}`); + } + + + /** + * Gets the actual size of a tile, e.g., width or height, taking rowspan or colspan into account. + * @internal + * @param baseSize Base size of a 1x1 tile (as computed in getBaseTileSize). + * @param span The tile's rowspan or colspan. + * @return Size of the tile as a CSS calc() expression. + */ + getTileSize(baseSize: string, span: number): string { + return `(${baseSize} * ${span}) + (${span - 1} * ${this._gutterSize})`; + } + + + /** Gets the style properties to be applied to a tile for the given row and column index. + * @internal + */ + setStyle(tile: MdGridTile, rowIndex: number, colIndex: number): void { + // Percent of the available horizontal space that one column takes up. + let percentWidthPerTile = 100 / this._cols; + + // Fraction of the vertical gutter size that each column takes up. + // For example, if there are 5 columns, each column uses 4/5 = 0.8 times the gutter width. + let gutterWidthFractionPerTile = (this._cols - 1) / this._cols; + + this.setColStyles(tile, colIndex, percentWidthPerTile, gutterWidthFractionPerTile); + this.setRowStyles(tile, rowIndex, percentWidthPerTile, gutterWidthFractionPerTile); + } + + /** Sets the horizontal placement of the tile in the list. + * @internal + */ + setColStyles(tile: MdGridTile, colIndex: number, percentWidth: number, + gutterWidth: number) { + // Base horizontal size of a column. + let baseTileWidth = this.getBaseTileSize(percentWidth, gutterWidth); + + // The width and horizontal position of each tile is always calculated the same way, but the + // height and vertical position depends on the rowMode. + let side = this._dir.value === 'ltr' ? 'left' : 'right'; + tile.setStyle(side, this.getTilePosition(baseTileWidth, colIndex)); + tile.setStyle('width', calc(this.getTileSize(baseTileWidth, tile.colspan))); + } + + /** Sets the vertical placement of the tile in the list. + * This method will be implemented by each type of TileStyler. + * @internal + */ + setRowStyles(tile: MdGridTile, rowIndex: number, percentWidth: number, gutterWidth: number) {} + + /** Calculates the computed height and returns the correct style property to set. + * This method will be implemented by each type of TileStyler. + * @internal + */ + getComputedHeight(): [string, string] { return null; } +} + + +/* This type of styler is instantiated when the user passes in a fixed row height +* Example */ +export class FixedTileStyler extends TileStyler { + + constructor(public fixedRowHeight: string) { super(); } + + /** @internal */ + init(gutterSize: string, tracker: TileCoordinator, cols: number, dir: Dir) { + super.init(gutterSize, tracker, cols, dir); + this.fixedRowHeight = normalizeUnits(this.fixedRowHeight); + } + + /** @internal */ + setRowStyles(tile: MdGridTile, rowIndex: number, percentWidth: number, + gutterWidth: number): void { + tile.setStyle('top', this.getTilePosition(this.fixedRowHeight, rowIndex)); + tile.setStyle('height', calc(this.getTileSize(this.fixedRowHeight, tile.rowspan))); + } + + /** @internal */ + getComputedHeight(): [string, string] { + return ['height', calc(`${this._rowspan} * ${this.getTileSize(this.fixedRowHeight, 1)}`)]; + } +} + +/* This type of styler is instantiated when the user passes in a width:height ratio + * for the row height. Example */ +export class RatioTileStyler extends TileStyler { + + /** Ratio width:height given by user to determine row height.*/ + rowHeightRatio: number; + baseTileHeight: string; + + constructor(value: string) { + super(); + this._parseRatio(value); + } + + /** @internal */ + setRowStyles(tile: MdGridTile, rowIndex: number, percentWidth: number, + gutterWidth: number): void { + let percentHeightPerTile = percentWidth / this.rowHeightRatio; + this.baseTileHeight = this.getBaseTileSize(percentHeightPerTile, gutterWidth); + + // Use paddingTop and marginTop to maintain the given aspect ratio, as + // a percentage-based value for these properties is applied versus the *width* of the + // containing block. See http://www.w3.org/TR/CSS2/box.html#margin-properties + tile.setStyle('marginTop', this.getTilePosition(this.baseTileHeight, rowIndex)); + tile.setStyle('paddingTop', calc(this.getTileSize(this.baseTileHeight, tile.rowspan))); + } + + /** @internal */ + getComputedHeight(): [string, string] { + return [ + 'paddingBottom', calc(`${this._rowspan} * ${this.getTileSize(this.baseTileHeight, 1)}`) + ]; + } + + /** @internal */ + private _parseRatio(value: string): void { + let ratioParts = value.split(':'); + + if (ratioParts.length !== 2) { + throw new MdGridListBadRatioError(value); + } + + this.rowHeightRatio = parseFloat(ratioParts[0]) / parseFloat(ratioParts[1]); + } +} + +/* This type of styler is instantiated when the user selects a "fit" row height mode. + * In other words, the row height will reflect the total height of the container divided + * by the number of rows. Example */ +export class FitTileStyler extends TileStyler { + + /** @internal */ + setRowStyles(tile: MdGridTile, rowIndex: number, percentWidth: number, + gutterWidth: number): void { + // Percent of the available vertical space that one row takes up. + let percentHeightPerTile = 100 / this._rowspan; + + // Fraction of the horizontal gutter size that each column takes up. + let gutterHeightPerTile = (this._rows - 1) / this._rows; + + // Base vertical size of a column. + let baseTileHeight = this.getBaseTileSize(percentHeightPerTile, gutterHeightPerTile); + + tile.setStyle('top', this.getTilePosition(baseTileHeight, rowIndex)); + tile.setStyle('height', calc(this.getTileSize(baseTileHeight, tile.rowspan))); + } +} + +/** Wraps a CSS string in a calc function + * @internal + */ +function calc(exp: string): string { return `calc(${exp})`; } + +/** Appends pixels to a CSS string if no units are given. + * @internal + */ +function normalizeUnits(value: string): string { + return (value.match(/px|em|rem/)) ? value : value + 'px'; +} + diff --git a/src/demo-app/grid-list/grid-list-demo.html b/src/demo-app/grid-list/grid-list-demo.html index 45e91f66ec0a..1ddfd2705072 100644 --- a/src/demo-app/grid-list/grid-list-demo.html +++ b/src/demo-app/grid-list/grid-list-demo.html @@ -1 +1,61 @@ - \ No newline at end of file +
+ + Basic grid list + + + One + Two + Three + Four + + + + + + Fixed-height grid list + + + + {{tile.text}} + + + + +

Change list cols:

+

Change row height:

+ +
+
+ + + Ratio-height grid list + + + + {{tile.text}} + + + + +

Change ratio:

+
+
+ + + Fit-height grid list + + + + {{tile.text}} + + + + +

Change list height:

+

Change gutter:

+
+
+
+ diff --git a/src/demo-app/grid-list/grid-list-demo.scss b/src/demo-app/grid-list/grid-list-demo.scss index e69de29bb2d1..4e6856e6a191 100644 --- a/src/demo-app/grid-list/grid-list-demo.scss +++ b/src/demo-app/grid-list/grid-list-demo.scss @@ -0,0 +1,15 @@ +.demo-grid-list { + width: 800px; + + md-card { + margin: 16px 0; + } + + p { + margin: 16px; + } + + .demo-basic-list md-grid-tile { + background: rgba(0,0,0,0.32); + } +} diff --git a/src/demo-app/grid-list/grid-list-demo.ts b/src/demo-app/grid-list/grid-list-demo.ts index 17892c52938a..fc947fb5bcb4 100644 --- a/src/demo-app/grid-list/grid-list-demo.ts +++ b/src/demo-app/grid-list/grid-list-demo.ts @@ -1,10 +1,27 @@ import {Component} from '@angular/core'; -import {MdGridList} from '../../components/grid-list/grid-list'; +import {MD_GRID_LIST_DIRECTIVES} from '../../components/grid-list/grid-list'; +import {MdButton} from '../../components/button/button'; +import {MD_CARD_DIRECTIVES} from '../../components/card/card'; @Component({ selector: 'grid-list-demo', templateUrl: 'demo-app/grid-list/grid-list-demo.html', styleUrls: ['demo-app/grid-list/grid-list-demo.css'], - directives: [MdGridList] + directives: [MD_GRID_LIST_DIRECTIVES, MdButton, MD_CARD_DIRECTIVES] }) -export class GridListDemo {} +export class GridListDemo { + tiles: any[] = [ + {text: 'One', cols: 3, rows: 1, color: 'lightblue'}, + {text: 'Two', cols: 1, rows: 2, color: 'lightgreen'}, + {text: 'Three', cols: 1, rows: 1, color: 'lightpink'}, + {text: 'Four', cols: 2, rows: 1, color: '#DDBDF1'}, + ]; + + fixedCols: number = 4; + fixedRowHeight: number = 100; + ratioGutter: number = 1; + fitListHeight: string = '400px'; + ratio: string = '4:1'; + + addTileCols() { this.tiles[2].cols++; } +} From a117b7a22ce4208ba762a0523eed586d612268ac Mon Sep 17 00:00:00 2001 From: Matthew de Nobrega Date: Sun, 24 Apr 2016 18:13:24 +0200 Subject: [PATCH 22/23] feat(input): add support for more input attributes --- src/components/input/input.html | 15 +- src/components/input/input.spec.ts | 373 ++++++++++++++++++++++++++++- src/components/input/input.ts | 14 +- 3 files changed, 396 insertions(+), 6 deletions(-) diff --git a/src/components/input/input.html b/src/components/input/input.html index f177fd3870a5..47f44eb332e9 100644 --- a/src/components/input/input.html +++ b/src/components/input/input.html @@ -12,11 +12,20 @@ [attr.aria-disabled]="ariaDisabled" [attr.aria-required]="ariaRequired" [attr.aria-invalid]="ariaInvalid" - [id]="id" + [attr.autocomplete]="autoComplete" + [autofocus]="autoFocus" [disabled]="disabled" - [required]="required" - [spellcheck]="spellcheck" + [id]="id" + [attr.list]="list" + [attr.max]="max" [attr.maxlength]="maxLength" + [attr.min]="min" + [attr.minlength]="minLength" + [readonly]="readOnly" + [required]="required" + [spellcheck]="spellCheck" + [attr.step]="step" + [attr.tabindex]="tabIndex" [type]="type" (focus)="onFocus()" (blur)="onBlur()" diff --git a/src/components/input/input.spec.ts b/src/components/input/input.spec.ts index 9d4f393b4b8e..2be08105c9d0 100644 --- a/src/components/input/input.spec.ts +++ b/src/components/input/input.spec.ts @@ -238,7 +238,369 @@ export function main() { expect(fixture.componentInstance.value).toBe(3); expect(typeof fixture.componentInstance.value).toBe('number'); }); - }); + })); + + it('supports the autoComplete attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('autocomplete')).toBeNull(); + + fixture.componentInstance.autoComplete = 'on'; + fixture.detectChanges(); + expect(el.getAttribute('autocomplete')).toEqual('on'); + })(); + }); + })); + + it('supports the autoComplete attribute as an unbound attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('autocomplete')).toEqual(''); + })(); + }); + })); + + it('supports the autoComplete attribute as an unbound value attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('autocomplete')).toEqual('name'); + })(); + }); + })); + + it('supports the autoFocus attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('autofocus')).toBeNull(); + + fixture.componentInstance.autoFocus = true; + fixture.detectChanges(); + expect(el.getAttribute('autofocus')).toEqual(''); + })(); + }); + })); + + it('supports the autoFocus attribute as an unbound attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('autofocus')).toEqual(''); + })(); + }); + })); + + it('supports the disabled attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.componentInstance.disabled = false; + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('disabled')).toEqual(null); + + fixture.componentInstance.disabled = true; + fixture.detectChanges(); + expect(el.getAttribute('disabled')).toEqual(''); + })(); + }); + })); + + it('supports the disabled attribute as an unbound attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('disabled')).toEqual(''); + })(); + }); + })); + + it('supports the list attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.componentInstance.disabled = false; + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('list')).toEqual(null); + + fixture.componentInstance.list = 'datalist-id'; + fixture.detectChanges(); + expect(el.getAttribute('list')).toEqual('datalist-id'); + })(); + }); + })); + + it('supports the max attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.componentInstance.disabled = false; + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('max')).toEqual(null); + + fixture.componentInstance.max = 10; + fixture.detectChanges(); + expect(el.getAttribute('max')).toEqual('10'); + + fixture.componentInstance.max = '2000-01-02'; + fixture.detectChanges(); + expect(el.getAttribute('max')).toEqual('2000-01-02'); + })(); + }); + })); + + it('supports the min attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.componentInstance.disabled = false; + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('min')).toEqual(null); + + fixture.componentInstance.min = 10; + fixture.detectChanges(); + expect(el.getAttribute('min')).toEqual('10'); + + fixture.componentInstance.min = '2000-01-02'; + fixture.detectChanges(); + expect(el.getAttribute('min')).toEqual('2000-01-02'); + })(); + }); + })); + + it('supports the readOnly attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('readonly')).toBeNull(); + + fixture.componentInstance.readOnly = true; + fixture.detectChanges(); + expect(el.getAttribute('readonly')).toEqual(''); + })(); + }); + })); + + it('supports the readOnly attribute as an unbound attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('readonly')).toEqual(''); + })(); + }); + })); + + it('supports the required attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('required')).toBeNull(); + + fixture.componentInstance.required = true; + fixture.detectChanges(); + expect(el.getAttribute('required')).toEqual(''); + })(); + }); + })); + + it('supports the required attribute as an unbound attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('required')).toEqual(''); + })(); + }); + })); + + it('supports the spellCheck attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('spellcheck')).toEqual('false'); + + fixture.componentInstance.spellCheck = true; + fixture.detectChanges(); + expect(el.getAttribute('spellcheck')).toEqual('true'); + })(); + }); + })); + + it('supports the spellCheck attribute as an unbound attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('spellcheck')).toEqual('true'); + })(); + }); + })); + + it('supports the step attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('step')).toEqual(null); + + fixture.componentInstance.step = 0.5; + fixture.detectChanges(); + expect(el.getAttribute('step')).toEqual('0.5'); + })(); + }); + })); + + it('supports the tabIndex attribute', injectAsync([], () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then((fixture: ComponentFixture) => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('tabindex')).toEqual(null); + + fixture.componentInstance.tabIndex = 1; + fixture.detectChanges(); + expect(el.getAttribute('tabindex')).toEqual('1'); + })(); + }); + })); }); } @@ -387,3 +749,12 @@ class MdInputAriaTestController { ariaLabel: string = 'label'; ariaDisabled: boolean = true; } + +@Component({ + selector: 'test-input-controller', + template: ` + + `, + directives: [MdInput] +}) +class MdInputOptionalAttributeController {} diff --git a/src/components/input/input.ts b/src/components/input/input.ts index c90d27ae0a4c..4d5d8c8587ff 100644 --- a/src/components/input/input.ts +++ b/src/components/input/input.ts @@ -130,14 +130,24 @@ export class MdInput implements ControlValueAccessor, AfterContentInit, OnChange */ @Input() align: 'start' | 'end' = 'start'; @Input() dividerColor: 'primary' | 'accent' | 'warn' = 'primary'; - @Input() @BooleanFieldValue() disabled: boolean = false; @Input() @BooleanFieldValue() floatingPlaceholder: boolean = true; @Input() hintLabel: string = ''; + + @Input() autoComplete: string; + @Input() @BooleanFieldValue() autoFocus: boolean = false; + @Input() @BooleanFieldValue() disabled: boolean = false; @Input() id: string = `md-input-${nextUniqueId++}`; + @Input() list: string; + @Input() max: string; @Input() maxLength: number = -1; + @Input() min: string; + @Input() minLength: number; @Input() placeholder: string; + @Input() @BooleanFieldValue() readOnly: boolean = false; @Input() @BooleanFieldValue() required: boolean = false; - @Input() @BooleanFieldValue() spellcheck: boolean = false; + @Input() @BooleanFieldValue() spellCheck: boolean = false; + @Input() step: number; + @Input() tabIndex: number; @Input() type: string = 'text'; get value(): any { return this._value; }; From b0ffe08bbb72c472f1ddb1ab163c89c837b0d392 Mon Sep 17 00:00:00 2001 From: Matthew de Nobrega Date: Tue, 10 May 2016 19:36:37 +0200 Subject: [PATCH 23/23] chore(input): updated tests to use untyped fixtures --- src/components/input/input.spec.ts | 110 ++++++++++++++--------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/src/components/input/input.spec.ts b/src/components/input/input.spec.ts index 2be08105c9d0..8245af871bd1 100644 --- a/src/components/input/input.spec.ts +++ b/src/components/input/input.spec.ts @@ -238,14 +238,14 @@ export function main() { expect(fixture.componentInstance.value).toBe(3); expect(typeof fixture.componentInstance.value).toBe('number'); }); - })); + }); - it('supports the autoComplete attribute', injectAsync([], () => { + it('supports the autoComplete attribute', () => { var template = ''; return builder.overrideTemplate(MdInputOptionalAttributeController, template) .createAsync(MdInputOptionalAttributeController) - .then((fixture: ComponentFixture) => { + .then((fixture) => { fakeAsync(() => { fixture.detectChanges(); @@ -259,14 +259,14 @@ export function main() { expect(el.getAttribute('autocomplete')).toEqual('on'); })(); }); - })); + }); - it('supports the autoComplete attribute as an unbound attribute', injectAsync([], () => { + it('supports the autoComplete attribute as an unbound attribute', () => { var template = ''; return builder.overrideTemplate(MdInputOptionalAttributeController, template) .createAsync(MdInputOptionalAttributeController) - .then((fixture: ComponentFixture) => { + .then((fixture) => { fakeAsync(() => { fixture.detectChanges(); @@ -276,14 +276,14 @@ export function main() { expect(el.getAttribute('autocomplete')).toEqual(''); })(); }); - })); + }); - it('supports the autoComplete attribute as an unbound value attribute', injectAsync([], () => { + it('supports the autoComplete attribute as an unbound value attribute', () => { var template = ''; return builder.overrideTemplate(MdInputOptionalAttributeController, template) .createAsync(MdInputOptionalAttributeController) - .then((fixture: ComponentFixture) => { + .then((fixture) => { fakeAsync(() => { fixture.detectChanges(); @@ -293,14 +293,14 @@ export function main() { expect(el.getAttribute('autocomplete')).toEqual('name'); })(); }); - })); + }); - it('supports the autoFocus attribute', injectAsync([], () => { + it('supports the autoFocus attribute', () => { var template = ''; return builder.overrideTemplate(MdInputOptionalAttributeController, template) .createAsync(MdInputOptionalAttributeController) - .then((fixture: ComponentFixture) => { + .then((fixture) => { fakeAsync(() => { fixture.detectChanges(); @@ -314,14 +314,14 @@ export function main() { expect(el.getAttribute('autofocus')).toEqual(''); })(); }); - })); + }); - it('supports the autoFocus attribute as an unbound attribute', injectAsync([], () => { + it('supports the autoFocus attribute as an unbound attribute', () => { var template = ''; return builder.overrideTemplate(MdInputOptionalAttributeController, template) .createAsync(MdInputOptionalAttributeController) - .then((fixture: ComponentFixture) => { + .then((fixture) => { fakeAsync(() => { fixture.detectChanges(); @@ -331,14 +331,14 @@ export function main() { expect(el.getAttribute('autofocus')).toEqual(''); })(); }); - })); + }); - it('supports the disabled attribute', injectAsync([], () => { + it('supports the disabled attribute', () => { var template = ''; return builder.overrideTemplate(MdInputOptionalAttributeController, template) .createAsync(MdInputOptionalAttributeController) - .then((fixture: ComponentFixture) => { + .then((fixture) => { fakeAsync(() => { fixture.componentInstance.disabled = false; fixture.detectChanges(); @@ -353,14 +353,14 @@ export function main() { expect(el.getAttribute('disabled')).toEqual(''); })(); }); - })); + }); - it('supports the disabled attribute as an unbound attribute', injectAsync([], () => { + it('supports the disabled attribute as an unbound attribute', () => { var template = ''; return builder.overrideTemplate(MdInputOptionalAttributeController, template) .createAsync(MdInputOptionalAttributeController) - .then((fixture: ComponentFixture) => { + .then((fixture) => { fakeAsync(() => { fixture.detectChanges(); @@ -370,14 +370,14 @@ export function main() { expect(el.getAttribute('disabled')).toEqual(''); })(); }); - })); + }); - it('supports the list attribute', injectAsync([], () => { + it('supports the list attribute', () => { var template = ''; return builder.overrideTemplate(MdInputOptionalAttributeController, template) .createAsync(MdInputOptionalAttributeController) - .then((fixture: ComponentFixture) => { + .then((fixture) => { fakeAsync(() => { fixture.componentInstance.disabled = false; fixture.detectChanges(); @@ -392,14 +392,14 @@ export function main() { expect(el.getAttribute('list')).toEqual('datalist-id'); })(); }); - })); + }); - it('supports the max attribute', injectAsync([], () => { + it('supports the max attribute', () => { var template = ''; return builder.overrideTemplate(MdInputOptionalAttributeController, template) .createAsync(MdInputOptionalAttributeController) - .then((fixture: ComponentFixture) => { + .then((fixture) => { fakeAsync(() => { fixture.componentInstance.disabled = false; fixture.detectChanges(); @@ -418,14 +418,14 @@ export function main() { expect(el.getAttribute('max')).toEqual('2000-01-02'); })(); }); - })); + }); - it('supports the min attribute', injectAsync([], () => { + it('supports the min attribute', () => { var template = ''; return builder.overrideTemplate(MdInputOptionalAttributeController, template) .createAsync(MdInputOptionalAttributeController) - .then((fixture: ComponentFixture) => { + .then((fixture) => { fakeAsync(() => { fixture.componentInstance.disabled = false; fixture.detectChanges(); @@ -444,14 +444,14 @@ export function main() { expect(el.getAttribute('min')).toEqual('2000-01-02'); })(); }); - })); + }); - it('supports the readOnly attribute', injectAsync([], () => { + it('supports the readOnly attribute', () => { var template = ''; return builder.overrideTemplate(MdInputOptionalAttributeController, template) .createAsync(MdInputOptionalAttributeController) - .then((fixture: ComponentFixture) => { + .then((fixture) => { fakeAsync(() => { fixture.detectChanges(); @@ -465,14 +465,14 @@ export function main() { expect(el.getAttribute('readonly')).toEqual(''); })(); }); - })); + }); - it('supports the readOnly attribute as an unbound attribute', injectAsync([], () => { + it('supports the readOnly attribute as an unbound attribute', () => { var template = ''; return builder.overrideTemplate(MdInputOptionalAttributeController, template) .createAsync(MdInputOptionalAttributeController) - .then((fixture: ComponentFixture) => { + .then((fixture) => { fakeAsync(() => { fixture.detectChanges(); @@ -482,14 +482,14 @@ export function main() { expect(el.getAttribute('readonly')).toEqual(''); })(); }); - })); + }); - it('supports the required attribute', injectAsync([], () => { + it('supports the required attribute', () => { var template = ''; return builder.overrideTemplate(MdInputOptionalAttributeController, template) .createAsync(MdInputOptionalAttributeController) - .then((fixture: ComponentFixture) => { + .then((fixture) => { fakeAsync(() => { fixture.detectChanges(); @@ -503,14 +503,14 @@ export function main() { expect(el.getAttribute('required')).toEqual(''); })(); }); - })); + }); - it('supports the required attribute as an unbound attribute', injectAsync([], () => { + it('supports the required attribute as an unbound attribute', () => { var template = ''; return builder.overrideTemplate(MdInputOptionalAttributeController, template) .createAsync(MdInputOptionalAttributeController) - .then((fixture: ComponentFixture) => { + .then((fixture) => { fakeAsync(() => { fixture.detectChanges(); @@ -520,14 +520,14 @@ export function main() { expect(el.getAttribute('required')).toEqual(''); })(); }); - })); + }); - it('supports the spellCheck attribute', injectAsync([], () => { + it('supports the spellCheck attribute', () => { var template = ''; return builder.overrideTemplate(MdInputOptionalAttributeController, template) .createAsync(MdInputOptionalAttributeController) - .then((fixture: ComponentFixture) => { + .then((fixture) => { fakeAsync(() => { fixture.detectChanges(); @@ -541,14 +541,14 @@ export function main() { expect(el.getAttribute('spellcheck')).toEqual('true'); })(); }); - })); + }); - it('supports the spellCheck attribute as an unbound attribute', injectAsync([], () => { + it('supports the spellCheck attribute as an unbound attribute', () => { var template = ''; return builder.overrideTemplate(MdInputOptionalAttributeController, template) .createAsync(MdInputOptionalAttributeController) - .then((fixture: ComponentFixture) => { + .then((fixture) => { fakeAsync(() => { fixture.detectChanges(); @@ -558,14 +558,14 @@ export function main() { expect(el.getAttribute('spellcheck')).toEqual('true'); })(); }); - })); + }); - it('supports the step attribute', injectAsync([], () => { + it('supports the step attribute', () => { var template = ''; return builder.overrideTemplate(MdInputOptionalAttributeController, template) .createAsync(MdInputOptionalAttributeController) - .then((fixture: ComponentFixture) => { + .then((fixture) => { fakeAsync(() => { fixture.detectChanges(); @@ -579,14 +579,14 @@ export function main() { expect(el.getAttribute('step')).toEqual('0.5'); })(); }); - })); + }); - it('supports the tabIndex attribute', injectAsync([], () => { + it('supports the tabIndex attribute', () => { var template = ''; return builder.overrideTemplate(MdInputOptionalAttributeController, template) .createAsync(MdInputOptionalAttributeController) - .then((fixture: ComponentFixture) => { + .then((fixture) => { fakeAsync(() => { fixture.detectChanges(); @@ -600,7 +600,7 @@ export function main() { expect(el.getAttribute('tabindex')).toEqual('1'); })(); }); - })); + }); }); }