Skip to content

Commit

Permalink
feat(radio): support for color binding
Browse files Browse the repository at this point in the history
* Introduces the `color` binding for radio buttons. By default every selection control uses accent as per Material design specifications.

Closes angular#4677
  • Loading branch information
devversion committed Jun 11, 2017
1 parent c2816ef commit 89458ff
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 20 deletions.
9 changes: 9 additions & 0 deletions src/demo-app/radio/radio-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ <h1>Basic Example</h1>
<md-radio-button name="group1">Option 2</md-radio-button>
<md-radio-button name="group1" disabled="true">Option 3 (disabled)</md-radio-button>
</section>

<h1>Color Example</h1>
<section class="demo-section">
<md-radio-button name="group2">Default (accent)</md-radio-button>
<md-radio-button name="group2" color="primary">Primary</md-radio-button>
<md-radio-button name="group2" color="accent">Accent</md-radio-button>
<md-radio-button name="group2" color="warn">Warn</md-radio-button>
</section>

<h1>Dynamic Example</h1>
<section class="demo-section">
<div>
Expand Down
39 changes: 27 additions & 12 deletions src/lib/radio/_radio-theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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) {
Expand Down
38 changes: 34 additions & 4 deletions src/lib/radio/radio.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down Expand Up @@ -595,10 +618,16 @@ describe('MdRadio', () => {
[labelPosition]="labelPos"
[value]="groupValue"
name="test-name">
<md-radio-button value="fire" [disableRipple]="disableRipple"
[disabled]="isFirstDisabled">Charmander</md-radio-button>
<md-radio-button value="water" [disableRipple]="disableRipple">Squirtle</md-radio-button>
<md-radio-button value="leaf" [disableRipple]="disableRipple">Bulbasaur</md-radio-button>
<md-radio-button value="fire" [disableRipple]="disableRipple" [disabled]="isFirstDisabled"
[color]="color">
Charmander
</md-radio-button>
<md-radio-button value="water" [disableRipple]="disableRipple" [color]="color">
Squirtle
</md-radio-button>
<md-radio-button value="leaf" [disableRipple]="disableRipple" [color]="color">
Bulbasaur
</md-radio-button>
</md-radio-group>
`
})
Expand All @@ -608,6 +637,7 @@ class RadiosInsideRadioGroup {
isFirstDisabled: boolean = false;
groupValue: string = null;
disableRipple: boolean = false;
color: string;
}


Expand Down
20 changes: 16 additions & 4 deletions src/lib/radio/radio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';


/**
Expand Down Expand Up @@ -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
*/
Expand All @@ -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',
Expand All @@ -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++}`;
Expand Down Expand Up @@ -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) => {
Expand Down

0 comments on commit 89458ff

Please sign in to comment.