Skip to content

Commit 59f84a7

Browse files
committed
Add dynamic handling for _tabs changes and E2E tests
Refs: #7390
1 parent ef6a319 commit 59f84a7

File tree

2 files changed

+52
-26
lines changed

2 files changed

+52
-26
lines changed

packages/components/src/components/tabs/shadow.tsx

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,7 @@ export class KolTabs implements TabsAPI {
385385
{
386386
hooks: {
387387
beforePatch: this.syncSelectedAndTabs,
388+
afterPatch: this.refreshTabPanels,
388389
},
389390
},
390391
);
@@ -400,37 +401,43 @@ export class KolTabs implements TabsAPI {
400401
this.validateBehavior(this._behavior);
401402
}
402403

403-
private readonly handleTabPanels = () => {
404-
if (this.tabPanelHost instanceof HTMLDivElement) {
405-
for (let i = this.tabPanelHost.children.length; i < this.state._tabs.length; i++) {
406-
const div = document.createElement('div');
407-
div.setAttribute('aria-labelledby', `${this.state._label.replace(/\s/g, '-')}-tab-${i}`);
408-
div.setAttribute('id', `tabpanel-${i}`);
409-
div.setAttribute('role', 'tabpanel');
410-
div.setAttribute('hidden', '');
411-
const slot = document.createElement('slot');
412-
slot.setAttribute('name', `tabpanel-slot-${i}`);
413-
div.appendChild(slot);
414-
this.tabPanelHost.appendChild(div);
415-
if (this.host?.children instanceof HTMLCollection && this.host?.children[i] /* SSR instanceof HTMLElement */) {
416-
// div.appendChild(this.host?.children[0]);
417-
this.host?.children[i].setAttribute('slot', `tabpanel-slot-${i}`);
418-
}
404+
private refreshTabPanels = () => {
405+
if (!this.tabPanelHost) return;
406+
// Clear existing panels
407+
while (this.tabPanelHost.firstChild) {
408+
this.tabPanelHost.removeChild(this.tabPanelHost.firstChild);
409+
}
410+
for (let i = 0; i < this.state._tabs?.length; i++) {
411+
const div = document.createElement('div');
412+
div.setAttribute('aria-labelledby', `${this.state._label.replace(/\s/g, '-')}-tab-${i}`);
413+
div.setAttribute('id', `tabpanel-${i}`);
414+
div.setAttribute('role', 'tabpanel');
415+
div.setAttribute('hidden', '');
416+
const slot = document.createElement('slot');
417+
slot.setAttribute('name', `tabpanel-slot-${i}`);
418+
div.appendChild(slot);
419+
this.tabPanelHost?.appendChild(div);
420+
421+
if (this.host?.children instanceof HTMLCollection && this.host?.children[i] /* SSR instanceof HTMLElement */) {
422+
this.host.children[i].setAttribute('slot', `tabpanel-slot-${i}`);
419423
}
420424
}
425+
this.updateVisiblePanel();
421426
};
422427

423-
public componentDidRender(): void {
424-
this.handleTabPanels();
425-
if (this.tabPanelHost instanceof HTMLDivElement) {
426-
for (let i = 0; i < this.tabPanelHost.children.length; i++) {
427-
if (i !== this.state._selected) {
428-
this.tabPanelHost.children[i].setAttribute('hidden', '');
429-
} else {
430-
this.tabPanelHost.children[i].removeAttribute('hidden');
431-
}
428+
private updateVisiblePanel = () => {
429+
if (!this.tabPanelHost) return;
430+
Array.from(this.tabPanelHost.children).forEach((child, i) => {
431+
if (i === this.state._selected) {
432+
child.removeAttribute('hidden');
433+
} else {
434+
child.setAttribute('hidden', '');
432435
}
433-
}
436+
});
437+
};
438+
439+
public componentDidRender(): void {
440+
this.updateVisiblePanel();
434441
}
435442

436443
private focusTabById(index: number): void {
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { expect } from '@playwright/test';
2+
import { test } from '@stencil/playwright';
3+
4+
test.describe('Dynamic Tabs', () => {
5+
test('should update tabs when _tabs prop changes', async ({ page }) => {
6+
const initialTabs = [{ _label: 'First tab' }, { _label: 'Second Tab' }];
7+
await page.setContent(`<kol-tabs _tabs='${JSON.stringify(initialTabs)}' _label="Tabs"> <div>Content 1</div> <div>Content 2</div> </kol-tabs>`);
8+
const kolTabs = page.locator('kol-tabs');
9+
await expect(kolTabs.getByRole('tab', { name: 'First tab' })).toBeVisible();
10+
await expect(kolTabs.getByRole('tab', { name: 'Second Tab' })).toBeVisible();
11+
await kolTabs.evaluate((el: HTMLKolTabsElement) => {
12+
el._tabs = JSON.stringify([{ _label: 'Updated Tab A' }, { _label: 'Updated Tab B' }, { _label: 'Updated Tab C' }]);
13+
});
14+
await expect(kolTabs.getByRole('tab', { name: 'Updated Tab A' })).toBeVisible();
15+
await expect(kolTabs.getByRole('tab', { name: 'Updated Tab B' })).toBeVisible();
16+
await expect(kolTabs.getByRole('tab', { name: 'Updated Tab C' })).toBeVisible();
17+
await expect(kolTabs.getByRole('tab', { name: 'First tab' })).toHaveCount(0);
18+
});
19+
});

0 commit comments

Comments
 (0)