Skip to content

Commit 5008f3a

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 0f6cd05 commit 5008f3a

File tree

4 files changed

+94
-11
lines changed

4 files changed

+94
-11
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: 59 additions & 1 deletion
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,6 +275,52 @@ describe('MatList', () => {
275275
expect(listItems[0].nativeElement.className).toContain('mat-2-line');
276276
expect(listItems[1].nativeElement.className).toContain('mat-2-line');
277277
});
278+
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+
it('should set aria-disabled on a disabled item', () => {
312+
const fixture = TestBed.createComponent(ListWithDisabledItems);
313+
const item: HTMLElement = fixture.nativeElement.querySelector('mat-list-item');
314+
fixture.detectChanges();
315+
316+
expect(item.getAttribute('aria-disabled')).toBe('false');
317+
318+
fixture.componentInstance.firstItemDisabled = true;
319+
fixture.detectChanges();
320+
321+
expect(item.getAttribute('aria-disabled')).toBe('true');
322+
});
323+
278324
});
279325

280326

@@ -425,3 +471,15 @@ class ListWithMultipleItems extends BaseTestList { }
425471
})
426472
class ListWithIndirectDescendantLines extends BaseTestList {
427473
}
474+
475+
476+
@Component({template: `
477+
<mat-list [disabled]="listDisabled">
478+
<mat-list-item [disabled]="firstItemDisabled">One</mat-list-item>
479+
<mat-list-item>Two</mat-list-item>
480+
<mat-list-item>Three</mat-list-item>
481+
</mat-list>`})
482+
class ListWithDisabledItems {
483+
firstItemDisabled = false;
484+
listDisabled = false;
485+
}

src/material/list/list.ts

Lines changed: 25 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 */
@@ -53,12 +58,12 @@ const _MatListItemMixinBase: CanDisableRippleCtor & typeof MatListItemBase =
5358
},
5459
templateUrl: 'list.html',
5560
styleUrls: ['list.css'],
56-
inputs: ['disableRipple'],
61+
inputs: ['disableRipple', 'disabled'],
5762
encapsulation: ViewEncapsulation.None,
5863
changeDetection: ChangeDetectionStrategy.OnPush,
5964
})
60-
export class MatNavList extends _MatListMixinBase implements CanDisableRipple, OnChanges,
61-
OnDestroy {
65+
export class MatNavList extends _MatListMixinBase implements CanDisable, CanDisableRipple,
66+
OnChanges, OnDestroy {
6267
/** Emits when the state of the list changes. */
6368
_stateChanges = new Subject<void>();
6469

@@ -82,11 +87,12 @@ export class MatNavList extends _MatListMixinBase implements CanDisableRipple, O
8287
'class': 'mat-list mat-list-base'
8388
},
8489
styleUrls: ['list.css'],
85-
inputs: ['disableRipple'],
90+
inputs: ['disableRipple', 'disabled'],
8691
encapsulation: ViewEncapsulation.None,
8792
changeDetection: ChangeDetectionStrategy.OnPush,
8893
})
89-
export class MatList extends _MatListMixinBase implements CanDisableRipple, OnChanges, OnDestroy {
94+
export class MatList extends _MatListMixinBase implements CanDisable, CanDisableRipple, OnChanges,
95+
OnDestroy {
9096
/** Emits when the state of the list changes. */
9197
_stateChanges = new Subject<void>();
9298

@@ -121,6 +127,7 @@ export class MatList extends _MatListMixinBase implements CanDisableRipple, OnCh
121127
}
122128

123129
static ngAcceptInputType_disableRipple: boolean | string;
130+
static ngAcceptInputType_disabled: boolean | string;
124131
}
125132

126133
/**
@@ -160,6 +167,8 @@ export class MatListSubheaderCssMatStyler {}
160167
exportAs: 'matListItem',
161168
host: {
162169
'class': 'mat-list-item',
170+
'[class.mat-list-item-disabled]': 'disabled',
171+
'[attr.aria-disabled]': 'disabled',
163172
// @breaking-change 8.0.0 Remove `mat-list-item-avatar` in favor of `mat-list-item-with-avatar`.
164173
'[class.mat-list-item-avatar]': '_avatar || _icon',
165174
'[class.mat-list-item-with-avatar]': '_avatar || _icon',
@@ -204,6 +213,14 @@ export class MatListItem extends _MatListItemMixinBase implements AfterContentIn
204213
}
205214
}
206215

216+
/** Whether the option is disabled. */
217+
@Input()
218+
get disabled() { return this._disabled || !!(this._list && this._list.disabled); }
219+
set disabled(value: boolean) {
220+
this._disabled = coerceBooleanProperty(value);
221+
}
222+
private _disabled = false;
223+
207224
ngAfterContentInit() {
208225
setLines(this._lines, this._element);
209226
}
@@ -225,4 +242,5 @@ export class MatListItem extends _MatListItemMixinBase implements AfterContentIn
225242
}
226243

227244
static ngAcceptInputType_disableRipple: boolean | string;
245+
static ngAcceptInputType_disabled: boolean | string;
228246
}

tools/public_api_guard/material/list.d.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
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;
10+
static ngAcceptInputType_disabled: boolean | string;
1011
}
1112

1213
export declare class MatListAvatarCssMatStyler {
@@ -19,12 +20,14 @@ export declare class MatListItem extends _MatListItemMixinBase implements AfterC
1920
_avatar: MatListAvatarCssMatStyler;
2021
_icon: MatListIconCssMatStyler;
2122
_lines: QueryList<MatLine>;
23+
disabled: boolean;
2224
constructor(_element: ElementRef<HTMLElement>, _changeDetectorRef: ChangeDetectorRef, navList?: MatNavList, list?: MatList);
2325
_getHostElement(): HTMLElement;
2426
_isRippleDisabled(): boolean;
2527
ngAfterContentInit(): void;
2628
ngOnDestroy(): void;
2729
static ngAcceptInputType_disableRipple: boolean | string;
30+
static ngAcceptInputType_disabled: boolean | string;
2831
}
2932

3033
export declare class MatListModule {
@@ -64,7 +67,7 @@ export declare class MatListOption extends _MatListOptionMixinBase implements Af
6467
export declare class MatListSubheaderCssMatStyler {
6568
}
6669

67-
export declare class MatNavList extends _MatListMixinBase implements CanDisableRipple, OnChanges, OnDestroy {
70+
export declare class MatNavList extends _MatListMixinBase implements CanDisable, CanDisableRipple, OnChanges, OnDestroy {
6871
_stateChanges: Subject<void>;
6972
ngOnChanges(): void;
7073
ngOnDestroy(): void;

0 commit comments

Comments
 (0)