mirror of
https://github.com/material-components/material-web.git
synced 2026-01-09 07:21:09 +08:00
fix(list): text items are no longer tabbable, links cannot be disabled
PiperOrigin-RevId: 568318309
This commit is contained in:
parent
6d0c7e8538
commit
54c4ddba4c
@ -37,7 +37,7 @@ const standard: MaterialStoryInit<StoryKnobs> = {
|
||||
styles,
|
||||
render(knobs) {
|
||||
return html`
|
||||
<md-list>
|
||||
<md-list aria-label="Static example">
|
||||
<md-list-item ?disabled=${knobs.disabled}>
|
||||
Single line item
|
||||
${getKnobContent(knobs)}
|
||||
@ -68,7 +68,7 @@ const interactive: MaterialStoryInit<StoryKnobs> = {
|
||||
render(knobs) {
|
||||
const knobsNoTrailing = {...knobs, trailingIcon: false};
|
||||
return html`
|
||||
<md-list>
|
||||
<md-list aria-label="Interactive example">
|
||||
<md-list-item ?disabled=${knobs.disabled}
|
||||
type="link"
|
||||
href="https://google.com"
|
||||
|
||||
@ -58,6 +58,11 @@ export interface ListControllerConfig<Item extends ListItem> {
|
||||
* Whether or not the key should be handled by the list for navigation.
|
||||
*/
|
||||
isNavigableKey: (key: string) => boolean;
|
||||
/**
|
||||
* Whether or not the item can be activated. Defaults to items that are not
|
||||
* disabled.
|
||||
*/
|
||||
isActivatable?: (item: Item) => boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -70,6 +75,7 @@ export class ListController<Item extends ListItem> {
|
||||
private readonly deactivateItem: (item: Item) => void;
|
||||
private readonly activateItem: (item: Item) => void;
|
||||
private readonly isNavigableKey: (key: string) => boolean;
|
||||
private readonly isActivatable?: (item: Item) => boolean;
|
||||
|
||||
constructor(config: ListControllerConfig<Item>) {
|
||||
const {
|
||||
@ -79,6 +85,7 @@ export class ListController<Item extends ListItem> {
|
||||
deactivateItem,
|
||||
activateItem,
|
||||
isNavigableKey,
|
||||
isActivatable,
|
||||
} = config;
|
||||
this.isItem = isItem;
|
||||
this.getPossibleItems = getPossibleItems;
|
||||
@ -86,6 +93,7 @@ export class ListController<Item extends ListItem> {
|
||||
this.deactivateItem = deactivateItem;
|
||||
this.activateItem = activateItem;
|
||||
this.isNavigableKey = isNavigableKey;
|
||||
this.isActivatable = isActivatable;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -131,7 +139,7 @@ export class ListController<Item extends ListItem> {
|
||||
return;
|
||||
}
|
||||
|
||||
const activeItemRecord = getActiveItem(items);
|
||||
const activeItemRecord = getActiveItem(items, this.isActivatable);
|
||||
|
||||
if (activeItemRecord) {
|
||||
activeItemRecord.item.tabIndex = -1;
|
||||
@ -149,23 +157,23 @@ export class ListController<Item extends ListItem> {
|
||||
// Activate the next item
|
||||
case NavigableKeys.ArrowDown:
|
||||
case inlineNext:
|
||||
activateNextItem(items, activeItemRecord);
|
||||
activateNextItem(items, activeItemRecord, this.isActivatable);
|
||||
break;
|
||||
|
||||
// Activate the previous item
|
||||
case NavigableKeys.ArrowUp:
|
||||
case inlinePrevious:
|
||||
activatePreviousItem(items, activeItemRecord);
|
||||
activatePreviousItem(items, activeItemRecord, this.isActivatable);
|
||||
break;
|
||||
|
||||
// Activate the first item
|
||||
case NavigableKeys.Home:
|
||||
activateFirstItem(items);
|
||||
activateFirstItem(items, this.isActivatable);
|
||||
break;
|
||||
|
||||
// Activate the last item
|
||||
case NavigableKeys.End:
|
||||
activateLastItem(items);
|
||||
activateLastItem(items, this.isActivatable);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -179,13 +187,13 @@ export class ListController<Item extends ListItem> {
|
||||
*
|
||||
* @return The activated list item or `null` if there are no items.
|
||||
*/
|
||||
activateNextItem(): ListItem|null {
|
||||
activateNextItem(): Item|null {
|
||||
const items = this.items;
|
||||
const activeItemRecord = getActiveItem(items);
|
||||
const activeItemRecord = getActiveItem(items, this.isActivatable);
|
||||
if (activeItemRecord) {
|
||||
activeItemRecord.item.tabIndex = -1;
|
||||
}
|
||||
return activateNextItem(items, activeItemRecord);
|
||||
return activateNextItem(items, activeItemRecord, this.isActivatable);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -194,13 +202,13 @@ export class ListController<Item extends ListItem> {
|
||||
*
|
||||
* @return The activated list item or `null` if there are no items.
|
||||
*/
|
||||
activatePreviousItem(): ListItem|null {
|
||||
activatePreviousItem(): Item|null {
|
||||
const items = this.items;
|
||||
const activeItemRecord = getActiveItem(items);
|
||||
const activeItemRecord = getActiveItem(items, this.isActivatable);
|
||||
if (activeItemRecord) {
|
||||
activeItemRecord.item.tabIndex = -1;
|
||||
}
|
||||
return activatePreviousItem(items, activeItemRecord);
|
||||
return activatePreviousItem(items, activeItemRecord, this.isActivatable);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -250,7 +258,8 @@ export class ListController<Item extends ListItem> {
|
||||
return;
|
||||
}
|
||||
|
||||
const firstActivatableItem = getFirstActivatableItem(items);
|
||||
const firstActivatableItem =
|
||||
getFirstActivatableItem(items, this.isActivatable);
|
||||
|
||||
if (!firstActivatableItem) {
|
||||
return;
|
||||
@ -258,4 +267,4 @@ export class ListController<Item extends ListItem> {
|
||||
|
||||
firstActivatableItem.tabIndex = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,8 +4,6 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// TODO: move this file to List
|
||||
|
||||
export interface ListItem extends HTMLElement {
|
||||
disabled: boolean;
|
||||
}
|
||||
@ -19,26 +17,20 @@ export interface ItemRecord<Item extends ListItem> {
|
||||
index: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* A record that describes a list item in a list with metadata such a reference
|
||||
* to the item and its index in the list.
|
||||
*/
|
||||
export interface ItemRecord<Item extends ListItem> {
|
||||
item: Item;
|
||||
index: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates the first non-disabled item of a given array of items.
|
||||
*
|
||||
* @param items {Array<ListItem>} The items from which to activate the
|
||||
* first item.
|
||||
* first item.
|
||||
* @param isActivatable Function to determine if an item can be activated.
|
||||
* Defaults to non-disabled items.
|
||||
*/
|
||||
export function activateFirstItem<Item extends ListItem>(items: Item[]) {
|
||||
export function activateFirstItem<Item extends ListItem>(
|
||||
items: Item[], isActivatable = isItemNotDisabled<Item>) {
|
||||
// NOTE: These selector functions are static and not on the instance such
|
||||
// that multiple operations can be chained and we do not have to re-query
|
||||
// the DOM
|
||||
const firstItem = getFirstActivatableItem(items);
|
||||
const firstItem = getFirstActivatableItem(items, isActivatable);
|
||||
if (firstItem) {
|
||||
firstItem.tabIndex = 0;
|
||||
firstItem.focus();
|
||||
@ -50,11 +42,14 @@ export function activateFirstItem<Item extends ListItem>(items: Item[]) {
|
||||
* Activates the last non-disabled item of a given array of items.
|
||||
*
|
||||
* @param items {Array<ListItem>} The items from which to activate the
|
||||
* last item.
|
||||
* last item.
|
||||
* @param isActivatable Function to determine if an item can be activated.
|
||||
* Defaults to non-disabled items.
|
||||
* @nocollapse
|
||||
*/
|
||||
export function activateLastItem<Item extends ListItem>(items: Item[]) {
|
||||
const lastItem = getLastActivatableItem(items);
|
||||
export function activateLastItem<Item extends ListItem>(
|
||||
items: Item[], isActivatable = isItemNotDisabled<Item>) {
|
||||
const lastItem = getLastActivatableItem(items, isActivatable);
|
||||
if (lastItem) {
|
||||
lastItem.tabIndex = 0;
|
||||
lastItem.focus();
|
||||
@ -66,13 +61,16 @@ export function activateLastItem<Item extends ListItem>(items: Item[]) {
|
||||
* Deactivates the currently active item of a given array of items.
|
||||
*
|
||||
* @param items {Array<ListItem>} The items from which to deactivate the
|
||||
* active item.
|
||||
* active item.
|
||||
* @param isActivatable Function to determine if an item can be activated.
|
||||
* Defaults to non-disabled items.
|
||||
* @return A record of the deleselcted activated item including the item and
|
||||
* the index of the item or `null` if none are deactivated.
|
||||
* the index of the item or `null` if none are deactivated.
|
||||
* @nocollapse
|
||||
*/
|
||||
export function deactivateActiveItem<Item extends ListItem>(items: Item[]) {
|
||||
const activeItem = getActiveItem(items);
|
||||
export function deactivateActiveItem<Item extends ListItem>(
|
||||
items: Item[], isActivatable = isItemNotDisabled<Item>) {
|
||||
const activeItem = getActiveItem(items, isActivatable);
|
||||
if (activeItem) {
|
||||
activeItem.item.tabIndex = -1;
|
||||
}
|
||||
@ -83,14 +81,17 @@ export function deactivateActiveItem<Item extends ListItem>(items: Item[]) {
|
||||
* Retrieves the first activated item of a given array of items.
|
||||
*
|
||||
* @param items {Array<ListItem>} The items to search.
|
||||
* @param isActivatable Function to determine if an item can be activated.
|
||||
* Defaults to non-disabled items.
|
||||
* @return A record of the first activated item including the item and the
|
||||
* index of the item or `null` if none are activated.
|
||||
* index of the item or `null` if none are activated.
|
||||
* @nocollapse
|
||||
*/
|
||||
export function getActiveItem<Item extends ListItem>(items: Item[]) {
|
||||
export function getActiveItem<Item extends ListItem>(
|
||||
items: Item[], isActivatable = isItemNotDisabled<Item>) {
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
if (item.tabIndex === 0 && !item.disabled) {
|
||||
if (item.tabIndex === 0 && isActivatable(item)) {
|
||||
return {
|
||||
item,
|
||||
index: i,
|
||||
@ -105,12 +106,15 @@ export function getActiveItem<Item extends ListItem>(items: Item[]) {
|
||||
* the first item that is not disabled.
|
||||
*
|
||||
* @param items {Array<ListItem>} The items to search.
|
||||
* @param isActivatable Function to determine if an item can be activated.
|
||||
* Defaults to non-disabled items.
|
||||
* @return The first activatable item or `null` if none are activatable.
|
||||
* @nocollapse
|
||||
*/
|
||||
export function getFirstActivatableItem<Item extends ListItem>(items: Item[]) {
|
||||
export function getFirstActivatableItem<Item extends ListItem>(
|
||||
items: Item[], isActivatable = isItemNotDisabled<Item>) {
|
||||
for (const item of items) {
|
||||
if (!item.disabled) {
|
||||
if (isActivatable(item)) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
@ -122,13 +126,16 @@ export function getFirstActivatableItem<Item extends ListItem>(items: Item[]) {
|
||||
* Retrieves the last non-disabled item of a given array of items.
|
||||
*
|
||||
* @param items {Array<ListItem>} The items to search.
|
||||
* @param isActivatable Function to determine if an item can be activated.
|
||||
* Defaults to non-disabled items.
|
||||
* @return The last activatable item or `null` if none are activatable.
|
||||
* @nocollapse
|
||||
*/
|
||||
export function getLastActivatableItem<Item extends ListItem>(items: Item[]) {
|
||||
export function getLastActivatableItem<Item extends ListItem>(
|
||||
items: Item[], isActivatable = isItemNotDisabled<Item>) {
|
||||
for (let i = items.length - 1; i >= 0; i--) {
|
||||
const item = items[i];
|
||||
if (!item.disabled) {
|
||||
if (isActivatable(item)) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
@ -141,14 +148,16 @@ export function getLastActivatableItem<Item extends ListItem>(items: Item[]) {
|
||||
*
|
||||
* @param items {Array<ListItem>} The items to search.
|
||||
* @param index {{index: number}} The index to search from.
|
||||
* @param isActivatable Function to determine if an item can be activated.
|
||||
* Defaults to non-disabled items.
|
||||
* @return The next activatable item or `null` if none are activatable.
|
||||
*/
|
||||
export function getNextItem<Item extends ListItem>(
|
||||
items: Item[], index: number) {
|
||||
items: Item[], index: number, isActivatable = isItemNotDisabled<Item>) {
|
||||
for (let i = 1; i < items.length; i++) {
|
||||
const nextIndex = (i + index) % items.length;
|
||||
const item = items[nextIndex];
|
||||
if (!item.disabled) {
|
||||
if (isActivatable(item)) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
@ -161,15 +170,17 @@ export function getNextItem<Item extends ListItem>(
|
||||
*
|
||||
* @param items {Array<ListItem>} The items to search.
|
||||
* @param index {{index: number}} The index to search from.
|
||||
* @param isActivatable Function to determine if an item can be activated.
|
||||
* Defaults to non-disabled items.
|
||||
* @return The previous activatable item or `null` if none are activatable.
|
||||
*/
|
||||
export function getPrevItem<Item extends ListItem>(
|
||||
items: Item[], index: number) {
|
||||
items: Item[], index: number, isActivatable = isItemNotDisabled<Item>) {
|
||||
for (let i = 1; i < items.length; i++) {
|
||||
const prevIndex = (index - i + items.length) % items.length;
|
||||
const item = items[prevIndex];
|
||||
|
||||
if (!item.disabled) {
|
||||
if (isActivatable(item)) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
@ -182,9 +193,10 @@ export function getPrevItem<Item extends ListItem>(
|
||||
* activates the first item.
|
||||
*/
|
||||
export function activateNextItem<Item extends ListItem>(
|
||||
items: Item[], activeItemRecord: null|ItemRecord<Item>): Item|null {
|
||||
items: Item[], activeItemRecord: null|ItemRecord<Item>,
|
||||
isActivatable = isItemNotDisabled<Item>): Item|null {
|
||||
if (activeItemRecord) {
|
||||
const next = getNextItem(items, activeItemRecord.index);
|
||||
const next = getNextItem(items, activeItemRecord.index, isActivatable);
|
||||
|
||||
if (next) {
|
||||
next.tabIndex = 0;
|
||||
@ -193,7 +205,7 @@ export function activateNextItem<Item extends ListItem>(
|
||||
|
||||
return next;
|
||||
} else {
|
||||
return activateFirstItem(items);
|
||||
return activateFirstItem(items, isActivatable);
|
||||
}
|
||||
}
|
||||
|
||||
@ -202,16 +214,17 @@ export function activateNextItem<Item extends ListItem>(
|
||||
* activated, activates the last item.
|
||||
*/
|
||||
export function activatePreviousItem<Item extends ListItem>(
|
||||
items: Item[], activeItemRecord: null|ItemRecord<Item>): Item|null {
|
||||
items: Item[], activeItemRecord: null|ItemRecord<Item>,
|
||||
isActivatable = isItemNotDisabled<Item>): Item|null {
|
||||
if (activeItemRecord) {
|
||||
const prev = getPrevItem(items, activeItemRecord.index);
|
||||
const prev = getPrevItem(items, activeItemRecord.index, isActivatable);
|
||||
if (prev) {
|
||||
prev.tabIndex = 0;
|
||||
prev.focus();
|
||||
}
|
||||
return prev;
|
||||
} else {
|
||||
return activateLastItem(items);
|
||||
return activateLastItem(items, isActivatable);
|
||||
}
|
||||
}
|
||||
|
||||
@ -246,4 +259,15 @@ export function createRequestActivationEvent() {
|
||||
* The type of the event that requests the list activates and focuses the item.
|
||||
*/
|
||||
export type RequestActivationEvent =
|
||||
ReturnType<typeof createRequestActivationEvent>;
|
||||
ReturnType<typeof createRequestActivationEvent>;
|
||||
|
||||
/**
|
||||
* The default `isActivatable` function, which checks if an item is not
|
||||
* disabled.
|
||||
*
|
||||
* @param item The item to check.
|
||||
* @return true if `item.disabled` is `false.
|
||||
*/
|
||||
function isItemNotDisabled<Item extends ListItem>(item: Item) {
|
||||
return !item.disabled;
|
||||
}
|
||||
|
||||
@ -10,10 +10,14 @@ import {queryAssignedElements} from 'lit/decorators.js';
|
||||
import {polyfillElementInternalsAria, setupHostAria} from '../../internal/aria/aria.js';
|
||||
|
||||
import {ListController, NavigableKeys} from './list-controller.js';
|
||||
import {ListItem} from './list-navigation-helpers.js';
|
||||
import {ListItem as SharedListItem} from './list-navigation-helpers.js';
|
||||
|
||||
const NAVIGABLE_KEY_SET = new Set<string>(Object.values(NavigableKeys));
|
||||
|
||||
interface ListItem extends SharedListItem {
|
||||
type: 'text'|'button'|'link';
|
||||
}
|
||||
|
||||
// tslint:disable-next-line:enforce-comments-on-exported-symbols
|
||||
export class List extends LitElement {
|
||||
static {
|
||||
@ -50,6 +54,7 @@ export class List extends LitElement {
|
||||
item.tabIndex = 0;
|
||||
},
|
||||
isNavigableKey: (key) => NAVIGABLE_KEY_SET.has(key),
|
||||
isActivatable: (item) => !item.disabled && item.type !== 'text',
|
||||
});
|
||||
|
||||
private readonly internals = polyfillElementInternalsAria(
|
||||
|
||||
@ -48,11 +48,6 @@
|
||||
);
|
||||
}
|
||||
|
||||
:host([disabled]) {
|
||||
opacity: map.get($tokens, 'disabled-opacity');
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
md-focus-ring {
|
||||
z-index: 1;
|
||||
|
||||
@ -82,10 +77,15 @@
|
||||
outline: none;
|
||||
// hide android tap color since we have ripple
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
&:not(.disabled).interactive {
|
||||
cursor: pointer;
|
||||
}
|
||||
.list-item.interactive {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.list-item.disabled {
|
||||
opacity: map.get($tokens, 'disabled-opacity');
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
[slot='container'] {
|
||||
|
||||
@ -8,8 +8,11 @@
|
||||
// go/keep-sorted end
|
||||
|
||||
@media (forced-colors: active) {
|
||||
:host([disabled]),
|
||||
:host([disabled]) slot {
|
||||
.disabled slot {
|
||||
color: GrayText;
|
||||
}
|
||||
|
||||
.list-item.disabled {
|
||||
color: GrayText;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@ -66,6 +66,10 @@ export class ListItemEl extends LitElement implements ListItem {
|
||||
|
||||
@query('.list-item') protected readonly listItemRoot!: HTMLElement|null;
|
||||
|
||||
private get isDisabled() {
|
||||
return this.disabled && this.type !== 'link';
|
||||
}
|
||||
|
||||
protected override willUpdate(changed: PropertyValues<ListItemEl>) {
|
||||
if (this.href) {
|
||||
this.type = 'link';
|
||||
@ -109,13 +113,15 @@ export class ListItemEl extends LitElement implements ListItem {
|
||||
break;
|
||||
}
|
||||
|
||||
const isInteractive = this.type !== 'text';
|
||||
// TODO(b/265339866): announce "button"/"link" inside of a list item. Until
|
||||
// then all are "listitem" roles for correct announcement.
|
||||
const target = isAnchor && !!this.target ? this.target : nothing;
|
||||
return staticHtml`
|
||||
<${tag}
|
||||
id="item"
|
||||
tabindex="${this.disabled && !isAnchor ? -1 : 0}"
|
||||
tabindex="${this.isDisabled || !isInteractive ? -1 : 0}"
|
||||
?disabled=${this.isDisabled}
|
||||
role="listitem"
|
||||
aria-selected=${(this as ARIAMixinStrict).ariaSelected || nothing}
|
||||
aria-checked=${(this as ARIAMixinStrict).ariaChecked || nothing}
|
||||
@ -141,7 +147,7 @@ export class ListItemEl extends LitElement implements ListItem {
|
||||
<md-ripple
|
||||
part="ripple"
|
||||
for="item"
|
||||
?disabled=${this.disabled}></md-ripple>`;
|
||||
?disabled=${this.isDisabled}></md-ripple>`;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -166,8 +172,7 @@ export class ListItemEl extends LitElement implements ListItem {
|
||||
* Classes applied to the list item root.
|
||||
*/
|
||||
protected getRenderClasses(): ClassInfo {
|
||||
// TODO(b/265339866): links may not be disabled
|
||||
return {'disabled': this.disabled};
|
||||
return {'disabled': this.isDisabled};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -28,9 +28,9 @@ describe('<md-list>', () => {
|
||||
it('non-nagivable key does nothing', async () => {
|
||||
const root = env.render(html`
|
||||
<md-list>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item tabindex="0"></md-list-item>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="0"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
</md-list>`);
|
||||
|
||||
const listEl = root.querySelector('md-list')!;
|
||||
@ -55,9 +55,9 @@ describe('<md-list>', () => {
|
||||
it('ArrowDown activates the next item', async () => {
|
||||
const root = env.render(html`
|
||||
<md-list>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item tabindex="0"></md-list-item>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="0"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
</md-list>`);
|
||||
|
||||
const listEl = root.querySelector('md-list')!;
|
||||
@ -82,9 +82,9 @@ describe('<md-list>', () => {
|
||||
it('ArrowRight in LTR activates the next item', async () => {
|
||||
const root = env.render(html`
|
||||
<md-list>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item tabindex="0"></md-list-item>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="0"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
</md-list>`);
|
||||
|
||||
const listEl = root.querySelector('md-list')!;
|
||||
@ -109,9 +109,9 @@ describe('<md-list>', () => {
|
||||
it('ArrowLeft in RTL activates the next item', async () => {
|
||||
const root = env.render(html`
|
||||
<md-list dir="rtl">
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item tabindex="0"></md-list-item>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="0"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
</md-list>`);
|
||||
|
||||
const listEl = root.querySelector('md-list')!;
|
||||
@ -136,9 +136,9 @@ describe('<md-list>', () => {
|
||||
it('ArrowDown will loop around', async () => {
|
||||
const root = env.render(html`
|
||||
<md-list>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item tabindex="0"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="0"></md-list-item>
|
||||
</md-list>`);
|
||||
const listEl = root.querySelector('md-list')!;
|
||||
const listHarness = new ListHarness(listEl);
|
||||
@ -162,8 +162,8 @@ describe('<md-list>', () => {
|
||||
it('ArrowDown will do nothing if nothing is selectable', async () => {
|
||||
const root = env.render(html`
|
||||
<md-list>
|
||||
<md-list-item disabled></md-list-item>
|
||||
<md-list-item disabled></md-list-item>
|
||||
<md-list-item type="button" disabled></md-list-item>
|
||||
<md-list-item type="button" disabled></md-list-item>
|
||||
</md-list>`);
|
||||
const listEl = root.querySelector('md-list')!;
|
||||
const listHarness = new ListHarness(listEl);
|
||||
@ -184,9 +184,9 @@ describe('<md-list>', () => {
|
||||
it('ArrowDown does not activate disabled items', async () => {
|
||||
const root = env.render(html`
|
||||
<md-list>
|
||||
<md-list-item></md-list-item>
|
||||
<md-list-item disabled></md-list-item>
|
||||
<md-list-item></md-list-item>
|
||||
<md-list-item type="button"></md-list-item>
|
||||
<md-list-item type="button" disabled></md-list-item>
|
||||
<md-list-item type="button"></md-list-item>
|
||||
</md-list>`);
|
||||
const listEl = root.querySelector('md-list')!;
|
||||
const listHarness = new ListHarness(listEl);
|
||||
@ -211,7 +211,7 @@ describe('<md-list>', () => {
|
||||
it('ArrowDown will select itself if its the only activatable', async () => {
|
||||
const root = env.render(html`
|
||||
<md-list>
|
||||
<md-list-item tabindex="0"></md-list-item>
|
||||
<md-list-item type="button" tabindex="0"></md-list-item>
|
||||
</md-list>`);
|
||||
const listEl = root.querySelector('md-list')!;
|
||||
const listHarness = new ListHarness(listEl);
|
||||
@ -231,9 +231,9 @@ describe('<md-list>', () => {
|
||||
it('ArrowUp activates the previous item', async () => {
|
||||
const root = env.render(html`
|
||||
<md-list>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item tabindex="0"></md-list-item>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="0"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
</md-list>`);
|
||||
const listEl = root.querySelector('md-list')!;
|
||||
const listHarness = new ListHarness(listEl);
|
||||
@ -258,9 +258,9 @@ describe('<md-list>', () => {
|
||||
it('ArrowLeft in LTR activates the previous item', async () => {
|
||||
const root = env.render(html`
|
||||
<md-list>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item tabindex="0"></md-list-item>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="0"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
</md-list>`);
|
||||
const listEl = root.querySelector('md-list')!;
|
||||
const listHarness = new ListHarness(listEl);
|
||||
@ -284,9 +284,9 @@ describe('<md-list>', () => {
|
||||
it('ArrowRight in RTL activates the previous item', async () => {
|
||||
const root = env.render(html`
|
||||
<md-list dir="rtl">
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item tabindex="0"></md-list-item>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="0"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
</md-list>`);
|
||||
const listEl = root.querySelector('md-list')!;
|
||||
const listHarness = new ListHarness(listEl);
|
||||
@ -310,9 +310,9 @@ describe('<md-list>', () => {
|
||||
it('activatePreviousItem will loop around', async () => {
|
||||
const root = env.render(html`
|
||||
<md-list>
|
||||
<md-list-item></md-list-item>
|
||||
<md-list-item></md-list-item>
|
||||
<md-list-item></md-list-item>
|
||||
<md-list-item type="button"></md-list-item>
|
||||
<md-list-item type="button"></md-list-item>
|
||||
<md-list-item type="button"></md-list-item>
|
||||
</md-list>`);
|
||||
const listEl = root.querySelector('md-list')!;
|
||||
const listHarness = new ListHarness(listEl);
|
||||
@ -337,8 +337,8 @@ describe('<md-list>', () => {
|
||||
it('ArrowUp will return null if nothing is selectable', async () => {
|
||||
const root = env.render(html`
|
||||
<md-list>
|
||||
<md-list-item disabled></md-list-item>
|
||||
<md-list-item disabled></md-list-item>
|
||||
<md-list-item type="button" disabled></md-list-item>
|
||||
<md-list-item type="button" disabled></md-list-item>
|
||||
</md-list>`);
|
||||
const listEl = root.querySelector('md-list')!;
|
||||
const listHarness = new ListHarness(listEl);
|
||||
@ -359,9 +359,9 @@ describe('<md-list>', () => {
|
||||
it('ArrowUp does not activate disabled items', async () => {
|
||||
const root = env.render(html`
|
||||
<md-list>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item disabled></md-list-item>
|
||||
<md-list-item tabindex="0"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" disabled></md-list-item>
|
||||
<md-list-item type="button" tabindex="0"></md-list-item>
|
||||
</md-list>`);
|
||||
const listEl = root.querySelector('md-list')!;
|
||||
const listHarness = new ListHarness(listEl);
|
||||
@ -386,7 +386,7 @@ describe('<md-list>', () => {
|
||||
it('ArrowUp will select itself if its the only activatable', async () => {
|
||||
const root = env.render(html`
|
||||
<md-list>
|
||||
<md-list-item></md-list-item>
|
||||
<md-list-item type="button"></md-list-item>
|
||||
</md-list>`);
|
||||
const listEl = root.querySelector('md-list')!;
|
||||
const listHarness = new ListHarness(listEl);
|
||||
@ -407,9 +407,9 @@ describe('<md-list>', () => {
|
||||
async () => {
|
||||
const root = env.render(html`
|
||||
<md-list>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item tabindex="0"></md-list-item>
|
||||
<md-list-item></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="0"></md-list-item>
|
||||
<md-list-item type="button"></md-list-item>
|
||||
</md-list>`);
|
||||
const listEl = root.querySelector('md-list')!;
|
||||
const listHarness = new ListHarness(listEl);
|
||||
@ -435,9 +435,9 @@ describe('<md-list>', () => {
|
||||
async () => {
|
||||
const root = env.render(html`
|
||||
<md-list>
|
||||
<md-list-item disabled></md-list-item>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item tabindex="0"></md-list-item>
|
||||
<md-list-item type="button" disabled></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="0"></md-list-item>
|
||||
</md-list>`);
|
||||
const listEl = root.querySelector('md-list')!;
|
||||
const listHarness = new ListHarness(listEl);
|
||||
@ -463,9 +463,9 @@ describe('<md-list>', () => {
|
||||
async () => {
|
||||
const root = env.render(html`
|
||||
<md-list>
|
||||
<md-list-item tabindex="0"></md-list-item>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="0"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
</md-list>`);
|
||||
const listEl = root.querySelector('md-list')!;
|
||||
const listHarness = new ListHarness(listEl);
|
||||
@ -491,9 +491,9 @@ describe('<md-list>', () => {
|
||||
async () => {
|
||||
const root = env.render(html`
|
||||
<md-list>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item tabindex="0"></md-list-item>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="0"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
</md-list>`);
|
||||
const listEl = root.querySelector('md-list')!;
|
||||
const listHarness = new ListHarness(listEl);
|
||||
@ -519,9 +519,9 @@ describe('<md-list>', () => {
|
||||
async () => {
|
||||
const root = env.render(html`
|
||||
<md-list>
|
||||
<md-list-item></md-list-item>
|
||||
<md-list-item></md-list-item>
|
||||
<md-list-item disabled></md-list-item>
|
||||
<md-list-item type="button"></md-list-item>
|
||||
<md-list-item type="button"></md-list-item>
|
||||
<md-list-item type="button" disabled></md-list-item>
|
||||
</md-list>`);
|
||||
const listEl = root.querySelector('md-list')!;
|
||||
const listHarness = new ListHarness(listEl);
|
||||
@ -546,9 +546,9 @@ describe('<md-list>', () => {
|
||||
it('End will select the last item if it is already selected', async () => {
|
||||
const root = env.render(html`
|
||||
<md-list>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item tabindex="0"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="0"></md-list-item>
|
||||
</md-list>`);
|
||||
const listEl = root.querySelector('md-list')!;
|
||||
const listHarness = new ListHarness(listEl);
|
||||
@ -573,9 +573,9 @@ describe('<md-list>', () => {
|
||||
it('Clicking on an item will activate it', async () => {
|
||||
const root = env.render(html`
|
||||
<md-list>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item tabindex="0"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="0"></md-list-item>
|
||||
</md-list>`);
|
||||
const [first, second, third] =
|
||||
Array.from(root.querySelectorAll('md-list-item'));
|
||||
@ -601,9 +601,9 @@ describe('<md-list>', () => {
|
||||
it('List will activate only first item by default', async () => {
|
||||
const root = env.render(html`
|
||||
<md-list>
|
||||
<md-list-item></md-list-item>
|
||||
<md-list-item></md-list-item>
|
||||
<md-list-item></md-list-item>
|
||||
<md-list-item type="button"></md-list-item>
|
||||
<md-list-item type="button"></md-list-item>
|
||||
<md-list-item type="button"></md-list-item>
|
||||
</md-list>`);
|
||||
const [first, second, third] =
|
||||
Array.from(root.querySelectorAll('md-list-item'));
|
||||
@ -620,9 +620,9 @@ describe('<md-list>', () => {
|
||||
async () => {
|
||||
const root = env.render(html`
|
||||
<md-list>
|
||||
<md-list-item disabled></md-list-item>
|
||||
<md-list-item></md-list-item>
|
||||
<md-list-item></md-list-item>
|
||||
<md-list-item type="button" disabled></md-list-item>
|
||||
<md-list-item type="button"></md-list-item>
|
||||
<md-list-item type="button"></md-list-item>
|
||||
</md-list>`);
|
||||
const [first, second, third] =
|
||||
Array.from(root.querySelectorAll('md-list-item'));
|
||||
@ -639,9 +639,9 @@ describe('<md-list>', () => {
|
||||
async () => {
|
||||
const root = env.render(html`
|
||||
<md-list>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
</md-list>`);
|
||||
const [first, second, third] =
|
||||
Array.from(root.querySelectorAll('md-list-item'));
|
||||
@ -658,9 +658,9 @@ describe('<md-list>', () => {
|
||||
async () => {
|
||||
const root = env.render(html`
|
||||
<md-list>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item tabindex="2"></md-list-item>
|
||||
<md-list-item tabindex="1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="2"></md-list-item>
|
||||
<md-list-item type="button" tabindex="1"></md-list-item>
|
||||
</md-list>`);
|
||||
const [first, second, third] =
|
||||
Array.from(root.querySelectorAll('md-list-item'));
|
||||
@ -676,9 +676,9 @@ describe('<md-list>', () => {
|
||||
it('activateNextItem activates the next item', async () => {
|
||||
const root = env.render(html`
|
||||
<md-list>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item tabindex="0"></md-list-item>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="0"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
</md-list>`);
|
||||
const listEl = root.querySelector('md-list')!;
|
||||
const [first, second, third] =
|
||||
@ -703,9 +703,9 @@ describe('<md-list>', () => {
|
||||
it('activateNextItem will loop around', async () => {
|
||||
const root = env.render(html`
|
||||
<md-list>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item tabindex="0"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="0"></md-list-item>
|
||||
</md-list>`);
|
||||
const listEl = root.querySelector('md-list')!;
|
||||
const [first, second, third] =
|
||||
@ -745,9 +745,9 @@ describe('<md-list>', () => {
|
||||
it('activateNextItem does not activate disabled items', async () => {
|
||||
const root = env.render(html`
|
||||
<md-list>
|
||||
<md-list-item></md-list-item>
|
||||
<md-list-item disabled></md-list-item>
|
||||
<md-list-item></md-list-item>
|
||||
<md-list-item type="button"></md-list-item>
|
||||
<md-list-item type="button" disabled></md-list-item>
|
||||
<md-list-item type="button"></md-list-item>
|
||||
</md-list>`);
|
||||
const listEl = root.querySelector('md-list')!;
|
||||
const [first, second, third] =
|
||||
@ -773,7 +773,7 @@ describe('<md-list>', () => {
|
||||
async () => {
|
||||
const root = env.render(html`
|
||||
<md-list>
|
||||
<md-list-item tabindex="0"></md-list-item>
|
||||
<md-list-item type="button" tabindex="0"></md-list-item>
|
||||
</md-list>`);
|
||||
const listEl = root.querySelector('md-list')!;
|
||||
const first = root.querySelector('md-list-item')!;
|
||||
@ -793,9 +793,9 @@ describe('<md-list>', () => {
|
||||
it('activatePreviousItem activates the previous item', async () => {
|
||||
const root = env.render(html`
|
||||
<md-list>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item tabindex="0"></md-list-item>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" tabindex="0"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
</md-list>`);
|
||||
const listEl = root.querySelector('md-list')!;
|
||||
const [first, second, third] =
|
||||
@ -820,9 +820,9 @@ describe('<md-list>', () => {
|
||||
it('activatePreviousItem will loop around', async () => {
|
||||
const root = env.render(html`
|
||||
<md-list>
|
||||
<md-list-item></md-list-item>
|
||||
<md-list-item></md-list-item>
|
||||
<md-list-item></md-list-item>
|
||||
<md-list-item type="button"></md-list-item>
|
||||
<md-list-item type="button"></md-list-item>
|
||||
<md-list-item type="button"></md-list-item>
|
||||
</md-list>`);
|
||||
const listEl = root.querySelector('md-list')!;
|
||||
const [first, second, third] =
|
||||
@ -863,9 +863,9 @@ describe('<md-list>', () => {
|
||||
it('activatePreviousItem does not activate disabled items', async () => {
|
||||
const root = env.render(html`
|
||||
<md-list>
|
||||
<md-list-item tabindex="-1"></md-list-item>
|
||||
<md-list-item disabled></md-list-item>
|
||||
<md-list-item tabindex="0"></md-list-item>
|
||||
<md-list-item type="button" tabindex="-1"></md-list-item>
|
||||
<md-list-item type="button" disabled></md-list-item>
|
||||
<md-list-item type="button" tabindex="0"></md-list-item>
|
||||
</md-list>`);
|
||||
const listEl = root.querySelector('md-list')!;
|
||||
const [first, second, third] =
|
||||
@ -891,7 +891,7 @@ describe('<md-list>', () => {
|
||||
async () => {
|
||||
const root = env.render(html`
|
||||
<md-list>
|
||||
<md-list-item></md-list-item>
|
||||
<md-list-item type="button"></md-list-item>
|
||||
</md-list>`);
|
||||
const listEl = root.querySelector('md-list')!;
|
||||
const first = root.querySelector('md-list-item')!;
|
||||
@ -960,7 +960,7 @@ describe('<md-list-item>', () => {
|
||||
});
|
||||
|
||||
it('disabled overrides tabIndex', async () => {
|
||||
const root = env.render(html`<md-list-item></md-list-item>`);
|
||||
const root = env.render(html`<md-list-item type="button"></md-list-item>`);
|
||||
|
||||
const listItem = root.querySelector('md-list-item')!;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user