From 1910e2743f24b5045a0f95f5a438775d7d81574b Mon Sep 17 00:00:00 2001 From: Jerome Nelson Date: Tue, 14 May 2019 22:40:42 +0200 Subject: [PATCH] feat(radio): add provider for default color input --- src/lib/radio/radio.md | 10 ++++++ src/lib/radio/radio.spec.ts | 49 +++++++++++++++++++++++++++ src/lib/radio/radio.ts | 37 +++++++++++++++++--- tools/public_api_guard/lib/radio.d.ts | 13 +++++-- 4 files changed, 103 insertions(+), 6 deletions(-) diff --git a/src/lib/radio/radio.md b/src/lib/radio/radio.md index 8a999eb056c5..0e978b65093f 100644 --- a/src/lib/radio/radio.md +++ b/src/lib/radio/radio.md @@ -34,3 +34,13 @@ This internal radio button receives focus and is automatically labelled by the t `` element. Radio button groups should be given a meaningful label via `aria-label` or `aria-labelledby`. + +### Default Color Configuration +The default color for radio buttons can be configured globally using the `MAT_RADIO_DEFAULT_OPTIONS` provider + +``` +providers: [ + provide: MAT_RADIO_DEFAULT_OPTIONS, + useValue: { color: 'accent' }, +] +``` \ No newline at end of file diff --git a/src/lib/radio/radio.spec.ts b/src/lib/radio/radio.spec.ts index 15097096e6ac..4676b9fd97ec 100644 --- a/src/lib/radio/radio.spec.ts +++ b/src/lib/radio/radio.spec.ts @@ -3,6 +3,8 @@ import {FormControl, FormsModule, NgModel, ReactiveFormsModule} from '@angular/f import {Component, DebugElement, ViewChild} from '@angular/core'; import {By} from '@angular/platform-browser'; import {dispatchFakeEvent} from '@angular/cdk/testing'; + +import {MAT_RADIO_DEFAULT_OPTIONS} from './radio'; import {MatRadioButton, MatRadioChange, MatRadioGroup, MatRadioModule} from './index'; describe('MatRadio', () => { @@ -773,6 +775,43 @@ describe('MatRadio', () => { }); }); +describe('MatRadioDefaultOverrides', () => { + describe('when MAT_RADIO_DEFAULT_OPTIONS overridden', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [MatRadioModule, FormsModule], + declarations: [DefaultRadioButton, RadioButtonWithColorBinding], + providers: [{ + provide: MAT_RADIO_DEFAULT_OPTIONS, + useValue: {color: 'primary'}, + }], + }); + + TestBed.compileComponents(); + })); + it('should override default color in Component', () => { + const fixture: ComponentFixture = + TestBed.createComponent(DefaultRadioButton); + fixture.detectChanges(); + const radioDebugElement: DebugElement = + fixture.debugElement.query(By.directive(MatRadioButton)); + expect( + radioDebugElement.nativeElement.classList + ).toContain('mat-primary'); + }); + it('should not override explicit input bindings', () => { + const fixture: ComponentFixture = + TestBed.createComponent(RadioButtonWithColorBinding); + fixture.detectChanges(); + const radioDebugElement: DebugElement = + fixture.debugElement.query(By.directive(MatRadioButton)); + expect( + radioDebugElement.nativeElement.classList + ).not.toContain('mat-primary'); + expect(radioDebugElement.nativeElement.classList).toContain('mat-warn'); + }); + }); +}); @Component({ template: ` @@ -914,3 +953,13 @@ class TranscludingWrapper {} template: `` }) class RadioButtonWithPredefinedTabindex {} + +@Component({ + template: `` +}) +class DefaultRadioButton {} + +@Component({ + template: `` +}) +class RadioButtonWithColorBinding {} diff --git a/src/lib/radio/radio.ts b/src/lib/radio/radio.ts index 4fc4d2e99f69..c2c45ab9bfb6 100644 --- a/src/lib/radio/radio.ts +++ b/src/lib/radio/radio.ts @@ -21,7 +21,9 @@ import { EventEmitter, forwardRef, Inject, + InjectionToken, Input, + OnChanges, OnDestroy, OnInit, Optional, @@ -41,10 +43,27 @@ import { mixinColor, mixinDisableRipple, mixinTabIndex, + ThemePalette, } from '@angular/material/core'; import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations'; +export interface MatRadioDefaultOptions { + color: ThemePalette; +} + +export const MAT_RADIO_DEFAULT_OPTIONS = + new InjectionToken('mat-radio-default-options', { + providedIn: 'root', + factory: MAT_RADIO_DEFAULT_OPTIONS_FACTORY +}); + +export function MAT_RADIO_DEFAULT_OPTIONS_FACTORY(): MatRadioDefaultOptions { + return { + color: 'accent' + }; +} + // Increasing integer for generating unique ids for radio components. let nextUniqueId = 0; @@ -304,7 +323,7 @@ export class MatRadioButtonBase { // palette by default. https://material.io/guidelines/components/selection-controls.html export const _MatRadioButtonMixinBase: CanColorCtor & CanDisableRippleCtor & HasTabIndexCtor & typeof MatRadioButtonBase = - mixinColor(mixinDisableRipple(mixinTabIndex(MatRadioButtonBase)), 'accent'); + mixinColor(mixinDisableRipple(mixinTabIndex(MatRadioButtonBase))); /** * A Material design radio-button. Typically placed inside of `` elements. @@ -332,8 +351,8 @@ export const _MatRadioButtonMixinBase: }, changeDetection: ChangeDetectionStrategy.OnPush, }) -export class MatRadioButton extends _MatRadioButtonMixinBase - implements OnInit, AfterViewInit, OnDestroy, CanColor, CanDisableRipple, HasTabIndex { +export class MatRadioButton extends _MatRadioButtonMixinBase implements + OnInit, AfterViewInit, OnChanges, OnDestroy, CanColor, CanDisableRipple, HasTabIndex { private _uniqueId: string = `mat-radio-${++nextUniqueId}`; @@ -462,13 +481,19 @@ export class MatRadioButton extends _MatRadioButtonMixinBase private _changeDetector: ChangeDetectorRef, private _focusMonitor: FocusMonitor, private _radioDispatcher: UniqueSelectionDispatcher, - @Optional() @Inject(ANIMATION_MODULE_TYPE) public _animationMode?: string) { + @Optional() @Inject(ANIMATION_MODULE_TYPE) public _animationMode?: string, + @Optional() @Inject(MAT_RADIO_DEFAULT_OPTIONS) + private _providerOverride?: MatRadioDefaultOptions) { super(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; + if (!this.color && this._providerOverride && this._providerOverride.color) { + this.color = this._providerOverride.color; + } + this._removeUniqueSelectionListener = _radioDispatcher.listen((id: string, name: string) => { if (id !== this.id && name === this.name) { @@ -493,6 +518,10 @@ export class MatRadioButton extends _MatRadioButtonMixinBase this._changeDetector.markForCheck(); } + ngOnChanges(): void { + this.color = !this.color ? 'accent' : this.color; + } + ngOnInit() { if (this.radioGroup) { // If the radio is inside a radio group, determine if it should be checked diff --git a/tools/public_api_guard/lib/radio.d.ts b/tools/public_api_guard/lib/radio.d.ts index c582fdf2748f..32c6999fb5d5 100644 --- a/tools/public_api_guard/lib/radio.d.ts +++ b/tools/public_api_guard/lib/radio.d.ts @@ -1,8 +1,12 @@ export declare const _MatRadioButtonMixinBase: CanColorCtor & CanDisableRippleCtor & HasTabIndexCtor & typeof MatRadioButtonBase; +export declare const MAT_RADIO_DEFAULT_OPTIONS: InjectionToken; + +export declare function MAT_RADIO_DEFAULT_OPTIONS_FACTORY(): MatRadioDefaultOptions; + export declare const MAT_RADIO_GROUP_CONTROL_VALUE_ACCESSOR: any; -export declare class MatRadioButton extends _MatRadioButtonMixinBase implements OnInit, AfterViewInit, OnDestroy, CanColor, CanDisableRipple, HasTabIndex { +export declare class MatRadioButton extends _MatRadioButtonMixinBase implements OnInit, AfterViewInit, OnChanges, OnDestroy, CanColor, CanDisableRipple, HasTabIndex { _animationMode?: string | undefined; _inputElement: ElementRef; ariaDescribedby: string; @@ -18,13 +22,14 @@ export declare class MatRadioButton extends _MatRadioButtonMixinBase implements radioGroup: MatRadioGroup; required: boolean; value: any; - constructor(radioGroup: MatRadioGroup, elementRef: ElementRef, _changeDetector: ChangeDetectorRef, _focusMonitor: FocusMonitor, _radioDispatcher: UniqueSelectionDispatcher, _animationMode?: string | undefined); + constructor(radioGroup: MatRadioGroup, elementRef: ElementRef, _changeDetector: ChangeDetectorRef, _focusMonitor: FocusMonitor, _radioDispatcher: UniqueSelectionDispatcher, _providerOverride?: MatRadioDefaultOptions | undefined, _animationMode?: string | undefined); _isRippleDisabled(): boolean; _markForCheck(): void; _onInputChange(event: Event): void; _onInputClick(event: Event): void; focus(): void; ngAfterViewInit(): void; + ngOnChanges(): void; ngOnDestroy(): void; ngOnInit(): void; } @@ -43,6 +48,10 @@ export declare class MatRadioChange { value: any); } +export interface MatRadioDefaultOptions { + color: ThemePalette; +} + export declare class MatRadioGroup implements AfterContentInit, ControlValueAccessor { _controlValueAccessorChangeFn: (value: any) => void; _radios: QueryList;