diff --git a/src/buttons/button-radio-group.directive.ts b/src/buttons/button-radio-group.directive.ts index c4a2c05282..b79b7e33bd 100644 --- a/src/buttons/button-radio-group.directive.ts +++ b/src/buttons/button-radio-group.directive.ts @@ -1,6 +1,15 @@ -import { ChangeDetectorRef, Directive, forwardRef, Provider } from '@angular/core'; +import { + ChangeDetectorRef, + ContentChildren, + Directive, + forwardRef, + Provider, + QueryList +} from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { ButtonRadioDirective } from './button-radio.directive'; + export const RADIO_CONTROL_VALUE_ACCESSOR: Provider = { provide: NG_VALUE_ACCESSOR, /* tslint:disable-next-line: no-use-before-declare */ @@ -20,6 +29,8 @@ export class ButtonRadioGroupDirective implements ControlValueAccessor { onChange = Function.prototype; onTouched = Function.prototype; + @ContentChildren(forwardRef(() => ButtonRadioDirective)) radioButtons: QueryList; + get value() { return this._value; } @@ -43,4 +54,12 @@ export class ButtonRadioGroupDirective implements ControlValueAccessor { registerOnTouched(fn: () => {}): void { this.onTouched = fn; } + + setDisabledState(disabled: boolean): void { + if (this.radioButtons) { + this.radioButtons.forEach(buttons => { + buttons.setDisabledState(disabled); + }); + } + } } diff --git a/src/spec/button.directive.spec.ts b/src/spec/button.directive.spec.ts index fab752e68e..7509e5a477 100644 --- a/src/spec/button.directive.spec.ts +++ b/src/spec/button.directive.spec.ts @@ -1,18 +1,27 @@ // tslint:disable:max-file-line-count no-floating-promises -import { ChangeDetectionStrategy, ChangeDetectorRef, Component } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core'; import { ComponentFixture, ComponentFixtureAutoDetect, fakeAsync, TestBed, tick } from '@angular/core/testing'; -import { FormsModule } from '@angular/forms'; +import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { ButtonsModule } from '../buttons'; @Component({selector: 'buttons-test', template: ''}) -class TestButtonsComponent { +class TestButtonsComponent implements OnInit { singleModel = '0'; /* tslint:disable-next-line: no-any */ checkModel: any = {left: false, middle: true, right: false}; radioModel = 'Middle'; + myForm: FormGroup; - constructor(public cdRef: ChangeDetectorRef) {} + constructor(public cdRef: ChangeDetectorRef, + private formBuilder: FormBuilder) { + } + + ngOnInit(): void { + this.myForm = this.formBuilder.group({ + radio: 'Middle' + }); + } } const html = ` @@ -49,6 +58,16 @@ const html = ` + +
+
+
+ + + +
+
+
`; @@ -86,7 +105,7 @@ describe('Directive: Buttons', () => { fakeAsync(() => { TestBed.configureTestingModule({ declarations: [TestButtonsComponent], - imports: [ButtonsModule, FormsModule], + imports: [ButtonsModule, FormsModule, ReactiveFormsModule], providers: [{provide: ComponentFixtureAutoDetect, useValue: true}] }); }) @@ -326,6 +345,126 @@ describe('Directive: Buttons', () => { }); describe('radio', () => { + + describe('with reactive form', () => { + let btn = null; + + beforeEach( + fakeAsync(() => { + fixture = createComponent(html); + context = fixture.componentInstance; + element = fixture.nativeElement; + + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + + btn = element.querySelector('.btn-group.reactive-radio'); + }) + ); + + it( + 'should set active class based on model', + fakeAsync(() => { + expect(btn.children[0].classList).not.toContain('active'); + expect(btn.children[1].classList).toContain('active'); + expect(btn.children[2].classList).not.toContain('active'); + + context.myForm.get('radio').setValue('Left'); + fixture.detectChanges(); + tick(); + + expect(btn.children[0].classList).toContain('active'); + expect(btn.children[1].classList).not.toContain('active'); + expect(btn.children[2].classList).not.toContain('active'); + }) + ); + + it( + 'should set active class via click', + fakeAsync(() => { + (btn.children[0] as HTMLElement).click(); + fixture.detectChanges(); + tick(); + + expect(context.myForm.get('radio').value).toEqual('Left'); + expect(btn.children[0].classList).toContain('active'); + expect(btn.children[1].classList).not.toContain('active'); + expect(btn.children[2].classList).not.toContain('active'); + + (btn.children[2] as HTMLElement).click(); + fixture.detectChanges(); + tick(); + + expect(context.myForm.get('radio').value).toEqual('Right'); + expect(btn.children[0].classList).not.toContain('active'); + expect(btn.children[1].classList).not.toContain('active'); + expect(btn.children[2].classList).toContain('active'); + }) + ); + + it( + 'should do nothing when clicking an active radio', + fakeAsync(() => { + context.myForm.get('radio').setValue('Left'); + fixture.detectChanges(); + tick(); + + expect(btn.children[0].classList).toContain('active'); + expect(btn.children[1].classList).not.toContain('active'); + expect(btn.children[2].classList).not.toContain('active'); + + (btn.children[0] as HTMLElement).click(); + fixture.detectChanges(); + + expect(context.myForm.get('radio').value).toEqual('Left'); + expect(btn.children[0].classList).toContain('active'); + expect(btn.children[1].classList).not.toContain('active'); + expect(btn.children[2].classList).not.toContain('active'); + }) + ); + + it( + 'should set disabled attribute when form status is changed to disabled', + fakeAsync(() => { + expect(btn.children[0].getAttribute('disabled')).toBeNull(); + expect(btn.children[1].getAttribute('disabled')).toBeNull(); + expect(btn.children[2].getAttribute('disabled')).toBeNull(); + + context.myForm.disable(); + fixture.detectChanges(); + tick(); + + expect(btn.children[0].getAttribute('disabled')).toEqual('disabled'); + expect(btn.children[1].getAttribute('disabled')).toEqual('disabled'); + expect(btn.children[2].getAttribute('disabled')).toEqual('disabled'); + }) + ); + + it( + 'should not change model when form is disabled', + fakeAsync(() => { + context.myForm.get('radio').setValue('Left'); + fixture.detectChanges(); + tick(); + + expect(btn.children[0].classList).toContain('active'); + expect(btn.children[1].classList).not.toContain('active'); + expect(btn.children[2].classList).not.toContain('active'); + + context.myForm.disable(); + (btn.children[1] as HTMLElement).click(); + fixture.detectChanges(); + tick(); + + expect(context.myForm.get('radio').value).toEqual('Left'); + expect(btn.children[0].classList).toContain('active'); + expect(btn.children[1].classList).not.toContain('active'); + expect(btn.children[2].classList).not.toContain('active'); + }) + ); + }); + it( 'should set active class based on model', fakeAsync(() => {