From 50fa883dd288ae9584eee8a223a77a141e6721cc Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Tue, 19 Feb 2019 21:19:08 +0100 Subject: [PATCH] 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 `` 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 `` as the pseudo checkbox will then ignore the color class set for the individual list option. Closes #15234 --- src/dev-app/list/list-demo.html | 7 +++--- src/lib/list/selection-list.spec.ts | 37 +++++++++++++++++++++++++--- src/lib/list/selection-list.ts | 19 +++++++++++++- tools/public_api_guard/lib/list.d.ts | 2 ++ 4 files changed, 58 insertions(+), 7 deletions(-) diff --git a/src/dev-app/list/list-demo.html b/src/dev-app/list/list-demo.html index cc1a33becb19..5df3da6e3b16 100644 --- a/src/dev-app/list/list-demo.html +++ b/src/dev-app/list/list-demo.html @@ -120,13 +120,14 @@

Selection list

(ngModelChange)="onSelectedOptionsChange($event)" (change)="changeEventCount = changeEventCount + 1" [disabled]="selectionListDisabled" - [disableRipple]="selectionListRippleDisabled"> + [disableRipple]="selectionListRippleDisabled" + color="primary">

Groceries

Bananas Oranges - Apples - Strawberries + Apples + Strawberries
diff --git a/src/lib/list/selection-list.spec.ts b/src/lib/list/selection-list.spec.ts index a1acfbe4ee3e..ab54217289ff 100644 --- a/src/lib/list/selection-list.spec.ts +++ b/src/lib/list/selection-list.spec.ts @@ -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, @@ -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); let selectList = @@ -1122,8 +1149,10 @@ describe('MatSelectionList with forms', () => { - + [disableRipple]="listRippleDisabled" + [color]="selectionListColor"> + Inbox (disabled selection-option) { class SelectionListWithListOptions { showLastOption: boolean = true; listRippleDisabled = false; + selectionListColor: ThemePalette; + firstOptionColor: ThemePalette; onValueChange(_change: MatSelectionListChange) {} } diff --git a/src/lib/list/selection-list.ts b/src/lib/list/selection-list.ts index cb7d00bd6c3b..1b76bbfa682c 100644 --- a/src/lib/list/selection-list.ts +++ b/src/lib/list/selection-list.ts @@ -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'; @@ -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()', }, @@ -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; } @@ -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 @@ -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(); } } diff --git a/tools/public_api_guard/lib/list.d.ts b/tools/public_api_guard/lib/list.d.ts index 97b751a59110..30ede83025be 100644 --- a/tools/public_api_guard/lib/list.d.ts +++ b/tools/public_api_guard/lib/list.d.ts @@ -48,6 +48,7 @@ export declare class MatListOption extends _MatListOptionMixinBase implements Af _lines: QueryList; _text: ElementRef; checkboxPosition: 'before' | 'after'; + color: ThemePalette; disabled: any; selected: boolean; selectionList: MatSelectionList; @@ -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; _onTouched: () => void; + color: ThemePalette; compareWith: (o1: any, o2: any) => boolean; disabled: boolean; options: QueryList;