- c5f88b5: BREAKING: Remove deprecated
source
property from the AppNodeSpec
type, use AppNodeSpec.plugin
instead.
- e4ddf22: BREAKING: The
defaultPath
param of PageBlueprint
has been renamed to path
. This change does not affect the compatibility of extensions created with older versions of this blueprint.
fda1bbc: BREAKING: The component system has been overhauled to use SwappableComponent
instead of ComponentRef
. Several APIs have been removed and replaced:
- Removed:
createComponentRef
, createComponentExtension
, ComponentRef
, ComponentsApi
, componentsApiRef
, useComponentRef
, coreComponentRefs
- Added:
createSwappableComponent
, SwappableComponentBlueprint
, SwappableComponentRef
, SwappableComponentsApi
, swappableComponentsApiRef
BREAKING: The default componentRefs
and exported Core*Props
have been removed and have replacement SwappableComponents
and revised type names instead.
- The
errorBoundaryFallback
component and CoreErrorBoundaryFallbackProps
type have been replaced with ErrorDisplay
swappable component and CoreErrorDisplayProps
respectively.
- The
progress
component and CoreProgressProps
type have been replaced with Progress
swappable component and ProgressProps
respectively.
- The
notFoundErrorPage
component and CoreNotFoundErrorPageProps
type have been replaced with NotFoundErrorPage
swappable component and NotFoundErrorPageProps
respectively.
Migration for creating swappable components:
// OLD: Using createComponentRef and createComponentExtension
import {
createComponentRef,
createComponentExtension,
} from '@backstage/frontend-plugin-api';
const myComponentRef = createComponentRef<{ title: string }>({
id: 'my-plugin.my-component',
});
const myComponentExtension = createComponentExtension({
ref: myComponentRef,
loader: {
lazy: () => import('./MyComponent').then(m => m.MyComponent),
},
});
// NEW: Using createSwappableComponent and SwappableComponentBlueprint
import {
createSwappableComponent,
SwappableComponentBlueprint,
} from '@backstage/frontend-plugin-api';
const MySwappableComponent = createSwappableComponent({
id: 'my-plugin.my-component',
loader: () => import('./MyComponent').then(m => m.MyComponent),
});
const myComponentExtension = SwappableComponentBlueprint.make({
name: 'my-component',
params: {
component: MySwappableComponent,
loader: () => import('./MyComponent').then(m => m.MyComponent),
},
});
Migration for using components:
```tsx
// OLD: Using ComponentsApi and useComponentRef
import {
useComponentRef,
componentsApiRef,
useApi,
coreComponentRefs,
} from '@backstage/frontend-plugin-api';
const MyComponent = useComponentRef(myComponentRef);
const ProgressComponent = useComponentRef(coreComponentRefs.progress);
// NEW: Direct component usage
import { Progress } from '@backstage/frontend-plugin-api';
// Use directly as React Component
<Progress />
<MySwappableComponent title="Hello World" />
- 6a75e00: BREAKING: Removed the deprecated
createFrontendPlugin
variant where the plugin ID is passed via an id
option. To update existing code, switch to using the pluginId
option instead.
- 12b6db7: BREAKING: Added a new
OverridableFrontendPlugin
type that is used as the return value of createFrontendPlugin
. This type includes the withOverrides
and .getExtension
methods that are helpful when creating plugin overrides, while the base FrontendPlugin
type no longer includes these methods. This is a breaking change for the AppTreeApi
and some other places where the FrontendPlugin
type is still used, but also fixes some cases where the extra plugin methods were causing issues.
- 37f2989: BREAKING: Removed the
routable
property from ExtensionBoundary
. This property was never needed in practice and is instead inferred from whether or not the extension outputs a route reference. It can be safely removed.
- 1e6410b: BREAKING: The
ResolveInputValueOverrides
type is no longer exported.
29786f6: BREAKING: The NavLogoBlueprint
has been removed and replaced by NavContentBlueprint
, which instead replaces the entire navbar. The default navbar has also been switched to a more minimal implementation.
To use NavContentBlueprint
to install new logos, you can use it as follows:
NavContentBlueprint.make({
params: {
component: ({ items }) => {
return compatWrapper(
<Sidebar>
<SidebarLogo />
{/* Other sidebar content */}
<SidebarScrollWrapper>
{items.map((item, index) => (
<SidebarItem {...item} key={index} />
))}
</SidebarScrollWrapper>
{/* Other sidebar content */}
</Sidebar>,
);
},
},
});
3243fa6: BREAKING: Removed the ability to define a default extension name
in blueprints. This option had no practical purpose as blueprints already use the kind
to identity the source of the extension.
- a082429: BREAKING: The separate
RouteResolutionApiResolveOptions
type has been removed.
5d31d66: BREAKING: In an attempt to align some of the API's around providing components to Blueprints
, we've renamed the parameters for both the RouterBlueprint
and AppRootWrapperBlueprint
from Component
to component
.
// old
RouterBlueprint.make({
params: {
Component: ({ children }) => <div>{children}</div>,
},
});
// new
RouterBlueprint.make({
params: {
component: ({ children }) => <div>{children}</div>,
},
});
// old
AppRootWrapperBlueprint.make({
params: {
Component: ({ children }) => <div>{children}</div>,
},
});
// new
AppRootWrapperBlueprint.make({
params: {
component: ({ children }) => <div>{children}</div>,
},
});
As part of this change, the type for component
has also changed from ComponentType<PropsWithChildren<{}>>
to (props: { children: ReactNode }) => JSX.Element | null
which is not breaking, just a little more reflective of the actual expected component.
45ead4a: BREAKING: The AnyRoutes
and AnyExternalRoutes
types have been removed and their usage has been inlined instead.
Existing usage can be replaced according to their previous definitions:
type AnyRoutes = { [name in string]: RouteRef | SubRouteRef };
type AnyExternalRoutes = { [name in string]: ExternalRouteRef };
805c298: BREAKING: The ApiBlueprint
has been updated to use the new advanced type parameters through the new defineParams
blueprint option. This is an immediate breaking change that requires all existing usages of ApiBlueprint
to switch to the new callback format. Existing extensions created with the old format are still compatible with the latest version of the plugin API however, meaning that this does not break existing plugins.
To update existing usages of ApiBlueprint
, you remove the outer level of the params
object and replace createApiFactory(...)
with defineParams => defineParams(...)
.
For example, the following old usage:
ApiBlueprint.make({
name: 'error',
params: {
factory: createApiFactory({
api: errorApiRef,
deps: { alertApi: alertApiRef },
factory: ({ alertApi }) => {
return ...;
},
})
},
})
is migrated to the following:
ApiBlueprint.make({
name: 'error',
params: defineParams =>
defineParams({
api: errorApiRef,
deps: { alertApi: alertApiRef },
factory: ({ alertApi }) => {
return ...;
},
}),
})
805c298: Added support for advanced parameter types in extension blueprints. The primary purpose of this is to allow extension authors to use type inference in the definition of the blueprint parameters. This often removes the need for extra imports and improves discoverability of blueprint parameters.
This feature is introduced through the new defineParams
option of createExtensionBlueprint
, along with accompanying createExtensionBlueprintParams
function to help implement the new format.
The following is an example of how to create an extension blueprint that uses the new option:
const ExampleBlueprint = createExtensionBlueprint({
kind: 'example',
attachTo: { id: 'example', input: 'example' },
output: [exampleComponentDataRef, exampleFetcherDataRef],
defineParams<T>(params: {
component(props: ExampleProps<T>): JSX.Element | null;
fetcher(options: FetchOptions): Promise<FetchResult<T>>;
}) {
// The returned params must be wrapped with `createExtensionBlueprintParams`
return createExtensionBlueprintParams(params);
},
*factory(params) {
// These params are now inferred
yield exampleComponentDataRef(params.component);
yield exampleFetcherDataRef(params.fetcher);
},
});
Usage of the above example looks as follows:
const example = ExampleBlueprint.make({
params: defineParams => defineParams({
component: ...,
fetcher: ...,
}),
});
This defineParams => defineParams(<params>)
is also known as the "callback syntax" and is required if a blueprint is created with the new defineParams
option. The callback syntax can also optionally be used for other blueprints too, which means that it is not a breaking change to remove the defineParams
option, as long as the external parameter types remain compatible.
121899a: BREAKING: The element
param for AppRootElementBlueprint
no longer accepts a component. If you are currently passing a component such as element: () => <MyComponent />
or element: MyComponent
, simply switch to element: <MyComponent />
.
- a321f3b: BREAKING: The
CommonAnalyticsContext
has been removed, and inlined into AnalyticsContextValue
instead.
d9e00e3: Add support for a new aliasFor
option for createRouteRef
. This allows for the creation of a new route ref that acts as an alias for an existing route ref that is installed in the app. This is particularly useful when creating modules that override existing plugin pages, without referring to the existing plugin. For example:
export default createFrontendModule({
pluginId: 'catalog',
extensions: [
PageBlueprint.make({
params: {
defaultPath: '/catalog',
routeRef: createRouteRef({ aliasFor: 'catalog.catalogIndex' }),
loader: () =>
import('./CustomCatalogIndexPage').then(m => (
<m.CustomCatalogIndexPage />
)),
},
}),
],
});
93b5e38: Plugins should now use the new AnalyticsImplementationBlueprint
to define and provide concrete analytics implementations. For example:
import { AnalyticsImplementationBlueprint } from '@backstage/frontend-plugin-api';
const AcmeAnalytics = AnalyticsImplementationBlueprint.make({
name: 'acme-analytics',
params: define =>
define({
deps: { config: configApiRef },
factory: ({ config }) => AcmeAnalyticsImpl.fromConfig(config),
}),
});
948de17: Tweaked the return types from createExtension
and createExtensionBlueprint
to avoid the forwarding of ConfigurableExtensionDataRef
into exported types.
- 147482b: Updated the recommended naming of the blueprint param callback from
define
to defineParams
, making the syntax defineParams => defineParams(...)
.
- 3c3c882: Added added defaults for all type parameters of
ExtensionDataRef
and deprecated AnyExtensionDataRef
, as it is now redundant.
- 9831f4e: Adjusted the dialog API types to have more sensible defaults
- 1c2cc37: Improved runtime error message clarity when extension factories don't return an iterable object.
- 24558f0: Added inline documentation for
createExtension
, createExtensionBlueprint
, createFrontendPlugin
, and createFrontendModule
.
- Updated dependencies
- @backstage/core-components@0.17.5
fda1bbc: BREAKING: The component system has been overhauled to use SwappableComponent
instead of ComponentRef
. Several APIs have been removed and replaced:
- Removed:
createComponentRef
, createComponentExtension
, ComponentRef
, ComponentsApi
, componentsApiRef
, useComponentRef
, coreComponentRefs
- Added:
createSwappableComponent
, SwappableComponentBlueprint
, SwappableComponentRef
, SwappableComponentsApi
, swappableComponentsApiRef
BREAKING: The default componentRefs
and exported Core*Props
have been removed and have replacement SwappableComponents
and revised type names instead.
- The
errorBoundaryFallback
component and CoreErrorBoundaryFallbackProps
type have been replaced with ErrorDisplay
swappable component and CoreErrorDisplayProps
respectively.
- The
progress
component and CoreProgressProps
type have been replaced with Progress
swappable component and ProgressProps
respectively.
- The
notFoundErrorPage
component and CoreNotFoundErrorPageProps
type have been replaced with NotFoundErrorPage
swappable component and NotFoundErrorPageProps
respectively.
Migration for creating swappable components:
// OLD: Using createComponentRef and createComponentExtension
import {
createComponentRef,
createComponentExtension,
} from '@backstage/frontend-plugin-api';
const myComponentRef = createComponentRef<{ title: string }>({
id: 'my-plugin.my-component',
});
const myComponentExtension = createComponentExtension({
ref: myComponentRef,
loader: {
lazy: () => import('./MyComponent').then(m => m.MyComponent),
},
});
// NEW: Using createSwappableComponent and SwappableComponentBlueprint
import {
createSwappableComponent,
SwappableComponentBlueprint,
} from '@backstage/frontend-plugin-api';
const MySwappableComponent = createSwappableComponent({
id: 'my-plugin.my-component',
loader: () => import('./MyComponent').then(m => m.MyComponent),
});
const myComponentExtension = SwappableComponentBlueprint.make({
name: 'my-component',
params: {
component: MySwappableComponent,
loader: () => import('./MyComponent').then(m => m.MyComponent),
},
});
Migration for using components:
```tsx
// OLD: Using ComponentsApi and useComponentRef
import {
useComponentRef,
componentsApiRef,
useApi,
coreComponentRefs,
} from '@backstage/frontend-plugin-api';
const MyComponent = useComponentRef(myComponentRef);
const ProgressComponent = useComponentRef(coreComponentRefs.progress);
// NEW: Direct component usage
import { Progress } from '@backstage/frontend-plugin-api';
// Use directly as React Component
<Progress />
<MySwappableComponent title="Hello World" />
- c5f88b5: BREAKING: Remove deprecated
source
property from the AppNodeSpec
type, use AppNodeSpec.plugin
instead.
- e4ddf22: BREAKING: The
defaultPath
param of PageBlueprint
has been renamed to path
. This change does not affect the compatibility of extensions created with older versions of this blueprint.
- 37f2989: BREAKING: Removed the
routable
property from ExtensionBoundary
. This property was never needed in practice and is instead inferred from whether or not the extension outputs a route reference. It can be safely removed.
- 3243fa6: BREAKING: Removed the ability to define a default extension
name
in blueprints. This option had no practical purpose as blueprints already use the kind
to identity the source of the extension.
- a082429: BREAKING: The separate
RouteResolutionApiResolveOptions
type has been removed.
5d31d66: BREAKING: In an attempt to align some of the API's around providing components to Blueprints
, we've renamed the parameters for both the RouterBlueprint
and AppRootWrapperBlueprint
from Component
to component
.
// old
RouterBlueprint.make({
params: {
Component: ({ children }) => <div>{children}</div>,
},
});
// new
RouterBlueprint.make({
params: {
component: ({ children }) => <div>{children}</div>,
},
});
// old
AppRootWrapperBlueprint.make({
params: {
Component: ({ children }) => <div>{children}</div>,
},
});
// new
AppRootWrapperBlueprint.make({
params: {
component: ({ children }) => <div>{children}</div>,
},
});
As part of this change, the type for component
has also changed from ComponentType<PropsWithChildren<{}>>
to (props: { children: ReactNode }) => JSX.Element | null
which is not breaking, just a little more reflective of the actual expected component.
45ead4a: BREAKING: The AnyRoutes
and AnyExternalRoutes
types have been removed and their usage has been inlined instead.
Existing usage can be replaced according to their previous definitions:
type AnyRoutes = { [name in string]: RouteRef | SubRouteRef };
type AnyExternalRoutes = { [name in string]: ExternalRouteRef };
121899a: BREAKING: The element
param for AppRootElementBlueprint
no longer accepts a component. If you are currently passing a component such as element: () => <MyComponent />
or element: MyComponent
, simply switch to element: <MyComponent />
.
- a321f3b: BREAKING: The
CommonAnalyticsContext
has been removed, and inlined into AnalyticsContextValue
instead.
d9e00e3: Add support for a new aliasFor
option for createRouteRef
. This allows for the creation of a new route ref that acts as an alias for an existing route ref that is installed in the app. This is particularly useful when creating modules that override existing plugin pages, without referring to the existing plugin. For example:
export default createFrontendModule({
pluginId: 'catalog',
extensions: [
PageBlueprint.make({
params: {
defaultPath: '/catalog',
routeRef: createRouteRef({ aliasFor: 'catalog.catalogIndex' }),
loader: () =>
import('./CustomCatalogIndexPage').then(m => (
<m.CustomCatalogIndexPage />
)),
},
}),
],
});
93b5e38: Plugins should now use the new AnalyticsImplementationBlueprint
to define and provide concrete analytics implementations. For example:
import { AnalyticsImplementationBlueprint } from '@backstage/frontend-plugin-api';
const AcmeAnalytics = AnalyticsImplementationBlueprint.make({
name: 'acme-analytics',
params: define =>
define({
deps: { config: configApiRef },
factory: ({ config }) => AcmeAnalyticsImpl.fromConfig(config),
}),
});
948de17: Tweaked the return types from createExtension
and createExtensionBlueprint
to avoid the forwarding of ConfigurableExtensionDataRef
into exported types.
- 147482b: Updated the recommended naming of the blueprint param callback from
define
to defineParams
, making the syntax defineParams => defineParams(...)
.
- 3c3c882: Added added defaults for all type parameters of
ExtensionDataRef
and deprecated AnyExtensionDataRef
, as it is now redundant.
- Updated dependencies
- @backstage/core-components@0.17.5-next.1
- @backstage/core-plugin-api@1.10.9
- @backstage/types@1.2.1
- @backstage/version-bridge@1.0.11
29786f6: BREAKING: The NavLogoBlueprint
has been removed and replaced by NavContentBlueprint
, which instead replaces the entire navbar. The default navbar has also been switched to a more minimal implementation.
To use NavContentBlueprint
to install new logos, you can use it as follows:
NavContentBlueprint.make({
params: {
component: ({ items }) => {
return compatWrapper(
<Sidebar>
<SidebarLogo />
{/* Other sidebar content */}
<SidebarScrollWrapper>
{items.map((item, index) => (
<SidebarItem {...item} key={index} />
))}
</SidebarScrollWrapper>
{/* Other sidebar content */}
</Sidebar>,
);
},
},
});
805c298: BREAKING: The ApiBlueprint
has been updated to use the new advanced type parameters through the new defineParams
blueprint option. This is an immediate breaking change that requires all existing usages of ApiBlueprint
to switch to the new callback format. Existing extensions created with the old format are still compatible with the latest version of the plugin API however, meaning that this does not break existing plugins.
To update existing usages of ApiBlueprint
, you remove the outer level of the params
object and replace createApiFactory(...)
with define => define(...)
.
For example, the following old usage:
ApiBlueprint.make({
name: 'error',
params: {
factory: createApiFactory({
api: errorApiRef,
deps: { alertApi: alertApiRef },
factory: ({ alertApi }) => {
return ...;
},
})
},
})
is migrated to the following:
ApiBlueprint.make({
name: 'error',
params: define =>
define({
api: errorApiRef,
deps: { alertApi: alertApiRef },
factory: ({ alertApi }) => {
return ...;
},
}),
})
805c298: Added support for advanced parameter types in extension blueprints. The primary purpose of this is to allow extension authors to use type inference in the definition of the blueprint parameters. This often removes the need for extra imports and improves discoverability of blueprint parameters.
This feature is introduced through the new defineParams
option of createExtensionBlueprint
, along with accompanying createExtensionBlueprintParams
function to help implement the new format.
The following is an example of how to create an extension blueprint that uses the new option:
const ExampleBlueprint = createExtensionBlueprint({
kind: 'example',
attachTo: { id: 'example', input: 'example' },
output: [exampleComponentDataRef, exampleFetcherDataRef],
defineParams<T>(params: {
component(props: ExampleProps<T>): JSX.Element | null;
fetcher(options: FetchOptions): Promise<FetchResult<T>>;
}) {
// The returned params must be wrapped with `createExtensionBlueprintParams`
return createExtensionBlueprintParams(params);
},
*factory(params) {
// These params are now inferred
yield exampleComponentDataRef(params.component);
yield exampleFetcherDataRef(params.fetcher);
},
});
Usage of the above example looks as follows:
const example = ExampleBlueprint.make({
params: define => define({
component: ...,
fetcher: ...,
}),
});
This define => define(<params>)
is also known as the "callback syntax" and is required if a blueprint is created with the new defineParams
option. The callback syntax can also optionally be used for other blueprints too, which means that it is not a breaking change to remove the defineParams
option, as long as the external parameter types remain compatible.
- 0169b23: Internal tweak to avoid circular dependencies
9e3868f: Added a new optional info
option to createFrontendPlugin
that lets you provide a loaders for different sources of metadata information about the plugin.
There are two available loaders. The first one is info.packageJson
, which can be used to point to a package.json
file for the plugin. This is recommended for any plugin that is defined within its own package, especially all plugins that are published to a package registry. Typical usage looks like this:
export default createFrontendPlugin({
pluginId: '...',
info: {
packageJson: () => import('../package.json'),
},
});
The second loader is info.manifest
, which can be used to point to an opaque plugin manifest. This MUST ONLY be used by plugins that are intended for use within a single organization. Plugins that are published to an open package registry should NOT use this loader. The loader is useful for adding additional internal metadata associated with the plugin, and it is up to the Backstage app to decide how these manifests are parsed and used. The default manifest parser in an app created with createApp
from @backstage/frontend-defaults
is able to parse the default catalog-info.yaml
format and built-in fields such as spec.owner
.
Typical usage looks like this:
export default createFrontendPlugin({
pluginId: '...',
info: {
manifest: () => import('../catalog-info.yaml'),
},
});
6f48f71: Added a new useAppNode
hook, which can be used to get a reference to the AppNode
from by the closest ExtensionBoundary
.
- Updated dependencies
- @backstage/core-components@0.17.3
- @backstage/core-plugin-api@1.10.8
- @backstage/types@1.2.1
- @backstage/version-bridge@1.0.11
9e3868f: Added a new optional info
option to createFrontendPlugin
that lets you provide a loaders for different sources of metadata information about the plugin.
There are two available loaders. The first one is info.packageJson
, which can be used to point to a package.json
file for the plugin. This is recommended for any plugin that is defined within its own package, especially all plugins that are published to a package registry. Typical usage looks like this:
export default createFrontendPlugin({
pluginId: '...',
info: {
packageJson: () => import('../package.json'),
},
});
The second loader is info.manifest
, which can be used to point to an opaque plugin manifest. This MUST ONLY be used by plugins that are intended for use within a single organization. Plugins that are published to an open package registry should NOT use this loader. The loader is useful for adding additional internal metadata associated with the plugin, and it is up to the Backstage app to decide how these manifests are parsed and used. The default manifest parser in an app created with createApp
from @backstage/frontend-defaults
is able to parse the default catalog-info.yaml
format and built-in fields such as spec.owner
.
Typical usage looks like this:
export default createFrontendPlugin({
pluginId: '...',
info: {
manifest: () => import('../catalog-info.yaml'),
},
});
6f48f71: Added a new useAppNode
hook, which can be used to get a reference to the AppNode
from by the closest ExtensionBoundary
.
- 2bb9517: Introduce the
@backstage/plugin-app
package to hold all of the built-in extensions for easy consumption and overriding.
c816e2d: Added createFrontendModule
as a replacement for createExtensionOverrides
, which is now deprecated.
Deprecated the BackstagePlugin
and FrontendFeature
type in favor of FrontendPlugin
and FrontendFeature
from @backstage/frontend-app-api
respectively.
52f9c5a: Deprecated the namespace
option for createExtensionBlueprint
and createExtension
, these are no longer required and will default to the pluginId
instead.
You can migrate some of your extensions that use createExtensionOverrides
to using createFrontendModule
instead and providing a pluginId
there.
// Before
createExtensionOverrides({
extensions: [
createExtension({
name: 'my-extension',
namespace: 'my-namespace',
kind: 'test',
...
})
],
});
// After
createFrontendModule({
pluginId: 'my-namespace',
extensions: [
createExtension({
name: 'my-extension',
kind: 'test',
...
})
],
});
f3a2b91: Moved several implementations of built-in APIs from being hardcoded in the app to instead be provided as API extensions. This moves all API-related inputs from the app
extension to the respective API extensions. For example, extensions created with ThemeBlueprint
are now attached to the themes
input of api:app-theme
rather than the app
extension.
- 836127c: Updated dependency
@testing-library/react
to ^16.0.0
.
- 948d431: Removing deprecated
namespace
parameter in favour of pluginId
instead
- 043d7cd: Internal refactor
- 220f4f7: Remove unnecessary config object on IconBundleBlueprint
- 2a61422: The
factory
option is no longer required when overriding an extension.
98850de: Added support for defining replaces
in createExtensionInput
which will allow extensions to redirect missing attachTo
points to an input of the created extension.
export const AppThemeApi = ApiBlueprint.makeWithOverrides({
name: 'app-theme',
inputs: {
themes: createExtensionInput([ThemeBlueprint.dataRefs.theme], {
// attachTo: { id: 'app', input: 'themes'} will be redirected to this input instead
replaces: [{ id: 'app', input: 'themes' }],
}),
},
factory: () {
...
}
});
4a66456: A new apis
parameter has been added to factory
for extensions. This is a way to access utility APIs without being coupled to the React context.
- Updated dependencies
- @backstage/core-components@0.15.0
- @backstage/core-plugin-api@1.9.4
- @backstage/version-bridge@1.0.9
- @backstage/types@1.1.1
- 6f72c2b: Fixing issue with extension blueprints
inputs
merging.
- 210d066: Added support for using the
params
in other properties of the createExtensionBlueprint
options by providing a callback.
- 9b356dc: Renamed
createPlugin
to createFrontendPlugin
. The old symbol is still exported but deprecated.
- a376559: Correct the
TConfig
type of data references to only contain config
4e53ad6: Introduce a new way to encapsulate extension kinds that replaces the extension creator pattern with createExtensionBlueprint
This allows the creation of extension instances with the following pattern:
// create the extension blueprint which is used to create instances
const EntityCardBlueprint = createExtensionBlueprint({
kind: 'entity-card',
attachTo: { id: 'test', input: 'default' },
output: [coreExtensionData.reactElement],
factory(params: { text: string }) {
return [coreExtensionData.reactElement(<h1>{params.text}</h1>)];
},
});
// create an instance of the extension blueprint with params
const testExtension = EntityCardBlueprint.make({
name: 'foo',
params: {
text: 'Hello World',
},
});
9b89b82: The ExtensionBoundary
now by default infers whether it's routable from whether it outputs a route path.
- e493020: Deprecated
inputs
and configSchema
options for createComponentExtenion
, these will be removed in a future release
7777b5f: Added a new IconBundleBlueprint
that lets you create icon bundle extensions that can be installed in an App in order to override or add new app icons.
import { IconBundleBlueprint } from '@backstage/frontend-plugin-api';
const exampleIconBundle = IconBundleBlueprint.make({
name: 'example-bundle',
params: {
icons: {
user: MyOwnUserIcon,
},
},
});
99abb6b: Support overriding of plugin extensions using the new plugin.withOverrides
method.
import homePlugin from '@backstage/plugin-home';
export default homePlugin.withOverrides({
extensions: [
homePage.getExtension('page:home').override({
*factory(originalFactory) {
yield* originalFactory();
yield coreExtensionData.reactElement(<h1>My custom home page</h1>);
},
}),
],
});
813cac4: Add an ExtensionBoundary.lazy
function to create properly wrapped lazy-loading enabled elements, suitable for use with coreExtensionData.reactElement
. The page blueprint now automatically leverages this.
- a65cfc8: Add support for accessing extensions definitions provided by a plugin via
plugin.getExtension(...)
. For this to work the extensions must be defined using the v2 format, typically using an extension blueprint.
3be9aeb: Extensions have been changed to be declared with an array of inputs and outputs, rather than a map of named data refs. This change was made to reduce confusion around the role of the input and output names, as well as enable more powerful APIs for overriding extensions.
An extension that was previously declared like this:
const exampleExtension = createExtension({
name: 'example',
inputs: {
items: createExtensionInput({
element: coreExtensionData.reactElement,
}),
},
output: {
element: coreExtensionData.reactElement,
},
factory({ inputs }) {
return {
element: (
<div>
Example
{inputs.items.map(item => {
return <div>{item.output.element}</div>;
})}
</div>
),
};
},
});
Should be migrated to the following:
const exampleExtension = createExtension({
name: 'example',
inputs: {
items: createExtensionInput([coreExtensionData.reactElement]),
},
output: [coreExtensionData.reactElement],
factory({ inputs }) {
return [
coreExtensionData.reactElement(
<div>
Example
{inputs.items.map(item => {
return <div>{item.get(coreExtensionData.reactElement)}</div>;
})}
</div>,
),
];
},
});
34f1b2a: Support merging of inputs
in extension blueprints, but stop merging output
. In addition, the original factory in extension blueprints now returns a data container that both provides access to the returned data, but can also be forwarded as output.
- 3fb421d: Added support to be able to define
zod
config schema in Blueprints, with built in schema merging from the Blueprint and the extension instances.
2d21599: Added support for being able to override extension definitions.
const TestCard = EntityCardBlueprint.make({
...
});
TestCard.override({
// override attachment points
attachTo: { id: 'something-else', input: 'overridden' },
// extend the config schema
config: {
schema: {
newConfig: z => z.string().optional(),
}
},
// override factory
*factory(originalFactory, { inputs, config }){
const originalOutput = originalFactory();
yield coreExentsionData.reactElement(
<Wrapping>
{originalOutput.get(coreExentsionData.reactElement)}
</Wrapping>
);
}
});
31bfc44: Extension data references can now be defined in a way that encapsulates the ID string in the type, in addition to the data type itself. The old way of creating extension data references is deprecated and will be removed in a future release.
For example, the following code:
export const myExtension =
createExtensionDataRef<MyType>('my-plugin.my-data');
Should be updated to the following:
export const myExtension = createExtensionDataRef<MyType>().with({
id: 'my-plugin.my-data',
});
6349099: Added config input type to the extensions
- Updated dependencies
- @backstage/core-components@0.14.10
- @backstage/core-plugin-api@1.9.3
- @backstage/types@1.1.1
- @backstage/version-bridge@1.0.8
3be9aeb: Extensions have been changed to be declared with an array of inputs and outputs, rather than a map of named data refs. This change was made to reduce confusion around the role of the input and output names, as well as enable more powerful APIs for overriding extensions.
An extension that was previously declared like this:
const exampleExtension = createExtension({
name: 'example',
inputs: {
items: createExtensionInput({
element: coreExtensionData.reactElement,
}),
},
output: {
element: coreExtensionData.reactElement,
},
factory({ inputs }) {
return {
element: (
<div>
Example
{inputs.items.map(item => {
return <div>{item.output.element}</div>;
})}
</div>
),
};
},
});
Should be migrated to the following:
const exampleExtension = createExtension({
name: 'example',
inputs: {
items: createExtensionInput([coreExtensionData.reactElement]),
},
output: [coreExtensionData.reactElement],
factory({ inputs }) {
return [
coreExtensionData.reactElement(
<div>
Example
{inputs.items.map(item => {
return <div>{item.get(coreExtensionData.reactElement)}</div>;
})}
</div>,
),
];
},
});
3fb421d: Added support to be able to define zod
config schema in Blueprints, with built in schema merging from the Blueprint and the extension instances.
- 6349099: Added config input type to the extensions
- Updated dependencies
- @backstage/core-components@0.14.10-next.0
- @backstage/core-plugin-api@1.9.3
- @backstage/types@1.1.1
- @backstage/version-bridge@1.0.8
4e53ad6: Introduce a new way to encapsulate extension kinds that replaces the extension creator pattern with createExtensionBlueprint
This allows the creation of extension instances with the following pattern:
// create the extension blueprint which is used to create instances
const EntityCardBlueprint = createExtensionBlueprint({
kind: 'entity-card',
attachTo: { id: 'test', input: 'default' },
output: {
element: coreExtensionData.reactElement,
},
factory(params: { text: string }) {
return {
element: <h1>{params.text}</h1>,
};
},
});
// create an instance of the extension blueprint with params
const testExtension = EntityCardBlueprint.make({
name: 'foo',
params: {
text: 'Hello World',
},
});
9b89b82: The ExtensionBoundary
now by default infers whether it's routable from whether it outputs a route path.
7777b5f: Added a new IconBundleBlueprint
that lets you create icon bundle extensions that can be installed in an App in order to override or add new app icons.
import { IconBundleBlueprint } from '@backstage/frontend-plugin-api';
const exampleIconBundle = IconBundleBlueprint.make({
name: 'example-bundle',
params: {
icons: {
user: MyOwnUserIcon,
},
},
});
31bfc44: Extension data references can now be defined in a way that encapsulates the ID string in the type, in addition to the data type itself. The old way of creating extension data references is deprecated and will be removed in a future release.
For example, the following code:
export const myExtension =
createExtensionDataRef<MyType>('my-plugin.my-data');
Should be updated to the following:
export const myExtension = createExtensionDataRef<MyType>().with({
id: 'my-plugin.my-data',
});
Updated dependencies
- @backstage/core-components@0.14.10-next.0
- @backstage/core-plugin-api@1.9.3
- @backstage/types@1.1.1
- @backstage/version-bridge@1.0.8