mirror of
https://github.com/material-components/material-web.git
synced 2024-09-11 13:46:10 +03:00
refactor(list)!: move list aria to host
BREAKING CHANGE: Aria and roles on List have been moved to the host element. list-tabindex attribute should be migrated to tabindex attribute. type attribute should be migrated to role attribute. PiperOrigin-RevId: 565767899
This commit is contained in:
parent
cfd053c397
commit
9447ec7d72
@ -56,7 +56,7 @@
|
||||
<!-- unrelated change to components -->
|
||||
{% block content %}{{ content | safe }}{% endblock %}
|
||||
</main>
|
||||
<md-list list-tabindex="-1">
|
||||
<md-list>
|
||||
{% for component in collections.component|filtersort('data.name') %}
|
||||
<li>
|
||||
<md-list-item
|
||||
|
@ -233,11 +233,15 @@ Videos can also be slotted into list-items' `start-video"` or
|
||||
|
||||
## Accessibility
|
||||
|
||||
List and List Items can have their internal `role` attribute set via the `type`
|
||||
attribute which by default is set to
|
||||
[`'list'`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/list_role)<!-- {.external} -->
|
||||
and[ `'listitem'`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/listitem_role)<!-- {.external} -->
|
||||
respectively.
|
||||
List can have its `role` and `tabindex` set via the `role` and `tabindex`
|
||||
attributes, and list items can have their internal `role` and `tabindex` set via
|
||||
the `type` and `item-tabindex` attributes respectively.
|
||||
|
||||
By default these values are set to
|
||||
[`role="list"`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/list_role)<!-- {.external} -->
|
||||
and `tabindex="-1"` for list, and
|
||||
[`role="listitem"`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/listitem_role)<!-- {.external} -->
|
||||
and `tabindex="0"` for list items.
|
||||
|
||||
The following example sets
|
||||
[`role="listbox"`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/listbox_role)<!-- {.external} -->
|
||||
@ -246,7 +250,7 @@ on the internal list and
|
||||
on the internal list item nodes.
|
||||
|
||||
```html
|
||||
<md-list type="listbox">
|
||||
<md-list role="listbox" tabindex="0">
|
||||
<md-list-item type="option" headline="icon">
|
||||
<md-icon slot="start-icon">account_circle</md-icon>
|
||||
</md-list-item>
|
||||
|
@ -8,7 +8,7 @@ import './index.js';
|
||||
import './material-collection.js';
|
||||
|
||||
import {KnobTypesToKnobs, MaterialCollection, materialInitsToStoryInits, setUpDemo, title} from './material-collection.js';
|
||||
import {boolInput, Knob, numberInput, textInput} from './index.js';
|
||||
import {boolInput, Knob, textInput} from './index.js';
|
||||
|
||||
import {stories, StoryKnobs} from './stories.js';
|
||||
|
||||
@ -36,7 +36,6 @@ export const VIDEO_URL =
|
||||
|
||||
const collection =
|
||||
new MaterialCollection<KnobTypesToKnobs<StoryKnobs>>('List', [
|
||||
new Knob('listTabIndex', {ui: numberInput(), defaultValue: -1}),
|
||||
new Knob('md-list-item', {ui: title()}),
|
||||
new Knob('disabled', {ui: boolInput(), defaultValue: false}),
|
||||
new Knob('noninteractive', {ui: boolInput(), defaultValue: false}),
|
||||
|
@ -14,8 +14,6 @@ import {css, html} from 'lit';
|
||||
|
||||
/** Knob types for list stories. */
|
||||
export interface StoryKnobs {
|
||||
listTabIndex: number;
|
||||
|
||||
'md-list-item': void;
|
||||
disabled: boolean;
|
||||
noninteractive: boolean;
|
||||
@ -59,7 +57,6 @@ const standard: MaterialStoryInit<StoryKnobs> = {
|
||||
}`,
|
||||
render(knobs) {
|
||||
const {
|
||||
listTabIndex,
|
||||
disabled,
|
||||
noninteractive,
|
||||
multiLineSupportingText,
|
||||
@ -72,9 +69,7 @@ const standard: MaterialStoryInit<StoryKnobs> = {
|
||||
} = knobs;
|
||||
return html`
|
||||
<div class="list-demo">
|
||||
<md-list
|
||||
.listTabIndex=${listTabIndex}
|
||||
class="list">
|
||||
<md-list class="list" role="listbox">
|
||||
<md-list-item
|
||||
.headline=${headline}
|
||||
.supportingText=${supportingText}
|
||||
|
@ -4,11 +4,10 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import {html, LitElement, nothing} from 'lit';
|
||||
import {property, query, queryAssignedElements} from 'lit/decorators.js';
|
||||
import {html, isServer, LitElement} from 'lit';
|
||||
import {query, queryAssignedElements} from 'lit/decorators.js';
|
||||
|
||||
import {ARIAMixinStrict} from '../../internal/aria/aria.js';
|
||||
import {requestUpdateOnAriaChange} from '../../internal/aria/delegate.js';
|
||||
import {polyfillElementInternalsAria, setupHostAria} from '../../internal/aria/aria.js';
|
||||
|
||||
import {ListItem} from './listitem/list-item.js';
|
||||
|
||||
@ -44,20 +43,9 @@ function isNavigableKey(key: string): key is NavigatableValues {
|
||||
// tslint:disable-next-line:enforce-comments-on-exported-symbols
|
||||
export class List extends LitElement {
|
||||
static {
|
||||
requestUpdateOnAriaChange(List);
|
||||
setupHostAria(List, {focusable: false});
|
||||
}
|
||||
|
||||
/** @nocollapse */
|
||||
static override shadowRootOptions:
|
||||
ShadowRootInit = {mode: 'open', delegatesFocus: true};
|
||||
|
||||
@property() type: 'menu'|'menubar'|'listbox'|'list'|'' = 'list';
|
||||
|
||||
/**
|
||||
* The tabindex of the underlying list.
|
||||
*/
|
||||
@property({type: Number, attribute: 'list-tabindex'}) listTabIndex = -1;
|
||||
|
||||
@query('.list') private listRoot!: HTMLElement|null;
|
||||
|
||||
/**
|
||||
@ -71,6 +59,17 @@ export class List extends LitElement {
|
||||
@queryAssignedElements({flatten: true, selector: '[md-list-item]'})
|
||||
items!: ListItem[];
|
||||
|
||||
private readonly internals = polyfillElementInternalsAria(
|
||||
this, (this as HTMLElement /* needed for closure */).attachInternals());
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
if (!isServer) {
|
||||
this.internals.role = 'list';
|
||||
this.addEventListener('keydown', this.handleKeydown.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
protected override render() {
|
||||
return this.renderList();
|
||||
}
|
||||
@ -79,15 +78,8 @@ export class List extends LitElement {
|
||||
* Renders the main list element.
|
||||
*/
|
||||
private renderList() {
|
||||
// Needed for closure conformance
|
||||
const {ariaLabel} = this as ARIAMixinStrict;
|
||||
return html`
|
||||
<ul class="list"
|
||||
aria-label=${ariaLabel || nothing}
|
||||
tabindex=${this.listTabIndex}
|
||||
role=${this.type || nothing}
|
||||
@keydown=${this.handleKeydown}
|
||||
>
|
||||
<ul class="list" role="presentation">
|
||||
${this.renderContent()}
|
||||
</ul>
|
||||
`;
|
||||
|
@ -937,6 +937,34 @@ describe('<md-list>', () => {
|
||||
expect(document.activeElement).toEqual(first);
|
||||
});
|
||||
});
|
||||
|
||||
describe('aria', () => {
|
||||
it('Sets default role to list', async () => {
|
||||
const root = env.render(html`<md-list></md-list>`);
|
||||
const listEl = root.querySelector('md-list')!;
|
||||
await env.waitForStability();
|
||||
const internals =
|
||||
(listEl as unknown as {internals: {role: string | null}}).internals;
|
||||
|
||||
expect(internals.role).toEqual('list');
|
||||
});
|
||||
|
||||
it('Does not override user given role attribute', async () => {
|
||||
const root = env.render(html`<md-list role="listbox"></md-list>`);
|
||||
const listEl = root.querySelector('md-list')!;
|
||||
await env.waitForStability();
|
||||
|
||||
expect(listEl.getAttribute('role')).toBe('listbox');
|
||||
});
|
||||
|
||||
it('Does not override user given role property', async () => {
|
||||
const root = env.render(html`<md-list .role=${'listbox'}></md-list>`);
|
||||
const listEl = root.querySelector('md-list')!;
|
||||
await env.waitForStability();
|
||||
|
||||
expect(listEl.role).toBe('listbox');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('<md-list-item>', () => {
|
||||
|
@ -306,8 +306,8 @@ export abstract class Menu extends LitElement {
|
||||
part="list"
|
||||
id="list"
|
||||
aria-label=${ariaLabel || nothing}
|
||||
type=${this.type}
|
||||
listTabIndex=${this.listTabIndex}
|
||||
role=${this.type}
|
||||
tabindex=${this.listTabIndex}
|
||||
@keydown=${this.handleListKeydown}>
|
||||
${this.renderMenuItems()}
|
||||
</md-list>`;
|
||||
|
Loading…
Reference in New Issue
Block a user