diff --git a/src/elements/tabs/tab-group/tab-group.spec.ts b/src/elements/tabs/tab-group/tab-group.spec.ts
index deadfabdc5..3d2bf5d934 100644
--- a/src/elements/tabs/tab-group/tab-group.spec.ts
+++ b/src/elements/tabs/tab-group/tab-group.spec.ts
@@ -1,4 +1,4 @@
-import { assert, expect } from '@open-wc/testing';
+import { assert, aTimeout, expect } from '@open-wc/testing';
import { sendKeys } from '@web/test-runner-commands';
import { html } from 'lit/static-html.js';
@@ -15,136 +15,138 @@ import '../tab.js';
describe(`sbb-tab-group`, () => {
let element: SbbTabGroupElement;
- beforeEach(async () => {
- element = await fixture(
- html`
- Test tab label 1
- Test tab content 1
- Test tab label 2
- Test tab content 2
- Test tab label 3
- Test tab content 3
- Test tab label 4
- Test tab content 4
- `,
- );
- });
+ describe('basic', () => {
+ beforeEach(async () => {
+ element = await fixture(
+ html`
+ Test tab label 1
+ Test tab content 1
+ Test tab label 2
+ Test tab content 2
+ Test tab label 3
+ Test tab content 3
+ Test tab label 4
+ Test tab content 4
+ `,
+ );
+ });
- it('renders', () => {
- assert.instanceOf(element, SbbTabGroupElement);
- });
+ it('renders', () => {
+ assert.instanceOf(element, SbbTabGroupElement);
+ });
- it('renders tab content', async () => {
- const content = element.querySelector(
- ':scope > sbb-tab-label:first-of-type + sbb-tab',
- )!;
+ it('renders tab content', async () => {
+ const content = element.querySelector(
+ ':scope > sbb-tab-label:first-of-type + sbb-tab',
+ )!;
- expect(content.textContent).to.be.equal('Test tab content 1');
- });
+ expect(content.textContent).to.be.equal('Test tab content 1');
+ });
- it('renders initial selected index', async () => {
- const initialSelectedTab = element.querySelector(':scope > sbb-tab-label#sbb-tab-2');
+ it('renders initial selected index', async () => {
+ const initialSelectedTab = element.querySelector(':scope > sbb-tab-label#sbb-tab-2');
- expect(initialSelectedTab).to.have.attribute('active');
- });
+ expect(initialSelectedTab).to.have.attribute('active');
+ });
- it('activates tab by index', async () => {
- element.activateTab(1);
- await waitForLitRender(element);
- const tab = element.querySelectorAll('sbb-tab-label')[1];
+ it('activates tab by index', async () => {
+ element.activateTab(1);
+ await waitForLitRender(element);
+ const tab = element.querySelectorAll('sbb-tab-label')[1];
- expect(tab).to.have.attribute('active');
- });
+ expect(tab).to.have.attribute('active');
+ });
- it('disables tab by index', async () => {
- element.disableTab(0);
- await waitForLitRender(element);
- const tab = element.querySelectorAll('sbb-tab-label')[0];
+ it('disables tab by index', async () => {
+ element.disableTab(0);
+ await waitForLitRender(element);
+ const tab = element.querySelectorAll('sbb-tab-label')[0];
- expect(tab).to.have.attribute('disabled');
- });
+ expect(tab).to.have.attribute('disabled');
+ });
- it('enables tab by index', async () => {
- element.enableTab(2);
- await waitForLitRender(element);
- const tab = element.querySelectorAll('sbb-tab-label')[2];
+ it('enables tab by index', async () => {
+ element.enableTab(2);
+ await waitForLitRender(element);
+ const tab = element.querySelectorAll('sbb-tab-label')[2];
- expect(tab).not.to.have.attribute('disabled');
- });
+ expect(tab).not.to.have.attribute('disabled');
+ });
- it('does not activate a disabled tab', async () => {
- const tab = element.querySelectorAll('sbb-tab-label')[2];
+ it('does not activate a disabled tab', async () => {
+ const tab = element.querySelectorAll('sbb-tab-label')[2];
- tab.disabled = true;
- element.activateTab(2);
- await waitForLitRender(element);
- expect(tab).not.to.have.attribute('active');
- });
+ tab.disabled = true;
+ element.activateTab(2);
+ await waitForLitRender(element);
+ expect(tab).not.to.have.attribute('active');
+ });
- it('activates the first tab', () => {
- const tab = element.querySelectorAll('sbb-tab-label')[1];
+ it('activates the first tab', () => {
+ const tab = element.querySelectorAll('sbb-tab-label')[1];
- expect(tab).to.have.attribute('active');
- });
+ expect(tab).to.have.attribute('active');
+ });
- describe('events', () => {
- it('selects tab on click', async () => {
- const tab = element.querySelector(':scope > sbb-tab-label#sbb-tab-1')!;
+ describe('events', () => {
+ it('selects tab on click', async () => {
+ const tab = element.querySelector(':scope > sbb-tab-label#sbb-tab-1')!;
- tab.click();
- await waitForLitRender(element);
+ tab.click();
+ await waitForLitRender(element);
- expect(tab).to.have.attribute('active');
- });
+ expect(tab).to.have.attribute('active');
+ });
- it('dispatches event on tab change', async () => {
- const tab = element.querySelector(':scope > sbb-tab-label#sbb-tab-1')!;
- const changeSpy = new EventSpy(SbbTabGroupElement.events.didChange);
+ it('dispatches event on tab change', async () => {
+ const tab = element.querySelector(':scope > sbb-tab-label#sbb-tab-1')!;
+ const changeSpy = new EventSpy(SbbTabGroupElement.events.didChange);
- tab.click();
- await changeSpy.calledOnce();
- expect(changeSpy.count).to.be.equal(1);
- });
+ tab.click();
+ await changeSpy.calledOnce();
+ expect(changeSpy.count).to.be.equal(1);
+ });
- it('selects tab on left arrow key pressed', async () => {
- await sendKeys({ press: tabKey });
- await sendKeys({ press: 'ArrowLeft' });
- await waitForLitRender(element);
- const tab = element.querySelector(':scope > sbb-tab-label#sbb-tab-1');
+ it('selects tab on left arrow key pressed', async () => {
+ await sendKeys({ press: tabKey });
+ await sendKeys({ press: 'ArrowLeft' });
+ await waitForLitRender(element);
+ const tab = element.querySelector(':scope > sbb-tab-label#sbb-tab-1');
- expect(tab).to.have.attribute('active');
- });
+ expect(tab).to.have.attribute('active');
+ });
- it('selects tab on right arrow key pressed', async () => {
- await sendKeys({ press: tabKey });
- await sendKeys({ press: 'ArrowRight' });
- await waitForLitRender(element);
+ it('selects tab on right arrow key pressed', async () => {
+ await sendKeys({ press: tabKey });
+ await sendKeys({ press: 'ArrowRight' });
+ await waitForLitRender(element);
- const tab = element.querySelector(':scope > sbb-tab-label#sbb-tab-4');
+ const tab = element.querySelector(':scope > sbb-tab-label#sbb-tab-4');
- expect(tab).to.have.attribute('active');
- });
+ expect(tab).to.have.attribute('active');
+ });
- it('wraps around on arrow key navigation', async () => {
- await sendKeys({ press: tabKey });
- await sendKeys({ press: 'ArrowRight' });
- await sendKeys({ press: 'ArrowRight' });
- await waitForLitRender(element);
+ it('wraps around on arrow key navigation', async () => {
+ await sendKeys({ press: tabKey });
+ await sendKeys({ press: 'ArrowRight' });
+ await sendKeys({ press: 'ArrowRight' });
+ await waitForLitRender(element);
- const tab = element.querySelector(':scope > sbb-tab-label#sbb-tab-1');
+ const tab = element.querySelector(':scope > sbb-tab-label#sbb-tab-1');
- expect(tab).to.have.attribute('active');
- });
+ expect(tab).to.have.attribute('active');
+ });
- it('wraps around on arrow left arrow key navigation', async () => {
- await sendKeys({ press: tabKey });
- await sendKeys({ press: 'ArrowLeft' });
- await sendKeys({ press: 'ArrowLeft' });
- await waitForLitRender(element);
+ it('wraps around on arrow left arrow key navigation', async () => {
+ await sendKeys({ press: tabKey });
+ await sendKeys({ press: 'ArrowLeft' });
+ await sendKeys({ press: 'ArrowLeft' });
+ await waitForLitRender(element);
- const tab = element.querySelector(':scope > sbb-tab-label#sbb-tab-4');
+ const tab = element.querySelector(':scope > sbb-tab-label#sbb-tab-4');
- expect(tab).to.have.attribute('active');
+ expect(tab).to.have.attribute('active');
+ });
});
});
@@ -173,4 +175,58 @@ describe(`sbb-tab-group`, () => {
const tab = element.querySelector('sbb-tab-label#sbb-tab-2');
expect(tab).to.have.attribute('active');
});
+
+ it('recovers if active tabs are added later', async () => {
+ element = await fixture(html``);
+ const changeSpy = new EventSpy(SbbTabGroupElement.events.didChange);
+
+ const newLabel = document.createElement('sbb-tab-label');
+ newLabel.textContent = 'Label 1';
+ newLabel.toggleAttribute('active', true);
+ const newTab = document.createElement('sbb-tab');
+ newTab.textContent = 'Tab 1';
+
+ element.append(newLabel, newTab);
+
+ await waitForLitRender(element);
+ // Await throttling
+ await aTimeout(200);
+
+ // console.log(element._selectedTab)
+ const newLabelActive = document.createElement('sbb-tab-label');
+ newLabelActive.textContent = 'Label 2';
+ const newTabActive = document.createElement('sbb-tab');
+ newTabActive.textContent = 'Tab 2';
+
+ element.append(newLabelActive, newTabActive);
+
+ await waitForLitRender(element);
+ // Await throttling
+ await aTimeout(200);
+
+ expect(changeSpy.count).to.be.equal(1);
+ expect(element.querySelector('sbb-tab-label')).to.have.attribute('active');
+ expect(element.querySelector('sbb-tab-label')).to.have.attribute('aria-selected', 'true');
+ expect(element.querySelector('sbb-tab')).to.have.attribute('active');
+ expect(element.querySelector('sbb-tab-label:nth-of-type(2)')).not.to.have.attribute('active');
+ expect(element.querySelector('sbb-tab-label:nth-of-type(2)')).to.have.attribute(
+ 'aria-selected',
+ 'false',
+ );
+ expect(element.querySelector('sbb-tab:nth-of-type(2)')).not.to.have.attribute('active');
+
+ newLabelActive.click();
+ await waitForLitRender(element);
+
+ expect(changeSpy.count).to.be.equal(2);
+ expect(element.querySelector('sbb-tab-label')).not.to.have.attribute('active');
+ expect(element.querySelector('sbb-tab-label')).to.have.attribute('aria-selected', 'false');
+ expect(element.querySelector('sbb-tab')).not.to.have.attribute('active');
+ expect(element.querySelector('sbb-tab-label:nth-of-type(2)')).to.have.attribute('active');
+ expect(element.querySelector('sbb-tab-label:nth-of-type(2)')).to.have.attribute(
+ 'aria-selected',
+ 'true',
+ );
+ expect(element.querySelector('sbb-tab:nth-of-type(2)')).to.have.attribute('active');
+ });
});
diff --git a/src/elements/tabs/tab-group/tab-group.ts b/src/elements/tabs/tab-group/tab-group.ts
index 7cf1a3ca27..384dc2b0a7 100644
--- a/src/elements/tabs/tab-group/tab-group.ts
+++ b/src/elements/tabs/tab-group/tab-group.ts
@@ -171,6 +171,9 @@ class SbbTabGroupElement extends SbbHydrationMixin(LitElement) {
if (loadedTabs.length) {
loadedTabs.forEach((tab) => this._configure(tab));
this._tabs = this._tabs.concat(loadedTabs);
+
+ // If there is an active tab in the new batch, it becomes the new selected
+ loadedTabs.find((tab) => tab.active)?.tabGroupActions?.select();
}
};
@@ -246,10 +249,13 @@ class SbbTabGroupElement extends SbbHydrationMixin(LitElement) {
}
private _onTabContentElementResize(entries: ResizeObserverEntry[]): void {
+ if (!this._tabContentElement) {
+ return;
+ }
for (const entry of entries) {
const contentHeight = Math.floor(entry.contentRect.height);
- (this._tabContentElement as HTMLElement).style.height = `${contentHeight}px`;
+ this._tabContentElement.style.height = `${contentHeight}px`;
}
}
@@ -319,6 +325,7 @@ class SbbTabGroupElement extends SbbHydrationMixin(LitElement) {
}
},
};
+
if (tabLabel.nextElementSibling?.localName === 'sbb-tab') {
tabLabel.tab = tabLabel.nextElementSibling as SbbTabElement;
tabLabel.tab.id = this._assignId();
@@ -337,7 +344,7 @@ class SbbTabGroupElement extends SbbHydrationMixin(LitElement) {
tabLabel.disabled = tabLabel.hasAttribute('disabled');
tabLabel.active = tabLabel.hasAttribute('active') && !tabLabel.disabled;
tabLabel.setAttribute('role', 'tab');
- tabLabel.setAttribute('aria-selected', 'false');
+ tabLabel.setAttribute('aria-selected', String(tabLabel.active));
tabLabel.addEventListener('click', () => {
tabLabel.tabGroupActions?.select();
});