feat(search-algolia): algolia externalUrl regex to navigate with window.href (#5795)

This commit is contained in:
Sergio Moreno 2021-10-29 19:53:47 +02:00 committed by GitHub
parent 8c12983a2a
commit adbc02ea38
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 69 additions and 17 deletions

View file

@ -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),

View file

@ -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)) {

View file

@ -93,3 +93,5 @@ export {
useIsomorphicLayoutEffect,
useDynamicCallback,
} from './utils/reactUtils';
export {isRegexpStringMatch} from './utils/regexpUtils';

View 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);
}

View file

@ -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',

View file

@ -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}) {
history.push(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;

View file

@ -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)}...`
: '',

View file

@ -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(),