Skip to content

Commit

Permalink
fix(list): Add list type initialization logic
Browse files Browse the repository at this point in the history
  • Loading branch information
williamernest committed Aug 21, 2018
1 parent 7fe2be1 commit 06121ac
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 37 deletions.
8 changes: 4 additions & 4 deletions demos/list.html
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
info
</a>
</li>
<li class="mdc-list-item">
<li class="mdc-list-item mdc-list-item--activated">
<span class="mdc-list-item__graphic" role="presentation">
<i class="material-icons" aria-hidden="true">folder</i>
</span>
Expand Down Expand Up @@ -1136,9 +1136,9 @@ <h3>Example - Interactive List</h3>
var list = mdc.list.MDCList.attachTo(ele);
list.wrapFocus = true;

if (ele.parentElement.classList.contains('hero')) {
list.singleSelection = true;
}
// if (ele.parentElement.classList.contains('hero')) {
// list.singleSelection = true;
// }
});

addRippleToListItems(document.getElementById('leading-checkbox-list'));
Expand Down
21 changes: 11 additions & 10 deletions packages/mdc-list/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ OR
### Single Selection List

MDC List can handle selecting/deselecting list elements based on click or keyboard action. When enabled, the `space` and `enter` keys (or `click` event) will trigger an
single list item to become activated or deactivated.
single list item to become selected and any other previous selected element to become deselected.

```html
<ul id="my-list" class="mdc-list" aria-orientation="vertical">
Expand All @@ -153,12 +153,13 @@ list.singleSelection = true;
#### Pre-selected list item

When rendering the list with a pre-selected list item, the list item that needs to be selected should contain
the `mdc-list-item--activated` class and `aria-selected="true"` attribute before creating the list.
the `mdc-list-item--selected` or `mdc-list-item--activated` class and `aria-selected="true"` attribute before
creating the list.

```html
<ul id="my-list" class="mdc-list" aria-orientation="vertical">
<li class="mdc-list-item">Single-line item</li>
<li class="mdc-list-item mdc-list-item--activated" aria-selected="true" tabindex="0">Single-line item</li>
<li class="mdc-list-item mdc-list-item--selected" aria-selected="true" tabindex="0">Single-line item</li>
<li class="mdc-list-item">Single-line item</li>
</ul>
```
Expand Down Expand Up @@ -267,15 +268,15 @@ these should also receive `tabIndex="-1"`.
#### Setup in `singleSelection()`

When implementing a component that will use the single selection variant, the HTML should be modified to include
the `aria-selected` attribute, the `mdc-list-item--activated` class should be added, and the `tabindex` of the activated
element should be `0`. The first list item should have the `tabindex` updated to `-1`. The foundation method
`setSelectedIndex()` should be called with the initially activated element immediately after the foundation is
instantiated.
the `aria-selected` attribute, the `mdc-list-item--selected` or `mdc-list-item--activated` class should be added,
and the `tabindex` of the selected element should be `0`. The first list item should have the `tabindex` updated
to `-1`. The foundation method `setSelectedIndex()` should be called with the initially selected element immediately
after the foundation is instantiated.

```html
<ul id="my-list" class="mdc-list" aria-orientation="vertical">
<li class="mdc-list-item" tabindex="-1">Single-line item<button tabindex="-1"></button></li>
<li class="mdc-list-item mdc-list-item--activated" aria-selected="true" tabindex="0">Single-line item</li>
<li class="mdc-list-item mdc-list-item--selected" aria-selected="true" tabindex="0">Single-line item</li>
<li class="mdc-list-item" tabindex="-1">Single-line item</li>
</ul>
```
Expand All @@ -301,8 +302,8 @@ Method Signature | Description
`setWrapFocus(value: Boolean) => void` | Sets the list to allow the up arrow on the first element to focus the last element of the list and vice versa.
`setVerticalOrientation(value: Boolean) => void` | Sets the list to an orientation causing the keys used for navigation to change. `true` results in the Up/Down arrow keys being used. `false` results in the Left/Right arrow keys being used.
`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 `activated` state of the list item at index `index`.
`setUseSelectedClass(useSelected: boolean) => void` | Sets the selection logic to apply/remove the `mdc-list-item--selected` class.
`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.
`handleKeydown(evt: Event) => void` | Handles determining if a focus action should occur when a key event is triggered.
Expand Down
14 changes: 7 additions & 7 deletions packages/mdc-list/foundation.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class MDCListFoundation extends MDCFoundation {
/** {number} */
this.selectedIndex_ = -1;
/** {boolean} */
this.useSelectedClass_ = false;
this.useActivatedClass_ = false;
}

/**
Expand Down Expand Up @@ -92,11 +92,11 @@ class MDCListFoundation extends MDCFoundation {
}

/**
* Sets the useSelectedClass_ private variable.
* @param {boolean} useSelected
* Sets the useActivatedClass_ private variable.
* @param {boolean} useActivated
*/
setUseSelectedClass(useSelected) {
this.useSelectedClass_ = useSelected;
setUseActivatedClass(useActivated) {
this.useActivatedClass_ = useActivated;
}

/** @param {number} index */
Expand All @@ -105,8 +105,8 @@ class MDCListFoundation extends MDCFoundation {
return;
}

const className = this.useSelectedClass_
? cssClasses.LIST_ITEM_SELECTED_CLASS : cssClasses.LIST_ITEM_ACTIVATED_CLASS;
const className = this.useActivatedClass_
? cssClasses.LIST_ITEM_ACTIVATED_CLASS : cssClasses.LIST_ITEM_SELECTED_CLASS;

if (this.selectedIndex_ >= 0) {
this.adapter_.removeAttributeForElementIndex(this.selectedIndex_, strings.ARIA_SELECTED);
Expand Down
26 changes: 16 additions & 10 deletions packages/mdc-list/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class MDCList extends MDCComponent {
this.root_.addEventListener('focusin', this.focusInEventListener_);
this.root_.addEventListener('focusout', this.focusOutEventListener_);
this.layout();
this.initializeListType();
}

layout() {
Expand All @@ -78,6 +79,21 @@ class MDCList extends MDCComponent {
.forEach((ele) => ele.setAttribute('tabindex', -1));
}

initializeListType() {
// Automatically set single selection if selected/activated classes are present.
const preselectedElement =
this.root_.querySelector(`.${cssClasses.LIST_ITEM_ACTIVATED_CLASS}, .${cssClasses.LIST_ITEM_SELECTED_CLASS}`);

if (preselectedElement) {
if (preselectedElement.classList.contains(cssClasses.LIST_ITEM_ACTIVATED_CLASS)) {
this.foundation_.setUseActivatedClass(true);
}

this.singleSelection = true;
this.selectedIndex = this.listElements_.indexOf(preselectedElement);
}
}

/** @param {boolean} value */
set vertical(value) {
this.foundation_.setVerticalOrientation(value);
Expand All @@ -102,16 +118,6 @@ class MDCList extends MDCComponent {
}

this.foundation_.setSingleSelection(isSingleSelectionList);
const selectedElement = this.root_.querySelector('.mdc-list-item--selected');

if (selectedElement) {
this.selectedIndex = this.listElements_.indexOf(selectedElement);
}
}

/** @param {boolean} useSelected */
set useSelectedClass(useSelected) {
this.foundation_.setUseSelectedClass(useSelected);
}

/** @param {number} index */
Expand Down
9 changes: 9 additions & 0 deletions test/unit/mdc-list/foundation.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -648,3 +648,12 @@ test('#focusLastElement is called when the list is empty does not focus an eleme

td.verify(mockAdapter.focusItemAtIndex(td.matchers.anything()), {times: 0});
});

test('#setUseActivatedClass causes setSelectedIndex to use the --activated class', () => {
const {foundation, mockAdapter} = setupTest();
td.when(mockAdapter.getListItemCount()).thenReturn(3);
foundation.setUseActivatedClass(true);
foundation.setSelectedIndex(1);

td.verify(mockAdapter.addClassForElementIndex(1, cssClasses.LIST_ITEM_ACTIVATED_CLASS), {times: 1});
});
30 changes: 24 additions & 6 deletions test/unit/mdc-list/mdc-list.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,30 @@ test('component calls setVerticalOrientation(true) on the foundation if aria-ori
td.verify(mockFoundation.setVerticalOrientation(true), {times: 1});
});

test('#initializeListType sets the selectedIndex if a list item has the --selected class', () => {
const {root, component, mockFoundation} = setupTest();
root.querySelector('.mdc-list-item').classList.add(MDCListFoundation.cssClasses.LIST_ITEM_SELECTED_CLASS);
component.initializeListType();
td.verify(mockFoundation.setSelectedIndex(0), {times: 1});
td.verify(mockFoundation.setSingleSelection(true), {times: 1});
});

test('#initializeListType sets the selectedIndex if a list item has the --activated class', () => {
const {root, component, mockFoundation} = setupTest();
root.querySelector('.mdc-list-item').classList.add(MDCListFoundation.cssClasses.LIST_ITEM_ACTIVATED_CLASS);
component.initializeListType();
td.verify(mockFoundation.setSelectedIndex(0), {times: 1});
td.verify(mockFoundation.setSingleSelection(true), {times: 1});
});

test('#initializeListType calls the foundation if the --activated class is present', () => {
const {root, component, mockFoundation} = setupTest();
root.querySelector('.mdc-list-item').classList.add(MDCListFoundation.cssClasses.LIST_ITEM_ACTIVATED_CLASS);
component.initializeListType();
td.verify(mockFoundation.setUseActivatedClass(true), {times: 1});
td.verify(mockFoundation.setSingleSelection(true), {times: 1});
});

test('#adapter.getListItemCount returns correct number of list items', () => {
const {root, component} = setupTest();
document.body.appendChild(root);
Expand Down Expand Up @@ -259,12 +283,6 @@ test('wrapFocus calls setWrapFocus on foundation', () => {
td.verify(mockFoundation.setWrapFocus(true), {times: 1});
});

test('singleSelection true sets the selectedIndex if a list item has the --selected class', () => {
const {root, component, mockFoundation} = setupTest();
root.querySelector('.mdc-list-item').classList.add(MDCListFoundation.cssClasses.LIST_ITEM_SELECTED_CLASS);
component.singleSelection = true;
td.verify(mockFoundation.setSelectedIndex(0), {times: 1});
});

test('singleSelection true sets the click handler from the root element', () => {
const {root, component, mockFoundation} = setupTest();
Expand Down

0 comments on commit 06121ac

Please sign in to comment.