Adding a guide for integrating search into existing plugins.

Signed-off-by: Eric Peterson <ericpeterson@spotify.com>
This commit is contained in:
Eric Peterson
2022-05-26 17:24:01 +02:00
parent c1511c99a5
commit 6107616ad8
2 changed files with 189 additions and 1 deletions
@@ -0,0 +1,187 @@
---
id: for-plugin-devs
title: Search for Plugin Developers
description: How to integrate search into a Backstage plugin
---
The Backstage Search Platform was designed to give plugin developers the APIs
and interfaces needed to offer search experiences within their plugins, while
abstracting away (and instead empowering application integrators to choose) the
specific underlying search technologies.
On this page, you'll find concepts, guides, and recipes for how to leverage the
Backstage Search Platform in your plugin.
## Providing data to the search platform
> A guide on how to create collators is coming soon!
## Building a search experience into your plugin
While the core Search plugin offers components and extensions that empower app
integrators to compose a global search experience, you may find that you want a
narrower search experience just within your plugin. This could be as literal as
an autocomplete-style search bar focused on documents provided by your plugin,
or as abstract as a widget that presents a list of links that relate in some
way to something else on the page.
### Concepts
Knowing these high-level concepts will help you as you craft your in-plugin
search experience.
- All search experiences must be wrapped in a `<SearchContextProvider>`, which
is provided by `@backstage/plugin-search-react`. This context keeps track
of state necessary to perform search queries and display any results. As
inputs to the query are updated (e.g. a `term` or `filter` values), the
updated query is executed and `results` are refreshed.
- The aforementioned state can be modified and/or consumed via the
`useSearch()` hook, also exported by `@backstage/plugin-search-react`.
- For more literal search experiences, reusable components are available
to import and compose into a cohesive experience in your plugin (e.g.
`<SearchBar />` or `<SearchFilter.Checkbox />`). You can see all such
components in [Backstage's storybook](https://backstage.io/storybook/?path=/story/plugins-search-searchbar--default).
### Recipes
#### Improved "404" page experience
Imagine you have a plugin that allows users to manage _widgets_. Perhaps they
can be viewed at a URL like `backstage.example.biz/widgets/{widgetName}`.
At some point, a widget is renamed, and links to that widget's page from
chat systems, wikis, or browser bookmarks become stale, resulting in errors or
404s.
What if instead of showing a broken page or the generic "looks like someone
dropped the mic" 404 page, you showed a list of possibly related widgets?
```javascript
import { Link } from '@backstage/core-components';
import { SearchResult } from '@backstage/plugin-search';
import { SearchContextProvider } from '@backstage/plugin-search-react';
export const Widget404Page = ({ widgetName }) => {
// Supplying this to <SearchContextProvider> runs a pre-filtered search with
// the given widgetName as the search term, focused on search result of type
// "widget" with no other filters.
const preFiltered = {
term: widgetName,
types: ['widget'],
filters: {},
};
return (
<SearchContextProvider initialState={preFiltered}>
{/* The <SearchResult> component allows us to iterate through results and
display them in whatever way fits best! */}
<SearchResult>
{({ results }) => (
{results.map(({ document }) => (
<Link to={document.location} key={document.location}>
{document.title}
</Link>
))}
)}
<SearchResult>
</SearchContextProvider>
);
);
```
Not all search experiences require user input! As you can see, it's possible to
leverage the Backstage Search Platform's frontend framework without necessarily
giving users input controls.
#### Simple search page
Of course, it's also possible to provide a more fully featured search
experience in your plugin. The simplest way is to leverage reusable components
provided by the `@backstage/plugin-search` package, like this:
```javascript
import { useProfile } from '@internal/api';
import {
Content,
ContentHeader,
PageWithHeader,
} from '@backstage/core-components';
import { SearchBar, SearchResult } from '@backstage/plugin-search';
import { SearchContextProvider } from '@backstage/plugin-search-react';
export const ManageMyWidgets = () => {
const { primaryTeam } = useProfile();
// In this example, note how we are pre-filtering results down to a specific
// owner field value (the currently logged-in user's team), but allowing the
// search term to be controlled by the user via the <SearchBar /> component.
const preFiltered = {
types: ['widget'],
term: '',
filters: {
owner: primaryTeam,
},
};
return (
<PageWithHeader title="Widgets Home">
<Content>
<ContentHeader title="All your Widgets and More" />
<SearchContextProvider initialState={preFiltered}>
<SearchBar />
<SearchResult>
{/* Render results here, just like above */}
</SearchResult>
</SearchContextProvider>
</Content>
</PageWithHeader>
);
};
```
#### Custom search control surfaces
If the reusable search components provided by `@backstage/plugin-search` aren't
adequate, no problem! There's an API in place that you can use to author your
own components to control the various parts of the search context.
```javascript
import { useSearch } from '@backstage/plugin-search-react';
import ChipInput from 'material-ui-chip-input';
export const CustomChipFilter = ({ name }) => {
const { filters, setFilters } = useSearch();
const chipValues = filters[name] || [];
// When a chip value is changed, update the filters value by calling the
// setFilters function from the search context.
const handleChipChange = (chip, index) => {
// There may be filters set for other fields. Be sure to maintain them.
setFilters(prevState => {
const { [name]: filter = [], ...others } = prevState;
if (index === undefined) {
filter.push(chip);
} else {
filter.splice(index, 1);
}
return { ...others, [name]: filter };
});
};
return (
<ChipInput
value={chipValues}
onAdd={handleChipChange}
onDelete={handleChipChange}
/>
);
};
```
Check out the [SearchContextValue type](https://github.com/backstage/backstage/blob/master/plugins/search-react/src/context/SearchContext.tsx)
for more details on what methods and values are available for manipulating and
reading the search context.
If you produce something generic and reusable, consider contributing your
component upstream so that all users of the Backstage Search Platform can
benefit. Issues and pull requests welcome.
+2 -1
View File
@@ -99,7 +99,8 @@
"features/search/concepts",
"features/search/architecture",
"features/search/search-engines",
"features/search/how-to-guides"
"features/search/how-to-guides",
"features/search/for-plugin-devs"
]
},
{