fix(tabs): do not focus tabs when selectedIndex is set (#729)

This commit is contained in:
Mathieu Puech 2020-05-27 10:44:39 -04:00 committed by GitHub
parent eabab4b78b
commit e4ec227566
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 41 additions and 23 deletions

View file

@ -34,9 +34,8 @@ const cleanButton = ({ element, clickHandler, keydownHandler, keyupHandler }) =>
element.removeEventListener('keydown', keydownHandler); element.removeEventListener('keydown', keydownHandler);
}; };
const selectButton = (element, firstUpdate = false) => { const selectButton = (element, withFocus = false) => {
// Don't focus on first update, as the component might be lower on the page if (withFocus) {
if (!firstUpdate) {
element.focus(); element.focus();
} }
@ -123,7 +122,6 @@ export class LionTabs extends LitElement {
firstUpdated() { firstUpdated() {
super.firstUpdated(); super.firstUpdated();
this.__firstUpdate = true;
this.__setupSlots(); this.__setupSlots();
} }
@ -132,7 +130,7 @@ export class LionTabs extends LitElement {
const handleSlotChange = () => { const handleSlotChange = () => {
this.__cleanStore(); this.__cleanStore();
this.__setupStore(); this.__setupStore();
this.__updateSelected(); this.__updateSelected(false);
}; };
tabSlot.addEventListener('slotchange', handleSlotChange); tabSlot.addEventListener('slotchange', handleSlotChange);
} }
@ -178,7 +176,7 @@ export class LionTabs extends LitElement {
__createButtonClickHandler(index) { __createButtonClickHandler(index) {
return () => { return () => {
this.selectedIndex = index; this._setSelectedIndexWithFocus(index);
}; };
} }
@ -187,34 +185,41 @@ export class LionTabs extends LitElement {
case 'ArrowDown': case 'ArrowDown':
case 'ArrowRight': case 'ArrowRight':
if (this.selectedIndex + 1 >= this._pairCount) { if (this.selectedIndex + 1 >= this._pairCount) {
this.selectedIndex = 0; this._setSelectedIndexWithFocus(0);
} else { } else {
this.selectedIndex += 1; this._setSelectedIndexWithFocus(this.selectedIndex + 1);
} }
break; break;
case 'ArrowUp': case 'ArrowUp':
case 'ArrowLeft': case 'ArrowLeft':
if (this.selectedIndex <= 0) { if (this.selectedIndex <= 0) {
this.selectedIndex = this._pairCount - 1; this._setSelectedIndexWithFocus(this._pairCount - 1);
} else { } else {
this.selectedIndex -= 1; this._setSelectedIndexWithFocus(this.selectedIndex - 1);
} }
break; break;
case 'Home': case 'Home':
this.selectedIndex = 0; this._setSelectedIndexWithFocus(0);
break; break;
case 'End': case 'End':
this.selectedIndex = this._pairCount - 1; this._setSelectedIndexWithFocus(this._pairCount - 1);
break; break;
/* no default */ /* no default */
} }
} }
set selectedIndex(value) { set selectedIndex(value) {
this.__firstUpdate = false;
const stale = this.__selectedIndex; const stale = this.__selectedIndex;
this.__selectedIndex = value; 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.dispatchEvent(new Event('selected-changed'));
this.requestUpdate('selectedIndex', stale); this.requestUpdate('selectedIndex', stale);
} }
@ -227,7 +232,7 @@ export class LionTabs extends LitElement {
return this.__store.length; return this.__store.length;
} }
__updateSelected() { __updateSelected(withFocus = false) {
if (!(this.__store && this.__store[this.selectedIndex])) { if (!(this.__store && this.__store[this.selectedIndex])) {
return; return;
} }
@ -245,7 +250,7 @@ export class LionTabs extends LitElement {
} }
const { button: currentButton, panel: currentPanel } = this.__store[this.selectedIndex]; const { button: currentButton, panel: currentPanel } = this.__store[this.selectedIndex];
if (currentButton) { if (currentButton) {
selectButton(currentButton, this.__firstUpdate); selectButton(currentButton, withFocus);
} }
if (currentPanel) { if (currentPanel) {
selectPanel(currentPanel); selectPanel(currentPanel);

View file

@ -244,7 +244,7 @@ describe('<lion-tabs>', () => {
}); });
describe('Initializing without Focus', () => { 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` const el = await fixture(html`
<lion-tabs> <lion-tabs>
<button slot="tab">tab 1</button> <button slot="tab">tab 1</button>
@ -254,9 +254,9 @@ describe('<lion-tabs>', () => {
</lion-tabs> </lion-tabs>
`); `);
expect(el.__firstUpdate).to.be.true;
el.selectedIndex = 1; 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 () => { it('does not focus a tab on firstUpdate', async () => {
@ -272,7 +272,7 @@ describe('<lion-tabs>', () => {
expect(tabs.some(tab => tab === document.activeElement)).to.be.false; 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` const el = await fixture(html`
<lion-tabs> <lion-tabs>
<button slot="tab">tab 1</button> <button slot="tab">tab 1</button>
@ -281,12 +281,25 @@ describe('<lion-tabs>', () => {
<div slot="panel">panel 2</div> <div slot="panel">panel 2</div>
</lion-tabs> </lion-tabs>
`); `);
el.selectedIndex = 1; el._setSelectedIndexWithFocus(1);
const tab = Array.from(el.children).filter(child => child.slot === 'tab')[1]; expect(el.querySelector('[slot="tab"]:nth-of-type(2)') === document.activeElement).to.be.true;
expect(tab).to.equal(document.activeElement);
}); });
}); });
it('focuses on a tab when the selected tab is changed by user interaction', async () => {
const el = await fixture(html`
<lion-tabs>
<button slot="tab">tab 1</button>
<div slot="panel">panel 1</div>
<button slot="tab">tab 2</button>
<div slot="panel">panel 2</div>
</lion-tabs>
`);
const secondTab = el.querySelector('[slot="tab"]:nth-of-type(2)');
secondTab.dispatchEvent(new MouseEvent('click'));
expect(secondTab === document.activeElement).to.be.true;
});
describe('Accessibility', () => { describe('Accessibility', () => {
it('does not make panels focusable', async () => { it('does not make panels focusable', async () => {
const el = await fixture(html` const el = await fixture(html`