diff --git a/src/components/overrides/Sidebar.astro b/src/components/overrides/Sidebar.astro
index 8ffb96337..1c3148072 100644
--- a/src/components/overrides/Sidebar.astro
+++ b/src/components/overrides/Sidebar.astro
@@ -1,18 +1,69 @@
---
/**
* Custom Sidebar component that mimics MkDocs Material theme's navigation.sections behavior.
+ * Includes an API language switcher (Python/TypeScript) that only appears on API pages.
*/
import MobileMenuFooter from 'virtual:starlight/components/MobileMenuFooter';
import SidebarPersister from '@astrojs/starlight/components/SidebarPersister.astro';
import SidebarSublist from './SidebarSublist.astro';
+import { pathWithBase } from '../../util/links';
-const { sidebar } = Astro.locals.starlightRoute;
+const { sidebar, id: currentSlug } = Astro.locals.starlightRoute;
+const isApiPage = currentSlug.startsWith('docs/api/');
+const isPython = currentSlug.startsWith('docs/api/python');
---
+ {isApiPage && (
+
+ )}
+
+
diff --git a/src/config/navbar.ts b/src/config/navbar.ts
index 808fc9b19..c0447d143 100644
--- a/src/config/navbar.ts
+++ b/src/config/navbar.ts
@@ -20,6 +20,12 @@ export interface NavLink {
* Example: href="/user-guide/quickstart/" but basePath="/user-guide/"
*/
basePath?: string
+ /**
+ * Additional base paths that should also be considered part of this nav section.
+ * Useful when a nav item encompasses multiple top-level sidebar sections.
+ * Example: Community nav item also covers /docs/labs/ and /docs/contribute/
+ */
+ additionalBasePaths?: string[]
/** Set to true for external links (opens in new tab) */
external?: boolean
}
@@ -54,6 +60,7 @@ function transformNavLinks(links: NavLink[]): NavLink[] {
...link,
href: withBase(link.href),
...(link.basePath ? { basePath: withBase(link.basePath) } : {}),
+ ...(link.additionalBasePaths ? { additionalBasePaths: link.additionalBasePaths.map(withBase) } : {}),
}
})
}
diff --git a/src/config/navigation.yml b/src/config/navigation.yml
index 63a0ead45..278c22350 100644
--- a/src/config/navigation.yml
+++ b/src/config/navigation.yml
@@ -12,8 +12,8 @@
navbar:
- label: Home
href: /
- - label: User Guide
- href: /docs/user-guide/quickstart/overview/
+ - label: Docs
+ href: /docs/user-guide/quickstart/python/
basePath: /docs/user-guide/
- label: Examples
href: /docs/examples/
@@ -21,49 +21,53 @@ navbar:
- label: Community
href: /docs/community/community-packages/
basePath: /docs/community/
- - label: Labs
- href: /docs/labs/
- basePath: /docs/labs/
- - label: Blog
- href: /blog/
- basePath: /blog/
- - label: Contribute ❤️
- href: /docs/contribute/
- basePath: /docs/contribute/
- - label: Python API
+ additionalBasePaths:
+ - /docs/labs/
+ - /docs/contribute/
+ - label: API Reference
href: /docs/api/python/
- basePath: /docs/api/python/
- - label: TypeScript API
- href: /docs/api/typescript/
- basePath: /docs/api/typescript/
+ basePath: /docs/api/
sidebar:
- - label: User Guide
+ - label: Docs
items:
- docs/index
- - label: Quickstart
+ - label: Get Started
items:
- docs/user-guide/quickstart/overview
- - docs/user-guide/quickstart/python
- - docs/user-guide/quickstart/typescript
+ - label: "Quickstart: Python"
+ slug: docs/user-guide/quickstart/python
+ - label: "Quickstart: TypeScript"
+ slug: docs/user-guide/quickstart/typescript
- docs/user-guide/build-with-ai
+ - label: Build
+ items:
+ - label: "Adding Tools"
+ slug: docs/user-guide/concepts/tools
+ - label: "Creating Custom Tools"
+ slug: docs/user-guide/concepts/tools/custom-tools
+ - label: "Using MCP Tools"
+ slug: docs/user-guide/concepts/tools/mcp-tools
+ - label: "Multi-Agent Systems"
+ slug: docs/user-guide/concepts/multi-agent/multi-agent-patterns
+ - label: "Structured Output"
+ slug: docs/user-guide/concepts/agents/structured-output
+ - label: "Streaming Responses"
+ slug: docs/user-guide/concepts/streaming
+ - label: "Voice & Realtime"
+ slug: docs/user-guide/concepts/bidirectional-streaming/quickstart
- label: Concepts
items:
- - label: Agents
- items:
- - docs/user-guide/concepts/agents/agent-loop
- - docs/user-guide/concepts/agents/state
- - docs/user-guide/concepts/agents/session-management
- - docs/user-guide/concepts/agents/prompts
- - docs/user-guide/concepts/agents/retry-strategies
- - docs/user-guide/concepts/agents/hooks
- - docs/user-guide/concepts/agents/structured-output
- - docs/user-guide/concepts/agents/conversation-management
+ - docs/user-guide/concepts/agents/agent-loop
+ - docs/user-guide/concepts/agents/state
+ - docs/user-guide/concepts/agents/session-management
+ - docs/user-guide/concepts/agents/prompts
+ - docs/user-guide/concepts/agents/hooks
+ - docs/user-guide/concepts/agents/conversation-management
+ - docs/user-guide/concepts/agents/retry-strategies
+ - docs/user-guide/concepts/interrupts
- label: Tools
items:
- - docs/user-guide/concepts/tools
- - docs/user-guide/concepts/tools/custom-tools
- - docs/user-guide/concepts/tools/mcp-tools
- docs/user-guide/concepts/tools/executors
- docs/user-guide/concepts/tools/community-tools-package
- label: Plugins
@@ -71,25 +75,8 @@ sidebar:
- docs/user-guide/concepts/plugins
- docs/user-guide/concepts/plugins/skills
- docs/user-guide/concepts/plugins/steering
- - label: Model Providers
- items:
- - docs/user-guide/concepts/model-providers
- - docs/user-guide/concepts/model-providers/amazon-bedrock
- - docs/user-guide/concepts/model-providers/amazon-nova
- - docs/user-guide/concepts/model-providers/anthropic
- - docs/user-guide/concepts/model-providers/gemini
- - docs/user-guide/concepts/model-providers/litellm
- - docs/user-guide/concepts/model-providers/llamacpp
- - docs/user-guide/concepts/model-providers/llamaapi
- - docs/user-guide/concepts/model-providers/mistral
- - docs/user-guide/concepts/model-providers/ollama
- - docs/user-guide/concepts/model-providers/openai
- - docs/user-guide/concepts/model-providers/sagemaker
- - docs/user-guide/concepts/model-providers/writer
- - docs/user-guide/concepts/model-providers/custom_model_provider
- label: Streaming
items:
- - docs/user-guide/concepts/streaming
- docs/user-guide/concepts/streaming/async-iterators
- docs/user-guide/concepts/streaming/callback-handlers
- label: Multi-agent
@@ -99,11 +86,8 @@ sidebar:
- docs/user-guide/concepts/multi-agent/swarm
- docs/user-guide/concepts/multi-agent/graph
- docs/user-guide/concepts/multi-agent/workflow
- - docs/user-guide/concepts/multi-agent/multi-agent-patterns
- - docs/user-guide/concepts/interrupts
- label: Bidirectional Streaming
items:
- - docs/user-guide/concepts/bidirectional-streaming/quickstart
- docs/user-guide/concepts/bidirectional-streaming/agent
- label: Models
items:
@@ -118,6 +102,47 @@ sidebar:
- label: Experimental
items:
- docs/user-guide/concepts/experimental/agent-config
+ - label: Model Providers
+ items:
+ - docs/user-guide/concepts/model-providers
+ - docs/user-guide/concepts/model-providers/amazon-bedrock
+ - docs/user-guide/concepts/model-providers/amazon-nova
+ - docs/user-guide/concepts/model-providers/anthropic
+ - docs/user-guide/concepts/model-providers/gemini
+ - docs/user-guide/concepts/model-providers/litellm
+ - docs/user-guide/concepts/model-providers/llamacpp
+ - docs/user-guide/concepts/model-providers/llamaapi
+ - docs/user-guide/concepts/model-providers/mistral
+ - docs/user-guide/concepts/model-providers/ollama
+ - docs/user-guide/concepts/model-providers/openai
+ - docs/user-guide/concepts/model-providers/sagemaker
+ - docs/user-guide/concepts/model-providers/writer
+ - docs/user-guide/concepts/model-providers/custom_model_provider
+ - docs/user-guide/concepts/model-providers/cohere
+ - docs/user-guide/concepts/model-providers/clova-studio
+ - docs/user-guide/concepts/model-providers/fireworksai
+ - docs/user-guide/concepts/model-providers/nebius-token-factory
+ - docs/user-guide/concepts/model-providers/xai
+ - label: Deploy
+ items:
+ - docs/user-guide/deploy/operating-agents-in-production
+ - label: Amazon Bedrock AgentCore
+ items:
+ - docs/user-guide/deploy/deploy_to_bedrock_agentcore
+ - docs/user-guide/deploy/deploy_to_bedrock_agentcore/python
+ - docs/user-guide/deploy/deploy_to_bedrock_agentcore/typescript
+ - docs/user-guide/deploy/deploy_to_aws_lambda
+ - docs/user-guide/deploy/deploy_to_aws_fargate
+ - docs/user-guide/deploy/deploy_to_aws_apprunner
+ - docs/user-guide/deploy/deploy_to_amazon_eks
+ - docs/user-guide/deploy/deploy_to_amazon_ec2
+ - label: Docker
+ items:
+ - docs/user-guide/deploy/deploy_to_docker
+ - docs/user-guide/deploy/deploy_to_docker/python
+ - docs/user-guide/deploy/deploy_to_docker/typescript
+ - docs/user-guide/deploy/deploy_to_kubernetes
+ - docs/user-guide/deploy/deploy_to_terraform
- label: Safety & Security
items:
- docs/user-guide/safety-security/responsible-ai
@@ -155,26 +180,6 @@ sidebar:
items:
- docs/user-guide/evals-sdk/how-to/experiment_management
- docs/user-guide/evals-sdk/how-to/serialization
- - label: Deploy
- items:
- - docs/user-guide/deploy/operating-agents-in-production
- - label: Amazon Bedrock AgentCore
- items:
- - docs/user-guide/deploy/deploy_to_bedrock_agentcore
- - docs/user-guide/deploy/deploy_to_bedrock_agentcore/python
- - docs/user-guide/deploy/deploy_to_bedrock_agentcore/typescript
- - docs/user-guide/deploy/deploy_to_aws_lambda
- - docs/user-guide/deploy/deploy_to_aws_fargate
- - docs/user-guide/deploy/deploy_to_aws_apprunner
- - docs/user-guide/deploy/deploy_to_amazon_eks
- - docs/user-guide/deploy/deploy_to_amazon_ec2
- - label: Docker
- items:
- - docs/user-guide/deploy/deploy_to_docker
- - docs/user-guide/deploy/deploy_to_docker/python
- - docs/user-guide/deploy/deploy_to_docker/typescript
- - docs/user-guide/deploy/deploy_to_kubernetes
- - docs/user-guide/deploy/deploy_to_terraform
- docs/user-guide/versioning-and-support
- label: Examples
@@ -229,25 +234,23 @@ sidebar:
- docs/community/tools/strands-teams
- docs/community/tools/strands-telegram
- docs/community/tools/strands-telegram-listener
-
- - label: Labs
- items:
- - docs/labs
- - label: Projects
+ - label: Labs
items:
- - docs/labs/robots
- - docs/labs/robots-sim
- - docs/labs/ai-functions
-
- - label: Contribute ❤️
- items:
- - docs/contribute
- - label: Contribution Types
+ - docs/labs
+ - label: Projects
+ items:
+ - docs/labs/robots
+ - docs/labs/robots-sim
+ - docs/labs/ai-functions
+ - label: Contribute
items:
- - docs/contribute/contributing/core-sdk
- - docs/contribute/contributing/documentation
- - docs/contribute/contributing/feature-proposals
- - docs/contribute/contributing/extensions
+ - docs/contribute
+ - label: Contribution Types
+ items:
+ - docs/contribute/contributing/core-sdk
+ - docs/contribute/contributing/documentation
+ - docs/contribute/contributing/feature-proposals
+ - docs/contribute/contributing/extensions
github:
sections:
diff --git a/src/pages/index.astro b/src/pages/index.astro
index df68429a9..8d9642670 100644
--- a/src/pages/index.astro
+++ b/src/pages/index.astro
@@ -80,7 +80,7 @@ const testimonials = await Promise.all(
in a few lines of code
diff --git a/src/pages/llms.txt.ts b/src/pages/llms.txt.ts
index 844cbf03f..d7244842f 100644
--- a/src/pages/llms.txt.ts
+++ b/src/pages/llms.txt.ts
@@ -6,7 +6,7 @@ import { loadSidebarFromConfig, type StarlightSidebarItem } from '../sidebar'
import path from 'node:path'
// Sections to pull from sidebar (with their nav labels)
-const SIDEBAR_SECTIONS = ['User Guide', 'Examples', 'Community']
+const SIDEBAR_SECTIONS = ['Docs', 'Examples', 'Community']
/**
* Recursively extract links from sidebar items
diff --git a/src/route-middleware.ts b/src/route-middleware.ts
index fe84f9e42..eb660ba85 100644
--- a/src/route-middleware.ts
+++ b/src/route-middleware.ts
@@ -14,6 +14,7 @@ function isSidebarGroup(entry: SidebarEntry): entry is SidebarGroup {
/**
* Find which nav section the current page belongs to based on URL path.
* Matches the most specific basePath (longest match wins).
+ * Also checks additionalBasePaths for nav items that span multiple sections.
*/
export function findCurrentNavSection(currentPath: string, links: NavLink[]): NavLink | undefined {
let bestMatch: NavLink | undefined
@@ -28,23 +29,36 @@ export function findCurrentNavSection(currentPath: string, links: NavLink[]): Na
bestMatch = link
bestMatchLength = basePath.length
}
+ // Also check additionalBasePaths
+ if (link.additionalBasePaths) {
+ for (const additionalPath of link.additionalBasePaths) {
+ if (currentPath.startsWith(additionalPath) && additionalPath.length > bestMatchLength) {
+ bestMatch = link
+ bestMatchLength = additionalPath.length
+ }
+ }
+ }
}
return bestMatch
}
/**
- * Filter sidebar entries to only include items matching a base path.
+ * Filter sidebar entries to only include items matching one or more base paths.
* If the result is a single top-level group, unwrap it to return just its entries.
*/
-export function filterSidebarByBasePath(entries: SidebarEntry[], basePath: string): SidebarEntry[] {
+export function filterSidebarByBasePath(entries: SidebarEntry[], basePath: string | string[]): SidebarEntry[] {
+ const basePaths = Array.isArray(basePath) ? basePath : [basePath]
+
+ const matchesAnyBase = (href: string) => basePaths.some((bp) => href.startsWith(bp))
+
const filtered = entries
.map((entry) => {
if (entry.type === 'link') {
- return entry.href.startsWith(basePath) ? entry : null
+ return matchesAnyBase(entry.href) ? entry : null
}
if (entry.type === 'group') {
- const filteredEntries = filterSidebarByBasePath(entry.entries, basePath)
+ const filteredEntries = filterSidebarByBasePath(entry.entries, basePaths)
return filteredEntries.length > 0 ? { ...entry, entries: filteredEntries } : null
}
return null
@@ -102,34 +116,8 @@ export const onRequest = defineRouteMiddleware(async (context) => {
const currentPath = context.url.pathname
const currentSlug = starlightRoute.id
- // Check if we're on a Python API page
- if (currentSlug.startsWith('docs/api/python')) {
- const docs = await getCollection('docs')
- const docInfos: DocInfo[] = docs.map((doc: { id: string; data: { title: unknown } }) => ({
- id: doc.id,
- title: doc.data.title as string,
- }))
-
- const pythonSidebar = buildPythonApiSidebar(docInfos, currentSlug)
-
- // Add index link at the top
- pythonSidebar.unshift({
- type: 'link',
- label: 'Overview',
- href: pathWithBase('/docs/api/python/'),
- isCurrent: currentSlug === 'docs/api/python',
- badge: undefined,
- attrs: {},
- })
-
- const titlesByHref = await buildTitlesByHref()
- starlightRoute.sidebar = pythonSidebar
- starlightRoute.pagination = getPrevNextLinks(pythonSidebar, titlesByHref)
- return
- }
-
- // Check if we're on a TypeScript API page
- if (currentSlug.startsWith('docs/api/typescript')) {
+ // Check if we're on an API page (Python or TypeScript)
+ if (currentSlug.startsWith('docs/api/python') || currentSlug.startsWith('docs/api/typescript')) {
const docs = await getCollection('docs')
const docInfos: DocInfo[] = docs.map((doc: { id: string; data: { title: unknown; category?: unknown } }) => ({
id: doc.id,
@@ -137,21 +125,26 @@ export const onRequest = defineRouteMiddleware(async (context) => {
category: doc.data.category as string | undefined,
}))
- const tsSidebar = buildTypeScriptApiSidebar(docInfos, currentSlug)
+ const isPython = currentSlug.startsWith('docs/api/python')
+ const apiSidebar = isPython
+ ? buildPythonApiSidebar(docInfos, currentSlug)
+ : buildTypeScriptApiSidebar(docInfos, currentSlug)
// Add index link at the top
- tsSidebar.unshift({
+ const overviewHref = isPython ? '/docs/api/python/' : '/docs/api/typescript/'
+ const overviewSlug = isPython ? 'docs/api/python' : 'docs/api/typescript'
+ apiSidebar.unshift({
type: 'link',
label: 'Overview',
- href: pathWithBase('/docs/api/typescript/'),
- isCurrent: currentSlug === 'docs/api/typescript',
+ href: pathWithBase(overviewHref),
+ isCurrent: currentSlug === overviewSlug,
badge: undefined,
attrs: {},
})
const titlesByHref = await buildTitlesByHref()
- starlightRoute.sidebar = tsSidebar
- starlightRoute.pagination = getPrevNextLinks(tsSidebar, titlesByHref)
+ starlightRoute.sidebar = apiSidebar
+ starlightRoute.pagination = getPrevNextLinks(apiSidebar, titlesByHref)
return
}
@@ -164,19 +157,23 @@ export const onRequest = defineRouteMiddleware(async (context) => {
return
}
+ // Collect all base paths for this nav section (primary + additional)
+ const primaryBasePath = currentNav.basePath || currentNav.href
+ const allBasePaths = [primaryBasePath, ...(currentNav.additionalBasePaths || [])]
+
// Otherwise filter it down to the major section that we're in
- const basePath = currentNav.basePath || currentNav.href
- const filteredSidebar = filterSidebarByBasePath(sidebar, basePath)
+ const filteredSidebar = filterSidebarByBasePath(sidebar, allBasePaths)
const expandedSidebar = expandFirstLevelGroups(filteredSidebar)
starlightRoute.sidebar = expandedSidebar
// Starlight pre-computes pagination from the full sidebar before our middleware runs.
// Prune any prev/next links that fall outside the current nav section, then override
// labels with actual page titles instead of sidebar nav labels.
+ const matchesAnyBase = (href: string) => allBasePaths.some((bp) => href.startsWith(bp))
const titlesByHref = await buildTitlesByHref()
const { prev, next } = starlightRoute.pagination
starlightRoute.pagination = {
- prev: prev?.href.startsWith(basePath) ? { ...prev, label: titlesByHref.get(prev.href) ?? prev.label } : undefined,
- next: next?.href.startsWith(basePath) ? { ...next, label: titlesByHref.get(next.href) ?? next.label } : undefined,
+ prev: prev && matchesAnyBase(prev.href) ? { ...prev, label: titlesByHref.get(prev.href) ?? prev.label } : undefined,
+ next: next && matchesAnyBase(next.href) ? { ...next, label: titlesByHref.get(next.href) ?? next.label } : undefined,
}
})
diff --git a/src/sidebar.ts b/src/sidebar.ts
index bf73867c8..c26ecfe18 100644
--- a/src/sidebar.ts
+++ b/src/sidebar.ts
@@ -12,6 +12,7 @@ export type StarlightSidebarItem =
interface NavConfigItem {
label?: string
items?: NavConfigItem[]
+ slug?: string // For labeled leaf items (e.g., { label: "Adding Tools", slug: "docs/user-guide/concepts/tools" })
}
type NavConfigEntry = string | NavConfigItem
@@ -82,6 +83,12 @@ function convertConfigItem(item: NavConfigEntry, ctx: ConvertContext): Starlight
return { label: item.label, items: children }
}
+
+ // Object with label and slug (labeled leaf item)
+ if (item.label && item.slug) {
+ if (!contentExists(item.slug, ctx.contentDir)) return null
+ return { slug: item.slug, label: item.label }
+ }
}
return null
diff --git a/test/sidebar.test.ts b/test/sidebar.test.ts
index aa2536e8f..842ba719b 100644
--- a/test/sidebar.test.ts
+++ b/test/sidebar.test.ts
@@ -69,81 +69,81 @@ describe('Sidebar Generation from navigation.yml', () => {
.filter((item): item is StarlightSidebarItem & { label: string } => 'label' in item)
.map((item) => item.label)
- expect(topLevelLabels).toContain('User Guide')
+ expect(topLevelLabels).toContain('Docs')
expect(topLevelLabels).toContain('Examples')
expect(topLevelLabels).toContain('Community')
- expect(topLevelLabels).toContain('Labs')
- expect(topLevelLabels).toContain('Contribute ❤️')
})
it('should have collapsed groups at depth >= 1', () => {
const sidebar = loadSidebarFromConfig(pathToNavigationYml)
- // Find the User Guide section
- const userGuide = sidebar.find(
+ // Find the Docs section
+ const docs = sidebar.find(
(item): item is StarlightSidebarItem & { label: string; items: StarlightSidebarItem[] } =>
- 'label' in item && item.label === 'User Guide'
+ 'label' in item && item.label === 'Docs'
)
- expect(userGuide).toBeDefined()
- if (userGuide) {
+ expect(docs).toBeDefined()
+ if (docs) {
// Top level should not be collapsed
- expect(userGuide).not.toHaveProperty('collapsed')
+ expect(docs).not.toHaveProperty('collapsed')
- // Find a nested group (like "Quickstart")
- const quickstart = userGuide.items.find(
- (item): item is StarlightSidebarItem & { label: string } => 'label' in item && item.label === 'Quickstart'
+ // Find a nested group (like "Get Started")
+ const getStarted = docs.items.find(
+ (item): item is StarlightSidebarItem & { label: string } => 'label' in item && item.label === 'Get Started'
)
// Nested groups should be collapsed
- expect(quickstart).toHaveProperty('collapsed', true)
+ expect(getStarted).toHaveProperty('collapsed', true)
}
})
- it('should have slugs without labels for file items', () => {
+ it('should support both labeled and unlabeled leaf items', () => {
const sidebar = loadSidebarFromConfig(pathToNavigationYml)
- // Find a leaf item (internal link) and verify it has slug but no label
- function findLeafItem(items: StarlightSidebarItem[]): StarlightSidebarItem | null {
+ // Collect all leaf items
+ function findLeafItems(items: StarlightSidebarItem[]): StarlightSidebarItem[] {
+ const leaves: StarlightSidebarItem[] = []
for (const item of items) {
if ('slug' in item && !('items' in item)) {
- return item
+ leaves.push(item)
}
if ('items' in item) {
- const found = findLeafItem(item.items as StarlightSidebarItem[])
- if (found) return found
+ leaves.push(...findLeafItems(item.items as StarlightSidebarItem[]))
}
}
- return null
+ return leaves
}
- const leafItem = findLeafItem(sidebar)
- expect(leafItem).toBeDefined()
- expect(leafItem).toHaveProperty('slug')
- expect(leafItem).not.toHaveProperty('label')
+ const leaves = findLeafItems(sidebar)
+ expect(leaves.length).toBeGreaterThan(0)
+
+ // Some leaves should have labels (Build section items)
+ const labeled = leaves.filter((item) => 'label' in item)
+ expect(labeled.length).toBeGreaterThan(0)
+
+ // Some leaves should not have labels (plain slug items)
+ const unlabeled = leaves.filter((item) => !('label' in item))
+ expect(unlabeled.length).toBeGreaterThan(0)
})
- it('should handle external links in sidebar', () => {
+ it('should include Labs and Contribute under Community', () => {
const sidebar = loadSidebarFromConfig(pathToNavigationYml)
- // Find the Contribute section which has an external link
- const contribute = sidebar.find(
- (item): item is StarlightSidebarItem & { label: string } =>
- 'label' in item && item.label === 'Contribute ❤️'
+ // Find the Community section
+ const community = sidebar.find(
+ (item): item is StarlightSidebarItem & { label: string; items: StarlightSidebarItem[] } =>
+ 'label' in item && item.label === 'Community'
)
- expect(contribute).toBeDefined()
- if (contribute && 'items' in contribute) {
- // Look for external links in the items
- const externalLink = (contribute.items as StarlightSidebarItem[]).find(
- (item): item is StarlightSidebarItem & { link: string } => 'link' in item && item.link?.startsWith('http')
- )
+ expect(community).toBeDefined()
+ if (community) {
+ const subLabels = community.items
+ .filter((item): item is StarlightSidebarItem & { label: string } => 'label' in item)
+ .map((item) => item.label)
- // If there's an external link, it should have the right attributes
- if (externalLink) {
- expect(externalLink).toHaveProperty('attrs')
- expect(externalLink.attrs).toHaveProperty('target', '_blank')
- }
+ expect(subLabels).toContain('Labs')
+ expect(subLabels).toContain('Contribute')
}
})
})