diff --git a/.changeset/stale-tips-begin.md b/.changeset/stale-tips-begin.md new file mode 100644 index 000000000..a4a5b1614 --- /dev/null +++ b/.changeset/stale-tips-begin.md @@ -0,0 +1,5 @@ +--- +'@lion/ui': patch +--- + +Handle focusin event in invokers in LionAccordion. Fix tabbing issues. diff --git a/packages/ui/components/accordion/src/LionAccordion.js b/packages/ui/components/accordion/src/LionAccordion.js index e3ecffc8c..b3fa4a6dd 100644 --- a/packages/ui/components/accordion/src/LionAccordion.js +++ b/packages/ui/components/accordion/src/LionAccordion.js @@ -10,6 +10,7 @@ import { uuid } from '@lion/ui/core.js'; * @property {HTMLElement} content content node * @property {(event: Event) => unknown} clickHandler executed on click event * @property {(event: Event) => unknown} keydownHandler executed on keydown event + * @property {(event: Event) => unknown} focusHandler executed on focusin event */ /** @@ -197,6 +198,7 @@ export class LionAccordion extends LitElement { content, clickHandler: this.__createInvokerClickHandler(index), keydownHandler: this.__handleInvokerKeydown.bind(this), + focusHandler: this.__createInvokerFocusHandler(index), }; this._setupContent(entry); this._setupInvoker(entry); @@ -248,6 +250,19 @@ export class LionAccordion extends LitElement { }; } + /** + * @param {number} index + * @private + */ + __createInvokerFocusHandler(index) { + return () => { + if (index === this.focusedIndex) { + return; + } + this.focusedIndex = index; + }; + } + /** * @param {Event} e * @private @@ -304,7 +319,7 @@ export class LionAccordion extends LitElement { * @protected */ _setupInvoker(entry) { - const { invoker, uid, index, clickHandler, keydownHandler } = entry; + const { invoker, uid, index, clickHandler, keydownHandler, focusHandler } = entry; invoker.style.setProperty('order', `${index + 1}`); const firstChild = invoker.firstElementChild; if (firstChild) { @@ -312,6 +327,7 @@ export class LionAccordion extends LitElement { firstChild.setAttribute('aria-controls', `content-${uid}`); firstChild.addEventListener('click', clickHandler); firstChild.addEventListener('keydown', keydownHandler); + firstChild.addEventListener('focusin', focusHandler); } } @@ -320,13 +336,14 @@ export class LionAccordion extends LitElement { * @protected */ _cleanInvoker(entry) { - const { invoker, clickHandler, keydownHandler } = entry; + const { invoker, clickHandler, keydownHandler, focusHandler } = entry; const firstChild = invoker.firstElementChild; if (firstChild) { firstChild.removeAttribute('id'); firstChild.removeAttribute('aria-controls'); firstChild.removeEventListener('click', clickHandler); firstChild.removeEventListener('keydown', keydownHandler); + firstChild.removeEventListener('focusin', focusHandler); } } diff --git a/packages/ui/components/accordion/test/lion-accordion.test.js b/packages/ui/components/accordion/test/lion-accordion.test.js index 54b045b58..a5f27743c 100644 --- a/packages/ui/components/accordion/test/lion-accordion.test.js +++ b/packages/ui/components/accordion/test/lion-accordion.test.js @@ -213,6 +213,17 @@ describe('', () => { el.focusedIndex = 1; expect(spy).to.have.been.calledOnce; }); + + it('tabbing sets the focusedIndex correctly', async () => { + const el = /** @type {LionAccordion} */ (await fixture(basicAccordion)); + const invokers = getInvokers(el); + el.focusedIndex = 0; + expect(el.focusedIndex).to.equal(0); + invokers[2].firstElementChild?.dispatchEvent(new Event('focusin')); + expect(el.focusedIndex).to.equal(2); + invokers[1].firstElementChild?.dispatchEvent(new Event('focusin')); + expect(el.focusedIndex).to.equal(1); + }); }); describe('Accordion Contents (slot=content)', () => {