diff --git a/src/lib/list/list.scss b/src/lib/list/list.scss
index 0b7f34621ead..3751684ec2a0 100644
--- a/src/lib/list/list.scss
+++ b/src/lib/list/list.scss
@@ -44,6 +44,7 @@ $mat-dense-three-line-height: 76px;
font-size: $font-size;
height: $base-height;
padding: 0 $mat-list-side-padding;
+ position: relative;
}
&.mat-list-item-avatar .mat-list-item-content {
diff --git a/src/lib/list/list.spec.ts b/src/lib/list/list.spec.ts
index db1937370472..b7a46ec95cdb 100644
--- a/src/lib/list/list.spec.ts
+++ b/src/lib/list/list.spec.ts
@@ -1,5 +1,5 @@
import {async, TestBed} from '@angular/core/testing';
-import {Component} from '@angular/core';
+import {Component, QueryList, ViewChildren} from '@angular/core';
import {By} from '@angular/platform-browser';
import {MdListItem, MdListModule} from './index';
@@ -19,6 +19,7 @@ describe('MdList', () => {
ListWithDynamicNumberOfLines,
ListWithMultipleItems,
ListWithManyLines,
+ NavListWithOneAnchorItem,
],
});
@@ -114,6 +115,29 @@ describe('MdList', () => {
expect(list.nativeElement.getAttribute('role')).toBe('list');
expect(listItem.nativeElement.getAttribute('role')).toBe('listitem');
});
+
+ it('should not show ripples for non-nav lists', () => {
+ let fixture = TestBed.createComponent(ListWithOneAnchorItem);
+ fixture.detectChanges();
+
+ const items: QueryList
= fixture.debugElement.componentInstance.listItems;
+ expect(items.length).toBeGreaterThan(0);
+ items.forEach(item => expect(item.isRippleEnabled()).toBe(false));
+ });
+
+ it('should maybe show ripples for nav lists', () => {
+ let fixture = TestBed.createComponent(NavListWithOneAnchorItem);
+ fixture.detectChanges();
+
+ const items: QueryList = fixture.debugElement.componentInstance.listItems;
+ expect(items.length).toBeGreaterThan(0);
+ // Ripples should be enabled by default, and can be disabled with a binding.
+ items.forEach(item => expect(item.isRippleEnabled()).toBe(true));
+
+ fixture.debugElement.componentInstance.disableRipple = true;
+ fixture.detectChanges();
+ items.forEach(item => expect(item.isRippleEnabled()).toBe(false));
+ });
});
@@ -132,7 +156,22 @@ class BaseTestList {
Paprika
`})
-class ListWithOneAnchorItem extends BaseTestList { }
+class ListWithOneAnchorItem extends BaseTestList {
+ // This needs to be declared directly on the class; if declared on the BaseTestList superclass,
+ // it doesn't get populated.
+ @ViewChildren(MdListItem) listItems: QueryList;
+}
+
+@Component({template: `
+
+
+ Paprika
+
+ `})
+class NavListWithOneAnchorItem extends BaseTestList {
+ @ViewChildren(MdListItem) listItems: QueryList;
+ disableRipple: boolean = false;
+}
@Component({template: `
diff --git a/src/lib/list/list.ts b/src/lib/list/list.ts
index f5a79b7f0778..5a7df8fe1d76 100644
--- a/src/lib/list/list.ts
+++ b/src/lib/list/list.ts
@@ -6,6 +6,10 @@ import {
QueryList,
Directive,
ElementRef,
+ Inject,
+ Input,
+ OpaqueToken,
+ Optional,
Renderer,
AfterContentInit,
} from '@angular/core';
@@ -16,6 +20,15 @@ import {MdLine, MdLineSetter} from '../core';
})
export class MdListDivider {}
+/**
+ * Token used to inject the list type into child MdListItem components so they can know whether
+ * they're in a nav list (and thus should use an MdRipple).
+ */
+export const LIST_TYPE_TOKEN = new OpaqueToken('list_type');
+
+const NORMAL_LIST_TYPE = 'normal_list_type';
+const NAV_LIST_TYPE = 'nav_list_type';
+
@Component({
moduleId: module.id,
selector: 'md-list, mat-list, md-nav-list, mat-nav-list',
@@ -23,6 +36,7 @@ export class MdListDivider {}
'role': 'list'},
template: '',
styleUrls: ['list.css'],
+ providers: [{ provide: LIST_TYPE_TOKEN, useValue: NORMAL_LIST_TYPE }],
encapsulation: ViewEncapsulation.None
})
export class MdList {}
@@ -51,6 +65,15 @@ export class MdListCssMatStyler {}
})
export class MdNavListCssMatStyler {}
+/**
+ * Directive to set the ListType token to NAV_LIST_TYPE.
+ */
+@Directive({
+ selector: 'md-nav-list, mat-nav-list',
+ providers: [{ provide: LIST_TYPE_TOKEN, useValue: NAV_LIST_TYPE }],
+})
+export class MdNavListTokenSetter {}
+
/**
* Directive whose purpose is to add the mat- CSS styling to this selector.
* @docs-private
@@ -112,6 +135,11 @@ export class MdListSubheaderCssMatStyler {}
encapsulation: ViewEncapsulation.None
})
export class MdListItem implements AfterContentInit {
+ /**
+ * Whether the ripple effect on click should be disabled. This applies only to list items that
+ * are children of an md-nav-list; md-list items never have ripples.
+ */
+ @Input() disableRipple: boolean = false;
_hasFocus: boolean = false;
private _lineSetter: MdLineSetter;
@@ -124,12 +152,18 @@ export class MdListItem implements AfterContentInit {
this._element.nativeElement, 'mat-list-item-avatar', avatar != null);
}
- constructor(private _renderer: Renderer, private _element: ElementRef) {}
+ constructor(private _renderer: Renderer, private _element: ElementRef,
+ @Optional() @Inject(LIST_TYPE_TOKEN) private _listType: string) {}
ngAfterContentInit() {
this._lineSetter = new MdLineSetter(this._lines, this._renderer, this._element);
}
+ /** Whether this list item should show a ripple effect when clicked. */
+ isRippleEnabled() {
+ return !this.disableRipple && (this._listType === NAV_LIST_TYPE);
+ }
+
_handleFocus() {
this._hasFocus = true;
}