Skip to content

Commit

Permalink
fix: Recursively check for last item in nested lists (#3798)
Browse files Browse the repository at this point in the history
* Recursively check for last item in nested lists

* Update grid focus tests with deep nesting

* Simplify with do...while; Always return previousElement
  • Loading branch information
bearfriend authored Jul 10, 2023
1 parent 2ad96d6 commit edd4a24
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 19 deletions.
18 changes: 10 additions & 8 deletions components/list/list-item-generic-layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -413,16 +413,18 @@ class ListItemGenericLayout extends RtlMixin(LitElement) {
while (previousElement) {
if (previousElement.role === 'rowgroup') {

// this check needs to account for standard list-items as well as custom
const nestedList = previousElement.querySelector('[slot="nested"]') || previousElement.shadowRoot.querySelector('d2l-list');
if (nestedList) {
const nestedListItems = [...nestedList.children].filter(node => node.role === 'rowgroup');
if (nestedListItems && nestedListItems.length > 0) {
return nestedListItems[nestedListItems.length - 1];
let nestedList;
do {
// this check needs to account for standard list-items as well as custom
nestedList = previousElement.querySelector('[slot="nested"]') || previousElement.shadowRoot.querySelector('d2l-list');
if (nestedList) {
const nestedListItems = [...nestedList.children].filter(node => node.role === 'rowgroup');
if (nestedListItems.length) {
previousElement = nestedListItems[nestedListItems.length - 1];
}
}
}
} while (nestedList);
return previousElement;

}
previousElement = previousElement.previousElementSibling;
}
Expand Down
21 changes: 10 additions & 11 deletions components/list/test/list.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,10 @@ describe('d2l-list', () => {
});

[
{ keyPress: 'ArrowUp', expectedFocus: 'L1-1' },
{ keyPress: 'ArrowDown', expectedFocus: 'L3-2' }
].forEach(testCase => {
it(`Focus navigates to other nested list items when in grid mode after "${testCase.keyPress}" is pressed`, async() => {
{ name: 'Previous', keyPress: 'ArrowUp', initialFocus: 'L1-2' },
{ name: 'Next', keyPress: 'ArrowDown', initialFocus: 'L2-2' }
].forEach(({ name, keyPress, initialFocus }) => {
it(`Focus navigates to ${name.toLowerCase()} list items when in grid mode after "${keyPress}" is pressed`, async() => {
const elem = await fixture(html`
<d2l-list id="L1" grid>
<d2l-list-item selectable key="L1-1" label="item">
Expand All @@ -121,19 +121,18 @@ describe('d2l-list', () => {
</d2l-list-item>
</d2l-list>
</d2l-list-item>
<d2l-list-item selectable key="L1-2" label="item"></d2l-list-item>
</d2l-list>
`);

const listItem = elem.querySelector('[key="L2-2"]');
const listItemLayout = elem.querySelector('[key="L2-2"]').shadowRoot.querySelector('d2l-list-item-generic-layout');
const listItem = elem.querySelector(`[key="${initialFocus}"]`);
const listItemLayout = listItem.shadowRoot.querySelector('d2l-list-item-generic-layout');
await focusElem(listItem);
await waitUntil(() => listItem.hasAttribute('_focusing'), 'Initial item should be focused', { timeout: 3000 });

setTimeout(() => sendKeysElem(listItemLayout, 'down', testCase.keyPress));
setTimeout(() => sendKeysElem(listItemLayout, 'down', keyPress));
await oneEvent(listItemLayout, 'keydown');

const focusedElement = elem.querySelector(`[key=${testCase.expectedFocus}`);
await waitUntil(() => focusedElement.hasAttribute('_focusing'), 'Next item should be focused', { timeout: 3000 });
const focusedElement = elem.querySelector('[key="L3-2"');
await waitUntil(() => focusedElement.hasAttribute('_focusing'), `${name} item should be focused`, { timeout: 3000 });
});
});

Expand Down

0 comments on commit edd4a24

Please sign in to comment.