@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docusaurus/theme-search-algolia",
3
- "version": "2.0.0-beta.8e9b829d9",
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.8e9b829d9",
18
- "@docusaurus/theme-common": "2.0.0-beta.8e9b829d9",
19
- "@docusaurus/utils": "2.0.0-beta.8e9b829d9",
20
- "@docusaurus/utils-validation": "2.0.0-beta.8e9b829d9",
21
- "algoliasearch": "^4.8.4",
22
- "algoliasearch-helper": "^3.3.4",
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.1",
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": ">=12.13.0"
32
+ "node": ">=14"
33
33
  },
34
- "gitHead": "cb79fda03ac12835068842a7e0a86f610974eae2"
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
@@ -60,7 +60,7 @@ function theme(context) {
60
60
  renderOpenSearchTemplate({
61
61
  title,
62
62
  url: url + baseUrl,
63
- favicon: normalizeUrl([url, baseUrl, favicon]),
63
+ favicon: favicon ? normalizeUrl([url, baseUrl, favicon]) : null,
64
64
  }),
65
65
  );
66
66
  } catch (err) {
@@ -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
- <Image width="16" height="16" type="image/x-icon"><%= it.favicon %></Image>
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
- history.push(itemUrl);
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
- // We transform the absolute URL into a relative URL.
112
- // Alternatively, we can use `new URL(item.url)` but it's not
113
- // supported in IE.
114
- const a = document.createElement('a');
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(`${a.pathname}${a.hash}`),
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 {useTitleFormatter, usePluralForm} from '@docusaurus/theme-common';
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 {searchValue, updateSearchPath} = useSearchQuery();
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 {pathname, hash} = new URL(url);
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: pathname + hash,
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
- observer.current.observe(loaderRef);
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, useLocation} from '@docusaurus/router';
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 location = useLocation();
17
- const {siteConfig: {baseUrl} = {}} = useDocusaurusContext();
16
+ const {
17
+ siteConfig: {baseUrl},
18
+ } = useDocusaurusContext();
18
19
 
19
- return {
20
- searchValue:
21
- (ExecutionEnvironment.canUseDOM &&
22
- new URLSearchParams(location.search).get(SEARCH_PARAM_QUERY)) ||
23
- '',
24
- updateSearchPath: (searchValue) => {
25
- const searchParams = new URLSearchParams(location.search);
26
-
27
- if (searchValue) {
28
- searchParams.set(SEARCH_PARAM_QUERY, searchValue);
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
- generateSearchPageLink: (searchValue) => {
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(searchValue)}`;
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(),