frontend-plugin-api: move app blueprints to new app-react package
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-app-backend': patch
|
||||
'@backstage/plugin-app-node': patch
|
||||
---
|
||||
|
||||
Updated plugin metadata.
|
||||
@@ -0,0 +1,13 @@
|
||||
---
|
||||
'@backstage/plugin-app-react': patch
|
||||
---
|
||||
|
||||
Moved the following blueprints from `@backstage/frontend-plugin-api`:
|
||||
|
||||
- `IconBundleBlueprint`
|
||||
- `NavContentBlueprint`
|
||||
- `RouterBlueprint`
|
||||
- `SignInPageBlueprint`
|
||||
- `SwappableComponentBlueprint`
|
||||
- `ThemeBlueprint`
|
||||
- `TranslationBlueprint`
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-app-react': minor
|
||||
---
|
||||
|
||||
Initial release of this web library for `@backstage/plugin-app`.
|
||||
@@ -0,0 +1,13 @@
|
||||
---
|
||||
'@backstage/frontend-plugin-api': patch
|
||||
---
|
||||
|
||||
The following blueprints are being restricted to only be used in app plugin overrides and modules. They are being moved to the `@backstage/plugin-app-react` package and have been deprecated:
|
||||
|
||||
- `IconBundleBlueprint`
|
||||
- `NavContentBlueprint`
|
||||
- `RouterBlueprint`
|
||||
- `SignInPageBlueprint`
|
||||
- `SwappableComponentBlueprint`
|
||||
- `ThemeBlueprint`
|
||||
- `TranslationBlueprint`
|
||||
@@ -0,0 +1,13 @@
|
||||
---
|
||||
'@backstage/plugin-app': patch
|
||||
---
|
||||
|
||||
The following blueprints are being restricted to only be used in app plugin overrides and modules. They will now produce a deprecation warning when used outside of the app plugin:
|
||||
|
||||
- `IconBundleBlueprint`
|
||||
- `NavContentBlueprint`
|
||||
- `RouterBlueprint`
|
||||
- `SignInPageBlueprint`
|
||||
- `SwappableComponentBlueprint`
|
||||
- `ThemeBlueprint`
|
||||
- `TranslationBlueprint`
|
||||
@@ -444,7 +444,7 @@ const app = createApp({
|
||||
Can be converted to the following extension:
|
||||
|
||||
```tsx
|
||||
import { SignInPageBlueprint } from '@backstage/frontend-plugin-api';
|
||||
import { SignInPageBlueprint } from '@backstage/plugin-app-react';
|
||||
|
||||
const signInPage = SignInPageBlueprint.make({
|
||||
params: {
|
||||
@@ -492,7 +492,7 @@ const app = createApp({
|
||||
Can be converted to the following extension:
|
||||
|
||||
```tsx
|
||||
import { ThemeBlueprint } from '@backstage/frontend-plugin-api';
|
||||
import { ThemeBlueprint } from '@backstage/plugin-app-react';
|
||||
|
||||
const customLightThemeExtension = ThemeBlueprint.make({
|
||||
name: 'custom-light',
|
||||
@@ -535,7 +535,7 @@ const app = createApp({
|
||||
Icons are now installed as extensions, using the `IconBundleBlueprint` to make new instances which can be added to the app.
|
||||
|
||||
```ts
|
||||
import { IconBundleBlueprint } from '@backstage/frontend-plugin-api';
|
||||
import { IconBundleBlueprint } from '@backstage/plugin-app-react';
|
||||
|
||||
const exampleIconBundle = IconBundleBlueprint.make({
|
||||
name: 'example-bundle',
|
||||
@@ -586,10 +586,8 @@ Can be converted to the following extension:
|
||||
|
||||
```tsx
|
||||
import { catalogTranslationRef } from '@backstage/plugin-catalog/alpha';
|
||||
import {
|
||||
createTranslationMessages,
|
||||
TranslationBlueprint,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { createTranslationMessages } from '@backstage/frontend-plugin-api';
|
||||
import { TranslationBlueprint } from '@backstage/plugin-app-react';
|
||||
|
||||
const catalogTranslations = TranslationBlueprint.make({
|
||||
name: 'catalog-overrides',
|
||||
@@ -705,7 +703,7 @@ export const navModule = createFrontendModule({
|
||||
Then in the actual implementation for the `SidebarContent` extension, you can provide something like the following, where you implement the entire `Sidebar` component.
|
||||
|
||||
```tsx title="in packages/app/src/modules/nav/Sidebar.tsx"
|
||||
import { NavContentBlueprint } from '@backstage/frontend-plugin-api';
|
||||
import { NavContentBlueprint } from '@backstage/plugin-app-react';
|
||||
|
||||
export const SidebarContent = NavContentBlueprint.make({
|
||||
params: {
|
||||
|
||||
@@ -23,6 +23,8 @@ Navigation item extensions are used to provide menu items that link to different
|
||||
|
||||
Page extensions provide content for a particular route in the app. By default pages are attached to the app routes extensions, which renders the root routes.
|
||||
|
||||
## Extension blueprints in `@backstage/plugin-app-react`
|
||||
|
||||
### SignInPage - [Reference](https://backstage.io/api/stable/variables/_backstage_frontend-plugin-api.SignInPageBlueprint.html)
|
||||
|
||||
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.
|
||||
@@ -43,6 +45,14 @@ Icon bundle extensions provide the ability to replace or provide new icons to th
|
||||
|
||||
Translation extension provide custom translation messages for the app. They can be used both to override the default english messages to custom ones, as well as provide translations for additional languages.
|
||||
|
||||
### NavContent - [Reference](https://backstage.io/api/stable/variables/_backstage_frontend-plugin-api.NavContentBlueprint.html)
|
||||
|
||||
Nav content extensions allow you to replace the entire navbar with your own component. They are always attached to the app nav extension.
|
||||
|
||||
### Router - [Reference](https://backstage.io/api/stable/variables/_backstage_frontend-plugin-api.RouterBlueprint.html)
|
||||
|
||||
Router extensions allow you to replace the router component used by the app. They are always attached to the app root extension.
|
||||
|
||||
## Extension blueprints in `@backstage/plugin-catalog-react/alpha`
|
||||
|
||||
These are the [extension blueprints](../architecture/23-extension-blueprints.md) provided by the Catalog plugin.
|
||||
|
||||
@@ -52,11 +52,8 @@ In order to override a Swappable Component, you need to create a `SwappableCompo
|
||||
There are two different ways to add extensions to the `app` plugin, both are documented below in an example of overriding the `Progress` Swappable Component.
|
||||
|
||||
```tsx title="in packages/app/src/App.tsx"
|
||||
import {
|
||||
Progress,
|
||||
SwappableComponentBlueprint,
|
||||
createFrontendModule,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { Progress, createFrontendModule } from '@backstage/frontend-plugin-api';
|
||||
import { SwappableComponentBlueprint } from '@backstage/plugin-app-react';
|
||||
import { MyCustomProgress } from './CustomProgress';
|
||||
import { createApp } from '@backstage/frontend-defaults';
|
||||
import appPlugin from '@backstage/plugin-app';
|
||||
|
||||
@@ -1457,7 +1457,7 @@ export const googleAuthApiRef: ApiRef<
|
||||
SessionApi
|
||||
>;
|
||||
|
||||
// @public (undocumented)
|
||||
// @public @deprecated (undocumented)
|
||||
export const IconBundleBlueprint: ExtensionBlueprint_2<{
|
||||
kind: 'icon-bundle';
|
||||
params: {
|
||||
@@ -1522,7 +1522,7 @@ export const microsoftAuthApiRef: ApiRef<
|
||||
SessionApi
|
||||
>;
|
||||
|
||||
// @public
|
||||
// @public @deprecated
|
||||
export const NavContentBlueprint: ExtensionBlueprint_2<{
|
||||
kind: 'nav-content';
|
||||
params: {
|
||||
@@ -1923,7 +1923,7 @@ export type RouteFunc<TParams extends AnyRouteRefParams> = (
|
||||
: readonly [params: TParams]
|
||||
) => string;
|
||||
|
||||
// @public (undocumented)
|
||||
// @public @deprecated (undocumented)
|
||||
export const RouterBlueprint: ExtensionBlueprint_2<{
|
||||
kind: 'app-router-component';
|
||||
params: {
|
||||
@@ -1998,7 +1998,7 @@ export namespace SessionState {
|
||||
export type SignedOut = typeof SessionState.SignedOut;
|
||||
}
|
||||
|
||||
// @public
|
||||
// @public @deprecated
|
||||
export const SignInPageBlueprint: ExtensionBlueprint_2<{
|
||||
kind: 'sign-in-page';
|
||||
params: {
|
||||
@@ -2066,7 +2066,7 @@ export interface SubRouteRef<
|
||||
readonly T: TParams;
|
||||
}
|
||||
|
||||
// @public
|
||||
// @public @deprecated
|
||||
export const SwappableComponentBlueprint: ExtensionBlueprint_2<{
|
||||
kind: 'component';
|
||||
params: <Ref extends SwappableComponentRef<any>>(params: {
|
||||
@@ -2150,7 +2150,7 @@ export interface SwappableComponentsApi {
|
||||
// @public
|
||||
export const swappableComponentsApiRef: ApiRef_2<SwappableComponentsApi>;
|
||||
|
||||
// @public
|
||||
// @public @deprecated
|
||||
export const ThemeBlueprint: ExtensionBlueprint_2<{
|
||||
kind: 'theme';
|
||||
params: {
|
||||
@@ -2186,7 +2186,7 @@ export type TranslationApi = {
|
||||
// @public (undocumented)
|
||||
export const translationApiRef: ApiRef<TranslationApi>;
|
||||
|
||||
// @public
|
||||
// @public @deprecated
|
||||
export const TranslationBlueprint: ExtensionBlueprint_2<{
|
||||
kind: 'translation';
|
||||
params: {
|
||||
|
||||
@@ -21,7 +21,10 @@ const iconsDataRef = createExtensionDataRef<{
|
||||
[key in string]: IconComponent;
|
||||
}>().with({ id: 'core.icons' });
|
||||
|
||||
/** @public */
|
||||
/**
|
||||
* @public
|
||||
* @deprecated Use {@link @backstage/plugin-app-react#IconBundleBlueprint} instead.
|
||||
*/
|
||||
export const IconBundleBlueprint = createExtensionBlueprint({
|
||||
kind: 'icon-bundle',
|
||||
attachTo: { id: 'api:app/icons', input: 'icons' },
|
||||
|
||||
@@ -60,6 +60,7 @@ const componentDataRef = createExtensionDataRef<NavContentComponent>().with({
|
||||
* Creates an extension that replaces the entire nav bar with your own component.
|
||||
*
|
||||
* @public
|
||||
* @deprecated Use {@link @backstage/plugin-app-react#NavContentBlueprint} instead.
|
||||
*/
|
||||
export const NavContentBlueprint = createExtensionBlueprint({
|
||||
kind: 'nav-content',
|
||||
|
||||
@@ -21,7 +21,10 @@ const componentDataRef = createExtensionDataRef<
|
||||
(props: { children: ReactNode }) => JSX.Element | null
|
||||
>().with({ id: 'app.router.wrapper' });
|
||||
|
||||
/** @public */
|
||||
/**
|
||||
* @public
|
||||
* @deprecated Use {@link @backstage/plugin-app-react#RouterBlueprint} instead.
|
||||
*/
|
||||
export const RouterBlueprint = createExtensionBlueprint({
|
||||
kind: 'app-router-component',
|
||||
attachTo: { id: 'app/root', input: 'router' },
|
||||
|
||||
@@ -44,6 +44,7 @@ const componentDataRef = createExtensionDataRef<
|
||||
* Creates an extension that replaces the sign in page.
|
||||
*
|
||||
* @public
|
||||
* @deprecated Use {@link @backstage/plugin-app-react#SignInPageBlueprint} instead.
|
||||
*/
|
||||
export const SignInPageBlueprint = createExtensionBlueprint({
|
||||
kind: 'sign-in-page',
|
||||
|
||||
@@ -31,6 +31,7 @@ export const componentDataRef = createExtensionDataRef<{
|
||||
* Blueprint for creating swappable components from a SwappableComponentRef and a loader
|
||||
*
|
||||
* @public
|
||||
* @deprecated Use {@link @backstage/plugin-app-react#SwappableComponentBlueprint} instead.
|
||||
*/
|
||||
export const SwappableComponentBlueprint = createExtensionBlueprint({
|
||||
kind: 'component',
|
||||
|
||||
@@ -25,6 +25,7 @@ const themeDataRef = createExtensionDataRef<AppTheme>().with({
|
||||
* Creates an extension that adds/replaces an app theme.
|
||||
*
|
||||
* @public
|
||||
* @deprecated Use {@link @backstage/plugin-app-react#ThemeBlueprint} instead.
|
||||
*/
|
||||
export const ThemeBlueprint = createExtensionBlueprint({
|
||||
kind: 'theme',
|
||||
|
||||
@@ -25,6 +25,7 @@ const translationDataRef = createExtensionDataRef<
|
||||
* Creates an extension that adds translations to your app.
|
||||
*
|
||||
* @public
|
||||
* @deprecated Use {@link @backstage/plugin-app-react#TranslationBlueprint} instead.
|
||||
*/
|
||||
export const TranslationBlueprint = createExtensionBlueprint({
|
||||
kind: 'translation',
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
"pluginPackages": [
|
||||
"@backstage/plugin-app",
|
||||
"@backstage/plugin-app-backend",
|
||||
"@backstage/plugin-app-node"
|
||||
"@backstage/plugin-app-node",
|
||||
"@backstage/plugin-app-react"
|
||||
]
|
||||
},
|
||||
"publishConfig": {
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
"pluginPackages": [
|
||||
"@backstage/plugin-app",
|
||||
"@backstage/plugin-app-backend",
|
||||
"@backstage/plugin-app-node"
|
||||
"@backstage/plugin-app-node",
|
||||
"@backstage/plugin-app-react"
|
||||
]
|
||||
},
|
||||
"publishConfig": {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
|
||||
@@ -0,0 +1,5 @@
|
||||
# @backstage/plugin-app-react
|
||||
|
||||
Welcome to the web library package for the app plugin!
|
||||
|
||||
_This plugin was created through the Backstage CLI_
|
||||
@@ -0,0 +1,10 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: backstage-plugin-app-react
|
||||
title: '@backstage/plugin-app-react'
|
||||
description: Web library for the app plugin
|
||||
spec:
|
||||
lifecycle: experimental
|
||||
type: backstage-web-library
|
||||
owner: framework-maintainers
|
||||
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"name": "@backstage/plugin-app-react",
|
||||
"version": "0.0.0",
|
||||
"description": "Web library for the app plugin",
|
||||
"backstage": {
|
||||
"role": "web-library",
|
||||
"pluginId": "app",
|
||||
"pluginPackages": [
|
||||
"@backstage/plugin-app",
|
||||
"@backstage/plugin-app-backend",
|
||||
"@backstage/plugin-app-node",
|
||||
"@backstage/plugin-app-react"
|
||||
]
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "dist/index.esm.js",
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/backstage/backstage",
|
||||
"directory": "plugins/app-react"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"sideEffects": false,
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "backstage-cli package build",
|
||||
"clean": "backstage-cli package clean",
|
||||
"lint": "backstage-cli package lint",
|
||||
"prepack": "backstage-cli package prepack",
|
||||
"postpack": "backstage-cli package postpack",
|
||||
"start": "backstage-cli package start",
|
||||
"test": "backstage-cli package test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/core-plugin-api": "workspace:^",
|
||||
"@backstage/frontend-plugin-api": "workspace:^",
|
||||
"@material-ui/core": "^4.9.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@backstage/frontend-test-utils": "workspace:^",
|
||||
"@backstage/test-utils": "workspace:^",
|
||||
"@testing-library/jest-dom": "^6.0.0",
|
||||
"@testing-library/react": "^16.0.0",
|
||||
"@types/react": "^18.0.0",
|
||||
"react": "^18.0.2",
|
||||
"react-dom": "^18.0.2",
|
||||
"react-router-dom": "^6.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^17.0.0 || ^18.0.0",
|
||||
"react": "^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0",
|
||||
"react-router-dom": "^6.3.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
## API Report File for "@backstage/plugin-app-react"
|
||||
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
import { AppTheme } from '@backstage/frontend-plugin-api';
|
||||
import { ComponentType } from 'react';
|
||||
import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionBlueprint } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
|
||||
import { IconComponent } from '@backstage/frontend-plugin-api';
|
||||
import { NavContentComponent } from '@backstage/frontend-plugin-api';
|
||||
import { NavContentComponentProps } from '@backstage/frontend-plugin-api';
|
||||
import { ReactNode } from 'react';
|
||||
import { SignInPageProps } from '@backstage/frontend-plugin-api';
|
||||
import { SwappableComponentRef } from '@backstage/frontend-plugin-api';
|
||||
import { TranslationMessages } from '@backstage/frontend-plugin-api';
|
||||
import { TranslationResource } from '@backstage/frontend-plugin-api';
|
||||
|
||||
// @public
|
||||
export const IconBundleBlueprint: ExtensionBlueprint<{
|
||||
kind: 'icon-bundle';
|
||||
params: {
|
||||
icons: { [key in string]: IconComponent };
|
||||
};
|
||||
output: ExtensionDataRef<
|
||||
{
|
||||
[x: string]: IconComponent;
|
||||
},
|
||||
'core.icons',
|
||||
{}
|
||||
>;
|
||||
inputs: {};
|
||||
config: {};
|
||||
configInput: {};
|
||||
dataRefs: {
|
||||
icons: ConfigurableExtensionDataRef<
|
||||
{
|
||||
[x: string]: IconComponent;
|
||||
},
|
||||
'core.icons',
|
||||
{}
|
||||
>;
|
||||
};
|
||||
}>;
|
||||
|
||||
// @public
|
||||
export const NavContentBlueprint: ExtensionBlueprint<{
|
||||
kind: 'nav-content';
|
||||
params: {
|
||||
component: NavContentComponent;
|
||||
};
|
||||
output: ExtensionDataRef<
|
||||
NavContentComponent,
|
||||
'core.nav-content.component',
|
||||
{}
|
||||
>;
|
||||
inputs: {};
|
||||
config: {};
|
||||
configInput: {};
|
||||
dataRefs: {
|
||||
component: ConfigurableExtensionDataRef<
|
||||
NavContentComponent,
|
||||
'core.nav-content.component',
|
||||
{}
|
||||
>;
|
||||
};
|
||||
}>;
|
||||
|
||||
export { NavContentComponent };
|
||||
|
||||
export { NavContentComponentProps };
|
||||
|
||||
// @public
|
||||
export const RouterBlueprint: ExtensionBlueprint<{
|
||||
kind: 'app-router-component';
|
||||
params: {
|
||||
Component?: [error: 'Use the `component` parameter instead'];
|
||||
component: (props: { children: ReactNode }) => JSX.Element | null;
|
||||
};
|
||||
output: ExtensionDataRef<
|
||||
(props: { children: ReactNode }) => JSX.Element | null,
|
||||
'app.router.wrapper',
|
||||
{}
|
||||
>;
|
||||
inputs: {};
|
||||
config: {};
|
||||
configInput: {};
|
||||
dataRefs: {
|
||||
component: ConfigurableExtensionDataRef<
|
||||
(props: { children: ReactNode }) => JSX.Element | null,
|
||||
'app.router.wrapper',
|
||||
{}
|
||||
>;
|
||||
};
|
||||
}>;
|
||||
|
||||
// @public
|
||||
export const SignInPageBlueprint: ExtensionBlueprint<{
|
||||
kind: 'sign-in-page';
|
||||
params: {
|
||||
loader: () => Promise<ComponentType<SignInPageProps>>;
|
||||
};
|
||||
output: ExtensionDataRef<
|
||||
ComponentType<SignInPageProps>,
|
||||
'core.sign-in-page.component',
|
||||
{}
|
||||
>;
|
||||
inputs: {};
|
||||
config: {};
|
||||
configInput: {};
|
||||
dataRefs: {
|
||||
component: ConfigurableExtensionDataRef<
|
||||
ComponentType<SignInPageProps>,
|
||||
'core.sign-in-page.component',
|
||||
{}
|
||||
>;
|
||||
};
|
||||
}>;
|
||||
|
||||
export { SignInPageProps };
|
||||
|
||||
// @public
|
||||
export const SwappableComponentBlueprint: ExtensionBlueprint<{
|
||||
kind: 'component';
|
||||
params: <Ref extends SwappableComponentRef<any>>(params: {
|
||||
component: Ref extends SwappableComponentRef<
|
||||
any,
|
||||
infer IExternalComponentProps
|
||||
>
|
||||
? {
|
||||
ref: Ref;
|
||||
} & ((props: IExternalComponentProps) => JSX.Element | null)
|
||||
: never;
|
||||
loader: Ref extends SwappableComponentRef<infer IInnerComponentProps, any>
|
||||
?
|
||||
| (() => (props: IInnerComponentProps) => JSX.Element | null)
|
||||
| (() => Promise<(props: IInnerComponentProps) => JSX.Element | null>)
|
||||
: never;
|
||||
}) => ExtensionBlueprintParams<{
|
||||
component: Ref extends SwappableComponentRef<
|
||||
any,
|
||||
infer IExternalComponentProps
|
||||
>
|
||||
? {
|
||||
ref: Ref;
|
||||
} & ((props: IExternalComponentProps) => JSX.Element | null)
|
||||
: never;
|
||||
loader: Ref extends SwappableComponentRef<infer IInnerComponentProps, any>
|
||||
?
|
||||
| (() => (props: IInnerComponentProps) => JSX.Element | null)
|
||||
| (() => Promise<(props: IInnerComponentProps) => JSX.Element | null>)
|
||||
: never;
|
||||
}>;
|
||||
output: ExtensionDataRef<
|
||||
{
|
||||
ref: SwappableComponentRef;
|
||||
loader:
|
||||
| (() => (props: {}) => JSX.Element | null)
|
||||
| (() => Promise<(props: {}) => JSX.Element | null>);
|
||||
},
|
||||
'core.swappableComponent',
|
||||
{}
|
||||
>;
|
||||
inputs: {};
|
||||
config: {};
|
||||
configInput: {};
|
||||
dataRefs: {
|
||||
component: ConfigurableExtensionDataRef<
|
||||
{
|
||||
ref: SwappableComponentRef;
|
||||
loader:
|
||||
| (() => (props: {}) => JSX.Element | null)
|
||||
| (() => Promise<(props: {}) => JSX.Element | null>);
|
||||
},
|
||||
'core.swappableComponent',
|
||||
{}
|
||||
>;
|
||||
};
|
||||
}>;
|
||||
|
||||
// @public
|
||||
export const ThemeBlueprint: ExtensionBlueprint<{
|
||||
kind: 'theme';
|
||||
params: {
|
||||
theme: AppTheme;
|
||||
};
|
||||
output: ExtensionDataRef<AppTheme, 'core.theme.theme', {}>;
|
||||
inputs: {};
|
||||
config: {};
|
||||
configInput: {};
|
||||
dataRefs: {
|
||||
theme: ConfigurableExtensionDataRef<AppTheme, 'core.theme.theme', {}>;
|
||||
};
|
||||
}>;
|
||||
|
||||
// @public
|
||||
export const TranslationBlueprint: ExtensionBlueprint<{
|
||||
kind: 'translation';
|
||||
params: {
|
||||
resource: TranslationResource | TranslationMessages;
|
||||
};
|
||||
output: ExtensionDataRef<
|
||||
| TranslationResource<string>
|
||||
| TranslationMessages<
|
||||
string,
|
||||
{
|
||||
[x: string]: string;
|
||||
},
|
||||
boolean
|
||||
>,
|
||||
'core.translation.translation',
|
||||
{}
|
||||
>;
|
||||
inputs: {};
|
||||
config: {};
|
||||
configInput: {};
|
||||
dataRefs: {
|
||||
translation: ConfigurableExtensionDataRef<
|
||||
| TranslationResource<string>
|
||||
| TranslationMessages<
|
||||
string,
|
||||
{
|
||||
[x: string]: string;
|
||||
},
|
||||
boolean
|
||||
>,
|
||||
'core.translation.translation',
|
||||
{}
|
||||
>;
|
||||
};
|
||||
}>;
|
||||
```
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
IconBundleBlueprint as _IconBundleBlueprint,
|
||||
NavContentBlueprint as _NavContentBlueprint,
|
||||
type NavContentComponent,
|
||||
type NavContentComponentProps,
|
||||
RouterBlueprint as _RouterBlueprint,
|
||||
SignInPageBlueprint as _SignInPageBlueprint,
|
||||
type SignInPageProps,
|
||||
SwappableComponentBlueprint as _SwappableComponentBlueprint,
|
||||
ThemeBlueprint as _ThemeBlueprint,
|
||||
TranslationBlueprint as _TranslationBlueprint,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
/**
|
||||
* Creates an extension that adds/replaces an app theme. This blueprint is limited to use by the app plugin.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const ThemeBlueprint = _ThemeBlueprint;
|
||||
|
||||
/**
|
||||
* Blueprint for creating swappable components from a SwappableComponentRef and a loader. This blueprint is limited to use by the app plugin.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const SwappableComponentBlueprint = _SwappableComponentBlueprint;
|
||||
|
||||
/**
|
||||
* Creates an extension that replaces the sign in page. This blueprint is limited to use by the app plugin.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const SignInPageBlueprint = _SignInPageBlueprint;
|
||||
|
||||
/**
|
||||
* Creates an extension that replaces the router component. This blueprint is limited to use by the app plugin.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const RouterBlueprint = _RouterBlueprint;
|
||||
|
||||
/**
|
||||
* Creates an extension that replaces the entire nav bar with your own component. This blueprint is limited to use by the app plugin.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const NavContentBlueprint = _NavContentBlueprint;
|
||||
|
||||
/**
|
||||
* Creates an extension that adds icon bundles to your app. This blueprint is limited to use by the app plugin.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const IconBundleBlueprint = _IconBundleBlueprint;
|
||||
|
||||
/**
|
||||
* Props for the `SignInPage` component.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type { SignInPageProps };
|
||||
|
||||
/**
|
||||
* The props for the {@link NavContentComponent}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type { NavContentComponentProps };
|
||||
|
||||
/**
|
||||
* A component that renders the nav bar content, to be passed to the {@link NavContentBlueprint}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type { NavContentComponent };
|
||||
|
||||
/**
|
||||
* Creates an extension that adds translations to your app. This blueprint is limited to use by the app plugin.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const TranslationBlueprint = _TranslationBlueprint;
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Web library for the app plugin.
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
export * from './blueprints';
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2026 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import '@testing-library/jest-dom';
|
||||
@@ -7,7 +7,8 @@
|
||||
"pluginPackages": [
|
||||
"@backstage/plugin-app",
|
||||
"@backstage/plugin-app-backend",
|
||||
"@backstage/plugin-app-node"
|
||||
"@backstage/plugin-app-node",
|
||||
"@backstage/plugin-app-react"
|
||||
]
|
||||
},
|
||||
"publishConfig": {
|
||||
@@ -54,6 +55,7 @@
|
||||
"@backstage/core-plugin-api": "workspace:^",
|
||||
"@backstage/frontend-plugin-api": "workspace:^",
|
||||
"@backstage/integration-react": "workspace:^",
|
||||
"@backstage/plugin-app-react": "workspace:^",
|
||||
"@backstage/plugin-permission-react": "workspace:^",
|
||||
"@backstage/theme": "workspace:^",
|
||||
"@backstage/types": "workspace:^",
|
||||
|
||||
@@ -94,6 +94,14 @@ export const AppNav = createExtension({
|
||||
},
|
||||
output: [coreExtensionData.reactElement],
|
||||
*factory({ inputs }) {
|
||||
if (inputs.content && inputs.content.node.spec.plugin?.id !== 'app') {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
`DEPRECATION WARNING: NavContent should only be installed as an extension in the app plugin. ` +
|
||||
`You can either use appPlugin.override(), or a module for the app plugin. The following extension will be ignored in the future: ${inputs.content.node.spec.id}`,
|
||||
);
|
||||
}
|
||||
|
||||
const Content =
|
||||
inputs.content?.get(NavContentBlueprint.dataRefs.component) ??
|
||||
DefaultNavContent;
|
||||
|
||||
@@ -74,6 +74,22 @@ export const AppRoot = createExtension({
|
||||
},
|
||||
output: [coreExtensionData.reactElement],
|
||||
factory({ inputs, apis }) {
|
||||
if (inputs.router && inputs.router.node.spec.plugin?.id !== 'app') {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
`DEPRECATION WARNING: Router should only be installed as an extension in the app plugin. ` +
|
||||
`You can either use appPlugin.override(), or a module for the app plugin. The following extension will be ignored in the future: ${inputs.router.node.spec.id}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (inputs.signInPage && inputs.signInPage.node.spec.plugin?.id !== 'app') {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
`DEPRECATION WARNING: SignInPage should only be installed as an extension in the app plugin. ` +
|
||||
`You can either use appPlugin.override(), or a module for the app plugin. The following extension will be ignored in the future: ${inputs.signInPage.node.spec.id}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (isProtectedApp()) {
|
||||
const identityApi = apis.get(identityApiRef);
|
||||
if (!identityApi) {
|
||||
|
||||
@@ -44,10 +44,24 @@ export const AppThemeApi = ApiBlueprint.makeWithOverrides({
|
||||
defineParams({
|
||||
api: appThemeApiRef,
|
||||
deps: {},
|
||||
factory: () =>
|
||||
AppThemeSelector.createWithStorage(
|
||||
factory: () => {
|
||||
const nonAppExtensions = inputs.themes.filter(
|
||||
i => i.node.spec.plugin?.id !== 'app',
|
||||
);
|
||||
|
||||
if (nonAppExtensions.length > 0) {
|
||||
const list = nonAppExtensions.map(i => i.node.spec.id).join(', ');
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
`DEPRECATION WARNING: Theme should only be installed as an extension in the app plugin. ` +
|
||||
`You can either use appPlugin.override(), or a module for the app plugin. The following extension will be ignored in the future: ${list}`,
|
||||
);
|
||||
}
|
||||
|
||||
return AppThemeSelector.createWithStorage(
|
||||
inputs.themes.map(i => i.get(ThemeBlueprint.dataRefs.theme)),
|
||||
),
|
||||
);
|
||||
},
|
||||
}),
|
||||
);
|
||||
},
|
||||
|
||||
@@ -40,12 +40,26 @@ export const IconsApi = ApiBlueprint.makeWithOverrides({
|
||||
defineParams({
|
||||
api: iconsApiRef,
|
||||
deps: {},
|
||||
factory: () =>
|
||||
new DefaultIconsApi(
|
||||
factory: () => {
|
||||
const nonAppExtensions = inputs.icons.filter(
|
||||
i => i.node.spec.plugin?.id !== 'app',
|
||||
);
|
||||
|
||||
if (nonAppExtensions.length > 0) {
|
||||
const list = nonAppExtensions.map(i => i.node.spec.id).join(', ');
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
`DEPRECATION WARNING: IconBundle should only be installed as an extension in the app plugin. ` +
|
||||
`You can either use appPlugin.override(), or a module for the app plugin. The following extension will be ignored in the future: ${list}`,
|
||||
);
|
||||
}
|
||||
|
||||
return new DefaultIconsApi(
|
||||
inputs.icons
|
||||
.map(i => i.get(IconBundleBlueprint.dataRefs.icons))
|
||||
.reduce((acc, bundle) => ({ ...acc, ...bundle }), defaultIcons),
|
||||
),
|
||||
);
|
||||
},
|
||||
}),
|
||||
);
|
||||
},
|
||||
|
||||
@@ -42,13 +42,27 @@ export const TranslationsApi = ApiBlueprint.makeWithOverrides({
|
||||
defineParams({
|
||||
api: translationApiRef,
|
||||
deps: { languageApi: appLanguageApiRef },
|
||||
factory: ({ languageApi }) =>
|
||||
I18nextTranslationApi.create({
|
||||
factory: ({ languageApi }) => {
|
||||
const nonAppExtensions = inputs.translations.filter(
|
||||
i => i.node.spec.plugin?.id !== 'app',
|
||||
);
|
||||
|
||||
if (nonAppExtensions.length > 0) {
|
||||
const list = nonAppExtensions.map(i => i.node.spec.id).join(', ');
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
`DEPRECATION WARNING: Translations should only be installed as an extension in the app plugin. ` +
|
||||
`You can either use appPlugin.override(), or a module for the app plugin. The following extension will be ignored in the future: ${list}`,
|
||||
);
|
||||
}
|
||||
|
||||
return I18nextTranslationApi.create({
|
||||
languageApi,
|
||||
resources: inputs.translations.map(i =>
|
||||
i.get(TranslationBlueprint.dataRefs.translation),
|
||||
),
|
||||
}),
|
||||
});
|
||||
},
|
||||
}),
|
||||
);
|
||||
},
|
||||
|
||||
@@ -4155,6 +4155,33 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@backstage/plugin-app-react@workspace:^, @backstage/plugin-app-react@workspace:plugins/app-react":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@backstage/plugin-app-react@workspace:plugins/app-react"
|
||||
dependencies:
|
||||
"@backstage/cli": "workspace:^"
|
||||
"@backstage/core-plugin-api": "workspace:^"
|
||||
"@backstage/frontend-plugin-api": "workspace:^"
|
||||
"@backstage/frontend-test-utils": "workspace:^"
|
||||
"@backstage/test-utils": "workspace:^"
|
||||
"@material-ui/core": "npm:^4.9.13"
|
||||
"@testing-library/jest-dom": "npm:^6.0.0"
|
||||
"@testing-library/react": "npm:^16.0.0"
|
||||
"@types/react": "npm:^18.0.0"
|
||||
react: "npm:^18.0.2"
|
||||
react-dom: "npm:^18.0.2"
|
||||
react-router-dom: "npm:^6.3.0"
|
||||
peerDependencies:
|
||||
"@types/react": ^17.0.0 || ^18.0.0
|
||||
react: ^17.0.0 || ^18.0.0
|
||||
react-dom: ^17.0.0 || ^18.0.0
|
||||
react-router-dom: ^6.3.0
|
||||
peerDependenciesMeta:
|
||||
"@types/react":
|
||||
optional: true
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@backstage/plugin-app-visualizer@workspace:^, @backstage/plugin-app-visualizer@workspace:plugins/app-visualizer":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@backstage/plugin-app-visualizer@workspace:plugins/app-visualizer"
|
||||
@@ -4194,6 +4221,7 @@ __metadata:
|
||||
"@backstage/frontend-plugin-api": "workspace:^"
|
||||
"@backstage/frontend-test-utils": "workspace:^"
|
||||
"@backstage/integration-react": "workspace:^"
|
||||
"@backstage/plugin-app-react": "workspace:^"
|
||||
"@backstage/plugin-permission-react": "workspace:^"
|
||||
"@backstage/test-utils": "workspace:^"
|
||||
"@backstage/theme": "workspace:^"
|
||||
|
||||
Reference in New Issue
Block a user