diff --git a/src/dev-app/list/list-demo.html b/src/dev-app/list/list-demo.html index 13067cf679df..cc1a33becb19 100644 --- a/src/dev-app/list/list-demo.html +++ b/src/dev-app/list/list-demo.html @@ -119,7 +119,8 @@

Selection list

+ [disabled]="selectionListDisabled" + [disableRipple]="selectionListRippleDisabled">

Groceries

Bananas @@ -128,7 +129,7 @@

Groceries

Strawberries
- +

Dogs

@@ -145,8 +146,16 @@

Dogs

Selected: {{selectedOptions | json}}

Change Event Count {{changeEventCount}}

Model Change Event Count {{modelChangeEventCount}}

- Disable Selection List - +

+ + Disable Selection List + +

+

+ + Disable Selection List ripples + +

diff --git a/src/dev-app/list/list-demo.ts b/src/dev-app/list/list-demo.ts index be6444af5d3e..bdee71397b2e 100644 --- a/src/dev-app/list/list-demo.ts +++ b/src/dev-app/list/list-demo.ts @@ -60,6 +60,7 @@ export class ListDemo { thirdLine = false; infoClicked = false; selectionListDisabled = false; + selectionListRippleDisabled = false; selectedOptions: string[] = ['apples']; changeEventCount = 0; diff --git a/src/lib/list/selection-list.spec.ts b/src/lib/list/selection-list.spec.ts index f058837d9bd7..a1acfbe4ee3e 100644 --- a/src/lib/list/selection-list.spec.ts +++ b/src/lib/list/selection-list.spec.ts @@ -4,6 +4,7 @@ import { dispatchFakeEvent, dispatchEvent, dispatchKeyboardEvent, + dispatchMouseEvent, } from '@angular/cdk/testing'; import { Component, @@ -13,7 +14,7 @@ import { ViewChildren, } from '@angular/core'; import {async, ComponentFixture, fakeAsync, TestBed, tick, flush} from '@angular/core/testing'; -import {MatRipple} from '@angular/material/core'; +import {MatRipple, defaultRippleAnimationConfig} from '@angular/material/core'; import {By} from '@angular/platform-browser'; import { MatListModule, @@ -521,6 +522,33 @@ describe('MatSelectionList without forms', () => { expect(list.options.toArray().every(option => option.selected)).toBe(true); }); + it('should disable list item ripples when the ripples on the list have been disabled', + fakeAsync(() => { + const rippleTarget = fixture.nativeElement + .querySelector('.mat-list-option:not(.mat-list-item-disabled) .mat-list-item-content'); + const {enterDuration, exitDuration} = defaultRippleAnimationConfig; + + dispatchMouseEvent(rippleTarget, 'mousedown'); + dispatchMouseEvent(rippleTarget, 'mouseup'); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length) + .toBe(1, 'Expected ripples to be enabled by default.'); + + // Wait for the ripples to go away. + tick(enterDuration + exitDuration); + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length) + .toBe(0, 'Expected ripples to go away.'); + + fixture.componentInstance.listRippleDisabled = true; + fixture.detectChanges(); + + dispatchMouseEvent(rippleTarget, 'mousedown'); + dispatchMouseEvent(rippleTarget, 'mouseup'); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length) + .toBe(0, 'Expected no ripples after list ripples are disabled.'); + })); + }); describe('with list option selected', () => { @@ -1091,7 +1119,10 @@ describe('MatSelectionList with forms', () => { @Component({template: ` - + Inbox (disabled selection-option) @@ -1108,6 +1139,7 @@ describe('MatSelectionList with forms', () => { `}) class SelectionListWithListOptions { showLastOption: boolean = true; + listRippleDisabled = false; onValueChange(_change: MatSelectionListChange) {} } diff --git a/src/lib/list/selection-list.ts b/src/lib/list/selection-list.ts index 3022bba7cc0f..cb7d00bd6c3b 100644 --- a/src/lib/list/selection-list.ts +++ b/src/lib/list/selection-list.ts @@ -38,6 +38,8 @@ import { QueryList, ViewChild, ViewEncapsulation, + SimpleChanges, + OnChanges, } from '@angular/core'; import { CanDisableRipple, CanDisableRippleCtor, @@ -299,7 +301,7 @@ export class MatListOption extends _MatListOptionMixinBase changeDetection: ChangeDetectionStrategy.OnPush }) export class MatSelectionList extends _MatSelectionListMixinBase implements FocusableOption, - CanDisableRipple, AfterContentInit, ControlValueAccessor, OnDestroy { + CanDisableRipple, AfterContentInit, ControlValueAccessor, OnDestroy, OnChanges { /** The FocusKeyManager which handles focus. */ _keyManager: FocusKeyManager; @@ -331,9 +333,7 @@ export class MatSelectionList extends _MatSelectionListMixinBase implements Focu // strategy. Therefore the options will not check for any changes if the `MatSelectionList` // changed its state. Since we know that a change to `disabled` property of the list affects // the state of the options, we manually mark each option for check. - if (this.options) { - this.options.forEach(option => option._markForCheck()); - } + this._markOptionsForCheck(); } private _disabled: boolean = false; @@ -387,6 +387,14 @@ export class MatSelectionList extends _MatSelectionListMixinBase implements Focu }); } + ngOnChanges(changes: SimpleChanges) { + const disableRippleChanges = changes.disableRipple; + + if (disableRippleChanges && !disableRippleChanges.firstChange) { + this._markOptionsForCheck(); + } + } + ngOnDestroy() { this._modelChanges.unsubscribe(); } @@ -581,4 +589,11 @@ export class MatSelectionList extends _MatSelectionListMixinBase implements Focu private _getOptionIndex(option: MatListOption): number { return this.options.toArray().indexOf(option); } + + /** Marks all the options to be checked in the next change detection run. */ + private _markOptionsForCheck() { + if (this.options) { + this.options.forEach(option => option._markForCheck()); + } + } } diff --git a/tools/public_api_guard/lib/list.d.ts b/tools/public_api_guard/lib/list.d.ts index 9a399e3c7a84..97b751a59110 100644 --- a/tools/public_api_guard/lib/list.d.ts +++ b/tools/public_api_guard/lib/list.d.ts @@ -81,7 +81,7 @@ export declare class MatNavList extends _MatListMixinBase implements CanDisableR ngOnDestroy(): void; } -export declare class MatSelectionList extends _MatSelectionListMixinBase implements FocusableOption, CanDisableRipple, AfterContentInit, ControlValueAccessor, OnDestroy { +export declare class MatSelectionList extends _MatSelectionListMixinBase implements FocusableOption, CanDisableRipple, AfterContentInit, ControlValueAccessor, OnDestroy, OnChanges { _keyManager: FocusKeyManager; _onTouched: () => void; compareWith: (o1: any, o2: any) => boolean; @@ -99,6 +99,7 @@ export declare class MatSelectionList extends _MatSelectionListMixinBase impleme deselectAll(): void; focus(): void; ngAfterContentInit(): void; + ngOnChanges(changes: SimpleChanges): void; ngOnDestroy(): void; registerOnChange(fn: (value: any) => void): void; registerOnTouched(fn: () => void): void;