Skip to content

Commit 1880478

Browse files
committed
fix(list): allow for list and list items to be disabled
Adds the ability to disable all variants of the list. Currently it's only the selection list that can be disabled. Fixes #17574.
1 parent 93dc69f commit 1880478

File tree

4 files changed

+83
-15
lines changed

4 files changed

+83
-15
lines changed

src/material/list/list.scss

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,8 +278,8 @@ mat-action-list {
278278
font: inherit;
279279
outline: inherit;
280280
-webkit-tap-highlight-color: transparent;
281-
282281
text-align: left;
282+
283283
[dir='rtl'] & {
284284
text-align: right;
285285
}
@@ -300,6 +300,10 @@ mat-action-list {
300300
outline: none;
301301
}
302302

303+
.mat-list-item-disabled {
304+
pointer-events: none;
305+
}
306+
303307
@include cdk-high-contrast {
304308
.mat-selection-list:focus {
305309
outline-style: dotted;

src/material/list/list.spec.ts

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ describe('MatList', () => {
1616
ListWithOneAnchorItem, ListWithOneItem, ListWithTwoLineItem, ListWithThreeLineItem,
1717
ListWithAvatar, ListWithItemWithCssClass, ListWithDynamicNumberOfLines,
1818
ListWithMultipleItems, ListWithManyLines, NavListWithOneAnchorItem, ActionListWithoutType,
19-
ActionListWithType, ListWithIndirectDescendantLines
19+
ActionListWithType, ListWithIndirectDescendantLines, ListWithDisabledItems,
2020
],
2121
});
2222

@@ -275,8 +275,40 @@ describe('MatList', () => {
275275
expect(listItems[0].nativeElement.className).toContain('mat-2-line');
276276
expect(listItems[1].nativeElement.className).toContain('mat-2-line');
277277
});
278-
});
279278

279+
it('should be able to disable a single list item', () => {
280+
const fixture = TestBed.createComponent(ListWithDisabledItems);
281+
const listItems: HTMLElement[] =
282+
Array.from(fixture.nativeElement.querySelectorAll('mat-list-item'));
283+
fixture.detectChanges();
284+
285+
expect(listItems.map(item => {
286+
return item.classList.contains('mat-list-item-disabled');
287+
})).toEqual([false, false, false]);
288+
289+
fixture.componentInstance.firstItemDisabled = true;
290+
fixture.detectChanges();
291+
292+
expect(listItems.map(item => {
293+
return item.classList.contains('mat-list-item-disabled');
294+
})).toEqual([true, false, false]);
295+
});
296+
297+
it('should be able to disable the entire list', () => {
298+
const fixture = TestBed.createComponent(ListWithDisabledItems);
299+
const listItems: HTMLElement[] =
300+
Array.from(fixture.nativeElement.querySelectorAll('mat-list-item'));
301+
fixture.detectChanges();
302+
303+
expect(listItems.every(item => item.classList.contains('mat-list-item-disabled'))).toBe(false);
304+
305+
fixture.componentInstance.listDisabled = true;
306+
fixture.detectChanges();
307+
308+
expect(listItems.every(item => item.classList.contains('mat-list-item-disabled'))).toBe(true);
309+
});
310+
311+
});
280312

281313
class BaseTestList {
282314
items: any[] = [
@@ -425,3 +457,15 @@ class ListWithMultipleItems extends BaseTestList { }
425457
})
426458
class ListWithIndirectDescendantLines extends BaseTestList {
427459
}
460+
461+
462+
@Component({template: `
463+
<mat-list [disabled]="listDisabled">
464+
<mat-list-item [disabled]="firstItemDisabled">One</mat-list-item>
465+
<mat-list-item>Two</mat-list-item>
466+
<mat-list-item>Three</mat-list-item>
467+
</mat-list>`})
468+
class ListWithDisabledItems {
469+
firstItemDisabled = false;
470+
listDisabled = false;
471+
}

src/material/list/list.ts

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,27 @@ import {
2020
OnChanges,
2121
OnDestroy,
2222
ChangeDetectorRef,
23+
Input,
2324
} from '@angular/core';
2425
import {
26+
CanDisable,
27+
CanDisableCtor,
2528
CanDisableRipple,
2629
CanDisableRippleCtor,
2730
MatLine,
2831
setLines,
2932
mixinDisableRipple,
33+
mixinDisabled,
3034
} from '@angular/material/core';
35+
import {coerceBooleanProperty} from '@angular/cdk/coercion';
3136
import {Subject} from 'rxjs';
3237
import {takeUntil} from 'rxjs/operators';
3338

3439
// Boilerplate for applying mixins to MatList.
3540
/** @docs-private */
3641
class MatListBase {}
37-
const _MatListMixinBase: CanDisableRippleCtor & typeof MatListBase =
38-
mixinDisableRipple(MatListBase);
42+
const _MatListMixinBase: CanDisableRippleCtor & CanDisableCtor & typeof MatListBase =
43+
mixinDisabled(mixinDisableRipple(MatListBase));
3944

4045
// Boilerplate for applying mixins to MatListItem.
4146
/** @docs-private */
@@ -52,12 +57,12 @@ const _MatListItemMixinBase: CanDisableRippleCtor & typeof MatListItemBase =
5257
},
5358
templateUrl: 'list.html',
5459
styleUrls: ['list.css'],
55-
inputs: ['disableRipple'],
60+
inputs: ['disableRipple', 'disabled'],
5661
encapsulation: ViewEncapsulation.None,
5762
changeDetection: ChangeDetectionStrategy.OnPush,
5863
})
59-
export class MatNavList extends _MatListMixinBase implements CanDisableRipple, OnChanges,
60-
OnDestroy {
64+
export class MatNavList extends _MatListMixinBase implements CanDisable, CanDisableRipple,
65+
OnChanges, OnDestroy {
6166
/** Emits when the state of the list changes. */
6267
_stateChanges = new Subject<void>();
6368

@@ -80,11 +85,12 @@ export class MatNavList extends _MatListMixinBase implements CanDisableRipple, O
8085
'class': 'mat-list mat-list-base'
8186
},
8287
styleUrls: ['list.css'],
83-
inputs: ['disableRipple'],
88+
inputs: ['disableRipple', 'disabled'],
8489
encapsulation: ViewEncapsulation.None,
8590
changeDetection: ChangeDetectionStrategy.OnPush,
8691
})
87-
export class MatList extends _MatListMixinBase implements CanDisableRipple, OnChanges, OnDestroy {
92+
export class MatList extends _MatListMixinBase implements CanDisable, CanDisableRipple, OnChanges,
93+
OnDestroy {
8894
/** Emits when the state of the list changes. */
8995
_stateChanges = new Subject<void>();
9096

@@ -119,6 +125,7 @@ export class MatList extends _MatListMixinBase implements CanDisableRipple, OnCh
119125
}
120126

121127
static ngAcceptInputType_disableRipple: boolean | string | null | undefined;
128+
static ngAcceptInputType_disabled: boolean | string | null | undefined;
122129
}
123130

124131
/**
@@ -157,6 +164,7 @@ export class MatListSubheaderCssMatStyler {}
157164
exportAs: 'matListItem',
158165
host: {
159166
'class': 'mat-list-item',
167+
'[class.mat-list-item-disabled]': 'disabled',
160168
// @breaking-change 8.0.0 Remove `mat-list-item-avatar` in favor of `mat-list-item-with-avatar`.
161169
'[class.mat-list-item-avatar]': '_avatar || _icon',
162170
'[class.mat-list-item-with-avatar]': '_avatar || _icon',
@@ -201,6 +209,14 @@ export class MatListItem extends _MatListItemMixinBase implements AfterContentIn
201209
}
202210
}
203211

212+
/** Whether the option is disabled. */
213+
@Input()
214+
get disabled() { return this._disabled || !!(this._list && this._list.disabled); }
215+
set disabled(value: boolean) {
216+
this._disabled = coerceBooleanProperty(value);
217+
}
218+
private _disabled = false;
219+
204220
ngAfterContentInit() {
205221
setLines(this._lines, this._element);
206222
}
@@ -222,4 +238,5 @@ export class MatListItem extends _MatListItemMixinBase implements AfterContentIn
222238
}
223239

224240
static ngAcceptInputType_disableRipple: boolean | string | null | undefined;
241+
static ngAcceptInputType_disabled: boolean | string | null | undefined;
225242
}

tools/public_api_guard/material/list.d.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
export declare const MAT_SELECTION_LIST_VALUE_ACCESSOR: any;
22

3-
export declare class MatList extends _MatListMixinBase implements CanDisableRipple, OnChanges, OnDestroy {
3+
export declare class MatList extends _MatListMixinBase implements CanDisable, CanDisableRipple, OnChanges, OnDestroy {
44
_stateChanges: Subject<void>;
55
constructor(_elementRef: ElementRef<HTMLElement>);
66
_getListType(): 'list' | 'action-list' | null;
77
ngOnChanges(): void;
88
ngOnDestroy(): void;
99
static ngAcceptInputType_disableRipple: boolean | string | null | undefined;
10-
static ɵcmp: i0.ɵɵComponentDefWithMeta<MatList, "mat-list, mat-action-list", ["matList"], { 'disableRipple': "disableRipple" }, {}, never>;
10+
static ngAcceptInputType_disabled: boolean | string | null | undefined;
11+
static ɵcmp: i0.ɵɵComponentDefWithMeta<MatList, "mat-list, mat-action-list", ["matList"], { 'disableRipple': "disableRipple", 'disabled': "disabled" }, {}, never>;
1112
static ɵfac: i0.ɵɵFactoryDef<MatList>;
1213
}
1314

@@ -25,13 +26,15 @@ export declare class MatListItem extends _MatListItemMixinBase implements AfterC
2526
_avatar: MatListAvatarCssMatStyler;
2627
_icon: MatListIconCssMatStyler;
2728
_lines: QueryList<MatLine>;
29+
disabled: boolean;
2830
constructor(_element: ElementRef<HTMLElement>, _changeDetectorRef: ChangeDetectorRef, navList?: MatNavList, list?: MatList);
2931
_getHostElement(): HTMLElement;
3032
_isRippleDisabled(): boolean;
3133
ngAfterContentInit(): void;
3234
ngOnDestroy(): void;
3335
static ngAcceptInputType_disableRipple: boolean | string | null | undefined;
34-
static ɵcmp: i0.ɵɵComponentDefWithMeta<MatListItem, "mat-list-item, a[mat-list-item], button[mat-list-item]", ["matListItem"], { 'disableRipple': "disableRipple" }, {}, ["_avatar", "_icon", "_lines"]>;
36+
static ngAcceptInputType_disabled: boolean | string | null | undefined;
37+
static ɵcmp: i0.ɵɵComponentDefWithMeta<MatListItem, "mat-list-item, a[mat-list-item], button[mat-list-item]", ["matListItem"], { 'disableRipple': "disableRipple", 'disabled': "disabled" }, {}, ["_avatar", "_icon", "_lines"]>;
3538
static ɵfac: i0.ɵɵFactoryDef<MatListItem>;
3639
}
3740

@@ -78,12 +81,12 @@ export declare class MatListSubheaderCssMatStyler {
7881
static ɵfac: i0.ɵɵFactoryDef<MatListSubheaderCssMatStyler>;
7982
}
8083

81-
export declare class MatNavList extends _MatListMixinBase implements CanDisableRipple, OnChanges, OnDestroy {
84+
export declare class MatNavList extends _MatListMixinBase implements CanDisable, CanDisableRipple, OnChanges, OnDestroy {
8285
_stateChanges: Subject<void>;
8386
ngOnChanges(): void;
8487
ngOnDestroy(): void;
8588
static ngAcceptInputType_disableRipple: boolean | string | null | undefined;
86-
static ɵcmp: i0.ɵɵComponentDefWithMeta<MatNavList, "mat-nav-list", ["matNavList"], { 'disableRipple': "disableRipple" }, {}, never>;
89+
static ɵcmp: i0.ɵɵComponentDefWithMeta<MatNavList, "mat-nav-list", ["matNavList"], { 'disableRipple': "disableRipple", 'disabled': "disabled" }, {}, never>;
8790
static ɵfac: i0.ɵɵFactoryDef<MatNavList>;
8891
}
8992

0 commit comments

Comments
 (0)