diff --git a/.changeset/search-gentle-chairs-grab.md b/.changeset/search-gentle-chairs-grab.md new file mode 100644 index 0000000000..f3e2cf3cec --- /dev/null +++ b/.changeset/search-gentle-chairs-grab.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search': patch +--- + +SearchBar component to accept optional placeholder prop diff --git a/docs/features/search/how-to-guides.md b/docs/features/search/how-to-guides.md new file mode 100644 index 0000000000..b6ea6ae364 --- /dev/null +++ b/docs/features/search/how-to-guides.md @@ -0,0 +1,46 @@ +--- +id: how-to-guides +title: Search "HOW TO" guides +sidebar_label: "HOW TO" guides +description: Search "HOW TO" guides +--- + +## How to implement your own Search API + +The Search plugin provides implementation of one primary API by default: the +[SearchApi](https://github.com/backstage/backstage/blob/db2666b980853c281b8fe77905d7639c5d255f13/plugins/search/src/apis.ts#L35), +which is responsible for talking to the search-backend to query search results. + +There may be occasions where you need to implement this API yourself, to +customize it to your own needs - for example if you have your own search backend +that you want to talk to. The purpose of this guide is to walk you through how +to do that in two steps. + +1. Implement the `SearchApi` + [interface](https://github.com/backstage/backstage/blob/db2666b980853c281b8fe77905d7639c5d255f13/plugins/search/src/apis.ts#L31) + according to your needs. + +```typescript +export class SearchClient implements SearchApi { + // your implementation +} +``` + +2. Override the API ref `searchApiRef` with your new implemented API in the + `App.tsx` using `ApiFactories`. + [Read more about App APIs](https://backstage.io/docs/api/utility-apis#app-apis). + +```typescript +const app = createApp({ + apis: [ + // SearchApi + createApiFactory({ + api: searchApiRef, + deps: { discovery: discoveryApiRef }, + factory({ discovery }) { + return new SearchClient({ discoveryApi: discovery }); + }, + }), + ], +}); +``` diff --git a/microsite/sidebars.json b/microsite/sidebars.json index 8b8b359fdc..bb9fd5783a 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -90,7 +90,8 @@ "features/search/getting-started", "features/search/concepts", "features/search/architecture", - "features/search/search-engines" + "features/search/search-engines", + "features/search/how-to-guides" ] }, { diff --git a/mkdocs.yml b/mkdocs.yml index b668ffdb15..7376893586 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -68,6 +68,7 @@ nav: - Concepts: 'features/search/concepts.md' - Search Architecture: 'features/search/architecture.md' - Search Engines: 'features/search/search-engines.md' + - HOW TO guides: 'features/search/how-to-guides.md' - TechDocs: - Overview: 'features/techdocs/README.md' - Getting Started: 'features/techdocs/getting-started.md' diff --git a/plugins/search/api-report.md b/plugins/search/api-report.md index 169972d02d..b29149805c 100644 --- a/plugins/search/api-report.md +++ b/plugins/search/api-report.md @@ -79,7 +79,11 @@ export const searchApiRef: ApiRef; // Warning: (ae-missing-release-tag) "SearchBar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const SearchBar: ({ className, debounceTime }: Props) => JSX.Element; +export const SearchBar: ({ + className, + debounceTime, + placeholder, +}: Props) => JSX.Element; // Warning: (ae-missing-release-tag) "SearchBarNext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -87,9 +91,11 @@ export const SearchBar: ({ className, debounceTime }: Props) => JSX.Element; export const SearchBarNext: ({ className, debounceTime, + placeholder, }: { className?: string | undefined; debounceTime?: number | undefined; + placeholder?: string | undefined; }) => JSX.Element; // Warning: (ae-missing-release-tag) "SearchContextProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/plugins/search/src/components/SearchBar/SearchBar.stories.tsx b/plugins/search/src/components/SearchBar/SearchBar.stories.tsx index fa2ba0d94e..2c990e512c 100644 --- a/plugins/search/src/components/SearchBar/SearchBar.stories.tsx +++ b/plugins/search/src/components/SearchBar/SearchBar.stories.tsx @@ -45,3 +45,20 @@ export const Default = () => { ); }; + +export const CustomPlaceholder = () => { + return ( + + {/* @ts-ignore (defaultValue requires more than what is used here) */} + + + + + + + + + + + ); +}; diff --git a/plugins/search/src/components/SearchBar/SearchBar.test.tsx b/plugins/search/src/components/SearchBar/SearchBar.test.tsx index de8ea9a5a7..3a11daa9fa 100644 --- a/plugins/search/src/components/SearchBar/SearchBar.test.tsx +++ b/plugins/search/src/components/SearchBar/SearchBar.test.tsx @@ -65,6 +65,26 @@ describe('SearchBar', () => { await waitFor(() => { expect(screen.getByRole('textbox', { name })).toBeInTheDocument(); + expect( + screen.getByPlaceholderText('Search in Mock title'), + ).toBeInTheDocument(); + }); + }); + + it('Renders with custom placeholder', async () => { + render( + + + + + , + , + ); + + await waitFor(() => { + expect( + screen.getByPlaceholderText('This is a custom placeholder'), + ).toBeInTheDocument(); }); }); diff --git a/plugins/search/src/components/SearchBar/SearchBar.tsx b/plugins/search/src/components/SearchBar/SearchBar.tsx index 214e22520c..53802899eb 100644 --- a/plugins/search/src/components/SearchBar/SearchBar.tsx +++ b/plugins/search/src/components/SearchBar/SearchBar.tsx @@ -89,9 +89,14 @@ export const SearchBarBase = ({ type Props = { className?: string; debounceTime?: number; + placeholder?: string; }; -export const SearchBar = ({ className, debounceTime = 0 }: Props) => { +export const SearchBar = ({ + className, + debounceTime = 0, + placeholder, +}: Props) => { const { term, setTerm } = useSearch(); const [value, setValue] = useState(term); @@ -113,6 +118,7 @@ export const SearchBar = ({ className, debounceTime = 0 }: Props) => { value={value} onChange={handleQuery} onClear={handleClear} + placeholder={placeholder} /> ); };