Skip to content

Commit

Permalink
Fix synced nested <Tabs> restoration issue (#2377)
Browse files Browse the repository at this point in the history
  • Loading branch information
HiDeoo authored Sep 19, 2024
1 parent b02b935 commit a257b83
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/beige-knives-visit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/starlight': patch
---

Fixes an issue with synced `<Tabs>` components containing nested `<Tabs>` causing tab panels to not render correctly.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
title: Tabs
---

import { Tabs, TabItem } from '@astrojs/starlight/components';

A set of tabs using the `pkg` sync key with some nested tabs using an `os` sync key.

<Tabs syncKey="pkg">

<TabItem label="npm">

npm content

<Tabs syncKey="os">
<TabItem label="macos">npm macOS</TabItem>
<TabItem label="windows">npm Windows</TabItem>
<TabItem label="linux">npm GNU/Linux</TabItem>
</Tabs>

</TabItem>

<TabItem label="pnpm">

pnpm content

<Tabs syncKey="os">
<TabItem label="macos">pnpm macOS</TabItem>
<TabItem label="windows">pnpm Windows</TabItem>
<TabItem label="linux">pnpm GNU/Linux</TabItem>
</Tabs>

</TabItem>

<TabItem label="yarn">

yarn content

<Tabs syncKey="os">
<TabItem label="macos">yarn macOS</TabItem>
<TabItem label="windows">yarn Windows</TabItem>
<TabItem label="linux">yarn GNU/Linux</TabItem>
</Tabs>

</TabItem>

</Tabs>
40 changes: 37 additions & 3 deletions packages/starlight/__e2e__/tabs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,41 @@ test('gracefully handles invalid persisted state for synced tabs', async ({
);
});

async function expectSelectedTab(tabs: Locator, label: string, panel: string) {
expect((await tabs.getByRole('tab', { selected: true }).textContent())?.trim()).toBe(label);
expect((await tabs.getByRole('tabpanel').textContent())?.trim()).toBe(panel);
test('syncs and restores nested tabs', async ({ page, getProdServer }) => {
const starlight = await getProdServer();
await starlight.goto('/tabs-nested');

const tabs = page.locator('starlight-tabs');
const pkgTabs = tabs.nth(0);
const osTabsA = tabs.nth(1);
const osTabsB = tabs.nth(2);

// Select the linux tab in the npm tab.
await osTabsA.getByRole('tab').filter({ hasText: 'linux' }).click();

await expectSelectedTab(osTabsA, 'linux', 'npm GNU/Linux');

// Select the pnpm tab.
await pkgTabs.getByRole('tab').filter({ hasText: 'pnpm' }).click();

await expectSelectedTab(pkgTabs, 'pnpm');
await expectSelectedTab(osTabsB, 'linux', 'pnpm GNU/Linux');

page.reload();

// The synced tabs should be restored.
await expectSelectedTab(pkgTabs, 'pnpm');
await expectSelectedTab(osTabsB, 'linux', 'pnpm GNU/Linux');
});

async function expectSelectedTab(tabs: Locator, label: string, panel?: string) {
expect(
(await tabs.locator(':scope > div [role=tab][aria-selected=true]').textContent())?.trim()
).toBe(label);

if (panel) {
const tabPanel = tabs.locator(':scope > [role=tabpanel]:not([hidden])');
await expect(tabPanel).toBeVisible();
expect((await tabPanel.textContent())?.trim()).toBe(panel);
}
}
2 changes: 1 addition & 1 deletion packages/starlight/user-components/Tabs.astro
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ if (isSynced) {
const tabIndexToRestore = tabs.findIndex(
(tab) => tab instanceof HTMLAnchorElement && tab.textContent?.trim() === label
);
const panels = starlightTabs?.querySelectorAll('[role="tabpanel"]');
const panels = starlightTabs?.querySelectorAll(':scope > [role="tabpanel"]');
const newTab = tabs[tabIndexToRestore];
const newPanel = panels[tabIndexToRestore];
if (tabIndexToRestore < 1 || !newTab || !newPanel) return;
Expand Down

0 comments on commit a257b83

Please sign in to comment.