Skip to content

Commit

Permalink
feat(v2): contextual search, dynamic Algolia facetFilters (#3550)
Browse files Browse the repository at this point in the history
* POC of contextual search dynamic filters

* fix useSearchTags bugs

* contextual search should use preferred version  (persisted in storage)

* Contextual search: make system decoupled from algolia + wire proper meta tags and facet filters

* rework doc tag + minor refactorings

* update snapshots

* polish contextual search

* add Algolia validateThemeConfig tests
  • Loading branch information
slorber authored Oct 15, 2020
1 parent d23940c commit 21264f5
Show file tree
Hide file tree
Showing 21 changed files with 468 additions and 138 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ Object {
\\"version\\": \\"current\\"
}",
"version-current-metadata-prop-751.json": "{
\\"pluginId\\": \\"default\\",
\\"version\\": \\"current\\",
\\"label\\": \\"Next\\",
\\"isLast\\": true,
Expand Down Expand Up @@ -640,6 +641,7 @@ Object {
\\"sidebar\\": \\"version-1.0.0/community\\"
}",
"version-1-0-0-metadata-prop-608.json": "{
\\"pluginId\\": \\"community\\",
\\"version\\": \\"1.0.0\\",
\\"label\\": \\"1.0.0\\",
\\"isLast\\": true,
Expand All @@ -657,6 +659,7 @@ Object {
}
}",
"version-current-metadata-prop-751.json": "{
\\"pluginId\\": \\"community\\",
\\"version\\": \\"current\\",
\\"label\\": \\"Next\\",
\\"isLast\\": false,
Expand Down Expand Up @@ -1102,6 +1105,7 @@ Object {
\\"version\\": \\"withSlugs\\"
}",
"version-1-0-0-metadata-prop-608.json": "{
\\"pluginId\\": \\"default\\",
\\"version\\": \\"1.0.0\\",
\\"label\\": \\"1.0.0\\",
\\"isLast\\": false,
Expand Down Expand Up @@ -1145,6 +1149,7 @@ Object {
}
}",
"version-1-0-1-metadata-prop-e87.json": "{
\\"pluginId\\": \\"default\\",
\\"version\\": \\"1.0.1\\",
\\"label\\": \\"1.0.1\\",
\\"isLast\\": true,
Expand Down Expand Up @@ -1182,6 +1187,7 @@ Object {
}
}",
"version-current-metadata-prop-751.json": "{
\\"pluginId\\": \\"default\\",
\\"version\\": \\"current\\",
\\"label\\": \\"Next\\",
\\"isLast\\": false,
Expand Down Expand Up @@ -1219,6 +1225,7 @@ Object {
}
}",
"version-with-slugs-metadata-prop-2bf.json": "{
\\"pluginId\\": \\"default\\",
\\"version\\": \\"withSlugs\\",
\\"label\\": \\"withSlugs\\",
\\"isLast\\": false,
Expand Down
6 changes: 5 additions & 1 deletion packages/docusaurus-plugin-content-docs/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,11 @@ export default function pluginContentDocs(
`${docuHash(
`version-${loadedVersion.versionName}-metadata-prop`,
)}.json`,
JSON.stringify(toVersionMetadataProp(loadedVersion), null, 2),
JSON.stringify(
toVersionMetadataProp(pluginId, loadedVersion),
null,
2,
),
);

addRoute({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ declare module '@docusaurus/plugin-content-docs-types' {
};

export type PropVersionMetadata = {
pluginId: string;
version: string;
label: string;
isLast: boolean;
Expand Down
2 changes: 2 additions & 0 deletions packages/docusaurus-plugin-content-docs/src/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,11 @@ Available document ids=
}

export function toVersionMetadataProp(
pluginId: string,
loadedVersion: LoadedVersion,
): PropVersionMetadata {
return {
pluginId,
version: loadedVersion.versionName,
label: loadedVersion.versionLabel,
isLast: loadedVersion.isLast,
Expand Down
18 changes: 18 additions & 0 deletions packages/docusaurus-plugin-content-docs/src/theme/hooks/useDocs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
getActiveDocContext,
getDocVersionSuggestions,
GetActivePluginOptions,
ActivePlugin,
} from '../../client/docsClientUtils';

export const useAllDocsData = (): Record<string, GlobalPluginData> =>
Expand All @@ -33,6 +34,23 @@ export const useActivePlugin = (options: GetActivePluginOptions = {}) => {
return getActivePlugin(data, pathname, options);
};

export const useActivePluginAndVersion = (
options: GetActivePluginOptions = {},
):
| undefined
| {activePlugin: ActivePlugin; activeVersion: GlobalVersion | undefined} => {
const activePlugin = useActivePlugin(options);
const {pathname} = useLocation();
if (activePlugin) {
const activeVersion = getActiveVersion(activePlugin.pluginData, pathname);
return {
activePlugin,
activeVersion,
};
}
return undefined;
};

// versions are returned ordered (most recent first)
export const useVersions = (pluginId: string | undefined): GlobalVersion[] => {
const data = useDocsData(pluginId);
Expand Down
82 changes: 30 additions & 52 deletions packages/docusaurus-theme-classic/src/theme/DocPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,76 +18,54 @@ import NotFound from '@theme/NotFound';
import type {DocumentRoute} from '@theme/DocItem';
import type {Props} from '@theme/DocPage';
import {matchPath} from '@docusaurus/router';
import Head from '@docusaurus/Head';

import styles from './styles.module.css';
import {docVersionSearchTag} from '../../utils/searchUtils';

type DocPageContentProps = {
readonly currentDocRoute: DocumentRoute;
readonly versionMetadata: PropVersionMetadata;
readonly children: ReactNode;
};

// This theme is not coupled to Algolia, but can we do something else?
// Note the last version is also indexed with "last", to avoid breaking search on new releases
// See https://github.com/facebook/docusaurus/issues/3391
function DocSearchVersionHeader({
version,
isLast,
}: {
version: string;
isLast: boolean;
}) {
const versions = isLast ? [version, 'latest'] : [version];
return (
<Head>
<meta
name="docsearch:version"
content={
// See https://github.com/facebook/docusaurus/issues/3391#issuecomment-685594160
versions.join(',')
}
/>
</Head>
);
}

function DocPageContent({
currentDocRoute,
versionMetadata,
children,
}: DocPageContentProps): JSX.Element {
const {siteConfig, isClient} = useDocusaurusContext();
const {permalinkToSidebar, docsSidebars, version, isLast} = versionMetadata;
const {pluginId, permalinkToSidebar, docsSidebars, version} = versionMetadata;
const sidebarName = permalinkToSidebar[currentDocRoute.path];
const sidebar = docsSidebars[sidebarName];
return (
<>
<DocSearchVersionHeader version={version} isLast={isLast} />
<Layout key={isClient}>
<div className={styles.docPage}>
{sidebar && (
<div className={styles.docSidebarContainer} role="complementary">
<DocSidebar
key={
// Reset sidebar state on sidebar changes
// See https://github.com/facebook/docusaurus/issues/3414
sidebarName
}
sidebar={sidebar}
path={currentDocRoute.path}
sidebarCollapsible={
siteConfig.themeConfig?.sidebarCollapsible ?? true
}
/>
</div>
)}
<main className={styles.docMainContainer}>
<MDXProvider components={MDXComponents}>{children}</MDXProvider>
</main>
</div>
</Layout>
</>
<Layout
key={isClient}
searchMetadatas={{
version,
tag: docVersionSearchTag(pluginId, version),
}}>
<div className={styles.docPage}>
{sidebar && (
<div className={styles.docSidebarContainer} role="complementary">
<DocSidebar
key={
// Reset sidebar state on sidebar changes
// See https://github.com/facebook/docusaurus/issues/3414
sidebarName
}
sidebar={sidebar}
path={currentDocRoute.path}
sidebarCollapsible={
siteConfig.themeConfig?.sidebarCollapsible ?? true
}
/>
</div>
)}
<main className={styles.docMainContainer}>
<MDXProvider components={MDXComponents}>{children}</MDXProvider>
</main>
</div>
</Layout>
);
}

Expand Down
87 changes: 7 additions & 80 deletions packages/docusaurus-theme-classic/src/theme/Layout/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,99 +7,26 @@

import React from 'react';
import clsx from 'clsx';
import Head from '@docusaurus/Head';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import useBaseUrl from '@docusaurus/useBaseUrl';

import ThemeProvider from '@theme/ThemeProvider';
import UserPreferencesProvider from '@theme/UserPreferencesProvider';
import AnnouncementBar from '@theme/AnnouncementBar';
import Navbar from '@theme/Navbar';
import Footer from '@theme/Footer';
import LayoutProviders from '@theme/LayoutProviders';
import LayoutHead from '@theme/LayoutHead';
import type {Props} from '@theme/Layout';

import './styles.css';
import DocsPreferredVersionContextProvider from '../../utils/docsPreferredVersion/DocsPreferredVersionProvider';

function Providers({children}) {
return (
<ThemeProvider>
<UserPreferencesProvider>
<DocsPreferredVersionContextProvider>
{children}
</DocsPreferredVersionContextProvider>
</UserPreferencesProvider>
</ThemeProvider>
);
}

function Layout(props: Props): JSX.Element {
const {siteConfig} = useDocusaurusContext();
const {
favicon,
title: siteTitle,
themeConfig: {image: defaultImage, metadatas},
url: siteUrl,
titleDelimiter,
} = siteConfig;
const {
children,
title,
noFooter,
description,
image,
keywords,
permalink,
wrapperClassName,
} = props;
const metaTitle = title
? `${title} ${titleDelimiter} ${siteTitle}`
: siteTitle;
const metaImage = image || defaultImage;
const metaImageUrl = useBaseUrl(metaImage, {absolute: true});
const faviconUrl = useBaseUrl(favicon);
const {children, noFooter, wrapperClassName} = props;
return (
<Providers>
<Head>
{/* TODO: Do not assume that it is in english language */}
<html lang="en" />
{metaTitle && <title>{metaTitle}</title>}
{metaTitle && <meta property="og:title" content={metaTitle} />}
{favicon && <link rel="shortcut icon" href={faviconUrl} />}
{description && <meta name="description" content={description} />}
{description && (
<meta property="og:description" content={description} />
)}
{keywords && keywords.length && (
<meta name="keywords" content={keywords.join(',')} />
)}
{metaImage && <meta property="og:image" content={metaImageUrl} />}
{metaImage && <meta property="twitter:image" content={metaImageUrl} />}
{metaImage && (
<meta name="twitter:image:alt" content={`Image for ${metaTitle}`} />
)}
{permalink && <meta property="og:url" content={siteUrl + permalink} />}
{permalink && <link rel="canonical" href={siteUrl + permalink} />}
<meta name="twitter:card" content="summary_large_image" />
</Head>

<Head
// it's important to have an additional <Head> element here,
// as it allows react-helmet to override values set in previous <Head>
// ie we can override default metadatas such as "twitter:card"
// In same Head, the same meta would appear twice instead of overriding
// See react-helmet doc
>
{metadatas.map((metadata, i) => (
<meta key={`metadata_${i}`} {...metadata} />
))}
</Head>
<LayoutProviders>
<LayoutHead {...props} />

<AnnouncementBar />
<Navbar />
<div className={clsx('main-wrapper', wrapperClassName)}>{children}</div>

{!noFooter && <Footer />}
</Providers>
</LayoutProviders>
);
}

Expand Down
Loading

0 comments on commit 21264f5

Please sign in to comment.