From a53a129b0f761b87692ba68d61d4fca22fc987c7 Mon Sep 17 00:00:00 2001 From: pmartin Date: Sat, 22 Oct 2016 17:28:13 +0200 Subject: [PATCH 01/30] fix(slider): constraints value between min and max --- src/lib/slider/slider.spec.ts | 84 +++++++++++++++++++++++++++++++++++ src/lib/slider/slider.ts | 2 +- 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/src/lib/slider/slider.spec.ts b/src/lib/slider/slider.spec.ts index fd05c4a6fb43..9ff136665b7f 100644 --- a/src/lib/slider/slider.spec.ts +++ b/src/lib/slider/slider.spec.ts @@ -23,6 +23,9 @@ describe('MdSlider', () => { SliderWithSetTickInterval, SliderWithThumbLabel, SliderWithTwoWayBinding, + SliderWithValueSmallerThanMin, + SliderWithValueGreaterThanMax, + SliderWithValueBetweenMinAndMax ], providers: [ {provide: HAMMER_GESTURE_CONFIG, useFactory: () => { @@ -621,6 +624,66 @@ describe('MdSlider', () => { // TODO: Add tests for ng-pristine, ng-touched, ng-invalid. }); + + describe('slider with set min and max and a value smaller than min', () => { + let fixture: ComponentFixture; + let sliderDebugElement: DebugElement; + let sliderInstance: MdSlider; + + beforeEach(() => { + fixture = TestBed.createComponent(SliderWithValueSmallerThanMin); + fixture.detectChanges(); + + sliderDebugElement = fixture.debugElement.query(By.directive(MdSlider)); + sliderInstance = sliderDebugElement.injector.get(MdSlider); + }); + + it('should set the value to the min value', () => { + expect(sliderInstance.value).toBe(4); + expect(sliderInstance.min).toBe(4); + expect(sliderInstance.max).toBe(6); + }); + }); + + describe('slider with set min and max and a value greater than max', () => { + let fixture: ComponentFixture; + let sliderDebugElement: DebugElement; + let sliderInstance: MdSlider; + + beforeEach(() => { + fixture = TestBed.createComponent(SliderWithValueGreaterThanMax); + fixture.detectChanges(); + + sliderDebugElement = fixture.debugElement.query(By.directive(MdSlider)); + sliderInstance = sliderDebugElement.injector.get(MdSlider); + }); + + it('should set the value to the max value', () => { + expect(sliderInstance.value).toBe(6); + expect(sliderInstance.min).toBe(4); + expect(sliderInstance.max).toBe(6); + }); + }); + + describe('slider with set min and max and a value between min and max', () => { + let fixture: ComponentFixture; + let sliderDebugElement: DebugElement; + let sliderInstance: MdSlider; + + beforeEach(() => { + fixture = TestBed.createComponent(SliderWithValueBetweenMinAndMax); + fixture.detectChanges(); + + sliderDebugElement = fixture.debugElement.query(By.directive(MdSlider)); + sliderInstance = sliderDebugElement.injector.get(MdSlider); + }); + + it('should set the value to the max value', () => { + expect(sliderInstance.value).toBe(5); + expect(sliderInstance.min).toBe(4); + expect(sliderInstance.max).toBe(6); + }); + }); }); // The transition has to be removed in order to test the updated positions without setTimeout. @@ -678,6 +741,27 @@ class SliderWithTwoWayBinding { control = new FormControl(''); } +@Component({ + template: ``, + styles: [noTransitionStyle], + encapsulation: ViewEncapsulation.None +}) +class SliderWithValueSmallerThanMin { } + +@Component({ + template: ``, + styles: [noTransitionStyle], + encapsulation: ViewEncapsulation.None +}) +class SliderWithValueGreaterThanMax { } + +@Component({ + template: ``, + styles: [noTransitionStyle], + encapsulation: ViewEncapsulation.None +}) +class SliderWithValueBetweenMinAndMax { } + /** * Dispatches a click event from an element. * Note: The mouse event truncates the position for the click. diff --git a/src/lib/slider/slider.ts b/src/lib/slider/slider.ts index 8c80ee4ad7ee..b6929b81a11b 100644 --- a/src/lib/slider/slider.ts +++ b/src/lib/slider/slider.ts @@ -151,7 +151,7 @@ export class MdSlider implements AfterContentInit, ControlValueAccessor { return; } - this._value = Number(v); + this._value = Math.min(Math.max(Number(v), this.min), this.max); this._isInitialized = true; this._controlValueAccessorChangeFn(this._value); } From da2af1ee730c556d68b4235a6d2e3d8638585514 Mon Sep 17 00:00:00 2001 From: Jeremy Elbourn Date: Mon, 24 Oct 2016 16:45:44 -0700 Subject: [PATCH 02/30] chore: add gulp-cli as dev dep (#1587) --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index a0897177fe64..663942541f04 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "gulp": "^3.9.1", "gulp-autoprefixer": "^3.1.1", "gulp-clean": "^0.3.2", + "gulp-cli": "^1.2.2", "gulp-sass": "^2.3.2", "gulp-server-livereload": "^1.8.2", "gulp-shell": "^0.5.2", From c9ef34c0299d530d52be730fffcb7d8a34c91a61 Mon Sep 17 00:00:00 2001 From: Philippe MARTIN Date: Tue, 25 Oct 2016 02:03:09 +0200 Subject: [PATCH 03/30] docs(dialog): document MdDialog (#1569) --- README.md | 3 +- src/lib/dialog/README.md | 88 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e415ddd1948a..0a16f66b8b68 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ High level items planned for October 2016: | menu | Initial version, needs enhancements | [README][17] | [#119][0119] | | tooltip | Initial version, needs enhancements | [README][18] | - | | ripples | Available, but needs to be applied | [README][19] | [#108][0108] | -| dialog | Started, not yet ready for release | - | [#114][0114] | +| dialog | Started, not yet ready for release | [README][22] | [#114][0114] | | snackbar / toast | Initial version, needs enhancements | [README][21] | [#115][0115] | | select | Design started | - | [#118][0118] | | textarea | Not started | - | [#546][0546] | @@ -102,6 +102,7 @@ High level items planned for October 2016: [19]: https://github.com/angular/material2/blob/master/src/lib/core/ripple/README.md [20]: https://github.com/angular/material2/blob/master/docs/theming.md [21]: https://github.com/angular/material2/blob/master/src/lib/snack-bar/README.md +[22]: https://github.com/angular/material2/blob/master/src/lib/dialog/README.md [0107]: https://github.com/angular/material2/issues/107 [0119]: https://github.com/angular/material2/issues/119 diff --git a/src/lib/dialog/README.md b/src/lib/dialog/README.md index e69de29bb2d1..8634a3b5b38e 100644 --- a/src/lib/dialog/README.md +++ b/src/lib/dialog/README.md @@ -0,0 +1,88 @@ +# MdDialog + +MdDialog is a service, which opens dialogs components in the view. + +### Methods + +| Name | Description | +| --- | --- | +| `open(component: ComponentType, config: MdDialogConfig): MdDialogRef` | Creates and opens a dialog matching material spec. | + +### Config + +| Key | Description | +| --- | --- | +| `viewContainerRef: ViewContainerRef` | The view container ref to attach the dialog to. | +| `role: DialogRole = 'dialog'` | The ARIA role of the dialog element. Possible values are `dialog` and `alertdialog`. Defaults to `dialog`. | + +## MdDialogRef + +A reference to the dialog created by the MdDialog `open` method. + +### Methods + +| Name | Description | +| --- | --- | +| `close(dialogResult?: any)` | Closes the dialog, pushing a value to the afterClosed observable. | +| `afterClosed(): Observable` | Returns an observable which will emit the dialog result, passed to the `close` method above. | + +## Example +The service can be injected in a component. + +```ts +@Component({ + selector: 'pizza-component', + template: ` + + ` +}) +export class PizzaComponent { + + dialogRef: MdDialogRef; + + constructor( + public dialog: MdDialog, + public viewContainerRef: ViewContainerRef) { } + + openDialog() { + let config = new MdDialogConfig(); + config.viewContainerRef = this.viewContainerRef; + + this.dialogRef = this.dialog.open(PizzaDialog, config); + + this.dialogRef.afterClosed().subscribe(result => { + console.log('result: ' + result); + this.dialogRef = null; + }); + } +} + +@Component({ + selector: 'pizza-dialog', + template: ` + + + ` +}) +export class PizzaDialog { + constructor(public dialogRef: MdDialogRef) { } +} +``` + +The dialog component should be declared in the list of entry components of the module: + +```ts +@NgModule({ + declarations: [ + ..., + PizzaDialog + ], + entryComponents: [ + ..., + PizzaDialog + ], + ... +}) +export class AppModule { } + +``` From 920c8756030976fa4eec9bae37ed5e90c47aa4bc Mon Sep 17 00:00:00 2001 From: Kara Date: Mon, 24 Oct 2016 17:03:27 -0700 Subject: [PATCH 04/30] fix(radio): only call change callback with user input (#1521) --- src/lib/radio/radio.spec.ts | 44 ++++++++++++++++++++----------------- src/lib/radio/radio.ts | 4 ++-- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/src/lib/radio/radio.spec.ts b/src/lib/radio/radio.spec.ts index af7303fa1939..7f15ba06feb6 100644 --- a/src/lib/radio/radio.spec.ts +++ b/src/lib/radio/radio.spec.ts @@ -1,4 +1,4 @@ -import {async, ComponentFixture, TestBed} from '@angular/core/testing'; +import {async, ComponentFixture, TestBed, fakeAsync, tick} from '@angular/core/testing'; import {NgControl, FormsModule} from '@angular/forms'; import {Component, DebugElement} from '@angular/core'; import {By} from '@angular/platform-browser'; @@ -176,12 +176,12 @@ describe('MdRadio', () => { expect(nativeRadioInput.classList).not.toContain('md-radio-focused'); - dispatchFocusChangeEvent('focus', nativeRadioInput); + dispatchEvent('focus', nativeRadioInput); fixture.detectChanges(); expect(radioNativeElements[0].classList).toContain('md-radio-focused'); - dispatchFocusChangeEvent('blur', nativeRadioInput); + dispatchEvent('blur', nativeRadioInput); fixture.detectChanges(); expect(radioNativeElements[0].classList).not.toContain('md-radio-focused'); @@ -223,7 +223,7 @@ describe('MdRadio', () => { let groupDebugElement: DebugElement; let groupNativeElement: HTMLElement; let radioDebugElements: DebugElement[]; - let radioNativeElements: HTMLElement[]; + let innerRadios: DebugElement[]; let radioLabelElements: HTMLLabelElement[]; let groupInstance: MdRadioGroup; let radioInstances: MdRadioButton[]; @@ -242,8 +242,8 @@ describe('MdRadio', () => { groupNgControl = groupDebugElement.injector.get(NgControl); radioDebugElements = fixture.debugElement.queryAll(By.directive(MdRadioButton)); - radioNativeElements = radioDebugElements.map(debugEl => debugEl.nativeElement); radioInstances = radioDebugElements.map(debugEl => debugEl.componentInstance); + innerRadios = fixture.debugElement.queryAll(By.css('input[type="radio"]')); radioLabelElements = radioDebugElements .map(debugEl => debugEl.query(By.css('label')).nativeElement); @@ -280,16 +280,16 @@ describe('MdRadio', () => { expect(groupNgControl.pristine).toBe(true); expect(groupNgControl.touched).toBe(false); - // After changing the value programmatically, the control should become dirty (not pristine), + // After changing the value programmatically, the control should stay pristine // but remain untouched. radioInstances[1].checked = true; fixture.detectChanges(); expect(groupNgControl.valid).toBe(true); - expect(groupNgControl.pristine).toBe(false); + expect(groupNgControl.pristine).toBe(true); expect(groupNgControl.touched).toBe(false); - // After a user interaction occurs (such as a click), the control should remain dirty and + // After a user interaction occurs (such as a click), the control should become dirty and // now also be touched. radioLabelElements[2].click(); fixture.detectChanges(); @@ -299,10 +299,18 @@ describe('MdRadio', () => { expect(groupNgControl.touched).toBe(true); }); - it('should update the ngModel value when selecting a radio button', () => { - radioInstances[1].checked = true; + it('should write to the radio button based on ngModel', fakeAsync(() => { + testComponent.modelValue = 'chocolate'; + fixture.detectChanges(); + tick(); fixture.detectChanges(); + expect(innerRadios[1].nativeElement.checked).toBe(true); + })); + + it('should update the ngModel value when selecting a radio button', () => { + dispatchEvent('change', innerRadios[1].nativeElement); + fixture.detectChanges(); expect(testComponent.modelValue).toBe('chocolate'); }); @@ -310,16 +318,12 @@ describe('MdRadio', () => { expect(testComponent.modelValue).toBeUndefined(); expect(testComponent.lastEvent).toBeUndefined(); - groupInstance.value = 'chocolate'; + dispatchEvent('change', innerRadios[1].nativeElement); fixture.detectChanges(); - - expect(testComponent.modelValue).toBe('chocolate'); expect(testComponent.lastEvent.value).toBe('chocolate'); - groupInstance.value = 'vanilla'; + dispatchEvent('change', innerRadios[0].nativeElement); fixture.detectChanges(); - - expect(testComponent.modelValue).toBe('vanilla'); expect(testComponent.lastEvent.value).toBe('vanilla'); }); }); @@ -484,14 +488,14 @@ class RadioGroupWithNgModel { lastEvent: MdRadioChange; } -// TODO(jelbourn): remove eveything below when Angular supports faking events. +// TODO(jelbourn): remove everything below when Angular supports faking events. /** - * Dispatches a focus change event from an element. - * @param eventName Name of the event, either 'focus' or 'blur'. + * Dispatches an event from an element. + * @param eventName Name of the event * @param element The element from which the event will be dispatched. */ -function dispatchFocusChangeEvent(eventName: string, element: HTMLElement): void { +function dispatchEvent(eventName: string, element: HTMLElement): void { let event = document.createEvent('Event'); event.initEvent(eventName, true, true); element.dispatchEvent(event); diff --git a/src/lib/radio/radio.ts b/src/lib/radio/radio.ts index 125943f9589e..20d43c5f6671 100644 --- a/src/lib/radio/radio.ts +++ b/src/lib/radio/radio.ts @@ -78,7 +78,7 @@ export class MdRadioGroup implements AfterContentInit, ControlValueAccessor { private _isInitialized: boolean = false; /** The method to be called in order to update ngModel */ - private _controlValueAccessorChangeFn: (value: any) => void = (value) => {}; + _controlValueAccessorChangeFn: (value: any) => void = (value) => {}; /** onTouch function registered via registerOnTouch (ControlValueAccessor). */ onTouched: () => any = () => {}; @@ -198,7 +198,6 @@ export class MdRadioGroup implements AfterContentInit, ControlValueAccessor { let event = new MdRadioChange(); event.source = this._selected; event.value = this._value; - this._controlValueAccessorChangeFn(event.value); this.change.emit(event); } @@ -405,6 +404,7 @@ export class MdRadioButton implements OnInit { event.stopPropagation(); this.checked = true; + this.radioGroup._controlValueAccessorChangeFn(this.value); this._emitChangeEvent(); if (this.radioGroup) { From ffbc295bf1ab005c566493f21e1abc499564c61d Mon Sep 17 00:00:00 2001 From: Kara Date: Mon, 24 Oct 2016 17:05:34 -0700 Subject: [PATCH 05/30] feat(overlay): set overlay size (#1583) --- src/lib/core/overlay/overlay-ref.ts | 16 +++++++++ src/lib/core/overlay/overlay-state.ts | 8 ++++- src/lib/core/overlay/overlay.spec.ts | 51 +++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 1 deletion(-) diff --git a/src/lib/core/overlay/overlay-ref.ts b/src/lib/core/overlay/overlay-ref.ts index aba1cac1e112..60cf3b28662d 100644 --- a/src/lib/core/overlay/overlay-ref.ts +++ b/src/lib/core/overlay/overlay-ref.ts @@ -23,6 +23,7 @@ export class OverlayRef implements PortalHost { } let attachResult = this._portalHost.attach(portal); + this.updateSize(); this.updatePosition(); return attachResult; @@ -58,6 +59,17 @@ export class OverlayRef implements PortalHost { } } + /** Updates the size of the overlay based on the overlay config. */ + updateSize() { + if (this._state.width || this._state.width === 0) { + this._pane.style.width = formatCssUnit(this._state.width); + } + + if (this._state.height || this._state.height === 0) { + this._pane.style.height = formatCssUnit(this._state.height); + } + } + /** Attaches a backdrop for this overlay. */ private _attachBackdrop() { this._backdropElement = document.createElement('div'); @@ -98,3 +110,7 @@ export class OverlayRef implements PortalHost { } } } + +function formatCssUnit(value: number | string) { + return typeof value === 'string' ? value as string : `${value}px`; +} diff --git a/src/lib/core/overlay/overlay-state.ts b/src/lib/core/overlay/overlay-state.ts index 95d9dcef3fbb..dd6486f0cac0 100644 --- a/src/lib/core/overlay/overlay-state.ts +++ b/src/lib/core/overlay/overlay-state.ts @@ -12,10 +12,16 @@ export class OverlayState { /** Whether the overlay has a backdrop. */ hasBackdrop: boolean = false; + /** Custom class to add to the backdrop **/ backdropClass: string = 'md-overlay-dark-backdrop'; + /** The width of the overlay panel. If a number is provided, pixel units are assumed. **/ + width: number | string; + + /** The height of the overlay panel. If a number is provided, pixel units are assumed. **/ + height: number | string; + // TODO(jelbourn): configuration still to add - // - overlay size // - focus trap // - disable pointer events // - z-index diff --git a/src/lib/core/overlay/overlay.spec.ts b/src/lib/core/overlay/overlay.spec.ts index 804ced72010c..2f896c4d8a2d 100644 --- a/src/lib/core/overlay/overlay.spec.ts +++ b/src/lib/core/overlay/overlay.spec.ts @@ -98,6 +98,57 @@ describe('Overlay', () => { }); }); + describe('size', () => { + let state: OverlayState; + + beforeEach(() => { + state = new OverlayState(); + }); + + it('should apply the width set in the config', () => { + state.width = 500; + + overlay.create(state).attach(componentPortal); + const pane = overlayContainerElement.children[0] as HTMLElement; + expect(pane.style.width).toEqual('500px'); + }); + + it('should support using other units if a string width is provided', () => { + state.width = '200%'; + + overlay.create(state).attach(componentPortal); + const pane = overlayContainerElement.children[0] as HTMLElement; + expect(pane.style.width).toEqual('200%'); + }); + + it('should apply the height set in the config', () => { + state.height = 500; + + overlay.create(state).attach(componentPortal); + const pane = overlayContainerElement.children[0] as HTMLElement; + expect(pane.style.height).toEqual('500px'); + }); + + it('should support using other units if a string height is provided', () => { + state.height = '100vh'; + + overlay.create(state).attach(componentPortal); + const pane = overlayContainerElement.children[0] as HTMLElement; + expect(pane.style.height).toEqual('100vh'); + }); + + it('should support zero widths and heights', () => { + state.width = 0; + state.height = 0; + + overlay.create(state).attach(componentPortal); + const pane = overlayContainerElement.children[0] as HTMLElement; + expect(pane.style.width).toEqual('0px'); + expect(pane.style.height).toEqual('0px'); + }); + + }); + describe('backdrop', () => { let config: OverlayState; From 1ad457b1b6e8853ba77ed8741a7b349a3fe3afb9 Mon Sep 17 00:00:00 2001 From: Jeremy Elbourn Date: Mon, 24 Oct 2016 18:01:40 -0700 Subject: [PATCH 06/30] chore: update sauce connect binary to latest version (#1563) --- scripts/sauce/sauce_connect_setup.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/sauce/sauce_connect_setup.sh b/scripts/sauce/sauce_connect_setup.sh index be110f567c53..3eb7e2202191 100755 --- a/scripts/sauce/sauce_connect_setup.sh +++ b/scripts/sauce/sauce_connect_setup.sh @@ -23,10 +23,10 @@ CONNECT_STDERR="$LOGS_DIR/sauce-connect.stderr" if [ `uname -s` = "Darwin" ]; then # If the user is running Mac, download the OSX version # https://en.wikipedia.org/wiki/Darwin_(operating_system) - CONNECT_URL="https://saucelabs.com/downloads/sc-4.3.11-osx.zip" + CONNECT_URL="https://saucelabs.com/downloads/sc-4.4.1-osx.zip" else # Otherwise, default to Linux for Travis-CI - CONNECT_URL="https://saucelabs.com/downloads/sc-4.3.11-linux.tar.gz" + CONNECT_URL="https://saucelabs.com/downloads/sc-4.4.1-linux.tar.gz" fi mkdir -p $CONNECT_DIR cd $CONNECT_DIR From 43786b8cc4285b7c90a94448ca4fb65a32046067 Mon Sep 17 00:00:00 2001 From: Jeremy Elbourn Date: Tue, 25 Oct 2016 10:14:30 -0700 Subject: [PATCH 07/30] chore(getting-started): make theming section more attention-grabbing. (#1588) --- GETTING_STARTED.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/GETTING_STARTED.md b/GETTING_STARTED.md index 6204063179a8..76f27a6772b7 100644 --- a/GETTING_STARTED.md +++ b/GETTING_STARTED.md @@ -33,19 +33,20 @@ import { MaterialModule } from '@angular/material'; export class PizzaPartyAppModule { } ``` -## Including core and theme styles: -This is required to apply all of the core and theme styles to your application. -See the [theming guide](docs/theming.md) for instructions. +## Include the core and theme styles: +This is **required** to apply all of the core and theme styles to your application. You can either +use a pre-built theme, or define your own custom theme. +:trident: See the [theming guide](docs/theming.md) for instructions. -## Additional setup for `md-slide-toggle` and `md-slider`: +### Additional setup for `md-slide-toggle` and `md-slider`: The slide-toggle and slider components have a dependency on [HammerJS](http://hammerjs.github.io/). Add HammerJS to your application via [npm](https://www.npmjs.com/package/hammerjs), a CDN (such as the [Google CDN](https://developers.google.com/speed/libraries/#hammerjs)), or served directly from your app. -## [Optional] Using Material Design icons with `md-icon`: +### [Optional] Using Material Design icons with `md-icon`: - If you want to use Material Design icons in addition to Angular Material components, load the Material Design font in your `index.html`. From 35f004493a5bd1b387310106c8306f31cefca8a7 Mon Sep 17 00:00:00 2001 From: Eytan Manor Date: Tue, 25 Oct 2016 20:15:29 +0300 Subject: [PATCH 08/30] chore(packages): add http module to package.json (#1579) --- src/lib/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/package.json b/src/lib/package.json index 2714448bc147..dafec68919d1 100644 --- a/src/lib/package.json +++ b/src/lib/package.json @@ -22,7 +22,8 @@ "homepage": "https://github.com/angular/material2#readme", "peerDependencies": { "@angular/core": "^2.0.0", - "@angular/common": "^2.0.0" + "@angular/common": "^2.0.0", + "@angular/http": "^2.0.0" }, "dependencies": { "@types/hammerjs": "^2.0.30" From 333b11eaa744dd7f54572e6fc493ee9b945d7896 Mon Sep 17 00:00:00 2001 From: Max Bothe Date: Tue, 25 Oct 2016 19:16:20 +0200 Subject: [PATCH 09/30] feat(checkbox): add color attribute. (#1463) --- src/demo-app/checkbox/checkbox-demo.html | 5 +++- src/demo-app/checkbox/checkbox-demo.ts | 5 ++++ src/lib/checkbox/README.md | 14 +++++++++++ src/lib/checkbox/_checkbox-theme.scss | 30 ++++++++++++++++------ src/lib/checkbox/checkbox.scss | 5 ++++ src/lib/checkbox/checkbox.spec.ts | 32 ++++++++++++++++++++++++ src/lib/checkbox/checkbox.ts | 28 ++++++++++++++++++++- 7 files changed, 110 insertions(+), 9 deletions(-) diff --git a/src/demo-app/checkbox/checkbox-demo.html b/src/demo-app/checkbox/checkbox-demo.html index 9fd9c922cd8e..713f8b008d4c 100644 --- a/src/demo-app/checkbox/checkbox-demo.html +++ b/src/demo-app/checkbox/checkbox-demo.html @@ -2,6 +2,7 @@

md-checkbox: Basic Example

md-checkbox: Basic Example + +

Alignment:

@@ -41,4 +44,4 @@

md-checkbox: Basic Example

Nested Checklist

- \ No newline at end of file + diff --git a/src/demo-app/checkbox/checkbox-demo.ts b/src/demo-app/checkbox/checkbox-demo.ts index edab7a8fa15c..aed0335dc761 100644 --- a/src/demo-app/checkbox/checkbox-demo.ts +++ b/src/demo-app/checkbox/checkbox-demo.ts @@ -67,6 +67,7 @@ export class CheckboxDemo { isChecked: boolean = false; isDisabled: boolean = false; alignment: string = 'start'; + useAlternativeColor: boolean = false; printResult() { if (this.isIndeterminate) { @@ -74,4 +75,8 @@ export class CheckboxDemo { } return this.isChecked ? 'Yes!' : 'No!'; } + + checkboxColor() { + return this.useAlternativeColor ? 'primary' : 'accent'; + } } diff --git a/src/lib/checkbox/README.md b/src/lib/checkbox/README.md index 4efc10ccf5b4..a8cffb6ae668 100644 --- a/src/lib/checkbox/README.md +++ b/src/lib/checkbox/README.md @@ -77,3 +77,17 @@ checkbox if you do not wish to have any label text. ```html ``` + +### Theming + +The color of a `md-checkbox` can be changed by using the `color` attribute. +The value `accent` is default and will correspond to your theme accent color. +Alternatively, you can specify `primary` or `warn`. + +Example: + + ```html + + I come after my label. + + ``` diff --git a/src/lib/checkbox/_checkbox-theme.scss b/src/lib/checkbox/_checkbox-theme.scss index ba66c87f06a2..4bde9b49407f 100644 --- a/src/lib/checkbox/_checkbox-theme.scss +++ b/src/lib/checkbox/_checkbox-theme.scss @@ -5,6 +5,7 @@ $is-dark-theme: map-get($theme, is-dark); $primary: map-get($theme, primary); $accent: map-get($theme, accent); + $warn: map-get($theme, warn); $background: map-get($theme, background); @@ -14,9 +15,6 @@ // The color of the checkbox's checkmark / mixedmark. $checkbox-mark-color: md-color($background, background); - // The color that is used as the checkbox background when it is checked. - $checkbox-background-color: md-color($accent, 500); - // NOTE(traviskaufman): While the spec calls for translucent blacks/whites for disabled colors, // this does not work well with elements layered on top of one another. To get around this we // blend the colors together based on the base color and the theme background. @@ -43,8 +41,16 @@ } .md-checkbox-indeterminate, .md-checkbox-checked { - .md-checkbox-background { - background-color: $checkbox-background-color; + &.md-primary .md-checkbox-background { + background-color: md-color($primary, 500); + } + + &.md-accent .md-checkbox-background { + background-color: md-color($accent, 500); + } + + &.md-warn .md-checkbox-background { + background-color: md-color($warn, 500); } } @@ -63,7 +69,17 @@ } // TODO(jelbourn): remove style for temporary ripple once the real ripple is applied. - .md-checkbox-focused .md-ink-ripple { - background-color: md-color($accent, 0.26); + .md-checkbox-focused { + &.md-primary .md-ink-ripple { + background-color: md-color($primary, 0.26); + } + + &.md-accent .md-ink-ripple { + background-color: md-color($accent, 0.26); + } + + &.md-warn .md-ink-ripple { + background-color: md-color($warn, 0.26); + } } } diff --git a/src/lib/checkbox/checkbox.scss b/src/lib/checkbox/checkbox.scss index e634fe878cbb..d1135520dbcc 100644 --- a/src/lib/checkbox/checkbox.scss +++ b/src/lib/checkbox/checkbox.scss @@ -1,4 +1,5 @@ @import '../core/theming/theming'; +@import '../core/style/elevation'; @import '../core/style/variables'; @import '../core/ripple/ripple'; @@ -189,6 +190,10 @@ $_md-checkbox-indeterminate-checked-easing-function: cubic-bezier(0.14, 0, 0, 1) md-checkbox { cursor: pointer; + + // Animation + transition: background $swift-ease-out-duration $swift-ease-out-timing-function, + md-elevation-transition-property-value(); } .md-checkbox-layout { diff --git a/src/lib/checkbox/checkbox.spec.ts b/src/lib/checkbox/checkbox.spec.ts index e7af963aeb46..e0e0a251baeb 100644 --- a/src/lib/checkbox/checkbox.spec.ts +++ b/src/lib/checkbox/checkbox.spec.ts @@ -267,6 +267,36 @@ describe('MdCheckbox', () => { expect(inputElement.required).toBe(false); }); + describe('color behaviour', () => { + it('should apply class based on color attribute', () => { + testComponent.checkboxColor = 'primary'; + fixture.detectChanges(); + expect(checkboxDebugElement.nativeElement.classList.contains('md-primary')).toBe(true); + + testComponent.checkboxColor = 'accent'; + fixture.detectChanges(); + expect(checkboxDebugElement.nativeElement.classList.contains('md-accent')).toBe(true); + }); + + it('should should not clear previous defined classes', () => { + checkboxDebugElement.nativeElement.classList.add('custom-class'); + + testComponent.checkboxColor = 'primary'; + fixture.detectChanges(); + + expect(checkboxDebugElement.nativeElement.classList.contains('md-primary')).toBe(true); + expect(checkboxDebugElement.nativeElement.classList.contains('custom-class')).toBe(true); + + testComponent.checkboxColor = 'accent'; + fixture.detectChanges(); + + expect(checkboxDebugElement.nativeElement.classList.contains('md-primary')).toBe(false); + expect(checkboxDebugElement.nativeElement.classList.contains('md-accent')).toBe(true); + expect(checkboxDebugElement.nativeElement.classList.contains('custom-class')).toBe(true); + + }); + }); + describe('state transition css classes', () => { it('should transition unchecked -> checked -> unchecked', () => { testComponent.isChecked = true; @@ -519,6 +549,7 @@ describe('MdCheckbox', () => { [checked]="isChecked" [indeterminate]="isIndeterminate" [disabled]="isDisabled" + [color]="checkboxColor" (change)="changeCount = changeCount + 1" (click)="onCheckboxClick($event)" (change)="onCheckboxChange($event)"> @@ -536,6 +567,7 @@ class SingleCheckbox { parentElementKeyedUp: boolean = false; lastKeydownEvent: Event = null; changeCount: number = 0; + checkboxColor: string = 'primary'; onCheckboxClick(event: Event) {} onCheckboxChange(event: MdCheckboxChange) {} diff --git a/src/lib/checkbox/checkbox.ts b/src/lib/checkbox/checkbox.ts index ab1b55fa6ba3..fd8dd3a427b0 100644 --- a/src/lib/checkbox/checkbox.ts +++ b/src/lib/checkbox/checkbox.ts @@ -128,11 +128,15 @@ export class MdCheckbox implements ControlValueAccessor { private _indeterminate: boolean = false; + private _color: string; + private _controlValueAccessorChangeFn: (value: any) => void = (value) => {}; hasFocus: boolean = false; - constructor(private _renderer: Renderer, private _elementRef: ElementRef) {} + constructor(private _renderer: Renderer, private _elementRef: ElementRef) { + this.color = 'accent'; + } /** * Whether the checkbox is checked. Note that setting `checked` will immediately set @@ -174,6 +178,28 @@ export class MdCheckbox implements ControlValueAccessor { } } + /** Sets the color of the checkbox */ + @Input() + get color(): string { + return this._color; + } + + set color(value: string) { + this._updateColor(value); + } + + _updateColor(newColor: string) { + this._setElementColor(this._color, false); + this._setElementColor(newColor, true); + this._color = newColor; + } + + _setElementColor(color: string, isAdd: boolean) { + if (color != null && color != '') { + this._renderer.setElementClass(this._elementRef.nativeElement, `md-${color}`, isAdd); + } + } + /** * Implemented as part of ControlValueAccessor. * TODO: internal From 6f322cf2fe668a208778029d08b5de690a38e7d3 Mon Sep 17 00:00:00 2001 From: Kara Date: Tue, 25 Oct 2016 10:21:25 -0700 Subject: [PATCH 10/30] feat(overlay): support all overlay config properties (#1591) --- src/demo-app/overlay/overlay-demo.html | 5 +- .../core/overlay/overlay-directives.spec.ts | 88 +++++++++++- src/lib/core/overlay/overlay-directives.ts | 132 +++++++++++++++--- 3 files changed, 200 insertions(+), 25 deletions(-) diff --git a/src/demo-app/overlay/overlay-demo.html b/src/demo-app/overlay/overlay-demo.html index 1575e8dbe222..83ac9bf82a26 100644 --- a/src/demo-app/overlay/overlay-demo.html +++ b/src/demo-app/overlay/overlay-demo.html @@ -15,8 +15,9 @@ Open menu -