From fe8473b5f12848908dcb3d8052fc88b44ed947b2 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 10 Apr 2026 13:58:38 +0200 Subject: [PATCH] Fix review feedback: override warnings and search docs Add explicit deprecation warnings in override and makeWithOverrides when config.schema is used, since the merged result now passes through configSchema. Update search declarative-integration docs to use SearchResultListItemBlueprint instead of the removed extension creator. Signed-off-by: Patrik Oldsberg Made-with: Cursor --- .../search/declarative-integration.md | 139 +++++------------- .../src/wiring/createExtension.ts | 4 + .../src/wiring/createExtensionBlueprint.ts | 5 + 3 files changed, 43 insertions(+), 105 deletions(-) diff --git a/docs/features/search/declarative-integration.md b/docs/features/search/declarative-integration.md index 06d46ae86c..cea5225f2f 100644 --- a/docs/features/search/declarative-integration.md +++ b/docs/features/search/declarative-integration.md @@ -71,29 +71,25 @@ app: ### Customizations -Plugin developers can use the `createSearchResultItemExtension` factory provided by the `@backstage/plugin-search-react` for building their own custom `Search` result item extensions. +Plugin developers can use the `SearchResultListItemBlueprint` from `@backstage/plugin-search-react/alpha` 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'; -import { z } from 'zod'; +// plugins/techdocs/src/alpha.tsx +import { SearchResultListItemBlueprint } from '@backstage/plugin-search-react/alpha'; -/** @alpha */ export const TechDocsSearchResultListItemExtension = - createSearchResultListItemExtension({ - id: 'techdocs', - configSchema: { - 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 => ; + SearchResultListItemBlueprint.make({ + name: 'techdocs', + params: { + predicate: result => result.type === 'techdocs', + component: async ({ config }) => { + const { TechDocsSearchResultListItem } = await import( + './components/TechDocsSearchResultListItem' + ); + return props => ; + }, }, }); ``` @@ -106,109 +102,42 @@ When a Backstage adopter doesn't want to use the custom `TechDocs` search result # app-config.yaml app: extensions: - - plugin.search.result.item.techdocs: false # ✨ + - search-result-list-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 analytics events tracking**: +The `SearchResultListItemBlueprint` includes a built-in `noTrack` config option that can be used to **disable automatic analytics events tracking**: ```yaml # app-config.yaml app: extensions: - - plugin.search.result.item.techdocs: - config: # ✨ + - search-result-list-item:techdocs: + config: noTrack: true - lineClamp: 3 ``` -[comment]: <> (TODO: Extract this explanation to a more central place in the future) -The `createSearchResultItemExtension` function returns a Backstage's extension representation as follows: +To complete the development cycle for creating a custom search result item extension, provide the extension via the `TechDocs` plugin. You can also take a look at the [actual implementation](https://github.com/backstage/backstage/blob/master/plugins/techdocs/src/alpha/index.tsx) of a custom `TechDocs` search result item: -```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 - } +```tsx +// plugins/techdocs/src/alpha.tsx +import { createFrontendPlugin } from '@backstage/frontend-plugin-api'; +import { SearchResultListItemBlueprint } from '@backstage/plugin-search-react/alpha'; + +const TechDocsSearchResultListItemExtension = + SearchResultListItemBlueprint.make({ + name: 'techdocs', + params: { + predicate: result => result.type === 'techdocs', + component: async ({ config }) => { + const { TechDocsSearchResultListItem } = await import( + './components/TechDocsSearchResultListItem' + ); + return props => ; }, - "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 a 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; -- **[4] `inputs`**: in this case is an empty object because this extension doesn't expect inputs; -- **[5] `output`**: Object representing the artifact produced by the `TechDocs` result item extension, on the example, it is a react component reference; -- **[6] `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; -- **[7] `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 the `plugins/techdocs/alpha/index.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/index.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'; -import { z } from 'zod'; - -/** @alpha */ -export const TechDocsSearchResultListItemExtension = - createSearchResultListItemExtension({ - id: 'techdocs', - configSchema: { - 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 +export default createFrontendPlugin({ id: 'techdocs', extensions: [TechDocsSearchResultListItemExtension], }); diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.ts b/packages/frontend-plugin-api/src/wiring/createExtension.ts index d7d90079a2..a89d138e2f 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.ts @@ -689,6 +689,10 @@ export function createExtension( ); } + if (overrideOptions.config?.schema) { + warnConfigSchemaPropDeprecation(describeParentCallSite()); + } + // TODO(Rugvip): Making this a type check would be optimal, but it seems // like it's tricky to add that and still have the type // inference work correctly for the factory output. diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts index afdbb2e939..ec74c7801e 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts @@ -38,6 +38,8 @@ import { import { ExtensionDataContainer } from './types'; import { PageBlueprint } from '../blueprints/PageBlueprint'; import { FilterPredicate } from '@backstage/filter-predicates'; +import { warnConfigSchemaPropDeprecation } from '../schema/createPortableSchema'; +import { describeParentCallSite } from '../routing/describeParentCallSite'; /** * A function used to define a parameter mapping function in order to facilitate @@ -729,6 +731,9 @@ export function createExtensionBlueprint(options: any): any { }) as OverridableExtensionDefinition; }, makeWithOverrides(args: any) { + if (args.config?.schema) { + warnConfigSchemaPropDeprecation(describeParentCallSite()); + } return createExtension({ kind: options.kind, name: args.name,