docs: migrate feature docs to new frontend system as primary content
Rewrite documentation for TechDocs, Software Templates, Software Catalog, Search, and Kubernetes features to use the new frontend system as the primary installation and configuration instructions. Old frontend system instructions are moved to separate `--old` suffixed files for pages with substantial legacy content, or updated inline for pages with minimal old-system content. Files migrated: - techdocs/getting-started.md - techdocs/how-to-guides.md - software-templates/writing-custom-step-layouts.md - software-templates/writing-custom-field-extensions.md - software-templates/index.md - software-catalog/catalog-customization.md - search/getting-started.md - search/how-to-guides.md - kubernetes/installation.md Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com> Made-with: Cursor
This commit is contained in:
@@ -0,0 +1,351 @@
|
||||
---
|
||||
id: getting-started--old
|
||||
title: Getting Started with Search (Old Frontend System)
|
||||
description: How to set up and install Backstage Search
|
||||
---
|
||||
|
||||
::::info
|
||||
This documentation is for Backstage apps that still use the old frontend
|
||||
system. If your app uses the new frontend system, read the
|
||||
[current guide](./getting-started.md) instead.
|
||||
::::
|
||||
|
||||
Search functions as a plugin to Backstage, so you will need to use Backstage to
|
||||
use Search.
|
||||
|
||||
If you haven't setup Backstage already, start
|
||||
[here](../../getting-started/index.md).
|
||||
|
||||
> If you used `npx @backstage/create-app`, and you have a search page defined in
|
||||
> `packages/app/src/components/search`, skip to
|
||||
> [`Customizing Search`](#customizing-search) below.
|
||||
|
||||
## Adding Search to the Frontend
|
||||
|
||||
```bash title="From your Backstage root directory"
|
||||
yarn --cwd packages/app add @backstage/plugin-search @backstage/plugin-search-react
|
||||
```
|
||||
|
||||
Create a new `packages/app/src/components/search/SearchPage.tsx` file in your
|
||||
Backstage app with the following contents:
|
||||
|
||||
```tsx
|
||||
import { Content, Header, Page } from '@backstage/core-components';
|
||||
import { Grid, List, Card, CardContent } from '@material-ui/core';
|
||||
import {
|
||||
SearchBar,
|
||||
SearchResult,
|
||||
DefaultResultListItem,
|
||||
SearchFilter,
|
||||
} from '@backstage/plugin-search-react';
|
||||
import { CatalogSearchResultListItem } from '@backstage/plugin-catalog';
|
||||
|
||||
export const searchPage = (
|
||||
<Page themeId="home">
|
||||
<Header title="Search" />
|
||||
<Content>
|
||||
<Grid container direction="row">
|
||||
<Grid item xs={12}>
|
||||
<SearchBar />
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Card>
|
||||
<CardContent>
|
||||
<SearchFilter.Select
|
||||
name="kind"
|
||||
values={['Component', 'Template']}
|
||||
/>
|
||||
</CardContent>
|
||||
<CardContent>
|
||||
<SearchFilter.Checkbox
|
||||
name="lifecycle"
|
||||
values={['experimental', 'production']}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
<Grid item xs={9}>
|
||||
<SearchResult>
|
||||
{({ results }) => (
|
||||
<List>
|
||||
{results.map(result => {
|
||||
switch (result.type) {
|
||||
case 'software-catalog':
|
||||
return (
|
||||
<CatalogSearchResultListItem
|
||||
key={result.document.location}
|
||||
result={result.document}
|
||||
highlight={result.highlight}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<DefaultResultListItem
|
||||
key={result.document.location}
|
||||
result={result.document}
|
||||
highlight={result.highlight}
|
||||
/>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</List>
|
||||
)}
|
||||
</SearchResult>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Content>
|
||||
</Page>
|
||||
);
|
||||
```
|
||||
|
||||
Bind the above Search Page to the `/search` route in your
|
||||
`packages/app/src/App.tsx` file, like this:
|
||||
|
||||
```tsx
|
||||
import { SearchPage } from '@backstage/plugin-search';
|
||||
import { searchPage } from './components/search/SearchPage';
|
||||
|
||||
const routes = (
|
||||
<FlatRoutes>
|
||||
<Route path="/search" element={<SearchPage />}>
|
||||
{searchPage}
|
||||
</Route>
|
||||
</FlatRoutes>
|
||||
);
|
||||
```
|
||||
|
||||
### Using the Search Modal
|
||||
|
||||
In `Root.tsx`, add the `SidebarSearchModal` component:
|
||||
|
||||
```bash
|
||||
import { SidebarSearchModal } from '@backstage/plugin-search';
|
||||
|
||||
export const Root = ({ children }: PropsWithChildren<{}>) => (
|
||||
<SidebarPage>
|
||||
<Sidebar>
|
||||
<SidebarLogo />
|
||||
<SidebarSearchModal />
|
||||
<SidebarDivider />
|
||||
...
|
||||
```
|
||||
|
||||
For more information about using `Root.tsx`, please see
|
||||
[the changelog](https://github.com/backstage/backstage/blob/master/packages/create-app/CHANGELOG.md#0315).
|
||||
|
||||
## Adding Search to the Backend
|
||||
|
||||
Add the following plugins into your backend app:
|
||||
|
||||
```bash title="From your Backstage root directory"
|
||||
yarn --cwd packages/backend add @backstage/plugin-search-backend @backstage/plugin-search-backend-module-pg @backstage/plugin-search-backend-module-catalog @backstage/plugin-search-backend-module-techdocs
|
||||
```
|
||||
|
||||
Then add the following lines:
|
||||
|
||||
```ts title="packages/backend/src/index.ts"
|
||||
const backend = createBackend();
|
||||
|
||||
// Other plugins...
|
||||
|
||||
/* highlight-add-start */
|
||||
// search plugin
|
||||
backend.add(import('@backstage/plugin-search-backend'));
|
||||
|
||||
// search engines
|
||||
backend.add(import('@backstage/plugin-search-backend-module-pg'));
|
||||
|
||||
// search collators
|
||||
backend.add(import('@backstage/plugin-search-backend-module-catalog'));
|
||||
backend.add(import('@backstage/plugin-search-backend-module-techdocs'));
|
||||
/* highlight-add-end */
|
||||
|
||||
backend.start();
|
||||
```
|
||||
|
||||
With the above setup Search will use the [Lunr](https://github.com/olivernn/lunr.js) in-memory Search Engine but if your have Postgres setup as your database then it will use Postgres as your Search Engine. Learn more in the [Search Engines](./search-engines.md) documentation.
|
||||
|
||||
The above also sets up two Collators for you - Catalog and TechDocs - which will index content from these two locations so that you can easily search them. Learn more in the [Collators documentation](./collators.md).
|
||||
|
||||
## Customizing Search
|
||||
|
||||
### Frontend
|
||||
|
||||
The Search Plugin web library (`@backstage/plugin-search-react`) exposes several default filter types as static properties,
|
||||
including `<SearchFilter.Select />` and `<SearchFilter.Checkbox />`. These allow
|
||||
you to provide values relevant to your Backstage instance that, when selected,
|
||||
get passed to the backend.
|
||||
|
||||
```tsx {2-5,8-11}
|
||||
<CardContent>
|
||||
<SearchFilter.Select
|
||||
name="kind"
|
||||
values={['Component', 'Template']}
|
||||
/>
|
||||
</CardContent>
|
||||
<CardContent>
|
||||
<SearchFilter.Checkbox
|
||||
name="lifecycle"
|
||||
values={['production', 'experimental']}
|
||||
/>
|
||||
</CardContent>
|
||||
```
|
||||
|
||||
If you have advanced filter needs, you can specify your own filter component
|
||||
like this (although new core filter contributions are welcome):
|
||||
|
||||
```tsx
|
||||
import { useSearch, SearchFilter } from '@backstage/plugin-search-react';
|
||||
|
||||
const MyCustomFilter = () => {
|
||||
// Note: filters contain filter data from other filter components. Be sure
|
||||
// not to clobber other filters' data!
|
||||
const { filters, setFilters } = useSearch();
|
||||
|
||||
return (/* ... */);
|
||||
};
|
||||
|
||||
// Which could be rendered like this:
|
||||
<SearchFilter component={MyCustomFilter} />
|
||||
```
|
||||
|
||||
It's good practice for search results to highlight information that was used to
|
||||
return it in the first place! The code below highlights how you might specify a
|
||||
custom result item component, using the `<CatalogSearchResultListItem />` component as
|
||||
an example:
|
||||
|
||||
```tsx {7-13}
|
||||
<SearchResult>
|
||||
{({ results }) => (
|
||||
<List>
|
||||
{results.map(result => {
|
||||
// result.type is the index type defined by the collator.
|
||||
switch (result.type) {
|
||||
case 'software-catalog':
|
||||
return (
|
||||
<CatalogSearchResultListItem
|
||||
key={result.document.location}
|
||||
result={result.document}
|
||||
highlight={result.highlight}
|
||||
/>
|
||||
);
|
||||
// ...
|
||||
}
|
||||
})}
|
||||
</List>
|
||||
)}
|
||||
</SearchResult>
|
||||
```
|
||||
|
||||
> For more advanced customization of the Search frontend, also see how to guides such as [How to implement your own Search API](./how-to-guides.md#how-to-implement-your-own-search-api) and [How to customize search results highlighting styling](./how-to-guides.md#how-to-customize-search-results-highlighting-styling)
|
||||
|
||||
### Backend
|
||||
|
||||
Backstage Search isn't a search engine itself, rather, it provides an interface
|
||||
between your Backstage instance and a
|
||||
[Search Engine](./concepts.md#search-engines) of your choice. Currently, we only
|
||||
support two engines, an in-memory search Engine called Lunr and Elasticsearch.
|
||||
See [Search Engines](./search-engines.md) documentation for more information how
|
||||
to configure these in your Backstage instance.
|
||||
|
||||
Backstage Search can be used to power search of anything! Plugins like the
|
||||
Catalog offer default [collators](./concepts.md#collators) (e.g.
|
||||
[DefaultCatalogCollator](https://github.com/backstage/backstage/blob/df12cc25aa4934a98bc42ed03c07f64a1a0a9d72/plugins/catalog-backend/src/search/DefaultCatalogCollator.ts))
|
||||
which are responsible for providing documents
|
||||
[to be indexed](./concepts.md#documents-and-indices). You can register any
|
||||
number of collators with the `IndexBuilder` like this:
|
||||
|
||||
```typescript
|
||||
const indexBuilder = new IndexBuilder({ logger: env.logger, searchEngine });
|
||||
|
||||
const every10MinutesSchedule = env.scheduler.createScheduledTaskRunner({
|
||||
frequency: { minutes: 10 },
|
||||
timeout: { minutes: 15 },
|
||||
initialDelay: { seconds: 3 },
|
||||
});
|
||||
|
||||
const everyHourSchedule = env.scheduler.createScheduledTaskRunner({
|
||||
frequency: { hours: 1 },
|
||||
timeout: { minutes: 90 },
|
||||
initialDelay: { seconds: 3 },
|
||||
});
|
||||
|
||||
indexBuilder.addCollator({
|
||||
schedule: every10MinutesSchedule,
|
||||
factory: DefaultCatalogCollatorFactory.fromConfig(env.config, {
|
||||
discovery: env.discovery,
|
||||
tokenManager: env.tokenManager,
|
||||
}),
|
||||
});
|
||||
|
||||
indexBuilder.addCollator({
|
||||
schedule: everyHourSchedule,
|
||||
factory: new MyCustomCollatorFactory(),
|
||||
});
|
||||
```
|
||||
|
||||
Backstage Search builds and maintains its index
|
||||
[on a schedule](./concepts.md#the-scheduler). You can change how often the
|
||||
indexes are rebuilt for a given type of document. You may want to do this if
|
||||
your documents are updated more or less frequently. You can do so by configuring
|
||||
a scheduled `SchedulerServiceTaskRunner` to pass into the `schedule` value, like this:
|
||||
|
||||
```typescript {3}
|
||||
const every10MinutesSchedule = env.scheduler.createScheduledTaskRunner({
|
||||
frequency: { minutes: 10 },
|
||||
timeout: { minutes: 15 },
|
||||
initialDelay: { seconds: 3 },
|
||||
});
|
||||
|
||||
indexBuilder.addCollator({
|
||||
schedule: every10MinutesSchedule,
|
||||
factory: DefaultCatalogCollatorFactory.fromConfig(env.config, {
|
||||
discovery: env.discovery,
|
||||
tokenManager: env.tokenManager,
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
:::note Note
|
||||
|
||||
if you are using the in-memory Lunr search engine, you probably want to
|
||||
implement a non-distributed `SchedulerServiceTaskRunner` like the following to ensure consistency
|
||||
if you're running multiple search backend nodes (alternatively, you can configure
|
||||
the search plugin to use a non-distributed database such as
|
||||
[SQLite](../../tutorials/configuring-plugin-databases.md#postgresql-and-sqlite-3)):
|
||||
|
||||
:::
|
||||
|
||||
```typescript
|
||||
import {
|
||||
SchedulerServiceTaskRunner,
|
||||
SchedulerServiceTaskInvocationDefinition,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
|
||||
const schedule: SchedulerServiceTaskRunner = {
|
||||
run: async (task: SchedulerServiceTaskInvocationDefinition) => {
|
||||
const startRefresh = async () => {
|
||||
while (!task.signal?.aborted) {
|
||||
try {
|
||||
await task.fn(task.signal);
|
||||
} catch {
|
||||
// ignore intentionally
|
||||
}
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 600 * 1000));
|
||||
}
|
||||
};
|
||||
startRefresh();
|
||||
},
|
||||
};
|
||||
|
||||
indexBuilder.addCollator({
|
||||
schedule,
|
||||
factory: DefaultCatalogCollatorFactory.fromConfig(env.config, {
|
||||
discovery: env.discovery,
|
||||
tokenManager: env.tokenManager,
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
> For more advanced customization of the Search backend, also see how to guides such as [How to index TechDocs documents](./how-to-guides.md#how-to-index-techdocs-documents) and [How to limit what can be searched in the Software Catalog](./how-to-guides.md#how-to-limit-what-can-be-searched-in-the-software-catalog)
|
||||
@@ -4,128 +4,60 @@ title: Getting Started with Search
|
||||
description: How to set up and install Backstage Search
|
||||
---
|
||||
|
||||
::::info
|
||||
This documentation is written for the new frontend system, which is the default
|
||||
in new Backstage apps. If your Backstage app still uses the old frontend system,
|
||||
read the [old frontend system version of this guide](./getting-started--old.md)
|
||||
instead.
|
||||
::::
|
||||
|
||||
Search functions as a plugin to Backstage, so you will need to use Backstage to
|
||||
use Search.
|
||||
|
||||
If you haven't setup Backstage already, start
|
||||
[here](../../getting-started/index.md).
|
||||
|
||||
> If you used `npx @backstage/create-app`, and you have a search page defined in
|
||||
> `packages/app/src/components/search`, skip to
|
||||
> [`Customizing Search`](#customizing-search) below.
|
||||
|
||||
## Adding Search to the Frontend
|
||||
|
||||
```bash title="From your Backstage root directory"
|
||||
yarn --cwd packages/app add @backstage/plugin-search @backstage/plugin-search-react
|
||||
```
|
||||
|
||||
Create a new `packages/app/src/components/search/SearchPage.tsx` file in your
|
||||
Backstage app with the following contents:
|
||||
Once installed, the search plugin is automatically available in your app through
|
||||
the default feature discovery. It provides a search page at `/search`, a search
|
||||
navigation item in the sidebar, and a search modal accessible from the sidebar.
|
||||
For more details and alternative installation methods, see
|
||||
[installing plugins](../../frontend-system/building-apps/05-installing-plugins.md).
|
||||
|
||||
```tsx
|
||||
import { Content, Header, Page } from '@backstage/core-components';
|
||||
import { Grid, List, Card, CardContent } from '@material-ui/core';
|
||||
import {
|
||||
SearchBar,
|
||||
SearchResult,
|
||||
DefaultResultListItem,
|
||||
SearchFilter,
|
||||
} from '@backstage/plugin-search-react';
|
||||
import { CatalogSearchResultListItem } from '@backstage/plugin-catalog';
|
||||
### Configuring the search page
|
||||
|
||||
export const searchPage = (
|
||||
<Page themeId="home">
|
||||
<Header title="Search" />
|
||||
<Content>
|
||||
<Grid container direction="row">
|
||||
<Grid item xs={12}>
|
||||
<SearchBar />
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Card>
|
||||
<CardContent>
|
||||
<SearchFilter.Select
|
||||
name="kind"
|
||||
values={['Component', 'Template']}
|
||||
/>
|
||||
</CardContent>
|
||||
<CardContent>
|
||||
<SearchFilter.Checkbox
|
||||
name="lifecycle"
|
||||
values={['experimental', 'production']}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
<Grid item xs={9}>
|
||||
<SearchResult>
|
||||
{({ results }) => (
|
||||
<List>
|
||||
{results.map(result => {
|
||||
switch (result.type) {
|
||||
case 'software-catalog':
|
||||
return (
|
||||
<CatalogSearchResultListItem
|
||||
key={result.document.location}
|
||||
result={result.document}
|
||||
highlight={result.highlight}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<DefaultResultListItem
|
||||
key={result.document.location}
|
||||
result={result.document}
|
||||
highlight={result.highlight}
|
||||
/>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</List>
|
||||
)}
|
||||
</SearchResult>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Content>
|
||||
</Page>
|
||||
);
|
||||
The search page can be configured through `app-config.yaml`. For example, to
|
||||
disable search result tracking:
|
||||
|
||||
```yaml title="app-config.yaml"
|
||||
app:
|
||||
extensions:
|
||||
- page:search:
|
||||
config:
|
||||
noTrack: true
|
||||
```
|
||||
|
||||
Bind the above Search Page to the `/search` route in your
|
||||
`packages/app/src/App.tsx` file, like this:
|
||||
### Search result list items
|
||||
|
||||
```tsx
|
||||
import { SearchPage } from '@backstage/plugin-search';
|
||||
import { searchPage } from './components/search/SearchPage';
|
||||
The search page automatically discovers and uses search result list item
|
||||
extensions provided by installed plugins. For example, the catalog plugin
|
||||
provides a `CatalogSearchResultListItem` and the TechDocs plugin provides a
|
||||
`TechDocsSearchResultListItem`. These are automatically registered when the
|
||||
respective plugins are installed.
|
||||
|
||||
const routes = (
|
||||
<FlatRoutes>
|
||||
<Route path="/search" element={<SearchPage />}>
|
||||
{searchPage}
|
||||
</Route>
|
||||
</FlatRoutes>
|
||||
);
|
||||
```
|
||||
You can also install additional search result list item extensions using the
|
||||
`SearchResultListItemBlueprint` from `@backstage/plugin-search-react/alpha`.
|
||||
|
||||
### Using the Search Modal
|
||||
### Search filters
|
||||
|
||||
In `Root.tsx`, add the `SidebarSearchModal` component:
|
||||
|
||||
```bash
|
||||
import { SidebarSearchModal } from '@backstage/plugin-search';
|
||||
|
||||
export const Root = ({ children }: PropsWithChildren<{}>) => (
|
||||
<SidebarPage>
|
||||
<Sidebar>
|
||||
<SidebarLogo />
|
||||
<SidebarSearchModal />
|
||||
<SidebarDivider />
|
||||
...
|
||||
```
|
||||
|
||||
For more information about using `Root.tsx`, please see
|
||||
[the changelog](https://github.com/backstage/backstage/blob/master/packages/create-app/CHANGELOG.md#0315).
|
||||
Similarly, search filter extensions are automatically discovered. You can add
|
||||
custom filters using the `SearchFilterBlueprint` or
|
||||
`SearchFilterResultTypeBlueprint` from `@backstage/plugin-search-react/alpha`.
|
||||
|
||||
## Adding Search to the Backend
|
||||
|
||||
@@ -165,70 +97,39 @@ The above also sets up two Collators for you - Catalog and TechDocs - which will
|
||||
|
||||
### Frontend
|
||||
|
||||
The Search Plugin web library (`@backstage/plugin-search-react`) exposes several default filter types as static properties,
|
||||
including `<SearchFilter.Select />` and `<SearchFilter.Checkbox />`. These allow
|
||||
you to provide values relevant to your Backstage instance that, when selected,
|
||||
get passed to the backend.
|
||||
The search plugin provides extension points for customizing the search
|
||||
experience through blueprints. You can add custom search result list items,
|
||||
filters, and result type filters.
|
||||
|
||||
```tsx {2-5,8-11}
|
||||
<CardContent>
|
||||
<SearchFilter.Select
|
||||
name="kind"
|
||||
values={['Component', 'Template']}
|
||||
/>
|
||||
</CardContent>
|
||||
<CardContent>
|
||||
<SearchFilter.Checkbox
|
||||
name="lifecycle"
|
||||
values={['production', 'experimental']}
|
||||
/>
|
||||
</CardContent>
|
||||
```
|
||||
|
||||
If you have advanced filter needs, you can specify your own filter component
|
||||
like this (although new core filter contributions are welcome):
|
||||
For example, to create a custom search result list item, use the
|
||||
`SearchResultListItemBlueprint` from `@backstage/plugin-search-react/alpha`:
|
||||
|
||||
```tsx
|
||||
import { useSearch, SearchFilter } from '@backstage/plugin-search-react';
|
||||
import { SearchResultListItemBlueprint } from '@backstage/plugin-search-react/alpha';
|
||||
|
||||
const MyCustomFilter = () => {
|
||||
// Note: filters contain filter data from other filter components. Be sure
|
||||
// not to clobber other filters' data!
|
||||
const { filters, setFilters } = useSearch();
|
||||
|
||||
return (/* ... */);
|
||||
};
|
||||
|
||||
// Which could be rendered like this:
|
||||
<SearchFilter component={MyCustomFilter} />
|
||||
export const MySearchResultListItem = SearchResultListItemBlueprint.make({
|
||||
name: 'my-result-item',
|
||||
params: {
|
||||
predicate: result => result.type === 'my-custom-type',
|
||||
component: async () => {
|
||||
const { MyResultItem } = await import('./components/MyResultItem');
|
||||
return MyResultItem;
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
It's good practice for search results to highlight information that was used to
|
||||
return it in the first place! The code below highlights how you might specify a
|
||||
custom result item component, using the `<CatalogSearchResultListItem />` component as
|
||||
an example:
|
||||
Install this in your app by passing it to `createApp`:
|
||||
|
||||
```tsx {7-13}
|
||||
<SearchResult>
|
||||
{({ results }) => (
|
||||
<List>
|
||||
{results.map(result => {
|
||||
// result.type is the index type defined by the collator.
|
||||
switch (result.type) {
|
||||
case 'software-catalog':
|
||||
return (
|
||||
<CatalogSearchResultListItem
|
||||
key={result.document.location}
|
||||
result={result.document}
|
||||
highlight={result.highlight}
|
||||
/>
|
||||
);
|
||||
// ...
|
||||
}
|
||||
})}
|
||||
</List>
|
||||
)}
|
||||
</SearchResult>
|
||||
```tsx title="packages/app/src/App.tsx"
|
||||
import { createApp } from '@backstage/frontend-defaults';
|
||||
import { MySearchResultListItem } from './search/MySearchResultListItem';
|
||||
|
||||
const app = createApp({
|
||||
features: [MySearchResultListItem],
|
||||
});
|
||||
|
||||
export default app.createRoot();
|
||||
```
|
||||
|
||||
> For more advanced customization of the Search frontend, also see how to guides such as [How to implement your own Search API](./how-to-guides.md#how-to-implement-your-own-search-api) and [How to customize search results highlighting styling](./how-to-guides.md#how-to-customize-search-results-highlighting-styling)
|
||||
|
||||
@@ -0,0 +1,367 @@
|
||||
---
|
||||
id: how-to-guides--old
|
||||
title: Search How-To guides (Old Frontend System)
|
||||
sidebar_label: How-To guides
|
||||
description: Search How To guides
|
||||
---
|
||||
|
||||
::::info
|
||||
This documentation is for Backstage apps that still use the old frontend
|
||||
system. If your app uses the new frontend system, read the
|
||||
[current guide](./how-to-guides.md) instead.
|
||||
::::
|
||||
|
||||
## 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 });
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
## How to customize fields in the Software Catalog or TechDocs index
|
||||
|
||||
Sometimes, you might want to have the ability to control which data passes into the search index
|
||||
in the catalog collator or customize data for a specific kind. You can easily achieve this
|
||||
by passing an `entityTransformer` callback to the `DefaultCatalogCollatorFactory`. This behavior
|
||||
is also possible for the `DefaultTechDocsCollatorFactory`. You can either simply amend the default behavior
|
||||
or even write an entirely new document (which should still follow some required basic structure).
|
||||
|
||||
> `authorization` and `location` cannot be modified via a `entityTransformer`, `location` can be modified only through `locationTemplate`.
|
||||
|
||||
```ts title="packages/backend/src/plugins/search.ts"
|
||||
const catalogEntityTransformer: CatalogCollatorEntityTransformer = (
|
||||
entity: Entity,
|
||||
) => {
|
||||
if (entity.kind === 'SomeKind') {
|
||||
return {
|
||||
// customize here output for 'SomeKind' kind
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
// and customize default output
|
||||
...defaultCatalogCollatorEntityTransformer(entity),
|
||||
text: 'my super cool text',
|
||||
};
|
||||
};
|
||||
|
||||
indexBuilder.addCollator({
|
||||
collator: DefaultCatalogCollatorFactory.fromConfig(env.config, {
|
||||
discovery: env.discovery,
|
||||
tokenManager: env.tokenManager,
|
||||
/* highlight-add-next-line */
|
||||
entityTransformer: catalogEntityTransformer,
|
||||
}),
|
||||
});
|
||||
|
||||
const techDocsEntityTransformer: TechDocsCollatorEntityTransformer = (
|
||||
entity: Entity,
|
||||
) => {
|
||||
return {
|
||||
// add more fields to the index
|
||||
tags: entity.metadata.tags,
|
||||
};
|
||||
};
|
||||
|
||||
const techDocsDocumentTransformer: TechDocsCollatorDocumentTransformer = (
|
||||
doc: MkSearchIndexDoc,
|
||||
) => {
|
||||
return {
|
||||
// add more fields to the index
|
||||
bost: doc.boost,
|
||||
};
|
||||
};
|
||||
|
||||
indexBuilder.addCollator({
|
||||
collator: DefaultTechDocsCollatorFactory.fromConfig(env.config, {
|
||||
discovery: env.discovery,
|
||||
tokenManager: env.tokenManager,
|
||||
/* highlight-add-next-line */
|
||||
entityTransformer: techDocsEntityTransformer,
|
||||
/* highlight-add-next-line */
|
||||
documentTransformer: techDocsDocumentTransformer,
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
## How to customize search results highlighting styling
|
||||
|
||||
The default highlighting styling for matched terms in search results is your
|
||||
browsers default styles for the `<mark>` HTML tag. If you want to customize
|
||||
how highlighted terms look you can follow Backstage's guide on how to
|
||||
[Customizing Your App's UI](https://backstage.io/docs/conf/user-interface)
|
||||
to create an override with your preferred styling.
|
||||
|
||||
For example, using the new MUI V4+V5 unified theming method, the following will result
|
||||
in highlighted words to be bold & underlined:
|
||||
|
||||
```typescript jsx title=packages/app/src/theme/theme.ts
|
||||
import {
|
||||
createBaseThemeOptions,
|
||||
createUnifiedTheme,
|
||||
palettes,
|
||||
UnifiedTheme,
|
||||
} from '@backstage/theme';
|
||||
|
||||
export const myLightTheme: UnifiedTheme = createUnifiedTheme({
|
||||
...createBaseThemeOptions({
|
||||
palette: palettes.light,
|
||||
}),
|
||||
defaultPageTheme: 'home',
|
||||
components: {
|
||||
/** @ts-ignore This is temporarily necessary until MUI V5 transition is completed. */
|
||||
BackstageHighlightedSearchResultText: {
|
||||
styleOverrides: {
|
||||
highlight: {
|
||||
color: 'inherit',
|
||||
backgroundColor: 'inherit',
|
||||
fontWeight: 'bold',
|
||||
textDecoration: 'underline',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```typescript jsx title= packages/app/src/App.tsx
|
||||
|
||||
const app : BackstageApp = createApp({
|
||||
...
|
||||
themes: [{
|
||||
id: 'my-light-theme',
|
||||
title: 'Light Theme',
|
||||
variant: 'light',
|
||||
icon: <LightIcon />,
|
||||
Provider: ({ children }) => (<UnifiedThemeProvider theme={myLightTheme} children={children } />)
|
||||
}]
|
||||
});
|
||||
```
|
||||
|
||||
Obviously if you wanted a dark theme, you would need to provide that as well.
|
||||
|
||||
## How to render search results using extensions
|
||||
|
||||
Extensions for search results let you customize components used to render search result items, It is possible to provide your own search result item extensions or use the ones provided by plugin packages.
|
||||
|
||||
### 1. Providing an extension in your plugin package
|
||||
|
||||
> Note: You must use the `plugin.provide()` function to make a search item renderer available. Unlike rendering a list in a standard MUI Table or similar, you cannot simply provide
|
||||
> a rendering function to the `<SearchResult />` component.
|
||||
|
||||
Using the example below, you can provide an extension to be used as a search result item:
|
||||
|
||||
```tsx title="plugins/your-plugin/src/plugin.ts"
|
||||
import { createPlugin } from '@backstage/core-plugin-api';
|
||||
import { createSearchResultListItemExtension } from '@backstage/plugin-search-react';
|
||||
|
||||
const plugin = createPlugin({ id: 'YOUR_PLUGIN_ID' });
|
||||
|
||||
export const YourSearchResultListItemExtension = plugin.provide(
|
||||
createSearchResultListItemExtension({
|
||||
name: 'YourSearchResultListItem',
|
||||
component: () =>
|
||||
import('./components').then(m => m.YourSearchResultListItem),
|
||||
}),
|
||||
);
|
||||
```
|
||||
|
||||
If your list item accept props, you can extend the `SearchResultListItemExtensionProps` with your component specific props:
|
||||
|
||||
```tsx
|
||||
export const YourSearchResultListItemExtension: (
|
||||
props: SearchResultListItemExtensionProps<YourSearchResultListItemProps>,
|
||||
) => JSX.Element | null = plugin.provide(
|
||||
createSearchResultListItemExtension({
|
||||
name: 'YourSearchResultListItem',
|
||||
component: () =>
|
||||
import('./components').then(m => m.YourSearchResultListItem),
|
||||
}),
|
||||
);
|
||||
```
|
||||
|
||||
Additionally, you can define a predicate function that receives a result and returns whether your extension should be used to render it or not:
|
||||
|
||||
```tsx title="plugins/your-plugin/src/plugin.ts"
|
||||
import { createPlugin } from '@backstage/core-plugin-api';
|
||||
import { createSearchResultListItemExtension } from '@backstage/plugin-search-react';
|
||||
|
||||
const plugin = createPlugin({ id: 'YOUR_PLUGIN_ID' });
|
||||
|
||||
export const YourSearchResultListItemExtension = plugin.provide(
|
||||
createSearchResultListItemExtension({
|
||||
name: 'YourSearchResultListItem',
|
||||
component: () =>
|
||||
import('./components').then(m => m.YourSearchResultListItem),
|
||||
// Only results matching your type will be rendered by this extension
|
||||
predicate: result => result.type === 'YOUR_RESULT_TYPE',
|
||||
}),
|
||||
);
|
||||
```
|
||||
|
||||
Remember to export your new extension via your plugin's `index.ts` so that it is available from within your app:
|
||||
|
||||
```tsx title="plugins/your-plugin/src/index.ts"
|
||||
export { YourSearchResultListItem } from './plugin.ts';
|
||||
```
|
||||
|
||||
For more details, see the [createSearchResultListItemExtension](https://backstage.io/api/stable/functions/_backstage_plugin-search-react.index.createSearchResultListItemExtension.html) API reference.
|
||||
|
||||
### 2. Custom search result extension in the SearchPage
|
||||
|
||||
Once you have exposed your item renderer via the `plugin.provide()` function, you can now override the default search item renderers and tell the `<SearchResult>` component
|
||||
which renderers to use. Note that the order of the renderers matters! The first one that matches via its predicate function will be used.
|
||||
|
||||
Here is an example of customizing your `SearchPage`:
|
||||
|
||||
```tsx title="packages/app/src/components/searchPage.tsx"
|
||||
import { Grid, Paper } from '@material-ui/core';
|
||||
import BuildIcon from '@material-ui/icons/Build';
|
||||
|
||||
import {
|
||||
Page,
|
||||
Header,
|
||||
Content,
|
||||
DocsIcon,
|
||||
CatalogIcon,
|
||||
} from '@backstage/core-components';
|
||||
import { SearchBar, SearchResult } from '@backstage/plugin-search-react';
|
||||
|
||||
// Your search result item extension
|
||||
import { YourSearchResultListItem } from '@backstage/your-plugin';
|
||||
|
||||
// Extensions provided by other plugin developers
|
||||
import { ToolSearchResultListItem } from '@backstage/plugin-explore';
|
||||
import { TechDocsSearchResultListItem } from '@backstage/plugin-techdocs';
|
||||
import { CatalogSearchResultListItem } from '@internal/plugin-catalog-customized';
|
||||
|
||||
// This example omits other components, like filter and pagination
|
||||
const SearchPage = () => (
|
||||
<Page themeId="home">
|
||||
<Header title="Search" />
|
||||
<Content>
|
||||
<Grid container direction="row">
|
||||
<Grid item xs={12}>
|
||||
<Paper>
|
||||
<SearchBar />
|
||||
</Paper>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<SearchResult>
|
||||
<YourSearchResultListItem />
|
||||
<CatalogSearchResultListItem icon={<CatalogIcon />} />
|
||||
<TechDocsSearchResultListItem icon={<DocsIcon />} />
|
||||
<ToolSearchResultListItem icon={<BuildIcon />} />
|
||||
</SearchResult>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Content>
|
||||
</Page>
|
||||
);
|
||||
|
||||
export const searchPage = <SearchPage />;
|
||||
```
|
||||
|
||||
> **Important**: A default result item extension (one that does not have a predicate) should be placed as the last child, so it can be used only when no other extensions match the result being rendered.
|
||||
> If a non-default extension is specified, the `DefaultResultListItem` component will be used.
|
||||
|
||||
### 2. Custom search result extension in the SidebarSearchModal
|
||||
|
||||
You may be using the SidebarSearchModal component. In this case, you can customize the search items in this component as follows:
|
||||
|
||||
```tsx title="packages/app/src/components/Root/Root.tsx"
|
||||
import { SidebarSearchModal } from '@backstage/plugin-search';
|
||||
...
|
||||
export const Root = ({ children }: PropsWithChildren<{}>) => {
|
||||
const styles = useStyles();
|
||||
|
||||
return <SidebarPage>
|
||||
<Sidebar>
|
||||
...
|
||||
<SidebarSearchModal resultItemComponents={[
|
||||
/* Provide a custom Extension search item renderer */
|
||||
<CustomSearchResultListItem icon={<CatalogIcon />} />,
|
||||
/* Provide an existing search item renderer */
|
||||
<TechDocsSearchResultListItem icon={<DocsIcon />} />
|
||||
]} />
|
||||
...
|
||||
</Sidebar>
|
||||
{children}
|
||||
</SidebarPage>;
|
||||
};
|
||||
```
|
||||
|
||||
### 3. Custom search result extension in a custom SearchModal
|
||||
|
||||
Assuming you have completely customized your SearchModal, here's an example that renders results with extensions:
|
||||
|
||||
```tsx title="packages/app/src/components/searchModal.tsx"
|
||||
import { DialogContent, DialogTitle, Paper } from '@material-ui/core';
|
||||
import BuildIcon from '@material-ui/icons/Build';
|
||||
|
||||
import { DocsIcon, CatalogIcon } from '@backstage/core-components';
|
||||
import { SearchBar, SearchResult } from '@backstage/plugin-search-react';
|
||||
|
||||
// Your search result item extension
|
||||
import { YourSearchResultListItem } from '@backstage/your-plugin';
|
||||
|
||||
// Extensions provided by other plugin developers
|
||||
import { ToolSearchResultListItem } from '@backstage/plugin-explore';
|
||||
import { TechDocsSearchResultListItem } from '@backstage/plugin-techdocs';
|
||||
import { CatalogSearchResultListItem } from '@internal/plugin-catalog-customized';
|
||||
|
||||
export const SearchModal = ({ toggleModal }: { toggleModal: () => void }) => (
|
||||
<>
|
||||
<DialogTitle>
|
||||
<Paper>
|
||||
<SearchBar />
|
||||
</Paper>
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<SearchResult onClick={toggleModal}>
|
||||
<CatalogSearchResultListItem icon={<CatalogIcon />} />
|
||||
<TechDocsSearchResultListItem icon={<DocsIcon />} />
|
||||
<ToolSearchResultListItem icon={<BuildIcon />} />
|
||||
{/* As a "default" extension, it does not define a predicate function,
|
||||
so it must be the last child to render results that do not match the above extensions */}
|
||||
<YourSearchResultListItem />
|
||||
</SearchResult>
|
||||
</DialogContent>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
There are other more specific search results layout components that also accept result item extensions, check their documentation: [SearchResultList](https://backstage.io/storybook/?path=/story/plugins-search-searchresultlist--with-result-item-extensions) and [SearchResultGroup](https://backstage.io/storybook/?path=/story/plugins-search-searchresultgroup--with-result-item-extensions).
|
||||
@@ -5,6 +5,13 @@ sidebar_label: How-To guides
|
||||
description: Search How To guides
|
||||
---
|
||||
|
||||
::::info
|
||||
This documentation is written for the new frontend system, which is the default
|
||||
in new Backstage apps. If your Backstage app still uses the old frontend system,
|
||||
read the [old frontend system version of this guide](./how-to-guides--old.md)
|
||||
instead.
|
||||
::::
|
||||
|
||||
## How to implement your own Search API
|
||||
|
||||
The Search plugin provides implementation of one primary API by default: the
|
||||
@@ -26,24 +33,9 @@ to do that in two steps.
|
||||
}
|
||||
```
|
||||
|
||||
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 });
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
```
|
||||
2. Override the default API extension by creating a custom API extension using
|
||||
`createApiExtension` from `@backstage/frontend-plugin-api`, and install it
|
||||
in your app. See the [Utility APIs](../../frontend-system/utility-apis/01-index.md) documentation for details on how to create and install custom API extensions.
|
||||
|
||||
## How to customize fields in the Software Catalog or TechDocs index
|
||||
|
||||
@@ -119,7 +111,7 @@ how highlighted terms look you can follow Backstage's guide on how to
|
||||
[Customizing Your App's UI](https://backstage.io/docs/conf/user-interface)
|
||||
to create an override with your preferred styling.
|
||||
|
||||
For example, using the new MUI V4+V5 unified theming method, the following will result
|
||||
For example, using the unified theming method, the following will result
|
||||
in highlighted words to be bold & underlined:
|
||||
|
||||
```typescript jsx title=packages/app/src/theme/theme.ts
|
||||
@@ -151,211 +143,59 @@ export const myLightTheme: UnifiedTheme = createUnifiedTheme({
|
||||
});
|
||||
```
|
||||
|
||||
```typescript jsx title= packages/app/src/App.tsx
|
||||
|
||||
const app : BackstageApp = createApp({
|
||||
...
|
||||
themes: [{
|
||||
id: 'my-light-theme',
|
||||
title: 'Light Theme',
|
||||
variant: 'light',
|
||||
icon: <LightIcon />,
|
||||
Provider: ({ children }) => (<UnifiedThemeProvider theme={myLightTheme} children={children } />)
|
||||
}]
|
||||
});
|
||||
```
|
||||
|
||||
Obviously if you wanted a dark theme, you would need to provide that as well.
|
||||
Custom themes are installed as extensions in the new frontend system. See the
|
||||
[theming documentation](../../frontend-system/building-apps/02-configuring-extensions.md)
|
||||
for details on how to install custom themes.
|
||||
|
||||
## How to render search results using extensions
|
||||
|
||||
Extensions for search results let you customize components used to render search result items, It is possible to provide your own search result item extensions or use the ones provided by plugin packages.
|
||||
Extensions for search results let you customize components used to render
|
||||
search result items. It is possible to provide your own search result item
|
||||
extensions or use the ones provided by plugin packages.
|
||||
|
||||
### 1. Providing an extension in your plugin package
|
||||
### Providing a search result list item extension
|
||||
|
||||
> Note: You must use the `plugin.provide()` function to make a search item renderer available. Unlike rendering a list in a standard MUI Table or similar, you cannot simply provide
|
||||
> a rendering function to the `<SearchResult />` component.
|
||||
In the new frontend system, search result list item extensions are created
|
||||
using the `SearchResultListItemBlueprint` from
|
||||
`@backstage/plugin-search-react/alpha`:
|
||||
|
||||
Using the example below, you can provide an extension to be used as a search result item:
|
||||
```tsx title="plugins/your-plugin/src/extensions.ts"
|
||||
import { SearchResultListItemBlueprint } from '@backstage/plugin-search-react/alpha';
|
||||
|
||||
```tsx title="plugins/your-plugin/src/plugin.ts"
|
||||
import { createPlugin } from '@backstage/core-plugin-api';
|
||||
import { createSearchResultListItemExtension } from '@backstage/plugin-search-react';
|
||||
|
||||
const plugin = createPlugin({ id: 'YOUR_PLUGIN_ID' });
|
||||
|
||||
export const YourSearchResultListItemExtension = plugin.provide(
|
||||
createSearchResultListItemExtension({
|
||||
name: 'YourSearchResultListItem',
|
||||
component: () =>
|
||||
import('./components').then(m => m.YourSearchResultListItem),
|
||||
}),
|
||||
);
|
||||
```
|
||||
|
||||
If your list item accept props, you can extend the `SearchResultListItemExtensionProps` with your component specific props:
|
||||
|
||||
```tsx
|
||||
export const YourSearchResultListItemExtension: (
|
||||
props: SearchResultListItemExtensionProps<YourSearchResultListItemProps>,
|
||||
) => JSX.Element | null = plugin.provide(
|
||||
createSearchResultListItemExtension({
|
||||
name: 'YourSearchResultListItem',
|
||||
component: () =>
|
||||
import('./components').then(m => m.YourSearchResultListItem),
|
||||
}),
|
||||
);
|
||||
```
|
||||
|
||||
Additionally, you can define a predicate function that receives a result and returns whether your extension should be used to render it or not:
|
||||
|
||||
```tsx title="plugins/your-plugin/src/plugin.ts"
|
||||
import { createPlugin } from '@backstage/core-plugin-api';
|
||||
import { createSearchResultListItemExtension } from '@backstage/plugin-search-react';
|
||||
|
||||
const plugin = createPlugin({ id: 'YOUR_PLUGIN_ID' });
|
||||
|
||||
export const YourSearchResultListItemExtension = plugin.provide(
|
||||
createSearchResultListItemExtension({
|
||||
name: 'YourSearchResultListItem',
|
||||
component: () =>
|
||||
import('./components').then(m => m.YourSearchResultListItem),
|
||||
// Only results matching your type will be rendered by this extension
|
||||
export const YourSearchResultListItem = SearchResultListItemBlueprint.make({
|
||||
name: 'your-result-item',
|
||||
params: {
|
||||
predicate: result => result.type === 'YOUR_RESULT_TYPE',
|
||||
}),
|
||||
);
|
||||
component: async () => {
|
||||
const { YourSearchResultListItem } = await import('./components');
|
||||
return YourSearchResultListItem;
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Remember to export your new extension via your plugin's `index.ts` so that it is available from within your app:
|
||||
The extension is then exported from your plugin's alpha entry point and
|
||||
automatically discovered when the plugin is installed.
|
||||
|
||||
```tsx title="plugins/your-plugin/src/index.ts"
|
||||
export { YourSearchResultListItem } from './plugin.ts';
|
||||
If you need to provide a search result list item extension from your app
|
||||
rather than a plugin, you can install it directly in `createApp`:
|
||||
|
||||
```tsx title="packages/app/src/App.tsx"
|
||||
import { createApp } from '@backstage/frontend-defaults';
|
||||
import { YourSearchResultListItem } from './search/YourSearchResultListItem';
|
||||
|
||||
const app = createApp({
|
||||
features: [YourSearchResultListItem],
|
||||
});
|
||||
|
||||
export default app.createRoot();
|
||||
```
|
||||
|
||||
For more details, see the [createSearchResultListItemExtension](https://backstage.io/api/stable/functions/_backstage_plugin-search-react.index.createSearchResultListItemExtension.html) API reference.
|
||||
### Search result item ordering
|
||||
|
||||
### 2. Custom search result extension in the SearchPage
|
||||
|
||||
Once you have exposed your item renderer via the `plugin.provide()` function, you can now override the default search item renderers and tell the `<SearchResult>` component
|
||||
which renderers to use. Note that the order of the renderers matters! The first one that matches via its predicate function will be used.
|
||||
|
||||
Here is an example of customizing your `SearchPage`:
|
||||
|
||||
```tsx title="packages/app/src/components/searchPage.tsx"
|
||||
import { Grid, Paper } from '@material-ui/core';
|
||||
import BuildIcon from '@material-ui/icons/Build';
|
||||
|
||||
import {
|
||||
Page,
|
||||
Header,
|
||||
Content,
|
||||
DocsIcon,
|
||||
CatalogIcon,
|
||||
} from '@backstage/core-components';
|
||||
import { SearchBar, SearchResult } from '@backstage/plugin-search-react';
|
||||
|
||||
// Your search result item extension
|
||||
import { YourSearchResultListItem } from '@backstage/your-plugin';
|
||||
|
||||
// Extensions provided by other plugin developers
|
||||
import { ToolSearchResultListItem } from '@backstage/plugin-explore';
|
||||
import { TechDocsSearchResultListItem } from '@backstage/plugin-techdocs';
|
||||
import { CatalogSearchResultListItem } from '@internal/plugin-catalog-customized';
|
||||
|
||||
// This example omits other components, like filter and pagination
|
||||
const SearchPage = () => (
|
||||
<Page themeId="home">
|
||||
<Header title="Search" />
|
||||
<Content>
|
||||
<Grid container direction="row">
|
||||
<Grid item xs={12}>
|
||||
<Paper>
|
||||
<SearchBar />
|
||||
</Paper>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<SearchResult>
|
||||
<YourSearchResultListItem />
|
||||
<CatalogSearchResultListItem icon={<CatalogIcon />} />
|
||||
<TechDocsSearchResultListItem icon={<DocsIcon />} />
|
||||
<ToolSearchResultListItem icon={<BuildIcon />} />
|
||||
</SearchResult>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Content>
|
||||
</Page>
|
||||
);
|
||||
|
||||
export const searchPage = <SearchPage />;
|
||||
```
|
||||
|
||||
> **Important**: A default result item extension (one that does not have a predicate) should be placed as the last child, so it can be used only when no other extensions match the result being rendered.
|
||||
> If a non-default extension is specified, the `DefaultResultListItem` component will be used.
|
||||
|
||||
### 2. Custom search result extension in the SidebarSearchModal
|
||||
|
||||
You may be using the SidebarSearchModal component. In this case, you can customize the search items in this component as follows:
|
||||
|
||||
```tsx title="packages/app/src/components/Root/Root.tsx"
|
||||
import { SidebarSearchModal } from '@backstage/plugin-search';
|
||||
...
|
||||
export const Root = ({ children }: PropsWithChildren<{}>) => {
|
||||
const styles = useStyles();
|
||||
|
||||
return <SidebarPage>
|
||||
<Sidebar>
|
||||
...
|
||||
<SidebarSearchModal resultItemComponents={[
|
||||
/* Provide a custom Extension search item renderer */
|
||||
<CustomSearchResultListItem icon={<CatalogIcon />} />,
|
||||
/* Provide an existing search item renderer */
|
||||
<TechDocsSearchResultListItem icon={<DocsIcon />} />
|
||||
]} />
|
||||
...
|
||||
</Sidebar>
|
||||
{children}
|
||||
</SidebarPage>;
|
||||
};
|
||||
```
|
||||
|
||||
### 3. Custom search result extension in a custom SearchModal
|
||||
|
||||
Assuming you have completely customized your SearchModal, here's an example that renders results with extensions:
|
||||
|
||||
```tsx title="packages/app/src/components/searchModal.tsx"
|
||||
import { DialogContent, DialogTitle, Paper } from '@material-ui/core';
|
||||
import BuildIcon from '@material-ui/icons/Build';
|
||||
|
||||
import { DocsIcon, CatalogIcon } from '@backstage/core-components';
|
||||
import { SearchBar, SearchResult } from '@backstage/plugin-search-react';
|
||||
|
||||
// Your search result item extension
|
||||
import { YourSearchResultListItem } from '@backstage/your-plugin';
|
||||
|
||||
// Extensions provided by other plugin developers
|
||||
import { ToolSearchResultListItem } from '@backstage/plugin-explore';
|
||||
import { TechDocsSearchResultListItem } from '@backstage/plugin-techdocs';
|
||||
import { CatalogSearchResultListItem } from '@internal/plugin-catalog-customized';
|
||||
|
||||
export const SearchModal = ({ toggleModal }: { toggleModal: () => void }) => (
|
||||
<>
|
||||
<DialogTitle>
|
||||
<Paper>
|
||||
<SearchBar />
|
||||
</Paper>
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<SearchResult onClick={toggleModal}>
|
||||
<CatalogSearchResultListItem icon={<CatalogIcon />} />
|
||||
<TechDocsSearchResultListItem icon={<DocsIcon />} />
|
||||
<ToolSearchResultListItem icon={<BuildIcon />} />
|
||||
{/* As a "default" extension, it does not define a predicate function,
|
||||
so it must be the last child to render results that do not match the above extensions */}
|
||||
<YourSearchResultListItem />
|
||||
</SearchResult>
|
||||
</DialogContent>
|
||||
</>
|
||||
);
|
||||
```
|
||||
When multiple search result list item extensions are installed, the search page
|
||||
uses them to render results based on their predicate functions. The first
|
||||
extension whose predicate matches a given result is used to render it. Extensions
|
||||
without a predicate act as fallback renderers and should be ordered last.
|
||||
|
||||
There are other more specific search results layout components that also accept result item extensions, check their documentation: [SearchResultList](https://backstage.io/storybook/?path=/story/plugins-search-searchresultlist--with-result-item-extensions) and [SearchResultGroup](https://backstage.io/storybook/?path=/story/plugins-search-searchresultgroup--with-result-item-extensions).
|
||||
|
||||
Reference in New Issue
Block a user