diff --git a/README.md b/README.md index 09354f6..62103ab 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ If none of the tabs have `aria-selected=true`, then the first tab will be select +``` ### Events @@ -109,6 +110,7 @@ In those cases, apply `data-tab-container-no-tabstop` to the `tabpanel` element. ### Unmanaged slots `` aims to simplify complex markup away in the ShadowDOM, so that the HTML you end up writing is overall less. However sometimes it can be useful to have _full_ control over the markup. Each of the `::part` selectors are also ``s, this means you can take any part and slot it, overriding the built-in ShadowDOM. + #### Unmanaged `tablist` You are able to provide your own `role=tablist` and `` will accommodate. This can be useful if you need extra presentational markup in the tablist. But remember: @@ -148,6 +150,7 @@ You are able to slot the `tablist-tab-wrapper` part. This slot manages the tabs
``` + #### Unmanaged `tablist-wrapper` If you want to take full control over the entire tab region, including managing the content before and after the tabs, then you can slot the `tablist-wrapper` element. Bear in mind if you're supplying this element that: diff --git a/examples/index.html b/examples/index.html index 12fc8e7..238e319 100644 --- a/examples/index.html +++ b/examples/index.html @@ -67,27 +67,6 @@

Horizontal (custom tablist and tablist-wrapper)

-

Horizontal (custom tablist and tablist-tab-wrapper)

- - -
-
- - - -
-
-
- Panel 1 -
- - -
-

Vertical (shadow tablist)

@@ -144,7 +123,7 @@

Set initially selected tab

Set default tab

- + diff --git a/src/tab-container-element.ts b/src/tab-container-element.ts index 93e625a..ebccbd9 100644 --- a/src/tab-container-element.ts +++ b/src/tab-container-element.ts @@ -1,5 +1,20 @@ const HTMLElement = globalThis.HTMLElement || (null as unknown as (typeof window)['HTMLElement']) -const manualSlotsSupported = 'assign' in (globalThis.HTMLSlotElement?.prototype || {}) + +// Function to see if manual slots are supported and if not, manual assign the slot attribute +const assignSlotWithFallback = + 'assign' in (globalThis.HTMLSlotElement?.prototype || {}) + ? (slot: HTMLSlotElement, ...elements: Element[]) => { + slot.assign(...elements) + } + : (slot: HTMLSlotElement, ...elements: Element[]) => { + const host = (slot.getRootNode() as ShadowRoot).host + for (const element of host.querySelectorAll(`[slot="${slot.name}"]`)) { + element.removeAttribute('slot') + } + for (const element of elements) { + element.setAttribute('slot', slot.name) + } + } export class TabContainerChangeEvent extends Event { constructor( @@ -95,7 +110,7 @@ export class TabContainerElement extends HTMLElement { static observedAttributes = ['vertical'] get #tabList() { - const wrapper = this.querySelector('[slot=tablist-wrapper],[slot=tablist-tab-wrapper]') + const wrapper = this.querySelector('[slot=tablist-wrapper]') if (wrapper?.closest(this.tagName) === this) { return wrapper.querySelector('[role=tablist]') as HTMLElement } @@ -112,7 +127,7 @@ export class TabContainerElement extends HTMLElement { } get #tabListTabWrapper() { - return this.shadowRoot!.querySelector('slot[part="tablist-tab-wrapper"]')! + return this.shadowRoot!.querySelector('div[part="tablist-tab-wrapper"]')! } get #beforeTabsSlot() { @@ -174,7 +189,7 @@ export class TabContainerElement extends HTMLElement { tabListContainer.style.display = 'flex' tabListContainer.setAttribute('part', 'tablist-wrapper') tabListContainer.setAttribute('name', 'tablist-wrapper') - const tabListTabWrapper = document.createElement('slot') + const tabListTabWrapper = document.createElement('div') tabListTabWrapper.setAttribute('part', 'tablist-tab-wrapper') tabListTabWrapper.setAttribute('name', 'tablist-tab-wrapper') const tabListSlot = document.createElement('slot') @@ -184,7 +199,6 @@ export class TabContainerElement extends HTMLElement { const panelSlot = document.createElement('slot') panelSlot.setAttribute('part', 'panel') panelSlot.setAttribute('name', 'panel') - panelSlot.setAttribute('role', 'presentation') const beforeTabSlot = document.createElement('slot') beforeTabSlot.setAttribute('part', 'before-tabs') beforeTabSlot.setAttribute('name', 'before-tabs') @@ -285,37 +299,15 @@ export class TabContainerElement extends HTMLElement { if (!this.#setupComplete) { const tabListSlot = this.#tabListSlot const tabListWrapper = this.#tabListWrapper - const tabListTabWrapper = this.#tabListTabWrapper const customTabList = this.querySelector('[role=tablist]') const customTabListWrapper = this.querySelector('[slot=tablist-wrapper]') - const customTabListTabWrapper = this.querySelector('[slot=tablist-tab-wrapper]') if (customTabListWrapper && customTabListWrapper.closest(this.tagName) === this) { - if (manualSlotsSupported) { - tabListWrapper.assign(customTabListWrapper) - } else { - customTabListWrapper.setAttribute('slot', 'tablist-wrapper') - } - } else if (customTabListTabWrapper && customTabListTabWrapper.closest(this.tagName) === this) { - if (manualSlotsSupported) { - tabListTabWrapper.assign(customTabListTabWrapper) - } else { - customTabListTabWrapper.setAttribute('slot', 'tablist-tab-wrapper') - } + assignSlotWithFallback(tabListWrapper, customTabListWrapper) } else if (customTabList && customTabList.closest(this.tagName) === this) { - if (manualSlotsSupported) { - tabListSlot.assign(customTabList) - } else { - customTabList.setAttribute('slot', 'tablist') - } + assignSlotWithFallback(tabListSlot, customTabList) } else { this.#tabListTabWrapper.role = 'tablist' - if (manualSlotsSupported) { - tabListSlot.assign(...[...this.children].filter(e => e.matches('[role=tab]'))) - } else { - for (const e of this.children) { - if (e.matches('[role=tab]')) e.setAttribute('slot', 'tablist') - } - } + assignSlotWithFallback(tabListSlot, ...[...this.children].filter(e => e.matches('[role=tab]'))) } const tabList = this.#tabList this.#reflectAttributeToShadow('aria-description', tabList) @@ -330,11 +322,7 @@ export class TabContainerElement extends HTMLElement { const afterSlotted: Element[] = [] let autoSlotted = beforeSlotted for (const child of this.children) { - if ( - child.getAttribute('role') === 'tab' || - child.getAttribute('role') === 'tablist' || - child.getAttribute('slot') === 'tablist-tab-wrapper' - ) { + if (child.getAttribute('role') === 'tab' || child.getAttribute('role') === 'tablist') { autoSlotted = afterTabSlotted continue } @@ -350,15 +338,9 @@ export class TabContainerElement extends HTMLElement { autoSlotted.push(child) } } - if (manualSlotsSupported) { - this.#beforeTabsSlot.assign(...beforeSlotted) - this.#afterTabsSlot.assign(...afterTabSlotted) - this.#afterPanelsSlot.assign(...afterSlotted) - } else { - for (const el of beforeSlotted) el.setAttribute('slot', 'before-tabs') - for (const el of afterTabSlotted) el.setAttribute('slot', 'after-tabs') - for (const el of afterSlotted) el.setAttribute('slot', 'after-panels') - } + assignSlotWithFallback(this.#beforeTabsSlot, ...beforeSlotted) + assignSlotWithFallback(this.#afterTabsSlot, ...afterTabSlotted) + assignSlotWithFallback(this.#afterPanelsSlot, ...afterSlotted) } const defaultTab = this.defaultTabIndex const defaultIndex = defaultTab >= 0 ? defaultTab : this.selectedTabIndex @@ -401,18 +383,11 @@ export class TabContainerElement extends HTMLElement { if (!panel.hasAttribute('tabindex') && !panel.hasAttribute('data-tab-container-no-tabstop')) { panel.setAttribute('tabindex', '0') } - if (!manualSlotsSupported && panel.hasAttribute('slot')) { - panel.removeAttribute('slot') - } } selectedTab.setAttribute('aria-selected', 'true') selectedTab.setAttribute('tabindex', '0') - if (manualSlotsSupported) { - this.#panelSlot.assign(selectedPanel) - } else { - selectedPanel.setAttribute('slot', 'panel') - } + assignSlotWithFallback(this.#panelSlot, selectedPanel) selectedPanel.hidden = false if (this.#setupComplete) { diff --git a/test/test.js b/test/test.js index f5dce71..a65a03d 100644 --- a/test/test.js +++ b/test/test.js @@ -719,54 +719,4 @@ describe('tab-container', function () { assert.deepStrictEqual(panels.map(isHidden), [false, true, true], 'First panel is visible') }) }) - - describe('with custom tablist-tab-wrapper', function () { - beforeEach(function () { - document.body.innerHTML = ` - -
-
- - - -
-
- -
- Panel 2 -
- -
- ` - tabs = Array.from(document.querySelectorAll('button')) - panels = Array.from(document.querySelectorAll('[role="tabpanel"]')) - }) - - afterEach(function () { - // Check to make sure we still have accessible markup after the test finishes running. - expect(document.body).to.be.accessible() - - document.body.innerHTML = '' - }) - - it('has accessible markup', function () { - expect(document.body).to.be.accessible() - }) - - it('the second tab is still selected', function () { - assert.deepStrictEqual(tabs.map(isSelected), [false, true, false], 'Second tab is selected') - assert.deepStrictEqual(panels.map(isHidden), [true, false, true], 'Second panel is visible') - }) - - it('selects the clicked tab', function () { - tabs[0].click() - - assert.deepStrictEqual(tabs.map(isSelected), [true, false, false], 'First tab is selected') - assert.deepStrictEqual(panels.map(isHidden), [false, true, true], 'First panel is visible') - }) - }) }) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy