fix(@lion/ui): [select-rich] listbox element always focused in overlay

when the overlay is shown, the "autofocus" attribute is added to _listboxNode (_inputNode) to make sure that keyboard navigation continues to work when the element is inside a bottomsheet. When the overlay is closed the attribute is removed.
This commit is contained in:
Danny Moerkerke 2023-02-09 18:41:18 +01:00 committed by GitHub
parent faae93376a
commit 84173cdba1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 44 additions and 0 deletions

View file

@ -0,0 +1,5 @@
---
'@lion/ui': patch
---
lion-select-rich: when the overlay is shown, the "autofocus" attribute is added to \_listboxNode (\_inputNode) to make sure that keyboard navigation continues to work when the element is inside a an element with `trapsKeyboardFocus:true`, like the bottomsheet created via `withBottomSheetConfig()`. When the overlay is closed the attribute is removed.

View file

@ -369,6 +369,11 @@ export class LionSelectRich extends SlotMixin(ScopedElementsMixin(OverlayMixin(L
if (this.hasNoDefaultSelected) {
this._noDefaultSelectedInheritsWidth();
}
// When `trapsKeyboardFocus:true` is configured in our overlay, we need to make sure
// that our element with [role=listbox] gets focus. The `containFocus` util will look
// for an element with [autofocus] (otherwise it sets focus to `contentNode` (the 'root' of the overlay))
this._listboxNode.setAttribute('autofocus', '');
}
/** @private */
@ -382,6 +387,7 @@ export class LionSelectRich extends SlotMixin(ScopedElementsMixin(OverlayMixin(L
/** @private */
__overlayOnHide() {
this._invokerNode.focus();
this._listboxNode.removeAttribute('autofocus');
}
/**

View file

@ -424,6 +424,39 @@ describe('lion-select-rich', () => {
expect(el.singleOption).to.be.false;
expect(_invokerNode.singleOption).to.be.false;
});
it('adds/removes the autofocus attribute to/from _listboxNode', async () => {
const el = await fixture(html` <lion-select-rich></lion-select-rich> `);
const { _listboxNode } = getSelectRichMembers(el);
expect(_listboxNode.hasAttribute('autofocus')).to.be.false;
el.opened = true;
await el.updateComplete;
expect(_listboxNode.hasAttribute('autofocus')).to.be.true;
el.opened = false;
await el.updateComplete;
await el.updateComplete; // safari takes a little longer
expect(_listboxNode.hasAttribute('autofocus')).to.be.false;
});
it('adds focus to element with [role=listbox] when trapsKeyboardFocus is true', async () => {
const el = await fixture(
html` <lion-select-rich .config=${{ trapsKeyboardFocus: true }}></lion-select-rich> `,
);
const { _listboxNode } = getSelectRichMembers(el);
expect(document.activeElement).to.not.equal(_listboxNode);
el.opened = true;
await el.updateComplete;
expect(document.activeElement).to.equal(_listboxNode);
el.opened = false;
await el.updateComplete;
await el.updateComplete; // safari takes a little longer
expect(document.activeElement).to.not.equal(_listboxNode);
});
});
describe('interaction-mode', () => {