diff --git a/.changeset/silent-games-know.md b/.changeset/silent-games-know.md new file mode 100644 index 000000000..dc2e8fc5c --- /dev/null +++ b/.changeset/silent-games-know.md @@ -0,0 +1,6 @@ +--- +'gitbook-v2': patch +'gitbook': patch +--- + +fix: lost section groups diff --git a/packages/gitbook-v2/src/lib/context.ts b/packages/gitbook-v2/src/lib/context.ts index 44d0625ca..74818c8a7 100644 --- a/packages/gitbook-v2/src/lib/context.ts +++ b/packages/gitbook-v2/src/lib/context.ts @@ -189,7 +189,9 @@ export async function fetchSiteContextByIds( ...(customizations.site?.title ? { title: customizations.site.title } : {}), }; - const sections = ids.siteSection ? parseSiteSectionsList(siteStructure, ids.siteSection) : null; + const sections = ids.siteSection + ? parseSiteSectionsAndGroups(siteStructure, ids.siteSection) + : null; const siteSpace = ( siteStructure.type === 'siteSpaces' && siteStructure.structure @@ -283,9 +285,14 @@ export async function fetchSpaceContextByIds( }; } -function parseSiteSectionsList(structure: SiteStructure, siteSectionId: string) { - const sections = getSiteStructureSections(structure); - const section = sections.find((section) => section.id === siteSectionId); +function parseSiteSectionsAndGroups(structure: SiteStructure, siteSectionId: string) { + const sectionsAndGroups = getSiteStructureSections(structure, { ignoreGroups: false }); + const section = parseCurrentSection(structure, siteSectionId); assert(section, 'A section must be defined when there are multiple sections'); - return { list: sections, current: section } satisfies SiteSections; + return { list: sectionsAndGroups, current: section } satisfies SiteSections; +} + +function parseCurrentSection(structure: SiteStructure, siteSectionId: string) { + const sections = getSiteStructureSections(structure, { ignoreGroups: true }); + return sections.find((section) => section.id === siteSectionId); } diff --git a/packages/gitbook/e2e/internal.spec.ts b/packages/gitbook/e2e/internal.spec.ts index 8dd408a3f..1838e086c 100644 --- a/packages/gitbook/e2e/internal.spec.ts +++ b/packages/gitbook/e2e/internal.spec.ts @@ -220,6 +220,37 @@ const testCases: TestsCase[] = [ }, ], }, + { + name: 'GitBook Site (Sections and Section Groups)', + baseUrl: 'https://gitbook-open-e2e-sites.gitbook.io/sections/', + tests: [ + { + name: 'Site with sections and section groups', + url: '', + }, + { + name: 'Section group dropdown', + url: '', + run: async (page) => { + await page.getByRole('button', { name: 'Test Section Group 1' }).hover(); + await expect(page.getByRole('link', { name: /Section B/ })).toBeVisible(); + }, + }, + { + name: 'Section group link', + url: '', + screenshot: false, + run: async (page) => { + const sectionGroupDropdown = await page.getByText('Test Section Group 1'); + await sectionGroupDropdown.hover(); + await page.getByText('Section B').click(); + await page.waitForURL( + 'https://gitbook-open-e2e-sites.gitbook.io/sections/sections-4' + ); + }, + }, + ], + }, { name: 'GitBook', baseUrl: 'https://docs.gitbook.com', diff --git a/packages/gitbook/src/app/middleware/(site)/(core)/llms.txt/route.ts b/packages/gitbook/src/app/middleware/(site)/(core)/llms.txt/route.ts index 27b06859e..f4428f582 100644 --- a/packages/gitbook/src/app/middleware/(site)/(core)/llms.txt/route.ts +++ b/packages/gitbook/src/app/middleware/(site)/(core)/llms.txt/route.ts @@ -60,7 +60,9 @@ export async function GET(_req: NextRequest) { async function getNodesFromSiteStructure(siteStructure: SiteStructure): Promise { switch (siteStructure.type) { case 'sections': - return getNodesFromSections(getSiteStructureSections(siteStructure)); + return getNodesFromSections( + getSiteStructureSections(siteStructure, { ignoreGroups: true }) + ); case 'siteSpaces': return getNodesFromSiteSpaces(siteStructure.structure, { heading: true }); default: diff --git a/packages/gitbook/src/app/middleware/(site)/(core)/sitemap.xml/route.ts b/packages/gitbook/src/app/middleware/(site)/(core)/sitemap.xml/route.ts index 6e1bc4855..069552bfc 100644 --- a/packages/gitbook/src/app/middleware/(site)/(core)/sitemap.xml/route.ts +++ b/packages/gitbook/src/app/middleware/(site)/(core)/sitemap.xml/route.ts @@ -62,7 +62,9 @@ export async function GET() { async function getUrlsFromSiteStructure(siteStructure: SiteStructure): Promise { switch (siteStructure.type) { case 'sections': - return getUrlsFromSiteSections(getSiteStructureSections(siteStructure)); + return getUrlsFromSiteSections( + getSiteStructureSections(siteStructure, { ignoreGroups: true }) + ); case 'siteSpaces': return getUrlsFromSiteSpaces(siteStructure.structure); default: diff --git a/packages/gitbook/src/lib/pointer.ts b/packages/gitbook/src/lib/pointer.ts index 7b622ee86..3ff8862d0 100644 --- a/packages/gitbook/src/lib/pointer.ts +++ b/packages/gitbook/src/lib/pointer.ts @@ -53,7 +53,7 @@ export function checkIsRootPointer( ): boolean { switch (siteStructure.type) { case 'sections': { - return getSiteStructureSections(siteStructure).some( + return getSiteStructureSections(siteStructure, { ignoreGroups: true }).some( (structure) => structure.default && structure.id === pointer.siteSectionId && diff --git a/packages/gitbook/src/lib/sites.ts b/packages/gitbook/src/lib/sites.ts index 48f619a2e..88d63e75d 100644 --- a/packages/gitbook/src/lib/sites.ts +++ b/packages/gitbook/src/lib/sites.ts @@ -1,13 +1,28 @@ -import type { SiteSection, SiteSpace, SiteStructure } from '@gitbook/api'; +import type { SiteSection, SiteSectionGroup, SiteSpace, SiteStructure } from '@gitbook/api'; /** * Get all sections from a site structure. + * Set the `ignoreGroups` option to true to flatten the list to only include SiteSection and to not include SiteSectionGroups. */ -export function getSiteStructureSections(siteStructure: SiteStructure) { +export function getSiteStructureSections( + siteStructure: SiteStructure, + options: { ignoreGroups: true } +): SiteSection[]; +export function getSiteStructureSections( + siteStructure: SiteStructure, + options?: { ignoreGroups: false } +): SiteSection[] | SiteSectionGroup[]; +export function getSiteStructureSections( + siteStructure: SiteStructure, + options?: { ignoreGroups: boolean } +) { + const { ignoreGroups } = options ?? { ignoreGroups: false }; return siteStructure.type === 'sections' - ? siteStructure.structure.flatMap((item) => - item.object === 'site-section-group' ? item.sections : item - ) + ? ignoreGroups + ? siteStructure.structure.flatMap((item) => + item.object === 'site-section-group' ? item.sections : item + ) + : siteStructure.structure : []; }