Skip to content
This repository has been archived by the owner on Jan 13, 2025. It is now read-only.

Commit

Permalink
feat(list): Update list to toggle tabindex of radio/checkbox (#3542)
Browse files Browse the repository at this point in the history
  • Loading branch information
williamernest authored Sep 13, 2018
1 parent e203aa4 commit 13abb24
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 19 deletions.
22 changes: 11 additions & 11 deletions packages/mdc-list/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -268,12 +268,12 @@ The MDCList JavaScript component implements the WAI-ARIA best practices for
[Listbox](https://www.w3.org/TR/wai-aria-practices-1.1/#Listbox). This includes overriding the default tab behavior
within the list component. You should not add `tabindex` to any of the `li` elements in a list.

As the user navigates through the list, any `button` or `a` elements within the list will receive `tabindex="-1"`
when the list item is not focused. When the list item receives focus, the child `button` and `a` elements will
receive `tabIndex="0"`. This allows for the user to tab through list item elements and then tab to the
first element after the list. The `Arrow`, `Home`, and `End` keys should be used for navigating internal list elements.
If `singleSelection=true`, the list will allow the user to use the `Space` or `Enter` keys to select or deselect
a list item. The MDCList will perform the following actions for each key press
As the user navigates through the list, any `button`, `a`, `input[type="radio"]` and `input[type="checkbox"]` elements
within the list will receive `tabindex="-1"` when the list item is not focused. When the list item receives focus, the
aforementioned elements will receive `tabIndex="0"`. This allows for the user to tab through list item elements
and then tab to the first element after the list. The `Arrow`, `Home`, and `End` keys should be used for navigating
internal list elements. If `singleSelection=true`, the list will allow the user to use the `Space` or `Enter` keys to
select or deselect a list item. The MDCList will perform the following actions for each key press

Key | Action
--- | ---
Expand All @@ -300,8 +300,8 @@ The default component requires that every list item receives a `tabindex` value
(`li` elements cannot receive focus at all without a `tabindex` value). Any element not already containing a
`tabindex` attribute will receive `tabindex=-1`. The first list item should have `tabindex="0"` so that the
user can find the first element using the `tab` key, but subsequent `tab` keys strokes will cause focus to
skip over the entire list. If the list items contain sub-elements that are focusable (`button` or `a` elements),
these should also receive `tabIndex="-1"`.
skip over the entire list. If the list items contain sub-elements that are focusable (`button`, `a`,
`input[type="radio]`, and `input[type="checkbox"]` elements), these should also receive `tabIndex="-1"`.

```html
<ul id="my-list" class="mdc-list" aria-orientation="vertical">
Expand Down Expand Up @@ -352,7 +352,7 @@ Method Signature | Description
`addClassForElementIndex(index: Number, className: String) => void` | Adds the `className` class to the list item at `index`.
`removeClassForElementIndex(index: Number, className: String) => void` | Removes the `className` class to the list item at `index`.
`focusItemAtIndex(index: Number) => void` | Focuses the list item at the `index` value specified.
`setTabIndexForListItemChildren(index: Number, value: Number) => void` | Sets the `tabindex` attribute to `value` for each child `button` and `a` element in the list item at the `index` specified.
`setTabIndexForListItemChildren(index: Number, value: Number) => void` | Sets the `tabindex` attribute to `value` for each child button, anchor, radio button and checkbox element in the list item at the `index` specified.
`followHref(element: Element) => void` | If the given element has an href, follows the link.

### `MDCListFoundation`
Expand All @@ -364,8 +364,8 @@ Method Signature | Description
`setSingleSelection(value: Boolean) => void` | Sets the list to be a selection list. Enables the `enter` and `space` keys for selecting/deselecting a list item.
`setSelectedIndex(index: Number) => void` | Toggles the `selected` state of the list item at index `index`.
`setUseActivated(useActivated: boolean) => void` | Sets the selection logic to apply/remove the `mdc-list-item--activated` class.
`handleFocusIn(evt: Event) => void` | Handles the changing of `tabindex` to `0` for all `button` and `a` elements when a list item receives focus.
`handleFocusOut(evt: Event) => void` | Handles the changing of `tabindex` to `-1` for all `button` and `a` elements when a list item loses focus.
`handleFocusIn(evt: Event) => void` | Handles the changing of `tabindex` to `0` for all button, anchor, radio, and checkbox elements when a list item receives focus.
`handleFocusOut(evt: Event) => void` | Handles the changing of `tabindex` to `-1` for all button, anchor, radio and checkbox elements when a list item loses focus.
`handleKeydown(evt: Event) => void` | Handles determining if a focus action should occur when a key event is triggered.
`handleClick(evt: Event) => void` | Handles toggling the selected/deselected state for a list item when clicked. This method is only used by the single selection list.
`focusNextElement(index: Number) => void` | Handles focusing the next element using the current `index`.
Expand Down
4 changes: 3 additions & 1 deletion packages/mdc-list/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ const strings = {
ARIA_ORIENTATION: 'aria-orientation',
ARIA_ORIENTATION_HORIZONTAL: 'horizontal',
ARIA_SELECTED: 'aria-selected',
FOCUSABLE_CHILD_ELEMENTS: `.${cssClasses.LIST_ITEM_CLASS} button:not(:disabled), .${cssClasses.LIST_ITEM_CLASS} a`,
FOCUSABLE_CHILD_ELEMENTS: `.${cssClasses.LIST_ITEM_CLASS} button:not(:disabled), .${cssClasses.LIST_ITEM_CLASS} a,
.${cssClasses.LIST_ITEM_CLASS} input[type="radio"]:not(:disabled),
.${cssClasses.LIST_ITEM_CLASS} input[type="checkbox"]:not(:disabled)`,
ENABLED_ITEMS_SELECTOR: '.mdc-list-item:not(.mdc-list-item--disabled)',
};

Expand Down
21 changes: 14 additions & 7 deletions test/unit/mdc-list/mdc-list.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,17 @@ function getFixture() {
Fruit
<button>one</button>
</li>
<li class="mdc-list-item">
Potato
<a href="http://www.google.com">Link</a>
</li>
<li class="mdc-list-item">
Pasta
<button>two</button>
<input type="checkbox"/>
</li>
<li class="mdc-list-item">
Pizza
<input type="radio"/>
</li>
</ul>
`;
Expand Down Expand Up @@ -216,15 +221,17 @@ test('adapter#focusItemAtIndex focuses the list item at the index specified', ()
document.body.removeChild(root);
});

test('adapter#setTabIndexForListItemChildren sets the child button/a elements of index', () => {
test('adapter#setTabIndexForListItemChildren sets the child button/a/radio/checkbox elements of index', () => {
const {root, component} = setupTest();
document.body.appendChild(root);
const listItemIndex = 1;
const listItem = root.querySelectorAll('.mdc-list-item')[listItemIndex];
component.getDefaultFoundation().adapter_.setTabIndexForListItemChildren(listItemIndex, 0);
const listItems = root.querySelectorAll('.mdc-list-item');

for (let index = 0; index < listItems.length; index++) {
assert.equal(0, listItems[index].querySelectorAll('[tabindex="0"]').length);
component.getDefaultFoundation().adapter_.setTabIndexForListItemChildren(index, 0);
assert.equal(1, listItems[index].querySelectorAll('[tabindex="0"]').length);
}

assert.equal(1, root.querySelectorAll('button[tabindex="0"]').length);
assert.equal(listItem, root.querySelectorAll('button[tabindex="0"]')[0].parentElement);
document.body.removeChild(root);
});

Expand Down

0 comments on commit 13abb24

Please sign in to comment.