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.
This commit is contained in:
vivian-hu
2018-09-11 10:41:20 -07:00
committed by Jeremy Elbourn
parent 8ee5fb66c6
commit 69fa76236a
7 changed files with 99 additions and 3 deletions
+9
View File
@@ -104,6 +104,15 @@
</mat-nav-list>
</div>
<div>
<h2>Action list</h2>
<mat-action-list>
<button mat-list-item *ngFor="let link of links" (click)="alertItem(link.name)">
{{link.name}}
</button>
</mat-action-list>
</div>
<div>
<h2>Selection list</h2>
-1
View File
@@ -6,7 +6,6 @@
border: 1px solid rgba(0, 0, 0, 0.12);
width: 350px;
margin: 20px 20px 0 0;
}
h2 {
margin-top: 20px;
+4
View File
@@ -69,4 +69,8 @@ export class ListDemo {
this.selectedOptions = values;
this.modelChangeEventCount++;
}
alertItem(msg: string) {
alert(msg);
}
}
+14
View File
@@ -42,6 +42,20 @@ element in an `<mat-list-item>`.
</mat-nav-list>
```
### Action lists
Use the `<mat-action-list>` element when each item in the list performs some _action_. Each item
in an action list is a `<button>` element.
Simple action lists can use the `mat-list-item` attribute on button tag elements directly:
```html
<mat-action-list>
<button mat-list-item (click)="save()"> Save </button>
<button mat-list-item (click)="undo()"> Undo </button>
</mat-action-list>
```
### Selection lists
A selection list provides an interface for selecting values, where each list item is an option.
+16
View File
@@ -268,6 +268,22 @@ $mat-list-item-inset-divider-offset: 72px;
}
}
mat-action-list {
//remove the native button look and make it look like a list item
button {
background: none;
color: inherit;
border: none;
font: inherit;
outline: inherit;
}
.mat-list-item {
cursor: pointer;
outline: inherit;
}
}
.mat-list-option:not(.mat-list-item-disabled) {
cursor: pointer;
outline: none;
+46
View File
@@ -20,6 +20,8 @@ describe('MatList', () => {
ListWithMultipleItems,
ListWithManyLines,
NavListWithOneAnchorItem,
ActionListWithoutType,
ActionListWithType
],
});
@@ -144,6 +146,30 @@ describe('MatList', () => {
items.forEach(item => expect(item._isRippleDisabled()).toBe(true));
});
it('should create an action list', () => {
const fixture = TestBed.createComponent(ActionListWithoutType);
fixture.detectChanges();
const items = fixture.componentInstance.listItems;
expect(items.length).toBeGreaterThan(0);
});
it('should set default type attribute to button for action list', () => {
const fixture = TestBed.createComponent(ActionListWithoutType);
fixture.detectChanges();
const listItemEl = fixture.debugElement.query(By.css('.mat-list-item'));
expect(listItemEl.nativeElement.getAttribute('type')).toBe('button');
});
it('should not change type attribute if it is already specified', () => {
const fixture = TestBed.createComponent(ActionListWithType);
fixture.detectChanges();
const listItemEl = fixture.debugElement.query(By.css('.mat-list-item'));
expect(listItemEl.nativeElement.getAttribute('type')).toBe('submit');
});
it('should allow disabling ripples for the whole nav-list', () => {
let fixture = TestBed.createComponent(NavListWithOneAnchorItem);
fixture.detectChanges();
@@ -195,6 +221,26 @@ class NavListWithOneAnchorItem extends BaseTestList {
disableListRipple: boolean = false;
}
@Component({template: `
<mat-action-list>
<button mat-list-item>
Paprika
</button>
</mat-action-list>`})
class ActionListWithoutType extends BaseTestList {
@ViewChildren(MatListItem) listItems: QueryList<MatListItem>;
}
@Component({template: `
<mat-action-list>
<button mat-list-item type="submit">
Paprika
</button>
</mat-action-list>`})
class ActionListWithType extends BaseTestList {
@ViewChildren(MatListItem) listItems: QueryList<MatListItem>;
}
@Component({template: `
<mat-list>
<mat-list-item>
+10 -2
View File
@@ -56,7 +56,7 @@ export class MatNavList extends _MatListMixinBase implements CanDisableRipple {}
@Component({
moduleId: module.id,
selector: 'mat-list',
selector: 'mat-list, mat-action-list',
exportAs: 'matList',
templateUrl: 'list.html',
host: {'class': 'mat-list'},
@@ -100,7 +100,7 @@ export class MatListSubheaderCssMatStyler {}
/** An item within a Material Design list. */
@Component({
moduleId: module.id,
selector: 'mat-list-item, a[mat-list-item]',
selector: 'mat-list-item, a[mat-list-item], button[mat-list-item]',
exportAs: 'matListItem',
host: {
'class': 'mat-list-item',
@@ -127,6 +127,14 @@ export class MatListItem extends _MatListItemMixinBase implements AfterContentIn
@Optional() private _navList: MatNavList) {
super();
this._isNavList = !!_navList;
// If no type attributed is specified for <button>, set it to "button".
// If a type attribute is already specified, do nothing.
const element = this._getHostElement();
if (element.nodeName && element.nodeName.toLowerCase() === 'button'
&& !element.hasAttribute('type')) {
element.setAttribute('type', 'button');
}
}
ngAfterContentInit() {