Skip to content

feat(selection-list): support specifying theme color #15237

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/dev-app/list/list-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -120,13 +120,14 @@ <h2>Selection list</h2>
(ngModelChange)="onSelectedOptionsChange($event)"
(change)="changeEventCount = changeEventCount + 1"
[disabled]="selectionListDisabled"
[disableRipple]="selectionListRippleDisabled">
[disableRipple]="selectionListRippleDisabled"
color="primary">
<h3 mat-subheader>Groceries</h3>

<mat-list-option value="bananas" checkboxPosition="before">Bananas</mat-list-option>
<mat-list-option selected value="oranges">Oranges</mat-list-option>
<mat-list-option value="apples">Apples</mat-list-option>
<mat-list-option value="strawberries">Strawberries</mat-list-option>
<mat-list-option value="apples" color="accent">Apples</mat-list-option>
<mat-list-option value="strawberries" color="warn">Strawberries</mat-list-option>
</mat-selection-list>

<mat-selection-list [disableRipple]="selectionListRippleDisabled">
Expand Down
37 changes: 34 additions & 3 deletions src/lib/list/selection-list.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
ViewChildren,
} from '@angular/core';
import {async, ComponentFixture, fakeAsync, TestBed, tick, flush} from '@angular/core/testing';
import {MatRipple, defaultRippleAnimationConfig} from '@angular/material/core';
import {MatRipple, defaultRippleAnimationConfig, ThemePalette} from '@angular/material/core';
import {By} from '@angular/platform-browser';
import {
MatListModule,
Expand Down Expand Up @@ -123,6 +123,33 @@ describe('MatSelectionList without forms', () => {
expect(listOptions[2].nativeElement.getAttribute('aria-disabled')).toBe('false');
});

it('should be able to specify a color for list options', () => {
const optionNativeElements = listOptions.map(option => option.nativeElement);

expect(optionNativeElements.every(option => !option.classList.contains('mat-primary')))
.toBe(true);
expect(optionNativeElements.every(option => !option.classList.contains('mat-warn')))
.toBe(true);

// All options will be set to the "warn" color.
fixture.componentInstance.selectionListColor = 'warn';
fixture.detectChanges();

expect(optionNativeElements.every(option => !option.classList.contains('mat-primary')))
.toBe(true);
expect(optionNativeElements.every(option => option.classList.contains('mat-warn')))
.toBe(true);

// Color will be set explicitly for an option and should take precedence.
fixture.componentInstance.firstOptionColor = 'primary';
fixture.detectChanges();

expect(optionNativeElements[0].classList).toContain('mat-primary');
expect(optionNativeElements[0].classList).not.toContain('mat-warn');
expect(optionNativeElements.slice(1).every(option => option.classList.contains('mat-warn')))
.toBe(true);
});

it('should be able to deselect an option', () => {
let testListItem = listOptions[2].injector.get<MatListOption>(MatListOption);
let selectList =
Expand Down Expand Up @@ -1122,8 +1149,10 @@ describe('MatSelectionList with forms', () => {
<mat-selection-list
id="selection-list-1"
(selectionChange)="onValueChange($event)"
[disableRipple]="listRippleDisabled">
<mat-list-option checkboxPosition="before" disabled="true" value="inbox">
[disableRipple]="listRippleDisabled"
[color]="selectionListColor">
<mat-list-option checkboxPosition="before" disabled="true" value="inbox"
[color]="firstOptionColor">
Inbox (disabled selection-option)
</mat-list-option>
<mat-list-option id="testSelect" checkboxPosition="before" class="test-native-focus"
Expand All @@ -1140,6 +1169,8 @@ describe('MatSelectionList with forms', () => {
class SelectionListWithListOptions {
showLastOption: boolean = true;
listRippleDisabled = false;
selectionListColor: ThemePalette;
firstOptionColor: ThemePalette;

onValueChange(_change: MatSelectionListChange) {}
}
Expand Down
19 changes: 18 additions & 1 deletion src/lib/list/selection-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {
MatLine,
setLines,
mixinDisableRipple,
ThemePalette,
} from '@angular/material/core';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
import {Subscription} from 'rxjs';
Expand Down Expand Up @@ -97,6 +98,11 @@ export class MatSelectionListChange {
'tabindex': '-1',
'[class.mat-list-item-disabled]': 'disabled',
'[class.mat-list-item-with-avatar]': '_avatar || _icon',
// Manually set the "primary" or "warn" class if the color has been explicitly
// set to "primary" or "warn". The pseudo checkbox picks up these classes for
// its theme. The accent theme palette is the default and doesn't need to be set.
'[class.mat-primary]': 'color === "primary"',
'[class.mat-warn]': 'color === "warn"',
'[attr.aria-selected]': 'selected.toString()',
'[attr.aria-disabled]': 'disabled.toString()',
},
Expand All @@ -121,6 +127,12 @@ export class MatListOption extends _MatListOptionMixinBase
/** Whether the label should appear before or after the checkbox. Defaults to 'after' */
@Input() checkboxPosition: 'before' | 'after' = 'after';

/** Theme color of the list option. This sets the color of the checkbox. */
@Input()
get color(): ThemePalette { return this._color || this.selectionList.color; }
set color(newValue: ThemePalette) { this._color = newValue; }
private _color: ThemePalette;

/** Value of the option */
@Input()
get value(): any { return this._value; }
Expand Down Expand Up @@ -316,6 +328,9 @@ export class MatSelectionList extends _MatSelectionListMixinBase implements Focu
/** Tabindex of the selection list. */
@Input() tabIndex: number = 0;

/** Theme color of the selection list. This sets the checkbox color for all list options. */
@Input() color: ThemePalette = 'accent';

/**
* Function used for comparing an option against the selected value when determining which
* options should appear as selected. The first argument is the value of an options. The second
Expand Down Expand Up @@ -389,8 +404,10 @@ export class MatSelectionList extends _MatSelectionListMixinBase implements Focu

ngOnChanges(changes: SimpleChanges) {
const disableRippleChanges = changes.disableRipple;
const colorChanges = changes.color;

if (disableRippleChanges && !disableRippleChanges.firstChange) {
if ((disableRippleChanges && !disableRippleChanges.firstChange) ||
(colorChanges && !colorChanges.firstChange)) {
this._markOptionsForCheck();
}
}
Expand Down
2 changes: 2 additions & 0 deletions tools/public_api_guard/lib/list.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export declare class MatListOption extends _MatListOptionMixinBase implements Af
_lines: QueryList<MatLine>;
_text: ElementRef;
checkboxPosition: 'before' | 'after';
color: ThemePalette;
disabled: any;
selected: boolean;
selectionList: MatSelectionList;
Expand Down Expand Up @@ -84,6 +85,7 @@ export declare class MatNavList extends _MatListMixinBase implements CanDisableR
export declare class MatSelectionList extends _MatSelectionListMixinBase implements FocusableOption, CanDisableRipple, AfterContentInit, ControlValueAccessor, OnDestroy, OnChanges {
_keyManager: FocusKeyManager<MatListOption>;
_onTouched: () => void;
color: ThemePalette;
compareWith: (o1: any, o2: any) => boolean;
disabled: boolean;
options: QueryList<MatListOption>;
Expand Down