Skip to content

Commit aa3360a

Browse files
dozingcatmmalerba
authored andcommitted
feat(list): add ripples to list items that are links (#930)
* feat(list): add ripples to list items that are links * Add MdRippleModule to imports
1 parent 05c865d commit aa3360a

File tree

5 files changed

+86
-8
lines changed

5 files changed

+86
-8
lines changed

src/lib/list/index.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {NgModule, ModuleWithProviders} from '@angular/core';
2-
import {MdLineModule, CompatibilityModule} from '../core';
2+
import {MdLineModule, MdRippleModule, CompatibilityModule} from '../core';
33
import {
44
MdList,
55
MdListItem,
@@ -10,11 +10,12 @@ import {
1010
MdNavListCssMatStyler,
1111
MdDividerCssMatStyler,
1212
MdListSubheaderCssMatStyler,
13+
MdNavListTokenSetter,
1314
} from './list';
1415

1516

1617
@NgModule({
17-
imports: [MdLineModule, CompatibilityModule],
18+
imports: [MdLineModule, MdRippleModule, CompatibilityModule],
1819
exports: [
1920
MdList,
2021
MdListItem,
@@ -26,7 +27,8 @@ import {
2627
MdListCssMatStyler,
2728
MdNavListCssMatStyler,
2829
MdDividerCssMatStyler,
29-
MdListSubheaderCssMatStyler
30+
MdListSubheaderCssMatStyler,
31+
MdNavListTokenSetter,
3032
],
3133
declarations: [
3234
MdList,
@@ -37,7 +39,8 @@ import {
3739
MdListCssMatStyler,
3840
MdNavListCssMatStyler,
3941
MdDividerCssMatStyler,
40-
MdListSubheaderCssMatStyler
42+
MdListSubheaderCssMatStyler,
43+
MdNavListTokenSetter,
4144
],
4245
})
4346
export class MdListModule {

src/lib/list/list-item.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
<div class="mat-list-item-content" [class.mat-list-item-focus]="_hasFocus">
1+
<div class="mat-list-item-content" [class.mat-list-item-focus]="_hasFocus"
2+
md-ripple [mdRippleDisabled]="!isRippleEnabled()">
23
<ng-content
34
select="[md-list-avatar],[md-list-icon], [mat-list-avatar], [mat-list-icon]"></ng-content>
45
<div class="mat-list-text"><ng-content select="[md-line], [mat-line]"></ng-content></div>

src/lib/list/list.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ $mat-dense-three-line-height: 76px;
4444
font-size: $font-size;
4545
height: $base-height;
4646
padding: 0 $mat-list-side-padding;
47+
position: relative;
4748
}
4849

4950
&.mat-list-item-avatar .mat-list-item-content {

src/lib/list/list.spec.ts

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {async, TestBed} from '@angular/core/testing';
2-
import {Component} from '@angular/core';
2+
import {Component, QueryList, ViewChildren} from '@angular/core';
33
import {By} from '@angular/platform-browser';
44
import {MdListItem, MdListModule} from './index';
55

@@ -19,6 +19,7 @@ describe('MdList', () => {
1919
ListWithDynamicNumberOfLines,
2020
ListWithMultipleItems,
2121
ListWithManyLines,
22+
NavListWithOneAnchorItem,
2223
],
2324
});
2425

@@ -114,6 +115,29 @@ describe('MdList', () => {
114115
expect(list.nativeElement.getAttribute('role')).toBe('list');
115116
expect(listItem.nativeElement.getAttribute('role')).toBe('listitem');
116117
});
118+
119+
it('should not show ripples for non-nav lists', () => {
120+
let fixture = TestBed.createComponent(ListWithOneAnchorItem);
121+
fixture.detectChanges();
122+
123+
const items: QueryList<MdListItem> = fixture.debugElement.componentInstance.listItems;
124+
expect(items.length).toBeGreaterThan(0);
125+
items.forEach(item => expect(item.isRippleEnabled()).toBe(false));
126+
});
127+
128+
it('should maybe show ripples for nav lists', () => {
129+
let fixture = TestBed.createComponent(NavListWithOneAnchorItem);
130+
fixture.detectChanges();
131+
132+
const items: QueryList<MdListItem> = fixture.debugElement.componentInstance.listItems;
133+
expect(items.length).toBeGreaterThan(0);
134+
// Ripples should be enabled by default, and can be disabled with a binding.
135+
items.forEach(item => expect(item.isRippleEnabled()).toBe(true));
136+
137+
fixture.debugElement.componentInstance.disableRipple = true;
138+
fixture.detectChanges();
139+
items.forEach(item => expect(item.isRippleEnabled()).toBe(false));
140+
});
117141
});
118142

119143

@@ -132,7 +156,22 @@ class BaseTestList {
132156
Paprika
133157
</a>
134158
</md-list>`})
135-
class ListWithOneAnchorItem extends BaseTestList { }
159+
class ListWithOneAnchorItem extends BaseTestList {
160+
// This needs to be declared directly on the class; if declared on the BaseTestList superclass,
161+
// it doesn't get populated.
162+
@ViewChildren(MdListItem) listItems: QueryList<MdListItem>;
163+
}
164+
165+
@Component({template: `
166+
<md-nav-list>
167+
<a md-list-item [disableRipple]="disableRipple">
168+
Paprika
169+
</a>
170+
</md-nav-list>`})
171+
class NavListWithOneAnchorItem extends BaseTestList {
172+
@ViewChildren(MdListItem) listItems: QueryList<MdListItem>;
173+
disableRipple: boolean = false;
174+
}
136175

137176
@Component({template: `
138177
<md-list>

src/lib/list/list.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import {
66
QueryList,
77
Directive,
88
ElementRef,
9+
Inject,
10+
Input,
11+
OpaqueToken,
12+
Optional,
913
Renderer,
1014
AfterContentInit,
1115
} from '@angular/core';
@@ -16,13 +20,23 @@ import {MdLine, MdLineSetter} from '../core';
1620
})
1721
export class MdListDivider {}
1822

23+
/**
24+
* Token used to inject the list type into child MdListItem components so they can know whether
25+
* they're in a nav list (and thus should use an MdRipple).
26+
*/
27+
export const LIST_TYPE_TOKEN = new OpaqueToken('list_type');
28+
29+
const NORMAL_LIST_TYPE = 'normal_list_type';
30+
const NAV_LIST_TYPE = 'nav_list_type';
31+
1932
@Component({
2033
moduleId: module.id,
2134
selector: 'md-list, mat-list, md-nav-list, mat-nav-list',
2235
host: {
2336
'role': 'list'},
2437
template: '<ng-content></ng-content>',
2538
styleUrls: ['list.css'],
39+
providers: [{ provide: LIST_TYPE_TOKEN, useValue: NORMAL_LIST_TYPE }],
2640
encapsulation: ViewEncapsulation.None
2741
})
2842
export class MdList {}
@@ -51,6 +65,15 @@ export class MdListCssMatStyler {}
5165
})
5266
export class MdNavListCssMatStyler {}
5367

68+
/**
69+
* Directive to set the ListType token to NAV_LIST_TYPE.
70+
*/
71+
@Directive({
72+
selector: 'md-nav-list, mat-nav-list',
73+
providers: [{ provide: LIST_TYPE_TOKEN, useValue: NAV_LIST_TYPE }],
74+
})
75+
export class MdNavListTokenSetter {}
76+
5477
/**
5578
* Directive whose purpose is to add the mat- CSS styling to this selector.
5679
* @docs-private
@@ -112,6 +135,11 @@ export class MdListSubheaderCssMatStyler {}
112135
encapsulation: ViewEncapsulation.None
113136
})
114137
export class MdListItem implements AfterContentInit {
138+
/**
139+
* Whether the ripple effect on click should be disabled. This applies only to list items that
140+
* are children of an md-nav-list; md-list items never have ripples.
141+
*/
142+
@Input() disableRipple: boolean = false;
115143
_hasFocus: boolean = false;
116144

117145
private _lineSetter: MdLineSetter;
@@ -124,12 +152,18 @@ export class MdListItem implements AfterContentInit {
124152
this._element.nativeElement, 'mat-list-item-avatar', avatar != null);
125153
}
126154

127-
constructor(private _renderer: Renderer, private _element: ElementRef) {}
155+
constructor(private _renderer: Renderer, private _element: ElementRef,
156+
@Optional() @Inject(LIST_TYPE_TOKEN) private _listType: string) {}
128157

129158
ngAfterContentInit() {
130159
this._lineSetter = new MdLineSetter(this._lines, this._renderer, this._element);
131160
}
132161

162+
/** Whether this list item should show a ripple effect when clicked. */
163+
isRippleEnabled() {
164+
return !this.disableRipple && (this._listType === NAV_LIST_TYPE);
165+
}
166+
133167
_handleFocus() {
134168
this._hasFocus = true;
135169
}

0 commit comments

Comments
 (0)