plugin icons and titles

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2026-02-09 23:02:34 +01:00
parent 4b996d05d3
commit e1f22f2d15
31 changed files with 1362 additions and 89 deletions
@@ -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.
@@ -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`
@@ -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 |
+48 -3
View File
@@ -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<string, 'core.routing.path', {}>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
RouteRef<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
string,
'core.title',
{
optional: true;
}
>
| ExtensionDataRef<
IconComponent,
'core.icon',
{
optional: true;
}
>;
inputs: {};
inputs: {
pages: ExtensionInput<
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ConfigurableExtensionDataRef<string, 'core.routing.path', {}>
| ConfigurableExtensionDataRef<
RouteRef<AnyRouteRefParams>,
'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<JSX.Element>;
title?: string;
icon?: IconComponent;
loader?: () => Promise<JSX.Element>;
routeRef?: RouteRef;
};
}>;
@@ -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<unknown>[];
readonly featureFlags: FeatureFlagConfig[];
readonly infoOptions?: {
+126 -3
View File
@@ -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<string, 'core.title', {}>;
icon: ConfigurableExtensionDataRef_2<IconComponent, 'core.icon', {}>;
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<FrontendPluginInfo>;
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<JSX.Element>;
};
output: ExtensionDataRef_2<JSX_3, 'core.reactElement', {}>;
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<JSX.Element>;
title?: string;
icon?: IconComponent;
loader?: () => Promise<JSX.Element>;
routeRef?: RouteRef;
};
output:
| ExtensionDataRef_2<string, 'core.routing.path', {}>
| ExtensionDataRef_2<JSX_3, 'core.reactElement', {}>
| ExtensionDataRef_2<
RouteRef<AnyRouteRefParams_2>,
'core.routing.ref',
{
optional: true;
}
>
| ExtensionDataRef_2<JSX_3, 'core.reactElement', {}>
| ExtensionDataRef_2<
string,
'core.title',
{
optional: true;
}
>
| ExtensionDataRef_2<
IconComponent,
'core.icon',
{
optional: true;
}
>;
inputs: {};
inputs: {
pages: ExtensionInput_2<
| ConfigurableExtensionDataRef_2<JSX_3, 'core.reactElement', {}>
| ConfigurableExtensionDataRef_2<string, 'core.routing.path', {}>
| ConfigurableExtensionDataRef_2<
RouteRef<AnyRouteRefParams_2>,
'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<PageLayoutProps, PageLayoutProps>;
};
// @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<TValue extends JsonValue> =
value: TValue;
};
// @public
export const SubPageBlueprint: ExtensionBlueprint_2<{
kind: 'sub-page';
params: {
path: string;
title: string;
loader: () => Promise<JSX.Element>;
routeRef?: RouteRef;
};
output:
| ExtensionDataRef_2<string, 'core.routing.path', {}>
| ExtensionDataRef_2<
RouteRef<AnyRouteRefParams_2>,
'core.routing.ref',
{
optional: true;
}
>
| ExtensionDataRef_2<JSX_3, 'core.reactElement', {}>
| ExtensionDataRef_2<string, 'core.title', {}>;
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,
@@ -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<JSX.Element>;
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 = () => (
<PageLayout title={title}>
<PageLayout title={title} icon={icon}>
{ExtensionBoundary.lazy(node, loader)}
</PageLayout>
);
yield coreExtensionData.reactElement(<PageContent />);
} 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 (
<PageLayout title={title} tabs={tabs}>
<PageLayout title={title} icon={icon} tabs={tabs}>
<Routes>
{/* Index route redirects to first sub-page */}
{firstPagePath && (
<Route
index
@@ -117,8 +122,9 @@ export const PageBlueprint = createExtensionBlueprint({
yield coreExtensionData.reactElement(<PageContent />);
} else {
// Parent page without loader or sub-pages - render just header
yield coreExtensionData.reactElement(<PageLayout title={title} />);
yield coreExtensionData.reactElement(
<PageLayout title={title} icon={icon} />,
);
}
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);
}
},
});
@@ -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 (
<div
@@ -65,11 +67,15 @@ function DefaultPageLayout(props: PageLayoutProps): JSX.Element {
{title && (
<div
style={{
display: 'flex',
alignItems: 'center',
gap: '8px',
padding: '12px 24px 8px',
fontSize: '18px',
fontWeight: 500,
}}
>
{Icon && <Icon fontSize="small" />}
{title}
</div>
)}
@@ -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
*/
@@ -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<string>().with({ id: 'core.title' }),
icon: createExtensionDataRef<IconComponent>().with({ id: 'core.icon' }),
reactElement: createExtensionDataRef<JSX.Element>().with({
id: 'core.reactElement',
}),
@@ -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 ?? [],
+49 -5
View File
@@ -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<string, 'core.routing.path', {}>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'core.routing.ref',
@@ -356,6 +357,7 @@ const _default: OverridableFrontendPlugin<
optional: true;
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
(entity: Entity) => boolean,
'catalog.entity-filter-function',
@@ -418,7 +420,6 @@ const _default: OverridableFrontendPlugin<
};
output:
| ExtensionDataRef<string, 'core.routing.path', {}>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'core.routing.ref',
@@ -426,6 +427,7 @@ const _default: OverridableFrontendPlugin<
optional: true;
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| 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<string, 'core.routing.path', {}>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
string,
'core.title',
{
optional: true;
}
>
| ExtensionDataRef<
IconComponent,
'core.icon',
{
optional: true;
}
>;
inputs: {};
inputs: {
pages: ExtensionInput<
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ConfigurableExtensionDataRef<string, 'core.routing.path', {}>
| ConfigurableExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'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<JSX.Element>;
title?: string;
icon?: IconComponent;
loader?: () => Promise<JSX.Element>;
routeRef?: RouteRef_2;
};
}>;
+205 -2
View File
@@ -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<string, 'core.routing.path', {}>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
RouteRef<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
string,
'core.title',
{
optional: true;
}
>
| ExtensionDataRef<
IconComponent,
'core.icon',
{
optional: true;
}
>;
inputs: {};
inputs: {
pages: ExtensionInput<
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ConfigurableExtensionDataRef<string, 'core.routing.path', {}>
| ConfigurableExtensionDataRef<
RouteRef<AnyRouteRefParams>,
'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<JSX.Element>;
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<string, 'core.routing.path', {}>
| ExtensionDataRef<
RouteRef<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
string,
'core.title',
{
optional: true;
}
>
| ExtensionDataRef<
IconComponent,
'core.icon',
{
optional: true;
}
>;
inputs: {
pages: ExtensionInput<
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ConfigurableExtensionDataRef<string, 'core.routing.path', {}>
| ConfigurableExtensionDataRef<
RouteRef<AnyRouteRefParams>,
'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<JSX.Element>;
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<string, 'core.routing.path', {}>
| ExtensionDataRef<
RouteRef<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<string, 'core.title', {}>;
inputs: {};
params: {
path: string;
title: string;
loader: () => Promise<JSX.Element>;
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<string, 'core.routing.path', {}>
| ExtensionDataRef<
RouteRef<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<string, 'core.title', {}>;
inputs: {};
params: {
path: string;
title: string;
loader: () => Promise<JSX.Element>;
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<string, 'core.routing.path', {}>
| ExtensionDataRef<
RouteRef<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<string, 'core.title', {}>;
inputs: {};
params: {
path: string;
title: string;
loader: () => Promise<JSX.Element>;
routeRef?: RouteRef;
};
+13 -11
View File
@@ -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 => <m.TreeVisualizer />,
),
import('./components/AppVisualizerPage/TreeVisualizer').then(m => (
<m.TreeVisualizer />
)),
},
});
const appVisualizerDetailedPage = SubPageBlueprint.make({
@@ -94,9 +94,9 @@ const appVisualizerDetailedPage = SubPageBlueprint.make({
routeRef: detailedRouteRef,
title: 'Detailed',
loader: () =>
import('./components/AppVisualizerPage/DetailedVisualizer').then(
m => <m.DetailedVisualizer />,
),
import('./components/AppVisualizerPage/DetailedVisualizer').then(m => (
<m.DetailedVisualizer />
)),
},
});
const appVisualizerTextPage = SubPageBlueprint.make({
@@ -107,16 +107,16 @@ const appVisualizerTextPage = SubPageBlueprint.make({
routeRef: textRouteRef,
title: 'Text',
loader: () =>
import('./components/AppVisualizerPage/TextVisualizer').then(
m => <m.TextVisualizer />,
),
import('./components/AppVisualizerPage/TextVisualizer').then(m => (
<m.TextVisualizer />
)),
},
});
export const appVisualizerNavItem = NavItemBlueprint.make({
params: {
title: 'Visualizer',
icon: () => <VisualizerIcon />,
icon: () => <RiEyeLine />,
routeRef: rootRouteRef,
},
});
@@ -124,6 +124,8 @@ export const appVisualizerNavItem = NavItemBlueprint.make({
/** @public */
export const visualizerPlugin = createFrontendPlugin({
pluginId: 'app-visualizer',
title: 'App Visualizer',
icon: () => <RiEyeLine />,
info: { packageJson: () => import('../package.json') },
extensions: [
appVisualizerPage,
+56
View File
@@ -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: <Ref extends SwappableComponentRef<any>>(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';
+2 -2
View File
@@ -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 (
<Flex direction="column" style={{ flexGrow: 1, minHeight: 0 }}>
<Header title={title} tabs={tabs} />
<Header title={title} icon={Icon && <Icon />} tabs={tabs} />
<Flex direction="column" style={{ flexGrow: 1, minHeight: 0 }}>
{children}
</Flex>
+48 -3
View File
@@ -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<string, 'core.routing.path', {}>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
RouteRef<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
string,
'core.title',
{
optional: true;
}
>
| ExtensionDataRef<
IconComponent,
'core.icon',
{
optional: true;
}
>;
inputs: {};
inputs: {
pages: ExtensionInput<
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ConfigurableExtensionDataRef<string, 'core.routing.path', {}>
| ConfigurableExtensionDataRef<
RouteRef<AnyRouteRefParams>,
'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<JSX.Element>;
title?: string;
icon?: IconComponent;
loader?: () => Promise<JSX.Element>;
routeRef?: RouteRef;
};
}>;
+48 -3
View File
@@ -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<string, 'core.routing.path', {}>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
string,
'core.title',
{
optional: true;
}
>
| ExtensionDataRef<
IconComponent,
'core.icon',
{
optional: true;
}
>;
inputs: {};
inputs: {
pages: ExtensionInput<
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ConfigurableExtensionDataRef<string, 'core.routing.path', {}>
| ConfigurableExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'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<JSX.Element>;
title?: string;
icon?: IconComponent;
loader?: () => Promise<JSX.Element>;
routeRef?: RouteRef_2;
};
}>;
+48 -3
View File
@@ -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<string, 'core.routing.path', {}>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
string,
'core.title',
{
optional: true;
}
>
| ExtensionDataRef<
IconComponent,
'core.icon',
{
optional: true;
}
>;
inputs: {};
inputs: {
pages: ExtensionInput<
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ConfigurableExtensionDataRef<string, 'core.routing.path', {}>
| ConfigurableExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'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<JSX.Element>;
title?: string;
icon?: IconComponent;
loader?: () => Promise<JSX.Element>;
routeRef?: RouteRef_2;
};
}>;
+1 -1
View File
@@ -258,7 +258,6 @@ export const EntityContentBlueprint: ExtensionBlueprint<{
};
output:
| ExtensionDataRef<string, 'core.routing.path', {}>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
RouteRef<AnyRouteRefParams>,
'core.routing.ref',
@@ -266,6 +265,7 @@ export const EntityContentBlueprint: ExtensionBlueprint<{
optional: true;
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
(entity: Entity) => boolean,
'catalog.entity-filter-function',
@@ -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<string, 'core.routing.path', {}>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
string,
'core.title',
{
optional: true;
}
>
| ExtensionDataRef<
IconComponent,
'core.icon',
{
optional: true;
}
>;
inputs: {};
inputs: {
pages: ExtensionInput<
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ConfigurableExtensionDataRef<string, 'core.routing.path', {}>
| ConfigurableExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'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<JSX.Element>;
title?: string;
icon?: IconComponent;
loader?: () => Promise<JSX.Element>;
routeRef?: RouteRef_2;
};
}>;
@@ -104,7 +148,6 @@ export const unprocessedEntitiesDevToolsContent: OverridableExtensionDefinition<
};
output:
| ExtensionDataRef<string, 'core.routing.path', {}>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'core.routing.ref',
@@ -112,6 +155,7 @@ export const unprocessedEntitiesDevToolsContent: OverridableExtensionDefinition<
optional: true;
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<string, 'core.title', {}>;
inputs: {};
params: DevToolsContentBlueprintParams;
+87 -5
View File
@@ -756,7 +756,6 @@ const _default: OverridableFrontendPlugin<
};
output:
| ExtensionDataRef<string, 'core.routing.path', {}>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'core.routing.ref',
@@ -764,6 +763,7 @@ const _default: OverridableFrontendPlugin<
optional: true;
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| 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<string, 'core.routing.path', {}>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
string,
'core.title',
{
optional: true;
}
>
| ExtensionDataRef<
IconComponent,
'core.icon',
{
optional: true;
}
>;
inputs: {
pages: ExtensionInput<
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ConfigurableExtensionDataRef<string, 'core.routing.path', {}>
| ConfigurableExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ConfigurableExtensionDataRef<
string,
'core.title',
{
optional: true;
}
>,
{
singleton: false;
optional: false;
internal: false;
}
>;
filters: ExtensionInput<
ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>,
{
@@ -1035,7 +1074,9 @@ const _default: OverridableFrontendPlugin<
params: {
defaultPath?: [Error: `Use the 'path' param instead`];
path: string;
loader: () => Promise<JSX.Element>;
title?: string;
icon?: IconComponent;
loader?: () => Promise<JSX.Element>;
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<string, 'core.routing.path', {}>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
string,
'core.title',
{
optional: true;
}
>
| ExtensionDataRef<
IconComponent,
'core.icon',
{
optional: true;
}
>;
inputs: {
pages: ExtensionInput<
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ConfigurableExtensionDataRef<string, 'core.routing.path', {}>
| ConfigurableExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'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<JSX.Element>;
title?: string;
icon?: IconComponent;
loader?: () => Promise<JSX.Element>;
routeRef?: RouteRef_2;
};
}>;
+1 -1
View File
@@ -15,7 +15,6 @@ export const DevToolsContentBlueprint: ExtensionBlueprint<{
params: DevToolsContentBlueprintParams;
output:
| ExtensionDataRef<string, 'core.routing.path', {}>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
RouteRef<AnyRouteRefParams>,
'core.routing.ref',
@@ -23,6 +22,7 @@ export const DevToolsContentBlueprint: ExtensionBlueprint<{
optional: true;
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<string, 'core.title', {}>;
inputs: {};
config: {
+43 -2
View File
@@ -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<string, 'core.routing.path', {}>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
string,
'core.title',
{
optional: true;
}
>
| ExtensionDataRef<
IconComponent,
'core.icon',
{
optional: true;
}
>;
inputs: {
pages: ExtensionInput<
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ConfigurableExtensionDataRef<string, 'core.routing.path', {}>
| ConfigurableExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ConfigurableExtensionDataRef<
string,
'core.title',
{
optional: true;
}
>,
{
singleton: false;
optional: false;
internal: false;
}
>;
contents: ExtensionInput<
| ConfigurableExtensionDataRef<string, 'core.title', {}>
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
@@ -101,7 +140,9 @@ const _default: OverridableFrontendPlugin<
params: {
defaultPath?: [Error: `Use the 'path' param instead`];
path: string;
loader: () => Promise<JSX.Element>;
title?: string;
icon?: IconComponent;
loader?: () => Promise<JSX.Element>;
routeRef?: RouteRef_2;
};
}>;
+43 -2
View File
@@ -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<string, 'core.routing.path', {}>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
RouteRef<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
string,
'core.title',
{
optional: true;
}
>
| ExtensionDataRef<
IconComponent,
'core.icon',
{
optional: true;
}
>;
inputs: {
pages: ExtensionInput<
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ConfigurableExtensionDataRef<string, 'core.routing.path', {}>
| ConfigurableExtensionDataRef<
RouteRef<AnyRouteRefParams>,
'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<JSX.Element>;
title?: string;
icon?: IconComponent;
loader?: () => Promise<JSX.Element>;
routeRef?: RouteRef;
};
}>;
+49 -4
View File
@@ -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<string, 'core.routing.path', {}>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'core.routing.ref',
@@ -114,6 +116,7 @@ const _default: OverridableFrontendPlugin<
optional: true;
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| 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<string, 'core.routing.path', {}>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
string,
'core.title',
{
optional: true;
}
>
| ExtensionDataRef<
IconComponent,
'core.icon',
{
optional: true;
}
>;
inputs: {};
inputs: {
pages: ExtensionInput<
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ConfigurableExtensionDataRef<string, 'core.routing.path', {}>
| ConfigurableExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'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<JSX.Element>;
title?: string;
icon?: IconComponent;
loader?: () => Promise<JSX.Element>;
routeRef?: RouteRef_2;
};
}>;
+48 -3
View File
@@ -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<string, 'core.routing.path', {}>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
string,
'core.title',
{
optional: true;
}
>
| ExtensionDataRef<
IconComponent,
'core.icon',
{
optional: true;
}
>;
inputs: {};
inputs: {
pages: ExtensionInput<
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ConfigurableExtensionDataRef<string, 'core.routing.path', {}>
| ConfigurableExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'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<JSX.Element>;
title?: string;
icon?: IconComponent;
loader?: () => Promise<JSX.Element>;
routeRef?: RouteRef_2;
};
}>;
+48 -3
View File
@@ -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<string, 'core.routing.path', {}>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
string,
'core.title',
{
optional: true;
}
>
| ExtensionDataRef<
IconComponent,
'core.icon',
{
optional: true;
}
>;
inputs: {};
inputs: {
pages: ExtensionInput<
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ConfigurableExtensionDataRef<string, 'core.routing.path', {}>
| ConfigurableExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'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<JSX.Element>;
title?: string;
icon?: IconComponent;
loader?: () => Promise<JSX.Element>;
routeRef?: RouteRef_2;
};
}>;
+43 -2
View File
@@ -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<string, 'core.routing.path', {}>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
string,
'core.title',
{
optional: true;
}
>
| ExtensionDataRef<
IconComponent,
'core.icon',
{
optional: true;
}
>;
inputs: {
pages: ExtensionInput<
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ConfigurableExtensionDataRef<string, 'core.routing.path', {}>
| ConfigurableExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ConfigurableExtensionDataRef<
string,
'core.title',
{
optional: true;
}
>,
{
singleton: false;
optional: false;
internal: false;
}
>;
formFields: ExtensionInput<
ConfigurableExtensionDataRef<
() => Promise<FormField>,
@@ -227,7 +266,9 @@ const _default: OverridableFrontendPlugin<
params: {
defaultPath?: [Error: `Use the 'path' param instead`];
path: string;
loader: () => Promise<JSX.Element>;
title?: string;
icon?: IconComponent;
loader?: () => Promise<JSX.Element>;
routeRef?: RouteRef_2;
};
}>;
+86 -4
View File
@@ -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<string, 'core.routing.path', {}>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
RouteRef<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
string,
'core.title',
{
optional: true;
}
>
| ExtensionDataRef<
IconComponent,
'core.icon',
{
optional: true;
}
>;
inputs: {
pages: ExtensionInput<
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ConfigurableExtensionDataRef<string, 'core.routing.path', {}>
| ConfigurableExtensionDataRef<
RouteRef<AnyRouteRefParams>,
'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<JSX.Element>;
title?: string;
icon?: IconComponent;
loader?: () => Promise<JSX.Element>;
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<string, 'core.routing.path', {}>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
RouteRef<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
string,
'core.title',
{
optional: true;
}
>
| ExtensionDataRef<
IconComponent,
'core.icon',
{
optional: true;
}
>;
inputs: {
pages: ExtensionInput<
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ConfigurableExtensionDataRef<string, 'core.routing.path', {}>
| ConfigurableExtensionDataRef<
RouteRef<AnyRouteRefParams>,
'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<JSX.Element>;
title?: string;
icon?: IconComponent;
loader?: () => Promise<JSX.Element>;
routeRef?: RouteRef;
};
}>;
+89 -6
View File
@@ -139,7 +139,6 @@ const _default: OverridableFrontendPlugin<
};
output:
| ExtensionDataRef<string, 'core.routing.path', {}>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'core.routing.ref',
@@ -147,6 +146,7 @@ const _default: OverridableFrontendPlugin<
optional: true;
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| 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<string, 'core.routing.path', {}>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
string,
'core.title',
{
optional: true;
}
>
| ExtensionDataRef<
IconComponent,
'core.icon',
{
optional: true;
}
>;
inputs: {};
inputs: {
pages: ExtensionInput<
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ConfigurableExtensionDataRef<string, 'core.routing.path', {}>
| ConfigurableExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'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<JSX.Element>;
title?: string;
icon?: IconComponent;
loader?: () => Promise<JSX.Element>;
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<string, 'core.routing.path', {}>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
string,
'core.title',
{
optional: true;
}
>
| ExtensionDataRef<
IconComponent,
'core.icon',
{
optional: true;
}
>;
inputs: {
pages: ExtensionInput<
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ConfigurableExtensionDataRef<string, 'core.routing.path', {}>
| ConfigurableExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'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<JSX.Element>;
title?: string;
icon?: IconComponent;
loader?: () => Promise<JSX.Element>;
routeRef?: RouteRef_2;
};
}>;
+43 -2
View File
@@ -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<string, 'core.routing.path', {}>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
RouteRef<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
string,
'core.title',
{
optional: true;
}
>
| ExtensionDataRef<
IconComponent,
'core.icon',
{
optional: true;
}
>;
inputs: {
pages: ExtensionInput<
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ConfigurableExtensionDataRef<string, 'core.routing.path', {}>
| ConfigurableExtensionDataRef<
RouteRef<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ConfigurableExtensionDataRef<
string,
'core.title',
{
optional: true;
}
>,
{
singleton: false;
optional: false;
internal: false;
}
>;
providerSettings: ExtensionInput<
ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>,
{
@@ -75,7 +114,9 @@ const _default: OverridableFrontendPlugin<
params: {
defaultPath?: [Error: `Use the 'path' param instead`];
path: string;
loader: () => Promise<JSX.Element>;
title?: string;
icon?: IconComponent;
loader?: () => Promise<JSX.Element>;
routeRef?: RouteRef;
};
}>;