Skip to content

Commit a44ecd3

Browse files
committed
feat(selection-list): support specifying theme color
Currently the selection list always uses the `accent` color for the underlying pseudo checkbox of list options. In order to make this configurable by the user, we add a `color` input that matches our other usages of the `color` input in order to be consistent. Since we want to allow that a color can be specified for the `<mat-selection-list>` and that it's possible to overwrite the color for an individual list option, we don't use the color mixin as it brings in a lot of overhead and also doesn't make it easy to provide the desired precedence for individual options. Also we **can't** set the color class on the `<mat-selection-list>` as the pseudo checkbox will then ignore the color class set for the individual list option. Closes #15234
1 parent 3ae6eb2 commit a44ecd3

File tree

3 files changed

+51
-6
lines changed

3 files changed

+51
-6
lines changed

src/dev-app/list/list-demo.html

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,13 +120,14 @@ <h2>Selection list</h2>
120120
(ngModelChange)="onSelectedOptionsChange($event)"
121121
(change)="changeEventCount = changeEventCount + 1"
122122
[disabled]="selectionListDisabled"
123-
[disableRipple]="selectionListRippleDisabled">
123+
[disableRipple]="selectionListRippleDisabled"
124+
color="primary">
124125
<h3 mat-subheader>Groceries</h3>
125126

126127
<mat-list-option value="bananas" checkboxPosition="before">Bananas</mat-list-option>
127128
<mat-list-option selected value="oranges">Oranges</mat-list-option>
128-
<mat-list-option value="apples">Apples</mat-list-option>
129-
<mat-list-option value="strawberries">Strawberries</mat-list-option>
129+
<mat-list-option value="apples" color="accent">Apples</mat-list-option>
130+
<mat-list-option value="strawberries" color="warn">Strawberries</mat-list-option>
130131
</mat-selection-list>
131132

132133
<mat-selection-list [disableRipple]="selectionListRippleDisabled">

src/lib/list/selection-list.spec.ts

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
ViewChildren,
1515
} from '@angular/core';
1616
import {async, ComponentFixture, fakeAsync, TestBed, tick, flush} from '@angular/core/testing';
17-
import {MatRipple, defaultRippleAnimationConfig} from '@angular/material/core';
17+
import {MatRipple, defaultRippleAnimationConfig, ThemePalette} from '@angular/material/core';
1818
import {By} from '@angular/platform-browser';
1919
import {
2020
MatListModule,
@@ -123,6 +123,31 @@ describe('MatSelectionList without forms', () => {
123123
expect(listOptions[2].nativeElement.getAttribute('aria-disabled')).toBe('false');
124124
});
125125

126+
it('should be able to specify a color for list options', () => {
127+
expect(listOptions.map(option => option.nativeElement)
128+
.every(option => !option.classList.contains('mat-primary'))).toBe(true);
129+
expect(listOptions.map(option => option.nativeElement)
130+
.every(option => !option.classList.contains('mat-warn'))).toBe(true);
131+
132+
// All options will be set to the "warn" color.
133+
fixture.componentInstance.selectionListColor = 'warn';
134+
fixture.detectChanges();
135+
136+
expect(listOptions.map(option => option.nativeElement)
137+
.every(option => !option.classList.contains('mat-primary'))).toBe(true);
138+
expect(listOptions.map(option => option.nativeElement)
139+
.every(option => option.classList.contains('mat-warn'))).toBe(true);
140+
141+
// Color will be set explicitly for an option and should take precedence.
142+
fixture.componentInstance.firstOptionColor = 'primary';
143+
fixture.detectChanges();
144+
145+
expect(listOptions[0].nativeElement.classList).toContain('mat-primary');
146+
expect(listOptions[0].nativeElement.classList).not.toContain('mat-warn');
147+
expect(listOptions.slice(1).map(option => option.nativeElement)
148+
.every(option => option.classList.contains('mat-warn'))).toBe(true);
149+
});
150+
126151
it('should be able to deselect an option', () => {
127152
let testListItem = listOptions[2].injector.get<MatListOption>(MatListOption);
128153
let selectList =
@@ -1122,8 +1147,10 @@ describe('MatSelectionList with forms', () => {
11221147
<mat-selection-list
11231148
id="selection-list-1"
11241149
(selectionChange)="onValueChange($event)"
1125-
[disableRipple]="listRippleDisabled">
1126-
<mat-list-option checkboxPosition="before" disabled="true" value="inbox">
1150+
[disableRipple]="listRippleDisabled"
1151+
[color]="selectionListColor">
1152+
<mat-list-option checkboxPosition="before" disabled="true" value="inbox"
1153+
[color]="firstOptionColor">
11271154
Inbox (disabled selection-option)
11281155
</mat-list-option>
11291156
<mat-list-option id="testSelect" checkboxPosition="before" class="test-native-focus"
@@ -1140,6 +1167,8 @@ describe('MatSelectionList with forms', () => {
11401167
class SelectionListWithListOptions {
11411168
showLastOption: boolean = true;
11421169
listRippleDisabled = false;
1170+
selectionListColor: ThemePalette;
1171+
firstOptionColor: ThemePalette;
11431172

11441173
onValueChange(_change: MatSelectionListChange) {}
11451174
}

src/lib/list/selection-list.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import {
4646
MatLine,
4747
setLines,
4848
mixinDisableRipple,
49+
ThemePalette,
4950
} from '@angular/material/core';
5051
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
5152
import {Subscription} from 'rxjs';
@@ -97,6 +98,11 @@ export class MatSelectionListChange {
9798
'tabindex': '-1',
9899
'[class.mat-list-item-disabled]': 'disabled',
99100
'[class.mat-list-item-with-avatar]': '_avatar || _icon',
101+
// Manually set the "primary" or "warn" class if the color has been explicitly
102+
// set to "primary" or "warn". The pseudo checkbox picks up these classes for
103+
// its theme. The accent theme palette is the default and doesn't need to be set.
104+
'[class.mat-primary]': 'color === "primary"',
105+
'[class.mat-warn]': 'color === "warn"',
100106
'[attr.aria-selected]': 'selected.toString()',
101107
'[attr.aria-disabled]': 'disabled.toString()',
102108
},
@@ -121,6 +127,12 @@ export class MatListOption extends _MatListOptionMixinBase
121127
/** Whether the label should appear before or after the checkbox. Defaults to 'after' */
122128
@Input() checkboxPosition: 'before' | 'after' = 'after';
123129

130+
/** Theme color of the list option. This sets the color of the checkbox. */
131+
@Input()
132+
get color(): ThemePalette { return this._color || this.selectionList.color; }
133+
set color(newValue: ThemePalette) { this._color = newValue; }
134+
private _color: ThemePalette;
135+
124136
/** Value of the option */
125137
@Input()
126138
get value(): any { return this._value; }
@@ -316,6 +328,9 @@ export class MatSelectionList extends _MatSelectionListMixinBase implements Focu
316328
/** Tabindex of the selection list. */
317329
@Input() tabIndex: number = 0;
318330

331+
/** Theme color of the selection list. This sets the checkbox color for all list options. */
332+
@Input() color: ThemePalette = 'accent';
333+
319334
/**
320335
* Function used for comparing an option against the selected value when determining which
321336
* options should appear as selected. The first argument is the value of an options. The second

0 commit comments

Comments
 (0)