Skip to content

Preserve current variant when navigating between sections #3306

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/hip-bobcats-cover.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"gitbook": minor
---

Best effort at preserving current variant when navigating between sections by matching the pathname against site spaces in the new section.
19 changes: 2 additions & 17 deletions packages/gitbook/src/components/Header/SpacesDropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import type { SiteSpace } from '@gitbook/api';

import { getSiteSpaceURL } from '@/lib/sites';
import { tcls } from '@/lib/tailwind';

import { joinPath } from '@/lib/paths';
import type { GitBookSiteContext } from '@v2/lib/context';
import { DropdownChevron, DropdownMenu } from './DropdownMenu';
import { SpacesDropdownMenuItem } from './SpacesDropdownMenuItem';
Expand All @@ -14,7 +13,6 @@ export function SpacesDropdown(props: {
className?: string;
}) {
const { context, siteSpace, siteSpaces, className } = props;
const { linker } = context;

return (
<DropdownMenu
Expand Down Expand Up @@ -73,24 +71,11 @@ export function SpacesDropdown(props: {
variantSpace={{
id: otherSiteSpace.id,
title: otherSiteSpace.title,
url: otherSiteSpace.urls.published
? linker.toLinkForContent(otherSiteSpace.urls.published)
: getFallbackSiteSpaceURL(otherSiteSpace, context),
url: getSiteSpaceURL(context, otherSiteSpace),
}}
active={otherSiteSpace.id === siteSpace.id}
/>
))}
</DropdownMenu>
);
}

/**
* When the site is not published yet, `urls.published` is not available.
* To ensure navigation works in preview, we compute a relative URL from the siteSpace path.
*/
function getFallbackSiteSpaceURL(siteSpace: SiteSpace, context: GitBookSiteContext) {
const { linker, sections } = context;
return linker.toPathInSite(
sections?.current ? joinPath(sections.current.path, siteSpace.path) : siteSpace.path
);
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { getSectionURL, getSiteSpaceURL } from '@/lib/sites';
import type { SiteSection, SiteSectionGroup } from '@gitbook/api';
import type { GitBookSiteContext, SiteSections } from '@v2/lib/context';

Expand Down Expand Up @@ -46,15 +47,35 @@ export function encodeClientSiteSections(context: GitBookSiteContext, sections:
}

function encodeSection(context: GitBookSiteContext, section: SiteSection) {
const { linker } = context;
return {
id: section.id,
title: section.title,
description: section.description,
icon: section.icon,
object: section.object,
url: section.urls.published
? linker.toLinkForContent(section.urls.published)
: linker.toPathInSite(section.path),
url: findBestTargetURL(context, section),
};
}

/**
* Find the best default site space to navigate to for a givent section:
* 1. If we are on the default, continue on the default.
* 2. If a site space has the same path as the current one, return it.
* 3. Otherwise, return the default one.
*/
function findBestTargetURL(context: GitBookSiteContext, section: SiteSection) {
const { siteSpace: currentSiteSpace } = context;

if (section.siteSpaces.length === 1 || currentSiteSpace.default) {
return getSectionURL(context, section);
}

const bestMatch = section.siteSpaces.find(
(siteSpace) => siteSpace.path === currentSiteSpace.path
);
if (bestMatch) {
return getSiteSpaceURL(context, bestMatch);
}

return getSectionURL(context, section);
}
30 changes: 30 additions & 0 deletions packages/gitbook/src/lib/sites.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import type { SiteSection, SiteSectionGroup, SiteSpace, SiteStructure } from '@gitbook/api';
import type { GitBookSiteContext } from '@v2/lib/context';
import { joinPath } from './paths';

/**
* Get all sections from a site structure.
Expand Down Expand Up @@ -64,6 +66,34 @@ export function findSiteSpaceById(siteStructure: SiteStructure, spaceId: string)
return null;
}

/**
* Get the URL to navigate to for a section.
* When the site is not published yet, `urls.published` is not available.
* To ensure navigation works in preview, we compute a relative URL from the siteSection path.
*/
export function getSectionURL(context: GitBookSiteContext, section: SiteSection) {
const { linker } = context;
return section.urls.published
? linker.toLinkForContent(section.urls.published)
: linker.toPathInSite(section.path);
}

/**
* Get the URL to navigate to for a site space.
* When the site is not published yet, `urls.published` is not available.
* To ensure navigation works in preview, we compute a relative URL from the siteSpace path.
*/
export function getSiteSpaceURL(context: GitBookSiteContext, siteSpace: SiteSpace) {
const { linker, sections } = context;
if (siteSpace.urls.published) {
return linker.toLinkForContent(siteSpace.urls.published);
}

return linker.toPathInSite(
sections?.current ? joinPath(sections.current.path, siteSpace.path) : siteSpace.path
);
}

function findSiteSpaceByIdInSections(sections: SiteSection[], spaceId: string): SiteSpace | null {
for (const section of sections) {
const siteSpace =
Expand Down