feat(noResults): allow providing URL to report missing results (#1289)
This commit is contained in:
parent
e324f4ce51
commit
2c1fe7c77c
7 changed files with 149 additions and 41 deletions
|
|
@ -42,6 +42,7 @@ export interface DocSearchProps {
|
||||||
initialQuery?: string;
|
initialQuery?: string;
|
||||||
navigator?: AutocompleteOptions<InternalDocSearchHit>['navigator'];
|
navigator?: AutocompleteOptions<InternalDocSearchHit>['navigator'];
|
||||||
translations?: DocSearchTranslations;
|
translations?: DocSearchTranslations;
|
||||||
|
getMissingResultsUrl?: ({ query: string }) => string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DocSearch(props: DocSearchProps) {
|
export function DocSearch(props: DocSearchProps) {
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ export function DocSearchModal({
|
||||||
disableUserPersonalization = false,
|
disableUserPersonalization = false,
|
||||||
initialQuery: initialQueryFromProp = '',
|
initialQuery: initialQueryFromProp = '',
|
||||||
translations = {},
|
translations = {},
|
||||||
|
getMissingResultsUrl,
|
||||||
}: DocSearchModalProps) {
|
}: DocSearchModalProps) {
|
||||||
const {
|
const {
|
||||||
footer: footerTranslations,
|
footer: footerTranslations,
|
||||||
|
|
@ -429,6 +430,7 @@ export function DocSearchModal({
|
||||||
favoriteSearches={favoriteSearches}
|
favoriteSearches={favoriteSearches}
|
||||||
inputRef={inputRef}
|
inputRef={inputRef}
|
||||||
translations={screenStateTranslations}
|
translations={screenStateTranslations}
|
||||||
|
getMissingResultsUrl={getMissingResultsUrl}
|
||||||
onItemClick={(item) => {
|
onItemClick={(item) => {
|
||||||
saveRecentSearch(item);
|
saveRecentSearch(item);
|
||||||
onClose();
|
onClose();
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ import type { InternalDocSearchHit } from './types';
|
||||||
export type NoResultsScreenTranslations = Partial<{
|
export type NoResultsScreenTranslations = Partial<{
|
||||||
noResultsText: string;
|
noResultsText: string;
|
||||||
suggestedQueryText: string;
|
suggestedQueryText: string;
|
||||||
openIssueText: string;
|
reportMissingResultsText: string;
|
||||||
openIssueLinkText: string;
|
reportMissingResultsLinkText: string;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
type NoResultsScreenProps = Omit<
|
type NoResultsScreenProps = Omit<
|
||||||
|
|
@ -25,8 +25,8 @@ export function NoResultsScreen({
|
||||||
const {
|
const {
|
||||||
noResultsText = 'No results for',
|
noResultsText = 'No results for',
|
||||||
suggestedQueryText = 'Try searching for',
|
suggestedQueryText = 'Try searching for',
|
||||||
openIssueText = 'Believe this query should return results?',
|
reportMissingResultsText = 'Believe this query should return results?',
|
||||||
openIssueLinkText = 'Let us know',
|
reportMissingResultsLinkText = 'Let us know.',
|
||||||
} = translations;
|
} = translations;
|
||||||
const searchSuggestions: string[] | undefined = props.state.context
|
const searchSuggestions: string[] | undefined = props.state.context
|
||||||
.searchSuggestions as string[];
|
.searchSuggestions as string[];
|
||||||
|
|
@ -68,17 +68,18 @@ export function NoResultsScreen({
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<p className="DocSearch-Help">
|
{props.getMissingResultsUrl && (
|
||||||
{`${openIssueText} `}
|
<p className="DocSearch-Help">
|
||||||
<a
|
{`${reportMissingResultsText} `}
|
||||||
href={`https://github.com/algolia/docsearch-configs/issues/new?template=Missing_results.md&title=[${props.indexName}]+Missing+results+for+query+"${props.state.query}"`}
|
<a
|
||||||
target="_blank"
|
href={props.getMissingResultsUrl({ query: props.state.query })}
|
||||||
rel="noopener noreferrer"
|
target="_blank"
|
||||||
>
|
rel="noopener noreferrer"
|
||||||
{openIssueLinkText}
|
>
|
||||||
</a>
|
{reportMissingResultsLinkText}
|
||||||
.
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ export interface ScreenStateProps<TItem extends BaseItem>
|
||||||
disableUserPersonalization: boolean;
|
disableUserPersonalization: boolean;
|
||||||
resultsFooterComponent: DocSearchProps['resultsFooterComponent'];
|
resultsFooterComponent: DocSearchProps['resultsFooterComponent'];
|
||||||
translations: ScreenStateTranslations;
|
translations: ScreenStateTranslations;
|
||||||
|
getMissingResultsUrl?: DocSearchProps['getMissingResultsUrl'];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ScreenState = React.memo(
|
export const ScreenState = React.memo(
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,27 @@ function DocSearch(props: Partial<DocSearchProps>) {
|
||||||
return <DocSearchComponent apiKey="foo" indexName="bar" {...props} />;
|
return <DocSearchComponent apiKey="foo" indexName="bar" {...props} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mock empty response
|
||||||
|
function noResultSearch(_queries: any, _requestOptions?: any): Promise<any> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
resolve({
|
||||||
|
results: [
|
||||||
|
{
|
||||||
|
hits: [],
|
||||||
|
hitsPerPage: 0,
|
||||||
|
nbHits: 0,
|
||||||
|
nbPages: 0,
|
||||||
|
page: 0,
|
||||||
|
processingTimeMS: 0,
|
||||||
|
exhaustiveNbHits: true,
|
||||||
|
params: '',
|
||||||
|
query: '',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
describe('api', () => {
|
describe('api', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
document.body.innerHTML = '';
|
document.body.innerHTML = '';
|
||||||
|
|
@ -71,40 +92,23 @@ describe('api', () => {
|
||||||
it('overrides the default DocSearchModal noResultsScreen text', async () => {
|
it('overrides the default DocSearchModal noResultsScreen text', async () => {
|
||||||
render(
|
render(
|
||||||
<DocSearch
|
<DocSearch
|
||||||
// mock empty response
|
|
||||||
transformSearchClient={(searchClient) => {
|
transformSearchClient={(searchClient) => {
|
||||||
return {
|
return {
|
||||||
...searchClient,
|
...searchClient,
|
||||||
search: () => {
|
search: noResultSearch,
|
||||||
return new Promise((resolve) => {
|
|
||||||
resolve({
|
|
||||||
results: [
|
|
||||||
{
|
|
||||||
hits: [],
|
|
||||||
hitsPerPage: 0,
|
|
||||||
nbHits: 0,
|
|
||||||
nbPages: 0,
|
|
||||||
page: 0,
|
|
||||||
processingTimeMS: 0,
|
|
||||||
exhaustiveNbHits: true,
|
|
||||||
params: '',
|
|
||||||
query: '',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}}
|
}}
|
||||||
translations={{
|
translations={{
|
||||||
modal: {
|
modal: {
|
||||||
noResultsScreen: {
|
noResultsScreen: {
|
||||||
noResultsText: 'Pas de résultats pour',
|
noResultsText: 'Pas de résultats pour',
|
||||||
openIssueText: 'Ouvrez une issue sur docsearch-configs',
|
reportMissingResultsText:
|
||||||
openIssueLinkText: 'Lien du repo',
|
'Ouvrez une issue sur docsearch-configs',
|
||||||
|
reportMissingResultsLinkText: 'Lien du repo',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
getMissingResultsUrl={() => 'algolia.com'}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -190,4 +194,68 @@ describe('api', () => {
|
||||||
expect(screen.getByText('Selectionner')).toBeInTheDocument();
|
expect(screen.getByText('Selectionner')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getMissingResultsUrl', () => {
|
||||||
|
it('does not render the link to the repository by default', async () => {
|
||||||
|
render(
|
||||||
|
<DocSearch
|
||||||
|
transformSearchClient={(searchClient) => {
|
||||||
|
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(
|
||||||
|
<DocSearch
|
||||||
|
transformSearchClient={(searchClient) => {
|
||||||
|
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'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ title: API Reference
|
||||||
|
|
||||||
import Tabs from '@theme/Tabs';
|
import Tabs from '@theme/Tabs';
|
||||||
import TabItem from '@theme/TabItem';
|
import TabItem from '@theme/TabItem';
|
||||||
|
import useBaseUrl from '@docusaurus/useBaseUrl';
|
||||||
|
|
||||||
:::info
|
:::info
|
||||||
|
|
||||||
|
|
@ -160,8 +161,8 @@ const translations: DocSearchTranslations = {
|
||||||
noResultsScreen: {
|
noResultsScreen: {
|
||||||
noResultsText: 'No results for',
|
noResultsText: 'No results for',
|
||||||
suggestedQueryText: 'Try searching for',
|
suggestedQueryText: 'Try searching for',
|
||||||
openIssueText: 'Believe this query should return results?',
|
reportMissingResultsText: 'Believe this query should return results?',
|
||||||
openIssueLinkText: 'Let us know',
|
reportMissingResultsLinkText: 'Let us know.',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -170,6 +171,23 @@ const translations: DocSearchTranslations = {
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
## `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.
|
||||||
|
|
||||||
|
<div className="uil-ta-center">
|
||||||
|
<img
|
||||||
|
src={useBaseUrl('img/assets/noResultsScreen.png')}
|
||||||
|
alt="No results screen with informative message"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|
||||||
<TabItem value="react">
|
<TabItem value="react">
|
||||||
|
|
@ -299,8 +317,8 @@ const translations: DocSearchTranslations = {
|
||||||
noResultsScreen: {
|
noResultsScreen: {
|
||||||
noResultsText: 'No results for',
|
noResultsText: 'No results for',
|
||||||
suggestedQueryText: 'Try searching for',
|
suggestedQueryText: 'Try searching for',
|
||||||
openIssueText: 'Believe this query should return results?',
|
reportMissingResultsText: 'Believe this query should return results?',
|
||||||
openIssueLinkText: 'Let us know',
|
reportMissingResultsLinkText: 'Let us know.',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -309,6 +327,23 @@ const translations: DocSearchTranslations = {
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
## `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.
|
||||||
|
|
||||||
|
<div className="uil-ta-center">
|
||||||
|
<img
|
||||||
|
src={useBaseUrl('img/assets/noResultsScreen.png')}
|
||||||
|
alt="No results screen with informative message"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
|
||||||
BIN
packages/website/static/img/assets/noResultsScreen.png
Normal file
BIN
packages/website/static/img/assets/noResultsScreen.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 41 KiB |
Loading…
Reference in a new issue