diff --git a/.changeset/blue-otters-shave.md b/.changeset/blue-otters-shave.md new file mode 100644 index 0000000000..850356c458 --- /dev/null +++ b/.changeset/blue-otters-shave.md @@ -0,0 +1,5 @@ +--- +'@astrojs/starlight': patch +--- + +Fixes a `` sync issue when inconsistently using the `icon` prop or not on `` components. diff --git a/packages/starlight/__e2e__/fixtures/basics/src/content/docs/tabs.mdx b/packages/starlight/__e2e__/fixtures/basics/src/content/docs/tabs.mdx index 17e643a613..c01ba8c12f 100644 --- a/packages/starlight/__e2e__/fixtures/basics/src/content/docs/tabs.mdx +++ b/packages/starlight/__e2e__/fixtures/basics/src/content/docs/tabs.mdx @@ -35,3 +35,20 @@ A set of tabs using the `style` sync key. css code tailwind code + +Another set of tabs using the `pkg` sync key and using icons. + + + + another npm command + + + another pnpm command + + + another bun command + + + another yarn command + + diff --git a/packages/starlight/__e2e__/tabs.test.ts b/packages/starlight/__e2e__/tabs.test.ts index e0a36ed536..f10f79729a 100644 --- a/packages/starlight/__e2e__/tabs.test.ts +++ b/packages/starlight/__e2e__/tabs.test.ts @@ -116,6 +116,29 @@ test('preserves tabs position when alternating between tabs with different conte expect((await tabs.boundingBox())?.y).toBe(initialBoundingBox?.y); }); +test('syncs tabs with the same sync key if they do not consistenly use icons', async ({ + page, + starlight, +}) => { + await starlight.goto('/tabs'); + + const tabs = page.locator('starlight-tabs'); + const pkgTabsA = tabs.nth(0); // This set does not use icons for tab items. + const pkgTabsB = tabs.nth(4); // This set uses icons for tab items. + + // Select the pnpm tab in the first set of synced tabs. + await pkgTabsA.getByRole('tab').filter({ hasText: 'pnpm' }).click(); + + await expectSelectedTab(pkgTabsA, 'pnpm', 'pnpm command'); + await expectSelectedTab(pkgTabsB, 'pnpm', 'another pnpm command'); + + // Select the yarn tab in the second set of synced tabs. + await pkgTabsB.getByRole('tab').filter({ hasText: 'yarn' }).click(); + + await expectSelectedTab(pkgTabsB, 'yarn', 'another yarn command'); + await expectSelectedTab(pkgTabsA, 'yarn', 'yarn command'); +}); + 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); diff --git a/packages/starlight/user-components/Tabs.astro b/packages/starlight/user-components/Tabs.astro index 376fb19c93..f93475c17b 100644 --- a/packages/starlight/user-components/Tabs.astro +++ b/packages/starlight/user-components/Tabs.astro @@ -158,7 +158,7 @@ const { html, panels } = processPanels(panelHtml); newTab.setAttribute('aria-selected', 'true'); if (shouldSync) { newTab.focus(); - StarlightTabs.#syncTabs(this, newTab.textContent); + StarlightTabs.#syncTabs(this, newTab.innerText); window.scrollTo({ top: window.scrollY + (this.getBoundingClientRect().top - previousTabsOffset), }); @@ -173,7 +173,7 @@ const { html, panels } = processPanels(panelHtml); for (const receiver of syncedTabs) { if (receiver === emitter) continue; - const labelIndex = receiver.tabs.findIndex((tab) => tab.textContent === label); + const labelIndex = receiver.tabs.findIndex((tab) => tab.innerText === label); if (labelIndex === -1) continue; receiver.switchTab(receiver.tabs[labelIndex], labelIndex, false); }