From e1f22f2d1576550d1380496c6449395871eb0c36 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 9 Feb 2026 23:02:34 +0100 Subject: [PATCH] plugin icons and titles Signed-off-by: Patrik Oldsberg --- .../architecture/15-plugins.md | 26 +++ .../03-common-extension-blueprints.md | 2 +- .../building-plugins/04-built-in-data-refs.md | 8 + packages/app-example-plugin/report.api.md | 51 ++++- .../src/wiring/InternalFrontendPlugin.ts | 3 + packages/frontend-plugin-api/report.api.md | 129 ++++++++++- .../src/blueprints/PageBlueprint.tsx | 33 ++- .../src/components/PageLayout.tsx | 10 +- .../src/wiring/coreExtensionData.ts | 2 + .../src/wiring/createFrontendPlugin.ts | 21 ++ plugins/api-docs/report-alpha.api.md | 54 ++++- plugins/app-visualizer/report.api.md | 207 +++++++++++++++++- plugins/app-visualizer/src/plugin.tsx | 24 +- plugins/app/report.api.md | 56 +++++ plugins/app/src/extensions/components.tsx | 4 +- plugins/auth/report.api.md | 51 ++++- plugins/catalog-graph/report-alpha.api.md | 51 ++++- plugins/catalog-import/report-alpha.api.md | 51 ++++- plugins/catalog-react/report-alpha.api.md | 2 +- .../report-alpha.api.md | 52 ++++- plugins/catalog/report-alpha.api.md | 92 +++++++- plugins/devtools-react/report.api.md | 2 +- plugins/devtools/report-alpha.api.md | 45 +++- plugins/home/report-alpha.api.md | 45 +++- plugins/kubernetes/report-alpha.api.md | 53 ++++- plugins/mui-to-bui/report.api.md | 51 ++++- plugins/notifications/report-alpha.api.md | 51 ++++- plugins/scaffolder/report-alpha.api.md | 45 +++- plugins/search/report-alpha.api.md | 90 +++++++- plugins/techdocs/report-alpha.api.md | 95 +++++++- plugins/user-settings/report-alpha.api.md | 45 +++- 31 files changed, 1362 insertions(+), 89 deletions(-) diff --git a/docs/frontend-system/architecture/15-plugins.md b/docs/frontend-system/architecture/15-plugins.md index 0918961c73..600dd3b4bd 100644 --- a/docs/frontend-system/architecture/15-plugins.md +++ b/docs/frontend-system/architecture/15-plugins.md @@ -26,6 +26,8 @@ const myPage = PageBlueprint.make({ export default createFrontendPlugin({ pluginId: 'my-plugin', + title: 'My Plugin', + icon: MyPluginIcon, extensions: [myPage], }); ``` @@ -36,6 +38,30 @@ Each plugin needs an ID, which is used to uniquely identify the plugin within an The plugin ID should generally be part of the of the package name and use kebab-case. See both the [frontend naming patterns section](./50-naming-patterns.md), as well as the [package metadata section](../../tooling/package-metadata.md#name) for more information. +### `title` option + +The display title of the plugin, used in page headers and navigation. Falls back to the plugin ID if not provided. + +```tsx +export default createFrontendPlugin({ + pluginId: 'my-plugin', + title: 'My Plugin', + extensions: [...], +}); +``` + +### `icon` option + +The display icon of the plugin, used in page headers and navigation. The type is `IconComponent` from `@backstage/frontend-plugin-api`, which is a React component that accepts an optional `fontSize` prop. + +```tsx +export default createFrontendPlugin({ + pluginId: 'my-plugin', + icon: MyPluginIcon, + extensions: [...], +}); +``` + ### `extensions` option These are the [extensions](./20-extensions.md) that the plugin provides to the app. Note that you should not export any of these extensions separately from the plugin package, as they can already by accessed via the `getExtension` method of the plugin instance using the extension ID. diff --git a/docs/frontend-system/building-plugins/03-common-extension-blueprints.md b/docs/frontend-system/building-plugins/03-common-extension-blueprints.md index 3bb613848f..876f035bfd 100644 --- a/docs/frontend-system/building-plugins/03-common-extension-blueprints.md +++ b/docs/frontend-system/building-plugins/03-common-extension-blueprints.md @@ -21,7 +21,7 @@ Navigation item extensions are used to provide menu items that link to different ### Page - [Reference](https://backstage.io/api/stable/variables/_backstage_frontend-plugin-api.PageBlueprint.html) -Page extensions provide content for a particular route in the app. By default pages are attached to the app routes extensions, which renders the root routes. +Page extensions provide content for a particular route in the app. By default pages are attached to the app routes extensions, which renders the root routes. Pages automatically inherit the plugin's `title` and `icon` as defaults, which can be overridden per-page via `PageBlueprint` params. ## Extension blueprints in `@backstage/frontend-plugin-api/alpha` diff --git a/docs/frontend-system/building-plugins/04-built-in-data-refs.md b/docs/frontend-system/building-plugins/04-built-in-data-refs.md index c01520d7bd..1cd30f6bc6 100644 --- a/docs/frontend-system/building-plugins/04-built-in-data-refs.md +++ b/docs/frontend-system/building-plugins/04-built-in-data-refs.md @@ -42,6 +42,14 @@ const examplePage = createExtension({ The `title` data reference can be used for defining the extension input/output of string titles. +### `icon` + +| id | type | +| :---------: | :-------------: | +| `core.icon` | `IconComponent` | + +The `icon` data reference can be used for defining the extension input/output of icon components. The type is `IconComponent` from `@backstage/frontend-plugin-api`. + ### `routePath` | id | type | diff --git a/packages/app-example-plugin/report.api.md b/packages/app-example-plugin/report.api.md index 028795ed72..6bc2228170 100644 --- a/packages/app-example-plugin/report.api.md +++ b/packages/app-example-plugin/report.api.md @@ -4,7 +4,10 @@ ```ts import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; +import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; +import { ExtensionInput } from '@backstage/frontend-plugin-api'; +import { IconComponent } from '@backstage/frontend-plugin-api'; import { JSX as JSX_2 } from 'react'; import { JSX as JSX_3 } from 'react/jsx-runtime'; import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api'; @@ -21,25 +24,67 @@ const examplePlugin: OverridableFrontendPlugin< name: undefined; config: { path: string | undefined; + title: string | undefined; }; configInput: { + title?: string | undefined; path?: string | undefined; }; output: | ExtensionDataRef - | ExtensionDataRef | ExtensionDataRef< RouteRef, 'core.routing.ref', { optional: true; } + > + | ExtensionDataRef + | ExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + > + | ExtensionDataRef< + IconComponent, + 'core.icon', + { + optional: true; + } >; - inputs: {}; + inputs: { + pages: ExtensionInput< + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + >, + { + singleton: false; + optional: false; + internal: false; + } + >; + }; params: { defaultPath?: [Error: `Use the 'path' param instead`]; path: string; - loader: () => Promise; + title?: string; + icon?: IconComponent; + loader?: () => Promise; routeRef?: RouteRef; }; }>; diff --git a/packages/frontend-internal/src/wiring/InternalFrontendPlugin.ts b/packages/frontend-internal/src/wiring/InternalFrontendPlugin.ts index 3e964af5ca..923a6f1e5a 100644 --- a/packages/frontend-internal/src/wiring/InternalFrontendPlugin.ts +++ b/packages/frontend-internal/src/wiring/InternalFrontendPlugin.ts @@ -17,6 +17,7 @@ import { Extension, FeatureFlagConfig, + IconComponent, OverridableFrontendPlugin, } from '@backstage/frontend-plugin-api'; import { JsonObject } from '@backstage/types'; @@ -26,6 +27,8 @@ export const OpaqueFrontendPlugin = OpaqueType.create<{ public: OverridableFrontendPlugin; versions: { readonly version: 'v1'; + readonly title?: string; + readonly icon?: IconComponent; readonly extensions: Extension[]; readonly featureFlags: FeatureFlagConfig[]; readonly infoOptions?: { diff --git a/packages/frontend-plugin-api/report.api.md b/packages/frontend-plugin-api/report.api.md index 89fc72992a..571ce51c86 100644 --- a/packages/frontend-plugin-api/report.api.md +++ b/packages/frontend-plugin-api/report.api.md @@ -13,6 +13,7 @@ import { ExpandRecursive } from '@backstage/types'; import { ExtensionBlueprint as ExtensionBlueprint_2 } from '@backstage/frontend-plugin-api'; import { ExtensionBlueprintParams as ExtensionBlueprintParams_2 } from '@backstage/frontend-plugin-api'; import { ExtensionDataRef as ExtensionDataRef_2 } from '@backstage/frontend-plugin-api'; +import { ExtensionInput as ExtensionInput_2 } from '@backstage/frontend-plugin-api'; import { JsonObject } from '@backstage/types'; import { JsonValue } from '@backstage/types'; import { JSX as JSX_2 } from 'react/jsx-runtime'; @@ -388,6 +389,7 @@ export interface ConfigurableExtensionDataRef< // @public (undocumented) export const coreExtensionData: { title: ConfigurableExtensionDataRef_2; + icon: ConfigurableExtensionDataRef_2; reactElement: ConfigurableExtensionDataRef_2< JSX_3.Element, 'core.reactElement', @@ -1367,12 +1369,14 @@ export interface FrontendPlugin< readonly $$type: '@backstage/FrontendPlugin'; // (undocumented) readonly externalRoutes: TExternalRoutes; + readonly icon?: IconComponent; // @deprecated readonly id: string; info(): Promise; readonly pluginId: string; // (undocumented) readonly routes: TRoutes; + readonly title?: string; } // @public @@ -1420,6 +1424,19 @@ export const googleAuthApiRef: ApiRef< SessionApi >; +// @public +export const HeaderActionBlueprint: ExtensionBlueprint_2<{ + kind: 'header-action'; + params: { + loader: () => Promise; + }; + output: ExtensionDataRef_2; + inputs: {}; + config: {}; + configInput: {}; + dataRefs: never; +}>; + // @public export type IconComponent = ComponentType<{ fontSize?: 'medium' | 'large' | 'small' | 'inherit'; @@ -1710,29 +1727,101 @@ export const PageBlueprint: ExtensionBlueprint_2<{ params: { defaultPath?: [Error: `Use the 'path' param instead`]; path: string; - loader: () => Promise; + title?: string; + icon?: IconComponent; + loader?: () => Promise; routeRef?: RouteRef; }; output: | ExtensionDataRef_2 - | ExtensionDataRef_2 | ExtensionDataRef_2< RouteRef, 'core.routing.ref', { optional: true; } + > + | ExtensionDataRef_2 + | ExtensionDataRef_2< + string, + 'core.title', + { + optional: true; + } + > + | ExtensionDataRef_2< + IconComponent, + 'core.icon', + { + optional: true; + } >; - inputs: {}; + inputs: { + pages: ExtensionInput_2< + | ConfigurableExtensionDataRef_2 + | ConfigurableExtensionDataRef_2 + | ConfigurableExtensionDataRef_2< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + > + | ConfigurableExtensionDataRef_2< + string, + 'core.title', + { + optional: true; + } + >, + { + singleton: false; + optional: false; + internal: false; + } + >; + }; config: { path: string | undefined; + title: string | undefined; }; configInput: { + title?: string | undefined; path?: string | undefined; }; dataRefs: never; }>; +// @public +export const PageLayout: { + (props: PageLayoutProps): JSX.Element | null; + ref: SwappableComponentRef_2; +}; + +// @public +export interface PageLayoutProps { + // (undocumented) + children?: ReactNode; + // (undocumented) + icon?: IconComponent; + // (undocumented) + tabs?: PageTab[]; + // (undocumented) + title?: string; +} + +// @public +export interface PageTab { + // (undocumented) + href: string; + // (undocumented) + id: string; + // (undocumented) + label: string; + // (undocumented) + matchStrategy?: 'prefix' | 'exact'; +} + // @public export type PendingOAuthRequest = { provider: AuthProviderInfo; @@ -1757,12 +1846,14 @@ export interface PluginOptions< externalRoutes?: TExternalRoutes; // (undocumented) featureFlags?: FeatureFlagConfig[]; + icon?: IconComponent; // (undocumented) info?: FrontendPluginInfoOptions; // (undocumented) pluginId: TId; // (undocumented) routes?: TRoutes; + title?: string; } // @public (undocumented) @@ -1898,6 +1989,38 @@ export type StorageValueSnapshot = value: TValue; }; +// @public +export const SubPageBlueprint: ExtensionBlueprint_2<{ + kind: 'sub-page'; + params: { + path: string; + title: string; + loader: () => Promise; + routeRef?: RouteRef; + }; + output: + | ExtensionDataRef_2 + | ExtensionDataRef_2< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + > + | ExtensionDataRef_2 + | ExtensionDataRef_2; + inputs: {}; + config: { + path: string | undefined; + title: string | undefined; + }; + configInput: { + title?: string | undefined; + path?: string | undefined; + }; + dataRefs: never; +}>; + // @public export interface SubRouteRef< TParams extends AnyRouteRefParams = AnyRouteRefParams, diff --git a/packages/frontend-plugin-api/src/blueprints/PageBlueprint.tsx b/packages/frontend-plugin-api/src/blueprints/PageBlueprint.tsx index 1721f4172d..c3e4921af7 100644 --- a/packages/frontend-plugin-api/src/blueprints/PageBlueprint.tsx +++ b/packages/frontend-plugin-api/src/blueprints/PageBlueprint.tsx @@ -15,6 +15,7 @@ */ import { Routes, Route, Navigate } from 'react-router-dom'; +import { IconComponent } from '../icons/types'; import { RouteRef } from '../routing'; import { coreExtensionData, @@ -24,7 +25,7 @@ import { import { ExtensionBoundary, PageLayout, PageTab } from '../components'; /** - * Createx extensions that are routable React page components. + * Creates extensions that are routable React page components. * * @public */ @@ -44,6 +45,7 @@ export const PageBlueprint = createExtensionBlueprint({ coreExtensionData.reactElement, coreExtensionData.routeRef.optional(), coreExtensionData.title.optional(), + coreExtensionData.icon.optional(), ], config: { schema: { @@ -54,30 +56,35 @@ export const PageBlueprint = createExtensionBlueprint({ *factory( params: { /** - * @deprecated Use the `path' param instead. + * @deprecated Use the `path` param instead. */ defaultPath?: [Error: `Use the 'path' param instead`]; path: string; title?: string; + icon?: IconComponent; loader?: () => Promise; routeRef?: RouteRef; }, { config, node, inputs }, ) { - const title = config.title ?? params.title ?? node.spec.plugin.pluginId; + const title = + config.title ?? + params.title ?? + node.spec.plugin.title ?? + node.spec.plugin.pluginId; + const icon = params.icon ?? node.spec.plugin.icon; yield coreExtensionData.routePath(config.path ?? params.path); if (params.loader) { - // Simple page with loader - render header + content - const loader = params.loader; // Capture for closure + const loader = params.loader; const PageContent = () => ( - + {ExtensionBoundary.lazy(node, loader)} ); yield coreExtensionData.reactElement(); } else if (inputs.pages.length > 0) { - // Parent page with sub-pages - render Header with tabs and Routes for sub-pages + // Parent page with sub-pages - render header with tabs const tabs: PageTab[] = inputs.pages.map(page => { const path = page.get(coreExtensionData.routePath); const tabTitle = page.get(coreExtensionData.title); @@ -90,13 +97,11 @@ export const PageBlueprint = createExtensionBlueprint({ }); const PageContent = () => { - // Get first sub-page path for default navigation const firstPagePath = inputs.pages[0]?.get(coreExtensionData.routePath); return ( - + - {/* Index route redirects to first sub-page */} {firstPagePath && ( ); } else { - // Parent page without loader or sub-pages - render just header - yield coreExtensionData.reactElement(); + yield coreExtensionData.reactElement( + , + ); } if (params.routeRef) { yield coreExtensionData.routeRef(params.routeRef); @@ -126,5 +132,8 @@ export const PageBlueprint = createExtensionBlueprint({ if (title) { yield coreExtensionData.title(title); } + if (icon) { + yield coreExtensionData.icon(icon); + } }, }); diff --git a/packages/frontend-plugin-api/src/components/PageLayout.tsx b/packages/frontend-plugin-api/src/components/PageLayout.tsx index ae0399afc9..f5b2082c90 100644 --- a/packages/frontend-plugin-api/src/components/PageLayout.tsx +++ b/packages/frontend-plugin-api/src/components/PageLayout.tsx @@ -15,6 +15,7 @@ */ import { ReactNode } from 'react'; +import { IconComponent } from '../icons/types'; import { createSwappableComponent } from './createSwappableComponent'; /** @@ -34,6 +35,7 @@ export interface PageTab { */ export interface PageLayoutProps { title?: string; + icon?: IconComponent; tabs?: PageTab[]; children?: ReactNode; } @@ -42,7 +44,7 @@ export interface PageLayoutProps { * Default implementation of PageLayout using plain HTML elements */ function DefaultPageLayout(props: PageLayoutProps): JSX.Element { - const { title, tabs, children } = props; + const { title, icon: Icon, tabs, children } = props; return (
+ {Icon && } {title}
)} @@ -116,7 +122,7 @@ function DefaultPageLayout(props: PageLayoutProps): JSX.Element { /** * Swappable component for laying out page content with header and navigation. * The default implementation uses plain HTML elements. - * Apps can override this with a custom implementation (e.g., using @backstage/ui). + * Apps can override this with a custom implementation (e.g., using \@backstage/ui). * * @public */ diff --git a/packages/frontend-plugin-api/src/wiring/coreExtensionData.ts b/packages/frontend-plugin-api/src/wiring/coreExtensionData.ts index b778539101..6d0ca3c471 100644 --- a/packages/frontend-plugin-api/src/wiring/coreExtensionData.ts +++ b/packages/frontend-plugin-api/src/wiring/coreExtensionData.ts @@ -15,12 +15,14 @@ */ import { JSX } from 'react'; +import { IconComponent } from '../icons/types'; import { RouteRef } from '../routing/RouteRef'; import { createExtensionDataRef } from './createExtensionDataRef'; /** @public */ export const coreExtensionData = { title: createExtensionDataRef().with({ id: 'core.title' }), + icon: createExtensionDataRef().with({ id: 'core.icon' }), reactElement: createExtensionDataRef().with({ id: 'core.reactElement', }), diff --git a/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.ts b/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.ts index 62fd1e88ff..8fa32f6ccd 100644 --- a/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.ts +++ b/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.ts @@ -29,6 +29,7 @@ import { import { FeatureFlagConfig } from './types'; import { MakeSortedExtensionsMap } from './MakeSortedExtensionsMap'; import { JsonObject } from '@backstage/types'; +import { IconComponent } from '../icons/types'; import { RouteRef, SubRouteRef, ExternalRouteRef } from '../routing'; import { ID_PATTERN } from './constants'; @@ -141,6 +142,15 @@ export interface FrontendPlugin< * @deprecated Use `pluginId` instead. */ readonly id: string; + /** + * The display title of the plugin, used in page headers and navigation. + * Falls back to the plugin ID if not provided. + */ + readonly title?: string; + /** + * The display icon of the plugin, used in page headers and navigation. + */ + readonly icon?: IconComponent; readonly routes: TRoutes; readonly externalRoutes: TExternalRoutes; @@ -158,6 +168,15 @@ export interface PluginOptions< TExtensions extends readonly ExtensionDefinition[], > { pluginId: TId; + /** + * The display title of the plugin, used in page headers and navigation. + * Falls back to the plugin ID if not provided. + */ + title?: string; + /** + * The display icon of the plugin, used in page headers and navigation. + */ + icon?: IconComponent; routes?: TRoutes; externalRoutes?: TExternalRoutes; extensions?: TExtensions; @@ -250,6 +269,8 @@ export function createFrontendPlugin< return OpaqueFrontendPlugin.createInstance('v1', { pluginId, id: pluginId, + title: options.title, + icon: options.icon, routes: options.routes ?? ({} as TRoutes), externalRoutes: options.externalRoutes ?? ({} as TExternalRoutes), featureFlags: options.featureFlags ?? [], diff --git a/plugins/api-docs/report-alpha.api.md b/plugins/api-docs/report-alpha.api.md index 3865a53d16..0273ad21da 100644 --- a/plugins/api-docs/report-alpha.api.md +++ b/plugins/api-docs/report-alpha.api.md @@ -6,11 +6,13 @@ import { AnyApiFactory } from '@backstage/frontend-plugin-api'; import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; import { ApiFactory } from '@backstage/frontend-plugin-api'; +import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; import { defaultEntityContentGroups } from '@backstage/plugin-catalog-react/alpha'; import { Entity } from '@backstage/catalog-model'; import { EntityCardType } from '@backstage/plugin-catalog-react/alpha'; import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api'; import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; +import { ExtensionInput } from '@backstage/frontend-plugin-api'; import { ExternalRouteRef } from '@backstage/core-plugin-api'; import { FilterPredicate } from '@backstage/filter-predicates'; import { IconComponent } from '@backstage/frontend-plugin-api'; @@ -348,7 +350,6 @@ const _default: OverridableFrontendPlugin< }; output: | ExtensionDataRef - | ExtensionDataRef | ExtensionDataRef< RouteRef_2, 'core.routing.ref', @@ -356,6 +357,7 @@ const _default: OverridableFrontendPlugin< optional: true; } > + | ExtensionDataRef | ExtensionDataRef< (entity: Entity) => boolean, 'catalog.entity-filter-function', @@ -418,7 +420,6 @@ const _default: OverridableFrontendPlugin< }; output: | ExtensionDataRef - | ExtensionDataRef | ExtensionDataRef< RouteRef_2, 'core.routing.ref', @@ -426,6 +427,7 @@ const _default: OverridableFrontendPlugin< optional: true; } > + | ExtensionDataRef | ExtensionDataRef< (entity: Entity) => boolean, 'catalog.entity-filter-function', @@ -494,28 +496,70 @@ const _default: OverridableFrontendPlugin< config: { initiallySelectedFilter: 'all' | 'owned' | 'starred' | undefined; path: string | undefined; + title: string | undefined; }; configInput: { initiallySelectedFilter?: 'all' | 'owned' | 'starred' | undefined; + title?: string | undefined; path?: string | undefined; }; output: | ExtensionDataRef - | ExtensionDataRef | ExtensionDataRef< RouteRef_2, 'core.routing.ref', { optional: true; } + > + | ExtensionDataRef + | ExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + > + | ExtensionDataRef< + IconComponent, + 'core.icon', + { + optional: true; + } >; - inputs: {}; + inputs: { + pages: ExtensionInput< + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef_2, + 'core.routing.ref', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + >, + { + singleton: false; + optional: false; + internal: false; + } + >; + }; kind: 'page'; name: undefined; params: { defaultPath?: [Error: `Use the 'path' param instead`]; path: string; - loader: () => Promise; + title?: string; + icon?: IconComponent; + loader?: () => Promise; routeRef?: RouteRef_2; }; }>; diff --git a/plugins/app-visualizer/report.api.md b/plugins/app-visualizer/report.api.md index 180507677e..5608546930 100644 --- a/plugins/app-visualizer/report.api.md +++ b/plugins/app-visualizer/report.api.md @@ -4,7 +4,9 @@ ```ts import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; +import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; +import { ExtensionInput } from '@backstage/frontend-plugin-api'; import { IconComponent } from '@backstage/frontend-plugin-api'; import { JSX as JSX_2 } from 'react'; import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api'; @@ -42,24 +44,225 @@ const visualizerPlugin: OverridableFrontendPlugin< name: undefined; config: { path: string | undefined; + title: string | undefined; }; configInput: { + title?: string | undefined; path?: string | undefined; }; output: | ExtensionDataRef - | ExtensionDataRef | ExtensionDataRef< RouteRef, 'core.routing.ref', { optional: true; } + > + | ExtensionDataRef + | ExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + > + | ExtensionDataRef< + IconComponent, + 'core.icon', + { + optional: true; + } >; - inputs: {}; + inputs: { + pages: ExtensionInput< + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + >, + { + singleton: false; + optional: false; + internal: false; + } + >; + }; params: { defaultPath?: [Error: `Use the 'path' param instead`]; path: string; + title?: string; + icon?: IconComponent; + loader?: () => Promise; + routeRef?: RouteRef; + }; + }>; + 'page:app-visualizer/2': OverridableExtensionDefinition<{ + kind: 'page'; + name: '2'; + config: { + path: string | undefined; + title: string | undefined; + }; + configInput: { + title?: string | undefined; + path?: string | undefined; + }; + output: + | ExtensionDataRef + | ExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + > + | ExtensionDataRef + | ExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + > + | ExtensionDataRef< + IconComponent, + 'core.icon', + { + optional: true; + } + >; + inputs: { + pages: ExtensionInput< + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + >, + { + singleton: false; + optional: false; + internal: false; + } + >; + }; + params: { + defaultPath?: [Error: `Use the 'path' param instead`]; + path: string; + title?: string; + icon?: IconComponent; + loader?: () => Promise; + routeRef?: RouteRef; + }; + }>; + 'sub-page:app-visualizer/details': OverridableExtensionDefinition<{ + kind: 'sub-page'; + name: 'details'; + config: { + path: string | undefined; + title: string | undefined; + }; + configInput: { + title?: string | undefined; + path?: string | undefined; + }; + output: + | ExtensionDataRef + | ExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + > + | ExtensionDataRef + | ExtensionDataRef; + inputs: {}; + params: { + path: string; + title: string; + loader: () => Promise; + routeRef?: RouteRef; + }; + }>; + 'sub-page:app-visualizer/text': OverridableExtensionDefinition<{ + kind: 'sub-page'; + name: 'text'; + config: { + path: string | undefined; + title: string | undefined; + }; + configInput: { + title?: string | undefined; + path?: string | undefined; + }; + output: + | ExtensionDataRef + | ExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + > + | ExtensionDataRef + | ExtensionDataRef; + inputs: {}; + params: { + path: string; + title: string; + loader: () => Promise; + routeRef?: RouteRef; + }; + }>; + 'sub-page:app-visualizer/tree': OverridableExtensionDefinition<{ + kind: 'sub-page'; + name: 'tree'; + config: { + path: string | undefined; + title: string | undefined; + }; + configInput: { + title?: string | undefined; + path?: string | undefined; + }; + output: + | ExtensionDataRef + | ExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + > + | ExtensionDataRef + | ExtensionDataRef; + inputs: {}; + params: { + path: string; + title: string; loader: () => Promise; routeRef?: RouteRef; }; diff --git a/plugins/app-visualizer/src/plugin.tsx b/plugins/app-visualizer/src/plugin.tsx index f4936ef088..1ffb17bba1 100644 --- a/plugins/app-visualizer/src/plugin.tsx +++ b/plugins/app-visualizer/src/plugin.tsx @@ -21,7 +21,7 @@ import { PageBlueprint, SubPageBlueprint, } from '@backstage/frontend-plugin-api'; -import { RiEyeLine as VisualizerIcon } from '@remixicon/react'; +import { RiEyeLine } from '@remixicon/react'; const rootRouteRef = createRouteRef(); @@ -81,9 +81,9 @@ const appVisualizerTreePage = SubPageBlueprint.make({ routeRef: treeRouteRef, title: 'Tree', loader: () => - import('./components/AppVisualizerPage/TreeVisualizer').then( - m => , - ), + import('./components/AppVisualizerPage/TreeVisualizer').then(m => ( + + )), }, }); const appVisualizerDetailedPage = SubPageBlueprint.make({ @@ -94,9 +94,9 @@ const appVisualizerDetailedPage = SubPageBlueprint.make({ routeRef: detailedRouteRef, title: 'Detailed', loader: () => - import('./components/AppVisualizerPage/DetailedVisualizer').then( - m => , - ), + import('./components/AppVisualizerPage/DetailedVisualizer').then(m => ( + + )), }, }); const appVisualizerTextPage = SubPageBlueprint.make({ @@ -107,16 +107,16 @@ const appVisualizerTextPage = SubPageBlueprint.make({ routeRef: textRouteRef, title: 'Text', loader: () => - import('./components/AppVisualizerPage/TextVisualizer').then( - m => , - ), + import('./components/AppVisualizerPage/TextVisualizer').then(m => ( + + )), }, }); export const appVisualizerNavItem = NavItemBlueprint.make({ params: { title: 'Visualizer', - icon: () => , + icon: () => , routeRef: rootRouteRef, }, }); @@ -124,6 +124,8 @@ export const appVisualizerNavItem = NavItemBlueprint.make({ /** @public */ export const visualizerPlugin = createFrontendPlugin({ pluginId: 'app-visualizer', + title: 'App Visualizer', + icon: () => , info: { packageJson: () => import('../package.json') }, extensions: [ appVisualizerPage, diff --git a/plugins/app/report.api.md b/plugins/app/report.api.md index 256517b86f..6a85a26f8c 100644 --- a/plugins/app/report.api.md +++ b/plugins/app/report.api.md @@ -907,6 +907,62 @@ const appPlugin: OverridableFrontendPlugin< : never; }>; }>; + 'component:app/core-page-layout': OverridableExtensionDefinition<{ + kind: 'component'; + name: 'core-page-layout'; + config: {}; + configInput: {}; + output: ExtensionDataRef< + { + ref: SwappableComponentRef; + loader: + | (() => (props: {}) => JSX.Element | null) + | (() => Promise<(props: {}) => JSX.Element | null>); + }, + 'core.swappableComponent', + {} + >; + inputs: {}; + params: >(params: { + component: Ref extends SwappableComponentRef< + any, + infer IExternalComponentProps + > + ? { + ref: Ref; + } & ((props: IExternalComponentProps) => JSX.Element | null) + : never; + loader: Ref extends SwappableComponentRef< + infer IInnerComponentProps, + any + > + ? + | (() => (props: IInnerComponentProps) => JSX.Element | null) + | (() => Promise< + (props: IInnerComponentProps) => JSX.Element | null + >) + : never; + }) => ExtensionBlueprintParams<{ + component: Ref extends SwappableComponentRef< + any, + infer IExternalComponentProps + > + ? { + ref: Ref; + } & ((props: IExternalComponentProps) => JSX.Element | null) + : never; + loader: Ref extends SwappableComponentRef< + infer IInnerComponentProps, + any + > + ? + | (() => (props: IInnerComponentProps) => JSX.Element | null) + | (() => Promise< + (props: IInnerComponentProps) => JSX.Element | null + >) + : never; + }>; + }>; 'component:app/core-progress': OverridableExtensionDefinition<{ kind: 'component'; name: 'core-progress'; diff --git a/plugins/app/src/extensions/components.tsx b/plugins/app/src/extensions/components.tsx index 5c985986ca..4934a207c7 100644 --- a/plugins/app/src/extensions/components.tsx +++ b/plugins/app/src/extensions/components.tsx @@ -73,10 +73,10 @@ export const PageLayout = SwappableComponentBlueprint.make({ define({ component: SwappablePageLayout, loader: () => (props: PageLayoutProps) => { - const { title, tabs, children } = props; + const { title, icon: Icon, tabs, children } = props; return ( -
+
} tabs={tabs} /> {children} diff --git a/plugins/auth/report.api.md b/plugins/auth/report.api.md index 1d346a9ce4..0671136f79 100644 --- a/plugins/auth/report.api.md +++ b/plugins/auth/report.api.md @@ -4,7 +4,10 @@ ```ts import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; +import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; +import { ExtensionInput } from '@backstage/frontend-plugin-api'; +import { IconComponent } from '@backstage/frontend-plugin-api'; import { JSX as JSX_2 } from 'react'; import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api'; import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api'; @@ -22,25 +25,67 @@ const _default: OverridableFrontendPlugin< name: undefined; config: { path: string | undefined; + title: string | undefined; }; configInput: { + title?: string | undefined; path?: string | undefined; }; output: | ExtensionDataRef - | ExtensionDataRef | ExtensionDataRef< RouteRef, 'core.routing.ref', { optional: true; } + > + | ExtensionDataRef + | ExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + > + | ExtensionDataRef< + IconComponent, + 'core.icon', + { + optional: true; + } >; - inputs: {}; + inputs: { + pages: ExtensionInput< + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + >, + { + singleton: false; + optional: false; + internal: false; + } + >; + }; params: { defaultPath?: [Error: `Use the 'path' param instead`]; path: string; - loader: () => Promise; + title?: string; + icon?: IconComponent; + loader?: () => Promise; routeRef?: RouteRef; }; }>; diff --git a/plugins/catalog-graph/report-alpha.api.md b/plugins/catalog-graph/report-alpha.api.md index 826155e87a..74415759ca 100644 --- a/plugins/catalog-graph/report-alpha.api.md +++ b/plugins/catalog-graph/report-alpha.api.md @@ -6,12 +6,15 @@ import { AnyApiFactory } from '@backstage/frontend-plugin-api'; import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; import { ApiFactory } from '@backstage/frontend-plugin-api'; +import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; import { Entity } from '@backstage/catalog-model'; import { EntityCardType } from '@backstage/plugin-catalog-react/alpha'; import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api'; import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; +import { ExtensionInput } from '@backstage/frontend-plugin-api'; import { ExternalRouteRef } from '@backstage/core-plugin-api'; import { FilterPredicate } from '@backstage/filter-predicates'; +import { IconComponent } from '@backstage/frontend-plugin-api'; import { JSX as JSX_2 } from 'react'; import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api'; import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api'; @@ -157,6 +160,7 @@ const _default: OverridableFrontendPlugin< relationPairs: [string, string][] | undefined; zoom: 'disabled' | 'enabled' | 'enable-on-click' | undefined; path: string | undefined; + title: string | undefined; }; configInput: { curve?: 'curveStepBefore' | 'curveMonotoneX' | undefined; @@ -172,25 +176,66 @@ const _default: OverridableFrontendPlugin< selectedRelations?: string[] | undefined; selectedKinds?: string[] | undefined; showFilters?: boolean | undefined; + title?: string | undefined; path?: string | undefined; }; output: | ExtensionDataRef - | ExtensionDataRef | ExtensionDataRef< RouteRef_2, 'core.routing.ref', { optional: true; } + > + | ExtensionDataRef + | ExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + > + | ExtensionDataRef< + IconComponent, + 'core.icon', + { + optional: true; + } >; - inputs: {}; + inputs: { + pages: ExtensionInput< + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef_2, + 'core.routing.ref', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + >, + { + singleton: false; + optional: false; + internal: false; + } + >; + }; kind: 'page'; name: undefined; params: { defaultPath?: [Error: `Use the 'path' param instead`]; path: string; - loader: () => Promise; + title?: string; + icon?: IconComponent; + loader?: () => Promise; routeRef?: RouteRef_2; }; }>; diff --git a/plugins/catalog-import/report-alpha.api.md b/plugins/catalog-import/report-alpha.api.md index 85c412f84d..f4b58a335c 100644 --- a/plugins/catalog-import/report-alpha.api.md +++ b/plugins/catalog-import/report-alpha.api.md @@ -6,8 +6,11 @@ import { AnyApiFactory } from '@backstage/frontend-plugin-api'; import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; import { ApiFactory } from '@backstage/frontend-plugin-api'; +import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api'; import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; +import { ExtensionInput } from '@backstage/frontend-plugin-api'; +import { IconComponent } from '@backstage/frontend-plugin-api'; import { JSX as JSX_2 } from 'react'; import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api'; import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api'; @@ -115,25 +118,67 @@ const _default: OverridableFrontendPlugin< name: undefined; config: { path: string | undefined; + title: string | undefined; }; configInput: { + title?: string | undefined; path?: string | undefined; }; output: | ExtensionDataRef - | ExtensionDataRef | ExtensionDataRef< RouteRef_2, 'core.routing.ref', { optional: true; } + > + | ExtensionDataRef + | ExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + > + | ExtensionDataRef< + IconComponent, + 'core.icon', + { + optional: true; + } >; - inputs: {}; + inputs: { + pages: ExtensionInput< + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef_2, + 'core.routing.ref', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + >, + { + singleton: false; + optional: false; + internal: false; + } + >; + }; params: { defaultPath?: [Error: `Use the 'path' param instead`]; path: string; - loader: () => Promise; + title?: string; + icon?: IconComponent; + loader?: () => Promise; routeRef?: RouteRef_2; }; }>; diff --git a/plugins/catalog-react/report-alpha.api.md b/plugins/catalog-react/report-alpha.api.md index 04c900ee41..a514820f20 100644 --- a/plugins/catalog-react/report-alpha.api.md +++ b/plugins/catalog-react/report-alpha.api.md @@ -258,7 +258,6 @@ export const EntityContentBlueprint: ExtensionBlueprint<{ }; output: | ExtensionDataRef - | ExtensionDataRef | ExtensionDataRef< RouteRef, 'core.routing.ref', @@ -266,6 +265,7 @@ export const EntityContentBlueprint: ExtensionBlueprint<{ optional: true; } > + | ExtensionDataRef | ExtensionDataRef< (entity: Entity) => boolean, 'catalog.entity-filter-function', diff --git a/plugins/catalog-unprocessed-entities/report-alpha.api.md b/plugins/catalog-unprocessed-entities/report-alpha.api.md index d3bd6dd28f..2a988eeee2 100644 --- a/plugins/catalog-unprocessed-entities/report-alpha.api.md +++ b/plugins/catalog-unprocessed-entities/report-alpha.api.md @@ -6,9 +6,11 @@ import { AnyApiFactory } from '@backstage/frontend-plugin-api'; import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; import { ApiFactory } from '@backstage/frontend-plugin-api'; +import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; import { DevToolsContentBlueprintParams } from '@backstage/plugin-devtools-react'; import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api'; import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; +import { ExtensionInput } from '@backstage/frontend-plugin-api'; import { IconComponent } from '@backstage/frontend-plugin-api'; import { JSX as JSX_2 } from 'react'; import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api'; @@ -64,25 +66,67 @@ const _default: OverridableFrontendPlugin< name: undefined; config: { path: string | undefined; + title: string | undefined; }; configInput: { + title?: string | undefined; path?: string | undefined; }; output: | ExtensionDataRef - | ExtensionDataRef | ExtensionDataRef< RouteRef_2, 'core.routing.ref', { optional: true; } + > + | ExtensionDataRef + | ExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + > + | ExtensionDataRef< + IconComponent, + 'core.icon', + { + optional: true; + } >; - inputs: {}; + inputs: { + pages: ExtensionInput< + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef_2, + 'core.routing.ref', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + >, + { + singleton: false; + optional: false; + internal: false; + } + >; + }; params: { defaultPath?: [Error: `Use the 'path' param instead`]; path: string; - loader: () => Promise; + title?: string; + icon?: IconComponent; + loader?: () => Promise; routeRef?: RouteRef_2; }; }>; @@ -104,7 +148,6 @@ export const unprocessedEntitiesDevToolsContent: OverridableExtensionDefinition< }; output: | ExtensionDataRef - | ExtensionDataRef | ExtensionDataRef< RouteRef_2, 'core.routing.ref', @@ -112,6 +155,7 @@ export const unprocessedEntitiesDevToolsContent: OverridableExtensionDefinition< optional: true; } > + | ExtensionDataRef | ExtensionDataRef; inputs: {}; params: DevToolsContentBlueprintParams; diff --git a/plugins/catalog/report-alpha.api.md b/plugins/catalog/report-alpha.api.md index d154ed4981..aa29c6cc1e 100644 --- a/plugins/catalog/report-alpha.api.md +++ b/plugins/catalog/report-alpha.api.md @@ -756,7 +756,6 @@ const _default: OverridableFrontendPlugin< }; output: | ExtensionDataRef - | ExtensionDataRef | ExtensionDataRef< RouteRef_2, 'core.routing.ref', @@ -764,6 +763,7 @@ const _default: OverridableFrontendPlugin< optional: true; } > + | ExtensionDataRef | ExtensionDataRef< (entity: Entity) => boolean, 'catalog.entity-filter-function', @@ -998,6 +998,7 @@ const _default: OverridableFrontendPlugin< limit?: number | undefined; }; path: string | undefined; + title: string | undefined; }; configInput: { pagination?: @@ -1008,19 +1009,57 @@ const _default: OverridableFrontendPlugin< limit?: number | undefined; } | undefined; + title?: string | undefined; path?: string | undefined; }; output: | ExtensionDataRef - | ExtensionDataRef | ExtensionDataRef< RouteRef_2, 'core.routing.ref', { optional: true; } + > + | ExtensionDataRef + | ExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + > + | ExtensionDataRef< + IconComponent, + 'core.icon', + { + optional: true; + } >; inputs: { + pages: ExtensionInput< + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef_2, + 'core.routing.ref', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + >, + { + singleton: false; + optional: false; + internal: false; + } + >; filters: ExtensionInput< ConfigurableExtensionDataRef, { @@ -1035,7 +1074,9 @@ const _default: OverridableFrontendPlugin< params: { defaultPath?: [Error: `Use the 'path' param instead`]; path: string; - loader: () => Promise; + title?: string; + icon?: IconComponent; + loader?: () => Promise; routeRef?: RouteRef_2; }; }>; @@ -1052,6 +1093,7 @@ const _default: OverridableFrontendPlugin< | undefined; showNavItemIcons: boolean; path: string | undefined; + title: string | undefined; }; configInput: { groups?: @@ -1064,19 +1106,57 @@ const _default: OverridableFrontendPlugin< >[] | undefined; showNavItemIcons?: boolean | undefined; + title?: string | undefined; path?: string | undefined; }; output: | ExtensionDataRef - | ExtensionDataRef | ExtensionDataRef< RouteRef_2, 'core.routing.ref', { optional: true; } + > + | ExtensionDataRef + | ExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + > + | ExtensionDataRef< + IconComponent, + 'core.icon', + { + optional: true; + } >; inputs: { + pages: ExtensionInput< + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef_2, + 'core.routing.ref', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + >, + { + singleton: false; + optional: false; + internal: false; + } + >; headers: ExtensionInput< | ConfigurableExtensionDataRef< (entity: Entity) => boolean, @@ -1168,7 +1248,9 @@ const _default: OverridableFrontendPlugin< params: { defaultPath?: [Error: `Use the 'path' param instead`]; path: string; - loader: () => Promise; + title?: string; + icon?: IconComponent; + loader?: () => Promise; routeRef?: RouteRef_2; }; }>; diff --git a/plugins/devtools-react/report.api.md b/plugins/devtools-react/report.api.md index d1b1a42250..6916afed84 100644 --- a/plugins/devtools-react/report.api.md +++ b/plugins/devtools-react/report.api.md @@ -15,7 +15,6 @@ export const DevToolsContentBlueprint: ExtensionBlueprint<{ params: DevToolsContentBlueprintParams; output: | ExtensionDataRef - | ExtensionDataRef | ExtensionDataRef< RouteRef, 'core.routing.ref', @@ -23,6 +22,7 @@ export const DevToolsContentBlueprint: ExtensionBlueprint<{ optional: true; } > + | ExtensionDataRef | ExtensionDataRef; inputs: {}; config: { diff --git a/plugins/devtools/report-alpha.api.md b/plugins/devtools/report-alpha.api.md index 8f3e24f7f3..5a90b56cab 100644 --- a/plugins/devtools/report-alpha.api.md +++ b/plugins/devtools/report-alpha.api.md @@ -63,21 +63,60 @@ const _default: OverridableFrontendPlugin< 'page:devtools': OverridableExtensionDefinition<{ config: { path: string | undefined; + title: string | undefined; }; configInput: { + title?: string | undefined; path?: string | undefined; }; output: | ExtensionDataRef - | ExtensionDataRef | ExtensionDataRef< RouteRef_2, 'core.routing.ref', { optional: true; } + > + | ExtensionDataRef + | ExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + > + | ExtensionDataRef< + IconComponent, + 'core.icon', + { + optional: true; + } >; inputs: { + pages: ExtensionInput< + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef_2, + 'core.routing.ref', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + >, + { + singleton: false; + optional: false; + internal: false; + } + >; contents: ExtensionInput< | ConfigurableExtensionDataRef | ConfigurableExtensionDataRef @@ -101,7 +140,9 @@ const _default: OverridableFrontendPlugin< params: { defaultPath?: [Error: `Use the 'path' param instead`]; path: string; - loader: () => Promise; + title?: string; + icon?: IconComponent; + loader?: () => Promise; routeRef?: RouteRef_2; }; }>; diff --git a/plugins/home/report-alpha.api.md b/plugins/home/report-alpha.api.md index 4568fc8c2a..ef291b0972 100644 --- a/plugins/home/report-alpha.api.md +++ b/plugins/home/report-alpha.api.md @@ -104,21 +104,60 @@ const _default: OverridableFrontendPlugin< 'page:home': OverridableExtensionDefinition<{ config: { path: string | undefined; + title: string | undefined; }; configInput: { + title?: string | undefined; path?: string | undefined; }; output: | ExtensionDataRef - | ExtensionDataRef | ExtensionDataRef< RouteRef, 'core.routing.ref', { optional: true; } + > + | ExtensionDataRef + | ExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + > + | ExtensionDataRef< + IconComponent, + 'core.icon', + { + optional: true; + } >; inputs: { + pages: ExtensionInput< + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + >, + { + singleton: false; + optional: false; + internal: false; + } + >; widgets: ExtensionInput< ConfigurableExtensionDataRef< HomePageWidgetData, @@ -149,7 +188,9 @@ const _default: OverridableFrontendPlugin< params: { defaultPath?: [Error: `Use the 'path' param instead`]; path: string; - loader: () => Promise; + title?: string; + icon?: IconComponent; + loader?: () => Promise; routeRef?: RouteRef; }; }>; diff --git a/plugins/kubernetes/report-alpha.api.md b/plugins/kubernetes/report-alpha.api.md index 46a576191f..b4dc80d439 100644 --- a/plugins/kubernetes/report-alpha.api.md +++ b/plugins/kubernetes/report-alpha.api.md @@ -6,11 +6,14 @@ import { AnyApiFactory } from '@backstage/frontend-plugin-api'; import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; import { ApiFactory } from '@backstage/frontend-plugin-api'; +import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; import { defaultEntityContentGroups } from '@backstage/plugin-catalog-react/alpha'; import { Entity } from '@backstage/catalog-model'; import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api'; import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; +import { ExtensionInput } from '@backstage/frontend-plugin-api'; import { FilterPredicate } from '@backstage/filter-predicates'; +import { IconComponent } from '@backstage/frontend-plugin-api'; import { JSX as JSX_2 } from 'react'; import { JSXElementConstructor } from 'react'; import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api'; @@ -106,7 +109,6 @@ const _default: OverridableFrontendPlugin< }; output: | ExtensionDataRef - | ExtensionDataRef | ExtensionDataRef< RouteRef_2, 'core.routing.ref', @@ -114,6 +116,7 @@ const _default: OverridableFrontendPlugin< optional: true; } > + | ExtensionDataRef | ExtensionDataRef< (entity: Entity) => boolean, 'catalog.entity-filter-function', @@ -162,25 +165,67 @@ const _default: OverridableFrontendPlugin< name: undefined; config: { path: string | undefined; + title: string | undefined; }; configInput: { + title?: string | undefined; path?: string | undefined; }; output: | ExtensionDataRef - | ExtensionDataRef | ExtensionDataRef< RouteRef_2, 'core.routing.ref', { optional: true; } + > + | ExtensionDataRef + | ExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + > + | ExtensionDataRef< + IconComponent, + 'core.icon', + { + optional: true; + } >; - inputs: {}; + inputs: { + pages: ExtensionInput< + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef_2, + 'core.routing.ref', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + >, + { + singleton: false; + optional: false; + internal: false; + } + >; + }; params: { defaultPath?: [Error: `Use the 'path' param instead`]; path: string; - loader: () => Promise; + title?: string; + icon?: IconComponent; + loader?: () => Promise; routeRef?: RouteRef_2; }; }>; diff --git a/plugins/mui-to-bui/report.api.md b/plugins/mui-to-bui/report.api.md index db1d49f828..2ba75b2777 100644 --- a/plugins/mui-to-bui/report.api.md +++ b/plugins/mui-to-bui/report.api.md @@ -5,7 +5,10 @@ ```ts import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; +import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; +import { ExtensionInput } from '@backstage/frontend-plugin-api'; +import { IconComponent } from '@backstage/frontend-plugin-api'; import { JSX as JSX_2 } from 'react'; import { JSX as JSX_3 } from 'react/jsx-runtime'; import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api'; @@ -36,25 +39,67 @@ const _default: OverridableFrontendPlugin< name: undefined; config: { path: string | undefined; + title: string | undefined; }; configInput: { + title?: string | undefined; path?: string | undefined; }; output: | ExtensionDataRef - | ExtensionDataRef | ExtensionDataRef< RouteRef_2, 'core.routing.ref', { optional: true; } + > + | ExtensionDataRef + | ExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + > + | ExtensionDataRef< + IconComponent, + 'core.icon', + { + optional: true; + } >; - inputs: {}; + inputs: { + pages: ExtensionInput< + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef_2, + 'core.routing.ref', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + >, + { + singleton: false; + optional: false; + internal: false; + } + >; + }; params: { defaultPath?: [Error: `Use the 'path' param instead`]; path: string; - loader: () => Promise; + title?: string; + icon?: IconComponent; + loader?: () => Promise; routeRef?: RouteRef_2; }; }>; diff --git a/plugins/notifications/report-alpha.api.md b/plugins/notifications/report-alpha.api.md index 9a27b67d80..40d43cff7c 100644 --- a/plugins/notifications/report-alpha.api.md +++ b/plugins/notifications/report-alpha.api.md @@ -6,8 +6,11 @@ import { AnyApiFactory } from '@backstage/frontend-plugin-api'; import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; import { ApiFactory } from '@backstage/frontend-plugin-api'; +import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api'; import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; +import { ExtensionInput } from '@backstage/frontend-plugin-api'; +import { IconComponent } from '@backstage/frontend-plugin-api'; import { JSX as JSX_2 } from 'react'; import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api'; import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api'; @@ -42,25 +45,67 @@ const _default: OverridableFrontendPlugin< name: undefined; config: { path: string | undefined; + title: string | undefined; }; configInput: { + title?: string | undefined; path?: string | undefined; }; output: | ExtensionDataRef - | ExtensionDataRef | ExtensionDataRef< RouteRef_2, 'core.routing.ref', { optional: true; } + > + | ExtensionDataRef + | ExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + > + | ExtensionDataRef< + IconComponent, + 'core.icon', + { + optional: true; + } >; - inputs: {}; + inputs: { + pages: ExtensionInput< + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef_2, + 'core.routing.ref', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + >, + { + singleton: false; + optional: false; + internal: false; + } + >; + }; params: { defaultPath?: [Error: `Use the 'path' param instead`]; path: string; - loader: () => Promise; + title?: string; + icon?: IconComponent; + loader?: () => Promise; routeRef?: RouteRef_2; }; }>; diff --git a/plugins/scaffolder/report-alpha.api.md b/plugins/scaffolder/report-alpha.api.md index 0eda5c0ca9..05e5cb05ec 100644 --- a/plugins/scaffolder/report-alpha.api.md +++ b/plugins/scaffolder/report-alpha.api.md @@ -194,21 +194,60 @@ const _default: OverridableFrontendPlugin< 'page:scaffolder': OverridableExtensionDefinition<{ config: { path: string | undefined; + title: string | undefined; }; configInput: { + title?: string | undefined; path?: string | undefined; }; output: | ExtensionDataRef - | ExtensionDataRef | ExtensionDataRef< RouteRef_2, 'core.routing.ref', { optional: true; } + > + | ExtensionDataRef + | ExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + > + | ExtensionDataRef< + IconComponent, + 'core.icon', + { + optional: true; + } >; inputs: { + pages: ExtensionInput< + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef_2, + 'core.routing.ref', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + >, + { + singleton: false; + optional: false; + internal: false; + } + >; formFields: ExtensionInput< ConfigurableExtensionDataRef< () => Promise, @@ -227,7 +266,9 @@ const _default: OverridableFrontendPlugin< params: { defaultPath?: [Error: `Use the 'path' param instead`]; path: string; - loader: () => Promise; + title?: string; + icon?: IconComponent; + loader?: () => Promise; routeRef?: RouteRef_2; }; }>; diff --git a/plugins/search/report-alpha.api.md b/plugins/search/report-alpha.api.md index 2e82c77416..d9364b1fe7 100644 --- a/plugins/search/report-alpha.api.md +++ b/plugins/search/report-alpha.api.md @@ -68,22 +68,61 @@ const _default: OverridableFrontendPlugin< config: { noTrack: boolean; path: string | undefined; + title: string | undefined; }; configInput: { noTrack?: boolean | undefined; + title?: string | undefined; path?: string | undefined; }; output: | ExtensionDataRef - | ExtensionDataRef | ExtensionDataRef< RouteRef, 'core.routing.ref', { optional: true; } + > + | ExtensionDataRef + | ExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + > + | ExtensionDataRef< + IconComponent, + 'core.icon', + { + optional: true; + } >; inputs: { + pages: ExtensionInput< + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + >, + { + singleton: false; + optional: false; + internal: false; + } + >; items: ExtensionInput< ConfigurableExtensionDataRef< { @@ -136,7 +175,9 @@ const _default: OverridableFrontendPlugin< params: { defaultPath?: [Error: `Use the 'path' param instead`]; path: string; - loader: () => Promise; + title?: string; + icon?: IconComponent; + loader?: () => Promise; routeRef?: RouteRef; }; }>; @@ -189,22 +230,61 @@ export const searchPage: OverridableExtensionDefinition<{ config: { noTrack: boolean; path: string | undefined; + title: string | undefined; }; configInput: { noTrack?: boolean | undefined; + title?: string | undefined; path?: string | undefined; }; output: | ExtensionDataRef - | ExtensionDataRef | ExtensionDataRef< RouteRef, 'core.routing.ref', { optional: true; } + > + | ExtensionDataRef + | ExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + > + | ExtensionDataRef< + IconComponent, + 'core.icon', + { + optional: true; + } >; inputs: { + pages: ExtensionInput< + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + >, + { + singleton: false; + optional: false; + internal: false; + } + >; items: ExtensionInput< ConfigurableExtensionDataRef< { @@ -257,7 +337,9 @@ export const searchPage: OverridableExtensionDefinition<{ params: { defaultPath?: [Error: `Use the 'path' param instead`]; path: string; - loader: () => Promise; + title?: string; + icon?: IconComponent; + loader?: () => Promise; routeRef?: RouteRef; }; }>; diff --git a/plugins/techdocs/report-alpha.api.md b/plugins/techdocs/report-alpha.api.md index 3092a010d4..7f9dd28973 100644 --- a/plugins/techdocs/report-alpha.api.md +++ b/plugins/techdocs/report-alpha.api.md @@ -139,7 +139,6 @@ const _default: OverridableFrontendPlugin< }; output: | ExtensionDataRef - | ExtensionDataRef | ExtensionDataRef< RouteRef_2, 'core.routing.ref', @@ -147,6 +146,7 @@ const _default: OverridableFrontendPlugin< optional: true; } > + | ExtensionDataRef | ExtensionDataRef< (entity: Entity) => boolean, 'catalog.entity-filter-function', @@ -284,25 +284,67 @@ const _default: OverridableFrontendPlugin< name: undefined; config: { path: string | undefined; + title: string | undefined; }; configInput: { + title?: string | undefined; path?: string | undefined; }; output: | ExtensionDataRef - | ExtensionDataRef | ExtensionDataRef< RouteRef_2, 'core.routing.ref', { optional: true; } + > + | ExtensionDataRef + | ExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + > + | ExtensionDataRef< + IconComponent, + 'core.icon', + { + optional: true; + } >; - inputs: {}; + inputs: { + pages: ExtensionInput< + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef_2, + 'core.routing.ref', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + >, + { + singleton: false; + optional: false; + internal: false; + } + >; + }; params: { defaultPath?: [Error: `Use the 'path' param instead`]; path: string; - loader: () => Promise; + title?: string; + icon?: IconComponent; + loader?: () => Promise; routeRef?: RouteRef_2; }; }>; @@ -311,23 +353,62 @@ const _default: OverridableFrontendPlugin< withoutSearch: boolean; withoutHeader: boolean; path: string | undefined; + title: string | undefined; }; configInput: { withoutSearch?: boolean | undefined; withoutHeader?: boolean | undefined; + title?: string | undefined; path?: string | undefined; }; output: | ExtensionDataRef - | ExtensionDataRef | ExtensionDataRef< RouteRef_2, 'core.routing.ref', { optional: true; } + > + | ExtensionDataRef + | ExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + > + | ExtensionDataRef< + IconComponent, + 'core.icon', + { + optional: true; + } >; inputs: { + pages: ExtensionInput< + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef_2, + 'core.routing.ref', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + >, + { + singleton: false; + optional: false; + internal: false; + } + >; addons: ExtensionInput< ConfigurableExtensionDataRef< TechDocsAddonOptions, @@ -346,7 +427,9 @@ const _default: OverridableFrontendPlugin< params: { defaultPath?: [Error: `Use the 'path' param instead`]; path: string; - loader: () => Promise; + title?: string; + icon?: IconComponent; + loader?: () => Promise; routeRef?: RouteRef_2; }; }>; diff --git a/plugins/user-settings/report-alpha.api.md b/plugins/user-settings/report-alpha.api.md index 056b5ee557..b4b8a11825 100644 --- a/plugins/user-settings/report-alpha.api.md +++ b/plugins/user-settings/report-alpha.api.md @@ -46,21 +46,60 @@ const _default: OverridableFrontendPlugin< 'page:user-settings': OverridableExtensionDefinition<{ config: { path: string | undefined; + title: string | undefined; }; configInput: { + title?: string | undefined; path?: string | undefined; }; output: | ExtensionDataRef - | ExtensionDataRef | ExtensionDataRef< RouteRef, 'core.routing.ref', { optional: true; } + > + | ExtensionDataRef + | ExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + > + | ExtensionDataRef< + IconComponent, + 'core.icon', + { + optional: true; + } >; inputs: { + pages: ExtensionInput< + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + >, + { + singleton: false; + optional: false; + internal: false; + } + >; providerSettings: ExtensionInput< ConfigurableExtensionDataRef, { @@ -75,7 +114,9 @@ const _default: OverridableFrontendPlugin< params: { defaultPath?: [Error: `Use the 'path' param instead`]; path: string; - loader: () => Promise; + title?: string; + icon?: IconComponent; + loader?: () => Promise; routeRef?: RouteRef; }; }>;