Skip to content

Commit 69fa762

Browse files
vivian-hu-zzjelbourn
authored andcommitted
feat(list): add new '<mat-action-list>' (#12415)
An action-list is a list where each list item is a native '<button>' element, meant to be used in cases where each list item has a single action with no sub-action, navigation or selection.
1 parent 8ee5fb6 commit 69fa762

File tree

7 files changed

+99
-3
lines changed

7 files changed

+99
-3
lines changed

src/demo-app/list/list-demo.html

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,15 @@ <h2>Nav lists</h2>
104104
</mat-nav-list>
105105
</div>
106106

107+
<div>
108+
<h2>Action list</h2>
109+
<mat-action-list>
110+
<button mat-list-item *ngFor="let link of links" (click)="alertItem(link.name)">
111+
{{link.name}}
112+
</button>
113+
</mat-action-list>
114+
</div>
115+
107116
<div>
108117
<h2>Selection list</h2>
109118

src/demo-app/list/list-demo.scss

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
border: 1px solid rgba(0, 0, 0, 0.12);
77
width: 350px;
88
margin: 20px 20px 0 0;
9-
109
}
1110
h2 {
1211
margin-top: 20px;

src/demo-app/list/list-demo.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,8 @@ export class ListDemo {
6969
this.selectedOptions = values;
7070
this.modelChangeEventCount++;
7171
}
72+
73+
alertItem(msg: string) {
74+
alert(msg);
75+
}
7276
}

src/lib/list/list.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,20 @@ element in an `<mat-list-item>`.
4242
</mat-nav-list>
4343
```
4444

45+
### Action lists
46+
47+
Use the `<mat-action-list>` element when each item in the list performs some _action_. Each item
48+
in an action list is a `<button>` element.
49+
50+
Simple action lists can use the `mat-list-item` attribute on button tag elements directly:
51+
52+
```html
53+
<mat-action-list>
54+
<button mat-list-item (click)="save()"> Save </button>
55+
<button mat-list-item (click)="undo()"> Undo </button>
56+
</mat-action-list>
57+
```
58+
4559
### Selection lists
4660
A selection list provides an interface for selecting values, where each list item is an option.
4761

src/lib/list/list.scss

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,22 @@ $mat-list-item-inset-divider-offset: 72px;
268268
}
269269
}
270270

271+
mat-action-list {
272+
//remove the native button look and make it look like a list item
273+
button {
274+
background: none;
275+
color: inherit;
276+
border: none;
277+
font: inherit;
278+
outline: inherit;
279+
}
280+
281+
.mat-list-item {
282+
cursor: pointer;
283+
outline: inherit;
284+
}
285+
}
286+
271287
.mat-list-option:not(.mat-list-item-disabled) {
272288
cursor: pointer;
273289
outline: none;

src/lib/list/list.spec.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ describe('MatList', () => {
2020
ListWithMultipleItems,
2121
ListWithManyLines,
2222
NavListWithOneAnchorItem,
23+
ActionListWithoutType,
24+
ActionListWithType
2325
],
2426
});
2527

@@ -144,6 +146,30 @@ describe('MatList', () => {
144146
items.forEach(item => expect(item._isRippleDisabled()).toBe(true));
145147
});
146148

149+
it('should create an action list', () => {
150+
const fixture = TestBed.createComponent(ActionListWithoutType);
151+
fixture.detectChanges();
152+
153+
const items = fixture.componentInstance.listItems;
154+
expect(items.length).toBeGreaterThan(0);
155+
});
156+
157+
it('should set default type attribute to button for action list', () => {
158+
const fixture = TestBed.createComponent(ActionListWithoutType);
159+
fixture.detectChanges();
160+
161+
const listItemEl = fixture.debugElement.query(By.css('.mat-list-item'));
162+
expect(listItemEl.nativeElement.getAttribute('type')).toBe('button');
163+
});
164+
165+
it('should not change type attribute if it is already specified', () => {
166+
const fixture = TestBed.createComponent(ActionListWithType);
167+
fixture.detectChanges();
168+
169+
const listItemEl = fixture.debugElement.query(By.css('.mat-list-item'));
170+
expect(listItemEl.nativeElement.getAttribute('type')).toBe('submit');
171+
});
172+
147173
it('should allow disabling ripples for the whole nav-list', () => {
148174
let fixture = TestBed.createComponent(NavListWithOneAnchorItem);
149175
fixture.detectChanges();
@@ -195,6 +221,26 @@ class NavListWithOneAnchorItem extends BaseTestList {
195221
disableListRipple: boolean = false;
196222
}
197223

224+
@Component({template: `
225+
<mat-action-list>
226+
<button mat-list-item>
227+
Paprika
228+
</button>
229+
</mat-action-list>`})
230+
class ActionListWithoutType extends BaseTestList {
231+
@ViewChildren(MatListItem) listItems: QueryList<MatListItem>;
232+
}
233+
234+
@Component({template: `
235+
<mat-action-list>
236+
<button mat-list-item type="submit">
237+
Paprika
238+
</button>
239+
</mat-action-list>`})
240+
class ActionListWithType extends BaseTestList {
241+
@ViewChildren(MatListItem) listItems: QueryList<MatListItem>;
242+
}
243+
198244
@Component({template: `
199245
<mat-list>
200246
<mat-list-item>

src/lib/list/list.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export class MatNavList extends _MatListMixinBase implements CanDisableRipple {}
5656

5757
@Component({
5858
moduleId: module.id,
59-
selector: 'mat-list',
59+
selector: 'mat-list, mat-action-list',
6060
exportAs: 'matList',
6161
templateUrl: 'list.html',
6262
host: {'class': 'mat-list'},
@@ -100,7 +100,7 @@ export class MatListSubheaderCssMatStyler {}
100100
/** An item within a Material Design list. */
101101
@Component({
102102
moduleId: module.id,
103-
selector: 'mat-list-item, a[mat-list-item]',
103+
selector: 'mat-list-item, a[mat-list-item], button[mat-list-item]',
104104
exportAs: 'matListItem',
105105
host: {
106106
'class': 'mat-list-item',
@@ -127,6 +127,14 @@ export class MatListItem extends _MatListItemMixinBase implements AfterContentIn
127127
@Optional() private _navList: MatNavList) {
128128
super();
129129
this._isNavList = !!_navList;
130+
131+
// If no type attributed is specified for <button>, set it to "button".
132+
// If a type attribute is already specified, do nothing.
133+
const element = this._getHostElement();
134+
if (element.nodeName && element.nodeName.toLowerCase() === 'button'
135+
&& !element.hasAttribute('type')) {
136+
element.setAttribute('type', 'button');
137+
}
130138
}
131139

132140
ngAfterContentInit() {

0 commit comments

Comments
 (0)