diff --git a/packages/tabs/src/LionTabs.js b/packages/tabs/src/LionTabs.js index f7a5013f8..35d7a777f 100644 --- a/packages/tabs/src/LionTabs.js +++ b/packages/tabs/src/LionTabs.js @@ -34,9 +34,8 @@ const cleanButton = ({ element, clickHandler, keydownHandler, keyupHandler }) => element.removeEventListener('keydown', keydownHandler); }; -const selectButton = (element, firstUpdate = false) => { - // Don't focus on first update, as the component might be lower on the page - if (!firstUpdate) { +const selectButton = (element, withFocus = false) => { + if (withFocus) { element.focus(); } @@ -123,7 +122,6 @@ export class LionTabs extends LitElement { firstUpdated() { super.firstUpdated(); - this.__firstUpdate = true; this.__setupSlots(); } @@ -132,7 +130,7 @@ export class LionTabs extends LitElement { const handleSlotChange = () => { this.__cleanStore(); this.__setupStore(); - this.__updateSelected(); + this.__updateSelected(false); }; tabSlot.addEventListener('slotchange', handleSlotChange); } @@ -178,7 +176,7 @@ export class LionTabs extends LitElement { __createButtonClickHandler(index) { return () => { - this.selectedIndex = index; + this._setSelectedIndexWithFocus(index); }; } @@ -187,34 +185,41 @@ export class LionTabs extends LitElement { case 'ArrowDown': case 'ArrowRight': if (this.selectedIndex + 1 >= this._pairCount) { - this.selectedIndex = 0; + this._setSelectedIndexWithFocus(0); } else { - this.selectedIndex += 1; + this._setSelectedIndexWithFocus(this.selectedIndex + 1); } break; case 'ArrowUp': case 'ArrowLeft': if (this.selectedIndex <= 0) { - this.selectedIndex = this._pairCount - 1; + this._setSelectedIndexWithFocus(this._pairCount - 1); } else { - this.selectedIndex -= 1; + this._setSelectedIndexWithFocus(this.selectedIndex - 1); } break; case 'Home': - this.selectedIndex = 0; + this._setSelectedIndexWithFocus(0); break; case 'End': - this.selectedIndex = this._pairCount - 1; + this._setSelectedIndexWithFocus(this._pairCount - 1); break; /* no default */ } } set selectedIndex(value) { - this.__firstUpdate = false; const stale = this.__selectedIndex; this.__selectedIndex = value; - this.__updateSelected(); + this.__updateSelected(false); + this.dispatchEvent(new Event('selected-changed')); + this.requestUpdate('selectedIndex', stale); + } + + _setSelectedIndexWithFocus(value) { + const stale = this.__selectedIndex; + this.__selectedIndex = value; + this.__updateSelected(true); this.dispatchEvent(new Event('selected-changed')); this.requestUpdate('selectedIndex', stale); } @@ -227,7 +232,7 @@ export class LionTabs extends LitElement { return this.__store.length; } - __updateSelected() { + __updateSelected(withFocus = false) { if (!(this.__store && this.__store[this.selectedIndex])) { return; } @@ -245,7 +250,7 @@ export class LionTabs extends LitElement { } const { button: currentButton, panel: currentPanel } = this.__store[this.selectedIndex]; if (currentButton) { - selectButton(currentButton, this.__firstUpdate); + selectButton(currentButton, withFocus); } if (currentPanel) { selectPanel(currentPanel); diff --git a/packages/tabs/test/lion-tabs.test.js b/packages/tabs/test/lion-tabs.test.js index 478064c14..c58765612 100644 --- a/packages/tabs/test/lion-tabs.test.js +++ b/packages/tabs/test/lion-tabs.test.js @@ -244,7 +244,7 @@ describe('', () => { }); describe('Initializing without Focus', () => { - it('keeps track of when the component is updated', async () => { + it('does not focus a tab when setting selectedIndex property', async () => { const el = await fixture(html` @@ -254,9 +254,9 @@ describe('', () => { `); - expect(el.__firstUpdate).to.be.true; el.selectedIndex = 1; - expect(el.__firstUpdate).to.be.false; + expect(el.querySelector('[slot="tab"]:nth-of-type(2)') === document.activeElement).to.be + .false; }); it('does not focus a tab on firstUpdate', async () => { @@ -272,7 +272,7 @@ describe('', () => { expect(tabs.some(tab => tab === document.activeElement)).to.be.false; }); - it('focuses on a tab when switching the selectedIndex', async () => { + it('focuses on a tab when setting with _setSelectedIndexWithFocus method', async () => { const el = await fixture(html` @@ -281,12 +281,25 @@ describe('', () => {
panel 2
`); - el.selectedIndex = 1; - const tab = Array.from(el.children).filter(child => child.slot === 'tab')[1]; - expect(tab).to.equal(document.activeElement); + el._setSelectedIndexWithFocus(1); + expect(el.querySelector('[slot="tab"]:nth-of-type(2)') === document.activeElement).to.be.true; }); }); + it('focuses on a tab when the selected tab is changed by user interaction', async () => { + const el = await fixture(html` + + +
panel 1
+ +
panel 2
+
+ `); + const secondTab = el.querySelector('[slot="tab"]:nth-of-type(2)'); + secondTab.dispatchEvent(new MouseEvent('click')); + expect(secondTab === document.activeElement).to.be.true; + }); + describe('Accessibility', () => { it('does not make panels focusable', async () => { const el = await fixture(html`