mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-24 03:16:40 +00:00
Fix embeddable search links (#4196)
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Fix embeddable search links
|
||||
@@ -82,7 +82,9 @@ export async function EmbeddableDocsPage(
|
||||
{context.sections ? (
|
||||
<SiteSectionTabs
|
||||
className="not-theme-bold:-mt-2 theme-bold:bg-tint-base"
|
||||
sections={encodeClientSiteSections(context, context.sections)}
|
||||
sections={encodeClientSiteSections(context, context.sections, {
|
||||
asEmbeddable: true,
|
||||
})}
|
||||
>
|
||||
{variants.translations.length > 1 ? (
|
||||
<TranslationsDropdown
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { GitBookSiteContext, SiteSections } from '@/lib/context';
|
||||
import { toEmbeddableLinkForPublishedContent } from '@/lib/embeddable-linker';
|
||||
import {
|
||||
getLocalizedDescription,
|
||||
getLocalizedTitle,
|
||||
@@ -27,16 +28,21 @@ export type ClientSiteSectionGroup = Pick<SiteSectionGroup, 'id' | 'title' | 'ic
|
||||
/**
|
||||
* Encode the list of site sections into the data to be rendered in the client.
|
||||
*/
|
||||
export function encodeClientSiteSections(context: GitBookSiteContext, sections: SiteSections) {
|
||||
export function encodeClientSiteSections(
|
||||
context: GitBookSiteContext,
|
||||
sections: SiteSections,
|
||||
options?: { asEmbeddable?: boolean }
|
||||
) {
|
||||
const { list, current } = sections;
|
||||
const currentLanguage = context.locale;
|
||||
const asEmbeddable = Boolean(options?.asEmbeddable);
|
||||
|
||||
const clientSections: (ClientSiteSection | ClientSiteSectionGroup)[] = [];
|
||||
|
||||
for (const item of list) {
|
||||
switch (item.object) {
|
||||
case 'site-section-group': {
|
||||
const children = encodeChildren(context, item.children);
|
||||
const children = encodeChildren(context, item.children, asEmbeddable);
|
||||
|
||||
// Skip empty groups
|
||||
if (children.length === 0) {
|
||||
@@ -53,7 +59,7 @@ export function encodeClientSiteSections(context: GitBookSiteContext, sections:
|
||||
continue;
|
||||
}
|
||||
case 'site-section': {
|
||||
clientSections.push(encodeSection(context, item));
|
||||
clientSections.push(encodeSection(context, item, asEmbeddable));
|
||||
continue;
|
||||
}
|
||||
default:
|
||||
@@ -63,13 +69,14 @@ export function encodeClientSiteSections(context: GitBookSiteContext, sections:
|
||||
|
||||
return {
|
||||
list: clientSections,
|
||||
current: encodeSection(context, current),
|
||||
current: encodeSection(context, current, asEmbeddable),
|
||||
};
|
||||
}
|
||||
|
||||
function encodeChildren(
|
||||
context: GitBookSiteContext,
|
||||
children: (SiteSection | SiteSectionGroup)[]
|
||||
children: (SiteSection | SiteSectionGroup)[],
|
||||
asEmbeddable: boolean
|
||||
): (ClientSiteSection | ClientSiteSectionGroup)[] {
|
||||
const clientChildren: (ClientSiteSection | ClientSiteSectionGroup)[] = [];
|
||||
const currentLanguage = context.locale;
|
||||
@@ -77,11 +84,11 @@ function encodeChildren(
|
||||
for (const child of children) {
|
||||
switch (child.object) {
|
||||
case 'site-section': {
|
||||
clientChildren.push(encodeSection(context, child));
|
||||
clientChildren.push(encodeSection(context, child, asEmbeddable));
|
||||
break;
|
||||
}
|
||||
case 'site-section-group': {
|
||||
const nestedChildren = encodeChildren(context, child.children);
|
||||
const nestedChildren = encodeChildren(context, child.children, asEmbeddable);
|
||||
|
||||
// Skip empty groups
|
||||
if (nestedChildren.length === 0) {
|
||||
@@ -105,7 +112,7 @@ function encodeChildren(
|
||||
return clientChildren;
|
||||
}
|
||||
|
||||
function encodeSection(context: GitBookSiteContext, section: SiteSection) {
|
||||
function encodeSection(context: GitBookSiteContext, section: SiteSection, asEmbeddable: boolean) {
|
||||
const currentLanguage = context.locale;
|
||||
return {
|
||||
id: section.id,
|
||||
@@ -113,7 +120,7 @@ function encodeSection(context: GitBookSiteContext, section: SiteSection) {
|
||||
description: getLocalizedDescription(section, currentLanguage),
|
||||
icon: section.icon,
|
||||
object: section.object,
|
||||
url: findBestTargetURL(context, section),
|
||||
url: findBestTargetURL(context, section, asEmbeddable),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -125,11 +132,15 @@ function encodeSection(context: GitBookSiteContext, section: SiteSection) {
|
||||
* 4. Otherwise, return the default first language match.
|
||||
* 5. Otherwise, return the default one.
|
||||
*/
|
||||
function findBestTargetURL(context: GitBookSiteContext, section: SiteSection) {
|
||||
function findBestTargetURL(
|
||||
context: GitBookSiteContext,
|
||||
section: SiteSection,
|
||||
asEmbeddable: boolean
|
||||
) {
|
||||
const { siteSpace: currentSiteSpace } = context;
|
||||
|
||||
if (section.siteSpaces.length === 1 || currentSiteSpace.default) {
|
||||
return getSectionURL(context, section);
|
||||
return getTargetURLForSection(context, section, asEmbeddable);
|
||||
}
|
||||
|
||||
const possibleMatches =
|
||||
@@ -142,12 +153,36 @@ function findBestTargetURL(context: GitBookSiteContext, section: SiteSection) {
|
||||
possibleMatches[0];
|
||||
|
||||
if (bestMatch) {
|
||||
return getSiteSpaceURL(context, bestMatch);
|
||||
return getTargetURLForSiteSpace(context, bestMatch, asEmbeddable);
|
||||
}
|
||||
|
||||
return getTargetURLForSection(context, section, asEmbeddable);
|
||||
}
|
||||
|
||||
function getTargetURLForSection(
|
||||
context: GitBookSiteContext,
|
||||
section: SiteSection,
|
||||
asEmbeddable: boolean
|
||||
) {
|
||||
if (asEmbeddable && section.urls.published) {
|
||||
return toEmbeddableLinkForPublishedContent(context.linker, section.urls.published, '');
|
||||
}
|
||||
|
||||
return getSectionURL(context, section);
|
||||
}
|
||||
|
||||
function getTargetURLForSiteSpace(
|
||||
context: GitBookSiteContext,
|
||||
siteSpace: SiteSpace,
|
||||
asEmbeddable: boolean
|
||||
) {
|
||||
if (asEmbeddable && siteSpace.urls.published) {
|
||||
return toEmbeddableLinkForPublishedContent(context.linker, siteSpace.urls.published, '');
|
||||
}
|
||||
|
||||
return getSiteSpaceURL(context, siteSpace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if 2 site spaces are equivalent.
|
||||
*/
|
||||
|
||||
@@ -2,12 +2,25 @@ import type { GitBookLinker } from '@/lib/links';
|
||||
import { getPagePath } from '@/lib/pages';
|
||||
import { joinPath, joinPathWithBaseURL } from '@/lib/paths';
|
||||
|
||||
const EMBED_PAGE_PATH = '~gitbook/embed/page';
|
||||
|
||||
function createLocalURL(href: string) {
|
||||
return new URL(href, 'https://gitbook.local');
|
||||
}
|
||||
|
||||
function toEmbeddablePath(pathname: string, contentPath: string) {
|
||||
return joinPath(pathname, '~gitbook/embed/page', contentPath);
|
||||
const normalizedPath = removeEmbeddablePath(pathname);
|
||||
return contentPath
|
||||
? joinPath(normalizedPath, EMBED_PAGE_PATH, contentPath)
|
||||
: joinPath(normalizedPath, EMBED_PAGE_PATH);
|
||||
}
|
||||
|
||||
function removeEmbeddablePath(pathname: string) {
|
||||
return stripEmbeddablePath(pathname).replace(/\/~gitbook\/embed\/page\/?/, '/');
|
||||
}
|
||||
|
||||
function stripEmbeddablePath(pathname: string) {
|
||||
return pathname.replace(/\/~gitbook\/embed\/page\/?$/, '');
|
||||
}
|
||||
|
||||
export function toEmbeddableLinkForPublishedContent(
|
||||
@@ -32,7 +45,7 @@ export function getEmbeddableLinker(linker: GitBookLinker): GitBookLinker {
|
||||
...linker,
|
||||
toPathForPage({ pages, page, anchor }) {
|
||||
const pagePath = getPagePath(pages, page);
|
||||
const embedPagePath = joinPath('~gitbook/embed/page', pagePath);
|
||||
const embedPagePath = joinPath(EMBED_PAGE_PATH, pagePath);
|
||||
|
||||
return `${linker.toPathInSpace(embedPagePath)}${anchor ? `#${anchor}` : ''}`;
|
||||
},
|
||||
@@ -40,7 +53,7 @@ export function getEmbeddableLinker(linker: GitBookLinker): GitBookLinker {
|
||||
withOtherSiteSpace(override: { spaceBasePath: string }): GitBookLinker {
|
||||
return linker.withOtherSiteSpace({
|
||||
// We make sure that links in the other site space will be shown in the embeddable view.
|
||||
spaceBasePath: joinPath(override.spaceBasePath, '~gitbook/embed/page'),
|
||||
spaceBasePath: joinPath(override.spaceBasePath, EMBED_PAGE_PATH),
|
||||
});
|
||||
},
|
||||
|
||||
@@ -54,7 +67,7 @@ export function getEmbeddableLinker(linker: GitBookLinker): GitBookLinker {
|
||||
const url = createLocalURL(result);
|
||||
if (url.pathname.startsWith(linker.spaceBasePath)) {
|
||||
const contentPath = url.pathname.slice(linker.spaceBasePath.length);
|
||||
return `${linker.toPathInSpace(joinPath('~gitbook/embed/page', contentPath))}${url.search}${url.hash}`;
|
||||
return `${linker.toPathInSpace(joinPath(EMBED_PAGE_PATH, contentPath))}${url.search}${url.hash}`;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -52,6 +52,60 @@ describe('getEmbeddableLinker', () => {
|
||||
)
|
||||
).toBe('/api/python/~gitbook/embed/page/getting-started');
|
||||
});
|
||||
|
||||
it('toEmbeddableLinkForPublishedContent should not duplicate the embed path for embeddable linkers', () => {
|
||||
const root = createLinker({
|
||||
host: 'docs.company.com',
|
||||
spaceBasePath: '/docs',
|
||||
siteBasePath: '/',
|
||||
});
|
||||
|
||||
const embeddableLinker = getEmbeddableLinker(root);
|
||||
|
||||
expect(
|
||||
toEmbeddableLinkForPublishedContent(
|
||||
embeddableLinker,
|
||||
'https://docs.company.com/docs',
|
||||
'nested/page-path'
|
||||
)
|
||||
).toBe('/docs/~gitbook/embed/page/nested/page-path');
|
||||
});
|
||||
|
||||
it('toEmbeddableLinkForPublishedContent should place section paths before the embed path', () => {
|
||||
const root = createLinker({
|
||||
host: 'docs.company.com',
|
||||
spaceBasePath: '/docs',
|
||||
siteBasePath: '/',
|
||||
});
|
||||
|
||||
const embeddableLinker = getEmbeddableLinker(root);
|
||||
|
||||
expect(
|
||||
toEmbeddableLinkForPublishedContent(
|
||||
embeddableLinker,
|
||||
'https://docs.company.com/docs/guides',
|
||||
''
|
||||
)
|
||||
).toBe('/docs/guides/~gitbook/embed/page');
|
||||
});
|
||||
|
||||
it('toEmbeddableLinkForPublishedContent should keep page slugs after the embed path in other sections', () => {
|
||||
const root = createLinker({
|
||||
host: 'docs.company.com',
|
||||
spaceBasePath: '/docs',
|
||||
siteBasePath: '/',
|
||||
});
|
||||
|
||||
const embeddableLinker = getEmbeddableLinker(root);
|
||||
|
||||
expect(
|
||||
toEmbeddableLinkForPublishedContent(
|
||||
embeddableLinker,
|
||||
'https://docs.company.com/docs/guides',
|
||||
'getting-started'
|
||||
)
|
||||
).toBe('/docs/guides/~gitbook/embed/page/getting-started');
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveEmbeddableTheme', () => {
|
||||
|
||||
Reference in New Issue
Block a user