Skip to content

Commit e922f03

Browse files
committed
fix(material/list): allow mat-list-item outside list (#26092)
In the MDC-based list having a `MatListItem` outside of a list throws a DI error, whereas in the legacy version it didn't. These changes make injecting the list optional and try to gracefully handle the absence of a list. I've also removed the `mat-mdc-list-non-interactive` class since the same can be achieved without it. Fixes #26013. (cherry picked from commit 936741c)
1 parent 4300f39 commit e922f03

File tree

5 files changed

+29
-14
lines changed

5 files changed

+29
-14
lines changed

src/material/list/list-base.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ import {
3838

3939
@Directive({
4040
host: {
41-
'[class.mat-mdc-list-non-interactive]': '_isNonInteractive',
4241
'[attr.aria-disabled]': 'disabled',
4342
},
4443
})
@@ -121,7 +120,10 @@ export abstract class MatListItemBase implements AfterViewInit, OnDestroy, Rippl
121120
@Input()
122121
get disableRipple(): boolean {
123122
return (
124-
this.disabled || this._disableRipple || this._listBase.disableRipple || this._noopAnimations
123+
this.disabled ||
124+
this._disableRipple ||
125+
this._noopAnimations ||
126+
!!this._listBase?.disableRipple
125127
);
126128
}
127129
set disableRipple(value: boolean) {
@@ -132,7 +134,7 @@ export abstract class MatListItemBase implements AfterViewInit, OnDestroy, Rippl
132134
/** Whether the list-item is disabled. */
133135
@Input()
134136
get disabled(): boolean {
135-
return this._disabled || (this._listBase && this._listBase.disabled);
137+
return this._disabled || !!this._listBase?.disabled;
136138
}
137139
set disabled(value: BooleanInput) {
138140
this._disabled = coerceBooleanProperty(value);
@@ -162,7 +164,7 @@ export abstract class MatListItemBase implements AfterViewInit, OnDestroy, Rippl
162164
constructor(
163165
public _elementRef: ElementRef<HTMLElement>,
164166
protected _ngZone: NgZone,
165-
private _listBase: MatListBase,
167+
@Optional() private _listBase: MatListBase | null,
166168
private _platform: Platform,
167169
@Optional()
168170
@Inject(MAT_RIPPLE_GLOBAL_OPTIONS)
@@ -173,7 +175,7 @@ export abstract class MatListItemBase implements AfterViewInit, OnDestroy, Rippl
173175
this._hostElement = this._elementRef.nativeElement;
174176
this._noopAnimations = animationMode === 'NoopAnimations';
175177

176-
if (!this._listBase._isNonInteractive) {
178+
if (_listBase && !_listBase._isNonInteractive) {
177179
this._initInteractiveListItem();
178180
}
179181

src/material/list/list.scss

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ a.mdc-list-item--activated {
3333
.mat-mdc-list-option {
3434
width: 100%;
3535
box-sizing: border-box;
36+
37+
// MDC always sets the cursor to `pointer`. We do not want to show this for non-interactive
38+
// lists. See: https://github.com/material-components/material-components-web/issues/6443
39+
&:not(.mat-mdc-list-item-interactive) {
40+
cursor: default;
41+
}
3642
}
3743

3844
// MDC doesn't have list dividers, so we use mat-divider and style appropriately.
@@ -64,12 +70,6 @@ a.mdc-list-item--activated {
6470
pointer-events: none;
6571
}
6672

67-
// MDC always sets the cursor to `pointer`. We do not want to show this for non-interactive
68-
// lists. See: https://github.com/material-components/material-components-web/issues/6443
69-
.mat-mdc-list-non-interactive .mdc-list-item {
70-
cursor: default;
71-
}
72-
7373
// The MDC-based list items already use the `::before` pseudo element for the standard
7474
// focus/selected/hover state. Hence, we need to have a separate list-item spanning
7575
// element that can be used for strong focus indicators.

src/material/list/list.spec.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ describe('MDC-based MatList', () => {
2323
ActionListWithoutType,
2424
ActionListWithType,
2525
ListWithDisabledItems,
26+
StandaloneListItem,
2627
],
2728
});
2829

@@ -369,6 +370,13 @@ describe('MDC-based MatList', () => {
369370

370371
expect(listItems.every(item => item.classList.contains('mdc-list-item--disabled'))).toBe(true);
371372
});
373+
374+
it('should allow a list item outside of a list', () => {
375+
expect(() => {
376+
const fixture = TestBed.createComponent(StandaloneListItem);
377+
fixture.detectChanges();
378+
}).not.toThrow();
379+
});
372380
});
373381

374382
class BaseTestList {
@@ -558,3 +566,8 @@ class ListWithDisabledItems {
558566
firstItemDisabled = false;
559567
listDisabled = false;
560568
}
569+
570+
@Component({
571+
template: `<mat-list-item></mat-list-item>`,
572+
})
573+
class StandaloneListItem {}

src/material/list/list.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ export class MatListItem extends MatListItemBase {
8484
constructor(
8585
element: ElementRef,
8686
ngZone: NgZone,
87-
listBase: MatListBase,
87+
@Optional() listBase: MatListBase | null,
8888
platform: Platform,
8989
@Optional() @Inject(MAT_RIPPLE_GLOBAL_OPTIONS) globalRippleOptions?: RippleGlobalOptions,
9090
@Optional() @Inject(ANIMATION_MODULE_TYPE) animationMode?: string,

tools/public_api_guard/material/list.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export class MatList extends MatListBase {
5858

5959
// @public (undocumented)
6060
export class MatListItem extends MatListItemBase {
61-
constructor(element: ElementRef, ngZone: NgZone, listBase: MatListBase, platform: Platform, globalRippleOptions?: RippleGlobalOptions, animationMode?: string);
61+
constructor(element: ElementRef, ngZone: NgZone, listBase: MatListBase | null, platform: Platform, globalRippleOptions?: RippleGlobalOptions, animationMode?: string);
6262
get activated(): boolean;
6363
set activated(activated: boolean);
6464
// (undocumented)
@@ -77,7 +77,7 @@ export class MatListItem extends MatListItemBase {
7777
// (undocumented)
7878
static ɵcmp: i0.ɵɵComponentDeclaration<MatListItem, "mat-list-item, a[mat-list-item], button[mat-list-item]", ["matListItem"], { "activated": "activated"; }, {}, ["_lines", "_titles", "_meta"], ["[matListItemAvatar],[matListItemIcon]", "[matListItemTitle]", "[matListItemLine]", "*", "[matListItemMeta]", "mat-divider"], false, never>;
7979
// (undocumented)
80-
static ɵfac: i0.ɵɵFactoryDeclaration<MatListItem, [null, null, null, null, { optional: true; }, { optional: true; }]>;
80+
static ɵfac: i0.ɵɵFactoryDeclaration<MatListItem, [null, null, { optional: true; }, null, { optional: true; }, { optional: true; }]>;
8181
}
8282

8383
// @public

0 commit comments

Comments
 (0)