Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(tabs): ensure tabs become enabled when parent disabled attribute is removed #5323

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/twenty-ads-shake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@spectrum-web-components/tabs': patch
---

Fixed a bug where removing the `disabled` attribute (or setting it to `false`) on an `sp-tabs` element would not correctly enable the selected `sp-tab`. The fix updates the `focusInIndex` method in the component's `RovingTabindexController` to properly identify the selected tab that should become focusable when the parent tabs component is enabled.
4 changes: 2 additions & 2 deletions packages/tabs/src/Tabs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ export class Tabs extends SizedMixin(Focusable, { noDefaultSize: true }) {
let focusInIndex = 0;
const firstFocusableElement = elements.find((el, index) => {
const focusInElement = this.selected
? !el.disabled && el.value === this.selected
? el.value === this.selected
: !el.disabled;
focusInIndex = index;
return focusInElement;
Expand All @@ -224,7 +224,7 @@ export class Tabs extends SizedMixin(Focusable, { noDefaultSize: true }) {
this.selectTarget(el);
},
elements: () => this.tabs,
isFocusableElement: (el) => !el.disabled,
isFocusableElement: (el) => !this.disabled && !el.disabled,
listenerScope: () => this.tabList,
});

Expand Down
126 changes: 66 additions & 60 deletions packages/tabs/test/tabs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,16 @@ import {
import { sendKeys } from '@web/test-runner-commands';

const createTabs = async (): Promise<Tabs> => {
const tabs = await fixture<Tabs>(
html`
<sp-tabs selected="first">
<sp-tab label="Tab 1" value="first"></sp-tab>
<sp-tab label="Tab 2" value="second"></sp-tab>
<sp-tab label="Tab 3" value="third"></sp-tab>
<sp-tab-panel value="first">First tab content</sp-tab-panel>
<sp-tab-panel value="second">Second tab content</sp-tab-panel>
<sp-tab-panel value="third">Third tab content</sp-tab-panel>
</sp-tabs>
`
);
const tabs = await fixture<Tabs>(html`
<sp-tabs selected="first">
<sp-tab label="Tab 1" value="first"></sp-tab>
<sp-tab label="Tab 2" value="second"></sp-tab>
<sp-tab label="Tab 3" value="third"></sp-tab>
<sp-tab-panel value="first">First tab content</sp-tab-panel>
<sp-tab-panel value="second">Second tab content</sp-tab-panel>
<sp-tab-panel value="third">Third tab content</sp-tab-panel>
</sp-tabs>
`);
await elementUpdated(tabs);
return tabs;
};
Expand All @@ -63,15 +61,13 @@ describe('Tabs', () => {
});

it('loads accessibly w/o panels', async () => {
const tabs = await fixture<Tabs>(
html`
<sp-tabs selected="first">
<sp-tab value="first">Tab 1</sp-tab>
<sp-tab value="second">Tab 2</sp-tab>
<sp-tab value="third">Tab 3</sp-tab>
</sp-tabs>
`
);
const tabs = await fixture<Tabs>(html`
<sp-tabs selected="first">
<sp-tab value="first">Tab 1</sp-tab>
<sp-tab value="second">Tab 2</sp-tab>
<sp-tab value="third">Tab 3</sp-tab>
</sp-tabs>
`);

const tabList = tabs.querySelectorAll('sp-tab');

Expand All @@ -92,6 +88,26 @@ describe('Tabs', () => {
expect(tabs.selected).to.equal('first');
});

it('can have disabled state set to true and then to false', async () => {
const tabs = await createTabs();
tabs.disabled = true;
await elementUpdated(tabs);

expect(tabs.selected).to.equal('first');
const selectedTab = tabs.querySelector('sp-tab[selected]') as Tab;
expect(selectedTab.disabled).to.be.true;

const anotherTab = tabs.querySelector('[label="Tab 3"]') as Tab;
anotherTab.click();
await elementUpdated(tabs);
expect(tabs.selected).to.equal('first');

tabs.disabled = false;
await elementUpdated(tabs);
expect(tabs.selected).to.equal('first');
expect(selectedTab.disabled).to.be.false;
});

it('can have disabled sp-tab children', async () => {
const tabs = await createTabs();
const tab2 = tabs.querySelector('[label="Tab 2"]') as Tab;
Expand Down Expand Up @@ -157,15 +173,13 @@ describe('Tabs', () => {
});

it('autofocuses', async () => {
const tabs = await fixture<Tabs>(
html`
<sp-tabs selected="second" autofocus>
<sp-tab label="Tab 1" value="first"></sp-tab>
<sp-tab label="Tab 2" value="second"></sp-tab>
<sp-tab label="Tab 3" value="third"></sp-tab>
</sp-tabs>
`
);
const tabs = await fixture<Tabs>(html`
<sp-tabs selected="second" autofocus>
<sp-tab label="Tab 1" value="first"></sp-tab>
<sp-tab label="Tab 2" value="second"></sp-tab>
<sp-tab label="Tab 3" value="third"></sp-tab>
</sp-tabs>
`);

await elementUpdated(tabs);

Expand All @@ -178,15 +192,13 @@ describe('Tabs', () => {
});

it('auto', async () => {
const el = await fixture<Tabs>(
html`
<sp-tabs selected="second" auto>
<sp-tab label="Tab 1" value="first"></sp-tab>
<sp-tab label="Tab 2" value="second"></sp-tab>
<sp-tab label="Tab 3" value="third"></sp-tab>
</sp-tabs>
`
);
const el = await fixture<Tabs>(html`
<sp-tabs selected="second" auto>
<sp-tab label="Tab 1" value="first"></sp-tab>
<sp-tab label="Tab 2" value="second"></sp-tab>
<sp-tab label="Tab 3" value="third"></sp-tab>
</sp-tabs>
`);

await elementUpdated(el);

Expand Down Expand Up @@ -427,11 +439,9 @@ describe('Tabs', () => {
}
}
customElements.define('tab-test-el', TabTestEl);
const el = await fixture<TabTestEl>(
html`
<tab-test-el></tab-test-el>
`
);
const el = await fixture<TabTestEl>(html`
<tab-test-el></tab-test-el>
`);

await elementUpdated(el);
const rootNode = el.shadowRoot as ShadowRoot;
Expand Down Expand Up @@ -528,14 +538,12 @@ describe('Tabs', () => {
.true;
});
it('selects through slotted DOM', async () => {
const el = await fixture<Tabs>(
html`
<sp-tabs selected="first">
<sp-tab value="first">Tab 1</sp-tab>
<sp-tab value="second"><span>Tab 2</span></sp-tab>
</sp-tabs>
`
);
const el = await fixture<Tabs>(html`
<sp-tabs selected="first">
<sp-tab value="first">Tab 1</sp-tab>
<sp-tab value="second"><span>Tab 2</span></sp-tab>
</sp-tabs>
`);
const span = el.querySelector('span') as HTMLSpanElement;
await elementUpdated(el);

Expand All @@ -547,14 +555,12 @@ describe('Tabs', () => {
expect(el.selected).to.equal('second');
});
it('updates selection indicator in response to tab updates', async () => {
const el = await fixture<Tabs>(
html`
<sp-tabs selected="first">
<sp-tab value="first">Tab 1</sp-tab>
<sp-tab value="second">Tab 2</sp-tab>
</sp-tabs>
`
);
const el = await fixture<Tabs>(html`
<sp-tabs selected="first">
<sp-tab value="first">Tab 1</sp-tab>
<sp-tab value="second">Tab 2</sp-tab>
</sp-tabs>
`);
const selected = el.querySelector('[value="first"]') as Tab;
await elementUpdated(el);

Expand Down
Loading