@docusaurus/theme-search-algolia 2.0.0-beta.8e9b829d9 → 2.0.0-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +10 -10
- package/src/__tests__/validateThemeConfig.test.js +14 -0
- package/src/index.js +1 -1
- package/src/templates/opensearch.js +3 -1
- package/src/theme/SearchBar/index.js +16 -8
- package/src/theme/SearchPage/index.js +20 -23
- package/src/theme/hooks/useSearchQuery.js +35 -16
- package/src/validateThemeConfig.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@docusaurus/theme-search-algolia",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.9",
|
|
4
4
|
"description": "Algolia search component for Docusaurus.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"publishConfig": {
|
|
@@ -14,14 +14,14 @@
|
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@docsearch/react": "^3.0.0-alpha.39",
|
|
17
|
-
"@docusaurus/core": "2.0.0-beta.
|
|
18
|
-
"@docusaurus/theme-common": "2.0.0-beta.
|
|
19
|
-
"@docusaurus/utils": "2.0.0-beta.
|
|
20
|
-
"@docusaurus/utils-validation": "2.0.0-beta.
|
|
21
|
-
"algoliasearch": "^4.
|
|
22
|
-
"algoliasearch-helper": "^3.
|
|
17
|
+
"@docusaurus/core": "2.0.0-beta.9",
|
|
18
|
+
"@docusaurus/theme-common": "2.0.0-beta.9",
|
|
19
|
+
"@docusaurus/utils": "2.0.0-beta.9",
|
|
20
|
+
"@docusaurus/utils-validation": "2.0.0-beta.9",
|
|
21
|
+
"algoliasearch": "^4.10.5",
|
|
22
|
+
"algoliasearch-helper": "^3.5.5",
|
|
23
23
|
"clsx": "^1.1.1",
|
|
24
|
-
"eta": "^1.12.
|
|
24
|
+
"eta": "^1.12.3",
|
|
25
25
|
"lodash": "^4.17.20"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"react-dom": "^16.8.4 || ^17.0.0"
|
|
30
30
|
},
|
|
31
31
|
"engines": {
|
|
32
|
-
"node": ">=
|
|
32
|
+
"node": ">=14"
|
|
33
33
|
},
|
|
34
|
-
"gitHead": "
|
|
34
|
+
"gitHead": "8a491fc29ad002f90e97f5b5fe4178ac8fa0c4d7"
|
|
35
35
|
}
|
|
@@ -103,6 +103,20 @@ describe('validateThemeConfig', () => {
|
|
|
103
103
|
});
|
|
104
104
|
});
|
|
105
105
|
|
|
106
|
+
test('externalUrlRegex config', () => {
|
|
107
|
+
const algolia = {
|
|
108
|
+
indexName: 'index',
|
|
109
|
+
apiKey: 'apiKey',
|
|
110
|
+
externalUrlRegex: 'http://external-domain.com',
|
|
111
|
+
};
|
|
112
|
+
expect(testValidateThemeConfig({algolia})).toEqual({
|
|
113
|
+
algolia: {
|
|
114
|
+
...DEFAULT_CONFIG,
|
|
115
|
+
...algolia,
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
106
120
|
test('searchParameters.facetFilters search config', () => {
|
|
107
121
|
const algolia = {
|
|
108
122
|
indexName: 'index',
|
package/src/index.js
CHANGED
|
@@ -12,7 +12,9 @@ module.exports = `
|
|
|
12
12
|
<ShortName><%= it.title %></ShortName>
|
|
13
13
|
<Description>Search <%= it.title %></Description>
|
|
14
14
|
<InputEncoding>UTF-8</InputEncoding>
|
|
15
|
-
|
|
15
|
+
<% if (it.favicon) { _%>
|
|
16
|
+
<Image width="16" height="16" type="image/x-icon"><%= it.favicon %></Image>
|
|
17
|
+
<% } _%>
|
|
16
18
|
<Url type="text/html" method="get" template="<%= it.url %>search?q={searchTerms}"/>
|
|
17
19
|
<Url type="application/opensearchdescription+xml" rel="self" template="<%= it.url %>opensearch.xml" />
|
|
18
20
|
<moz:SearchForm><%= it.url %></moz:SearchForm>
|
|
@@ -13,6 +13,7 @@ import {useBaseUrlUtils} from '@docusaurus/useBaseUrl';
|
|
|
13
13
|
import Link from '@docusaurus/Link';
|
|
14
14
|
import Head from '@docusaurus/Head';
|
|
15
15
|
import useSearchQuery from '@theme/hooks/useSearchQuery';
|
|
16
|
+
import {isRegexpStringMatch} from '@docusaurus/theme-common';
|
|
16
17
|
import {DocSearchButton, useDocSearchKeyboardEvents} from '@docsearch/react';
|
|
17
18
|
import useAlgoliaContextualFacetFilters from '@theme/hooks/useAlgoliaContextualFacetFilters';
|
|
18
19
|
import {translate} from '@docusaurus/Translate';
|
|
@@ -34,7 +35,7 @@ function ResultsFooter({state, onClose}) {
|
|
|
34
35
|
);
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
function DocSearch({contextualSearch, ...props}) {
|
|
38
|
+
function DocSearch({contextualSearch, externalUrlRegex, ...props}) {
|
|
38
39
|
const {siteMetadata} = useDocusaurusContext();
|
|
39
40
|
|
|
40
41
|
const contextualSearchFacetFilters = useAlgoliaContextualFacetFilters();
|
|
@@ -102,21 +103,28 @@ function DocSearch({contextualSearch, ...props}) {
|
|
|
102
103
|
|
|
103
104
|
const navigator = useRef({
|
|
104
105
|
navigate({itemUrl}) {
|
|
105
|
-
|
|
106
|
+
// Algolia results could contain URL's from other domains which cannot
|
|
107
|
+
// be served through history and should navigate with window.location
|
|
108
|
+
if (isRegexpStringMatch(externalUrlRegex, itemUrl)) {
|
|
109
|
+
window.location.href = itemUrl;
|
|
110
|
+
} else {
|
|
111
|
+
history.push(itemUrl);
|
|
112
|
+
}
|
|
106
113
|
},
|
|
107
114
|
}).current;
|
|
108
115
|
|
|
109
116
|
const transformItems = useRef((items) => {
|
|
110
117
|
return items.map((item) => {
|
|
111
|
-
//
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
a.href = item.url;
|
|
118
|
+
// If Algolia contains a external domain, we should navigate without relative URL
|
|
119
|
+
if (isRegexpStringMatch(externalUrlRegex, item.url)) {
|
|
120
|
+
return item;
|
|
121
|
+
}
|
|
116
122
|
|
|
123
|
+
// We transform the absolute URL into a relative URL.
|
|
124
|
+
const url = new URL(item.url);
|
|
117
125
|
return {
|
|
118
126
|
...item,
|
|
119
|
-
url: withBaseUrl(`${
|
|
127
|
+
url: withBaseUrl(`${url.pathname}${url.hash}`),
|
|
120
128
|
};
|
|
121
129
|
});
|
|
122
130
|
}).current;
|
|
@@ -16,7 +16,12 @@ import clsx from 'clsx';
|
|
|
16
16
|
import Head from '@docusaurus/Head';
|
|
17
17
|
import Link from '@docusaurus/Link';
|
|
18
18
|
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
useTitleFormatter,
|
|
21
|
+
usePluralForm,
|
|
22
|
+
isRegexpStringMatch,
|
|
23
|
+
useDynamicCallback,
|
|
24
|
+
} from '@docusaurus/theme-common';
|
|
20
25
|
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
|
21
26
|
import {useAllDocsData} from '@theme/hooks/useDocs';
|
|
22
27
|
import useSearchQuery from '@theme/hooks/useSearchQuery';
|
|
@@ -117,7 +122,7 @@ function SearchPage() {
|
|
|
117
122
|
const {
|
|
118
123
|
siteConfig: {
|
|
119
124
|
themeConfig: {
|
|
120
|
-
algolia: {appId, apiKey, indexName},
|
|
125
|
+
algolia: {appId, apiKey, indexName, externalUrlRegex},
|
|
121
126
|
},
|
|
122
127
|
},
|
|
123
128
|
i18n: {currentLocale},
|
|
@@ -125,8 +130,7 @@ function SearchPage() {
|
|
|
125
130
|
const documentsFoundPlural = useDocumentsFoundPlural();
|
|
126
131
|
|
|
127
132
|
const docsSearchVersionsHelpers = useDocsSearchVersionsHelpers();
|
|
128
|
-
const {
|
|
129
|
-
const [searchQuery, setSearchQuery] = useState(searchValue);
|
|
133
|
+
const {searchQuery, setSearchQuery} = useSearchQuery();
|
|
130
134
|
const initialSearchResultState = {
|
|
131
135
|
items: [],
|
|
132
136
|
query: null,
|
|
@@ -173,6 +177,7 @@ function SearchPage() {
|
|
|
173
177
|
},
|
|
174
178
|
initialSearchResultState,
|
|
175
179
|
);
|
|
180
|
+
|
|
176
181
|
const algoliaClient = algoliaSearch(appId, apiKey);
|
|
177
182
|
const algoliaHelper = algoliaSearchHelper(algoliaClient, indexName, {
|
|
178
183
|
hitsPerPage: 15,
|
|
@@ -201,14 +206,16 @@ function SearchPage() {
|
|
|
201
206
|
_highlightResult: {hierarchy},
|
|
202
207
|
_snippetResult: snippet = {},
|
|
203
208
|
}) => {
|
|
204
|
-
const
|
|
209
|
+
const parsedURL = new URL(url);
|
|
205
210
|
const titles = Object.keys(hierarchy).map((key) => {
|
|
206
211
|
return sanitizeValue(hierarchy[key].value);
|
|
207
212
|
});
|
|
208
213
|
|
|
209
214
|
return {
|
|
210
215
|
title: titles.pop(),
|
|
211
|
-
url:
|
|
216
|
+
url: isRegexpStringMatch(externalUrlRegex, parsedURL.href)
|
|
217
|
+
? parsedURL.href
|
|
218
|
+
: parsedURL.pathname + parsedURL.hash,
|
|
212
219
|
summary: snippet.content
|
|
213
220
|
? `${sanitizeValue(snippet.content.value)}...`
|
|
214
221
|
: '',
|
|
@@ -271,7 +278,7 @@ function SearchPage() {
|
|
|
271
278
|
description: 'The search page title for empty query',
|
|
272
279
|
});
|
|
273
280
|
|
|
274
|
-
const makeSearch = (page = 0) => {
|
|
281
|
+
const makeSearch = useDynamicCallback((page = 0) => {
|
|
275
282
|
algoliaHelper.addDisjunctiveFacetRefinement('docusaurus_tag', 'default');
|
|
276
283
|
algoliaHelper.addDisjunctiveFacetRefinement('language', currentLocale);
|
|
277
284
|
|
|
@@ -285,23 +292,19 @@ function SearchPage() {
|
|
|
285
292
|
);
|
|
286
293
|
|
|
287
294
|
algoliaHelper.setQuery(searchQuery).setPage(page).search();
|
|
288
|
-
};
|
|
295
|
+
});
|
|
289
296
|
|
|
290
297
|
useEffect(() => {
|
|
291
298
|
if (!loaderRef) {
|
|
292
299
|
return undefined;
|
|
293
300
|
}
|
|
301
|
+
const currentObserver = observer.current;
|
|
294
302
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
return () => {
|
|
298
|
-
observer.current.unobserve(loaderRef);
|
|
299
|
-
};
|
|
303
|
+
currentObserver.observe(loaderRef);
|
|
304
|
+
return () => currentObserver.unobserve(loaderRef);
|
|
300
305
|
}, [loaderRef]);
|
|
301
306
|
|
|
302
307
|
useEffect(() => {
|
|
303
|
-
updateSearchPath(searchQuery);
|
|
304
|
-
|
|
305
308
|
searchResultStateDispatcher({type: 'reset'});
|
|
306
309
|
|
|
307
310
|
if (searchQuery) {
|
|
@@ -311,7 +314,7 @@ function SearchPage() {
|
|
|
311
314
|
makeSearch();
|
|
312
315
|
}, 300);
|
|
313
316
|
}
|
|
314
|
-
}, [searchQuery, docsSearchVersionsHelpers.searchVersions]);
|
|
317
|
+
}, [searchQuery, docsSearchVersionsHelpers.searchVersions, makeSearch]);
|
|
315
318
|
|
|
316
319
|
useEffect(() => {
|
|
317
320
|
if (!searchResultState.lastPage || searchResultState.lastPage === 0) {
|
|
@@ -319,13 +322,7 @@ function SearchPage() {
|
|
|
319
322
|
}
|
|
320
323
|
|
|
321
324
|
makeSearch(searchResultState.lastPage);
|
|
322
|
-
}, [searchResultState.lastPage]);
|
|
323
|
-
|
|
324
|
-
useEffect(() => {
|
|
325
|
-
if (searchValue && searchValue !== searchQuery) {
|
|
326
|
-
setSearchQuery(searchValue);
|
|
327
|
-
}
|
|
328
|
-
}, [searchValue]);
|
|
325
|
+
}, [makeSearch, searchResultState.lastPage]);
|
|
329
326
|
|
|
330
327
|
return (
|
|
331
328
|
<Layout wrapperClassName="search-page-wrapper">
|
|
@@ -5,27 +5,34 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {useHistory
|
|
9
|
-
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
|
|
8
|
+
import {useHistory} from '@docusaurus/router';
|
|
10
9
|
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
|
10
|
+
import {useCallback, useEffect, useState} from 'react';
|
|
11
11
|
|
|
12
12
|
const SEARCH_PARAM_QUERY = 'q';
|
|
13
13
|
|
|
14
14
|
function useSearchQuery() {
|
|
15
15
|
const history = useHistory();
|
|
16
|
-
const
|
|
17
|
-
|
|
16
|
+
const {
|
|
17
|
+
siteConfig: {baseUrl},
|
|
18
|
+
} = useDocusaurusContext();
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
20
|
+
const [searchQuery, setSearchQueryState] = useState('');
|
|
21
|
+
|
|
22
|
+
// Init search query just after React hydration
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
const searchQueryStringValue =
|
|
25
|
+
new URLSearchParams(window.location.search).get(SEARCH_PARAM_QUERY) ?? '';
|
|
26
|
+
|
|
27
|
+
setSearchQueryState(searchQueryStringValue);
|
|
28
|
+
}, []);
|
|
29
|
+
|
|
30
|
+
const setSearchQuery = useCallback(
|
|
31
|
+
(newSearchQuery) => {
|
|
32
|
+
const searchParams = new URLSearchParams(window.location.search);
|
|
33
|
+
|
|
34
|
+
if (newSearchQuery) {
|
|
35
|
+
searchParams.set(SEARCH_PARAM_QUERY, newSearchQuery);
|
|
29
36
|
} else {
|
|
30
37
|
searchParams.delete(SEARCH_PARAM_QUERY);
|
|
31
38
|
}
|
|
@@ -33,11 +40,23 @@ function useSearchQuery() {
|
|
|
33
40
|
history.replace({
|
|
34
41
|
search: searchParams.toString(),
|
|
35
42
|
});
|
|
43
|
+
setSearchQueryState(newSearchQuery);
|
|
36
44
|
},
|
|
37
|
-
|
|
45
|
+
[history],
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const generateSearchPageLink = useCallback(
|
|
49
|
+
(targetSearchQuery) => {
|
|
38
50
|
// Refer to https://github.com/facebook/docusaurus/pull/2838
|
|
39
|
-
return `${baseUrl}search?q=${encodeURIComponent(
|
|
51
|
+
return `${baseUrl}search?q=${encodeURIComponent(targetSearchQuery)}`;
|
|
40
52
|
},
|
|
53
|
+
[baseUrl],
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
searchQuery,
|
|
58
|
+
setSearchQuery,
|
|
59
|
+
generateSearchPageLink,
|
|
41
60
|
};
|
|
42
61
|
}
|
|
43
62
|
|
|
@@ -22,7 +22,7 @@ const Schema = Joi.object({
|
|
|
22
22
|
algolia: Joi.object({
|
|
23
23
|
// Docusaurus attributes
|
|
24
24
|
contextualSearch: Joi.boolean().default(DEFAULT_CONFIG.contextualSearch),
|
|
25
|
-
|
|
25
|
+
externalUrlRegex: Joi.string().optional(),
|
|
26
26
|
// Algolia attributes
|
|
27
27
|
appId: Joi.string().default(DEFAULT_CONFIG.appId),
|
|
28
28
|
apiKey: Joi.string().required(),
|