mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-02 11:47:23 +02:00
feat(search-algolia): algolia externalUrl regex to navigate with window.href (#5795)
This commit is contained in:
parent
8c12983a2a
commit
adbc02ea38
9 changed files with 69 additions and 17 deletions
|
@ -16,6 +16,7 @@ import type {
|
|||
} from '@theme/NavbarItem/DefaultNavbarItem';
|
||||
import IconExternalLink from '@theme/IconExternalLink';
|
||||
import isInternalUrl from '@docusaurus/isInternalUrl';
|
||||
import {isRegexpStringMatch} from '@docusaurus/theme-common';
|
||||
import {getInfimaActiveClassName} from './index';
|
||||
|
||||
const dropdownLinkActiveClass = 'dropdown__link--active';
|
||||
|
@ -54,7 +55,7 @@ export function NavLink({
|
|||
? {
|
||||
isActive: (_match, location) =>
|
||||
activeBaseRegex
|
||||
? new RegExp(activeBaseRegex).test(location.pathname)
|
||||
? isRegexpStringMatch(activeBaseRegex, location.pathname)
|
||||
: location.pathname.startsWith(activeBaseUrl),
|
||||
}
|
||||
: null),
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
isSamePath,
|
||||
useCollapsible,
|
||||
Collapsible,
|
||||
isRegexpStringMatch,
|
||||
useLocalPathname,
|
||||
} from '@docusaurus/theme-common';
|
||||
import type {
|
||||
|
@ -31,10 +32,7 @@ function isItemActive(
|
|||
if (isSamePath(item.to, localPathname)) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
item.activeBaseRegex &&
|
||||
new RegExp(item.activeBaseRegex).test(localPathname)
|
||||
) {
|
||||
if (isRegexpStringMatch(item.activeBaseRegex, localPathname)) {
|
||||
return true;
|
||||
}
|
||||
if (item.activeBasePath && localPathname.startsWith(item.activeBasePath)) {
|
||||
|
|
|
@ -93,3 +93,5 @@ export {
|
|||
useIsomorphicLayoutEffect,
|
||||
useDynamicCallback,
|
||||
} from './utils/reactUtils';
|
||||
|
||||
export {isRegexpStringMatch} from './utils/regexpUtils';
|
||||
|
|
23
packages/docusaurus-theme-common/src/utils/regexpUtils.ts
Normal file
23
packages/docusaurus-theme-common/src/utils/regexpUtils.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Utility to convert an optional string into a Regex case sensitive and global
|
||||
*/
|
||||
export function isRegexpStringMatch(
|
||||
regexAsString?: string,
|
||||
valueToTest?: string,
|
||||
): boolean {
|
||||
if (
|
||||
typeof regexAsString === 'undefined' ||
|
||||
typeof valueToTest === 'undefined'
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return new RegExp(regexAsString, 'gi').test(valueToTest);
|
||||
}
|
|
@ -103,6 +103,20 @@ describe('validateThemeConfig', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('externalUrlRegex config', () => {
|
||||
const algolia = {
|
||||
indexName: 'index',
|
||||
apiKey: 'apiKey',
|
||||
externalUrlRegex: 'http://external-domain.com',
|
||||
};
|
||||
expect(testValidateThemeConfig({algolia})).toEqual({
|
||||
algolia: {
|
||||
...DEFAULT_CONFIG,
|
||||
...algolia,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('searchParameters.facetFilters search config', () => {
|
||||
const algolia = {
|
||||
indexName: 'index',
|
||||
|
|
|
@ -13,6 +13,7 @@ import {useBaseUrlUtils} from '@docusaurus/useBaseUrl';
|
|||
import Link from '@docusaurus/Link';
|
||||
import Head from '@docusaurus/Head';
|
||||
import useSearchQuery from '@theme/hooks/useSearchQuery';
|
||||
import {isRegexpStringMatch} from '@docusaurus/theme-common';
|
||||
import {DocSearchButton, useDocSearchKeyboardEvents} from '@docsearch/react';
|
||||
import useAlgoliaContextualFacetFilters from '@theme/hooks/useAlgoliaContextualFacetFilters';
|
||||
import {translate} from '@docusaurus/Translate';
|
||||
|
@ -34,7 +35,7 @@ function ResultsFooter({state, onClose}) {
|
|||
);
|
||||
}
|
||||
|
||||
function DocSearch({contextualSearch, ...props}) {
|
||||
function DocSearch({contextualSearch, externalUrlRegex, ...props}) {
|
||||
const {siteMetadata} = useDocusaurusContext();
|
||||
|
||||
const contextualSearchFacetFilters = useAlgoliaContextualFacetFilters();
|
||||
|
@ -102,21 +103,28 @@ function DocSearch({contextualSearch, ...props}) {
|
|||
|
||||
const navigator = useRef({
|
||||
navigate({itemUrl}) {
|
||||
// Algolia results could contain URL's from other domains which cannot
|
||||
// be served through history and should navigate with window.location
|
||||
if (isRegexpStringMatch(externalUrlRegex, itemUrl)) {
|
||||
window.location.href = itemUrl;
|
||||
} else {
|
||||
history.push(itemUrl);
|
||||
}
|
||||
},
|
||||
}).current;
|
||||
|
||||
const transformItems = useRef((items) => {
|
||||
return items.map((item) => {
|
||||
// We transform the absolute URL into a relative URL.
|
||||
// Alternatively, we can use `new URL(item.url)` but it's not
|
||||
// supported in IE.
|
||||
const a = document.createElement('a');
|
||||
a.href = item.url;
|
||||
// If Algolia contains a external domain, we should navigate without relative URL
|
||||
if (isRegexpStringMatch(externalUrlRegex, item.url)) {
|
||||
return item;
|
||||
}
|
||||
|
||||
// We transform the absolute URL into a relative URL.
|
||||
const url = new URL(item.url);
|
||||
return {
|
||||
...item,
|
||||
url: withBaseUrl(`${a.pathname}${a.hash}`),
|
||||
url: withBaseUrl(`${url.pathname}${url.hash}`),
|
||||
};
|
||||
});
|
||||
}).current;
|
||||
|
|
|
@ -19,6 +19,7 @@ import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
|
|||
import {
|
||||
useTitleFormatter,
|
||||
usePluralForm,
|
||||
isRegexpStringMatch,
|
||||
useDynamicCallback,
|
||||
} from '@docusaurus/theme-common';
|
||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||
|
@ -121,7 +122,7 @@ function SearchPage() {
|
|||
const {
|
||||
siteConfig: {
|
||||
themeConfig: {
|
||||
algolia: {appId, apiKey, indexName},
|
||||
algolia: {appId, apiKey, indexName, externalUrlRegex},
|
||||
},
|
||||
},
|
||||
i18n: {currentLocale},
|
||||
|
@ -205,14 +206,16 @@ function SearchPage() {
|
|||
_highlightResult: {hierarchy},
|
||||
_snippetResult: snippet = {},
|
||||
}) => {
|
||||
const {pathname, hash} = new URL(url);
|
||||
const parsedURL = new URL(url);
|
||||
const titles = Object.keys(hierarchy).map((key) => {
|
||||
return sanitizeValue(hierarchy[key].value);
|
||||
});
|
||||
|
||||
return {
|
||||
title: titles.pop(),
|
||||
url: pathname + hash,
|
||||
url: isRegexpStringMatch(externalUrlRegex, parsedURL.href)
|
||||
? parsedURL.href
|
||||
: parsedURL.pathname + parsedURL.hash,
|
||||
summary: snippet.content
|
||||
? `${sanitizeValue(snippet.content.value)}...`
|
||||
: '',
|
||||
|
|
|
@ -22,7 +22,7 @@ const Schema = Joi.object({
|
|||
algolia: Joi.object({
|
||||
// Docusaurus attributes
|
||||
contextualSearch: Joi.boolean().default(DEFAULT_CONFIG.contextualSearch),
|
||||
|
||||
externalUrlRegex: Joi.string().optional(),
|
||||
// Algolia attributes
|
||||
appId: Joi.string().default(DEFAULT_CONFIG.appId),
|
||||
apiKey: Joi.string().required(),
|
||||
|
|
|
@ -86,6 +86,9 @@ module.exports = {
|
|||
// Optional: see doc section below
|
||||
contextualSearch: true,
|
||||
|
||||
// Optional: Specify domains where the navigation should occur through window.location instead on history.push. Useful when our Algolia config crawls multiple documentation sites and we want to navigate with window.location.href to them.
|
||||
externalUrlRegex: 'external\\.com|domain\\.com',
|
||||
|
||||
// Optional: see doc section below
|
||||
appId: 'YOUR_APP_ID',
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue