diff --git a/src/lib/checkbox/checkbox.spec.ts b/src/lib/checkbox/checkbox.spec.ts index 342c4032bed2..dfe107d0b1fc 100644 --- a/src/lib/checkbox/checkbox.spec.ts +++ b/src/lib/checkbox/checkbox.spec.ts @@ -8,6 +8,8 @@ import { import { NgControl, FormsModule, + ReactiveFormsModule, + FormControl, } from '@angular/forms'; import {Component, DebugElement} from '@angular/core'; import {By} from '@angular/platform-browser'; @@ -21,7 +23,7 @@ describe('MdCheckbox', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [MdCheckboxModule.forRoot(), FormsModule], + imports: [MdCheckboxModule.forRoot(), FormsModule, ReactiveFormsModule], declarations: [ SingleCheckbox, CheckboxWithFormDirectives, @@ -31,6 +33,7 @@ describe('MdCheckbox', () => { CheckboxWithAriaLabelledby, CheckboxWithNameAttribute, CheckboxWithChangeEvent, + CheckboxWithFormControl, ], }); @@ -561,18 +564,48 @@ describe('MdCheckbox', () => { expect(inputElement.getAttribute('name')).toBe('test-name'); }); }); + + + describe('with form control', () => { + let checkboxDebugElement: DebugElement; + let checkboxInstance: MdCheckbox; + let testComponent: CheckboxWithFormControl; + + beforeEach(() => { + fixture = TestBed.createComponent(CheckboxWithFormControl); + fixture.detectChanges(); + + checkboxDebugElement = fixture.debugElement.query(By.directive(MdCheckbox)); + checkboxInstance = checkboxDebugElement.componentInstance; + testComponent = fixture.debugElement.componentInstance; + }); + + it('should toggle the disabled state', () => { + expect(checkboxInstance.disabled).toBe(false); + + testComponent.formControl.disable(); + fixture.detectChanges(); + + expect(checkboxInstance.disabled).toBe(true); + + testComponent.formControl.enable(); + fixture.detectChanges(); + + expect(checkboxInstance.disabled).toBe(false); + }); + }); }); /** Simple component for testing a single checkbox. */ @Component({ template: ` -
- + `, }) @@ -660,3 +693,11 @@ class CheckboxWithNameAttribute {} class CheckboxWithChangeEvent { lastEvent: MdCheckboxChange; } + +/** Test component with reactive forms */ +@Component({ + template: `` +}) +class CheckboxWithFormControl { + formControl = new FormControl(); +} diff --git a/src/lib/checkbox/checkbox.ts b/src/lib/checkbox/checkbox.ts index a477cd575618..1f6f768f54ba 100644 --- a/src/lib/checkbox/checkbox.ts +++ b/src/lib/checkbox/checkbox.ts @@ -112,7 +112,7 @@ export class MdCheckbox implements ControlValueAccessor { /** Whether or not the checkbox should come before or after the label. */ @Input() align: 'start' | 'end' = 'start'; - private _disabled: boolean; + private _disabled: boolean = false; /** * Whether the checkbox is disabled. When the checkbox is disabled it cannot be interacted with. @@ -245,6 +245,13 @@ export class MdCheckbox implements ControlValueAccessor { this.onTouched = fn; } + /** + * Implemented as a part of ControlValueAccessor. + */ + setDisabledState(isDisabled: boolean) { + this.disabled = isDisabled; + } + private _transitionCheckState(newState: TransitionCheckState) { let oldState = this._currentCheckState; let renderer = this._renderer; diff --git a/src/lib/input/input.spec.ts b/src/lib/input/input.spec.ts index d1591c615210..e6a1ed7df351 100644 --- a/src/lib/input/input.spec.ts +++ b/src/lib/input/input.spec.ts @@ -3,7 +3,7 @@ import { TestBed, } from '@angular/core/testing'; import {Component} from '@angular/core'; -import {FormsModule} from '@angular/forms'; +import {FormsModule, ReactiveFormsModule, FormControl} from '@angular/forms'; import {By} from '@angular/platform-browser'; import {MdInput, MdInputModule} from './input'; @@ -14,7 +14,7 @@ function isInternetExplorer11() { describe('MdInput', function () { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [MdInputModule.forRoot(), FormsModule], + imports: [MdInputModule.forRoot(), FormsModule, ReactiveFormsModule], declarations: [ MdInputNumberTypeConservedTestComponent, MdInputPlaceholderRequiredTestComponent, @@ -58,6 +58,7 @@ describe('MdInput', function () { MdInputPasswordTestController, MdInputNumberTestController, MdTextareaWithBindings, + MdInputWithFormControl, ], }); @@ -621,6 +622,27 @@ describe('MdInput', function () { expect(inputElement.name).toBe('some-name'); }); + it('toggles the disabled state when used with a FormControl', () => { + let fixture = TestBed.createComponent(MdInputWithFormControl); + + fixture.detectChanges(); + + let input: MdInput = fixture.debugElement.query(By.directive(MdInput)).componentInstance; + let testComponent: MdInputWithFormControl = fixture.debugElement.componentInstance; + + expect(input.disabled).toBe(false); + + testComponent.formControl.disable(); + fixture.detectChanges(); + + expect(input.disabled).toBe(true); + + testComponent.formControl.enable(); + fixture.detectChanges(); + + expect(input.disabled).toBe(false); + }); + describe('md-textarea', () => { it('supports the rows, cols, and wrap attributes', () => { let fixture = TestBed.createComponent(MdTextareaWithBindings); @@ -807,6 +829,11 @@ class MdInputNumberTestController { placeholder: string = ''; } +@Component({template: ``}) +class MdInputWithFormControl { + formControl = new FormControl(); +} + @Component({template: ``}) class MdTextareaWithBindings { diff --git a/src/lib/input/input.ts b/src/lib/input/input.ts index 3003b3e3e71b..13b38e26a436 100644 --- a/src/lib/input/input.ts +++ b/src/lib/input/input.ts @@ -293,6 +293,13 @@ export class MdInput implements ControlValueAccessor, AfterContentInit, OnChange this._onTouchedCallback = fn; } + /** + * Implemented as a part of ControlValueAccessor. + */ + setDisabledState(isDisabled: boolean) { + this.disabled = isDisabled; + } + /** TODO: internal */ ngAfterContentInit() { this._validateConstraints(); diff --git a/src/lib/radio/radio.spec.ts b/src/lib/radio/radio.spec.ts index 5ff9427ba1af..e9553888257a 100644 --- a/src/lib/radio/radio.spec.ts +++ b/src/lib/radio/radio.spec.ts @@ -1,5 +1,5 @@ import {async, ComponentFixture, TestBed, fakeAsync, tick} from '@angular/core/testing'; -import {NgControl, FormsModule} from '@angular/forms'; +import {NgControl, FormsModule, ReactiveFormsModule, FormControl} from '@angular/forms'; import {Component, DebugElement} from '@angular/core'; import {By} from '@angular/platform-browser'; import {MdRadioGroup, MdRadioButton, MdRadioChange, MdRadioModule} from './radio'; @@ -9,10 +9,11 @@ describe('MdRadio', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [MdRadioModule.forRoot(), FormsModule], + imports: [MdRadioModule.forRoot(), FormsModule, ReactiveFormsModule], declarations: [ RadiosInsideRadioGroup, RadioGroupWithNgModel, + RadioGroupWithFormControl, StandaloneRadioButtons, ], }); @@ -152,7 +153,7 @@ describe('MdRadio', () => { expect(spies[1]).toHaveBeenCalledTimes(1); }); - it(`should not emit a change event from the radio group when change group value + it(`should not emit a change event from the radio group when change group value programmatically`, () => { expect(groupInstance.value).toBeFalsy(); @@ -246,7 +247,7 @@ describe('MdRadio', () => { } })); - it(`should update the group's selected radio to null when unchecking that radio + it(`should update the group's selected radio to null when unchecking that radio programmatically`, () => { let changeSpy = jasmine.createSpy('radio-group change listener'); groupInstance.change.subscribe(changeSpy); @@ -420,6 +421,36 @@ describe('MdRadio', () => { }); }); + describe('group with FormControl', () => { + let fixture: ComponentFixture; + let groupDebugElement: DebugElement; + let groupInstance: MdRadioGroup; + let testComponent: RadioGroupWithFormControl; + + beforeEach(() => { + fixture = TestBed.createComponent(RadioGroupWithFormControl); + fixture.detectChanges(); + + testComponent = fixture.debugElement.componentInstance; + groupDebugElement = fixture.debugElement.query(By.directive(MdRadioGroup)); + groupInstance = groupDebugElement.injector.get(MdRadioGroup); + }); + + it('should toggle the disabled state', () => { + expect(groupInstance.disabled).toBeFalsy(); + + testComponent.formControl.disable(); + fixture.detectChanges(); + + expect(groupInstance.disabled).toBeTruthy(); + + testComponent.formControl.enable(); + fixture.detectChanges(); + + expect(groupInstance.disabled).toBeFalsy(); + }); + }); + describe('as standalone', () => { let fixture: ComponentFixture; let radioDebugElements: DebugElement[]; @@ -548,11 +579,11 @@ class RadiosInsideRadioGroup { Spring Summer Autumn - + Spring Summer Autumn - + Baby Banana @@ -581,6 +612,17 @@ class RadioGroupWithNgModel { lastEvent: MdRadioChange; } +@Component({ + template: ` + + One + + ` +}) +class RadioGroupWithFormControl { + formControl = new FormControl(); +} + // TODO(jelbourn): remove everything below when Angular supports faking events. /** diff --git a/src/lib/radio/radio.ts b/src/lib/radio/radio.ts index 2e94696561af..989f03ff5a9e 100644 --- a/src/lib/radio/radio.ts +++ b/src/lib/radio/radio.ts @@ -228,6 +228,13 @@ export class MdRadioGroup implements AfterContentInit, ControlValueAccessor { registerOnTouched(fn: any) { this.onTouched = fn; } + + /** + * Implemented as a part of ControlValueAccessor. + */ + setDisabledState(isDisabled: boolean) { + this.disabled = isDisabled; + } } diff --git a/src/lib/slide-toggle/slide-toggle.spec.ts b/src/lib/slide-toggle/slide-toggle.spec.ts index c1b8061c3e78..d9bf16609981 100644 --- a/src/lib/slide-toggle/slide-toggle.spec.ts +++ b/src/lib/slide-toggle/slide-toggle.spec.ts @@ -2,7 +2,7 @@ import {async, ComponentFixture, TestBed, fakeAsync, tick} from '@angular/core/t import {By, HAMMER_GESTURE_CONFIG} from '@angular/platform-browser'; import {Component} from '@angular/core'; import {MdSlideToggle, MdSlideToggleChange, MdSlideToggleModule} from './slide-toggle'; -import {FormsModule, NgControl} from '@angular/forms'; +import {FormsModule, NgControl, ReactiveFormsModule, FormControl} from '@angular/forms'; import {TestGestureConfig} from '../slider/test-gesture-config'; describe('MdSlideToggle', () => { @@ -11,8 +11,8 @@ describe('MdSlideToggle', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [MdSlideToggleModule.forRoot(), FormsModule], - declarations: [SlideToggleTestApp, SlideToggleFormsTestApp], + imports: [MdSlideToggleModule.forRoot(), FormsModule, ReactiveFormsModule], + declarations: [SlideToggleTestApp, SlideToggleFormsTestApp, SlideToggleWithFormControl], providers: [ {provide: HAMMER_GESTURE_CONFIG, useFactory: () => gestureConfig = new TestGestureConfig()} ] @@ -509,6 +509,34 @@ describe('MdSlideToggle', () => { }); + describe('with a FormControl', () => { + let fixture: ComponentFixture; + + let testComponent: SlideToggleWithFormControl; + let slideToggle: MdSlideToggle; + + beforeEach(() => { + fixture = TestBed.createComponent(SlideToggleWithFormControl); + fixture.detectChanges(); + + testComponent = fixture.debugElement.componentInstance; + slideToggle = fixture.debugElement.query(By.directive(MdSlideToggle)).componentInstance; + }); + + it('should toggle the disabled state', () => { + expect(slideToggle.disabled).toBe(false); + + testComponent.formControl.disable(); + fixture.detectChanges(); + + expect(slideToggle.disabled).toBe(true); + + testComponent.formControl.enable(); + fixture.detectChanges(); + + expect(slideToggle.disabled).toBe(false); + }); + }); }); /** @@ -525,20 +553,20 @@ function dispatchFocusChangeEvent(eventName: string, element: HTMLElement): void @Component({ selector: 'slide-toggle-test-app', template: ` - - + Test Slide Toggle - + `, }) class SlideToggleTestApp { @@ -572,3 +600,14 @@ class SlideToggleFormsTestApp { isSubmitted: boolean = false; isRequired: boolean = false; } + + +@Component({ + template: ` + + Test Slide Toggle + `, +}) +class SlideToggleWithFormControl { + formControl = new FormControl(); +} diff --git a/src/lib/slide-toggle/slide-toggle.ts b/src/lib/slide-toggle/slide-toggle.ts index 66632b784156..c858a5ef0d13 100644 --- a/src/lib/slide-toggle/slide-toggle.ts +++ b/src/lib/slide-toggle/slide-toggle.ts @@ -174,6 +174,13 @@ export class MdSlideToggle implements AfterContentInit, ControlValueAccessor { this.onTouched = fn; } + /** + * Implemented as a part of ControlValueAccessor. + */ + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + } + @Input() get checked() { return !!this._checked;