Skip to content

Commit 76044e8

Browse files
crisbetojelbourn
authored andcommitted
fix(list): add ripples to action list items (#13799)
Sets up ripples for the items inside an action list and makes it a little easier to set ripples up in the future, if we add other list types. Fixes #13795.
1 parent 235add9 commit 76044e8

File tree

2 files changed

+101
-29
lines changed

2 files changed

+101
-29
lines changed

src/lib/list/list.spec.ts

Lines changed: 63 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -29,62 +29,62 @@ describe('MatList', () => {
2929
}));
3030

3131
it('should not apply any additional class to a list without lines', () => {
32-
let fixture = TestBed.createComponent(ListWithOneItem);
33-
let listItem = fixture.debugElement.query(By.css('mat-list-item'));
32+
const fixture = TestBed.createComponent(ListWithOneItem);
33+
const listItem = fixture.debugElement.query(By.css('mat-list-item'));
3434
fixture.detectChanges();
3535
expect(listItem.nativeElement.className).toBe('mat-list-item');
3636
});
3737

3838
it('should apply mat-2-line class to lists with two lines', () => {
39-
let fixture = TestBed.createComponent(ListWithTwoLineItem);
39+
const fixture = TestBed.createComponent(ListWithTwoLineItem);
4040
fixture.detectChanges();
4141

42-
let listItems = fixture.debugElement.children[0].queryAll(By.css('mat-list-item'));
42+
const listItems = fixture.debugElement.children[0].queryAll(By.css('mat-list-item'));
4343
expect(listItems[0].nativeElement.className).toContain('mat-2-line');
4444
expect(listItems[1].nativeElement.className).toContain('mat-2-line');
4545
});
4646

4747
it('should apply mat-3-line class to lists with three lines', () => {
48-
let fixture = TestBed.createComponent(ListWithThreeLineItem);
48+
const fixture = TestBed.createComponent(ListWithThreeLineItem);
4949
fixture.detectChanges();
5050

51-
let listItems = fixture.debugElement.children[0].queryAll(By.css('mat-list-item'));
51+
const listItems = fixture.debugElement.children[0].queryAll(By.css('mat-list-item'));
5252
expect(listItems[0].nativeElement.className).toContain('mat-3-line');
5353
expect(listItems[1].nativeElement.className).toContain('mat-3-line');
5454
});
5555

5656
it('should apply mat-multi-line class to lists with more than 3 lines', () => {
57-
let fixture = TestBed.createComponent(ListWithManyLines);
57+
const fixture = TestBed.createComponent(ListWithManyLines);
5858
fixture.detectChanges();
5959

60-
let listItems = fixture.debugElement.children[0].queryAll(By.css('mat-list-item'));
60+
const listItems = fixture.debugElement.children[0].queryAll(By.css('mat-list-item'));
6161
expect(listItems[0].nativeElement.className).toContain('mat-multi-line');
6262
expect(listItems[1].nativeElement.className).toContain('mat-multi-line');
6363
});
6464

6565
it('should apply a class to list items with avatars', () => {
66-
let fixture = TestBed.createComponent(ListWithAvatar);
66+
const fixture = TestBed.createComponent(ListWithAvatar);
6767
fixture.detectChanges();
6868

69-
let listItems = fixture.debugElement.children[0].queryAll(By.css('mat-list-item'));
69+
const listItems = fixture.debugElement.children[0].queryAll(By.css('mat-list-item'));
7070
expect(listItems[0].nativeElement.className).toContain('mat-list-item-with-avatar');
7171
expect(listItems[1].nativeElement.className).not.toContain('mat-list-item-with-avatar');
7272
});
7373

7474
it('should not clear custom classes provided by user', () => {
75-
let fixture = TestBed.createComponent(ListWithItemWithCssClass);
75+
const fixture = TestBed.createComponent(ListWithItemWithCssClass);
7676
fixture.detectChanges();
7777

78-
let listItems = fixture.debugElement.children[0].queryAll(By.css('mat-list-item'));
78+
const listItems = fixture.debugElement.children[0].queryAll(By.css('mat-list-item'));
7979
expect(listItems[0].nativeElement.classList.contains('test-class')).toBe(true);
8080
});
8181

8282
it('should update classes if number of lines change', () => {
83-
let fixture = TestBed.createComponent(ListWithDynamicNumberOfLines);
83+
const fixture = TestBed.createComponent(ListWithDynamicNumberOfLines);
8484
fixture.debugElement.componentInstance.showThirdLine = false;
8585
fixture.detectChanges();
8686

87-
let listItem = fixture.debugElement.children[0].query(By.css('mat-list-item'));
87+
const listItem = fixture.debugElement.children[0].query(By.css('mat-list-item'));
8888
expect(listItem.nativeElement.classList.length).toBe(2);
8989
expect(listItem.nativeElement.classList).toContain('mat-2-line');
9090
expect(listItem.nativeElement.classList).toContain('mat-list-item');
@@ -95,17 +95,17 @@ describe('MatList', () => {
9595
});
9696

9797
it('should add aria roles properly', () => {
98-
let fixture = TestBed.createComponent(ListWithMultipleItems);
98+
const fixture = TestBed.createComponent(ListWithMultipleItems);
9999
fixture.detectChanges();
100100

101-
let list = fixture.debugElement.children[0];
102-
let listItem = fixture.debugElement.children[0].query(By.css('mat-list-item'));
101+
const list = fixture.debugElement.children[0];
102+
const listItem = fixture.debugElement.children[0].query(By.css('mat-list-item'));
103103
expect(list.nativeElement.getAttribute('role')).toBeNull('Expect mat-list no role');
104104
expect(listItem.nativeElement.getAttribute('role')).toBeNull('Expect mat-list-item no role');
105105
});
106106

107107
it('should not show ripples for non-nav lists', () => {
108-
let fixture = TestBed.createComponent(ListWithOneAnchorItem);
108+
const fixture = TestBed.createComponent(ListWithOneAnchorItem);
109109
fixture.detectChanges();
110110

111111
const items: QueryList<MatListItem> = fixture.debugElement.componentInstance.listItems;
@@ -114,7 +114,7 @@ describe('MatList', () => {
114114
});
115115

116116
it('should allow disabling ripples for specific nav-list items', () => {
117-
let fixture = TestBed.createComponent(NavListWithOneAnchorItem);
117+
const fixture = TestBed.createComponent(NavListWithOneAnchorItem);
118118
fixture.detectChanges();
119119

120120
const items = fixture.componentInstance.listItems;
@@ -137,6 +137,29 @@ describe('MatList', () => {
137137
expect(items.length).toBeGreaterThan(0);
138138
});
139139

140+
it('should enable ripples for action lists by default', () => {
141+
const fixture = TestBed.createComponent(ActionListWithoutType);
142+
fixture.detectChanges();
143+
144+
const items = fixture.componentInstance.listItems;
145+
expect(items.toArray().every(item => !item._isRippleDisabled())).toBe(true);
146+
});
147+
148+
it('should allow disabling ripples for specific action list items', () => {
149+
const fixture = TestBed.createComponent(ActionListWithoutType);
150+
fixture.detectChanges();
151+
152+
const items = fixture.componentInstance.listItems.toArray();
153+
expect(items.length).toBeGreaterThan(0);
154+
155+
expect(items.every(item => !item._isRippleDisabled())).toBe(true);
156+
157+
fixture.componentInstance.disableItemRipple = true;
158+
fixture.detectChanges();
159+
160+
expect(items.every(item => item._isRippleDisabled())).toBe(true);
161+
});
162+
140163
it('should set default type attribute to button for action list', () => {
141164
const fixture = TestBed.createComponent(ActionListWithoutType);
142165
fixture.detectChanges();
@@ -154,7 +177,7 @@ describe('MatList', () => {
154177
});
155178

156179
it('should allow disabling ripples for the whole nav-list', () => {
157-
let fixture = TestBed.createComponent(NavListWithOneAnchorItem);
180+
const fixture = TestBed.createComponent(NavListWithOneAnchorItem);
158181
fixture.detectChanges();
159182

160183
const items = fixture.componentInstance.listItems;
@@ -168,6 +191,22 @@ describe('MatList', () => {
168191

169192
items.forEach(item => expect(item._isRippleDisabled()).toBe(true));
170193
});
194+
195+
it('should allow disabling ripples for the entire action list', () => {
196+
const fixture = TestBed.createComponent(ActionListWithoutType);
197+
fixture.detectChanges();
198+
199+
const items = fixture.componentInstance.listItems.toArray();
200+
expect(items.length).toBeGreaterThan(0);
201+
202+
expect(items.every(item => !item._isRippleDisabled())).toBe(true);
203+
204+
fixture.componentInstance.disableListRipple = true;
205+
fixture.detectChanges();
206+
207+
expect(items.every(item => item._isRippleDisabled())).toBe(true);
208+
});
209+
171210
});
172211

173212

@@ -205,13 +244,15 @@ class NavListWithOneAnchorItem extends BaseTestList {
205244
}
206245

207246
@Component({template: `
208-
<mat-action-list>
209-
<button mat-list-item>
247+
<mat-action-list [disableRipple]="disableListRipple">
248+
<button mat-list-item [disableRipple]="disableItemRipple">
210249
Paprika
211250
</button>
212251
</mat-action-list>`})
213252
class ActionListWithoutType extends BaseTestList {
214253
@ViewChildren(MatListItem) listItems: QueryList<MatListItem>;
254+
disableListRipple = false;
255+
disableItemRipple = false;
215256
}
216257

217258
@Component({template: `

src/lib/list/list.ts

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,34 @@ export class MatNavList extends _MatListMixinBase implements CanDisableRipple {}
6565
encapsulation: ViewEncapsulation.None,
6666
changeDetection: ChangeDetectionStrategy.OnPush,
6767
})
68-
export class MatList extends _MatListMixinBase implements CanDisableRipple {}
68+
export class MatList extends _MatListMixinBase implements CanDisableRipple {
69+
/**
70+
* @deprecated _elementRef parameter to be made required.
71+
* @breaking-change 8.0.0
72+
*/
73+
constructor(private _elementRef?: ElementRef<HTMLElement>) {
74+
super();
75+
}
76+
77+
_getListType(): 'list' | 'action-list' | null {
78+
const elementRef = this._elementRef;
79+
80+
// @breaking-change 8.0.0 Remove null check once _elementRef is a required param.
81+
if (elementRef) {
82+
const nodeName = elementRef.nativeElement.nodeName.toLowerCase();
83+
84+
if (nodeName === 'mat-list') {
85+
return 'list';
86+
}
87+
88+
if (nodeName === 'mat-action-list') {
89+
return 'action-list';
90+
}
91+
}
92+
93+
return null;
94+
}
95+
}
6996

7097
/**
7198
* Directive whose purpose is to add the mat- CSS styling to this selector.
@@ -115,22 +142,25 @@ export class MatListSubheaderCssMatStyler {}
115142
})
116143
export class MatListItem extends _MatListItemMixinBase implements AfterContentInit,
117144
CanDisableRipple {
118-
private _isNavList: boolean = false;
145+
private _isInteractiveList: boolean = false;
146+
private _list?: MatNavList | MatList;
119147

120148
@ContentChildren(MatLine) _lines: QueryList<MatLine>;
121149
@ContentChild(MatListAvatarCssMatStyler) _avatar: MatListAvatarCssMatStyler;
122150
@ContentChild(MatListIconCssMatStyler) _icon: MatListIconCssMatStyler;
123151

124152
constructor(private _element: ElementRef<HTMLElement>,
125-
@Optional() private _navList: MatNavList) {
153+
@Optional() navList?: MatNavList,
154+
@Optional() list?: MatList) {
126155
super();
127-
this._isNavList = !!_navList;
156+
this._isInteractiveList = !!(navList || (list && list._getListType() === 'action-list'));
157+
this._list = navList || list;
128158

129159
// If no type attributed is specified for <button>, set it to "button".
130160
// If a type attribute is already specified, do nothing.
131161
const element = this._getHostElement();
132-
if (element.nodeName && element.nodeName.toLowerCase() === 'button'
133-
&& !element.hasAttribute('type')) {
162+
163+
if (element.nodeName.toLowerCase() === 'button' && !element.hasAttribute('type')) {
134164
element.setAttribute('type', 'button');
135165
}
136166
}
@@ -141,7 +171,8 @@ export class MatListItem extends _MatListItemMixinBase implements AfterContentIn
141171

142172
/** Whether this list item should show a ripple effect when clicked. */
143173
_isRippleDisabled() {
144-
return !this._isNavList || this.disableRipple || this._navList.disableRipple;
174+
return !this._isInteractiveList || this.disableRipple ||
175+
!!(this._list && this._list.disableRipple);
145176
}
146177

147178
/** Retrieves the DOM element of the component host. */

0 commit comments

Comments
 (0)