From 89458ff661e3d3963543658b894fa0a9d36ba01c Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Sun, 11 Jun 2017 12:50:26 +0200 Subject: [PATCH] feat(radio): support for color binding * Introduces the `color` binding for radio buttons. By default every selection control uses accent as per Material design specifications. Closes #4677 --- src/demo-app/radio/radio-demo.html | 9 +++++++ src/lib/radio/_radio-theme.scss | 39 +++++++++++++++++++++--------- src/lib/radio/radio.spec.ts | 38 ++++++++++++++++++++++++++--- src/lib/radio/radio.ts | 20 ++++++++++++--- 4 files changed, 86 insertions(+), 20 deletions(-) diff --git a/src/demo-app/radio/radio-demo.html b/src/demo-app/radio/radio-demo.html index 341c6735013c..6d0b02e458d3 100644 --- a/src/demo-app/radio/radio-demo.html +++ b/src/demo-app/radio/radio-demo.html @@ -4,6 +4,15 @@

Basic Example

Option 2 Option 3 (disabled) + +

Color Example

+
+ Default (accent) + Primary + Accent + Warn +
+

Dynamic Example

diff --git a/src/lib/radio/_radio-theme.scss b/src/lib/radio/_radio-theme.scss index 5bedd3658dc3..910db6386e3f 100644 --- a/src/lib/radio/_radio-theme.scss +++ b/src/lib/radio/_radio-theme.scss @@ -2,6 +2,19 @@ @import '../core/theming/theming'; @import '../core/typography/typography-utils'; +@mixin mat-radio-color($palette) { + &.mat-radio-checked .mat-radio-outer-circle { + border-color: mat-color($palette); + } + + .mat-radio-inner-circle { + background-color: mat-color($palette); + } + + .mat-radio-ripple .mat-ripple-element { + background-color: mat-color($palette, 0.26); + } +} @mixin mat-radio-theme($theme) { $primary: map-get($theme, primary); @@ -14,22 +27,10 @@ border-color: mat-color($foreground, secondary-text); } - .mat-radio-checked .mat-radio-outer-circle { - border-color: mat-color($accent); - } - .mat-radio-disabled .mat-radio-outer-circle { border-color: mat-color($foreground, disabled); } - .mat-radio-inner-circle { - background-color: mat-color($accent); - } - - .mat-radio-ripple .mat-ripple-element { - background-color: mat-color($accent, 0.26); - } - .mat-radio-disabled { .mat-radio-ripple .mat-ripple-element, .mat-radio-inner-circle { background-color: mat-color($foreground, disabled); @@ -39,6 +40,20 @@ color: mat-color($foreground, disabled); } } + + .mat-radio-button { + &.mat-primary { + @include mat-radio-color($primary); + } + + &.mat-accent { + @include mat-radio-color($accent); + } + + &.mat-warn { + @include mat-radio-color($warn); + } + } } @mixin mat-radio-typography($config) { diff --git a/src/lib/radio/radio.spec.ts b/src/lib/radio/radio.spec.ts index 35101d0e1a31..a633a0c4b20b 100644 --- a/src/lib/radio/radio.spec.ts +++ b/src/lib/radio/radio.spec.ts @@ -332,6 +332,29 @@ describe('MdRadio', () => { expect(radioInstances[1].checked).toBeFalsy('should not select the second button'); expect(radioInstances[2].checked).toBeFalsy('should not select the third button'); }); + + it('should apply class based on color attribute', () => { + expect(radioNativeElements.every(radioEl => radioEl.classList.contains('mat-accent'))) + .toBe(true, 'Expected every radio element to use the accent color by default.'); + + testComponent.color = 'primary'; + fixture.detectChanges(); + + expect(radioNativeElements.every(radioEl => radioEl.classList.contains('mat-primary'))) + .toBe(true, 'Expected every radio element to use the primary color from the binding.'); + + testComponent.color = 'warn'; + fixture.detectChanges(); + + expect(radioNativeElements.every(radioEl => radioEl.classList.contains('mat-warn'))) + .toBe(true, 'Expected every radio element to use the primary color from the binding.'); + + testComponent.color = null; + fixture.detectChanges(); + + expect(radioNativeElements.every(radioEl => radioEl.classList.contains('mat-accent'))) + .toBe(true, 'Expected every radio element to fallback to accent color if value is falsy.'); + }); }); describe('group with ngModel', () => { @@ -595,10 +618,16 @@ describe('MdRadio', () => { [labelPosition]="labelPos" [value]="groupValue" name="test-name"> - Charmander - Squirtle - Bulbasaur + + Charmander + + + Squirtle + + + Bulbasaur + ` }) @@ -608,6 +637,7 @@ class RadiosInsideRadioGroup { isFirstDisabled: boolean = false; groupValue: string = null; disableRipple: boolean = false; + color: string; } diff --git a/src/lib/radio/radio.ts b/src/lib/radio/radio.ts index 191be67bb80b..ff8cdde921c9 100644 --- a/src/lib/radio/radio.ts +++ b/src/lib/radio/radio.ts @@ -29,6 +29,7 @@ import { } from '../core'; import {coerceBooleanProperty} from '../core/coercion/boolean-property'; import {mixinDisabled, CanDisable} from '../core/common-behaviors/disabled'; +import {CanColor, mixinColor} from '../core/common-behaviors/color'; /** @@ -287,6 +288,14 @@ export class MdRadioGroup extends _MdRadioGroupMixinBase } } +// Boilerplate for applying mixins to MdRadioButton. +export class MdRadioButtonBase { + constructor(public _renderer: Renderer2, public _elementRef: ElementRef) {} +} +// As per Material design specifications the selection control radio should use the accent color +// palette by default. https://material.io/guidelines/components/selection-controls.html +export const _MdRadioButtonMixinBase = mixinColor(MdRadioButtonBase, 'accent'); + /** * A radio-button. May be inside of */ @@ -295,6 +304,7 @@ export class MdRadioGroup extends _MdRadioGroupMixinBase selector: 'md-radio-button, mat-radio-button', templateUrl: 'radio.html', styleUrls: ['radio.css'], + inputs: ['color'], encapsulation: ViewEncapsulation.None, host: { '[class.mat-radio-button]': 'true', @@ -304,7 +314,8 @@ export class MdRadioGroup extends _MdRadioGroupMixinBase }, changeDetection: ChangeDetectionStrategy.OnPush, }) -export class MdRadioButton implements OnInit, AfterViewInit, OnDestroy { +export class MdRadioButton extends _MdRadioButtonMixinBase + implements OnInit, AfterViewInit, OnDestroy, CanColor { /** The unique ID for the radio button. */ @Input() id: string = `md-radio-${_uniqueIdCounter++}`; @@ -443,14 +454,15 @@ export class MdRadioButton implements OnInit, AfterViewInit, OnDestroy { @ViewChild('input') _inputElement: ElementRef; constructor(@Optional() radioGroup: MdRadioGroup, - private _elementRef: ElementRef, - private _renderer: Renderer2, + elementRef: ElementRef, + renderer: Renderer2, private _changeDetector: ChangeDetectorRef, private _focusOriginMonitor: FocusOriginMonitor, private _radioDispatcher: UniqueSelectionDispatcher) { + super(renderer, elementRef); + // Assertions. Ideally these should be stripped out by the compiler. // TODO(jelbourn): Assert that there's no name binding AND a parent radio group. - this.radioGroup = radioGroup; _radioDispatcher.listen((id: string, name: string) => {