Skip to content

Commit 6f4c198

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 6f4c198

File tree

2 files changed

+105
-5
lines changed

2 files changed

+105
-5
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 ||

0 commit comments

Comments
 (0)