diff --git a/site/scripts/check-navigation.mjs b/site/scripts/check-navigation.mjs index 59520bef..27f933fb 100644 --- a/site/scripts/check-navigation.mjs +++ b/site/scripts/check-navigation.mjs @@ -103,6 +103,25 @@ export async function checkNavigation(page, base) { assert.equal(await page.locator('html').getAttribute('data-mcp-install-cooldown-enabled'), '1') console.log('Navigation: document retained, Back, search, code tabs, menu and saved theme/cooldown pass') + for (const [port, version] of [['ts', 'latest'], ['py', 'stable']]) { + const prefix = new URL(`${base}/${port}/${version}/`).pathname + for (const width of [1440, 390]) { + await page.setViewportSize({ width, height: 900 }) + const response = await page.goto(`${base}/${port}/${version}/examples/capture-pane-output/`, { waitUntil: 'load' }) + assert(response?.ok(), `${port}: owned example exists in the root build`) + const sidebars = page.locator('nav.sidebar-nav') + assert.equal(await sidebars.count(), 2, 'Desktop and mobile both have navigation') + for (const sidebar of await sidebars.all()) { + const query = sidebar.getByRole('link', { name: 'Filtering and queries', exact: true, includeHidden: true }) + assert.equal(await query.getAttribute('href'), `${prefix}concepts/queries/`, + `${port} ${version} at ${width}px: shared query page stays in the selected port`) + const links = await sidebar.locator('a[href^="/"]').evaluateAll((items) => items.map((a) => a.getAttribute('href'))) + for (const href of links) assert(href.startsWith(prefix), `${port} sidebar leaves ${prefix}: ${href}`) + } + } + } + console.log('Sidebars: root-mounted port pages retain the selected port and version on desktop and mobile') + const legacy = `${base}/go/latest/examples/workspace-from-file/` const current = `${base}/go/latest/workspace/internals/examples/` await page.goto(`${legacy}?from=legacy#where-this-comes-from`, { waitUntil: 'load' }) diff --git a/site/src/lib/sidebar.ts b/site/src/lib/sidebar.ts index 487ae13b..160252f4 100644 --- a/site/src/lib/sidebar.ts +++ b/site/src/lib/sidebar.ts @@ -92,8 +92,9 @@ export function entryPath(entry: CollectionEntry<'docs'>): string { return docsPath({ ...entry, id: sourceIdOf(entry.id) }) } -/** `entryPath`, joined to this build's own base and given the trailing slash `trailingSlash: 'always'` expects. */ -function linkHref(entry: CollectionEntry<'docs'>, version: string): string { +/** Port pages keep their navigation scope even when rendered by a root build. */ +function linkHref(entry: CollectionEntry<'docs'>, version: string, port?: string): string { + if (port) return portPageUrl(PORT_BY_SLUG[port], version, entryPath(entry)) const path = docsRoutePath({ ...entry, id: sourceIdOf(entry.id) }, process.env.LIBTMUX_DOCS_PORT, entry.data.port ? { [entry.data.port]: version } : {}) const base = import.meta.env.BASE_URL @@ -237,7 +238,7 @@ export async function getSidebar( const localised = translated.get(entryPath(entry)) ?? entry return { label: localised.data.sidebar?.label ?? localised.data.title, - href: linkHref(entry, version), + href: linkHref(entry, version, port), order: entry.data.sidebar?.order, group: entry.data.sidebar?.group, }