diff --git a/README.md b/README.md index 426d338..f5b20ad 100644 --- a/README.md +++ b/README.md @@ -44,8 +44,8 @@ If none of the tabs have `aria-selected=true`, then the first tab will be select ### Events -- `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 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) and `event.panel` is the newly visible tab panel. +- `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. ### Parts diff --git a/custom-elements.json b/custom-elements.json index bb7e766..fe0601c 100644 --- a/custom-elements.json +++ b/custom-elements.json @@ -117,6 +117,11 @@ "name": "detail", "readonly": true }, + { + "kind": "field", + "name": "tabIndex", + "readonly": true + }, { "kind": "field", "name": "panel", @@ -317,6 +322,23 @@ "name": "detail", "readonly": true }, + { + "kind": "field", + "name": "#tabIndex", + "privacy": "private", + "type": { + "text": "number | null" + }, + "default": "null" + }, + { + "kind": "field", + "name": "tabIndex", + "type": { + "text": "number | null" + }, + "readonly": true + }, { "kind": "field", "name": "#panel", diff --git a/src/tab-container-element-define.ts b/src/tab-container-element-define.ts index 208ac57..ab9c4e3 100644 --- a/src/tab-container-element-define.ts +++ b/src/tab-container-element-define.ts @@ -1,4 +1,4 @@ -import {TabContainerElement} from './tab-container-element.js' +import {TabContainerChangeEvent, TabContainerElement} from './tab-container-element.js' const root = (typeof globalThis !== 'undefined' ? globalThis : window) as typeof window try { @@ -27,6 +27,14 @@ declare global { ['tab-container']: JSXBase['span'] & Partial> } } + interface GlobalEventHandlersEventMap { + 'tab-container-change': TabContainerChangeEvent + 'tab-container-changed': TabContainerChangeEvent + } + interface ElementEventMap { + 'tab-container-change': TabContainerChangeEvent + 'tab-container-changed': TabContainerChangeEvent + } } export default TabContainerElement diff --git a/src/tab-container-element.ts b/src/tab-container-element.ts index c01164d..831409d 100644 --- a/src/tab-container-element.ts +++ b/src/tab-container-element.ts @@ -2,9 +2,13 @@ const HTMLElement = globalThis.HTMLElement || (null as unknown as (typeof window const manualSlotsSupported = 'assign' in (globalThis.HTMLSlotElement?.prototype || {}) export class TabContainerChangeEvent extends Event { - constructor(type: string, {tab, panel, ...init}: EventInit & {tab?: Element; panel?: Element}) { + constructor( + type: string, + {tabIndex, tab, panel, ...init}: EventInit & {tabIndex?: number; tab?: Element; panel?: Element}, + ) { super(type, init) this.#tab = tab || null + this.#tabIndex = tabIndex || null this.#panel = panel || null } @@ -14,6 +18,11 @@ export class TabContainerChangeEvent extends Event { return {relatedTarget: this.#panel} } + #tabIndex: number | null = null + get tabIndex(): number | null { + return this.#tabIndex + } + #panel: Element | null = null get panel(): Element | null { return this.#panel @@ -341,6 +350,7 @@ export class TabContainerElement extends HTMLElement { if (this.#setupComplete) { const cancelled = !this.dispatchEvent( new TabContainerChangeEvent('tab-container-change', { + tabIndex: index, bubbles: true, cancelable: true, tab: selectedTab, @@ -376,6 +386,7 @@ export class TabContainerElement extends HTMLElement { selectedTab.focus() this.dispatchEvent( new TabContainerChangeEvent('tab-container-changed', { + tabIndex: index, bubbles: true, tab: selectedTab, panel: selectedPanel, diff --git a/test/test.js b/test/test.js index eb0c08f..441aa41 100644 --- a/test/test.js +++ b/test/test.js @@ -186,7 +186,7 @@ describe('tab-container', function () { expect(document.body).to.be.accessible() }) - it('click works and `tab-container-changed` event is dispatched', function () { + it('click works and `tab-container-changed` event is dispatched with correct index', function () { tabs[1].click() assert.deepStrictEqual(tabs.map(isSelected), [false, true, false], 'Second tab is selected') assert.deepStrictEqual(panels.map(isHidden), [true, false, true], 'Second panel is visible') @@ -201,6 +201,11 @@ describe('tab-container', function () { [tabs[1], tabs[1]], 'change events point to second tab', ) + assert.deepStrictEqual( + events.map(e => e.tabIndex), + [1, 1], + 'change events point to second tabIndex', + ) assert.deepStrictEqual( events.map(e => e.panel), [panels[1], panels[1]],