Skip to content

Commit 3474aaa

Browse files
authored
mdc-list: support for strong focus indicators and fix incorrect cursor for non-interactive items (#20476)
* feat(material-experimental/mdc-list): add support for strong focus indicators Adds support for strong focus indicators to the MDC-based list. Also simplifies SCSS code for the hover/focus/selected styles. * fix(material-experimental/mdc-list): do not show pointer cursor for non-interactive list items The MDC list currently always shows the pointer cursor for all kinds of list items, regardless of them being interactive or not (through instantiation of the foundation). This seems to be unexpected as the pointer cursor incorrectly suggests interactivity of a list item to users. We manually reset the cursor for non-interactive list items and report an issue upstream: material-components/material-components-web#6443 Note: MDC list does no longer seem to support the `non-interactive` class we previously used, so we replace it with our own.
1 parent 7510f4c commit 3474aaa

File tree

7 files changed

+49
-23
lines changed

7 files changed

+49
-23
lines changed

src/material-experimental/mdc-helpers/_focus-indicators.scss

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,10 @@
7373
.mat-mdc-slide-toggle-focused .mat-mdc-focus-indicator::before,
7474
.mat-mdc-radio-button.cdk-focused .mat-mdc-focus-indicator::before,
7575

76-
// For buttons, render the focus indicator when the parent button is focused.
76+
// For buttons and list items, render the focus indicator when the parent
77+
// button or list item is focused.
7778
.mat-mdc-button-base:focus .mat-mdc-focus-indicator::before,
79+
.mat-mdc-list-item:focus > .mat-mdc-focus-indicator::before,
7880

7981
// For options, render the focus indicator when the class .mat-mdc-option-active is present.
8082
.mat-mdc-focus-indicator.mat-mdc-option-active::before,

src/material-experimental/mdc-list/list-base.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,6 @@ export abstract class MatListItemBase implements AfterContentInit, OnDestroy, Ri
8080
this._initInteractiveListItem();
8181
}
8282

83-
// Only interactive list items are commonly focusable, but in some situations,
84-
// consumers provide a custom tabindex. We still would want to have strong focus
85-
// indicator support in such scenarios.
86-
this._hostElement.classList.add('mat-mdc-focus-indicator');
87-
8883
// If no type attribute is specified for a host `<button>` element, set it to `button`. If a
8984
// type attribute is already specified, we do nothing. We do this for backwards compatibility.
9085
// TODO: Determine if we intend to continue doing this for the MDC-based list.
@@ -143,7 +138,7 @@ export abstract class MatListItemBase implements AfterContentInit, OnDestroy, Ri
143138
@Directive()
144139
/** @docs-private */
145140
export abstract class MatListBase {
146-
@HostBinding('class.mdc-list--non-interactive')
141+
@HostBinding('class.mat-mdc-list-non-interactive')
147142
_isNonInteractive: boolean = true;
148143

149144
/** Whether ripples for all list items is disabled. */

src/material-experimental/mdc-list/list-item.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,9 @@
1515
</span>
1616

1717
<ng-content select="mat-divider"></ng-content>
18+
19+
<!--
20+
Strong focus indicator element. MDC uses the `::before` pseudo element for the default
21+
focus/hover/selected state, so we need a separate element.
22+
-->
23+
<div class="mat-mdc-focus-indicator"></div>

src/material-experimental/mdc-list/list-option.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,9 @@
5050

5151
<!-- Divider -->
5252
<ng-content select="mat-divider"></ng-content>
53+
54+
<!--
55+
Strong focus indicator element. MDC uses the `::before` pseudo element for the default
56+
focus/hover/selected state, so we need a separate element.
57+
-->
58+
<div class="mat-mdc-focus-indicator"></div>

src/material-experimental/mdc-list/list.scss

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -97,14 +97,24 @@
9797
}
9898
}
9999

100-
// MDC's state styles are included with their ripple which we don't use. Instead we add
101-
// the focus and hover styles ourselves using this pseudo-element
100+
// MDC's hover and focus state styles are included with their ripple which we don't use.
101+
// Instead we add the focus, hover and selected styles ourselves using this pseudo-element
102102
.mat-mdc-list-item-interactive::before {
103+
@include mat-fill();
103104
content: '';
104-
position: absolute;
105-
top: 0;
106-
left: 0;
107-
bottom: 0;
108-
right: 0;
109105
opacity: 0;
110106
}
107+
108+
// MDC always sets the cursor to `pointer`. We do not want to show this for non-interactive
109+
// lists. See: https://github.com/material-components/material-components-web/issues/6443
110+
.mat-mdc-list-non-interactive .mdc-list-item {
111+
cursor: default;
112+
}
113+
114+
// The MDC-based list items already use the `::before` pseudo element for the standard
115+
// focus/selected/hover state. Hence, we need to have a separate list-item spanning
116+
// element that can be used for strong focus indicators.
117+
.mat-mdc-list-item > .mat-mdc-focus-indicator {
118+
@include mat-fill();
119+
pointer-events: none;
120+
}

src/material-experimental/mdc-list/list.spec.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,14 @@ describe('MDC-based MatList', () => {
2323
TestBed.compileComponents();
2424
}));
2525

26-
it('should apply an additional class lists without lines', () => {
26+
it('should apply an additional class to lists without lines', () => {
2727
const fixture = TestBed.createComponent(ListWithOneItem);
2828
const listItem = fixture.debugElement.query(By.css('mat-list-item'))!;
2929
fixture.detectChanges();
30-
expect(listItem.nativeElement.classList.length).toBe(4);
30+
expect(listItem.nativeElement.classList.length).toBe(3);
3131
expect(listItem.nativeElement.classList).toContain('mat-mdc-list-item');
3232
expect(listItem.nativeElement.classList).toContain('mdc-list-item');
3333
expect(listItem.nativeElement.classList).toContain('mat-mdc-list-item-single-line');
34-
35-
// This spec also ensures the focus indicator is present.
36-
expect(listItem.nativeElement.classList).toContain('mat-mdc-focus-indicator');
3734
});
3835

3936
it('should apply mat-mdc-2-line class to lists with two lines', () => {
@@ -63,6 +60,16 @@ describe('MDC-based MatList', () => {
6360
expect(listItems[1].nativeElement.className).toContain('mat-mdc-multi-line');
6461
});
6562

63+
it('should have a strong focus indicator configured for all list-items', () => {
64+
const fixture = TestBed.createComponent(ListWithManyLines);
65+
fixture.detectChanges();
66+
const listItems = fixture.debugElement.children[0].queryAll(By.css('mat-list-item'))
67+
.map(debugEl => debugEl.nativeElement as HTMLElement);
68+
69+
expect(listItems.every(i => i.querySelector('.mat-mdc-focus-indicator') !== null))
70+
.toBe(true, 'Expected all list items to have a strong focus indicator element.');
71+
});
72+
6673
it('should not clear custom classes provided by user', () => {
6774
const fixture = TestBed.createComponent(ListWithItemWithCssClass);
6875
fixture.detectChanges();
@@ -77,11 +84,10 @@ describe('MDC-based MatList', () => {
7784
fixture.detectChanges();
7885

7986
const listItem = fixture.debugElement.children[0].query(By.css('mat-list-item'))!;
80-
expect(listItem.nativeElement.classList.length).toBe(4);
87+
expect(listItem.nativeElement.classList.length).toBe(3);
8188
expect(listItem.nativeElement.classList).toContain('mat-mdc-2-line');
8289
expect(listItem.nativeElement.classList).toContain('mat-mdc-list-item');
8390
expect(listItem.nativeElement.classList).toContain('mdc-list-item');
84-
expect(listItem.nativeElement.classList).toContain('mat-mdc-focus-indicator');
8591

8692
fixture.debugElement.componentInstance.showThirdLine = true;
8793
fixture.detectChanges();

src/material-experimental/mdc-list/selection-list.spec.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -735,10 +735,11 @@ describe('MDC-based MatSelectionList without forms', () => {
735735
});
736736

737737
it('should have a focus indicator', () => {
738-
const optionNativeElements = listOptions.map(option => option.nativeElement);
738+
const optionNativeElements = listOptions.map(option =>
739+
option.nativeElement as HTMLElement);
739740

740741
expect(optionNativeElements
741-
.every(element => element.classList.contains('mat-mdc-focus-indicator'))).toBe(true);
742+
.every(element => element.querySelector('.mat-mdc-focus-indicator') !== null)).toBe(true);
742743
});
743744

744745
});

0 commit comments

Comments
 (0)