From 544237a21b56ba92c39e68f3b33f253db6500a49 Mon Sep 17 00:00:00 2001 From: Sergey Andrievskiy Date: Tue, 2 Apr 2019 22:09:33 +0300 Subject: [PATCH 01/30] feat(radio): eva styles BREAKING CHANGES: Following theme variables were renamed: radio-bg -> radio-border-color, radio-inner-circle-color, radio-[status]-border-color, radio-[status]-inner-circle-color radio-fg -> radio-text-color radio-size -> radio-width, radio-height radio-border-size -> radio-border-width radio-checkmark -> radio-inner-circle-color radio-disabled-checkmark -> radio-disabled-inner-circle Following theme variables were removed: radio-checked-bg radio-checked-size radio-checked-border-size radio-checked-border-color radio-checked-checkmark radio-disabled-bg radio-disabled-size radio-disabled-border-size --- .../theme/styles/themes/_default.scss | 87 +++++++++++++++---- 1 file changed, 71 insertions(+), 16 deletions(-) diff --git a/src/framework/theme/styles/themes/_default.scss b/src/framework/theme/styles/themes/_default.scss index ad833536d1..bf1241e12a 100644 --- a/src/framework/theme/styles/themes/_default.scss +++ b/src/framework/theme/styles/themes/_default.scss @@ -959,22 +959,77 @@ $theme: ( datepicker-shadow: none, datepicker-arrow-size: 11px, - radio-bg: transparent, - radio-fg: color-fg-text, - radio-size: 1.25rem, - radio-border-size: 2px, - radio-border-color: form-control-border-color, - radio-checkmark: transparent, - radio-checked-bg: transparent, - radio-checked-size: 1.25rem, - radio-checked-border-size: 2px, - radio-checked-border-color: color-success, - radio-checked-checkmark: color-success, - radio-disabled-bg: transparent, - radio-disabled-size: 1.25rem, - radio-disabled-border-size: 2px, - radio-disabled-border-color: radio-border-color, - radio-disabled-checkmark: radio-checkmark, + radio-width: 1.125rem, + radio-height: 1.125rem, + radio-border-width: 0.125rem, + radio-text-color: text-color, + radio-text-font-family: font-primary-family, + radio-text-font-size: font-primary-text-size, + radio-text-font-weight: font-primary-text-weight, + radio-text-line-height: font-primary-text-line-height, + radio-outline-color: outline-color, + radio-outline-width: outline-width, + + radio-border-color: color-primary, + radio-inner-circle-color: transparent, + + radio-disabled-border-color: color-basic, + radio-disabled-text-color: text-disabled-color, + radio-disabled-inner-circle: color-basic, + + radio-primary-border-color: color-primary, + radio-primary-inner-circle-color: color-primary, + radio-primary-focus-border-color: color-primary-700, + radio-primary-focus-inner-circle-color: color-primary-700, + radio-primary-hover-border-color: color-primary-400, + radio-primary-hover-inner-circle-color: color-primary-400, + radio-primary-active-border-color: color-primary-600, + radio-primary-active-inner-circle-color: color-primary-600, + + radio-success-border-color: color-success, + radio-success-inner-circle-color: color-success, + radio-success-focus-border-color: color-success-700, + radio-success-focus-inner-circle-color: color-success-700, + radio-success-hover-border-color: color-success-400, + radio-success-hover-inner-circle-color: color-success-400, + radio-success-active-border-color: color-success-600, + radio-success-active-inner-circle-color: color-success-600, + + radio-warning-border-color: color-warning, + radio-warning-inner-circle-color: color-warning, + radio-warning-focus-border-color: color-warning-700, + radio-warning-focus-inner-circle-color: color-warning-700, + radio-warning-hover-border-color: color-warning-400, + radio-warning-hover-inner-circle-color: color-warning-400, + radio-warning-active-border-color: color-warning-600, + radio-warning-active-inner-circle-color: color-warning-600, + + radio-danger-border-color: color-danger, + radio-danger-inner-circle-color: color-danger, + radio-danger-focus-border-color: color-danger-700, + radio-danger-focus-inner-circle-color: color-danger-700, + radio-danger-hover-border-color: color-danger-400, + radio-danger-hover-inner-circle-color: color-danger-400, + radio-danger-active-border-color: color-danger-600, + radio-danger-active-inner-circle-color: color-danger-600, + + radio-info-border-color: color-info, + radio-info-inner-circle-color: color-info, + radio-info-focus-border-color: color-info-700, + radio-info-focus-inner-circle-color: color-info-700, + radio-info-hover-border-color: color-info-400, + radio-info-hover-inner-circle-color: color-info-400, + radio-info-active-border-color: color-info-600, + radio-info-active-inner-circle-color: color-info-600, + + radio-white-border-color: color-white, + radio-white-inner-circle-color: color-white, + radio-white-focus-border-color: color-primary-700, + radio-white-focus-inner-circle-color: color-primary-700, + radio-white-hover-border-color: color-primary-400, + radio-white-hover-inner-circle-color: color-primary-400, + radio-white-active-border-color: color-primary-600, + radio-white-active-inner-circle-color: color-primary-600, tree-grid-cell-border-width: 1px, tree-grid-cell-border-style: solid, From 267670ba4e23d7548d8f04cb5838d71011841dfc Mon Sep 17 00:00:00 2001 From: Sergey Andrievskiy Date: Sun, 24 Mar 2019 17:39:20 +0300 Subject: [PATCH 02/30] refactor(radio): align class names with checkbox BREAKING CHANGES: Following classes were renamed: radio-indicator -> radio-circle radio-description -> text --- .../radio/_radio.component.theme.scss | 14 +++++++------- .../theme/components/radio/radio.component.scss | 17 ++--------------- .../theme/components/radio/radio.component.ts | 7 ++++--- 3 files changed, 13 insertions(+), 25 deletions(-) diff --git a/src/framework/theme/components/radio/_radio.component.theme.scss b/src/framework/theme/components/radio/_radio.component.theme.scss index 78fcb955f4..76a9005c0f 100644 --- a/src/framework/theme/components/radio/_radio.component.theme.scss +++ b/src/framework/theme/components/radio/_radio.component.theme.scss @@ -5,8 +5,8 @@ */ @mixin input-border-color($color) { - input:checked + .radio-indicator, - input:hover:not(:disabled) + .radio-indicator { + .native-input:checked + .radio-circle, + .native-input:hover:not(:disabled) + .radio-circle { border-color: $color; } } @@ -42,7 +42,7 @@ @mixin nb-radio-theme() { nb-radio { - .radio-indicator { + .radio-circle { @include set-box-style( nb-theme(radio-bg), nb-theme(radio-size), @@ -53,8 +53,8 @@ @include nb-radio-check-mark(nb-theme(radio-size), nb-theme(radio-checkmark)); } - input:checked + .radio-indicator, - input:disabled:checked + .radio-indicator { + .native-input:checked + .radio-circle, + .native-input:disabled:checked + .radio-circle { @include set-box-style( nb-theme(radio-checked-bg), nb-theme(radio-checked-size), @@ -64,7 +64,7 @@ @include nb-radio-check-mark(nb-theme(radio-checked-size), nb-theme(radio-checked-checkmark)); } - input:disabled + .radio-indicator { + .native-input:disabled + .radio-circle { @include set-box-style( nb-theme(radio-disabled-bg), nb-theme(radio-disabled-size), @@ -76,7 +76,7 @@ @include nb-input-status-color(nb-theme(radio-checked-border-color)); - .radio-description { + .text { color: nb-theme(radio-fg); } } diff --git a/src/framework/theme/components/radio/radio.component.scss b/src/framework/theme/components/radio/radio.component.scss index afe7e64422..3b5f5a5916 100644 --- a/src/framework/theme/components/radio/radio.component.scss +++ b/src/framework/theme/components/radio/radio.component.scss @@ -17,19 +17,7 @@ padding: 0.375rem 1.5rem 0.375rem 0; } - input { - position: absolute; - opacity: 0; - - &:disabled { - & + .radio-indicator, - & ~ .radio-description { - opacity: 0.5; - } - } - } - - .radio-indicator { + .radio-circle { border-radius: 50%; flex-shrink: 0; display: flex; @@ -38,12 +26,11 @@ &::before { content: ''; - transition: all 0.1s; border-radius: 50%; } } - .radio-description { + .text { @include nb-ltr(padding-left, 0.5rem); @include nb-rtl(padding-right, 0.5rem); } diff --git a/src/framework/theme/components/radio/radio.component.ts b/src/framework/theme/components/radio/radio.component.ts index a8875a5d83..35b7daf48e 100644 --- a/src/framework/theme/components/radio/radio.component.ts +++ b/src/framework/theme/components/radio/radio.component.ts @@ -22,7 +22,7 @@ import { convertToBoolProperty } from '../helpers'; * ```ts * @NgModule({ * imports: [ - * // ... + * // ... * NbRadioModule, * ], * }) @@ -71,14 +71,15 @@ import { convertToBoolProperty } from '../helpers'; From b07dbe979257dc9af5e3cc0ae52e7c67b5beaa24 Mon Sep 17 00:00:00 2001 From: Sergey Andrievskiy Date: Sun, 24 Mar 2019 19:03:39 +0300 Subject: [PATCH 03/30] fix(radio): update and resubscribe to option after they change --- .../components/radio/radio-group.component.ts | 37 ++++-- .../theme/components/radio/radio.spec.ts | 106 +++++++++++++++++- 2 files changed, 130 insertions(+), 13 deletions(-) diff --git a/src/framework/theme/components/radio/radio-group.component.ts b/src/framework/theme/components/radio/radio-group.component.ts index 3c017f0772..1747af74b1 100644 --- a/src/framework/theme/components/radio/radio-group.component.ts +++ b/src/framework/theme/components/radio/radio-group.component.ts @@ -23,7 +23,7 @@ import { import { isPlatformBrowser } from '@angular/common'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { fromEvent, merge } from 'rxjs'; -import { filter, switchMap, take, takeWhile } from 'rxjs/operators'; +import { filter, switchMap, take, takeUntil, takeWhile } from 'rxjs/operators'; import { convertToBoolProperty } from '../helpers'; import { NB_DOCUMENT } from '../../theme.options'; import { NbRadioComponent } from './radio.component'; @@ -108,6 +108,7 @@ export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, Contr protected value: any; protected name: string; protected alive: boolean = true; + protected isTouched: boolean = false; protected onChange = (value: any) => {}; protected onTouched = () => {}; @@ -119,11 +120,11 @@ export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, Contr ) {} ngAfterContentInit() { - this.updateNames(); - this.updateValues(); - this.updateDisabled(); - this.subscribeOnRadiosValueChange(); - this.subscribeOnRadiosBlur(); + this.updateAndSubscribeToRadios(); + + this.radios.changes + .pipe(takeWhile(() => this.alive)) + .subscribe(() => this.updateAndSubscribeToRadios()); } ngOnDestroy() { @@ -146,6 +147,14 @@ export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, Contr } } + protected updateAndSubscribeToRadios() { + this.updateNames(); + this.updateValues(); + this.updateDisabled(); + this.subscribeOnRadiosValueChange(); + this.subscribeOnRadiosBlur(); + } + protected updateNames() { if (this.radios) { this.radios.forEach((radio: NbRadioComponent) => radio.name = this.name); @@ -169,7 +178,10 @@ export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, Contr protected subscribeOnRadiosValueChange() { merge(...this.radios.map((radio: NbRadioComponent) => radio.valueChange)) - .pipe(takeWhile(() => this.alive)) + .pipe( + takeWhile(() => this.alive), + takeUntil(this.radios.changes), + ) .subscribe((value: any) => { this.writeValue(value); this.propagateValue(value); @@ -186,13 +198,14 @@ export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, Contr } protected subscribeOnRadiosBlur() { - if (!isPlatformBrowser(this.platformId)) { + if (!isPlatformBrowser(this.platformId) || this.isTouched) { return; } const hostElement = this.hostElement.nativeElement; fromEvent(hostElement, 'focusin') .pipe( + takeWhile(() => this.alive), filter(event => hostElement.contains(event.target as Node)), switchMap(() => merge( fromEvent(this.document, 'focusin'), @@ -200,7 +213,13 @@ export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, Contr )), filter(event => !hostElement.contains(event.target as Node)), take(1), + takeUntil(this.radios.changes), ) - .subscribe(() => this.onTouched()); + .subscribe(() => this.markTouched()); + } + + protected markTouched() { + this.isTouched = true; + this.onTouched(); } } diff --git a/src/framework/theme/components/radio/radio.spec.ts b/src/framework/theme/components/radio/radio.spec.ts index 0ee3dbacb1..1cf615894f 100644 --- a/src/framework/theme/components/radio/radio.spec.ts +++ b/src/framework/theme/components/radio/radio.spec.ts @@ -4,15 +4,25 @@ * Licensed under the MIT License. See License.txt in the project root for license information. */ -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; +import { + Component, + DebugElement, + EventEmitter, + Input, + Output, + QueryList, + ViewChild, + ViewChildren, +} from '@angular/core'; +import { By } from '@angular/platform-browser'; +import createSpy = jasmine.createSpy; import { NbRadioModule } from './radio.module'; import { NbRadioComponent } from './radio.component'; +import { NbRadioGroupComponent } from './radio-group.component'; import { NB_DOCUMENT } from '../../theme.options'; -import { Component, DebugElement, EventEmitter, Input, Output } from '@angular/core'; -import { By } from '@angular/platform-browser'; - @Component({ selector: 'nb-radio-test', template: ` @@ -28,6 +38,23 @@ export class NbRadioTestComponent { @Output() valueChange = new EventEmitter(); } +@Component({ + template: ` + + + {{radio}} + + + `, +}) +export class NbRadioWithDynamicValuesTestComponent { + radioValues: number[] = []; + showRadios: boolean = false; + + @ViewChild(NbRadioGroupComponent) radioGroupComponent: NbRadioGroupComponent; + @ViewChildren(NbRadioComponent) radioComponents: QueryList; +} + describe('radio', () => { let fixture: ComponentFixture; let comp: NbRadioTestComponent; @@ -56,3 +83,74 @@ describe('radio', () => { input.nativeElement.click(); }); }); + +describe('NbRadioGroupComponent', () => { + let fixture: ComponentFixture; + let radioTestComponent: NbRadioWithDynamicValuesTestComponent; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ NbRadioModule ], + declarations: [ NbRadioWithDynamicValuesTestComponent ], + providers: [ { provide: NB_DOCUMENT, useValue: document } ], + }); + + fixture = TestBed.createComponent(NbRadioWithDynamicValuesTestComponent); + radioTestComponent = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should update radio properties when radios added after radio group initialization', () => { + radioTestComponent.radioValues = [1, 2, 3]; + radioTestComponent.showRadios = true; + radioTestComponent.radioGroupComponent.setValue = 1; + fixture.detectChanges(); + + expect(radioTestComponent.radioComponents.first.checked).toEqual(true); + }); + + it('should update subscription to radio change when radios added after radio group initialization', fakeAsync(() => { + const radioValue = 333; + radioTestComponent.radioValues = [radioValue]; + radioTestComponent.showRadios = true; + fixture.detectChanges(); + + const valueChangeSpy = createSpy('valueChange'); + radioTestComponent.radioGroupComponent.valueChange.subscribe(valueChangeSpy); + radioTestComponent.radioComponents.first.valueChange.emit(radioValue); + + tick(); + + expect(valueChangeSpy).toHaveBeenCalledTimes(1); + expect(valueChangeSpy).toHaveBeenCalledWith(radioValue); + })); + + it('should update radio properties when radios change', () => { + radioTestComponent.showRadios = true; + radioTestComponent.radioGroupComponent.setValue = 1; + fixture.detectChanges(); + + radioTestComponent.radioValues = [1, 2, 3]; + fixture.detectChanges(); + + expect(radioTestComponent.radioComponents.first.checked).toEqual(true); + }); + + it('should update subscription to radio change when radios change', fakeAsync(() => { + radioTestComponent.showRadios = true; + fixture.detectChanges(); + + const valueChangeSpy = createSpy('valueChange'); + radioTestComponent.radioGroupComponent.valueChange.subscribe(valueChangeSpy); + + const radioValue = 333; + radioTestComponent.radioValues = [radioValue]; + fixture.detectChanges(); + + radioTestComponent.radioComponents.first.valueChange.emit(radioValue); + tick(); + + expect(valueChangeSpy).toHaveBeenCalledTimes(1); + expect(valueChangeSpy).toHaveBeenCalledWith(radioValue); + })); +}); From 3138b24ac89116357da1ccdb3deda5a624243b9c Mon Sep 17 00:00:00 2001 From: Sergey Andrievskiy Date: Sun, 24 Mar 2019 19:28:46 +0300 Subject: [PATCH 04/30] fix(radio): update and resubscribe to option after they change --- .../theme/components/radio/radio.spec.ts | 62 ++++++++++++++++++- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/src/framework/theme/components/radio/radio.spec.ts b/src/framework/theme/components/radio/radio.spec.ts index 1cf615894f..cf1b81a55b 100644 --- a/src/framework/theme/components/radio/radio.spec.ts +++ b/src/framework/theme/components/radio/radio.spec.ts @@ -100,13 +100,40 @@ describe('NbRadioGroupComponent', () => { fixture.detectChanges(); }); - it('should update radio properties when radios added after radio group initialization', () => { + it('should update radio value when radios added after radio group initialization', () => { radioTestComponent.radioValues = [1, 2, 3]; radioTestComponent.showRadios = true; radioTestComponent.radioGroupComponent.setValue = 1; fixture.detectChanges(); expect(radioTestComponent.radioComponents.first.checked).toEqual(true); + const otherRadios = radioTestComponent.radioComponents.toArray().slice(1); + for (const radio of otherRadios) { + expect(radio.checked).toBeFalsy(); + } + }); + + it('should update radio name when radios added after radio group initialization', () => { + const groupName = 'my-radio-group-name'; + radioTestComponent.radioValues = [1, 2, 3]; + radioTestComponent.showRadios = true; + radioTestComponent.radioGroupComponent.setName = groupName; + fixture.detectChanges(); + + for (const radio of radioTestComponent.radioComponents.toArray()) { + expect(radio.name).toEqual(groupName); + } + }); + + it('should update radio disabled state when radios added after radio group initialization', () => { + radioTestComponent.radioValues = [1, 2, 3]; + radioTestComponent.showRadios = true; + radioTestComponent.radioGroupComponent.setDisabled = true; + fixture.detectChanges(); + + for (const radio of radioTestComponent.radioComponents.toArray()) { + expect(radio.disabled).toEqual(true); + } }); it('should update subscription to radio change when radios added after radio group initialization', fakeAsync(() => { @@ -125,7 +152,7 @@ describe('NbRadioGroupComponent', () => { expect(valueChangeSpy).toHaveBeenCalledWith(radioValue); })); - it('should update radio properties when radios change', () => { + it('should update radio value when radios change', () => { radioTestComponent.showRadios = true; radioTestComponent.radioGroupComponent.setValue = 1; fixture.detectChanges(); @@ -134,6 +161,37 @@ describe('NbRadioGroupComponent', () => { fixture.detectChanges(); expect(radioTestComponent.radioComponents.first.checked).toEqual(true); + const otherRadios = radioTestComponent.radioComponents.toArray().slice(1); + for (const radio of otherRadios) { + expect(radio.checked).toBeFalsy(); + } + }); + + it('should update radio name when radios change', () => { + const groupName = 'my-radio-group-name'; + radioTestComponent.showRadios = true; + radioTestComponent.radioGroupComponent.setName = groupName; + fixture.detectChanges(); + + radioTestComponent.radioValues = [1, 2, 3]; + fixture.detectChanges(); + + for (const radio of radioTestComponent.radioComponents.toArray()) { + expect(radio.name).toEqual(groupName); + } + }); + + it('should update radio disabled state when radios change', () => { + radioTestComponent.showRadios = true; + radioTestComponent.radioGroupComponent.setDisabled = true; + fixture.detectChanges(); + + radioTestComponent.radioValues = [1, 2, 3]; + fixture.detectChanges(); + + for (const radio of radioTestComponent.radioComponents.toArray()) { + expect(radio.disabled).toEqual(true); + } }); it('should update subscription to radio change when radios change', fakeAsync(() => { From 3dd3cd7b5c43bd9e3a9048792f2d57a304815028 Mon Sep 17 00:00:00 2001 From: Sergey Andrievskiy Date: Sun, 24 Mar 2019 20:39:35 +0300 Subject: [PATCH 05/30] feat(radio): add statuses to radio button --- .../components/radio/radio-group.component.ts | 22 ++++++++++- .../radio/radio-status.component.ts | 8 ++++ .../theme/components/radio/radio.component.ts | 37 ++++++++++++++++++- .../theme/components/radio/radio.spec.ts | 25 +++++++++++++ 4 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 src/framework/theme/components/radio/radio-status.component.ts diff --git a/src/framework/theme/components/radio/radio-group.component.ts b/src/framework/theme/components/radio/radio-group.component.ts index 1747af74b1..db3ecc3c19 100644 --- a/src/framework/theme/components/radio/radio-group.component.ts +++ b/src/framework/theme/components/radio/radio-group.component.ts @@ -27,7 +27,7 @@ import { filter, switchMap, take, takeUntil, takeWhile } from 'rxjs/operators'; import { convertToBoolProperty } from '../helpers'; import { NB_DOCUMENT } from '../../theme.options'; import { NbRadioComponent } from './radio.component'; - +import { NbRadioStatus } from './radio-status.component'; /** * The `NbRadioGroupComponent` is the wrapper for `nb-radio` button. @@ -102,6 +102,15 @@ export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, Contr this.updateDisabled(); } + /** + * Radio buttons status. + */ + @Input('status') + set setStatus(status: NbRadioStatus) { + this.status = status; + this.updateStatus(); + } + @Output() valueChange: EventEmitter = new EventEmitter(); protected disabled: boolean; @@ -109,6 +118,7 @@ export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, Contr protected name: string; protected alive: boolean = true; protected isTouched: boolean = false; + protected status: NbRadioStatus = NbRadioStatus.PRIMARY; protected onChange = (value: any) => {}; protected onTouched = () => {}; @@ -151,6 +161,7 @@ export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, Contr this.updateNames(); this.updateValues(); this.updateDisabled(); + this.updateStatus(); this.subscribeOnRadiosValueChange(); this.subscribeOnRadiosBlur(); } @@ -222,4 +233,13 @@ export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, Contr this.isTouched = true; this.onTouched(); } + + protected updateStatus() { + if (this.radios) { + this.radios.forEach((radio: NbRadioComponent) => { + radio.status = this.status; + radio.markForCheck(); + }); + } + } } diff --git a/src/framework/theme/components/radio/radio-status.component.ts b/src/framework/theme/components/radio/radio-status.component.ts new file mode 100644 index 0000000000..783f995509 --- /dev/null +++ b/src/framework/theme/components/radio/radio-status.component.ts @@ -0,0 +1,8 @@ +export enum NbRadioStatus { + PRIMARY = 'primary', + SUCCESS = 'success', + WARNING = 'warning', + DANGER = 'danger', + INFO = 'info', + WHITE = 'white', +} diff --git a/src/framework/theme/components/radio/radio.component.ts b/src/framework/theme/components/radio/radio.component.ts index 35b7daf48e..2b9748abe2 100644 --- a/src/framework/theme/components/radio/radio.component.ts +++ b/src/framework/theme/components/radio/radio.component.ts @@ -7,7 +7,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core'; import { convertToBoolProperty } from '../helpers'; - +import { NbRadioStatus } from './radio-status.component'; /** * The `NbRadioComponent` provides the same functionality as native `` @@ -78,7 +78,14 @@ import { convertToBoolProperty } from '../helpers'; [disabled]="disabled" (change)="onChange($event)" (click)="onClick($event)"> - + + @@ -100,6 +107,8 @@ export class NbRadioComponent { this.disabled = convertToBoolProperty(disabled); } + @Input() status: NbRadioStatus; + @Output() valueChange: EventEmitter = new EventEmitter(); @Output() blur: EventEmitter = new EventEmitter(); @@ -108,6 +117,30 @@ export class NbRadioComponent { constructor(protected cd: ChangeDetectorRef) {} + get isPrimary(): boolean { + return this.status === NbRadioStatus.PRIMARY; + } + + get isSuccess(): boolean { + return this.status === NbRadioStatus.SUCCESS; + } + + get isWarning(): boolean { + return this.status === NbRadioStatus.WARNING; + } + + get isDanger(): boolean { + return this.status === NbRadioStatus.DANGER; + } + + get isInfo(): boolean { + return this.status === NbRadioStatus.INFO; + } + + get isWhite(): boolean { + return this.status === NbRadioStatus.WHITE; + } + markForCheck() { this.cd.markForCheck(); this.cd.detectChanges(); diff --git a/src/framework/theme/components/radio/radio.spec.ts b/src/framework/theme/components/radio/radio.spec.ts index cf1b81a55b..dc8a789b4c 100644 --- a/src/framework/theme/components/radio/radio.spec.ts +++ b/src/framework/theme/components/radio/radio.spec.ts @@ -22,6 +22,7 @@ import { NbRadioModule } from './radio.module'; import { NbRadioComponent } from './radio.component'; import { NbRadioGroupComponent } from './radio-group.component'; import { NB_DOCUMENT } from '../../theme.options'; +import { NbRadioStatus } from './radio-status.component'; @Component({ selector: 'nb-radio-test', @@ -136,6 +137,17 @@ describe('NbRadioGroupComponent', () => { } }); + it('should update radio status when radios added after radio group initialization', () => { + radioTestComponent.radioValues = [1, 2, 3]; + radioTestComponent.showRadios = true; + radioTestComponent.radioGroupComponent.setStatus = NbRadioStatus.INFO; + fixture.detectChanges(); + + for (const radio of radioTestComponent.radioComponents.toArray()) { + expect(radio.status).toEqual(NbRadioStatus.INFO); + } + }); + it('should update subscription to radio change when radios added after radio group initialization', fakeAsync(() => { const radioValue = 333; radioTestComponent.radioValues = [radioValue]; @@ -194,6 +206,19 @@ describe('NbRadioGroupComponent', () => { } }); + it('should update radio status when radios change', () => { + radioTestComponent.showRadios = true; + radioTestComponent.radioGroupComponent.setStatus = NbRadioStatus.INFO; + fixture.detectChanges(); + + radioTestComponent.radioValues = [1, 2, 3]; + fixture.detectChanges(); + + for (const radio of radioTestComponent.radioComponents.toArray()) { + expect(radio.status).toEqual(NbRadioStatus.INFO); + } + }); + it('should update subscription to radio change when radios change', fakeAsync(() => { radioTestComponent.showRadios = true; fixture.detectChanges(); From 00a4ce29847204f22d60fea6bd7e6a9989f8302a Mon Sep 17 00:00:00 2001 From: Sergey Andrievskiy Date: Sun, 24 Mar 2019 22:09:27 +0300 Subject: [PATCH 06/30] refactor(radio): style using updated component properties BREAKING CHANGES: 'radio-bg', 'radio-checkmark' theme variables were removed. Use radio-[status]-border-color and radio-[status]-inner-circle-color instead. --- .../radio/_radio.component.theme.scss | 109 ++++++++---------- .../components/radio/radio.component.scss | 13 ++- .../theme/styles/themes/_default.scss | 7 +- 3 files changed, 57 insertions(+), 72 deletions(-) diff --git a/src/framework/theme/components/radio/_radio.component.theme.scss b/src/framework/theme/components/radio/_radio.component.theme.scss index 76a9005c0f..386ee40e90 100644 --- a/src/framework/theme/components/radio/_radio.component.theme.scss +++ b/src/framework/theme/components/radio/_radio.component.theme.scss @@ -4,80 +4,65 @@ * Licensed under the MIT License. See License.txt in the project root for license information. */ -@mixin input-border-color($color) { - .native-input:checked + .radio-circle, - .native-input:hover:not(:disabled) + .radio-circle { - border-color: $color; - } -} - -@mixin nb-input-status-color($origin-border-color) { - @include input-border-color($origin-border-color); - &.success { - @include input-border-color(nb-theme(color-success)); - } - &.warning { - @include input-border-color(nb-theme(color-warning)); - } - &.danger { - @include input-border-color(nb-theme(color-danger)); - } -} - -@mixin nb-radio-check-mark($size, $color) { - &::before { - background-color: $color; - height: calc(#{$size} * 0.6); - width: calc(#{$size} * 0.6); - border: solid $color; - } -} - -@mixin set-box-style($bg, $size, $border-size, $border-color) { - background-color: $bg; - width: $size; - height: $size; - border: $border-size solid $border-color; -} - @mixin nb-radio-theme() { nb-radio { .radio-circle { - @include set-box-style( - nb-theme(radio-bg), - nb-theme(radio-size), - nb-theme(radio-border-size), - nb-theme(radio-border-color) - ); - - @include nb-radio-check-mark(nb-theme(radio-size), nb-theme(radio-checkmark)); + height: nb-theme(radio-height); + width: nb-theme(radio-width); + border-width: nb-theme(radio-border-width); } - .native-input:checked + .radio-circle, - .native-input:disabled:checked + .radio-circle { - @include set-box-style( - nb-theme(radio-checked-bg), - nb-theme(radio-checked-size), - nb-theme(radio-checked-border-size), - nb-theme(radio-checked-border-color) - ); - @include nb-radio-check-mark(nb-theme(radio-checked-size), nb-theme(radio-checked-checkmark)); + .native-input:focus + .radio-circle { + box-shadow: 0 0 0 nb-theme(radio-outline-width) nb-theme(radio-outline-color); } .native-input:disabled + .radio-circle { - @include set-box-style( - nb-theme(radio-disabled-bg), - nb-theme(radio-disabled-size), - nb-theme(radio-disabled-border-size), - nb-theme(radio-disabled-border-color) - ); - @include nb-radio-check-mark(nb-theme(radio-disabled-size), nb-theme(radio-disabled-checkmark)); + border-color: nb-theme(radio-disabled-border-color); + } + .native-input:disabled:checked + .radio-circle::before { + background-color: nb-theme(radio-disabled-inner-circle-color); } + .native-input:disabled ~ .text { + color: nb-theme(radio-disabled-text-color); + } + + $status-variants: 'primary', 'success', 'warning', 'danger', 'info', 'white'; + @each $status in $status-variants { + .native-input:enabled + .radio-circle.status-#{$status} { + border-color: nb-theme(radio-#{$status}-border-color); + } + .native-input:enabled:checked + .radio-circle.status-#{$status}::before { + background-color: nb-theme(radio-#{$status}-inner-circle-color);; + } + + .native-input:enabled:focus + .radio-circle.status-#{$status} { + border-color: nb-theme(radio-#{$status}-focus-border-color); + } + .native-input:enabled:checked:focus + .radio-circle.status-#{$status}::before { + background-color: nb-theme(radio-#{$status}-focus-inner-circle-color); + } - @include nb-input-status-color(nb-theme(radio-checked-border-color)); + label:hover .native-input:enabled + .radio-circle.status-#{$status} { + border-color: nb-theme(radio-#{$status}-hover-border-color); + } + label:hover .native-input:checked:enabled + .radio-circle.status-#{$status}::before { + background-color: nb-theme(radio-#{$status}-hover-inner-circle-color); + } + + .native-input:enabled:active + .radio-circle.status-#{$status} { + border-color: nb-theme(radio-#{$status}-active-border-color); + } + .native-input:enabled:checked:active + .radio-circle.status-#{$status}::before { + background-color: nb-theme(radio-#{$status}-active-inner-circle-color); + } + } .text { - color: nb-theme(radio-fg); + color: nb-theme(radio-text-color); + font-family: nb-theme(radio-text-font-family); + font-size: nb-theme(radio-text-font-size); + font-weight: nb-theme(radio-text-font-weight); + line-height: nb-theme(radio-text-line-height); } } } diff --git a/src/framework/theme/components/radio/radio.component.scss b/src/framework/theme/components/radio/radio.component.scss index 3b5f5a5916..7237d14426 100644 --- a/src/framework/theme/components/radio/radio.component.scss +++ b/src/framework/theme/components/radio/radio.component.scss @@ -10,24 +10,27 @@ display: block; label { - position: relative; display: inline-flex; margin: 0; min-height: inherit; padding: 0.375rem 1.5rem 0.375rem 0; + align-items: center; } .radio-circle { border-radius: 50%; + border-style: solid; flex-shrink: 0; display: flex; justify-content: center; align-items: center; + } - &::before { - content: ''; - border-radius: 50%; - } + .radio-circle::before { + content: ''; + border-radius: 50%; + height: 80%; + width: 80%; } .text { diff --git a/src/framework/theme/styles/themes/_default.scss b/src/framework/theme/styles/themes/_default.scss index bf1241e12a..91243d25ca 100644 --- a/src/framework/theme/styles/themes/_default.scss +++ b/src/framework/theme/styles/themes/_default.scss @@ -961,7 +961,7 @@ $theme: ( radio-width: 1.125rem, radio-height: 1.125rem, - radio-border-width: 0.125rem, + radio-border-width: 0.0625rem, radio-text-color: text-color, radio-text-font-family: font-primary-family, radio-text-font-size: font-primary-text-size, @@ -970,12 +970,9 @@ $theme: ( radio-outline-color: outline-color, radio-outline-width: outline-width, - radio-border-color: color-primary, - radio-inner-circle-color: transparent, - radio-disabled-border-color: color-basic, radio-disabled-text-color: text-disabled-color, - radio-disabled-inner-circle: color-basic, + radio-disabled-inner-circle-color: color-basic, radio-primary-border-color: color-primary, radio-primary-inner-circle-color: color-primary, From 409b1498917c2575e68a4ffe299169ffd2d90cf9 Mon Sep 17 00:00:00 2001 From: Sergey Andrievskiy Date: Sun, 24 Mar 2019 22:45:17 +0300 Subject: [PATCH 07/30] fix(radio): prevent inner circle distortion Set inner circle dimensions to be multiple of radio circle dimensions --- src/framework/theme/components/radio/radio.component.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/framework/theme/components/radio/radio.component.scss b/src/framework/theme/components/radio/radio.component.scss index 7237d14426..645cda9bcb 100644 --- a/src/framework/theme/components/radio/radio.component.scss +++ b/src/framework/theme/components/radio/radio.component.scss @@ -29,8 +29,8 @@ .radio-circle::before { content: ''; border-radius: 50%; - height: 80%; - width: 80%; + height: 75%; + width: 75%; } .text { From 37a550ad9521f1cca996df713f0761a78d53ef14 Mon Sep 17 00:00:00 2001 From: Sergey Andrievskiy Date: Sun, 24 Mar 2019 23:05:01 +0300 Subject: [PATCH 08/30] docs(radio): add statuses and disabled group examples --- src/app/playground-components.ts | 12 ++++++++ .../components/radio/radio-group.component.ts | 12 ++++---- .../radio/radio-disabled-group.component.ts | 24 +++++++++++++++ .../with-layout/radio/radio-routing.module.ts | 10 +++++++ .../radio/radio-statuses-group.component.scss | 13 +++++++++ .../radio/radio-statuses.component.ts | 29 +++++++++++++++++++ .../with-layout/radio/radio.module.ts | 5 ++++ 7 files changed, 99 insertions(+), 6 deletions(-) create mode 100644 src/playground/with-layout/radio/radio-disabled-group.component.ts create mode 100644 src/playground/with-layout/radio/radio-statuses-group.component.scss create mode 100644 src/playground/with-layout/radio/radio-statuses.component.ts diff --git a/src/app/playground-components.ts b/src/app/playground-components.ts index ec4a697804..d5e1598859 100644 --- a/src/app/playground-components.ts +++ b/src/app/playground-components.ts @@ -794,6 +794,18 @@ export const PLAYGROUND_COMPONENTS: ComponentLink[] = [ component: 'RadioShowcaseComponent', name: 'Radio Showcase', }, + { + path: 'radio-statuses.component', + link: '/radio/radio-statuses.component', + component: 'RadioStatusesComponent', + name: 'Radio Statuses', + }, + { + path: 'radio-disabled-group.component', + link: '/radio/radio-disabled-group.component', + component: 'RadioDisabledGroupComponent', + name: 'Radio Disabled Group', + }, ], }, { diff --git a/src/framework/theme/components/radio/radio-group.component.ts b/src/framework/theme/components/radio/radio-group.component.ts index db3ecc3c19..62557c9ba9 100644 --- a/src/framework/theme/components/radio/radio-group.component.ts +++ b/src/framework/theme/components/radio/radio-group.component.ts @@ -59,13 +59,12 @@ import { NbRadioStatus } from './radio-status.component'; * * ``` * + * You can change radio group status by setting `status` input. + * @stacked-example(Statuses, radio/radio-statuses.component) + * * Also, you can disable the whole group using `disabled` attribute. + * @stacked-example(Disabled group, radio/radio-disabled-group.component) * - * ```html - * - * ... - * - * ``` * */ @Component({ selector: 'nb-radio-group', @@ -103,7 +102,8 @@ export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, Contr } /** - * Radio buttons status. + * Radio buttons status. Primary by default. + * Possible values are 'primary', 'success', 'warning', 'danger', 'info', 'white'. */ @Input('status') set setStatus(status: NbRadioStatus) { diff --git a/src/playground/with-layout/radio/radio-disabled-group.component.ts b/src/playground/with-layout/radio/radio-disabled-group.component.ts new file mode 100644 index 0000000000..77454b5ab8 --- /dev/null +++ b/src/playground/with-layout/radio/radio-disabled-group.component.ts @@ -0,0 +1,24 @@ +import { Component } from '@angular/core'; + +@Component({ + template: ` + + + + + {{ option.label }} + + + + + `, +}) +export class RadioDisabledGroupComponent { + options = [ + { value: 'This is value 1', label: 'Option 1' }, + { value: 'This is value 2', label: 'Option 2' }, + { value: 'This is value 3', label: 'Option 3' }, + { value: 'This is value 4', label: 'Option 4' }, + { value: 'This is value 5', label: 'Option 5' }, + ]; +} diff --git a/src/playground/with-layout/radio/radio-routing.module.ts b/src/playground/with-layout/radio/radio-routing.module.ts index da68a8ee04..443cc7e4d4 100644 --- a/src/playground/with-layout/radio/radio-routing.module.ts +++ b/src/playground/with-layout/radio/radio-routing.module.ts @@ -8,6 +8,8 @@ import { NgModule } from '@angular/core'; import { RouterModule, Route} from '@angular/router'; import { RadioDisabledComponent } from './radio-disabled.component'; import { RadioShowcaseComponent } from './radio-showcase.component'; +import { RadioStatusesComponent } from './radio-statuses.component'; +import { RadioDisabledGroupComponent } from './radio-disabled-group.component'; const routes: Route[] = [ { @@ -18,6 +20,14 @@ const routes: Route[] = [ path: 'radio-showcase.component', component: RadioShowcaseComponent, }, + { + path: 'radio-statuses.component', + component: RadioStatusesComponent, + }, + { + path: 'radio-disabled-group.component', + component: RadioDisabledGroupComponent, + }, ]; @NgModule({ diff --git a/src/playground/with-layout/radio/radio-statuses-group.component.scss b/src/playground/with-layout/radio/radio-statuses-group.component.scss new file mode 100644 index 0000000000..01e581ba1f --- /dev/null +++ b/src/playground/with-layout/radio/radio-statuses-group.component.scss @@ -0,0 +1,13 @@ +:host { + display: flex; + flex-wrap: wrap; +} + +.gray { + background-color: #dde1eb; +} + +nb-radio-group { + border-radius: 0.5rem; + padding: 1rem; +} diff --git a/src/playground/with-layout/radio/radio-statuses.component.ts b/src/playground/with-layout/radio/radio-statuses.component.ts new file mode 100644 index 0000000000..9b131b0a51 --- /dev/null +++ b/src/playground/with-layout/radio/radio-statuses.component.ts @@ -0,0 +1,29 @@ +import { Component } from '@angular/core'; + +@Component({ + template: ` + + + {{ option.label }} + + + `, + styleUrls: ['./radio-statuses-group.component.scss'], +}) +export class RadioStatusesComponent { + options = [ + { value: 'This is value 1', label: 'Option 1' }, + { value: 'This is value 2', label: 'Option 2', disabled: true }, + { value: 'This is value 3', label: 'Option 3' }, + { value: 'This is value 4', label: 'Option 4', disabled: true }, + { value: 'This is value 5', label: 'Option 5' }, + ]; + + statuses = ['primary', 'success', 'warning', 'danger', 'info', 'white']; +} diff --git a/src/playground/with-layout/radio/radio.module.ts b/src/playground/with-layout/radio/radio.module.ts index b64b27c19b..ef0cde6c92 100644 --- a/src/playground/with-layout/radio/radio.module.ts +++ b/src/playground/with-layout/radio/radio.module.ts @@ -11,11 +11,16 @@ import { NbCardModule, NbRadioModule } from '@nebular/theme'; import { RadioRoutingModule } from './radio-routing.module'; import { RadioDisabledComponent } from './radio-disabled.component'; import { RadioShowcaseComponent } from './radio-showcase.component'; +import { RadioStatusesComponent } from './radio-statuses.component'; +import { RadioDisabledGroupComponent } from './radio-disabled-group.component'; @NgModule({ declarations: [ RadioDisabledComponent, RadioShowcaseComponent, + RadioDisabledComponent, + RadioStatusesComponent, + RadioDisabledGroupComponent, ], imports: [ CommonModule, From 79931f2ea023aa57eda29d97b02da6669c06f953 Mon Sep 17 00:00:00 2001 From: Sergey Andrievskiy Date: Sun, 24 Mar 2019 23:12:56 +0300 Subject: [PATCH 09/30] docs(radio): update theme properties list --- .../theme/components/radio/radio.component.ts | 77 +++++++++++++++---- 1 file changed, 61 insertions(+), 16 deletions(-) diff --git a/src/framework/theme/components/radio/radio.component.ts b/src/framework/theme/components/radio/radio.component.ts index 2b9748abe2..5f52570e98 100644 --- a/src/framework/theme/components/radio/radio.component.ts +++ b/src/framework/theme/components/radio/radio.component.ts @@ -48,22 +48,67 @@ import { NbRadioStatus } from './radio-status.component'; * * @styles * - * radio-bg - * radio-fg - * radio-size - * radio-border-size - * radio-border-color - * radio-checkmark - * radio-checked-bg - * radio-checked-size - * radio-checked-border-size - * radio-checked-border-color - * radio-checked-checkmark - * radio-disabled-bg - * radio-disabled-size - * radio-disabled-border-size - * radio-disabled-border-color - * radio-disabled-checkmark + * radio-width: + * radio-height: + * radio-border-width: + * radio-text-color: + * radio-text-font-family: + * radio-text-font-size: + * radio-text-font-weight: + * radio-text-line-height: + * radio-outline-color: + * radio-outline-width: + * radio-disabled-border-color: + * radio-disabled-text-color: + * radio-disabled-inner-circle-color: + * radio-primary-border-color: + * radio-primary-inner-circle-color: + * radio-primary-focus-border-color: + * radio-primary-focus-inner-circle-color: + * radio-primary-hover-border-color: + * radio-primary-hover-inner-circle-color: + * radio-primary-active-border-color: + * radio-primary-active-inner-circle-color: + * radio-success-border-color: + * radio-success-inner-circle-color: + * radio-success-focus-border-color: + * radio-success-focus-inner-circle-color: + * radio-success-hover-border-color: + * radio-success-hover-inner-circle-color: + * radio-success-active-border-color: + * radio-success-active-inner-circle-color: + * radio-warning-border-color: + * radio-warning-inner-circle-color: + * radio-warning-focus-border-color: + * radio-warning-focus-inner-circle-color: + * radio-warning-hover-border-color: + * radio-warning-hover-inner-circle-color: + * radio-warning-active-border-color: + * radio-warning-active-inner-circle-color: + * radio-danger-border-color: + * radio-danger-inner-circle-color: + * radio-danger-focus-border-color: + * radio-danger-focus-inner-circle-color: + * radio-danger-hover-border-color: + * radio-danger-hover-inner-circle-color: + * radio-danger-active-border-color: + * radio-danger-active-inner-circle-color: + * radio-info-border-color: + * radio-info-inner-circle-color: + * radio-info-focus-border-color: + * radio-info-focus-inner-circle-color: + * radio-info-hover-border-color: + * radio-info-hover-inner-circle-color: + * radio-info-active-border-color: + * radio-info-active-inner-circle-color: + * radio-white-border-color: + * radio-white-inner-circle-color: + * radio-white-focus-border-color: + * radio-white-focus-inner-circle-color: + * radio-white-hover-border-color: + * radio-white-hover-inner-circle-color: + * radio-white-active-border-color: + * radio-white-active-inner-circle-color: * */ @Component({ selector: 'nb-radio', From fc04ebf07800c24d694b1e9a6dbc9818d1a92374 Mon Sep 17 00:00:00 2001 From: Sergey Andrievskiy Date: Sun, 31 Mar 2019 11:40:19 +0300 Subject: [PATCH 10/30] fix(radio): proper typography settings --- src/framework/theme/styles/themes/_default.scss | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/framework/theme/styles/themes/_default.scss b/src/framework/theme/styles/themes/_default.scss index 91243d25ca..41eef17a53 100644 --- a/src/framework/theme/styles/themes/_default.scss +++ b/src/framework/theme/styles/themes/_default.scss @@ -962,11 +962,11 @@ $theme: ( radio-width: 1.125rem, radio-height: 1.125rem, radio-border-width: 0.0625rem, - radio-text-color: text-color, - radio-text-font-family: font-primary-family, - radio-text-font-size: font-primary-text-size, - radio-text-font-weight: font-primary-text-weight, - radio-text-line-height: font-primary-text-line-height, + radio-text-color: text-dark-color, + radio-text-font-family: text-subtitle-font-family, + radio-text-font-size: text-subtitle-font-size, + radio-text-font-weight: text-subtitle-font-weight, + radio-text-line-height: text-subtitle-line-height, radio-outline-color: outline-color, radio-outline-width: outline-width, From 0eb812f0cb6031d99f701594555b568f12a8175e Mon Sep 17 00:00:00 2001 From: Sergey Andrievskiy Date: Sun, 31 Mar 2019 12:02:47 +0300 Subject: [PATCH 11/30] refactor(radio): remove white status --- .../radio/_radio.component.theme.scss | 3 +- .../components/radio/radio-group.component.ts | 2 +- .../radio/radio-status.component.ts | 1 - .../theme/components/radio/radio.component.ts | 15 +--------- .../theme/styles/themes/_default.scss | 9 ------ .../radio/radio-statuses-group.component.scss | 5 ---- .../radio/radio-statuses.component.ts | 30 +++++++++++-------- 7 files changed, 20 insertions(+), 45 deletions(-) diff --git a/src/framework/theme/components/radio/_radio.component.theme.scss b/src/framework/theme/components/radio/_radio.component.theme.scss index 386ee40e90..a2bb0307fe 100644 --- a/src/framework/theme/components/radio/_radio.component.theme.scss +++ b/src/framework/theme/components/radio/_radio.component.theme.scss @@ -26,8 +26,7 @@ color: nb-theme(radio-disabled-text-color); } - $status-variants: 'primary', 'success', 'warning', 'danger', 'info', 'white'; - @each $status in $status-variants { + @each $status in nb-get-statuses() { .native-input:enabled + .radio-circle.status-#{$status} { border-color: nb-theme(radio-#{$status}-border-color); } diff --git a/src/framework/theme/components/radio/radio-group.component.ts b/src/framework/theme/components/radio/radio-group.component.ts index 62557c9ba9..16bb451338 100644 --- a/src/framework/theme/components/radio/radio-group.component.ts +++ b/src/framework/theme/components/radio/radio-group.component.ts @@ -103,7 +103,7 @@ export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, Contr /** * Radio buttons status. Primary by default. - * Possible values are 'primary', 'success', 'warning', 'danger', 'info', 'white'. + * Possible values are 'primary', 'success', 'warning', 'danger', 'info'. */ @Input('status') set setStatus(status: NbRadioStatus) { diff --git a/src/framework/theme/components/radio/radio-status.component.ts b/src/framework/theme/components/radio/radio-status.component.ts index 783f995509..2c30bcbe26 100644 --- a/src/framework/theme/components/radio/radio-status.component.ts +++ b/src/framework/theme/components/radio/radio-status.component.ts @@ -4,5 +4,4 @@ export enum NbRadioStatus { WARNING = 'warning', DANGER = 'danger', INFO = 'info', - WHITE = 'white', } diff --git a/src/framework/theme/components/radio/radio.component.ts b/src/framework/theme/components/radio/radio.component.ts index 5f52570e98..b3510bf742 100644 --- a/src/framework/theme/components/radio/radio.component.ts +++ b/src/framework/theme/components/radio/radio.component.ts @@ -101,14 +101,6 @@ import { NbRadioStatus } from './radio-status.component'; * radio-info-hover-inner-circle-color: * radio-info-active-border-color: * radio-info-active-inner-circle-color: - * radio-white-border-color: - * radio-white-inner-circle-color: - * radio-white-focus-border-color: - * radio-white-focus-inner-circle-color: - * radio-white-hover-border-color: - * radio-white-hover-inner-circle-color: - * radio-white-active-border-color: - * radio-white-active-inner-circle-color: * */ @Component({ selector: 'nb-radio', @@ -128,8 +120,7 @@ import { NbRadioStatus } from './radio-status.component'; [class.status-success]="isSuccess" [class.status-warning]="isWarning" [class.status-danger]="isDanger" - [class.status-info]="isInfo" - [class.status-white]="isWhite"> + [class.status-info]="isInfo"> @@ -182,10 +173,6 @@ export class NbRadioComponent { return this.status === NbRadioStatus.INFO; } - get isWhite(): boolean { - return this.status === NbRadioStatus.WHITE; - } - markForCheck() { this.cd.markForCheck(); this.cd.detectChanges(); diff --git a/src/framework/theme/styles/themes/_default.scss b/src/framework/theme/styles/themes/_default.scss index 41eef17a53..14ba22ad57 100644 --- a/src/framework/theme/styles/themes/_default.scss +++ b/src/framework/theme/styles/themes/_default.scss @@ -1019,15 +1019,6 @@ $theme: ( radio-info-active-border-color: color-info-600, radio-info-active-inner-circle-color: color-info-600, - radio-white-border-color: color-white, - radio-white-inner-circle-color: color-white, - radio-white-focus-border-color: color-primary-700, - radio-white-focus-inner-circle-color: color-primary-700, - radio-white-hover-border-color: color-primary-400, - radio-white-hover-inner-circle-color: color-primary-400, - radio-white-active-border-color: color-primary-600, - radio-white-active-inner-circle-color: color-primary-600, - tree-grid-cell-border-width: 1px, tree-grid-cell-border-style: solid, tree-grid-cell-border-color: separator, diff --git a/src/playground/with-layout/radio/radio-statuses-group.component.scss b/src/playground/with-layout/radio/radio-statuses-group.component.scss index 01e581ba1f..60622baa42 100644 --- a/src/playground/with-layout/radio/radio-statuses-group.component.scss +++ b/src/playground/with-layout/radio/radio-statuses-group.component.scss @@ -3,11 +3,6 @@ flex-wrap: wrap; } -.gray { - background-color: #dde1eb; -} - nb-radio-group { - border-radius: 0.5rem; padding: 1rem; } diff --git a/src/playground/with-layout/radio/radio-statuses.component.ts b/src/playground/with-layout/radio/radio-statuses.component.ts index 9b131b0a51..d2d028a1f3 100644 --- a/src/playground/with-layout/radio/radio-statuses.component.ts +++ b/src/playground/with-layout/radio/radio-statuses.component.ts @@ -1,15 +1,20 @@ import { Component } from '@angular/core'; +interface Option { + value: string; + label: string; + checked?: boolean; + disabled?: boolean; +} +type Options = Option[]; + @Component({ template: ` - - + + {{ option.label }} @@ -17,13 +22,12 @@ import { Component } from '@angular/core'; styleUrls: ['./radio-statuses-group.component.scss'], }) export class RadioStatusesComponent { - options = [ - { value: 'This is value 1', label: 'Option 1' }, - { value: 'This is value 2', label: 'Option 2', disabled: true }, + options: Options = [ + { value: 'This is value 1', label: 'Option 1', checked: true }, + { value: 'This is value 2', label: 'Option 2' }, { value: 'This is value 3', label: 'Option 3' }, { value: 'This is value 4', label: 'Option 4', disabled: true }, - { value: 'This is value 5', label: 'Option 5' }, ]; - statuses = ['primary', 'success', 'warning', 'danger', 'info', 'white']; + statuses = ['primary', 'success', 'warning', 'danger', 'info']; } From 89df6bd8b21155bafb2bf8c83d9ab8767a57530c Mon Sep 17 00:00:00 2001 From: Sergey Andrievskiy Date: Sun, 31 Mar 2019 12:09:36 +0300 Subject: [PATCH 12/30] feat(radio): add background color --- src/framework/theme/components/radio/_radio.component.theme.scss | 1 + src/framework/theme/styles/themes/_default.scss | 1 + 2 files changed, 2 insertions(+) diff --git a/src/framework/theme/components/radio/_radio.component.theme.scss b/src/framework/theme/components/radio/_radio.component.theme.scss index a2bb0307fe..14c109554a 100644 --- a/src/framework/theme/components/radio/_radio.component.theme.scss +++ b/src/framework/theme/components/radio/_radio.component.theme.scss @@ -9,6 +9,7 @@ .radio-circle { height: nb-theme(radio-height); width: nb-theme(radio-width); + background-color: nb-theme(radio-backround-color); border-width: nb-theme(radio-border-width); } diff --git a/src/framework/theme/styles/themes/_default.scss b/src/framework/theme/styles/themes/_default.scss index 14ba22ad57..4458634330 100644 --- a/src/framework/theme/styles/themes/_default.scss +++ b/src/framework/theme/styles/themes/_default.scss @@ -961,6 +961,7 @@ $theme: ( radio-width: 1.125rem, radio-height: 1.125rem, + radio-backround-color: transparent, radio-border-width: 0.0625rem, radio-text-color: text-dark-color, radio-text-font-family: text-subtitle-font-family, From 547681019c4b95b142665009bb9e514e54a25325 Mon Sep 17 00:00:00 2001 From: Sergey Andrievskiy Date: Sun, 31 Mar 2019 14:18:35 +0300 Subject: [PATCH 13/30] refactor(radio): remove named inputs BREAKING CHANGE: NbRadioGroupComponent setters 'setValue', 'setName', 'setDisabled', 'setStatus' were removed. Use 'value', 'name', 'disabled', 'status'. NbRadioComponent setter 'setDisabled' removed. Use 'disabled' instead. --- .../components/radio/radio-group.component.ts | 58 +++++++++++-------- .../theme/components/radio/radio.component.ts | 18 +++--- .../theme/components/radio/radio.spec.ts | 18 +++--- 3 files changed, 54 insertions(+), 40 deletions(-) diff --git a/src/framework/theme/components/radio/radio-group.component.ts b/src/framework/theme/components/radio/radio-group.component.ts index 16bb451338..e7e5aea5e4 100644 --- a/src/framework/theme/components/radio/radio-group.component.ts +++ b/src/framework/theme/components/radio/radio-group.component.ts @@ -81,46 +81,58 @@ import { NbRadioStatus } from './radio-status.component'; }) export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, ControlValueAccessor { - @ContentChildren(NbRadioComponent, { descendants: true }) radios: QueryList; + protected alive: boolean = true; + protected isTouched: boolean = false; + protected onChange = (value: any) => {}; + protected onTouched = () => {}; - @Input('value') - set setValue(value: any) { - this.value = value; + @Input() + get value(): any { + return this._value; + } + set value(value: any) { + this._value = value; this.updateValues(); } + protected _value: any; - @Input('name') - set setName(name: string) { - this.name = name; + @Input() + get name(): string { + return this._name; + } + set name(name: string) { + this._name = name; this.updateNames(); } + protected _name: string; - @Input('disabled') - set setDisabled(disabled: boolean) { - this.disabled = convertToBoolProperty(disabled); + @Input() + get disabled(): boolean { + return this._disabled; + } + set disabled(disabled: boolean) { + this._disabled = convertToBoolProperty(disabled); this.updateDisabled(); } + protected _disabled: boolean; /** * Radio buttons status. Primary by default. * Possible values are 'primary', 'success', 'warning', 'danger', 'info'. */ - @Input('status') - set setStatus(status: NbRadioStatus) { - this.status = status; + @Input() + get status(): NbRadioStatus { + return this._status; + } + set status(status: NbRadioStatus) { + this._status = status; this.updateStatus(); } + protected _status: NbRadioStatus = NbRadioStatus.PRIMARY; - @Output() valueChange: EventEmitter = new EventEmitter(); + @ContentChildren(NbRadioComponent, { descendants: true }) radios: QueryList; - protected disabled: boolean; - protected value: any; - protected name: string; - protected alive: boolean = true; - protected isTouched: boolean = false; - protected status: NbRadioStatus = NbRadioStatus.PRIMARY; - protected onChange = (value: any) => {}; - protected onTouched = () => {}; + @Output() valueChange: EventEmitter = new EventEmitter(); constructor( protected cd: ChangeDetectorRef, @@ -182,7 +194,7 @@ export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, Contr protected updateDisabled() { if (this.radios && typeof this.disabled !== 'undefined') { - this.radios.forEach((radio: NbRadioComponent) => radio.setDisabled = this.disabled); + this.radios.forEach((radio: NbRadioComponent) => radio.disabled = this.disabled); this.markRadiosForCheck(); } } diff --git a/src/framework/theme/components/radio/radio.component.ts b/src/framework/theme/components/radio/radio.component.ts index b3510bf742..29820a5f7e 100644 --- a/src/framework/theme/components/radio/radio.component.ts +++ b/src/framework/theme/components/radio/radio.component.ts @@ -35,9 +35,9 @@ import { NbRadioStatus } from './radio-status.component'; * * ```html * - * Option 1 - * Option 2 - * Option 3 + * Option 1 + * Option 2 + * Option 3 * * ``` * @@ -138,10 +138,14 @@ export class NbRadioComponent { @Input() value: any; - @Input('disabled') - set setDisabled(disabled: boolean) { - this.disabled = convertToBoolProperty(disabled); + @Input() + get disabled(): boolean { + return this._disabled; } + set disabled(disabled: boolean) { + this._disabled = convertToBoolProperty(disabled); + } + private _disabled: boolean; @Input() status: NbRadioStatus; @@ -149,8 +153,6 @@ export class NbRadioComponent { @Output() blur: EventEmitter = new EventEmitter(); - disabled: boolean; - constructor(protected cd: ChangeDetectorRef) {} get isPrimary(): boolean { diff --git a/src/framework/theme/components/radio/radio.spec.ts b/src/framework/theme/components/radio/radio.spec.ts index dc8a789b4c..ce9f8bf5a0 100644 --- a/src/framework/theme/components/radio/radio.spec.ts +++ b/src/framework/theme/components/radio/radio.spec.ts @@ -41,7 +41,7 @@ export class NbRadioTestComponent { @Component({ template: ` - + {{radio}} @@ -104,7 +104,7 @@ describe('NbRadioGroupComponent', () => { it('should update radio value when radios added after radio group initialization', () => { radioTestComponent.radioValues = [1, 2, 3]; radioTestComponent.showRadios = true; - radioTestComponent.radioGroupComponent.setValue = 1; + radioTestComponent.radioGroupComponent.value = 1; fixture.detectChanges(); expect(radioTestComponent.radioComponents.first.checked).toEqual(true); @@ -118,7 +118,7 @@ describe('NbRadioGroupComponent', () => { const groupName = 'my-radio-group-name'; radioTestComponent.radioValues = [1, 2, 3]; radioTestComponent.showRadios = true; - radioTestComponent.radioGroupComponent.setName = groupName; + radioTestComponent.radioGroupComponent.name = groupName; fixture.detectChanges(); for (const radio of radioTestComponent.radioComponents.toArray()) { @@ -129,7 +129,7 @@ describe('NbRadioGroupComponent', () => { it('should update radio disabled state when radios added after radio group initialization', () => { radioTestComponent.radioValues = [1, 2, 3]; radioTestComponent.showRadios = true; - radioTestComponent.radioGroupComponent.setDisabled = true; + radioTestComponent.radioGroupComponent.disabled = true; fixture.detectChanges(); for (const radio of radioTestComponent.radioComponents.toArray()) { @@ -140,7 +140,7 @@ describe('NbRadioGroupComponent', () => { it('should update radio status when radios added after radio group initialization', () => { radioTestComponent.radioValues = [1, 2, 3]; radioTestComponent.showRadios = true; - radioTestComponent.radioGroupComponent.setStatus = NbRadioStatus.INFO; + radioTestComponent.radioGroupComponent.status = NbRadioStatus.INFO; fixture.detectChanges(); for (const radio of radioTestComponent.radioComponents.toArray()) { @@ -166,7 +166,7 @@ describe('NbRadioGroupComponent', () => { it('should update radio value when radios change', () => { radioTestComponent.showRadios = true; - radioTestComponent.radioGroupComponent.setValue = 1; + radioTestComponent.radioGroupComponent.value = 1; fixture.detectChanges(); radioTestComponent.radioValues = [1, 2, 3]; @@ -182,7 +182,7 @@ describe('NbRadioGroupComponent', () => { it('should update radio name when radios change', () => { const groupName = 'my-radio-group-name'; radioTestComponent.showRadios = true; - radioTestComponent.radioGroupComponent.setName = groupName; + radioTestComponent.radioGroupComponent.name = groupName; fixture.detectChanges(); radioTestComponent.radioValues = [1, 2, 3]; @@ -195,7 +195,7 @@ describe('NbRadioGroupComponent', () => { it('should update radio disabled state when radios change', () => { radioTestComponent.showRadios = true; - radioTestComponent.radioGroupComponent.setDisabled = true; + radioTestComponent.radioGroupComponent.disabled = true; fixture.detectChanges(); radioTestComponent.radioValues = [1, 2, 3]; @@ -208,7 +208,7 @@ describe('NbRadioGroupComponent', () => { it('should update radio status when radios change', () => { radioTestComponent.showRadios = true; - radioTestComponent.radioGroupComponent.setStatus = NbRadioStatus.INFO; + radioTestComponent.radioGroupComponent.status = NbRadioStatus.INFO; fixture.detectChanges(); radioTestComponent.radioValues = [1, 2, 3]; From 7496c65fc0581d68e9e7589df6d927dc948e680d Mon Sep 17 00:00:00 2001 From: Sergey Andrievskiy Date: Sun, 31 Mar 2019 14:26:25 +0300 Subject: [PATCH 14/30] refactor(radio): remove own status enum and use shared --- .../components/radio/radio-group.component.ts | 8 ++++---- .../components/radio/radio-status.component.ts | 7 ------- .../theme/components/radio/radio.component.ts | 14 +++++++------- src/framework/theme/components/radio/radio.spec.ts | 9 ++++----- 4 files changed, 15 insertions(+), 23 deletions(-) delete mode 100644 src/framework/theme/components/radio/radio-status.component.ts diff --git a/src/framework/theme/components/radio/radio-group.component.ts b/src/framework/theme/components/radio/radio-group.component.ts index e7e5aea5e4..d5c7f5240a 100644 --- a/src/framework/theme/components/radio/radio-group.component.ts +++ b/src/framework/theme/components/radio/radio-group.component.ts @@ -27,7 +27,7 @@ import { filter, switchMap, take, takeUntil, takeWhile } from 'rxjs/operators'; import { convertToBoolProperty } from '../helpers'; import { NB_DOCUMENT } from '../../theme.options'; import { NbRadioComponent } from './radio.component'; -import { NbRadioStatus } from './radio-status.component'; +import { NbComponentStatus } from '../component-status'; /** * The `NbRadioGroupComponent` is the wrapper for `nb-radio` button. @@ -121,14 +121,14 @@ export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, Contr * Possible values are 'primary', 'success', 'warning', 'danger', 'info'. */ @Input() - get status(): NbRadioStatus { + get status(): NbComponentStatus { return this._status; } - set status(status: NbRadioStatus) { + set status(status: NbComponentStatus) { this._status = status; this.updateStatus(); } - protected _status: NbRadioStatus = NbRadioStatus.PRIMARY; + protected _status: NbComponentStatus = 'primary'; @ContentChildren(NbRadioComponent, { descendants: true }) radios: QueryList; diff --git a/src/framework/theme/components/radio/radio-status.component.ts b/src/framework/theme/components/radio/radio-status.component.ts deleted file mode 100644 index 2c30bcbe26..0000000000 --- a/src/framework/theme/components/radio/radio-status.component.ts +++ /dev/null @@ -1,7 +0,0 @@ -export enum NbRadioStatus { - PRIMARY = 'primary', - SUCCESS = 'success', - WARNING = 'warning', - DANGER = 'danger', - INFO = 'info', -} diff --git a/src/framework/theme/components/radio/radio.component.ts b/src/framework/theme/components/radio/radio.component.ts index 29820a5f7e..c64dd6de9d 100644 --- a/src/framework/theme/components/radio/radio.component.ts +++ b/src/framework/theme/components/radio/radio.component.ts @@ -7,7 +7,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core'; import { convertToBoolProperty } from '../helpers'; -import { NbRadioStatus } from './radio-status.component'; +import { NbComponentStatus } from '../component-status'; /** * The `NbRadioComponent` provides the same functionality as native `` @@ -147,7 +147,7 @@ export class NbRadioComponent { } private _disabled: boolean; - @Input() status: NbRadioStatus; + @Input() status: NbComponentStatus; @Output() valueChange: EventEmitter = new EventEmitter(); @@ -156,23 +156,23 @@ export class NbRadioComponent { constructor(protected cd: ChangeDetectorRef) {} get isPrimary(): boolean { - return this.status === NbRadioStatus.PRIMARY; + return this.status === 'primary'; } get isSuccess(): boolean { - return this.status === NbRadioStatus.SUCCESS; + return this.status === 'success'; } get isWarning(): boolean { - return this.status === NbRadioStatus.WARNING; + return this.status === 'warning'; } get isDanger(): boolean { - return this.status === NbRadioStatus.DANGER; + return this.status === 'danger'; } get isInfo(): boolean { - return this.status === NbRadioStatus.INFO; + return this.status === 'info'; } markForCheck() { diff --git a/src/framework/theme/components/radio/radio.spec.ts b/src/framework/theme/components/radio/radio.spec.ts index ce9f8bf5a0..529bb12261 100644 --- a/src/framework/theme/components/radio/radio.spec.ts +++ b/src/framework/theme/components/radio/radio.spec.ts @@ -22,7 +22,6 @@ import { NbRadioModule } from './radio.module'; import { NbRadioComponent } from './radio.component'; import { NbRadioGroupComponent } from './radio-group.component'; import { NB_DOCUMENT } from '../../theme.options'; -import { NbRadioStatus } from './radio-status.component'; @Component({ selector: 'nb-radio-test', @@ -140,11 +139,11 @@ describe('NbRadioGroupComponent', () => { it('should update radio status when radios added after radio group initialization', () => { radioTestComponent.radioValues = [1, 2, 3]; radioTestComponent.showRadios = true; - radioTestComponent.radioGroupComponent.status = NbRadioStatus.INFO; + radioTestComponent.radioGroupComponent.status = 'info'; fixture.detectChanges(); for (const radio of radioTestComponent.radioComponents.toArray()) { - expect(radio.status).toEqual(NbRadioStatus.INFO); + expect(radio.status).toEqual('info'); } }); @@ -208,14 +207,14 @@ describe('NbRadioGroupComponent', () => { it('should update radio status when radios change', () => { radioTestComponent.showRadios = true; - radioTestComponent.radioGroupComponent.status = NbRadioStatus.INFO; + radioTestComponent.radioGroupComponent.status = 'info'; fixture.detectChanges(); radioTestComponent.radioValues = [1, 2, 3]; fixture.detectChanges(); for (const radio of radioTestComponent.radioComponents.toArray()) { - expect(radio.status).toEqual(NbRadioStatus.INFO); + expect(radio.status).toEqual('info'); } }); From f72566726fd6ff1c023f68772845496b28ecdc1f Mon Sep 17 00:00:00 2001 From: Sergey Andrievskiy Date: Sun, 31 Mar 2019 14:28:04 +0300 Subject: [PATCH 15/30] fix(radio): typo --- .../theme/components/radio/_radio.component.theme.scss | 2 +- src/framework/theme/components/radio/radio.component.ts | 2 +- src/framework/theme/styles/themes/_default.scss | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/framework/theme/components/radio/_radio.component.theme.scss b/src/framework/theme/components/radio/_radio.component.theme.scss index 14c109554a..47914962a8 100644 --- a/src/framework/theme/components/radio/_radio.component.theme.scss +++ b/src/framework/theme/components/radio/_radio.component.theme.scss @@ -9,7 +9,7 @@ .radio-circle { height: nb-theme(radio-height); width: nb-theme(radio-width); - background-color: nb-theme(radio-backround-color); + background-color: nb-theme(radio-background-color); border-width: nb-theme(radio-border-width); } diff --git a/src/framework/theme/components/radio/radio.component.ts b/src/framework/theme/components/radio/radio.component.ts index c64dd6de9d..2f4baecf9c 100644 --- a/src/framework/theme/components/radio/radio.component.ts +++ b/src/framework/theme/components/radio/radio.component.ts @@ -50,6 +50,7 @@ import { NbComponentStatus } from '../component-status'; * * radio-width: * radio-height: + * radio-background-color: * radio-border-width: * radio-text-color: * radio-text-font-family: @@ -100,7 +101,6 @@ import { NbComponentStatus } from '../component-status'; * radio-info-hover-border-color: * radio-info-hover-inner-circle-color: * radio-info-active-border-color: - * radio-info-active-inner-circle-color: * */ @Component({ selector: 'nb-radio', diff --git a/src/framework/theme/styles/themes/_default.scss b/src/framework/theme/styles/themes/_default.scss index 4458634330..8078d62c01 100644 --- a/src/framework/theme/styles/themes/_default.scss +++ b/src/framework/theme/styles/themes/_default.scss @@ -961,7 +961,7 @@ $theme: ( radio-width: 1.125rem, radio-height: 1.125rem, - radio-backround-color: transparent, + radio-background-color: transparent, radio-border-width: 0.0625rem, radio-text-color: text-dark-color, radio-text-font-family: text-subtitle-font-family, From 1458ec02efef52e62255078761c3f94e72cfd75b Mon Sep 17 00:00:00 2001 From: Sergey Andrievskiy Date: Sun, 31 Mar 2019 14:31:17 +0300 Subject: [PATCH 16/30] docs(radio): fix examples --- .../theme/components/radio/radio-group.component.ts | 12 ++++++------ .../theme/components/radio/radio.component.ts | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/framework/theme/components/radio/radio-group.component.ts b/src/framework/theme/components/radio/radio-group.component.ts index d5c7f5240a..ec9b1cd047 100644 --- a/src/framework/theme/components/radio/radio-group.component.ts +++ b/src/framework/theme/components/radio/radio-group.component.ts @@ -35,9 +35,9 @@ import { NbComponentStatus } from '../component-status'; * * ```html * - * Option 1 - * Option 2 - * Option 3 + * Option 1 + * Option 2 + * Option 3 * * ``` * @@ -45,9 +45,9 @@ import { NbComponentStatus } from '../component-status'; * * ```html * - * Option 1 - * Option 2 - * Option 3 + * Option 1 + * Option 2 + * Option 3 * * ``` * diff --git a/src/framework/theme/components/radio/radio.component.ts b/src/framework/theme/components/radio/radio.component.ts index 2f4baecf9c..ee92835e91 100644 --- a/src/framework/theme/components/radio/radio.component.ts +++ b/src/framework/theme/components/radio/radio.component.ts @@ -35,9 +35,9 @@ import { NbComponentStatus } from '../component-status'; * * ```html * - * Option 1 - * Option 2 - * Option 3 + * Option 1 + * Option 2 + * Option 3 * * ``` * From 94d2f2b41fae717eb8c2212ce3b92e5725ab4fe0 Mon Sep 17 00:00:00 2001 From: Sergey Andrievskiy Date: Tue, 2 Apr 2019 22:27:26 +0300 Subject: [PATCH 17/30] feat(radio): add border style theme property --- src/framework/theme/components/radio/_radio.component.theme.scss | 1 + src/framework/theme/styles/themes/_default.scss | 1 + 2 files changed, 2 insertions(+) diff --git a/src/framework/theme/components/radio/_radio.component.theme.scss b/src/framework/theme/components/radio/_radio.component.theme.scss index 47914962a8..3290a97f0b 100644 --- a/src/framework/theme/components/radio/_radio.component.theme.scss +++ b/src/framework/theme/components/radio/_radio.component.theme.scss @@ -10,6 +10,7 @@ height: nb-theme(radio-height); width: nb-theme(radio-width); background-color: nb-theme(radio-background-color); + border-style: nb-theme(radio-border-style); border-width: nb-theme(radio-border-width); } diff --git a/src/framework/theme/styles/themes/_default.scss b/src/framework/theme/styles/themes/_default.scss index 8078d62c01..5791ef88f7 100644 --- a/src/framework/theme/styles/themes/_default.scss +++ b/src/framework/theme/styles/themes/_default.scss @@ -962,6 +962,7 @@ $theme: ( radio-width: 1.125rem, radio-height: 1.125rem, radio-background-color: transparent, + radio-border-style: solid, radio-border-width: 0.0625rem, radio-text-color: text-dark-color, radio-text-font-family: text-subtitle-font-family, From 218f14ea227144965460e100a84d7096d859f8f1 Mon Sep 17 00:00:00 2001 From: Sergey Andrievskiy Date: Tue, 2 Apr 2019 22:33:13 +0300 Subject: [PATCH 18/30] refactor(radio): use named state colors --- .../theme/styles/themes/_default.scss | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/framework/theme/styles/themes/_default.scss b/src/framework/theme/styles/themes/_default.scss index 5791ef88f7..953beddce9 100644 --- a/src/framework/theme/styles/themes/_default.scss +++ b/src/framework/theme/styles/themes/_default.scss @@ -978,48 +978,48 @@ $theme: ( radio-primary-border-color: color-primary, radio-primary-inner-circle-color: color-primary, - radio-primary-focus-border-color: color-primary-700, - radio-primary-focus-inner-circle-color: color-primary-700, - radio-primary-hover-border-color: color-primary-400, - radio-primary-hover-inner-circle-color: color-primary-400, - radio-primary-active-border-color: color-primary-600, - radio-primary-active-inner-circle-color: color-primary-600, + radio-primary-focus-border-color: color-primary-focus, + radio-primary-focus-inner-circle-color: color-primary-focus, + radio-primary-hover-border-color: color-primary-hover, + radio-primary-hover-inner-circle-color: color-primary-hover, + radio-primary-active-border-color: color-primary-active, + radio-primary-active-inner-circle-color: color-primary-active, radio-success-border-color: color-success, radio-success-inner-circle-color: color-success, - radio-success-focus-border-color: color-success-700, - radio-success-focus-inner-circle-color: color-success-700, - radio-success-hover-border-color: color-success-400, - radio-success-hover-inner-circle-color: color-success-400, - radio-success-active-border-color: color-success-600, - radio-success-active-inner-circle-color: color-success-600, + radio-success-focus-border-color: color-success-focus, + radio-success-focus-inner-circle-color: color-success-focus, + radio-success-hover-border-color: color-success-hover, + radio-success-hover-inner-circle-color: color-success-hover, + radio-success-active-border-color: color-success-active, + radio-success-active-inner-circle-color: color-success-active, radio-warning-border-color: color-warning, radio-warning-inner-circle-color: color-warning, - radio-warning-focus-border-color: color-warning-700, - radio-warning-focus-inner-circle-color: color-warning-700, - radio-warning-hover-border-color: color-warning-400, - radio-warning-hover-inner-circle-color: color-warning-400, - radio-warning-active-border-color: color-warning-600, - radio-warning-active-inner-circle-color: color-warning-600, + radio-warning-focus-border-color: color-warning-focus, + radio-warning-focus-inner-circle-color: color-warning-focus, + radio-warning-hover-border-color: color-warning-hover, + radio-warning-hover-inner-circle-color: color-warning-hover, + radio-warning-active-border-color: color-warning-active, + radio-warning-active-inner-circle-color: color-warning-active, radio-danger-border-color: color-danger, radio-danger-inner-circle-color: color-danger, - radio-danger-focus-border-color: color-danger-700, - radio-danger-focus-inner-circle-color: color-danger-700, - radio-danger-hover-border-color: color-danger-400, - radio-danger-hover-inner-circle-color: color-danger-400, - radio-danger-active-border-color: color-danger-600, - radio-danger-active-inner-circle-color: color-danger-600, + radio-danger-focus-border-color: color-danger-focus, + radio-danger-focus-inner-circle-color: color-danger-focus, + radio-danger-hover-border-color: color-danger-hover, + radio-danger-hover-inner-circle-color: color-danger-hover, + radio-danger-active-border-color: color-danger-active, + radio-danger-active-inner-circle-color: color-danger-active, radio-info-border-color: color-info, radio-info-inner-circle-color: color-info, - radio-info-focus-border-color: color-info-700, - radio-info-focus-inner-circle-color: color-info-700, - radio-info-hover-border-color: color-info-400, - radio-info-hover-inner-circle-color: color-info-400, - radio-info-active-border-color: color-info-600, - radio-info-active-inner-circle-color: color-info-600, + radio-info-focus-border-color: color-info-focus, + radio-info-focus-inner-circle-color: color-info-focus, + radio-info-hover-border-color: color-info-hover, + radio-info-hover-inner-circle-color: color-info-hover, + radio-info-active-border-color: color-info-active, + radio-info-active-inner-circle-color: color-info-active, tree-grid-cell-border-width: 1px, tree-grid-cell-border-style: solid, From 60e60c67107e20f72fff7c93de1c869d89920072 Mon Sep 17 00:00:00 2001 From: Sergey Andrievskiy Date: Tue, 2 Apr 2019 22:40:25 +0300 Subject: [PATCH 19/30] feat(radio): prevent setting empty status --- .../theme/components/radio/radio-group.component.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/framework/theme/components/radio/radio-group.component.ts b/src/framework/theme/components/radio/radio-group.component.ts index ec9b1cd047..36d976376b 100644 --- a/src/framework/theme/components/radio/radio-group.component.ts +++ b/src/framework/theme/components/radio/radio-group.component.ts @@ -117,18 +117,23 @@ export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, Contr protected _disabled: boolean; /** - * Radio buttons status. Primary by default. - * Possible values are 'primary', 'success', 'warning', 'danger', 'info'. + * Radio buttons status. + * Possible values are `primary` (default), `success`, `warning`, `danger`, `info`. */ @Input() get status(): NbComponentStatus { return this._status; } set status(status: NbComponentStatus) { - this._status = status; + if (status === '') { + this._status = this._defaultStatus; + } else { + this._status = status; + } this.updateStatus(); } - protected _status: NbComponentStatus = 'primary'; + protected readonly _defaultStatus: NbComponentStatus = 'primary'; + protected _status: NbComponentStatus = this._defaultStatus; @ContentChildren(NbRadioComponent, { descendants: true }) radios: QueryList; From c0abb372d1c95459597d0c70d01dc39a14b903ad Mon Sep 17 00:00:00 2001 From: Sergey Andrievskiy Date: Wed, 3 Apr 2019 10:13:27 +0300 Subject: [PATCH 20/30] docs(radio): update theme properties list --- src/framework/theme/components/radio/radio.component.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/framework/theme/components/radio/radio.component.ts b/src/framework/theme/components/radio/radio.component.ts index ee92835e91..b4ad934245 100644 --- a/src/framework/theme/components/radio/radio.component.ts +++ b/src/framework/theme/components/radio/radio.component.ts @@ -48,9 +48,10 @@ import { NbComponentStatus } from '../component-status'; * * @styles * - * radio-width: + * radio-width * radio-height: * radio-background-color: + * radio-border-style: * radio-border-width: * radio-text-color: * radio-text-font-family: @@ -101,6 +102,7 @@ import { NbComponentStatus } from '../component-status'; * radio-info-hover-border-color: * radio-info-hover-inner-circle-color: * radio-info-active-border-color: + * radio-info-active-inner-circle-color: * */ @Component({ selector: 'nb-radio', From d09570e07a9842d359a7128e834de1877e4ac399 Mon Sep 17 00:00:00 2001 From: Sergey Andrievskiy Date: Wed, 3 Apr 2019 12:13:24 +0300 Subject: [PATCH 21/30] refactor(radio): move status class to the host --- .../radio/_radio.component.theme.scss | 16 ++++++------- .../theme/components/radio/radio.component.ts | 23 ++++++++++++------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/src/framework/theme/components/radio/_radio.component.theme.scss b/src/framework/theme/components/radio/_radio.component.theme.scss index 3290a97f0b..26da5f118f 100644 --- a/src/framework/theme/components/radio/_radio.component.theme.scss +++ b/src/framework/theme/components/radio/_radio.component.theme.scss @@ -29,31 +29,31 @@ } @each $status in nb-get-statuses() { - .native-input:enabled + .radio-circle.status-#{$status} { + &.status-#{$status} .native-input:enabled + .radio-circle { border-color: nb-theme(radio-#{$status}-border-color); } - .native-input:enabled:checked + .radio-circle.status-#{$status}::before { + &.status-#{$status} .native-input:enabled:checked + .radio-circle::before { background-color: nb-theme(radio-#{$status}-inner-circle-color);; } - .native-input:enabled:focus + .radio-circle.status-#{$status} { + &.status-#{$status} .native-input:enabled:focus + .radio-circle { border-color: nb-theme(radio-#{$status}-focus-border-color); } - .native-input:enabled:checked:focus + .radio-circle.status-#{$status}::before { + &.status-#{$status} .native-input:enabled:checked:focus + .radio-circle::before { background-color: nb-theme(radio-#{$status}-focus-inner-circle-color); } - label:hover .native-input:enabled + .radio-circle.status-#{$status} { + &.status-#{$status} label:hover .native-input:enabled + .radio-circle { border-color: nb-theme(radio-#{$status}-hover-border-color); } - label:hover .native-input:checked:enabled + .radio-circle.status-#{$status}::before { + &.status-#{$status} label:hover .native-input:checked:enabled + .radio-circle::before { background-color: nb-theme(radio-#{$status}-hover-inner-circle-color); } - .native-input:enabled:active + .radio-circle.status-#{$status} { + &.status-#{$status} .native-input:enabled:active + .radio-circle { border-color: nb-theme(radio-#{$status}-active-border-color); } - .native-input:enabled:checked:active + .radio-circle.status-#{$status}::before { + &.status-#{$status} .native-input:enabled:checked:active + .radio-circle::before { background-color: nb-theme(radio-#{$status}-active-inner-circle-color); } } diff --git a/src/framework/theme/components/radio/radio.component.ts b/src/framework/theme/components/radio/radio.component.ts index b4ad934245..6e04f1fa51 100644 --- a/src/framework/theme/components/radio/radio.component.ts +++ b/src/framework/theme/components/radio/radio.component.ts @@ -4,7 +4,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. */ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core'; +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + EventEmitter, + HostBinding, + Input, + Output, +} from '@angular/core'; import { convertToBoolProperty } from '../helpers'; import { NbComponentStatus } from '../component-status'; @@ -117,13 +125,7 @@ import { NbComponentStatus } from '../component-status'; [disabled]="disabled" (change)="onChange($event)" (click)="onClick($event)"> - - + @@ -157,22 +159,27 @@ export class NbRadioComponent { constructor(protected cd: ChangeDetectorRef) {} + @HostBinding('class.status-primary') get isPrimary(): boolean { return this.status === 'primary'; } + @HostBinding('class.status-success') get isSuccess(): boolean { return this.status === 'success'; } + @HostBinding('class.status-warning') get isWarning(): boolean { return this.status === 'warning'; } + @HostBinding('class.status-danger') get isDanger(): boolean { return this.status === 'danger'; } + @HostBinding('class.status-info') get isInfo(): boolean { return this.status === 'info'; } From 42baf599ad1e0595f3ffa17cb6a2f7dbd0b9e17e Mon Sep 17 00:00:00 2001 From: Sergey Andrievskiy Date: Wed, 3 Apr 2019 12:26:28 +0300 Subject: [PATCH 22/30] feat(radio): set default status if value not passed --- .../components/radio/radio-group.component.ts | 2 +- .../theme/components/radio/radio.component.ts | 14 +++++++++++++- .../theme/components/radio/radio.spec.ts | 16 ++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/framework/theme/components/radio/radio-group.component.ts b/src/framework/theme/components/radio/radio-group.component.ts index 36d976376b..d788ce857e 100644 --- a/src/framework/theme/components/radio/radio-group.component.ts +++ b/src/framework/theme/components/radio/radio-group.component.ts @@ -125,7 +125,7 @@ export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, Contr return this._status; } set status(status: NbComponentStatus) { - if (status === '') { + if (!status) { this._status = this._defaultStatus; } else { this._status = status; diff --git a/src/framework/theme/components/radio/radio.component.ts b/src/framework/theme/components/radio/radio.component.ts index 6e04f1fa51..b6bdae9f39 100644 --- a/src/framework/theme/components/radio/radio.component.ts +++ b/src/framework/theme/components/radio/radio.component.ts @@ -151,7 +151,19 @@ export class NbRadioComponent { } private _disabled: boolean; - @Input() status: NbComponentStatus; + @Input() + get status(): NbComponentStatus { + return this._status; + } + set status(value: NbComponentStatus) { + if (!value) { + this._status = this._defaultStatus; + } else { + this._status = value; + } + } + private readonly _defaultStatus: NbComponentStatus = 'primary'; + private _status: NbComponentStatus = this._defaultStatus; @Output() valueChange: EventEmitter = new EventEmitter(); diff --git a/src/framework/theme/components/radio/radio.spec.ts b/src/framework/theme/components/radio/radio.spec.ts index 529bb12261..9b84a8a879 100644 --- a/src/framework/theme/components/radio/radio.spec.ts +++ b/src/framework/theme/components/radio/radio.spec.ts @@ -82,6 +82,14 @@ describe('radio', () => { const input = secondRadio.query(By.css('input')); input.nativeElement.click(); }); + + it(`should set default status if value isn't passed`, () => { + const radioFixture = TestBed.createComponent(NbRadioComponent); + radioFixture.detectChanges(); + + radioFixture.componentInstance.status = null; + expect(radioFixture.componentInstance.status).toEqual('primary'); + }); }); describe('NbRadioGroupComponent', () => { @@ -235,4 +243,12 @@ describe('NbRadioGroupComponent', () => { expect(valueChangeSpy).toHaveBeenCalledTimes(1); expect(valueChangeSpy).toHaveBeenCalledWith(radioValue); })); + + it(`should set default status if value isn't passed`, () => { + const radioFixture = TestBed.createComponent(NbRadioGroupComponent); + radioFixture.detectChanges(); + + radioFixture.componentInstance.status = null; + expect(radioFixture.componentInstance.status).toEqual('primary'); + }); }); From 09b5f3a455a50f1199f4c009284e4e1fba3b5d43 Mon Sep 17 00:00:00 2001 From: Sergey Andrievskiy Date: Wed, 3 Apr 2019 21:31:14 +0300 Subject: [PATCH 23/30] fix(radio): update radio status and trigger change detection Fixes 'ExpressionHasBeenChanged'. NbRadioComponent has host bindings which could change after view initialized since NbRadioGroupComponent updates radio status in AfterContentInit. --- src/framework/theme/components/radio/radio-group.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/theme/components/radio/radio-group.component.ts b/src/framework/theme/components/radio/radio-group.component.ts index d788ce857e..1a8d7b338b 100644 --- a/src/framework/theme/components/radio/radio-group.component.ts +++ b/src/framework/theme/components/radio/radio-group.component.ts @@ -147,7 +147,7 @@ export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, Contr ) {} ngAfterContentInit() { - this.updateAndSubscribeToRadios(); + Promise.resolve().then(() => this.updateAndSubscribeToRadios()); this.radios.changes .pipe(takeWhile(() => this.alive)) From 19e5fe7ad04ea74e98a5e7db53e71a0423c505fb Mon Sep 17 00:00:00 2001 From: Sergey Andrievskiy Date: Wed, 3 Apr 2019 21:32:50 +0300 Subject: [PATCH 24/30] refactor(radio): set provided status if it differ from current --- .../theme/components/radio/radio-group.component.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/framework/theme/components/radio/radio-group.component.ts b/src/framework/theme/components/radio/radio-group.component.ts index 1a8d7b338b..0dc2010b61 100644 --- a/src/framework/theme/components/radio/radio-group.component.ts +++ b/src/framework/theme/components/radio/radio-group.component.ts @@ -124,16 +124,13 @@ export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, Contr get status(): NbComponentStatus { return this._status; } - set status(status: NbComponentStatus) { - if (!status) { - this._status = this._defaultStatus; - } else { - this._status = status; + set status(value: NbComponentStatus) { + if (this._status !== value) { + this._status = value; + this.updateStatus(); } - this.updateStatus(); } - protected readonly _defaultStatus: NbComponentStatus = 'primary'; - protected _status: NbComponentStatus = this._defaultStatus; + protected _status: NbComponentStatus = 'primary'; @ContentChildren(NbRadioComponent, { descendants: true }) radios: QueryList; From b43cc88311eca44c448f0e9152f36816826b4105 Mon Sep 17 00:00:00 2001 From: Sergey Andrievskiy Date: Wed, 3 Apr 2019 21:33:46 +0300 Subject: [PATCH 25/30] refactor(radio): call change detector methods from radio component --- .../components/radio/radio-group.component.ts | 14 +---- .../theme/components/radio/radio.component.ts | 56 ++++++++++++++----- 2 files changed, 43 insertions(+), 27 deletions(-) diff --git a/src/framework/theme/components/radio/radio-group.component.ts b/src/framework/theme/components/radio/radio-group.component.ts index 0dc2010b61..46737f6c73 100644 --- a/src/framework/theme/components/radio/radio-group.component.ts +++ b/src/framework/theme/components/radio/radio-group.component.ts @@ -7,7 +7,6 @@ import { AfterContentInit, ChangeDetectionStrategy, - ChangeDetectorRef, Component, ContentChildren, EventEmitter, @@ -137,7 +136,6 @@ export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, Contr @Output() valueChange: EventEmitter = new EventEmitter(); constructor( - protected cd: ChangeDetectorRef, protected hostElement: ElementRef, @Inject(PLATFORM_ID) protected platformId, @Inject(NB_DOCUMENT) protected document, @@ -183,21 +181,18 @@ export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, Contr protected updateNames() { if (this.radios) { this.radios.forEach((radio: NbRadioComponent) => radio.name = this.name); - this.markRadiosForCheck(); } } protected updateValues() { if (this.radios && typeof this.value !== 'undefined') { this.radios.forEach((radio: NbRadioComponent) => radio.checked = radio.value === this.value); - this.markRadiosForCheck(); } } protected updateDisabled() { if (this.radios && typeof this.disabled !== 'undefined') { this.radios.forEach((radio: NbRadioComponent) => radio.disabled = this.disabled); - this.markRadiosForCheck(); } } @@ -218,10 +213,6 @@ export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, Contr this.onChange(value); } - protected markRadiosForCheck() { - this.radios.forEach((radio: NbRadioComponent) => radio.markForCheck()); - } - protected subscribeOnRadiosBlur() { if (!isPlatformBrowser(this.platformId) || this.isTouched) { return; @@ -250,10 +241,7 @@ export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, Contr protected updateStatus() { if (this.radios) { - this.radios.forEach((radio: NbRadioComponent) => { - radio.status = this.status; - radio.markForCheck(); - }); + this.radios.forEach((radio: NbRadioComponent) => radio.status = this.status); } } } diff --git a/src/framework/theme/components/radio/radio.component.ts b/src/framework/theme/components/radio/radio.component.ts index b6bdae9f39..ca201341e6 100644 --- a/src/framework/theme/components/radio/radio.component.ts +++ b/src/framework/theme/components/radio/radio.component.ts @@ -136,18 +136,53 @@ import { NbComponentStatus } from '../component-status'; }) export class NbRadioComponent { - @Input() name: string; + @Input() + get name(): string { + return this._name; + } + set name(value: string) { + if (this._name !== value) { + this._name = value; + this.cd.detectChanges(); + } + } + private _name: string; - @Input() checked: boolean; + @Input() + get checked(): boolean { + return this._checked; + } + set checked(value: boolean) { + const boolValue = convertToBoolProperty(value); + if (this._checked !== boolValue) { + this._checked = boolValue; + this.cd.markForCheck(); + } + } + private _checked: boolean; - @Input() value: any; + @Input() + get value(): any { + return this._value; + } + set value(value: any) { + if (this._value !== value) { + this._value = value; + this.cd.markForCheck(); + } + } + private _value: any; @Input() get disabled(): boolean { return this._disabled; } set disabled(disabled: boolean) { - this._disabled = convertToBoolProperty(disabled); + const boolValue = convertToBoolProperty(disabled); + if (this._disabled !== boolValue) { + this._disabled = boolValue; + this.cd.markForCheck(); + } } private _disabled: boolean; @@ -156,14 +191,12 @@ export class NbRadioComponent { return this._status; } set status(value: NbComponentStatus) { - if (!value) { - this._status = this._defaultStatus; - } else { + if (this._status !== value) { this._status = value; + this.cd.markForCheck(); } } - private readonly _defaultStatus: NbComponentStatus = 'primary'; - private _status: NbComponentStatus = this._defaultStatus; + private _status: NbComponentStatus = 'primary'; @Output() valueChange: EventEmitter = new EventEmitter(); @@ -196,11 +229,6 @@ export class NbRadioComponent { return this.status === 'info'; } - markForCheck() { - this.cd.markForCheck(); - this.cd.detectChanges(); - } - onChange(event: Event) { event.stopPropagation(); this.checked = true; From 32003be59354d792841222bb989016f207ffc89e Mon Sep 17 00:00:00 2001 From: Sergey Andrievskiy Date: Wed, 3 Apr 2019 21:35:15 +0300 Subject: [PATCH 26/30] fix(radio): set radio name right away --- .../components/radio/radio-group.component.ts | 6 ++++ .../theme/components/radio/radio.spec.ts | 33 ++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/framework/theme/components/radio/radio-group.component.ts b/src/framework/theme/components/radio/radio-group.component.ts index 46737f6c73..3accec0bdf 100644 --- a/src/framework/theme/components/radio/radio-group.component.ts +++ b/src/framework/theme/components/radio/radio-group.component.ts @@ -142,6 +142,12 @@ export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, Contr ) {} ngAfterContentInit() { + // In case option 'name' isn't set on nb-radio component, + // we need to set it's name right away, so it won't overlap with options + // without names from other radio groups. Otherwise they all would have + // same name and will be considered as options from one group so only the + // last option will stay selected. + this.updateNames(); Promise.resolve().then(() => this.updateAndSubscribeToRadios()); this.radios.changes diff --git a/src/framework/theme/components/radio/radio.spec.ts b/src/framework/theme/components/radio/radio.spec.ts index 9b84a8a879..3a8fd42aec 100644 --- a/src/framework/theme/components/radio/radio.spec.ts +++ b/src/framework/theme/components/radio/radio.spec.ts @@ -8,6 +8,7 @@ import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testin import { Component, DebugElement, + ElementRef, EventEmitter, Input, Output, @@ -55,6 +56,22 @@ export class NbRadioWithDynamicValuesTestComponent { @ViewChildren(NbRadioComponent) radioComponents: QueryList; } +@Component({ + template: ` + + + + + + + `, +}) +export class NbTwoRadioGroupsComponent { + @ViewChild('firstGroup', { read: NbRadioGroupComponent }) firstGroup: NbRadioGroupComponent; + @ViewChild('secondGroup', { read: NbRadioGroupComponent }) secondGroup: NbRadioGroupComponent; + @ViewChildren(NbRadioComponent, { read: ElementRef }) radios: QueryList; +} + describe('radio', () => { let fixture: ComponentFixture; let comp: NbRadioTestComponent; @@ -99,7 +116,7 @@ describe('NbRadioGroupComponent', () => { beforeEach(() => { TestBed.configureTestingModule({ imports: [ NbRadioModule ], - declarations: [ NbRadioWithDynamicValuesTestComponent ], + declarations: [ NbRadioWithDynamicValuesTestComponent, NbTwoRadioGroupsComponent ], providers: [ { provide: NB_DOCUMENT, useValue: document } ], }); @@ -251,4 +268,18 @@ describe('NbRadioGroupComponent', () => { radioFixture.componentInstance.status = null; expect(radioFixture.componentInstance.status).toEqual('primary'); }); + + it(`should set options name right away so it won't overlap with options from another groups`, () => { + const radioFixture = TestBed.createComponent(NbTwoRadioGroupsComponent); + radioFixture.detectChanges(); + + const { firstGroup, secondGroup, radios } = radioFixture.componentInstance; + const radioFromFirstGroup = radios.first.nativeElement.querySelector('input'); + const radioFromSecondGroup = radios.last.nativeElement.querySelector('input'); + + expect(firstGroup.radios.first.checked).toEqual(true); + expect(radioFromFirstGroup.checked).toEqual(true); + expect(secondGroup.radios.first.checked).toEqual(true); + expect(radioFromSecondGroup.checked).toEqual(true); + }); }); From 32753546fd2c14f857241c55272eadf4747351c6 Mon Sep 17 00:00:00 2001 From: Sergey Andrievskiy Date: Thu, 4 Apr 2019 15:33:20 +0300 Subject: [PATCH 27/30] fix(radio): prevent subscription creating if no options --- .../theme/components/radio/radio-group.component.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/framework/theme/components/radio/radio-group.component.ts b/src/framework/theme/components/radio/radio-group.component.ts index 3accec0bdf..02ae3da679 100644 --- a/src/framework/theme/components/radio/radio-group.component.ts +++ b/src/framework/theme/components/radio/radio-group.component.ts @@ -148,6 +148,7 @@ export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, Contr // same name and will be considered as options from one group so only the // last option will stay selected. this.updateNames(); + Promise.resolve().then(() => this.updateAndSubscribeToRadios()); this.radios.changes @@ -203,6 +204,10 @@ export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, Contr } protected subscribeOnRadiosValueChange() { + if (!this.radios || !this.radios.length) { + return; + } + merge(...this.radios.map((radio: NbRadioComponent) => radio.valueChange)) .pipe( takeWhile(() => this.alive), @@ -220,7 +225,8 @@ export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, Contr } protected subscribeOnRadiosBlur() { - if (!isPlatformBrowser(this.platformId) || this.isTouched) { + const hasNoRadios = !this.radios || !this.radios.length; + if (!isPlatformBrowser(this.platformId) || this.isTouched || hasNoRadios) { return; } From 5c2b1d725eb53b33dc881920f08148a5dd0cbdca Mon Sep 17 00:00:00 2001 From: Sergey Andrievskiy Date: Thu, 4 Apr 2019 15:34:39 +0300 Subject: [PATCH 28/30] feat(radio): initialize radio properties Prevents unnecessary mark for check when option property changed from 'undefined' to 'false'. --- src/framework/theme/components/radio/radio.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/framework/theme/components/radio/radio.component.ts b/src/framework/theme/components/radio/radio.component.ts index ca201341e6..ae644b5e76 100644 --- a/src/framework/theme/components/radio/radio.component.ts +++ b/src/framework/theme/components/radio/radio.component.ts @@ -159,7 +159,7 @@ export class NbRadioComponent { this.cd.markForCheck(); } } - private _checked: boolean; + private _checked: boolean = false; @Input() get value(): any { @@ -184,7 +184,7 @@ export class NbRadioComponent { this.cd.markForCheck(); } } - private _disabled: boolean; + private _disabled: boolean = false; @Input() get status(): NbComponentStatus { From bae04b913b733641f8e69b8475a18460d58d743e Mon Sep 17 00:00:00 2001 From: Sergey Andrievskiy Date: Thu, 4 Apr 2019 15:36:58 +0300 Subject: [PATCH 29/30] fix(radio): wait change detection to finish before updating options --- .../components/radio/radio-group.component.ts | 8 +- .../theme/components/radio/radio.spec.ts | 111 ++++++++++-------- 2 files changed, 67 insertions(+), 52 deletions(-) diff --git a/src/framework/theme/components/radio/radio-group.component.ts b/src/framework/theme/components/radio/radio-group.component.ts index 02ae3da679..69be77aec1 100644 --- a/src/framework/theme/components/radio/radio-group.component.ts +++ b/src/framework/theme/components/radio/radio-group.component.ts @@ -153,7 +153,13 @@ export class NbRadioGroupComponent implements AfterContentInit, OnDestroy, Contr this.radios.changes .pipe(takeWhile(() => this.alive)) - .subscribe(() => this.updateAndSubscribeToRadios()); + .subscribe(() => { + // 'changes' emit during change detection run and we can't update + // option properties right of since they already was initialized. + // Instead we schedule microtask to update radios after change detection + // run is finished. + Promise.resolve().then(() => this.updateAndSubscribeToRadios()); + }); } ngOnDestroy() { diff --git a/src/framework/theme/components/radio/radio.spec.ts b/src/framework/theme/components/radio/radio.spec.ts index 3a8fd42aec..65e629a423 100644 --- a/src/framework/theme/components/radio/radio.spec.ts +++ b/src/framework/theme/components/radio/radio.spec.ts @@ -4,7 +4,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. */ -import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; +import { ComponentFixture, fakeAsync, flush, TestBed, tick } from '@angular/core/testing'; import { Component, DebugElement, @@ -76,7 +76,7 @@ describe('radio', () => { let fixture: ComponentFixture; let comp: NbRadioTestComponent; - beforeEach(() => { + beforeEach(fakeAsync(() => { TestBed.configureTestingModule({ imports: [NbRadioModule], declarations: [NbRadioTestComponent], @@ -86,7 +86,9 @@ describe('radio', () => { fixture = TestBed.createComponent(NbRadioTestComponent); comp = fixture.componentInstance; fixture.detectChanges(); - }); + flush(); + fixture.detectChanges(); + })); it('should render radios', () => { const radios: DebugElement[] = fixture.debugElement.queryAll(By.directive(NbRadioComponent)); @@ -99,21 +101,13 @@ describe('radio', () => { const input = secondRadio.query(By.css('input')); input.nativeElement.click(); }); - - it(`should set default status if value isn't passed`, () => { - const radioFixture = TestBed.createComponent(NbRadioComponent); - radioFixture.detectChanges(); - - radioFixture.componentInstance.status = null; - expect(radioFixture.componentInstance.status).toEqual('primary'); - }); }); describe('NbRadioGroupComponent', () => { let fixture: ComponentFixture; let radioTestComponent: NbRadioWithDynamicValuesTestComponent; - beforeEach(() => { + beforeEach(fakeAsync(() => { TestBed.configureTestingModule({ imports: [ NbRadioModule ], declarations: [ NbRadioWithDynamicValuesTestComponent, NbTwoRadioGroupsComponent ], @@ -123,60 +117,72 @@ describe('NbRadioGroupComponent', () => { fixture = TestBed.createComponent(NbRadioWithDynamicValuesTestComponent); radioTestComponent = fixture.componentInstance; fixture.detectChanges(); - }); + flush(); // promise with 'updateAndSubscribeToRadios' + fixture.detectChanges(); // detect changes made during 'updateAndSubscribeToRadios' + })); - it('should update radio value when radios added after radio group initialization', () => { + it('should update radio value when radios added after radio group initialization', fakeAsync(() => { radioTestComponent.radioValues = [1, 2, 3]; radioTestComponent.showRadios = true; radioTestComponent.radioGroupComponent.value = 1; - fixture.detectChanges(); + fixture.detectChanges(); // adds radios + flush(); // promise with 'updateAndSubscribeToRadios' in NbRadioGroup.radios.changes + fixture.detectChanges(); // detect changes made during 'updateAndSubscribeToRadios' expect(radioTestComponent.radioComponents.first.checked).toEqual(true); const otherRadios = radioTestComponent.radioComponents.toArray().slice(1); for (const radio of otherRadios) { - expect(radio.checked).toBeFalsy(); + expect(radio.checked).toEqual(false); } - }); + })); - it('should update radio name when radios added after radio group initialization', () => { + it('should update radio name when radios added after radio group initialization', fakeAsync(() => { const groupName = 'my-radio-group-name'; radioTestComponent.radioValues = [1, 2, 3]; radioTestComponent.showRadios = true; radioTestComponent.radioGroupComponent.name = groupName; - fixture.detectChanges(); + fixture.detectChanges(); // adds radios + flush(); // promise with 'updateAndSubscribeToRadios' in NbRadioGroup.radios.changes + fixture.detectChanges(); // detect changes made during 'updateAndSubscribeToRadios' for (const radio of radioTestComponent.radioComponents.toArray()) { expect(radio.name).toEqual(groupName); } - }); + })); - it('should update radio disabled state when radios added after radio group initialization', () => { + it('should update radio disabled state when radios added after radio group initialization', fakeAsync(() => { radioTestComponent.radioValues = [1, 2, 3]; radioTestComponent.showRadios = true; radioTestComponent.radioGroupComponent.disabled = true; - fixture.detectChanges(); + fixture.detectChanges(); // adds radios + flush(); // promise with 'updateAndSubscribeToRadios' in NbRadioGroup.radios.changes + fixture.detectChanges(); // detect changes made during 'updateAndSubscribeToRadios' for (const radio of radioTestComponent.radioComponents.toArray()) { expect(radio.disabled).toEqual(true); } - }); + })); - it('should update radio status when radios added after radio group initialization', () => { + it('should update radio status when radios added after radio group initialization', fakeAsync(() => { radioTestComponent.radioValues = [1, 2, 3]; radioTestComponent.showRadios = true; radioTestComponent.radioGroupComponent.status = 'info'; - fixture.detectChanges(); + fixture.detectChanges(); // adds radios + flush(); // promise with 'updateAndSubscribeToRadios' in NbRadioGroup.radios.changes + fixture.detectChanges(); // detect changes made during 'updateAndSubscribeToRadios' for (const radio of radioTestComponent.radioComponents.toArray()) { expect(radio.status).toEqual('info'); } - }); + })); it('should update subscription to radio change when radios added after radio group initialization', fakeAsync(() => { const radioValue = 333; radioTestComponent.radioValues = [radioValue]; radioTestComponent.showRadios = true; - fixture.detectChanges(); + fixture.detectChanges(); // adds radios + flush(); // promise with 'updateAndSubscribeToRadios' in NbRadioGroup.radios.changes + fixture.detectChanges(); // detect changes made during 'updateAndSubscribeToRadios' const valueChangeSpy = createSpy('valueChange'); radioTestComponent.radioGroupComponent.valueChange.subscribe(valueChangeSpy); @@ -188,71 +194,82 @@ describe('NbRadioGroupComponent', () => { expect(valueChangeSpy).toHaveBeenCalledWith(radioValue); })); - it('should update radio value when radios change', () => { + it('should update radio value when radios change', fakeAsync(() => { radioTestComponent.showRadios = true; radioTestComponent.radioGroupComponent.value = 1; fixture.detectChanges(); radioTestComponent.radioValues = [1, 2, 3]; - fixture.detectChanges(); + fixture.detectChanges(); // adds radios + flush(); // promise with 'updateAndSubscribeToRadios' in NbRadioGroup.radios.changes + fixture.detectChanges(); // detect changes made during 'updateAndSubscribeToRadios' expect(radioTestComponent.radioComponents.first.checked).toEqual(true); const otherRadios = radioTestComponent.radioComponents.toArray().slice(1); for (const radio of otherRadios) { expect(radio.checked).toBeFalsy(); } - }); + })); - it('should update radio name when radios change', () => { + it('should update radio name when radios change', fakeAsync(() => { const groupName = 'my-radio-group-name'; radioTestComponent.showRadios = true; radioTestComponent.radioGroupComponent.name = groupName; fixture.detectChanges(); radioTestComponent.radioValues = [1, 2, 3]; - fixture.detectChanges(); + fixture.detectChanges(); // adds radios + flush(); // promise with 'updateAndSubscribeToRadios' in NbRadioGroup.radios.changes + fixture.detectChanges(); // detect changes made during 'updateAndSubscribeToRadios' for (const radio of radioTestComponent.radioComponents.toArray()) { expect(radio.name).toEqual(groupName); } - }); + })); - it('should update radio disabled state when radios change', () => { + it('should update radio disabled state when radios change', fakeAsync(() => { radioTestComponent.showRadios = true; radioTestComponent.radioGroupComponent.disabled = true; fixture.detectChanges(); radioTestComponent.radioValues = [1, 2, 3]; - fixture.detectChanges(); + fixture.detectChanges(); // adds radios + flush(); // promise with 'updateAndSubscribeToRadios' in NbRadioGroup.radios.changes + fixture.detectChanges(); // detect changes made during 'updateAndSubscribeToRadios' for (const radio of radioTestComponent.radioComponents.toArray()) { expect(radio.disabled).toEqual(true); } - }); + })); - it('should update radio status when radios change', () => { + it('should update radio status when radios change', fakeAsync(() => { radioTestComponent.showRadios = true; radioTestComponent.radioGroupComponent.status = 'info'; fixture.detectChanges(); radioTestComponent.radioValues = [1, 2, 3]; - fixture.detectChanges(); + fixture.detectChanges(); // adds radios + flush(); // promise with 'updateAndSubscribeToRadios' in NbRadioGroup.radios.changes + fixture.detectChanges(); // detect changes made during 'updateAndSubscribeToRadios' for (const radio of radioTestComponent.radioComponents.toArray()) { expect(radio.status).toEqual('info'); } - }); + })); it('should update subscription to radio change when radios change', fakeAsync(() => { - radioTestComponent.showRadios = true; - fixture.detectChanges(); - const valueChangeSpy = createSpy('valueChange'); radioTestComponent.radioGroupComponent.valueChange.subscribe(valueChangeSpy); + radioTestComponent.showRadios = true; + fixture.detectChanges(); + flush(); + fixture.detectChanges(); const radioValue = 333; radioTestComponent.radioValues = [radioValue]; - fixture.detectChanges(); + fixture.detectChanges(); // adds radios + flush(); // promise with 'updateAndSubscribeToRadios' in NbRadioGroup.radios.changes + fixture.detectChanges(); // detect changes made during 'updateAndSubscribeToRadios' radioTestComponent.radioComponents.first.valueChange.emit(radioValue); tick(); @@ -261,14 +278,6 @@ describe('NbRadioGroupComponent', () => { expect(valueChangeSpy).toHaveBeenCalledWith(radioValue); })); - it(`should set default status if value isn't passed`, () => { - const radioFixture = TestBed.createComponent(NbRadioGroupComponent); - radioFixture.detectChanges(); - - radioFixture.componentInstance.status = null; - expect(radioFixture.componentInstance.status).toEqual('primary'); - }); - it(`should set options name right away so it won't overlap with options from another groups`, () => { const radioFixture = TestBed.createComponent(NbTwoRadioGroupsComponent); radioFixture.detectChanges(); From 85335606834eeba6d1625fa5f39e446055a3c49b Mon Sep 17 00:00:00 2001 From: Sergey Andrievskiy Date: Thu, 4 Apr 2019 17:20:43 +0300 Subject: [PATCH 30/30] docs(radio): remove unnecessary interface --- .../with-layout/radio/radio-statuses.component.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/playground/with-layout/radio/radio-statuses.component.ts b/src/playground/with-layout/radio/radio-statuses.component.ts index d2d028a1f3..fbdfe2697c 100644 --- a/src/playground/with-layout/radio/radio-statuses.component.ts +++ b/src/playground/with-layout/radio/radio-statuses.component.ts @@ -1,13 +1,5 @@ import { Component } from '@angular/core'; -interface Option { - value: string; - label: string; - checked?: boolean; - disabled?: boolean; -} -type Options = Option[]; - @Component({ template: ` @@ -22,7 +14,7 @@ type Options = Option[]; styleUrls: ['./radio-statuses-group.component.scss'], }) export class RadioStatusesComponent { - options: Options = [ + options = [ { value: 'This is value 1', label: 'Option 1', checked: true }, { value: 'This is value 2', label: 'Option 2' }, { value: 'This is value 3', label: 'Option 3' },