Merge branch 'master' into bui-table

# Conflicts:
#	docs-ui/public/theme-backstage.css
This commit is contained in:
Charles de Dreuille
2025-08-11 10:42:48 +02:00
483 changed files with 9341 additions and 5817 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-openapi-utils': patch
---
Update `express-openapi-validator` to 5.5.8 to fix security vulnerability in transitive dependency `multer`
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-defaults': patch
---
Deprecated `createPublicSignInApp`, which has been replaced by the new `appModulePublicSignIn` from `@backstage/plugin-app/alpha` instead.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-plugin-api': minor
---
**BREAKING**: Remove deprecated `source` property from the `AppNodeSpec` type, use `AppNodeSpec.plugin` instead.
+24
View File
@@ -0,0 +1,24 @@
---
'@backstage/frontend-plugin-api': patch
'@backstage/frontend-app-api': patch
---
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:
```tsx
export default createFrontendModule({
pluginId: 'catalog',
extensions: [
PageBlueprint.make({
params: {
defaultPath: '/catalog',
routeRef: createRouteRef({ aliasFor: 'catalog.catalogIndex' }),
loader: () =>
import('./CustomCatalogIndexPage').then(m => (
<m.CustomCatalogIndexPage />
)),
},
}),
],
});
```
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-plugin-api': minor
---
**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.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-app': patch
---
Default implementations of core components are now provided by this package.
+15
View File
@@ -0,0 +1,15 @@
---
'@backstage/plugin-app': minor
---
**BREAKING**: The `componentsApi` implementation has been removed from the plugin and replaced with the new `SwappableComponentsApi` instead.
If you were overriding the `componentsApi` implementation, you can now use the new `SwappableComponentsApi` instead.
```ts
// old
appPlugin.getExtension('api:app/components').override(...)
// new
appPlugin.getExtension('api:app/swappable-components').override(...)
```
@@ -0,0 +1,96 @@
---
'@backstage/frontend-plugin-api': minor
---
**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:**
```tsx
// 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" />
```
**Migration for core component references:**
```tsx
// OLD: Core component refs
import { coreComponentRefs } from '@backstage/frontend-plugin-api';
coreComponentRefs.progress
coreComponentRefs.notFoundErrorPage
coreComponentRefs.errorBoundaryFallback
// NEW: Direct swappable component imports
import { Progress, NotFoundErrorPage, ErrorDisplay } from '@backstage/frontend-plugin-api';
// Use directly as React components
<Progress />
<NotFoundErrorPage />
<ErrorDisplay plugin={plugin} error={error} resetError={resetError} />
```
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli': minor
---
Added plugin and module templates for the new frontend system. These templates are not included by default, but can be included by adding `@backstage/cli/templates/new-frontend-plugin` and `@backstage/cli/templates/new-frontend-plugin-module` as [custom templates](https://backstage.io/docs/tooling/cli/templates#installing-custom-templates).
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/create-app': patch
---
Bumped create-app version.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-test-utils': patch
---
Updated import of the `FrontendFeature` type.
+21
View File
@@ -0,0 +1,21 @@
---
'@backstage/plugin-catalog-unprocessed-entities': patch
'@backstage/frontend-defaults': patch
'@backstage/core-compat-api': patch
'@backstage/plugin-app-visualizer': patch
'@backstage/plugin-catalog-import': patch
'@backstage/plugin-catalog-graph': patch
'@backstage/plugin-notifications': patch
'@backstage/plugin-user-settings': patch
'@backstage/plugin-search-react': patch
'@backstage/plugin-kubernetes': patch
'@backstage/plugin-scaffolder': patch
'@backstage/plugin-api-docs': patch
'@backstage/plugin-devtools': patch
'@backstage/plugin-techdocs': patch
'@backstage/plugin-catalog': patch
'@backstage/plugin-search': patch
'@backstage/plugin-home': patch
---
Internal update to align with new blueprint parameter naming in the new frontend system.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder-backend-module-gitlab': patch
---
Show additional information about the cause in error messages from GitLab
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-backend': patch
---
Attempt to circumvent event listener memory leak in compression middleware
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-plugin-api': minor
---
**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.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-plugin-api': minor
---
**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.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-plugin-api': minor
---
**BREAKING**: The `ResolveInputValueOverrides` type is no longer exported.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-plugin-api': minor
---
**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.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-app-api': minor
---
Use an app plugin for built-in extension app node specs.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-app': patch
---
Added a new module for implementing public sign-in apps, exported as `appModulePublicSignIn` via the `/alpha` sub-path export. This replaces the `createPublicSignInApp` export from `@backstage/frontend-defaults`, which is now deprecated.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-app-api': minor
---
**BREAKING**: Removed the deprecated `FrontendFeature` type, import it from `@backstage/frontend-plugin-api` instead.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-defaults': minor
---
**BREAKING**: Removed the deprecated `CreateAppFeatureLoader` and support for it in other APIs. Switch existing usage to use the newer `createFrontendFeatureLoader` from `@backstage/frontend-plugin-api` instead.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-plugin-api': minor
---
**BREAKING**: The separate `RouteResolutionApiResolveOptions` type has been removed.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/core-compat-api': minor
---
**BREAKING**: The `componentsApi` implementation has been removed from the plugin and replaced with the new `SwappableComponentsApi` instead. Which means that the `componentsApi` is not longer backwards compatible with legacy plugins.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder-backend': minor
---
Fixed fs:readdir action example
+39
View File
@@ -0,0 +1,39 @@
---
'@backstage/frontend-plugin-api': minor
---
**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`.
```tsx
// old
RouterBlueprint.make({
params: {
Component: ({ children }) => <div>{children}</div>,
},
});
// new
RouterBlueprint.make({
params: {
component: ({ children }) => <div>{children}</div>,
},
});
```
```tsx
// 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.
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/frontend-test-utils': patch
'@backstage/core-compat-api': patch
'@backstage/plugin-app': patch
---
Updated the usage of the `RouterBlueprint` and `AppRootWrapperBlueprint` to use the lowercase `component` parameter
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/ui': patch
---
**Breaking change** Move breadcrumb to fit in the `HeaderPage` instead of the `Header` in Backstage UI.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-app': minor
---
**BREAKING**: The `app-root-element` extension now only accepts `JSX.Element` in its `element` param, meaning overrides need to be updated.
+12
View File
@@ -0,0 +1,12 @@
---
'@backstage/frontend-plugin-api': minor
---
**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:
```ts
type AnyRoutes = { [name in string]: RouteRef | SubRouteRef };
type AnyExternalRoutes = { [name in string]: ExternalRouteRef };
```
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-proxy-backend': patch
---
correct rewrite rule to avoid extra subpath in proxy path
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-app-api': patch
---
Renaming the `getNodesByRoutePath` parameter from `sourcePath` to `routePath`
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/core-compat-api': minor
---
**BREAKING**: The `defaultPath` override of `convertLegacyPageExtension` has been renamed to `path`, in order to align with the same update that was made to the `PageBlueprint`.
+18
View File
@@ -0,0 +1,18 @@
---
'@backstage/frontend-plugin-api': patch
---
Plugins should now use the new `AnalyticsImplementationBlueprint` to define and provide concrete analytics implementations. For example:
```ts
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),
}),
});
```
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-app': patch
---
The default implementation of the Analytics API now collects and instantiates analytics implementations exposed via `AnalyticsImplementationBlueprint` extensions. If no such extensions are discovered, the API continues to do nothing with analytics events fired within Backstage. If multiple such extensions are discovered, every discovered implementation automatically receives analytics events.
+3 -3
View File
@@ -4,7 +4,7 @@
**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(...)`.
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:
@@ -28,8 +28,8 @@ is migrated to the following:
```ts
ApiBlueprint.make({
name: 'error',
params: define =>
define({
params: defineParams =>
defineParams({
api: errorApiRef,
deps: { alertApi: alertApiRef },
factory: ({ alertApi }) => {
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli': minor
---
**BREAKING**: Removed support for `.icon.svg` imports, which have been deprecated since the 1.19 release.
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/plugin-catalog-graph': patch
'@backstage/plugin-api-docs': patch
'@backstage/plugin-org': patch
---
Updated README instructions for the new frontend system
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-plugin-api': patch
---
Tweaked the return types from `createExtension` and `createExtensionBlueprint` to avoid the forwarding of `ConfigurableExtensionDataRef` into exported types.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder': minor
---
Add resizable panels width for the editor and preview panels in the template editor and template form playground layouts. Users can now resize these panels by dragging the divider between the two areas.
+33
View File
@@ -209,41 +209,74 @@
"blue-apples-pump",
"bright-elephants-sparkle",
"calm-geese-serve",
"chatty-coats-sin",
"chatty-dodos-design",
"chatty-schools-post",
"clean-chairs-sit-2",
"clean-chairs-sit",
"clever-plants-warn",
"cold-heads-arrive",
"common-heads-build",
"crazy-pants-exist",
"create-app-1753196727",
"create-app-1754401469",
"cruel-bars-buy",
"cruel-zoos-argue",
"eleven-tigers-drop",
"every-schools-find",
"evil-forks-hang",
"evil-phones-unite",
"fancy-ducks-help",
"fifty-ads-dance",
"five-ducks-hide",
"floppy-groups-hug",
"four-spiders-jump",
"free-months-share",
"fruity-rockets-rhyme",
"full-streets-take",
"funny-brooms-trade",
"funny-dancers-start",
"fuzzy-ducks-speak",
"great-snakes-dress",
"green-lies-invite copy",
"green-lies-invite",
"honest-seas-repeat",
"hot-clowns-behave",
"huge-heads-occur",
"huge-paws-design",
"itchy-doodles-boil",
"little-bugs-care",
"long-grapes-glow",
"lovely-fans-write",
"major-comics-stay",
"mighty-cycles-stay",
"mira-looking-ostrich",
"nej-inte-ostrich",
"nice-actors-cheer",
"nice-buttons-return",
"nice-crabs-clean",
"odd-beans-sell",
"open-seas-ring",
"orange-teams-smell",
"polite-trains-notice",
"quiet-parks-cheer",
"rich-seals-itch",
"ripe-comics-sip",
"sad-cities-lay",
"seven-crabs-stick",
"sixty-clowns-float",
"small-trams-do",
"smart-planes-march",
"spotty-clowns-lose",
"spotty-icons-shake",
"tender-crabs-stay",
"thick-breads-add",
"thirty-dingos-allow",
"thirty-jobs-cut",
"tired-lamps-start",
"twenty-pumas-brush",
"violet-weeks-trade",
"whole-hands-cut",
"wild-apes-care",
"yellow-ducks-burn"
]
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/ui': patch
---
Remove stylesheet import from Select component.
+2 -2
View File
@@ -32,11 +32,11 @@ Usage of the above example looks as follows:
```ts
const example = ExampleBlueprint.make({
params: define => define({
params: defineParams => defineParams({
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.
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.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/core-components': patch
---
Updated dependency `linkifyjs` to `4.3.2`.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-auth-backend-module-okta-provider': patch
---
Updated dependency `@davidzemon/passport-okta-oauth` to `^0.0.7`.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-plugin-api': patch
---
Updated the recommended naming of the blueprint param callback from `define` to `defineParams`, making the syntax `defineParams => defineParams(...)`.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder-backend-module-github': patch
---
Fixed bug in the `customProperties` type which was preventing it being used to set a list of values against a key (e.g. for multi-select fields)
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-events-backend-module-kafka': patch
---
Remove luxon dependency and minor internal improvements
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-plugin-api': patch
---
Added added defaults for all type parameters of `ExtensionDataRef` and deprecated `AnyExtensionDataRef`, as it is now redundant.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/repo-tools': patch
---
Removed build-in ignore of the `packages/canon` package for knip reports.
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/frontend-plugin-api': patch
'@backstage/plugin-app': patch
---
Adjusted the dialog API types to have more sensible defaults
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/ui': patch
---
Add `startCollapsed` prop on the `SearchField` component in BUI.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-react': minor
---
**BREAKING ALPHA**: The `defaultPath`, `defaultTitle`, and `defaultGroup` params of `PageBlueprint` has been renamed to `path`, `title`, and `group`. The `convertLegacyEntityContentExtension` utility has also received the same change. This change does not affect the compatibility of extensions created with older versions of this blueprint.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-plugin-api': minor
---
**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 />`.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-search-backend-module-catalog': patch
---
Allow filter to be an array in config schema
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/frontend-defaults': patch
'@backstage/frontend-app-api': patch
'@backstage/cli': patch
---
Deprecated new frontend system config setting `app.experimental.packages` to just `app.packages`. The old config will continue working for the time being, but may be removed in a future release.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-app-api': patch
---
Added a default implementation of the `SwappableComponentsApi` and removing the legacy `ComponentsApi` implementation
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/create-app': patch
---
Updated the `app.packages` config setting now that it no longer is experimental
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/frontend-plugin-api': patch
'@backstage/frontend-app-api': patch
---
Improved runtime error message clarity when extension factories don't return an iterable object.
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/plugin-signals': patch
'@backstage/plugin-home': patch
---
**BREAKING ALPHA**: The `app-root-element` extension now only accepts `JSX.Element` in its `element` param, meaning overrides need to be updated.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-backend-module-github': patch
---
Fix GitHub catalog entity provider branch matching on event processing.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/core-components': patch
---
Added `FavoriteToggleProps`.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-app-api': minor
---
The `AppNodeSpec.plugin` property is now required.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli': patch
---
The Node.js transform in `@backstage/cli/config/nodeTransformHooks.mjs` now supports the built-in type stripping in Node.js, which is enabled by default from v22.18.0.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-app-api': patch
---
Moved `createSpecializedApp` options to a new `CreateSpecializedAppOptions` type.
+23
View File
@@ -0,0 +1,23 @@
---
'@backstage/frontend-defaults': minor
'@backstage/frontend-app-api': minor
---
**BREAKING**: Restructured some of option fields of `createApp` and `createSpecializedApp`.
- For `createApp`, all option fields _except_ `features` and `bindRoutes` have been moved into a new `advanced` object field.
- For `createSpecializedApp`, all option fields _except_ `features`, `config`, and `bindRoutes` have been moved into a new `advanced` object field.
This helps highlight that some options are meant to rarely be needed or used, and simplifies the usage of those options that are almost always required.
As an example, if you used to supply a custom config loader, you would update your code as follows:
```diff
createApp({
features: [...],
- configLoader: new MyCustomLoader(),
+ advanced: {
+ configLoader: new MyCustomLoader(),
+ },
})
```
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-plugin-api': patch
---
Added inline documentation for `createExtension`, `createExtensionBlueprint`, `createFrontendPlugin`, and `createFrontendModule`.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-plugin-api': minor
---
**BREAKING**: The `CommonAnalyticsContext` has been removed, and inlined into `AnalyticsContextValue` instead.
-1
View File
@@ -28,7 +28,6 @@ yarn.lock @backstage/maintainers @backst
/microsite/static @backstage/maintainers @backstage/documentation-maintainers
/packages @backstage/framework-maintainers
/packages/backend-openapi-utils @backstage/maintainers @backstage/reviewers @backstage/openapi-tooling-maintainers
/packages/canon @backstage/design-system-maintainers
/packages/catalog-client @backstage/catalog-maintainers
/packages/catalog-model @backstage/catalog-maintainers
/packages/cli @backstage/tooling-maintainers
-5
View File
@@ -30,11 +30,6 @@
rangeStrategy: 'replace',
matchSourceUrls: ['https://github.com/microsoft/rushstack{/,}**'],
},
{
groupName: 'SVGR monorepo packages',
rangeStrategy: 'replace',
matchSourceUrls: ['https://github.com/gregberge/svgr{/,}**'],
},
{
groupName: 'Module-federation monorepo packages',
rangeStrategy: 'replace',
@@ -324,6 +324,8 @@ pagerduty
pageview
Pandey
parallelization
param
params
parseable
Patrik
pattison
@@ -378,6 +380,7 @@ replicasets
repo
Repo
repos
requestors
rerender
rerenders
resourcequotas
@@ -385,6 +388,9 @@ retryable
reusability
Reusability
roadmaps
rollbar
rollout
rollouts
Roboto
rollbar
Rollbar
@@ -461,6 +467,7 @@ Superfences
superset
supertype
SVGs
swappable
talkdesk
Talkdesk
Tanzu
@@ -545,3 +552,4 @@ zod
Zolotusky
zoomable
zsh
resizable
@@ -74,7 +74,7 @@ jobs:
- name: Cache Comment
if: ${{ steps.event.outputs.ACTION != 'closed' }}
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
with:
path: comment.md
key: ${{ steps.hash.outputs.COMMENT_FILE_HASH }}
-4
View File
@@ -127,10 +127,6 @@ jobs:
- name: build all packages
run: yarn backstage-cli repo build --all
# For now canon has a custom build script and needs to be built separately
- name: build canon
run: yarn --cwd packages/canon build
# For now BUI has a custom build script and needs to be built separately
- name: build BUI
run: yarn --cwd packages/ui build
-4
View File
@@ -110,10 +110,6 @@ jobs:
- name: build
run: yarn backstage-cli repo build --all
# For now canon has a custom build script and needs to be built separately
- name: build canon
run: yarn --cwd packages/canon build
# For now BUI has a custom build script and needs to be built separately
- name: build BUI
run: yarn --cwd packages/ui build
+2 -2
View File
@@ -39,7 +39,7 @@ jobs:
persist-credentials: false
- name: 'Run analysis'
uses: ossf/scorecard-action@dc50aa9510b46c811795eb24b2f1ba02a914e534 # v2.3.3
uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2
with:
results_file: results.sarif
results_format: sarif
@@ -67,6 +67,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: 'Upload to code-scanning'
uses: github/codeql-action/upload-sarif@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17
uses: github/codeql-action/upload-sarif@51f77329afa6477de8c49fc9c7046c15b9a4e79d # v3.29.5
with:
sarif_file: results.sarif
+4 -4
View File
@@ -1,4 +1,4 @@
name: Sync Canon Docs
name: Sync BUI Docs
on:
push:
branches: [master]
@@ -36,7 +36,7 @@ jobs:
- name: Configure Git
run: |
git config --global user.email noreply@backstage.io
git config --global user.name 'Github Canon Docs workflow'
git config --global user.name 'Github BUI Docs workflow'
- name: Install dependencies
working-directory: docs-ui
@@ -53,9 +53,9 @@ jobs:
git rm -rf .
cp -R ../docs-ui/dist/. .
- name: Commit to canon-storybook repo
- name: Commit to bui-storybook repo
working-directory: bui-external-docs
run: |
git add .
git commit -am "Canon Docs build for backstage/backstage@${{ github.sha }}"
git commit -am "BUI Docs build for backstage/backstage@${{ github.sha }}"
git push
@@ -29,7 +29,7 @@ jobs:
cache-prefix: ${{ runner.os }}-v20.x
- name: Create Snyk report
uses: snyk/actions/node@cdb760004ba9ea4d525f2e043745dfe85bb9077e # master
uses: snyk/actions/node@77490d94e966421e076e95ad8fa87aa55e5ca409 # master
continue-on-error: true # Snyk CLI exits with error when vulnerabilities are found
with:
args: >
+3 -3
View File
@@ -31,7 +31,7 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Monitor and Synchronize Snyk Policies
uses: snyk/actions/node@cdb760004ba9ea4d525f2e043745dfe85bb9077e # master
uses: snyk/actions/node@77490d94e966421e076e95ad8fa87aa55e5ca409 # master
with:
command: monitor
args: >
@@ -46,7 +46,7 @@ jobs:
# Above we run the `monitor` command, this runs the `test` command which is
# the one that generates the SARIF report that we can upload to GitHub.
- name: Create Snyk report
uses: snyk/actions/node@cdb760004ba9ea4d525f2e043745dfe85bb9077e # master
uses: snyk/actions/node@77490d94e966421e076e95ad8fa87aa55e5ca409 # master
continue-on-error: true # To make sure that SARIF upload gets called
with:
args: >
@@ -58,6 +58,6 @@ jobs:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
NODE_OPTIONS: --max-old-space-size=7168
- name: Upload Snyk report
uses: github/codeql-action/upload-sarif@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17
uses: github/codeql-action/upload-sarif@51f77329afa6477de8c49fc9c7046c15b9a4e79d # v3.29.5
with:
sarif_file: snyk.sarif
+3 -3
View File
@@ -55,7 +55,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17
uses: github/codeql-action/init@51f77329afa6477de8c49fc9c7046c15b9a4e79d # v3.29.5
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -66,7 +66,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17
uses: github/codeql-action/autobuild@51f77329afa6477de8c49fc9c7046c15b9a4e79d # v3.29.5
# ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -80,4 +80,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17
uses: github/codeql-action/analyze@51f77329afa6477de8c49fc9c7046c15b9a4e79d # v3.29.5
+1 -1
View File
@@ -40,7 +40,7 @@ These labels indicate which part of Backstage an issue or pull request relates t
- `area:auditor` - Auditor service and it's use in plugins.
- `area:auth` - Authentication and 3rd party authorization.
- `area:catalog` - The Catalog plugin and the Software Catalog model and integrations.
- `area:design-system` - The Canon design system and library.
- `area:design-system` - The Backstage UI design system and library.
- `area:documentation` - Documentation for adopters, users, and developers.
- `area:events` - The Events system and integrations for other plugins.
- `area:framework` - The core Backstage framework.
+1 -2
View File
@@ -1,8 +1,7 @@
app:
title: Backstage Example App
baseUrl: http://localhost:3000
experimental:
packages: all # ✨
packages: all # ✨
#datadogRum:
# clientToken: '123456789'
+6 -6
View File
@@ -12,8 +12,8 @@
"sync:css:watch": "node scripts/sync-css.js --watch"
},
"resolutions": {
"@types/react": "19.1.8",
"@types/react-dom": "19.1.6"
"@types/react": "19.1.9",
"@types/react-dom": "19.1.7"
},
"dependencies": {
"@codemirror/lang-sass": "^6.0.2",
@@ -31,16 +31,16 @@
"next": "15.3.4",
"next-mdx-remote-client": "^2.1.2",
"prop-types": "^15.8.1",
"react": "19.1.0",
"react-dom": "19.1.0",
"react": "19.1.1",
"react-dom": "19.1.1",
"shiki": "^1.26.1",
"storybook": "^8.6.8"
},
"devDependencies": {
"@types/mdx": "^2.0.13",
"@types/node": "^20",
"@types/react": "19.1.8",
"@types/react-dom": "19.1.6",
"@types/react": "19.1.9",
"@types/react-dom": "19.1.7",
"chokidar": "^3.6.0",
"concurrently": "^8.2.2",
"eslint": "^8",
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -65,6 +65,24 @@ export const propDefs: Record<string, PropDef> = {
},
},
},
breadcrumbs: {
type: 'complex',
complexType: {
name: 'Breadcrumb[]',
properties: {
label: {
type: 'string',
required: true,
description: 'Display text for the breadcrumb',
},
href: {
type: 'string',
required: true,
description: 'URL for the breadcrumb link',
},
},
},
},
...childrenPropDefs,
...classNamePropDefs,
...stylePropDefs,
@@ -18,24 +18,6 @@ export const propDefs: Record<string, PropDef> = {
type: 'string',
default: '/',
},
breadcrumbs: {
type: 'complex',
complexType: {
name: 'Breadcrumb[]',
properties: {
label: {
type: 'string',
required: true,
description: 'Display text for the breadcrumb',
},
href: {
type: 'string',
required: true,
description: 'URL for the breadcrumb link',
},
},
},
},
customActions: {
type: 'enum',
values: ['ReactNode'],
@@ -7,6 +7,7 @@ import {
searchFieldDefaultSnippet,
searchFieldSizesSnippet,
searchFieldDescriptionSnippet,
searchFieldCollapsibleSnippet,
} from './search-field.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
@@ -59,6 +60,18 @@ Here's a simple SearchField with a description.
code={searchFieldDescriptionSnippet}
/>
### Collapsible
You can make the SearchField collapsible by setting the `startCollapsed` prop to `true`.
<Snippet
align="center"
py={4}
open
preview={<SearchFieldSnippet story="StartCollapsed" />}
code={searchFieldCollapsibleSnippet}
/>
<Theming component="SearchField" />
<ChangelogComponent component="search-field" />
@@ -25,6 +25,10 @@ export const searchFieldPropDefs: Record<string, PropDef> = {
type: 'string',
required: true,
},
startCollapsed: {
type: 'boolean',
default: 'false',
},
...classNamePropDefs,
...stylePropDefs,
};
@@ -41,3 +45,5 @@ export const searchFieldSizesSnippet = `<Flex direction="row" gap="4">
</Flex>`;
export const searchFieldDescriptionSnippet = `<SearchField label="Label" description="Description" placeholder="Enter a URL" />`;
export const searchFieldCollapsibleSnippet = `<SearchField startCollapsed />`;
+49 -49
View File
@@ -1201,21 +1201,21 @@ __metadata:
languageName: node
linkType: hard
"@types/react-dom@npm:19.1.6":
version: 19.1.6
resolution: "@types/react-dom@npm:19.1.6"
"@types/react-dom@npm:19.1.7":
version: 19.1.7
resolution: "@types/react-dom@npm:19.1.7"
peerDependencies:
"@types/react": ^19.0.0
checksum: 10/b5b20b7f0797f34c5a11915b74dcf8b3b7a9da9fea90279975ce6f150ca5d31bb069dbb0838638a5e9e168098aa4bb4a6f61d078efa1bbb55d7f0bdfe47bb142
checksum: 10/a99465e5a17d40725dedb3708357f8998c57caab768cee0992b4bb7822ce7ed2ec697a5f426cb98d3397b020756a6e4b0986dc5f6f4254e13b3536afb38538e6
languageName: node
linkType: hard
"@types/react@npm:19.1.8":
version: 19.1.8
resolution: "@types/react@npm:19.1.8"
"@types/react@npm:19.1.9":
version: 19.1.9
resolution: "@types/react@npm:19.1.9"
dependencies:
csstype: "npm:^3.0.2"
checksum: 10/a3e6fe0f60f22828ef887f30993aa147b71532d7b1219dd00d246277eb7a9ca01ec533096237fa21ca1bccb3653373b4e8e59e5ae59f9c793058384bbc1f4d5c
checksum: 10/b1032eae52e3b4f2a8b9ea6aac936385a78b7eff55cad4ff4f0d7e726c6ea87c1f287a1ba0e57a76a8e4456d3fb918b4f97a01e71686fdc11a65b26b8d296be4
languageName: node
linkType: hard
@@ -1369,9 +1369,9 @@ __metadata:
languageName: node
linkType: hard
"@uiw/codemirror-extensions-basic-setup@npm:4.23.13":
version: 4.23.13
resolution: "@uiw/codemirror-extensions-basic-setup@npm:4.23.13"
"@uiw/codemirror-extensions-basic-setup@npm:4.24.2":
version: 4.24.2
resolution: "@uiw/codemirror-extensions-basic-setup@npm:4.24.2"
dependencies:
"@codemirror/autocomplete": "npm:^6.0.0"
"@codemirror/commands": "npm:^6.0.0"
@@ -1388,7 +1388,7 @@ __metadata:
"@codemirror/search": ">=6.0.0"
"@codemirror/state": ">=6.0.0"
"@codemirror/view": ">=6.0.0"
checksum: 10/35fd1894f9f7da8f94fa077a70821f92118597e2e2b12782b254b0844f06c1855508421c4cb47de7099a972ef706218c08203116aa93da4e8e1585c8d234015d
checksum: 10/077f64b4bbb6178038d30a3cb2961c0ff89c0836b73210d0ac3fca828c3ba8fbce61e42635ec2e2997cdffff5472b75ad58541c4e62f98d52b83d475da0c56eb
languageName: node
linkType: hard
@@ -1408,14 +1408,14 @@ __metadata:
linkType: hard
"@uiw/react-codemirror@npm:^4.23.7":
version: 4.23.13
resolution: "@uiw/react-codemirror@npm:4.23.13"
version: 4.24.2
resolution: "@uiw/react-codemirror@npm:4.24.2"
dependencies:
"@babel/runtime": "npm:^7.18.6"
"@codemirror/commands": "npm:^6.1.0"
"@codemirror/state": "npm:^6.1.1"
"@codemirror/theme-one-dark": "npm:^6.0.0"
"@uiw/codemirror-extensions-basic-setup": "npm:4.23.13"
"@uiw/codemirror-extensions-basic-setup": "npm:4.24.2"
codemirror: "npm:^6.0.0"
peerDependencies:
"@babel/runtime": ">=7.11.0"
@@ -1423,9 +1423,9 @@ __metadata:
"@codemirror/theme-one-dark": ">=6.0.0"
"@codemirror/view": ">=6.0.0"
codemirror: ">=6.0.0"
react: ">=16.8.0"
react-dom: ">=16.8.0"
checksum: 10/51777d7eb313be0716d3597f829e6ee9982ab2dc3e36dc5219f25cdf680b82a6c0a0951d9adaca85b9c829da92f7971aa6e65bdd50e7610972d89463fe2587a1
react: ">=17.0.0"
react-dom: ">=17.0.0"
checksum: 10/f0816fcf40c451c8bc15319d62f5b8d62a04c12c2bc053569212b31edba7c7164e45fd161ebb683a03fd5a50697b91a6ac4cab2d2d1a45da5f2626d959c399a0
languageName: node
linkType: hard
@@ -2315,8 +2315,8 @@ __metadata:
"@storybook/react": "npm:^8.6.8"
"@types/mdx": "npm:^2.0.13"
"@types/node": "npm:^20"
"@types/react": "npm:19.1.8"
"@types/react-dom": "npm:19.1.6"
"@types/react": "npm:19.1.9"
"@types/react-dom": "npm:19.1.7"
"@uiw/codemirror-themes": "npm:^4.23.7"
"@uiw/react-codemirror": "npm:^4.23.7"
chokidar: "npm:^3.6.0"
@@ -2329,8 +2329,8 @@ __metadata:
next: "npm:15.3.4"
next-mdx-remote-client: "npm:^2.1.2"
prop-types: "npm:^15.8.1"
react: "npm:19.1.0"
react-dom: "npm:19.1.0"
react: "npm:19.1.1"
react-dom: "npm:19.1.1"
shiki: "npm:^1.26.1"
storybook: "npm:^8.6.8"
typescript: "npm:^5"
@@ -3618,20 +3618,20 @@ __metadata:
linkType: hard
"html-react-parser@npm:^5.2.5":
version: 5.2.5
resolution: "html-react-parser@npm:5.2.5"
version: 5.2.6
resolution: "html-react-parser@npm:5.2.6"
dependencies:
domhandler: "npm:5.0.3"
html-dom-parser: "npm:5.1.1"
react-property: "npm:2.0.2"
style-to-js: "npm:1.1.16"
style-to-js: "npm:1.1.17"
peerDependencies:
"@types/react": 0.14 || 15 || 16 || 17 || 18 || 19
react: 0.14 || 15 || 16 || 17 || 18 || 19
peerDependenciesMeta:
"@types/react":
optional: true
checksum: 10/22852dc4826d3be9506e238e37a05c23b432675aac33f22bd5741caa195a32e99de99d2b99037fda532f198afbcdeacfc0b27627d6e529143a98c8f191e99c52
checksum: 10/be2903fd932d44ff6cae66bd18b025d318879e16eb1e67c0ae6dc640e5c77509da41b61948c0d404a43b5362b57b7cfa3963df242b9773d4c7a16f410db7f2ac
languageName: node
linkType: hard
@@ -5135,20 +5135,20 @@ __metadata:
linkType: hard
"next-mdx-remote-client@npm:^2.1.2":
version: 2.1.2
resolution: "next-mdx-remote-client@npm:2.1.2"
version: 2.1.3
resolution: "next-mdx-remote-client@npm:2.1.3"
dependencies:
"@babel/code-frame": "npm:^7.27.1"
"@mdx-js/mdx": "npm:^3.1.0"
"@mdx-js/react": "npm:^3.1.0"
remark-mdx-remove-esm: "npm:^1.1.0"
remark-mdx-remove-esm: "npm:^1.2.0"
serialize-error: "npm:^12.0.0"
vfile: "npm:^6.0.3"
vfile-matter: "npm:^5.0.1"
peerDependencies:
react: ^19.1.0
react-dom: ^19.1.0
checksum: 10/610e95bff6c1dcdb4d1fefc9333ac748f1899cbca1ea551e784cd1e8cfd077c14d6bb8ab27826886f354cc3b9cf700fe25810263f996c53f824a2cfdbc5a064f
checksum: 10/3837c63edf7d707de2eea9b4c565eeb7e505a769e93c2673f746ec9993c644f7cd0aacb8bad911eff9ea63190f48c2c16ffe98d47c39591c5dac29c8bf9a9e0c
languageName: node
linkType: hard
@@ -5585,14 +5585,14 @@ __metadata:
languageName: node
linkType: hard
"react-dom@npm:19.1.0":
version: 19.1.0
resolution: "react-dom@npm:19.1.0"
"react-dom@npm:19.1.1":
version: 19.1.1
resolution: "react-dom@npm:19.1.1"
dependencies:
scheduler: "npm:^0.26.0"
peerDependencies:
react: ^19.1.0
checksum: 10/c5b58605862c7b0bb044416b01c73647bb8e89717fb5d7a2c279b11815fb7b49b619fe685c404e59f55eb52c66831236cc565c25ee1c2d042739f4a2cc538aa2
react: ^19.1.1
checksum: 10/9005415d2175b1f1eb4a544ad04afb29691bb7b6dd43bbdaa09932146b310b73bd4552bc772ad78fa481f409eada1560cf887606c83c1a53a922c1e30f1b3a34
languageName: node
linkType: hard
@@ -5610,10 +5610,10 @@ __metadata:
languageName: node
linkType: hard
"react@npm:19.1.0":
version: 19.1.0
resolution: "react@npm:19.1.0"
checksum: 10/d0180689826fd9de87e839c365f6f361c561daea397d61d724687cae88f432a307d1c0f53a0ee95ddbe3352c10dac41d7ff1ad85530fb24951b27a39e5398db4
"react@npm:19.1.1":
version: 19.1.1
resolution: "react@npm:19.1.1"
checksum: 10/9801530fdc939e1a7a499422e930515b2400809cb39c2872984e99f832d233f61659a693871183dac3155c2f9b2c9dcf4440a56bd18983277ae92860e38c3a61
languageName: node
linkType: hard
@@ -5754,7 +5754,7 @@ __metadata:
languageName: node
linkType: hard
"remark-mdx-remove-esm@npm:^1.1.0":
"remark-mdx-remove-esm@npm:^1.2.0":
version: 1.2.0
resolution: "remark-mdx-remove-esm@npm:1.2.0"
dependencies:
@@ -6480,21 +6480,21 @@ __metadata:
languageName: node
linkType: hard
"style-to-js@npm:1.1.16, style-to-js@npm:^1.0.0":
version: 1.1.16
resolution: "style-to-js@npm:1.1.16"
"style-to-js@npm:1.1.17, style-to-js@npm:^1.0.0":
version: 1.1.17
resolution: "style-to-js@npm:1.1.17"
dependencies:
style-to-object: "npm:1.0.8"
checksum: 10/a876cc49a29ac90c7723b4d6f002ac6c1ac5ccc6b5bc963d9c607cfc74b15927b704c9324df6f824f576c65689fe4b4ff79caabcd44a13d8a02641f721f1b316
style-to-object: "npm:1.0.9"
checksum: 10/431f2fca8a55a61939a83ff0f58638e2996621ad93a97cf93f2be5115f411330d4e506ccf18621bd45607ec161546b763bb6961ad08238ad939b6261ff377230
languageName: node
linkType: hard
"style-to-object@npm:1.0.8":
version: 1.0.8
resolution: "style-to-object@npm:1.0.8"
"style-to-object@npm:1.0.9":
version: 1.0.9
resolution: "style-to-object@npm:1.0.9"
dependencies:
inline-style-parser: "npm:0.2.4"
checksum: 10/530b067325e3119bfaf75bdbe25cc86b02b559db00d881a74b98a2d5bb10ac953d1b455ed90c825963cf3b4bdaa1bda45f406d78d987391434b8d8ab3835df4e
checksum: 10/fd0c131a83103fe4025afd8e0fd90c605054d485ad80f2ab402e7afa79f482f4b05fff40b6aa661cb1b835e5c56bb0644dc38cbf9b3d2982fc552435db3dae50
languageName: node
linkType: hard
@@ -0,0 +1,77 @@
---
id: adrs-adr015
title: 'ADR015: Types and naming for element and component options'
description: Architecture Decision Record (ADR) for the proper types and naming for element and component options
---
## Context
Until now there hasn't been a clear standard for how to define options that are intended to provide JSX elements or components. This led to a mix of different patterns in public APIs, which this ADR aims to standardize.
## Decision
We will use one of the following option property names and types when defining options that are intended to provide JSX elements or components:
### Simple element
This option is used when a simple synchronous JSX element is provided. It must only be used in areas where lazy-loading is not needed.
```tsx
{
element: JSX.Element;
}
```
### Simple component
This option is used when a simple synchronous component is provided. It must only be used in areas where lazy-loading is not needed.
```tsx
{
component: (props: { ... }) => JSX.Element | null
}
```
### Async element loader
This option is used when a simple asynchronous JSX element is provided. It is the preferred option when only producing a single instance and there is no need to pass properties to the component. This format simplifies the creation of closures for passing additional properties in the loader implementation.
```tsx
{
loader: () => Promise<JSX.Element>;
}
```
### Async component loader
This option is used when a simple asynchronous component is provided. It is the preferred option when properties need to be passed to the component or multiple instance are needed, and lazy-loading is required.
```tsx
{
loader: () => Promise<(props: { ... }) => JSX.Element | null>
}
```
### Any component loader
This option is used in the same cases as the async component loader, but when the option of synchronous loading is also needed. The structure of always having the outer loader function, even in the synchronous case, makes it possible to determine the type of the loader at runtime.
```tsx
{
loader: (() => props => JSX.Element | null) | (() => Promise<props => JSX.Element | null>)
}
```
Note that when consuming this loader we'll need to unconditionally wrap it with `React.lazy`. This is because you can't delay the call to `React.lazy` until rendering, because you're not allowed to call it within a render function. This means that we can't first call the loader to check whether the returned value is a promise or not, and we must instead unconditionally wrap it with `React.lazy`. Therefore the implementation of accepting one of these loaders as an option needs to look something like this:
```tsx
const LazyComponent = React.lazy(() =>
Promise.resolve(options.loader()).then(loaded => ({ default: loaded })),
);
```
## Consequences
We will update all APIs for the new frontend system in the `@backstage/frontend-*` packages.
We will not update any of the existing APIs for the old frontend system in the `@backstage/core-*` packages.
@@ -366,7 +366,7 @@ your first set of parameter fields would be shown. The same goes for the nested
spec. Make sure to use the key `backstage:featureFlag` in your templates if
you want to use this functionality.
Feature Flags cannot be used in `spec.steps[].if`(the conditional on whether to execute an step/action). But you can use feature flags to display parameters that allow for skipping steps.
Feature Flags cannot be used in `spec.steps[].if`(the conditional on whether to execute a step/action). But you can use feature flags to display parameters that allow for skipping steps.
```yaml
spec:
@@ -807,7 +807,7 @@ Have in mind that changes in this form will not be saved on the template and is
### Custom Field Explorer
The custom filed explorer allows you to select any custom field loaded on the backstage instance and test different values and configurations.
The custom field explorer allows you to select any custom field loaded on the backstage instance and test different values and configurations.
## Presentation
+11 -14
View File
@@ -48,31 +48,28 @@ App feature discovery lets you automatically discover and install features provi
Because feature discovery needs to interact with the compilation process, it is only available when using the `@backstage/cli` to build your app. It is hooked into the WebPack compilation process by scanning your app package for compatible dependencies, which are then made part of the app compilation bundle.
Since the `@backstage/cli` is a more stable component than the new frontend system, feature discovery is currently marked as an experimental feature of the CLI and needs to be enabled manually. To enable it, add the following configuration to your `app-config.yaml`:
To enable frontend feature discovery, add the following configuration to your `app-config.yaml`:
```yaml
app:
experimental:
packages: all
packages: all
```
This will cause all dependencies in your app package to be installed automatically. If this is not desired, you can use include or exclude filters to narrow down the set of packages:
```yaml
app:
experimental:
packages:
# Only the following packages will be included
include:
- '@backstage/plugin-catalog'
- '@backstage/plugin-scaffolder'
packages:
# Only the following packages will be included
include:
- '@backstage/plugin-catalog'
- '@backstage/plugin-scaffolder'
---
app:
experimental:
packages:
# All but the following package will be included
exclude:
- '@backstage/plugin-catalog'
packages:
# All but the following package will be included
exclude:
- '@backstage/plugin-catalog'
```
Note that you do not need to manually exclude packages that you also import explicitly in code, since plugin instances are deduplicated by the app. You will never end up with duplicate plugin installations except if they are in fact two different plugin instances with different IDs.
@@ -21,7 +21,7 @@ Frontend plugin instances are created with the `createFrontendPlugin` function,
// This creates a new extension, see "Extension Blueprints" documentation for more details
const myPage = PageBlueprint.make({
params: {
defaultPath: '/my-page',
path: '/my-page',
loader: () => import('./MyPage').then(m => <m.MyPage />),
},
});
@@ -107,7 +107,7 @@ export default plugin.withOverrides({
// Override the catalog index page with a completely custom implementation
PageBlueprint.make({
params: {
defaultPath: '/catalog',
path: '/catalog',
routeRef: plugin.routes.catalogIndex,
loader: () => import('./CustomCatalogIndexPage').then(m => <m.Page />),
},
@@ -18,7 +18,7 @@ The following is a simple example of how one might use the blueprint `make` meth
```tsx
const myPageExtension = PageBlueprint.make({
params: {
defaultPath: '/my-page',
path: '/my-page',
loader: () => import('./components/MyPage').then(m => <m.MyPage />),
},
});
@@ -26,7 +26,7 @@ const myPageExtension = PageBlueprint.make({
The returned `myPageExtension` is an extension which is ready to be used in a plugin. It is the same type of object as is returned by the lower level `createExtension` function.
## Creating an extension from a blueprint with overrides
### Creating an extension from a blueprint with overrides
Every extension blueprint also provides a `makeWithOverrides` method. It is useful in cases where you want to provide additional integration points for an extension created with a blueprint. You might for example want to define additional inputs or configuration schema, or use the existing configuration to dynamically compute the parameters passed to the blueprint.
@@ -34,22 +34,34 @@ The following is an example of how one might use the blueprint `makeWithOverride
```tsx
const myPageExtension = PageBlueprint.makeWithOverrides({
// This defines additional configuration options for the extension.
config: {
schema: {
layout: z => z.enum(['grid', 'rows']).default('grid'),
},
},
// The original blueprint factory is provided as the first argument
factory(originalFactory, { config }) {
// This defines additional inputs for the extension.
inputs: {
content: createExtensionInput([coreExtensionData.reactElement], {
singleton: true,
optional: true,
}),
},
// The original blueprint factory is provided as the first argument.
// By convention the name is `originalFactory`, but you can also pick a different name.
factory(originalFactory, { config, inputs }) {
// Call and forward the result from the original factory, providing
// the blueprint parameters as the first argument.
return originalFactory({
defaultPath: '/my-page',
path: '/my-page',
loader: () =>
import('./components/MyPage').then(m => (
// We can now access values from the factory context when providing
// the blueprint parameters, such as config values.
<m.MyPage layout={config.layout} />
// the blueprint parameters, such as config values and inputs.
<m.MyPage
layout={config.layout}
content={inputs.content?.get(coreExtensionData.reactElement)}
/>
)),
});
},
@@ -58,18 +70,18 @@ const myPageExtension = PageBlueprint.makeWithOverrides({
When using `makeWithOverrides`, we no longer pass the blueprint parameters directly. Instead, we provide a `factory` function that receives the original blueprint factory as the first argument, and the extension factory context as the second. We can then call the original blueprint factory with the blueprint parameters and forward the result as the return value of out factory. Notice that when passing the blueprint parameters using this pattern we have access to a lot more information than when using the `make` method, at the cost of being more complex.
Apart from the addition of the blueprint parameters of the first argument to the original factory function, the `makeWithOverrides` method works the same way as [extension overrides](./25-extension-overrides.md). All the same options and rules apply, including the ability to define additional inputs, override outputs, and so on. We therefore defer to the [extension overrides](./25-extension-overrides.md) documentation for more information on how to use the `makeWithOverrides` method.
Apart from the addition of the blueprint parameters of the first argument to the original factory function, the `makeWithOverrides` method works the same way as [extension overrides](./25-extension-overrides.md). All the same options and rules apply, including the ability to define additional inputs, override outputs, and so on. For more details and examples on how this works, please refer to the [extension overrides](./25-extension-overrides.md) documentation. The patterns in that section also apply to the creation of extensions with the `makeWithOverrides` method.
### Creating an extension from a blueprint with advanced parameter types
Some blueprints may be defined with something known as "advanced parameter types". This is a feature that enables type inference and transform of the blueprint parameters, and the way that you pass the parameters look a little bit different. Rather than passing the parameters directly, they are instead passed as a callback function of the form `define => define(<params>)`.
Some blueprints may be defined with something known as "advanced parameter types". This is a feature that enables type inference and transform of the blueprint parameters, and the way that you pass the parameters look a little bit different. Rather than passing the parameters directly, they are instead passed as a callback function of the form `defineParams => defineParams(<params>)`.
An example of a blueprint that uses advanced parameter types is the `ApiBlueprint` blueprint. Using it to create a simple implementation for the `AlertApi` might look like this:
```ts
const alertApiBlueprint = ApiBlueprint.make({
params: define =>
define({
params: defineParams =>
defineParams({
api: alertApiRef,
deps: {},
factory: () => new MyAlertApi(),
@@ -82,8 +94,8 @@ This also works with `makeWithOverrides`, where the define callback is passed as
```ts
const alertApiBlueprint = ApiBlueprint.makeWithOverrides({
factory(originalFactory, { config }) {
return originalFactory(define =>
define({
return originalFactory(defineParams =>
defineParams({
api: alertApiRef,
deps: {},
factory: () => new MyAlertApi(config),
@@ -101,7 +113,7 @@ The following is an example of how one might create a new extension blueprint:
```tsx
export interface MyWidgetBlueprintParams {
defaultTitle: string;
title: string;
element: JSX.Element;
}
@@ -119,7 +131,7 @@ export const MyWidgetBlueprint = createExtensionBlueprint({
// Note that while this is a valid pattern, you might often want to
// return separate pieces of data instead, more on that below.
coreExtensionData.reactElement(
<MyWidgetContainer title={config.title ?? params.defaultTitle}>
<MyWidgetContainer title={config.title ?? params.title}>
{params.element}
</MyWidgetContainer>,
),
@@ -175,7 +187,7 @@ To do that, we create a new extension data reference for our widget title. This
```tsx
export interface MyWidgetBlueprintParams {
defaultTitle: string;
title: string;
element: JSX.Element;
}
@@ -194,7 +206,7 @@ export const MyWidgetBlueprint = createExtensionBlueprint({
output: [widgetTitleRef, coreExtensionData.reactElement],
factory(params: MyWidgetBlueprintParams, { config }) {
return [
widgetTitleRef(config.title ?? params.defaultTitle),
widgetTitleRef(config.title ?? params.title),
coreExtensionData.reactElement(params.element),
];
},
@@ -89,7 +89,7 @@ const exampleExtension = PageBlueprint.make({
params: {
loader: () =>
import('./components/ExamplePage').then(m => <m.ExamplePage />),
defaultPath: '/example',
path: '/example',
},
});
```
@@ -318,7 +318,7 @@ import {
const customSearchPage = PageBlueprint.make({
params: {
defaultPath: '/search',
path: '/search',
loader: () =>
import('./CustomSearchPage').then(m => <m.CustomSearchPage />),
},
+42 -3
View File
@@ -47,7 +47,7 @@ import { indexRouteRef } from './routes';
const catalogIndexPage = createPageExtension({
// The `name` option is omitted because this is an index page
defaultPath: '/entities',
path: '/entities',
// highlight-next-line
routeRef: indexRouteRef,
loader: () => import('./components').then(m => <m.IndexPage />),
@@ -197,7 +197,7 @@ import {
import { indexRouteRef, createComponentExternalRouteRef } from './routes';
const catalogIndexPage = createPageExtension({
defaultPath: '/entities',
path: '/entities',
routeRef: indexRouteRef,
loader: () => import('./components').then(m => <m.IndexPage />),
});
@@ -404,7 +404,7 @@ import {
import { indexRouteRef, detailsSubRouteRef } from './routes';
const catalogIndexPage = createPageExtension({
defaultPath: '/entities',
path: '/entities',
routeRef: indexRouteRef,
loader: () => import('./components').then(m => <m.IndexPage />),
});
@@ -419,3 +419,42 @@ export default createFrontendPlugin({
extensions: [catalogIndexPage],
});
```
## Route Aliases - Overriding Routed Extensions in Modules
It is possible to [override extensions of a plugin using a module](./25-extension-overrides.md#creating-a-frontend-module). In some cases the extension you're overriding may require a route reference. You could import import the plugin instance and access the it via the `routes` property, but this creates a direct dependency on the plugin and risks leading to package duplication issues that would also break the route reference.
Instead of accessing the route reference directly, you can create a new route reference that acts as an alias for the original one from the plugin. For example, you can override the catalog index page with a custom one like this:
```tsx
const indexRouteRef = createRouteRef({ aliasFor: 'catalog.catalogIndex' });
export default createFrontendModule({
pluginId: 'catalog',
extensions: [
PageBlueprint.make({
params: {
defaultPath: '/catalog',
routeRef: indexRouteRef,
loader: () =>
import('./CustomCatalogIndexPage').then(m => (
<m.CustomCatalogIndexPage />
)),
},
}),
],
});
```
Aliases are limited to the plugin that they are defined in. These aliases can also be imported and used as usual with for example `useRouteRef`, but they must always be registered in the app via an extension for this to work. For example, the following will not work:
```tsx
function MyInvalidComponent() {
// This is NOT valid
const link = useRouteRef(
createRouteRef({ aliasFor: 'catalog.catalogIndex' }),
);
// ...
}
```
@@ -42,7 +42,7 @@ The conversion functions such as `convertLegacyPageExtension` will attempt to in
```ts
const convertedIndexPage = convertLegacyPageExtension(TechDocsIndexPage, {
name: 'index',
defaultPath: '/docs',
path: '/docs',
});
```
@@ -72,10 +72,10 @@ const convertedTechdocsPlugin = convertLegacyPlugin(techdocsPlugin, {
extensions: [
convertLegacyPageExtension(TechDocsIndexPage, {
name: 'index',
defaultPath: '/docs',
path: '/docs',
}),
convertLegacyPageExtension(TechDocsReaderPage, {
defaultPath: '/docs/:namespace/:kind/:name/*',
path: '/docs/:namespace/:kind/:name/*',
}),
convertLegacyEntityContentExtension(EntityTechdocsContent),
],
@@ -199,8 +199,8 @@ import { ApiBlueprint } from '@backstage/frontend-plugin-api';
const scmIntegrationsApi = ApiBlueprint.make({
name: 'scm-integrations',
params: define =>
define({
params: defineParams =>
defineParams({
api: scmIntegrationsApiRef,
deps: { configApi: configApiRef },
factory: ({ configApi }) => ScmIntegrationsApi.fromConfig(configApi),
@@ -244,8 +244,7 @@ Plugins don't even have to be imported manually after installing their package i
```yaml title="in app-config.yaml"
app:
# Enabling plugin and override features discovery
experimental:
packages: all # ✨
packages: all # ✨
```
### `featureFlags`
@@ -75,7 +75,7 @@ const examplePage = PageBlueprint.make({
routeRef: rootRouteRef,
// This is the default path of this page, but integrators are free to override it
defaultPath: '/example',
path: '/example',
// Page extensions are always dynamically loaded using React.lazy().
// All of the functionality of this page is implemented in the
@@ -160,8 +160,8 @@ import { exampleApiRef, DefaultExampleApi } from './api';
// highlight-add-start
const exampleApi = ApiBlueprint.make({
name: 'example',
params: define =>
define({
params: defineParams =>
defineParams({
api: exampleApiRef,
deps: {},
factory: () => new DefaultExampleApi(),
@@ -198,8 +198,8 @@ import { EntityContentBlueprint } from '@backstage/plugin-catalog-react/alpha';
// route reference if you want to be able to generate a URL that links to the content.
const exampleEntityContent = EntityContentBlueprint.make({
params: {
defaultPath: 'example',
defaultTitle: 'Example',
path: 'example',
title: 'Example',
loader: () =>
import('./components/ExampleEntityContent').then(m => (
<m.ExampleEntityContent />
@@ -17,10 +17,6 @@ These are the [extension blueprints](../architecture/23-extension-blueprints.md)
An API extension is used to add or override [Utility API factories](../utility-apis/01-index.md) in the app. They are commonly used by plugins for both internal and shared APIs. There are also many built-in Api extensions provided by the framework that you are able to override.
### Component - [Reference](../../reference/frontend-plugin-api.createcomponentextension.md)
Components extensions are used to override the component associated with a component reference throughout the app. This uses an extension creator function rather than a blueprint, but will likely be migrated to a blueprint in the future.
### NavItem - [Reference](../../reference/frontend-plugin-api.navitemblueprint.md)
Navigation item extensions are used to provide menu items that link to different parts of the app. By default nav items are attached to the app nav extension, which by default is rendered as the left sidebar in the app.
@@ -33,6 +29,10 @@ Page extensions provide content for a particular route in the app. By default pa
Sign-in page extension have a single purpose - to implement a custom sign-in page. They are always attached to the app root extension and are rendered before the rest of the app until the user is signed in.
### SwappableComponent - [Reference](../../reference/frontend-plugin-api.swappablecomponentblueprint.md)
Swappable Components are extensions that are used to replace the implementations of components in the app and plugins.
### Theme - [Reference](../../reference/frontend-plugin-api.themeblueprint.md)
Theme extensions provide custom themes for the app. They are always attached to the app extension and you can have any number of themes extensions installed in an app at once, letting the user choose which theme to use.

Some files were not shown because too many files have changed in this diff Show More