diff --git a/app/components/PackageManagerTabs.vue b/app/components/PackageManagerTabs.vue index 5b66ab99f9..57812bec0e 100644 --- a/app/components/PackageManagerTabs.vue +++ b/app/components/PackageManagerTabs.vue @@ -1,5 +1,36 @@ @@ -7,13 +38,18 @@ const selectedPM = useSelectedPackageManager() class="flex items-center gap-1 p-0.5 bg-bg-subtle border border-border-subtle rounded-md overflow-x-auto" role="tablist" :aria-label="$t('package.get_started.pm_label')" + @keydown="onTabListKeydown" > diff --git a/app/pages/[...package].vue b/app/pages/[...package].vue index be2f2a16c4..f0db452f83 100644 --- a/app/pages/[...package].vue +++ b/app/pages/[...package].vue @@ -14,6 +14,8 @@ definePageMeta({ const router = useRouter() const { packageName, requestedVersion, orgName } = usePackageRoute() +const selectedPM = useSelectedPackageManager() +const activePmId = computed(() => selectedPM.value ?? 'npm') if (import.meta.server) { assertValidPackageName(packageName.value) @@ -845,11 +847,17 @@ function handleClick(event: MouseEvent) { - + + + @@ -873,14 +881,20 @@ function handleClick(event: MouseEvent) { - + + + diff --git a/tests/package-manager-tabs.spec.ts b/tests/package-manager-tabs.spec.ts new file mode 100644 index 0000000000..ab25880bc6 --- /dev/null +++ b/tests/package-manager-tabs.spec.ts @@ -0,0 +1,35 @@ +import { expect, test } from '@nuxt/test-utils/playwright' + +test.describe('Package Page', () => { + test('/vue → package manager tabs use roving tabindex', async ({ page, goto }) => { + await goto('/vue', { waitUntil: 'domcontentloaded' }) + + const tablist = page.locator('[role="tablist"]').first() + await expect(tablist).toBeVisible() + + const tabs = tablist.locator('[role="tab"]') + const tabCount = await tabs.count() + expect(tabCount).toBeGreaterThan(1) + + const firstTab = tabs.first() + await firstTab.focus() + await expect(firstTab).toBeFocused() + + await page.keyboard.press('ArrowRight') + + const secondTab = tabs.nth(1) + await expect(secondTab).toBeFocused() + await expect(secondTab).toHaveAttribute('aria-selected', 'true') + await expect(secondTab).toHaveAttribute('tabindex', '0') + await expect(firstTab).toHaveAttribute('tabindex', '-1') + + const tabpanel = page.locator('[role="tabpanel"]').first() + const controls = await secondTab.getAttribute('aria-controls') + const panelId = await tabpanel.getAttribute('id') + expect(controls).toBe(panelId) + + const labelledBy = await tabpanel.getAttribute('aria-labelledby') + const tabId = await secondTab.getAttribute('id') + expect(labelledBy).toBe(tabId) + }) +})