diff --git a/packages/docsearch-react/src/DocSearch.tsx b/packages/docsearch-react/src/DocSearch.tsx index b2759fe4..26cded14 100644 --- a/packages/docsearch-react/src/DocSearch.tsx +++ b/packages/docsearch-react/src/DocSearch.tsx @@ -42,6 +42,7 @@ export interface DocSearchProps { initialQuery?: string; navigator?: AutocompleteOptions['navigator']; translations?: DocSearchTranslations; + getMissingResultsUrl?: ({ query: string }) => string; } export function DocSearch(props: DocSearchProps) { diff --git a/packages/docsearch-react/src/DocSearchModal.tsx b/packages/docsearch-react/src/DocSearchModal.tsx index f3911833..d4cdc421 100644 --- a/packages/docsearch-react/src/DocSearchModal.tsx +++ b/packages/docsearch-react/src/DocSearchModal.tsx @@ -50,6 +50,7 @@ export function DocSearchModal({ disableUserPersonalization = false, initialQuery: initialQueryFromProp = '', translations = {}, + getMissingResultsUrl, }: DocSearchModalProps) { const { footer: footerTranslations, @@ -429,6 +430,7 @@ export function DocSearchModal({ favoriteSearches={favoriteSearches} inputRef={inputRef} translations={screenStateTranslations} + getMissingResultsUrl={getMissingResultsUrl} onItemClick={(item) => { saveRecentSearch(item); onClose(); diff --git a/packages/docsearch-react/src/NoResultsScreen.tsx b/packages/docsearch-react/src/NoResultsScreen.tsx index 19b8bd40..0c4fa9a1 100644 --- a/packages/docsearch-react/src/NoResultsScreen.tsx +++ b/packages/docsearch-react/src/NoResultsScreen.tsx @@ -7,8 +7,8 @@ import type { InternalDocSearchHit } from './types'; export type NoResultsScreenTranslations = Partial<{ noResultsText: string; suggestedQueryText: string; - openIssueText: string; - openIssueLinkText: string; + reportMissingResultsText: string; + reportMissingResultsLinkText: string; }>; type NoResultsScreenProps = Omit< @@ -25,8 +25,8 @@ export function NoResultsScreen({ const { noResultsText = 'No results for', suggestedQueryText = 'Try searching for', - openIssueText = 'Believe this query should return results?', - openIssueLinkText = 'Let us know', + reportMissingResultsText = 'Believe this query should return results?', + reportMissingResultsLinkText = 'Let us know.', } = translations; const searchSuggestions: string[] | undefined = props.state.context .searchSuggestions as string[]; @@ -68,17 +68,18 @@ export function NoResultsScreen({ )} -

- {`${openIssueText} `} - - {openIssueLinkText} - - . -

+ {props.getMissingResultsUrl && ( +

+ {`${reportMissingResultsText} `} + + {reportMissingResultsLinkText} + +

+ )} ); } diff --git a/packages/docsearch-react/src/ScreenState.tsx b/packages/docsearch-react/src/ScreenState.tsx index be2e8bf8..6696ef80 100644 --- a/packages/docsearch-react/src/ScreenState.tsx +++ b/packages/docsearch-react/src/ScreenState.tsx @@ -39,6 +39,7 @@ export interface ScreenStateProps disableUserPersonalization: boolean; resultsFooterComponent: DocSearchProps['resultsFooterComponent']; translations: ScreenStateTranslations; + getMissingResultsUrl?: DocSearchProps['getMissingResultsUrl']; } export const ScreenState = React.memo( diff --git a/packages/docsearch-react/src/__tests__/api.test.tsx b/packages/docsearch-react/src/__tests__/api.test.tsx index 3b9dea95..efd23979 100644 --- a/packages/docsearch-react/src/__tests__/api.test.tsx +++ b/packages/docsearch-react/src/__tests__/api.test.tsx @@ -16,6 +16,27 @@ function DocSearch(props: Partial) { return ; } +// mock empty response +function noResultSearch(_queries: any, _requestOptions?: any): Promise { + return new Promise((resolve) => { + resolve({ + results: [ + { + hits: [], + hitsPerPage: 0, + nbHits: 0, + nbPages: 0, + page: 0, + processingTimeMS: 0, + exhaustiveNbHits: true, + params: '', + query: '', + }, + ], + }); + }); +} + describe('api', () => { beforeEach(() => { document.body.innerHTML = ''; @@ -71,40 +92,23 @@ describe('api', () => { it('overrides the default DocSearchModal noResultsScreen text', async () => { render( { return { ...searchClient, - search: () => { - return new Promise((resolve) => { - resolve({ - results: [ - { - hits: [], - hitsPerPage: 0, - nbHits: 0, - nbPages: 0, - page: 0, - processingTimeMS: 0, - exhaustiveNbHits: true, - params: '', - query: '', - }, - ], - }); - }); - }, + search: noResultSearch, }; }} translations={{ modal: { noResultsScreen: { noResultsText: 'Pas de résultats pour', - openIssueText: 'Ouvrez une issue sur docsearch-configs', - openIssueLinkText: 'Lien du repo', + reportMissingResultsText: + 'Ouvrez une issue sur docsearch-configs', + reportMissingResultsLinkText: 'Lien du repo', }, }, }} + getMissingResultsUrl={() => 'algolia.com'} /> ); @@ -190,4 +194,68 @@ describe('api', () => { expect(screen.getByText('Selectionner')).toBeInTheDocument(); }); }); + + describe('getMissingResultsUrl', () => { + it('does not render the link to the repository by default', async () => { + render( + { + return { + ...searchClient, + search: noResultSearch, + }; + }} + /> + ); + + await act(async () => { + await waitFor(() => { + fireEvent.click(document.querySelector('.DocSearch-Button')); + }); + + fireEvent.input(document.querySelector('.DocSearch-Input'), { + target: { value: 'q' }, + }); + }); + + expect(screen.getByText(/No results for/)).toBeInTheDocument(); + expect( + document.querySelector('.DocSearch-Help a') + ).not.toBeInTheDocument(); + }); + + it('render the link to the repository', async () => { + render( + { + return { + ...searchClient, + search: noResultSearch, + }; + }} + getMissingResultsUrl={({ query }) => + `https://github.com/algolia/docsearch/issues/new?title=${query}` + } + /> + ); + + await act(async () => { + await waitFor(() => { + fireEvent.click(document.querySelector('.DocSearch-Button')); + }); + + fireEvent.input(document.querySelector('.DocSearch-Input'), { + target: { value: 'q' }, + }); + }); + + expect(screen.getByText(/No results for/)).toBeInTheDocument(); + + const link = document.querySelector('.DocSearch-Help a'); + expect(link).toBeInTheDocument(); + expect(link.getAttribute('href')).toBe( + 'https://github.com/algolia/docsearch/issues/new?title=q' + ); + }); + }); }); diff --git a/packages/website/docs/api.mdx b/packages/website/docs/api.mdx index 6f270aa4..117ddf26 100644 --- a/packages/website/docs/api.mdx +++ b/packages/website/docs/api.mdx @@ -4,6 +4,7 @@ title: API Reference import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; +import useBaseUrl from '@docusaurus/useBaseUrl'; :::info @@ -160,8 +161,8 @@ const translations: DocSearchTranslations = { noResultsScreen: { noResultsText: 'No results for', suggestedQueryText: 'Try searching for', - openIssueText: 'Believe this query should return results?', - openIssueLinkText: 'Let us know', + reportMissingResultsText: 'Believe this query should return results?', + reportMissingResultsLinkText: 'Let us know.', }, }, }; @@ -170,6 +171,23 @@ const translations: DocSearchTranslations = { +## `getMissingResultsUrl` + +> `type: ({ query: string }) => string` | **optional** + +> example: ({ query }) => `https://github.com/algolia/docsearch/issues/new?title=${query}` + +Function to return the URL of your documentation repository. + +When provided, an informative message wrapped with your link will be displayed on no results searches. The default text can be changed using the [translations](#translations) property. + +
+ No results screen with informative message +
+ @@ -299,8 +317,8 @@ const translations: DocSearchTranslations = { noResultsScreen: { noResultsText: 'No results for', suggestedQueryText: 'Try searching for', - openIssueText: 'Believe this query should return results?', - openIssueLinkText: 'Let us know', + reportMissingResultsText: 'Believe this query should return results?', + reportMissingResultsLinkText: 'Let us know.', }, }, }; @@ -309,6 +327,23 @@ const translations: DocSearchTranslations = { +## `getMissingResultsUrl` + +> `type: ({ query: string }) => string` | **optional** + +> example: ({ query }) => `https://github.com/algolia/docsearch/issues/new?title=${query}` + +Function to return the URL of your documentation repository. + +When provided, an informative message wrapped with your link will be displayed on no results searches. The default text can be changed using the [translations](#translations) property. + +
+ No results screen with informative message +
+
diff --git a/packages/website/static/img/assets/noResultsScreen.png b/packages/website/static/img/assets/noResultsScreen.png new file mode 100644 index 00000000..dbed5d4c Binary files /dev/null and b/packages/website/static/img/assets/noResultsScreen.png differ