From 165c5edf04dd2cf0654ebff74e85d1661fff6bff Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Mon, 2 Oct 2023 10:13:37 +0200 Subject: [PATCH] docs(search): draft search extension docs page Signed-off-by: Camila Belo --- .../search/search-extensions-example.svg | 4 + .../search/declarative-integration.md | 229 ++++++++++++++++++ 2 files changed, 233 insertions(+) create mode 100644 docs/assets/search/search-extensions-example.svg create mode 100644 docs/features/search/declarative-integration.md diff --git a/docs/assets/search/search-extensions-example.svg b/docs/assets/search/search-extensions-example.svg new file mode 100644 index 0000000000..19b208009f --- /dev/null +++ b/docs/assets/search/search-extensions-example.svg @@ -0,0 +1,4 @@ + + + +
Core Routes
EXTENSION
Core Routes...

Routes

Routes
Attachment
Point
Attachment...
Route Artifact
Route Artifact
PATH
+
ELEMENT
PATH...
Input
Input
COMPONENT
COMPONENT
Item Artifact
Item Artifact

Search Page
EXTENSION

Search Page...

Items

Items
Attachment
Point
Attachment...
Output
Output
Input
Input

Search Result Item
EXTENSION

Search Result Item...


Output
Output
id: plugin.search.page
id: plugin.search.page
at: plugin.search.page/items
at: plugin.search.page/items
id: core.router
id: core.router
id: plugin.search.result.item
id: plugin.search.result.i...
at: core.router/routes
at: core.router/routes
Target Extension
Identification
Target Extension...
Target Extension
Input Attachment Point
Target Extension...
Text is not SVG - cannot display
\ No newline at end of file diff --git a/docs/features/search/declarative-integration.md b/docs/features/search/declarative-integration.md new file mode 100644 index 0000000000..e68150a098 --- /dev/null +++ b/docs/features/search/declarative-integration.md @@ -0,0 +1,229 @@ +# Declarative Integrated Search Plugin + +> **Disclaimer:** +> Declarative integration is in an experimental stage and is not recommended for production. + +This is a guide for experimenting with `Search` in a declarative integrated Backstage front-end application. + +## Main Concepts + +Using declarative integration, you can customize your Backstage instance without writing code, see this [RFC](https://github.com/backstage/backstage/issues/18372) for more information. + +In this new system architecture, everything that extends the core Backstage features is called a extension, so a extension could be since an api to a page component, which means all plugins can provide are extensions. + +Extensions produces outputs artifacts and these artifacts are inputs consumed by other extensions: + +![search extensions example](../../assets/search/search-extensions-example.svg) + +In the image above, a `SearchResultItem` extension outputs a component and this component is injected as input to the `SearchPage` "items" attachment point. The `SearchPage` in turn outputs a route path and element which are inputs attached to the `CoreRoutes` extension. Finally, the `CoreRoutes` renders the page element when the location matches the search path. + +The basic concepts briefly mentioned are crucial to understanding how the declarative version of the `Search` plugin works. + +## Search Plugin + +The search plugin is a collection of extensions that implement the search feature in Backstage. + +### Installation + +Only one step is required to start using the `Search` plugin within declarative integration, so all you have to do is to install the `@backstage/plugin-catalog` and `@backstage/plugin-search` packages, (e.g., [app-next](https://github.com/backstage/backstage/tree/master/packages/app-next)): + +```sh +yarn add @backstage/plugin-catalog @backstage/plugin-search +``` + +The `Search` plugin depends on the `Catalog API`, that's is the reason we have to install the ` @backstage/plugin-catalog` package too. + +### Extensions + +The `Search` plugin provides the following [extensions preset](https://github.com/backstage/backstage/blob/3f4a44aef39bd8dbf5098e60b6fdf66fd754c6d9/plugins/search/src/alpha.tsx#L246): + +- **SearchApi**: Outputs a concrete implementation for the `Search API` that is attached as an input to the `Core` apis holder; +- **SearchPage**: Outputs a component that represents the advanced `Search` page interface, this extension expects `Search` result items components as inputs to use them for rendering results in a custom way; +- **SearchNavItem**: It is an extension that outputs a data that represents a `Search` item in the main application sidebar, in other words, it inputs a sidebar item to the `Core` nav extension. + +### Configurations + +The `Search` extensions are configurable via `app-config.yaml` file in the `app.extensions` field using the extension id as the configuration key: + +_Example disabling the search page extension_ + +```yaml +# app-config.yaml +app: + experimental: + packages: 'all' # enable packages auto discovery + extensions: + - plugin.search.page: false # ✨ +``` + +_Example setting the search sidebar item label_ + +```yaml +# app-config.yaml +app: + experimental: + packages: 'all' # enable packages auto discovery + extensions: + - plugin.search.nav.index: # ✨ + config: + label: 'Search Page' +``` + +> **Known limitations:** +> It is currently not possible to open modals in sidebar items and also configure a different icon via configuration file, but it is already on the maintainers' radar. + +### Customizations + +Plugin developers can use the `createSearchResultItemExtension` factory provided by the `@backstage/plugin-search-react` for building their own custom `Search` result item extensions. + +_Example creating a custom `TechDocsSearchResultItemExtension`_ + +```tsx +// plugins/techdocs/alpha.tsx +import { createSearchResultListItemExtension } from '@backstage/plugin-search-react/alpha'; + +/** @alpha */ +export const TechDocsSearchResultListItemExtension = + createSearchResultListItemExtension({ + id: 'techdocs', + configSchema: createSchemaFromZod(z => + z.object({ + noTrack: z.boolean().default(false), + lineClamp: z.number().default(5), + }), + ), + predicate: result => result.type === 'techdocs', + component: async ({ config }) => { + const { TechDocsSearchResultListItem } = await import( + './components/TechDocsSearchResultListItem' + ); + return props => ; + }, + }); +``` + +In snippet above, a plugin developer is providing a custom component for rendering search results of type "techdocs". The custom result item extension will be enabled by default once the `@backstage/plugin-techdocs` package is installed, that means adopters don't have to enable the extension manually via configuration file. + +When a Backstage adopter doesn't want to use the custom `TechDocs` search result item after installing the `TechDocs` plugin, they could disable it via Backstage configuration file: + +```yaml +# app-config.yaml +app: + experimental: + packages: 'all' + extensions: + - plugin.search.result.item.techdocs: false # ✨ +``` + +Because a configuration schema was provided to the extension factory, Backstage adopters will be able to customize `TechDocs` search results **line clamp** that defaults to 3 and also **disable automatic analytic events tracking**: + +```yaml +# app-config.yaml +app: + experimental: + packages: 'all' # enable packages auto discovery + extensions: + - plugin.search.result.item.techdocs: + config: # ✨ + noTrack: true + lineClamp: 3 +``` + +The `createSearchResultItemExtension` function return an Backstage's extension representation as follows: + +```ts +{ + "$$type": "@backstage/Extension", // [1] + "id": "plugin.search.result.item.techdocs", // [2] + "at": "plugin.search.page/items", // [3] + "inputs": {} // [4️] + "output": { // [5️] + "item": { + "$$type": "@backstage/ExtensionDataRef", + "id": "plugin.search.result.item.data", + "config": {} + } + }, + "configSchema": { // [6️] + "schema": { + "type": "object", + "properties": { + "noTrack": { + "type": "boolean", + "default": false + }, + "lineClamp": { + "type": "number", + "default": 5 + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + } + }, + "disabled": false, // [7️] +} +``` + +In this object, you can see exactly what will happen once the custom extension is installed: + +- **[1] $$type**: declares that the object represents an extension; +- **[2] id**: Is an unique identification for the extension, the `plugin.search.result.item.techdocs` is the key used to configure the extension in the `app-config.yaml` file; +- **[3] at**: It represents the extension attachment point, so the value `plugin.search.page/items` says that the `TechDocs`'s search result item output will be injected as input on the "items" attachment expected by the search page extension; +- configSchema: represents the `TechDocs` search result item configuration definition, this is the same schema that adopters will use for customizing the extension via `app-config.yaml` file; +- **[4] inputs**: in this case is an empty object because this extension doesn't expect inputs; +- **[5] output**: Is an abject that represents the artifact produced by the TechDocs result item extension, on the example, it is a react component reference; +- **[6] disable**: Says that the result item extension will be enable by default when the `TechDocs` plugin is installed in the app. + +To complete the development cycle for creating a custom search result item extension, we should provide the extension via `TechDocs` plugin: + +```tsx +// plugins/techdocs/alpha.tsx +import { createPlugin } from "@backstage/frontend-plugin-api"; + +// plugins should be always exported as default +export default createPlugin({ + id: 'techdocs' + extensions: [TechDocsSearchResultItemExtension] +}) +``` + +Here is `plugins/techdocs/alpha.tsx` final version, and you can also take a look at the [actual implementation](https://github.com/backstage/backstage/blob/master/plugins/techdocs/src/alpha.tsx) of a custom `TechDocs` search result item: + +```tsx +// plugins/techdocs/alpha.tsx +import { createPlugin } from '@backstage/frontend-plugin-api'; +import { createSearchResultListItemExtension } from '@backstage/plugin-search-react/alpha'; + +/** @alpha */ +export const TechDocsSearchResultListItemExtension = + createSearchResultListItemExtension({ + id: 'techdocs', + configSchema: createSchemaFromZod(z => + z.object({ + noTrack: z.boolean().default(false), + lineClamp: z.number().default(5), + }), + ), + predicate: result => result.type === 'techdocs', + component: async ({ config }) => { + const { TechDocsSearchResultListItem } = await import( + './components/TechDocsSearchResultListItem' + ); + return props => ; + }, + }); + +/** @alpha */ +export default createPlugin({ + // plugins should be always exported as default + id: 'techdocs', + extensions: [TechDocsSearchResultListItemExtension], +}); +``` + +### Future enhancement opportunities + +Backstage maintainers are currently working on the extension replacement feature, and with this release, adopters will also be able to replace extensions provided by plugins, so stay tuned for future updates to this documentation. + +The first version of the `SearchPage` extension makes room for the `Search` plugin maintainers to convert filters into extensions as well in the future, if you also would like to collaborate with them on this idea, don't hesitate to open an issue and submit a pull request, your contribution is more than welcome!