From 578a89f44cba8548130fa58b88221e90d821098b Mon Sep 17 00:00:00 2001 From: Mike Edmunds Date: Thu, 16 Jul 2026 19:18:59 -0700 Subject: [PATCH] Add focus option to selectTab method Allow an optional, second `options` argument to `selectTab()`. Add support for a `focus` option which controls whether the newly selected tab is given focus. The default is true, matching previous behavior without the option. Document the `selectTab()` method in the readme. --- README.md | 4 ++++ src/tab-container-element.ts | 7 +++++-- test/test.js | 20 ++++++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 62103ab..9d5fbe2 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,10 @@ If none of the tabs have `aria-selected=true`, then the first tab will be select - `tab-container-change` (bubbles, cancelable): fired on `` before a new tab is selected and visibility is updated. `event.tab` is the tab that will be focused, `event.tabIndex` is the 0-based index of the `tab` and `tab.panel` is the panel that will be shown if the event isn't cancelled. - `tab-container-changed` (bubbles): fired on `` after a new tab is selected and visibility is updated. `event.tab` is the tab that is now active (and will be focused right after this event), `event.tabIndex` is the 0-based index of the `tab` and `event.panel` is the newly visible tab panel. +### Methods + +- `selectTab(tabIndex, options: {focus?: boolean} = {})`: programmatically selects the tab with the given 0-based index and shows its panel. Fires events as described above. By default, sets focus to the selected tab; call with `{focus: false}` to prevent this. + ### Parts - `::part(tablist-wrapper)` is the wrapper which contains `before-tabs`, `tablist` and `after-tabs`. diff --git a/src/tab-container-element.ts b/src/tab-container-element.ts index 054c8e0..ccce06a 100644 --- a/src/tab-container-element.ts +++ b/src/tab-container-element.ts @@ -306,7 +306,8 @@ export class TabContainerElement extends HTMLElement { this.setAttribute('default-tab', String(index)) } - selectTab(index: number): void { + selectTab(index: number, options: {focus?: boolean} = {}): void { + const {focus = true} = options if (!this.#setupComplete) { const tabListSlot = this.#tabListSlot const tabListWrapper = this.#tabListWrapper @@ -405,7 +406,9 @@ export class TabContainerElement extends HTMLElement { selectedPanel.hidden = false if (this.#setupComplete) { - selectedTab.focus() + if (focus) { + selectedTab.focus() + } this.dispatchEvent( new TabContainerChangeEvent('tab-container-changed', { tabIndex: index, diff --git a/test/test.js b/test/test.js index 27c3059..e845f4c 100644 --- a/test/test.js +++ b/test/test.js @@ -343,6 +343,26 @@ describe('tab-container', function () { assert.deepStrictEqual(tabs.map(isSelected), [true, false, false], 'First tab is selected') assert.deepStrictEqual(panels.map(isHidden), [false, true, true], 'First panel is visible') }) + + it('`selectTab` supports `focus` option', function () { + const button = document.createElement('button') + button.textContent = 'button' + document.body.appendChild(button) + button.focus() + + tabContainer.selectTab(1, {focus: false}) + assert.equal(tabContainer.selectedTabIndex, 1) + assert.equal(document.activeElement, button) + + tabContainer.selectTab(2, {focus: true}) + assert.equal(tabContainer.selectedTabIndex, 2) + assert.equal(document.activeElement, tabs[2]) + + // Default is `focus: true`. + tabContainer.selectTab(0) + assert.equal(tabContainer.selectedTabIndex, 0) + assert.equal(document.activeElement, tabs[0]) + }) }) describe('nesting', function () {