Skip to content

Commit 64145fd

Browse files
authored
fix(checkbox): not handling unknown color palette (#18467)
Fixes the checkbox component not falling back to the default color, if the color is `undefined`. We need to handle this, because `ThemePalette` allows `undefined`. Fixes #18465.
1 parent 21f2df3 commit 64145fd

File tree

4 files changed

+44
-17
lines changed

4 files changed

+44
-17
lines changed

src/material-experimental/mdc-checkbox/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ ng_test_library(
6767
"//src/cdk/testing/private",
6868
"//src/cdk/testing/testbed",
6969
"//src/material/checkbox",
70+
"//src/material/core",
7071
"@npm//@angular/forms",
7172
"@npm//@angular/platform-browser",
7273
],

src/material-experimental/mdc-checkbox/checkbox.spec.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
flushMicrotasks,
88
TestBed,
99
} from '@angular/core/testing';
10+
import {ThemePalette} from '@angular/material/core';
1011
import {FormControl, FormsModule, NgModel, ReactiveFormsModule} from '@angular/forms';
1112
import {By} from '@angular/platform-browser';
1213
import {
@@ -467,6 +468,13 @@ describe('MDC-based MatCheckbox', () => {
467468
expect(checkboxNativeElement.classList.contains('mat-accent')).toBe(true);
468469
expect(checkboxNativeElement.classList.contains('custom-class')).toBe(true);
469470
}));
471+
472+
it('should default to accent if no color is passed in', fakeAsync(() => {
473+
testComponent.checkboxColor = undefined;
474+
fixture.detectChanges();
475+
expect(checkboxNativeElement.classList).toContain('mat-accent');
476+
}));
477+
470478
});
471479

472480
describe(`when MAT_CHECKBOX_CLICK_ACTION is set`, () => {
@@ -1078,7 +1086,7 @@ class SingleCheckbox {
10781086
parentElementClicked: boolean = false;
10791087
parentElementKeyedUp: boolean = false;
10801088
checkboxId: string|null = 'simple-check';
1081-
checkboxColor: string = 'primary';
1089+
checkboxColor: ThemePalette = 'primary';
10821090
checkboxValue: string = 'single_checkbox';
10831091

10841092
onCheckboxClick: (event?: Event) => void = () => {};

src/material-experimental/mdc-checkbox/checkbox.ts

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,16 @@ import {
3030
MAT_CHECKBOX_DEFAULT_OPTIONS,
3131
MatCheckboxClickAction, MatCheckboxDefaultOptions
3232
} from '@angular/material/checkbox';
33-
import {ThemePalette, RippleAnimationConfig} from '@angular/material/core';
33+
import {
34+
ThemePalette,
35+
RippleAnimationConfig,
36+
CanColorCtor,
37+
CanDisableCtor,
38+
mixinColor,
39+
mixinDisabled,
40+
CanColor,
41+
CanDisable,
42+
} from '@angular/material/core';
3443
import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations';
3544
import {MDCCheckboxAdapter, MDCCheckboxFoundation} from '@material/checkbox';
3645
import {numbers} from '@material/ripple';
@@ -51,6 +60,18 @@ export class MatCheckboxChange {
5160
checked: boolean;
5261
}
5362

63+
// Boilerplate for applying mixins to MatCheckbox.
64+
/** @docs-private */
65+
class MatCheckboxBase {
66+
constructor(public _elementRef: ElementRef) {}
67+
}
68+
const _MatCheckboxMixinBase:
69+
CanColorCtor &
70+
CanDisableCtor &
71+
typeof MatCheckboxBase =
72+
mixinColor(mixinDisabled(MatCheckboxBase));
73+
74+
5475
/** Configuration for the ripple animation. */
5576
const RIPPLE_ANIMATION_CONFIG: RippleAnimationConfig = {
5677
enterDuration: numbers.DEACTIVATION_TIMEOUT_MS,
@@ -61,12 +82,10 @@ const RIPPLE_ANIMATION_CONFIG: RippleAnimationConfig = {
6182
selector: 'mat-checkbox',
6283
templateUrl: 'checkbox.html',
6384
styleUrls: ['checkbox.css'],
85+
inputs: ['color', 'disabled'],
6486
host: {
6587
'class': 'mat-mdc-checkbox',
6688
'[attr.tabindex]': 'null',
67-
'[class.mat-primary]': 'color == "primary"',
68-
'[class.mat-accent]': 'color == "accent"',
69-
'[class.mat-warn]': 'color == "warn"',
7089
'[class._mat-animation-noopable]': `_animationMode === 'NoopAnimations'`,
7190
'[class.mdc-checkbox--disabled]': 'disabled',
7291
'[id]': 'id',
@@ -76,7 +95,8 @@ const RIPPLE_ANIMATION_CONFIG: RippleAnimationConfig = {
7695
encapsulation: ViewEncapsulation.None,
7796
changeDetection: ChangeDetectionStrategy.OnPush,
7897
})
79-
export class MatCheckbox implements AfterViewInit, OnDestroy, ControlValueAccessor {
98+
export class MatCheckbox extends _MatCheckboxMixinBase implements AfterViewInit, OnDestroy,
99+
ControlValueAccessor, CanColor, CanDisable {
80100
/**
81101
* The `aria-label` attribute to use for the input element. In most cases, `aria-labelledby` will
82102
* take precedence so this may be omitted.
@@ -135,16 +155,6 @@ export class MatCheckbox implements AfterViewInit, OnDestroy, ControlValueAccess
135155
}
136156
private _indeterminate = false;
137157

138-
/** Whether the checkbox is disabled. */
139-
@Input()
140-
get disabled(): boolean {
141-
return this._disabled;
142-
}
143-
set disabled(disabled) {
144-
this._disabled = coerceBooleanProperty(disabled);
145-
}
146-
private _disabled = false;
147-
148158
/** Whether the checkbox is required. */
149159
@Input()
150160
get required(): boolean {
@@ -235,6 +245,7 @@ export class MatCheckbox implements AfterViewInit, OnDestroy, ControlValueAccess
235245

236246
constructor(
237247
private _changeDetectorRef: ChangeDetectorRef,
248+
elementRef: ElementRef<HTMLElement>,
238249
@Attribute('tabindex') tabIndex: string,
239250
/**
240251
* @deprecated `_clickAction` parameter to be removed, use
@@ -245,6 +256,7 @@ export class MatCheckbox implements AfterViewInit, OnDestroy, ControlValueAccess
245256
@Optional() @Inject(ANIMATION_MODULE_TYPE) public _animationMode?: string,
246257
@Optional() @Inject(MAT_CHECKBOX_DEFAULT_OPTIONS)
247258
private _options?: MatCheckboxDefaultOptions) {
259+
super(elementRef);
248260
// Note: We don't need to set up the MDCFormFieldFoundation. Its only purpose is to manage the
249261
// ripple, which we do ourselves instead.
250262
this.tabIndex = parseInt(tabIndex) || 0;
@@ -253,7 +265,7 @@ export class MatCheckbox implements AfterViewInit, OnDestroy, ControlValueAccess
253265
this._options = this._options || {};
254266

255267
if (this._options.color) {
256-
this.color = this._options.color;
268+
this.color = this.defaultColor = this._options.color;
257269
}
258270

259271
// @breaking-change 10.0.0: Remove this after the `_clickAction` parameter is removed as an

src/material/checkbox/checkbox.spec.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,8 +465,14 @@ describe('MatCheckbox', () => {
465465
expect(checkboxNativeElement.classList.contains('mat-primary')).toBe(false);
466466
expect(checkboxNativeElement.classList.contains('mat-accent')).toBe(true);
467467
expect(checkboxNativeElement.classList.contains('custom-class')).toBe(true);
468+
});
468469

470+
it('should default to accent if no color is passed in', () => {
471+
testComponent.checkboxColor = undefined;
472+
fixture.detectChanges();
473+
expect(checkboxNativeElement.classList).toContain('mat-accent');
469474
});
475+
470476
});
471477

472478
describe('state transition css classes', () => {

0 commit comments

Comments
 (0)