Skip to content

Commit

Permalink
fix(CQ-4295097): Accessibility - Spectrum SelectList user is not able…
Browse files Browse the repository at this point in the history
… to select next group with keyboard (#127)

* CQ-4295097 : Accessibility - Spectrum SelectList user is not able to select next group with keyboard

CQ-4295097 refactor _tabTarget setter and _resetTabTarget method

With SelectList groups only one item should receive focus at a time.

When resetting tabTarget, prioritized selected items over selectable items, but prioritize the last focused item over the first of the items in the array.

In Select component, we _resetTabTarget before the list opens, prioritizing the first selected item over the first selectable item.

* CQ-4295097 SelectableCollection: fix _getPreviousSelectable and _getNextSelectable

Fixes TabList unit test failures.

* fix(CQ-4295097): add unit tests for keyboard navigation between groups

* fix(CQ-4295097): refactor per code comments

* fix(CQ-4295097): Updates per code comments

Co-authored-by: abdul pathan <pathan@adobe.com>
  • Loading branch information
majornista and abdul pathan committed Oct 19, 2020
1 parent b8b5463 commit c80e629
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 17 deletions.
25 changes: 17 additions & 8 deletions coral-collection/src/scripts/SelectableCollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,21 +87,26 @@ class SelectableCollection extends Collection {
@protected
*/
_getPreviousSelectable(item) {
let sibling = item.previousElementSibling;
const items = this.getAll();
let index = items.indexOf(item);
let sibling = index > 0 ? items[index - 1] : null;

while (sibling) {
if (sibling.matches(this._selectableItemSelector)) {
break;
}
else {
sibling = sibling.previousElementSibling;
index--;
sibling = index > 0 ? items[index - 1] : null;
}
}

return sibling || item;

// in case the item is not specified, or it is not inside the collection, we need to return the first selectable
return sibling || (item.matches(this._selectableItemSelector) ? item : this._getFirstSelectable());
}

/**
Returns the net selectable item.
Returns the next selectable item.
@param {HTMLElement} item
The reference item.
Expand All @@ -112,16 +117,20 @@ class SelectableCollection extends Collection {
@protected
*/
_getNextSelectable(item) {
let sibling = item.nextElementSibling;
const items = this.getAll();
let index = items.indexOf(item);
let sibling = index < items.length - 1 ? items[index + 1] : null;

while (sibling) {
if (sibling.matches(this._selectableItemSelector)) {
break;
}
else {
sibling = sibling.nextElementSibling;
index++;
sibling = index < items.length - 1 ? items[index + 1] : null;
}
}

return sibling || item;
}

Expand Down
24 changes: 15 additions & 9 deletions coral-component-list/src/scripts/SelectList.js
Original file line number Diff line number Diff line change
Expand Up @@ -218,12 +218,13 @@ class SelectList extends BaseComponent(HTMLElement) {
get _tabTarget() {
return this.__tabTarget || null;
}

set _tabTarget(value) {
this.__tabTarget = value;

// Set all but the current set _tabTarget to not be a tab target:
this.items.getAll().forEach((item) => {
item.setAttribute('tabindex', item === value ? 0 : -1);
this.items._getSelectableItems().forEach((item) => {
item.setAttribute('tabindex', !value || item === value ? 0 : -1);
});
}

Expand Down Expand Up @@ -277,14 +278,14 @@ class SelectList extends BaseComponent(HTMLElement) {
/** @private */
_focusPreviousItem(event) {
event.preventDefault();

this._focusItem(this.items._getPreviousSelectable(event.target));
}

/** @private */
_focusNextItem(event) {
event.preventDefault();

this._focusItem(this.items._getNextSelectable(event.target));
}

Expand Down Expand Up @@ -335,7 +336,7 @@ class SelectList extends BaseComponent(HTMLElement) {
}, this._keypressTimeoutDuration);

// Search within selectable items
const selectableItems = this.items._getSelectableItems();
const selectableItems = this.items._getSelectableItems().filter(item => !item.hasAttribute('hidden') && item.offsetParent);

// Remember the index of the focused item within the array of selectable items
const currentIndex = selectableItems.indexOf(this._tabTarget);
Expand Down Expand Up @@ -402,9 +403,14 @@ class SelectList extends BaseComponent(HTMLElement) {
@private
*/
_resetTabTarget() {
const selectedItems = this.items._getAllSelected().filter(item => !item.hasAttribute('hidden'));
this._tabTarget = selectedItems.length ? selectedItems[0] : this.items._getSelectableItems().filter(item => !item.hasAttribute('hidden'))[0];
_resetTabTarget(forceFirst = false) {
let items = this.items._getAllSelected().filter(item => !item.hasAttribute('hidden') && item.offsetParent);

if (items.length === 0) {
items = this.items._getSelectableItems().filter(item => !item.hasAttribute('hidden') && item.offsetParent);
}

this._tabTarget = forceFirst ? items[0] : (items.find(item => item.tabIndex === 0) || items[0]);
}

/** @private */
Expand Down
65 changes: 65 additions & 0 deletions coral-component-list/src/tests/test.SelectList.js
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,71 @@ describe('SelectList', function() {
done();
});
});

describe('groups', function() {
let el;
let groups;
let item1;
let item2;

beforeEach(function() {
el = helpers.build(window.__html__['SelectList.groups.html']);
});

afterEach(function() {
el.remove();
el = groups = item1 = item2 = null;
});

it('navigates between groups using ArrowUp/ArrowDown', function() {
groups = el.groups.getAll();

// last item in first group
item1 = groups[0].items.last();

// first item in last group
item2 = groups[1].items.first();

// focus last item in first group
item1.focus();

// ArrowDown focuses first item in next group
helpers.keypress('down', item1);

// verify focus on first item of second group
expect(item2).equals(document.activeElement);

// ArrowUp focuses last item in previous group
helpers.keypress('up', item2);

// verify focus on last item of first group
expect(item1).equals(document.activeElement);

// PageDown focuses first item in next group
helpers.keypress('pagedown', item1);

// verify focus on first item of second group
expect(item2).equals(document.activeElement);

// PageUp focuses last item in previous group
helpers.keypress('pageup', item2);

// verify focus on last item of first group
expect(item1).equals(document.activeElement);

// End focuses last item of last group
helpers.keypress('end', item1);

// verify focus on last item of last group
expect(groups[1].items.last()).equals(document.activeElement);

// Home focuses first item of first group
helpers.keypress('home', item1);

// verify focus on first item of first group
expect(groups[0].items.first()).equals(document.activeElement);
});
});
// @todo: test focus of initial state
// @todo: test focus of an empty list
// @todo: test focus of a list with a selected item
Expand Down
3 changes: 3 additions & 0 deletions coral-component-select/src/scripts/Select.js
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,9 @@ class Select extends BaseFormField(BaseComponent(HTMLElement)) {
if (this.readOnly) {
event.preventDefault();
}

// focus first selected or tabbable item when the list expands
this._elements.list._resetTabTarget(true);
}

/** @private */
Expand Down

0 comments on commit c80e629

Please sign in to comment.