Skip to content

Commit 8544591

Browse files
committed
fix(list): disableRipple on list input not taking effect after init
Fixes the `disableRipple` on the list not having an effect if it's changed through the input after init. Fixes #14824.
1 parent 0fd6456 commit 8544591

File tree

3 files changed

+116
-9
lines changed

3 files changed

+116
-9
lines changed

src/lib/list/list.spec.ts

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import {async, TestBed} from '@angular/core/testing';
1+
import {async, TestBed, fakeAsync, tick} from '@angular/core/testing';
22
import {Component, QueryList, ViewChildren} from '@angular/core';
3+
import {dispatchMouseEvent} from '@angular/cdk/testing';
34
import {By} from '@angular/platform-browser';
45
import {MatListItem, MatListModule} from './index';
56

@@ -207,6 +208,62 @@ describe('MatList', () => {
207208
expect(items.every(item => item._isRippleDisabled())).toBe(true);
208209
});
209210

211+
it('should disable item ripples when list ripples are disabled via the input in nav list',
212+
fakeAsync(() => {
213+
const fixture = TestBed.createComponent(NavListWithOneAnchorItem);
214+
fixture.detectChanges();
215+
216+
const rippleTarget = fixture.nativeElement.querySelector('.mat-list-item-content');
217+
218+
dispatchMouseEvent(rippleTarget, 'mousedown');
219+
dispatchMouseEvent(rippleTarget, 'mouseup');
220+
221+
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length)
222+
.toBe(1, 'Expected ripples to be enabled by default.');
223+
224+
// Wait for the ripples to go away.
225+
tick(1000);
226+
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length)
227+
.toBe(0, 'Expected ripples to go away.');
228+
229+
fixture.componentInstance.disableListRipple = true;
230+
fixture.detectChanges();
231+
232+
dispatchMouseEvent(rippleTarget, 'mousedown');
233+
dispatchMouseEvent(rippleTarget, 'mouseup');
234+
235+
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length)
236+
.toBe(0, 'Expected no ripples after list ripples are disabled.');
237+
}));
238+
239+
it('should disable item ripples when list ripples are disabled via the input in an action list',
240+
fakeAsync(() => {
241+
const fixture = TestBed.createComponent(ActionListWithoutType);
242+
fixture.detectChanges();
243+
244+
const rippleTarget = fixture.nativeElement.querySelector('.mat-list-item-content');
245+
246+
dispatchMouseEvent(rippleTarget, 'mousedown');
247+
dispatchMouseEvent(rippleTarget, 'mouseup');
248+
249+
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length)
250+
.toBe(1, 'Expected ripples to be enabled by default.');
251+
252+
// Wait for the ripples to go away.
253+
tick(1000);
254+
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length)
255+
.toBe(0, 'Expected ripples to go away.');
256+
257+
fixture.componentInstance.disableListRipple = true;
258+
fixture.detectChanges();
259+
260+
dispatchMouseEvent(rippleTarget, 'mousedown');
261+
dispatchMouseEvent(rippleTarget, 'mouseup');
262+
263+
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length)
264+
.toBe(0, 'Expected no ripples after list ripples are disabled.');
265+
}));
266+
210267
});
211268

212269

src/lib/list/list.ts

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ import {
1717
Optional,
1818
QueryList,
1919
ViewEncapsulation,
20+
OnChanges,
21+
OnDestroy,
22+
ChangeDetectorRef,
2023
} from '@angular/core';
2124
import {
2225
CanDisableRipple,
@@ -25,6 +28,8 @@ import {
2528
setLines,
2629
mixinDisableRipple,
2730
} from '@angular/material/core';
31+
import {Subject} from 'rxjs';
32+
import {takeUntil} from 'rxjs/operators';
2833

2934
// Boilerplate for applying mixins to MatList.
3035
/** @docs-private */
@@ -52,7 +57,19 @@ export const _MatListItemMixinBase: CanDisableRippleCtor & typeof MatListItemBas
5257
encapsulation: ViewEncapsulation.None,
5358
changeDetection: ChangeDetectionStrategy.OnPush,
5459
})
55-
export class MatNavList extends _MatListMixinBase implements CanDisableRipple {}
60+
export class MatNavList extends _MatListMixinBase implements CanDisableRipple, OnChanges,
61+
OnDestroy {
62+
/** Emits when the state of the list changes. */
63+
_stateChanges = new Subject<void>();
64+
65+
ngOnChanges() {
66+
this._stateChanges.next();
67+
}
68+
69+
ngOnDestroy() {
70+
this._stateChanges.complete();
71+
}
72+
}
5673

5774
@Component({
5875
moduleId: module.id,
@@ -67,7 +84,10 @@ export class MatNavList extends _MatListMixinBase implements CanDisableRipple {}
6784
encapsulation: ViewEncapsulation.None,
6885
changeDetection: ChangeDetectionStrategy.OnPush,
6986
})
70-
export class MatList extends _MatListMixinBase implements CanDisableRipple {
87+
export class MatList extends _MatListMixinBase implements CanDisableRipple, OnChanges, OnDestroy {
88+
/** Emits when the state of the list changes. */
89+
_stateChanges = new Subject<void>();
90+
7191
/**
7292
* @deprecated _elementRef parameter to be made required.
7393
* @breaking-change 8.0.0
@@ -94,6 +114,14 @@ export class MatList extends _MatListMixinBase implements CanDisableRipple {
94114

95115
return null;
96116
}
117+
118+
ngOnChanges() {
119+
this._stateChanges.next();
120+
}
121+
122+
ngOnDestroy() {
123+
this._stateChanges.complete();
124+
}
97125
}
98126

99127
/**
@@ -143,17 +171,20 @@ export class MatListSubheaderCssMatStyler {}
143171
changeDetection: ChangeDetectionStrategy.OnPush,
144172
})
145173
export class MatListItem extends _MatListItemMixinBase implements AfterContentInit,
146-
CanDisableRipple {
174+
CanDisableRipple, OnDestroy {
147175
private _isInteractiveList: boolean = false;
148176
private _list?: MatNavList | MatList;
177+
private _destroyed = new Subject<void>();
149178

150179
@ContentChildren(MatLine) _lines: QueryList<MatLine>;
151180
@ContentChild(MatListAvatarCssMatStyler) _avatar: MatListAvatarCssMatStyler;
152181
@ContentChild(MatListIconCssMatStyler) _icon: MatListIconCssMatStyler;
153182

154183
constructor(private _element: ElementRef<HTMLElement>,
155184
@Optional() navList?: MatNavList,
156-
@Optional() list?: MatList) {
185+
@Optional() list?: MatList,
186+
// @breaking-change 8.0.0 `_changeDetectorRef` to be made into a required parameter.
187+
_changeDetectorRef?: ChangeDetectorRef) {
157188
super();
158189
this._isInteractiveList = !!(navList || (list && list._getListType() === 'action-list'));
159190
this._list = navList || list;
@@ -165,12 +196,24 @@ export class MatListItem extends _MatListItemMixinBase implements AfterContentIn
165196
if (element.nodeName.toLowerCase() === 'button' && !element.hasAttribute('type')) {
166197
element.setAttribute('type', 'button');
167198
}
199+
200+
// @breaking-change 8.0.0 Remove null check for _changeDetectorRef.
201+
if (this._list && _changeDetectorRef) {
202+
this._list._stateChanges.pipe(takeUntil(this._destroyed)).subscribe(() => {
203+
_changeDetectorRef.markForCheck();
204+
});
205+
}
168206
}
169207

170208
ngAfterContentInit() {
171209
setLines(this._lines, this._element);
172210
}
173211

212+
ngOnDestroy() {
213+
this._destroyed.next();
214+
this._destroyed.complete();
215+
}
216+
174217
/** Whether this list item should show a ripple effect when clicked. */
175218
_isRippleDisabled() {
176219
return !this._isInteractiveList || this.disableRipple ||

tools/public_api_guard/lib/list.d.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@ export declare const _MatSelectionListMixinBase: CanDisableRippleCtor & typeof M
88

99
export declare const MAT_SELECTION_LIST_VALUE_ACCESSOR: any;
1010

11-
export declare class MatList extends _MatListMixinBase implements CanDisableRipple {
11+
export declare class MatList extends _MatListMixinBase implements CanDisableRipple, OnChanges, OnDestroy {
12+
_stateChanges: Subject<void>;
1213
constructor(_elementRef?: ElementRef<HTMLElement> | undefined);
1314
_getListType(): 'list' | 'action-list' | null;
15+
ngOnChanges(): void;
16+
ngOnDestroy(): void;
1417
}
1518

1619
export declare class MatListAvatarCssMatStyler {
@@ -22,14 +25,15 @@ export declare class MatListBase {
2225
export declare class MatListIconCssMatStyler {
2326
}
2427

25-
export declare class MatListItem extends _MatListItemMixinBase implements AfterContentInit, CanDisableRipple {
28+
export declare class MatListItem extends _MatListItemMixinBase implements AfterContentInit, CanDisableRipple, OnDestroy {
2629
_avatar: MatListAvatarCssMatStyler;
2730
_icon: MatListIconCssMatStyler;
2831
_lines: QueryList<MatLine>;
29-
constructor(_element: ElementRef<HTMLElement>, navList?: MatNavList, list?: MatList);
32+
constructor(_element: ElementRef<HTMLElement>, navList?: MatNavList, list?: MatList, _changeDetectorRef?: ChangeDetectorRef);
3033
_getHostElement(): HTMLElement;
3134
_isRippleDisabled(): boolean;
3235
ngAfterContentInit(): void;
36+
ngOnDestroy(): void;
3337
}
3438

3539
export declare class MatListItemBase {
@@ -71,7 +75,10 @@ export declare class MatListOptionBase {
7175
export declare class MatListSubheaderCssMatStyler {
7276
}
7377

74-
export declare class MatNavList extends _MatListMixinBase implements CanDisableRipple {
78+
export declare class MatNavList extends _MatListMixinBase implements CanDisableRipple, OnChanges, OnDestroy {
79+
_stateChanges: Subject<void>;
80+
ngOnChanges(): void;
81+
ngOnDestroy(): void;
7582
}
7683

7784
export declare class MatSelectionList extends _MatSelectionListMixinBase implements FocusableOption, CanDisableRipple, AfterContentInit, ControlValueAccessor, OnDestroy {

0 commit comments

Comments
 (0)