mirror of
https://github.com/facebook/docusaurus.git
synced 2025-06-06 12:52:31 +02:00
fix(search): search page should react to querystring changes + cleanup/refactor (#8757)
This commit is contained in:
parent
ea2b13ea94
commit
2f75979bc5
6 changed files with 70 additions and 69 deletions
|
@ -5,32 +5,24 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {useCallback, useEffect, useState} from 'react';
|
import {useCallback} from 'react';
|
||||||
import {useHistory} from '@docusaurus/router';
|
|
||||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||||
|
import {useQueryString} from '../utils/historyUtils';
|
||||||
import type {ThemeConfig as AlgoliaThemeConfig} from '@docusaurus/theme-search-algolia';
|
import type {ThemeConfig as AlgoliaThemeConfig} from '@docusaurus/theme-search-algolia';
|
||||||
|
|
||||||
const SEARCH_PARAM_QUERY = 'q';
|
const SEARCH_PARAM_QUERY = 'q';
|
||||||
|
|
||||||
/** Some utility functions around search queries. */
|
/**
|
||||||
export function useSearchPage(): {
|
* Permits to read/write the current search query string
|
||||||
/**
|
*/
|
||||||
* Works hand-in-hand with `setSearchQuery`; whatever the user has inputted
|
export function useSearchQueryString(): [string, (newValue: string) => void] {
|
||||||
* into the search box.
|
return useQueryString(SEARCH_PARAM_QUERY);
|
||||||
*/
|
}
|
||||||
searchQuery: string;
|
|
||||||
/**
|
/**
|
||||||
* Set a new search query. In addition to updating `searchQuery`, this handle
|
* Permits to create links to the search page with the appropriate query string
|
||||||
* also mutates the location and appends the query.
|
*/
|
||||||
*/
|
export function useSearchLinkCreator(): (searchValue: string) => string {
|
||||||
setSearchQuery: (newSearchQuery: string) => void;
|
|
||||||
/**
|
|
||||||
* Given a query, this handle generates the corresponding search page link,
|
|
||||||
* with base URL prepended.
|
|
||||||
*/
|
|
||||||
generateSearchPageLink: (targetSearchQuery: string) => string;
|
|
||||||
} {
|
|
||||||
const history = useHistory();
|
|
||||||
const {
|
const {
|
||||||
siteConfig: {baseUrl, themeConfig},
|
siteConfig: {baseUrl, themeConfig},
|
||||||
} = useDocusaurusContext();
|
} = useDocusaurusContext();
|
||||||
|
@ -38,47 +30,13 @@ export function useSearchPage(): {
|
||||||
algolia: {searchPagePath},
|
algolia: {searchPagePath},
|
||||||
} = themeConfig as AlgoliaThemeConfig;
|
} = themeConfig as AlgoliaThemeConfig;
|
||||||
|
|
||||||
const [searchQuery, setSearchQueryState] = useState('');
|
return useCallback(
|
||||||
|
(searchValue: string) =>
|
||||||
// Init search query just after React hydration
|
|
||||||
useEffect(() => {
|
|
||||||
const searchQueryStringValue =
|
|
||||||
new URLSearchParams(window.location.search).get(SEARCH_PARAM_QUERY) ?? '';
|
|
||||||
|
|
||||||
setSearchQueryState(searchQueryStringValue);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const setSearchQuery = useCallback(
|
|
||||||
(newSearchQuery: string) => {
|
|
||||||
const searchParams = new URLSearchParams(window.location.search);
|
|
||||||
|
|
||||||
if (newSearchQuery) {
|
|
||||||
searchParams.set(SEARCH_PARAM_QUERY, newSearchQuery);
|
|
||||||
} else {
|
|
||||||
searchParams.delete(SEARCH_PARAM_QUERY);
|
|
||||||
}
|
|
||||||
|
|
||||||
history.replace({
|
|
||||||
search: searchParams.toString(),
|
|
||||||
});
|
|
||||||
setSearchQueryState(newSearchQuery);
|
|
||||||
},
|
|
||||||
[history],
|
|
||||||
);
|
|
||||||
|
|
||||||
const generateSearchPageLink = useCallback(
|
|
||||||
(targetSearchQuery: string) =>
|
|
||||||
// Refer to https://github.com/facebook/docusaurus/pull/2838
|
// Refer to https://github.com/facebook/docusaurus/pull/2838
|
||||||
// Note: if searchPagePath is falsy, useSearchPage() will not be called
|
// Note: if searchPagePath is falsy, useSearchPage() will not be called
|
||||||
`${baseUrl}${
|
`${baseUrl}${
|
||||||
searchPagePath as string
|
searchPagePath as string
|
||||||
}?${SEARCH_PARAM_QUERY}=${encodeURIComponent(targetSearchQuery)}`,
|
}?${SEARCH_PARAM_QUERY}=${encodeURIComponent(searchValue)}`,
|
||||||
[baseUrl, searchPagePath],
|
[baseUrl, searchPagePath],
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
|
||||||
searchQuery,
|
|
||||||
setSearchQuery,
|
|
||||||
generateSearchPageLink,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,6 +73,11 @@ export {
|
||||||
type TagLetterEntry,
|
type TagLetterEntry,
|
||||||
} from './utils/tagsUtils';
|
} from './utils/tagsUtils';
|
||||||
|
|
||||||
|
export {
|
||||||
|
useSearchQueryString,
|
||||||
|
useSearchLinkCreator,
|
||||||
|
} from './hooks/useSearchPage';
|
||||||
|
|
||||||
export {isMultiColumnFooterLinks} from './utils/footerUtils';
|
export {isMultiColumnFooterLinks} from './utils/footerUtils';
|
||||||
|
|
||||||
export {isRegexpStringMatch} from './utils/regexpUtils';
|
export {isRegexpStringMatch} from './utils/regexpUtils';
|
||||||
|
|
|
@ -121,7 +121,6 @@ export {
|
||||||
keyboardFocusedClassName,
|
keyboardFocusedClassName,
|
||||||
} from './hooks/useKeyboardNavigation';
|
} from './hooks/useKeyboardNavigation';
|
||||||
export {useLockBodyScroll} from './hooks/useLockBodyScroll';
|
export {useLockBodyScroll} from './hooks/useLockBodyScroll';
|
||||||
export {useSearchPage} from './hooks/useSearchPage';
|
|
||||||
export {useCodeWordWrap} from './hooks/useCodeWordWrap';
|
export {useCodeWordWrap} from './hooks/useCodeWordWrap';
|
||||||
export {getPrismCssVariables} from './utils/codeBlockUtils';
|
export {getPrismCssVariables} from './utils/codeBlockUtils';
|
||||||
export {useBackToTopButton} from './hooks/useBackToTopButton';
|
export {useBackToTopButton} from './hooks/useBackToTopButton';
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {useEffect} from 'react';
|
import {useCallback, useEffect} from 'react';
|
||||||
import {useHistory} from '@docusaurus/router';
|
import {useHistory} from '@docusaurus/router';
|
||||||
// @ts-expect-error: TODO temporary until React 18 upgrade
|
// @ts-expect-error: TODO temporary until React 18 upgrade
|
||||||
import {useSyncExternalStore} from 'use-sync-external-store/shim';
|
import {useSyncExternalStore} from 'use-sync-external-store/shim';
|
||||||
|
@ -75,3 +75,42 @@ export function useQueryStringValue(key: string | null): string | null {
|
||||||
return new URLSearchParams(history.location.search).get(key);
|
return new URLSearchParams(history.location.search).get(key);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useQueryStringKeySetter(): (
|
||||||
|
key: string,
|
||||||
|
newValue: string | null,
|
||||||
|
options?: {push: boolean},
|
||||||
|
) => void {
|
||||||
|
const history = useHistory();
|
||||||
|
return useCallback(
|
||||||
|
(key, newValue, options) => {
|
||||||
|
const searchParams = new URLSearchParams(history.location.search);
|
||||||
|
if (newValue) {
|
||||||
|
searchParams.set(key, newValue);
|
||||||
|
} else {
|
||||||
|
searchParams.delete(key);
|
||||||
|
}
|
||||||
|
const updaterFn = options?.push ? history.push : history.replace;
|
||||||
|
updaterFn({
|
||||||
|
search: searchParams.toString(),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[history],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useQueryString(
|
||||||
|
key: string,
|
||||||
|
): [string, (newValue: string, options?: {push: boolean}) => void] {
|
||||||
|
const value = useQueryStringValue(key) ?? '';
|
||||||
|
const setQueryString = useQueryStringKeySetter();
|
||||||
|
return [
|
||||||
|
value,
|
||||||
|
useCallback(
|
||||||
|
(newValue: string, options) => {
|
||||||
|
setQueryString(key, newValue, options);
|
||||||
|
},
|
||||||
|
[setQueryString, key],
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
|
@ -10,8 +10,10 @@ import {DocSearchButton, useDocSearchKeyboardEvents} from '@docsearch/react';
|
||||||
import Head from '@docusaurus/Head';
|
import Head from '@docusaurus/Head';
|
||||||
import Link from '@docusaurus/Link';
|
import Link from '@docusaurus/Link';
|
||||||
import {useHistory} from '@docusaurus/router';
|
import {useHistory} from '@docusaurus/router';
|
||||||
import {isRegexpStringMatch} from '@docusaurus/theme-common';
|
import {
|
||||||
import {useSearchPage} from '@docusaurus/theme-common/internal';
|
isRegexpStringMatch,
|
||||||
|
useSearchLinkCreator,
|
||||||
|
} from '@docusaurus/theme-common';
|
||||||
import {
|
import {
|
||||||
useAlgoliaContextualFacetFilters,
|
useAlgoliaContextualFacetFilters,
|
||||||
useSearchResultUrlProcessor,
|
useSearchResultUrlProcessor,
|
||||||
|
@ -59,10 +61,10 @@ type ResultsFooterProps = {
|
||||||
};
|
};
|
||||||
|
|
||||||
function ResultsFooter({state, onClose}: ResultsFooterProps) {
|
function ResultsFooter({state, onClose}: ResultsFooterProps) {
|
||||||
const {generateSearchPageLink} = useSearchPage();
|
const createSearchLink = useSearchLinkCreator();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link to={generateSearchPageLink(state.query)} onClick={onClose}>
|
<Link to={createSearchLink(state.query)} onClick={onClose}>
|
||||||
<Translate
|
<Translate
|
||||||
id="theme.SearchBar.seeAll"
|
id="theme.SearchBar.seeAll"
|
||||||
values={{count: state.context.nbHits}}>
|
values={{count: state.context.nbHits}}>
|
||||||
|
|
|
@ -21,11 +21,9 @@ import {
|
||||||
HtmlClassNameProvider,
|
HtmlClassNameProvider,
|
||||||
useEvent,
|
useEvent,
|
||||||
usePluralForm,
|
usePluralForm,
|
||||||
|
useSearchQueryString,
|
||||||
} from '@docusaurus/theme-common';
|
} from '@docusaurus/theme-common';
|
||||||
import {
|
import {useTitleFormatter} from '@docusaurus/theme-common/internal';
|
||||||
useSearchPage,
|
|
||||||
useTitleFormatter,
|
|
||||||
} from '@docusaurus/theme-common/internal';
|
|
||||||
import Translate, {translate} from '@docusaurus/Translate';
|
import Translate, {translate} from '@docusaurus/Translate';
|
||||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||||
import {
|
import {
|
||||||
|
@ -167,7 +165,7 @@ function SearchPageContent(): JSX.Element {
|
||||||
const documentsFoundPlural = useDocumentsFoundPlural();
|
const documentsFoundPlural = useDocumentsFoundPlural();
|
||||||
|
|
||||||
const docsSearchVersionsHelpers = useDocsSearchVersionsHelpers();
|
const docsSearchVersionsHelpers = useDocsSearchVersionsHelpers();
|
||||||
const {searchQuery, setSearchQuery} = useSearchPage();
|
const [searchQuery, setSearchQuery] = useSearchQueryString();
|
||||||
const initialSearchResultState: ResultDispatcherState = {
|
const initialSearchResultState: ResultDispatcherState = {
|
||||||
items: [],
|
items: [],
|
||||||
query: null,
|
query: null,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue