Merge pull request #32496 from backstage/rugvip/internal
frontend-plugin-api: add new internal extension input option, complete app-react deprecations
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/frontend-app-api': patch
|
||||
---
|
||||
|
||||
Implemented support for the `internal` extension input option.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/core-compat-api': patch
|
||||
'@backstage/plugin-app-visualizer': patch
|
||||
---
|
||||
|
||||
Internal updates for blueprint moves to `@backstage/plugin-app-react`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/create-app': patch
|
||||
---
|
||||
|
||||
Switched `next-app` template to use blueprint from `@backstage/plugin-app-react`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/frontend-plugin-api': patch
|
||||
---
|
||||
|
||||
Added a new `internal` option to `createExtensionInput` that marks the input as only allowing attachments from the same plugin.
|
||||
@@ -0,0 +1,13 @@
|
||||
---
|
||||
'@backstage/frontend-plugin-api': minor
|
||||
---
|
||||
|
||||
**BREAKING**: The following blueprints have been removed and are now only available from `@backstage/plugin-app-react`:
|
||||
|
||||
- `IconBundleBlueprint`
|
||||
- `NavContentBlueprint`
|
||||
- `RouterBlueprint`
|
||||
- `SignInPageBlueprint`
|
||||
- `SwappableComponentBlueprint`
|
||||
- `ThemeBlueprint`
|
||||
- `TranslationBlueprint`
|
||||
@@ -0,0 +1,13 @@
|
||||
---
|
||||
'@backstage/plugin-app': minor
|
||||
---
|
||||
|
||||
**BREAKING**: Extensions created with the following blueprints must now be provided via an override or a module for the `app` plugin. Extensions from other plugins will now trigger a warning in the app and be ignored.
|
||||
|
||||
- `IconBundleBlueprint`
|
||||
- `NavContentBlueprint`
|
||||
- `RouterBlueprint`
|
||||
- `SignInPageBlueprint`
|
||||
- `SwappableComponentBlueprint`
|
||||
- `ThemeBlueprint`
|
||||
- `TranslationBlueprint`
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/frontend-defaults': patch
|
||||
---
|
||||
|
||||
Dependency update for tests.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-app-react': patch
|
||||
---
|
||||
|
||||
Internal refactor to move implementation of blueprints from `@backstage/frontend-plugin-api` to this package.
|
||||
@@ -174,6 +174,8 @@ const navigationExtension = createExtension({
|
||||
|
||||
The input (see [1] above) is an object that we create using `createExtensionInput`. The first argument is the set of extension data that we accept via this input, and works just like the `output` option. The second argument is optional, and it allows us to put constraints on the extensions that are attached to our input. If the `singleton: true` option is set, only a single extension can be attached at a time, and unless the `optional: true` option is set it will also be required that there is exactly one attached extension.
|
||||
|
||||
Another option that can be used when creating an extension input is the `internal: true` option, which restricts the input to only accept extensions from the same plugin as the extension defining the input. Extensions from other plugins that attempt to attach to an internal input will be ignored, and a warning will be reported. This is useful when you want to limit extensibility to overrides and modules of your plugin, rather than letting it be open to any plugin.
|
||||
|
||||
So how can we now attach the output to the parent extension's input? If we think about a navigation component, like the Sidebar in Backstage, there might be plugins that want to attach a link to their plugin to this navigation component. In this case the plugin only needs to know the extension `id` and the name of the extension `input` to attach the extension `output` returned by the `factory` to the specified extension:
|
||||
|
||||
```tsx
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
"@backstage/integration-react": "workspace:^",
|
||||
"@backstage/plugin-api-docs": "workspace:^",
|
||||
"@backstage/plugin-app": "workspace:^",
|
||||
"@backstage/plugin-app-react": "workspace:^",
|
||||
"@backstage/plugin-app-visualizer": "workspace:^",
|
||||
"@backstage/plugin-auth": "workspace:^",
|
||||
"@backstage/plugin-auth-react": "workspace:^",
|
||||
|
||||
@@ -14,10 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
SwappableComponentBlueprint,
|
||||
NotFoundErrorPage,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { NotFoundErrorPage } from '@backstage/frontend-plugin-api';
|
||||
import { SwappableComponentBlueprint } from '@backstage/plugin-app-react';
|
||||
import Box from '@material-ui/core/Box';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import { Button } from '@backstage/core-components';
|
||||
|
||||
@@ -28,10 +28,8 @@ import {
|
||||
import SearchIcon from '@material-ui/icons/Search';
|
||||
import MenuIcon from '@material-ui/icons/Menu';
|
||||
import BuildIcon from '@material-ui/icons/Build';
|
||||
import {
|
||||
createFrontendModule,
|
||||
NavContentBlueprint,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { createFrontendModule } from '@backstage/frontend-plugin-api';
|
||||
import { NavContentBlueprint } from '@backstage/plugin-app-react';
|
||||
import { SidebarSearchModal } from '@backstage/plugin-search';
|
||||
import { NotificationsSidebarItem } from '@backstage/plugin-notifications';
|
||||
import {
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
"dependencies": {
|
||||
"@backstage/core-plugin-api": "workspace:^",
|
||||
"@backstage/frontend-plugin-api": "workspace:^",
|
||||
"@backstage/plugin-app-react": "workspace:^",
|
||||
"@backstage/plugin-catalog-react": "workspace:^",
|
||||
"@backstage/types": "workspace:^",
|
||||
"@backstage/version-bridge": "workspace:^",
|
||||
|
||||
@@ -16,21 +16,23 @@
|
||||
|
||||
import { ComponentType } from 'react';
|
||||
import {
|
||||
SwappableComponentBlueprint,
|
||||
ApiBlueprint,
|
||||
ErrorDisplayProps,
|
||||
createExtension,
|
||||
createFrontendModule,
|
||||
ExtensionDefinition,
|
||||
FrontendModule,
|
||||
IconBundleBlueprint,
|
||||
RouterBlueprint,
|
||||
SignInPageBlueprint,
|
||||
ThemeBlueprint,
|
||||
ErrorDisplay as SwappableErrorDisplay,
|
||||
NotFoundErrorPage as SwappableNotFoundErrorPage,
|
||||
Progress as SwappableProgress,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import {
|
||||
IconBundleBlueprint,
|
||||
RouterBlueprint,
|
||||
SignInPageBlueprint,
|
||||
SwappableComponentBlueprint,
|
||||
ThemeBlueprint,
|
||||
} from '@backstage/plugin-app-react';
|
||||
import {
|
||||
AnyApiFactory,
|
||||
AppComponents,
|
||||
|
||||
@@ -55,6 +55,7 @@ import { version as ui } from '../../../ui/package.json';
|
||||
import { version as pluginApiDocs } from '../../../../plugins/api-docs/package.json';
|
||||
import { version as pluginAppVisualizer } from '../../../../plugins/app-visualizer/package.json';
|
||||
import { version as pluginAppBackend } from '../../../../plugins/app-backend/package.json';
|
||||
import { version as pluginAppReact } from '../../../../plugins/app-react/package.json';
|
||||
import { version as pluginAuthBackend } from '../../../../plugins/auth-backend/package.json';
|
||||
import { version as pluginAuthBackendModuleGithubProvider } from '../../../../plugins/auth-backend-module-github-provider/package.json';
|
||||
import { version as pluginAuthBackendModuleGuestProvider } from '../../../../plugins/auth-backend-module-guest-provider/package.json';
|
||||
@@ -118,6 +119,7 @@ export const packageVersions = {
|
||||
'@backstage/repo-tools': repoTools,
|
||||
'@backstage/plugin-api-docs': pluginApiDocs,
|
||||
'@backstage/plugin-app-backend': pluginAppBackend,
|
||||
'@backstage/plugin-app-react': pluginAppReact,
|
||||
'@backstage/plugin-app-visualizer': pluginAppVisualizer,
|
||||
'@backstage/plugin-auth-backend': pluginAuthBackend,
|
||||
'@backstage/plugin-auth-backend-module-github-provider':
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"@backstage/frontend-defaults": "^{{ version '@backstage/frontend-defaults'}}",
|
||||
"@backstage/frontend-plugin-api": "^{{ version '@backstage/frontend-plugin-api'}}",
|
||||
"@backstage/integration-react": "^{{ version '@backstage/integration-react'}}",
|
||||
"@backstage/plugin-app-react": "^{{ version '@backstage/plugin-app-react'}}",
|
||||
"@backstage/plugin-app-visualizer": "^{{ version '@backstage/plugin-app-visualizer'}}",
|
||||
"@backstage/plugin-catalog": "^{{ version '@backstage/plugin-catalog'}}",
|
||||
"@backstage/plugin-notifications": "^{{ version '@backstage/plugin-notifications'}}",
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
} from '@backstage/core-components';
|
||||
import { compatWrapper } from '@backstage/core-compat-api';
|
||||
import { Sidebar } from '@backstage/core-components';
|
||||
import { NavContentBlueprint } from '@backstage/frontend-plugin-api';
|
||||
import { NavContentBlueprint } from '@backstage/plugin-app-react';
|
||||
import { SidebarLogo } from './SidebarLogo';
|
||||
import CreateComponentIcon from '@material-ui/icons/AddCircleOutline';
|
||||
import HomeIcon from '@material-ui/icons/Home';
|
||||
|
||||
@@ -59,6 +59,14 @@ export type AppErrorTypes = {
|
||||
inputName: string;
|
||||
};
|
||||
};
|
||||
EXTENSION_INPUT_INTERNAL_IGNORED: {
|
||||
context: {
|
||||
node: AppNode;
|
||||
inputName: string;
|
||||
extensionId: string;
|
||||
plugin: FrontendPlugin;
|
||||
};
|
||||
};
|
||||
EXTENSION_ATTACHMENT_CONFLICT: {
|
||||
context: {
|
||||
node: AppNode;
|
||||
|
||||
@@ -142,6 +142,36 @@ function createV1Extension(opts: {
|
||||
return ext;
|
||||
}
|
||||
|
||||
function mirrorInputs(ctx: {
|
||||
inputs: {
|
||||
[name in string]:
|
||||
| undefined
|
||||
| ResolvedExtensionInput<ExtensionInput>
|
||||
| Array<ResolvedExtensionInput<ExtensionInput>>;
|
||||
};
|
||||
}) {
|
||||
return [
|
||||
inputMirrorDataRef(
|
||||
Object.fromEntries(
|
||||
Object.entries(ctx.inputs).map(([k, v]) => [
|
||||
k,
|
||||
Array.isArray(v)
|
||||
? v.map(vi => ({
|
||||
node: vi.node,
|
||||
test: vi.get(testDataRef),
|
||||
other: vi.get(otherDataRef),
|
||||
}))
|
||||
: {
|
||||
node: v?.node,
|
||||
test: v?.get(testDataRef),
|
||||
other: v?.get(otherDataRef),
|
||||
},
|
||||
]),
|
||||
),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
describe('instantiateAppNodeTree', () => {
|
||||
describe('v1', () => {
|
||||
const simpleExtension = createV1Extension({
|
||||
@@ -237,6 +267,60 @@ describe('instantiateAppNodeTree', () => {
|
||||
expect(childNode?.instance).toBeDefined();
|
||||
});
|
||||
|
||||
it('should ignore non-matching plugin attachments for internal inputs', () => {
|
||||
const otherPlugin = createFrontendPlugin({ pluginId: 'other' });
|
||||
const tree = resolveAppTree(
|
||||
'root-node',
|
||||
[
|
||||
makeSpec(
|
||||
resolveExtensionDefinition(
|
||||
createExtension({
|
||||
attachTo: { id: 'ignored', input: 'ignored' },
|
||||
inputs: {
|
||||
test: createExtensionInput([testDataRef], {
|
||||
singleton: true,
|
||||
internal: true,
|
||||
}),
|
||||
},
|
||||
output: [inputMirrorDataRef],
|
||||
factory: mirrorInputs,
|
||||
}),
|
||||
{ namespace: 'root-node' },
|
||||
),
|
||||
),
|
||||
makeSpec(simpleExtension, {
|
||||
id: 'child-node-app',
|
||||
attachTo: { id: 'root-node', input: 'test' },
|
||||
}),
|
||||
makeSpec(simpleExtension, {
|
||||
id: 'child-node-other',
|
||||
attachTo: { id: 'root-node', input: 'test' },
|
||||
plugin: otherPlugin,
|
||||
}),
|
||||
],
|
||||
collector,
|
||||
);
|
||||
|
||||
instantiateAppNodeTree(tree.root, testApis, collector);
|
||||
|
||||
expect(tree.root.instance?.getData(inputMirrorDataRef)).toMatchObject({
|
||||
test: { node: { spec: { id: 'child-node-app' } }, test: 'test' },
|
||||
});
|
||||
expect(collector.collectErrors()).toEqual([
|
||||
{
|
||||
code: 'EXTENSION_INPUT_INTERNAL_IGNORED',
|
||||
message:
|
||||
"extension 'child-node-other' from plugin 'other' attached to input 'test' on 'root-node' was ignored, the input is marked as internal and attached extensions must therefore be provided via an override or a module for the 'app' plugin, not the 'other' plugin",
|
||||
context: {
|
||||
node: tree.root,
|
||||
inputName: 'test',
|
||||
extensionId: 'child-node-other',
|
||||
plugin: otherPlugin,
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should not instantiate disabled attachments', () => {
|
||||
const tree = resolveAppTree(
|
||||
'root-node',
|
||||
@@ -783,36 +867,6 @@ describe('instantiateAppNodeTree', () => {
|
||||
{ namespace: 'app' },
|
||||
);
|
||||
|
||||
function mirrorInputs(ctx: {
|
||||
inputs: {
|
||||
[name in string]:
|
||||
| undefined
|
||||
| ResolvedExtensionInput<ExtensionInput>
|
||||
| Array<ResolvedExtensionInput<ExtensionInput>>;
|
||||
};
|
||||
}) {
|
||||
return [
|
||||
inputMirrorDataRef(
|
||||
Object.fromEntries(
|
||||
Object.entries(ctx.inputs).map(([k, v]) => [
|
||||
k,
|
||||
Array.isArray(v)
|
||||
? v.map(vi => ({
|
||||
node: vi.node,
|
||||
test: vi.get(testDataRef),
|
||||
other: vi.get(otherDataRef),
|
||||
}))
|
||||
: {
|
||||
node: v?.node,
|
||||
test: v?.get(testDataRef),
|
||||
other: v?.get(otherDataRef),
|
||||
},
|
||||
]),
|
||||
),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
it('should instantiate a single node', () => {
|
||||
const tree = resolveAppTree(
|
||||
'root-node',
|
||||
|
||||
@@ -247,10 +247,32 @@ function resolveV2Inputs(
|
||||
inputMap: { [inputName in string]: ExtensionInput },
|
||||
attachments: ReadonlyMap<string, AppNode[]>,
|
||||
parentCollector: ErrorCollector<{ node: AppNode }>,
|
||||
node: AppNode,
|
||||
): ResolvedExtensionInputs<{ [inputName in string]: ExtensionInput }> {
|
||||
return mapValues(inputMap, (input, inputName) => {
|
||||
const attachedNodes = attachments.get(inputName) ?? [];
|
||||
const allAttachedNodes = attachments.get(inputName) ?? [];
|
||||
const collector = parentCollector.child({ inputName });
|
||||
const inputPluginId = node.spec.plugin.id;
|
||||
|
||||
const attachedNodes = input.config.internal
|
||||
? allAttachedNodes.filter(attachment => {
|
||||
const attachmentPluginId = attachment.spec.plugin.id;
|
||||
if (attachmentPluginId !== inputPluginId) {
|
||||
collector.report({
|
||||
code: 'EXTENSION_INPUT_INTERNAL_IGNORED',
|
||||
message:
|
||||
`extension '${attachment.spec.id}' from plugin '${attachmentPluginId}' attached to input '${inputName}' on '${node.spec.id}' was ignored, ` +
|
||||
`the input is marked as internal and attached extensions must therefore be provided via an override or a module for the '${inputPluginId}' plugin, not the '${attachmentPluginId}' plugin`,
|
||||
context: {
|
||||
extensionId: attachment.spec.id,
|
||||
plugin: attachment.spec.plugin,
|
||||
},
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
: allAttachedNodes;
|
||||
|
||||
if (input.config.singleton) {
|
||||
if (attachedNodes.length > 1) {
|
||||
@@ -371,6 +393,7 @@ export function createAppNodeInstance(options: {
|
||||
internalExtension.inputs,
|
||||
attachments,
|
||||
collector,
|
||||
node,
|
||||
),
|
||||
};
|
||||
const outputDataValues = options.extensionFactoryMiddleware
|
||||
|
||||
@@ -38,6 +38,14 @@ export type AppErrorTypes = {
|
||||
EXTENSION_INPUT_DATA_MISSING: {
|
||||
context: { node: AppNode; inputName: string };
|
||||
};
|
||||
EXTENSION_INPUT_INTERNAL_IGNORED: {
|
||||
context: {
|
||||
node: AppNode;
|
||||
inputName: string;
|
||||
extensionId: string;
|
||||
plugin: FrontendPlugin;
|
||||
};
|
||||
};
|
||||
EXTENSION_ATTACHMENT_CONFLICT: {
|
||||
context: { node: AppNode; inputName: string };
|
||||
};
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@backstage/core-plugin-api": "workspace:^",
|
||||
"@backstage/plugin-app-react": "workspace:^",
|
||||
"@backstage/test-utils": "workspace:^",
|
||||
"@testing-library/jest-dom": "^6.0.0",
|
||||
"@testing-library/react": "^16.0.0",
|
||||
|
||||
@@ -22,11 +22,11 @@ import {
|
||||
PageBlueprint,
|
||||
createFrontendPlugin,
|
||||
createFrontendFeatureLoader,
|
||||
ThemeBlueprint,
|
||||
createFrontendModule,
|
||||
useAppNode,
|
||||
FrontendPluginInfo,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { ThemeBlueprint } from '@backstage/plugin-app-react';
|
||||
import { screen, waitFor } from '@testing-library/react';
|
||||
import { createApp } from './createApp';
|
||||
import { mockApis, renderWithEffects } from '@backstage/test-utils';
|
||||
@@ -60,8 +60,8 @@ describe('createApp', () => {
|
||||
}),
|
||||
},
|
||||
features: [
|
||||
createFrontendPlugin({
|
||||
pluginId: 'test',
|
||||
createFrontendModule({
|
||||
pluginId: 'app',
|
||||
extensions: [
|
||||
ThemeBlueprint.make({
|
||||
name: 'derp',
|
||||
|
||||
@@ -14,10 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
SignInPageBlueprint,
|
||||
createFrontendModule,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { createFrontendModule } from '@backstage/frontend-plugin-api';
|
||||
import { SignInPageBlueprint } from '@backstage/plugin-app-react';
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import { useEffect } from 'react';
|
||||
import { createPublicSignInApp } from './createPublicSignInApp';
|
||||
|
||||
@@ -22,6 +22,7 @@ const DEFAULT_WARNING_CODES: Array<keyof AppErrorTypes> = [
|
||||
'EXTENSION_IGNORED',
|
||||
'INVALID_EXTENSION_CONFIG_KEY',
|
||||
'EXTENSION_INPUT_DATA_IGNORED',
|
||||
'EXTENSION_INPUT_INTERNAL_IGNORED',
|
||||
'EXTENSION_OUTPUT_IGNORED',
|
||||
];
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ import { ExpandRecursive } from '@backstage/types';
|
||||
import { ExtensionBlueprint as ExtensionBlueprint_2 } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionBlueprintParams as ExtensionBlueprintParams_2 } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionDataRef as ExtensionDataRef_2 } from '@backstage/frontend-plugin-api';
|
||||
import { IconComponent as IconComponent_2 } from '@backstage/frontend-plugin-api';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
import { JsonValue } from '@backstage/types';
|
||||
import { JSX as JSX_2 } from 'react/jsx-runtime';
|
||||
@@ -21,7 +20,6 @@ import { JSX as JSX_3 } from 'react';
|
||||
import { Observable } from '@backstage/types';
|
||||
import { PropsWithChildren } from 'react';
|
||||
import { ReactNode } from 'react';
|
||||
import { RouteRef as RouteRef_2 } from '@backstage/frontend-plugin-api';
|
||||
import { SwappableComponentRef as SwappableComponentRef_2 } from '@backstage/frontend-plugin-api';
|
||||
import type { z } from 'zod';
|
||||
|
||||
@@ -271,30 +269,6 @@ export const AppRootElementBlueprint: ExtensionBlueprint_2<{
|
||||
dataRefs: never;
|
||||
}>;
|
||||
|
||||
// @public @deprecated
|
||||
export const AppRootWrapperBlueprint: ExtensionBlueprint_2<{
|
||||
kind: 'app-root-wrapper';
|
||||
params: {
|
||||
Component?: [error: 'Use the `component` parameter instead'];
|
||||
component: (props: { children: ReactNode }) => JSX.Element | null;
|
||||
};
|
||||
output: ExtensionDataRef_2<
|
||||
(props: { children: ReactNode }) => JSX.Element | null,
|
||||
'app.root.wrapper',
|
||||
{}
|
||||
>;
|
||||
inputs: {};
|
||||
config: {};
|
||||
configInput: {};
|
||||
dataRefs: {
|
||||
component: ConfigurableExtensionDataRef_2<
|
||||
(props: { children: ReactNode }) => JSX.Element | null,
|
||||
'app.root.wrapper',
|
||||
{}
|
||||
>;
|
||||
};
|
||||
}>;
|
||||
|
||||
// @public
|
||||
export type AppTheme = {
|
||||
id: string;
|
||||
@@ -604,7 +578,7 @@ export function createExtensionDataRef<TData>(): {
|
||||
}): ConfigurableExtensionDataRef<TData, TId>;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
// @public
|
||||
export function createExtensionInput<
|
||||
UExtensionData extends ExtensionDataRef<
|
||||
unknown,
|
||||
@@ -616,6 +590,7 @@ export function createExtensionInput<
|
||||
TConfig extends {
|
||||
singleton?: boolean;
|
||||
optional?: boolean;
|
||||
internal?: boolean;
|
||||
},
|
||||
>(
|
||||
extensionData: Array<UExtensionData>,
|
||||
@@ -630,6 +605,7 @@ export function createExtensionInput<
|
||||
{
|
||||
singleton: TConfig['singleton'] extends true ? true : false;
|
||||
optional: TConfig['optional'] extends true ? true : false;
|
||||
internal: TConfig['internal'] extends true ? true : false;
|
||||
}
|
||||
>;
|
||||
|
||||
@@ -1288,9 +1264,11 @@ export interface ExtensionInput<
|
||||
TConfig extends {
|
||||
singleton: boolean;
|
||||
optional: boolean;
|
||||
internal?: boolean;
|
||||
} = {
|
||||
singleton: boolean;
|
||||
optional: boolean;
|
||||
internal?: boolean;
|
||||
},
|
||||
> {
|
||||
// (undocumented)
|
||||
@@ -1457,33 +1435,6 @@ export const googleAuthApiRef: ApiRef<
|
||||
SessionApi
|
||||
>;
|
||||
|
||||
// @public @deprecated (undocumented)
|
||||
export const IconBundleBlueprint: ExtensionBlueprint_2<{
|
||||
kind: 'icon-bundle';
|
||||
params: {
|
||||
icons: { [key in string]: IconComponent };
|
||||
};
|
||||
output: ExtensionDataRef_2<
|
||||
{
|
||||
[x: string]: IconComponent;
|
||||
},
|
||||
'core.icons',
|
||||
{}
|
||||
>;
|
||||
inputs: {};
|
||||
config: {};
|
||||
configInput: {};
|
||||
dataRefs: {
|
||||
icons: ConfigurableExtensionDataRef_2<
|
||||
{
|
||||
[x: string]: IconComponent;
|
||||
},
|
||||
'core.icons',
|
||||
{}
|
||||
>;
|
||||
};
|
||||
}>;
|
||||
|
||||
// @public
|
||||
export type IconComponent = ComponentType<{
|
||||
fontSize?: 'medium' | 'large' | 'small' | 'inherit';
|
||||
@@ -1522,45 +1473,6 @@ export const microsoftAuthApiRef: ApiRef<
|
||||
SessionApi
|
||||
>;
|
||||
|
||||
// @public @deprecated
|
||||
export const NavContentBlueprint: ExtensionBlueprint_2<{
|
||||
kind: 'nav-content';
|
||||
params: {
|
||||
component: NavContentComponent;
|
||||
};
|
||||
output: ExtensionDataRef_2<
|
||||
NavContentComponent,
|
||||
'core.nav-content.component',
|
||||
{}
|
||||
>;
|
||||
inputs: {};
|
||||
config: {};
|
||||
configInput: {};
|
||||
dataRefs: {
|
||||
component: ConfigurableExtensionDataRef_2<
|
||||
NavContentComponent,
|
||||
'core.nav-content.component',
|
||||
{}
|
||||
>;
|
||||
};
|
||||
}>;
|
||||
|
||||
// @public
|
||||
export type NavContentComponent = (
|
||||
props: NavContentComponentProps,
|
||||
) => JSX.Element | null;
|
||||
|
||||
// @public
|
||||
export interface NavContentComponentProps {
|
||||
items: Array<{
|
||||
icon: IconComponent_2;
|
||||
title: string;
|
||||
routeRef: RouteRef_2<undefined>;
|
||||
to: string;
|
||||
text: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export const NavItemBlueprint: ExtensionBlueprint_2<{
|
||||
kind: 'nav-item';
|
||||
@@ -1923,30 +1835,6 @@ export type RouteFunc<TParams extends AnyRouteRefParams> = (
|
||||
: readonly [params: TParams]
|
||||
) => string;
|
||||
|
||||
// @public @deprecated (undocumented)
|
||||
export const RouterBlueprint: ExtensionBlueprint_2<{
|
||||
kind: 'app-router-component';
|
||||
params: {
|
||||
Component?: [error: 'Use the `component` parameter instead'];
|
||||
component: (props: { children: ReactNode }) => JSX.Element | null;
|
||||
};
|
||||
output: ExtensionDataRef_2<
|
||||
(props: { children: ReactNode }) => JSX.Element | null,
|
||||
'app.router.wrapper',
|
||||
{}
|
||||
>;
|
||||
inputs: {};
|
||||
config: {};
|
||||
configInput: {};
|
||||
dataRefs: {
|
||||
component: ConfigurableExtensionDataRef_2<
|
||||
(props: { children: ReactNode }) => JSX.Element | null,
|
||||
'app.router.wrapper',
|
||||
{}
|
||||
>;
|
||||
};
|
||||
}>;
|
||||
|
||||
// @public
|
||||
export interface RouteRef<
|
||||
TParams extends AnyRouteRefParams = AnyRouteRefParams,
|
||||
@@ -1998,35 +1886,6 @@ export namespace SessionState {
|
||||
export type SignedOut = typeof SessionState.SignedOut;
|
||||
}
|
||||
|
||||
// @public @deprecated
|
||||
export const SignInPageBlueprint: ExtensionBlueprint_2<{
|
||||
kind: 'sign-in-page';
|
||||
params: {
|
||||
loader: () => Promise<ComponentType<SignInPageProps>>;
|
||||
};
|
||||
output: ExtensionDataRef_2<
|
||||
ComponentType<SignInPageProps>,
|
||||
'core.sign-in-page.component',
|
||||
{}
|
||||
>;
|
||||
inputs: {};
|
||||
config: {};
|
||||
configInput: {};
|
||||
dataRefs: {
|
||||
component: ConfigurableExtensionDataRef_2<
|
||||
ComponentType<SignInPageProps>,
|
||||
'core.sign-in-page.component',
|
||||
{}
|
||||
>;
|
||||
};
|
||||
}>;
|
||||
|
||||
// @public
|
||||
export type SignInPageProps = {
|
||||
onSignInSuccess(identityApi: IdentityApi): void;
|
||||
children?: ReactNode;
|
||||
};
|
||||
|
||||
// @public
|
||||
export interface StorageApi {
|
||||
forBucket(name: string): StorageApi;
|
||||
@@ -2066,65 +1925,6 @@ export interface SubRouteRef<
|
||||
readonly T: TParams;
|
||||
}
|
||||
|
||||
// @public @deprecated
|
||||
export const SwappableComponentBlueprint: ExtensionBlueprint_2<{
|
||||
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_2<{
|
||||
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_2<
|
||||
{
|
||||
ref: SwappableComponentRef;
|
||||
loader:
|
||||
| (() => (props: {}) => JSX.Element | null)
|
||||
| (() => Promise<(props: {}) => JSX.Element | null>);
|
||||
},
|
||||
'core.swappableComponent',
|
||||
{}
|
||||
>;
|
||||
inputs: {};
|
||||
config: {};
|
||||
configInput: {};
|
||||
dataRefs: {
|
||||
component: ConfigurableExtensionDataRef_2<
|
||||
{
|
||||
ref: SwappableComponentRef;
|
||||
loader:
|
||||
| (() => (props: {}) => JSX.Element | null)
|
||||
| (() => Promise<(props: {}) => JSX.Element | null>);
|
||||
},
|
||||
'core.swappableComponent',
|
||||
{}
|
||||
>;
|
||||
};
|
||||
}>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type SwappableComponentRef<
|
||||
TInnerComponentProps extends {} = {},
|
||||
@@ -2150,21 +1950,6 @@ export interface SwappableComponentsApi {
|
||||
// @public
|
||||
export const swappableComponentsApiRef: ApiRef_2<SwappableComponentsApi>;
|
||||
|
||||
// @public @deprecated
|
||||
export const ThemeBlueprint: ExtensionBlueprint_2<{
|
||||
kind: 'theme';
|
||||
params: {
|
||||
theme: AppTheme;
|
||||
};
|
||||
output: ExtensionDataRef_2<AppTheme, 'core.theme.theme', {}>;
|
||||
inputs: {};
|
||||
config: {};
|
||||
configInput: {};
|
||||
dataRefs: {
|
||||
theme: ConfigurableExtensionDataRef_2<AppTheme, 'core.theme.theme', {}>;
|
||||
};
|
||||
}>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type TranslationApi = {
|
||||
getTranslation<
|
||||
@@ -2186,43 +1971,6 @@ export type TranslationApi = {
|
||||
// @public (undocumented)
|
||||
export const translationApiRef: ApiRef<TranslationApi>;
|
||||
|
||||
// @public @deprecated
|
||||
export const TranslationBlueprint: ExtensionBlueprint_2<{
|
||||
kind: 'translation';
|
||||
params: {
|
||||
resource: TranslationResource | TranslationMessages;
|
||||
};
|
||||
output: ExtensionDataRef_2<
|
||||
| TranslationResource<string>
|
||||
| TranslationMessages<
|
||||
string,
|
||||
{
|
||||
[x: string]: string;
|
||||
},
|
||||
boolean
|
||||
>,
|
||||
'core.translation.translation',
|
||||
{}
|
||||
>;
|
||||
inputs: {};
|
||||
config: {};
|
||||
configInput: {};
|
||||
dataRefs: {
|
||||
translation: ConfigurableExtensionDataRef_2<
|
||||
| TranslationResource<string>
|
||||
| TranslationMessages<
|
||||
string,
|
||||
{
|
||||
[x: string]: string;
|
||||
},
|
||||
boolean
|
||||
>,
|
||||
'core.translation.translation',
|
||||
{}
|
||||
>;
|
||||
};
|
||||
}>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type TranslationFunction<
|
||||
TMessages extends {
|
||||
|
||||
@@ -200,6 +200,7 @@ describe('ApiBlueprint', () => {
|
||||
"test": {
|
||||
"$$type": "@backstage/ExtensionInput",
|
||||
"config": {
|
||||
"internal": false,
|
||||
"optional": false,
|
||||
"singleton": false,
|
||||
},
|
||||
|
||||
@@ -20,20 +20,5 @@ export {
|
||||
} from './AnalyticsImplementationBlueprint';
|
||||
export { ApiBlueprint } from './ApiBlueprint';
|
||||
export { AppRootElementBlueprint } from './AppRootElementBlueprint';
|
||||
export { AppRootWrapperBlueprint } from './AppRootWrapperBlueprint';
|
||||
export { IconBundleBlueprint } from './IconBundleBlueprint';
|
||||
export {
|
||||
NavContentBlueprint,
|
||||
type NavContentComponent,
|
||||
type NavContentComponentProps,
|
||||
} from './NavContentBlueprint';
|
||||
export { NavItemBlueprint } from './NavItemBlueprint';
|
||||
export { PageBlueprint } from './PageBlueprint';
|
||||
export { RouterBlueprint } from './RouterBlueprint';
|
||||
export {
|
||||
type SignInPageProps,
|
||||
SignInPageBlueprint,
|
||||
} from './SignInPageBlueprint';
|
||||
export { ThemeBlueprint } from './ThemeBlueprint';
|
||||
export { TranslationBlueprint } from './TranslationBlueprint';
|
||||
export { SwappableComponentBlueprint } from './SwappableComponentBlueprint';
|
||||
|
||||
@@ -29,28 +29,28 @@ describe('createExtensionInput', () => {
|
||||
expect(input).toEqual({
|
||||
$$type: '@backstage/ExtensionInput',
|
||||
extensionData: [stringDataRef, numberDataRef],
|
||||
config: { singleton: false, optional: false },
|
||||
config: { singleton: false, optional: false, internal: false },
|
||||
withContext: expect.any(Function),
|
||||
});
|
||||
|
||||
const x1: ExtensionInput<
|
||||
typeof stringDataRef | typeof numberDataRef,
|
||||
{ singleton: false; optional: false }
|
||||
{ singleton: false; optional: false; internal: false }
|
||||
> = input;
|
||||
// @ts-expect-error
|
||||
const x2: ExtensionInput<
|
||||
typeof stringDataRef,
|
||||
{ singleton: false; optional: false }
|
||||
{ singleton: false; optional: false; internal: false }
|
||||
> = input;
|
||||
// @ts-expect-error
|
||||
const x3: ExtensionInput<
|
||||
typeof stringDataRef | typeof numberDataRef,
|
||||
{ singleton: true; optional: false }
|
||||
{ singleton: true; optional: false; internal: false }
|
||||
> = input;
|
||||
// @ts-expect-error
|
||||
const x4: ExtensionInput<
|
||||
typeof stringDataRef | typeof numberDataRef,
|
||||
{ singleton: false; optional: true }
|
||||
{ singleton: false; optional: true; internal: false }
|
||||
> = input;
|
||||
|
||||
unused(x1, x2, x3, x4);
|
||||
@@ -64,7 +64,7 @@ describe('createExtensionInput', () => {
|
||||
expect(inputWithContext).toEqual({
|
||||
$$type: '@backstage/ExtensionInput',
|
||||
extensionData: [stringDataRef, numberDataRef],
|
||||
config: { singleton: false, optional: false },
|
||||
config: { singleton: false, optional: false, internal: false },
|
||||
withContext: expect.any(Function),
|
||||
context,
|
||||
});
|
||||
@@ -77,28 +77,28 @@ describe('createExtensionInput', () => {
|
||||
expect(input).toEqual({
|
||||
$$type: '@backstage/ExtensionInput',
|
||||
extensionData: [stringDataRef, numberDataRef],
|
||||
config: { singleton: true, optional: false },
|
||||
config: { singleton: true, optional: false, internal: false },
|
||||
withContext: expect.any(Function),
|
||||
});
|
||||
|
||||
const x1: ExtensionInput<
|
||||
typeof stringDataRef | typeof numberDataRef,
|
||||
{ singleton: true; optional: false }
|
||||
{ singleton: true; optional: false; internal: false }
|
||||
> = input;
|
||||
// @ts-expect-error
|
||||
const x2: ExtensionInput<
|
||||
typeof stringDataRef,
|
||||
{ singleton: true; optional: false }
|
||||
{ singleton: true; optional: false; internal: false }
|
||||
> = input;
|
||||
// @ts-expect-error
|
||||
const x3: ExtensionInput<
|
||||
typeof stringDataRef | typeof numberDataRef,
|
||||
{ singleton: false; optional: false }
|
||||
{ singleton: false; optional: false; internal: false }
|
||||
> = input;
|
||||
// @ts-expect-error
|
||||
const x4: ExtensionInput<
|
||||
typeof stringDataRef | typeof numberDataRef,
|
||||
{ singleton: false; optional: true }
|
||||
{ singleton: false; optional: true; internal: false }
|
||||
> = input;
|
||||
|
||||
unused(x1, x2, x3, x4);
|
||||
@@ -112,28 +112,28 @@ describe('createExtensionInput', () => {
|
||||
expect(input).toEqual({
|
||||
$$type: '@backstage/ExtensionInput',
|
||||
extensionData: [stringDataRef, numberDataRef],
|
||||
config: { singleton: true, optional: true },
|
||||
config: { singleton: true, optional: true, internal: false },
|
||||
withContext: expect.any(Function),
|
||||
});
|
||||
|
||||
const x1: ExtensionInput<
|
||||
typeof stringDataRef | typeof numberDataRef,
|
||||
{ singleton: true; optional: true }
|
||||
{ singleton: true; optional: true; internal: false }
|
||||
> = input;
|
||||
// @ts-expect-error
|
||||
const x2: ExtensionInput<
|
||||
typeof stringDataRef,
|
||||
{ singleton: true; optional: true }
|
||||
{ singleton: true; optional: true; internal: false }
|
||||
> = input;
|
||||
// @ts-expect-error
|
||||
const x3: ExtensionInput<
|
||||
typeof stringDataRef | typeof numberDataRef,
|
||||
{ singleton: false; optional: false }
|
||||
{ singleton: false; optional: false; internal: false }
|
||||
> = input;
|
||||
// @ts-expect-error
|
||||
const x4: ExtensionInput<
|
||||
typeof stringDataRef | typeof numberDataRef,
|
||||
{ singleton: false; optional: true }
|
||||
{ singleton: false; optional: true; internal: false }
|
||||
> = input;
|
||||
|
||||
unused(x1, x2, x3, x4);
|
||||
@@ -144,4 +144,14 @@ describe('createExtensionInput', () => {
|
||||
createExtensionInput([stringDataRef, stringDataRef], { singleton: true }),
|
||||
).toThrow("ExtensionInput may not have duplicate data refs: 'str'");
|
||||
});
|
||||
|
||||
it('should create an internal input', () => {
|
||||
const input = createExtensionInput([stringDataRef], { internal: true });
|
||||
expect(input).toEqual({
|
||||
$$type: '@backstage/ExtensionInput',
|
||||
extensionData: [stringDataRef],
|
||||
config: { singleton: false, optional: false, internal: true },
|
||||
withContext: expect.any(Function),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -27,9 +27,14 @@ export interface ExtensionInput<
|
||||
string,
|
||||
{ optional?: true }
|
||||
> = ExtensionDataRef,
|
||||
TConfig extends { singleton: boolean; optional: boolean } = {
|
||||
TConfig extends {
|
||||
singleton: boolean;
|
||||
optional: boolean;
|
||||
internal?: boolean;
|
||||
} = {
|
||||
singleton: boolean;
|
||||
optional: boolean;
|
||||
internal?: boolean;
|
||||
},
|
||||
> {
|
||||
readonly $$type: '@backstage/ExtensionInput';
|
||||
@@ -38,10 +43,60 @@ export interface ExtensionInput<
|
||||
readonly replaces?: Array<{ id: string; input: string }>;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
/**
|
||||
* Creates a new extension input to be passed to the input map of an extension.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* Extension inputs created with this function can be passed to any `inputs` map
|
||||
* as part of creating or overriding an extension.
|
||||
*
|
||||
* The array of extension data references defines the data this input expects.
|
||||
* If the required data is not provided by the attached extension, the
|
||||
* attachment will fail.
|
||||
*
|
||||
* The `config` object can be used to restrict the behavior and shape of the
|
||||
* input. By default an input will accept zero or more extensions from any
|
||||
* plugin. The following options are available:
|
||||
*
|
||||
* - `singleton`: If set to `true`, only one extension can be attached to the
|
||||
* input at a time. Additional extensions will trigger an app error and be
|
||||
* ignored.
|
||||
* - `optional`: If set to `true`, the input is optional and can be omitted,
|
||||
* this only has an effect if the `singleton` is set to `true`.
|
||||
* - `internal`: If set to `true`, only extensions from the same plugin will be
|
||||
* allowed to attach to this input. Other extensions will trigger an app error
|
||||
* and be ignored.
|
||||
*
|
||||
* @param extensionData - The array of extension data references that this input
|
||||
* expects.
|
||||
* @param config - The configuration object for the input.
|
||||
* @returns An extension input declaration.
|
||||
* @example
|
||||
* ```ts
|
||||
* const extension = createExtension({
|
||||
* attachTo: { id: 'example-parent', input: 'example-input' },
|
||||
* inputs: {
|
||||
* content: createExtensionInput([coreExtensionData.reactElement], {
|
||||
* singleton: true,
|
||||
* }),
|
||||
* },
|
||||
* output: [coreExtensionData.reactElement],
|
||||
* *factory({ inputs }) {
|
||||
* const content = inputs.content?.get(coreExtensionData.reactElement);
|
||||
* yield coreExtensionData.reactElement(<ContentWrapper>{content}</ContentWrapper>);
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
* @public
|
||||
*/
|
||||
export function createExtensionInput<
|
||||
UExtensionData extends ExtensionDataRef<unknown, string, { optional?: true }>,
|
||||
TConfig extends { singleton?: boolean; optional?: boolean },
|
||||
TConfig extends {
|
||||
singleton?: boolean;
|
||||
optional?: boolean;
|
||||
internal?: boolean;
|
||||
},
|
||||
>(
|
||||
extensionData: Array<UExtensionData>,
|
||||
config?: TConfig & { replaces?: Array<{ id: string; input: string }> },
|
||||
@@ -50,6 +105,7 @@ export function createExtensionInput<
|
||||
{
|
||||
singleton: TConfig['singleton'] extends true ? true : false;
|
||||
optional: TConfig['optional'] extends true ? true : false;
|
||||
internal: TConfig['internal'] extends true ? true : false;
|
||||
}
|
||||
> {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
@@ -81,6 +137,9 @@ export function createExtensionInput<
|
||||
optional: Boolean(config?.optional) as TConfig['optional'] extends true
|
||||
? true
|
||||
: false,
|
||||
internal: Boolean(config?.internal) as TConfig['internal'] extends true
|
||||
? true
|
||||
: false,
|
||||
},
|
||||
replaces: config?.replaces,
|
||||
};
|
||||
@@ -90,6 +149,7 @@ export function createExtensionInput<
|
||||
{
|
||||
singleton: TConfig['singleton'] extends true ? true : false;
|
||||
optional: TConfig['optional'] extends true ? true : false;
|
||||
internal: TConfig['internal'] extends true ? true : false;
|
||||
}
|
||||
> {
|
||||
return OpaqueExtensionInput.createInstance(undefined, {
|
||||
|
||||
@@ -37,6 +37,7 @@ export type ResolvedInputValueOverrides<
|
||||
{
|
||||
optional: infer IOptional extends boolean;
|
||||
singleton: boolean;
|
||||
internal?: boolean;
|
||||
}
|
||||
>
|
||||
? IOptional extends true
|
||||
@@ -44,7 +45,11 @@ export type ResolvedInputValueOverrides<
|
||||
: KName
|
||||
: never]: TInputs[KName] extends ExtensionInput<
|
||||
infer IDataRefs,
|
||||
{ optional: boolean; singleton: infer ISingleton extends boolean }
|
||||
{
|
||||
optional: boolean;
|
||||
singleton: infer ISingleton extends boolean;
|
||||
internal?: boolean;
|
||||
}
|
||||
>
|
||||
? ISingleton extends true
|
||||
? Iterable<ExtensionDataRefToValue<IDataRefs>>
|
||||
@@ -56,6 +61,7 @@ export type ResolvedInputValueOverrides<
|
||||
{
|
||||
optional: infer IOptional extends boolean;
|
||||
singleton: boolean;
|
||||
internal?: boolean;
|
||||
}
|
||||
>
|
||||
? IOptional extends true
|
||||
@@ -63,7 +69,11 @@ export type ResolvedInputValueOverrides<
|
||||
: never
|
||||
: never]?: TInputs[KName] extends ExtensionInput<
|
||||
infer IDataRefs,
|
||||
{ optional: boolean; singleton: infer ISingleton extends boolean }
|
||||
{
|
||||
optional: boolean;
|
||||
singleton: infer ISingleton extends boolean;
|
||||
internal?: boolean;
|
||||
}
|
||||
>
|
||||
? ISingleton extends true
|
||||
? Iterable<ExtensionDataRefToValue<IDataRefs>>
|
||||
|
||||
@@ -10,10 +10,9 @@ 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 { IdentityApi } from '@backstage/frontend-plugin-api';
|
||||
import { ReactNode } from 'react';
|
||||
import { SignInPageProps } from '@backstage/frontend-plugin-api';
|
||||
import { RouteRef } 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';
|
||||
@@ -92,9 +91,21 @@ export const NavContentBlueprint: ExtensionBlueprint<{
|
||||
};
|
||||
}>;
|
||||
|
||||
export { NavContentComponent };
|
||||
// @public
|
||||
export type NavContentComponent = (
|
||||
props: NavContentComponentProps,
|
||||
) => JSX.Element | null;
|
||||
|
||||
export { NavContentComponentProps };
|
||||
// @public
|
||||
export interface NavContentComponentProps {
|
||||
items: Array<{
|
||||
icon: IconComponent;
|
||||
title: string;
|
||||
routeRef: RouteRef<undefined>;
|
||||
to: string;
|
||||
text: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export const RouterBlueprint: ExtensionBlueprint<{
|
||||
@@ -143,7 +154,11 @@ export const SignInPageBlueprint: ExtensionBlueprint<{
|
||||
};
|
||||
}>;
|
||||
|
||||
export { SignInPageProps };
|
||||
// @public
|
||||
export type SignInPageProps = {
|
||||
onSignInSuccess(identityApi: IdentityApi): void;
|
||||
children?: ReactNode;
|
||||
};
|
||||
|
||||
// @public
|
||||
export const SwappableComponentBlueprint: ExtensionBlueprint<{
|
||||
@@ -160,14 +175,7 @@ export const SwappableComponentBlueprint: ExtensionBlueprint<{
|
||||
loader: Ref extends SwappableComponentRef<infer IInnerComponentProps, any>
|
||||
?
|
||||
| (() => (props: IInnerComponentProps) => JSX.Element | null)
|
||||
| (() => Promise<
|
||||
(props: IInnerComponentProps) => JSX.Element
|
||||
/**
|
||||
* Creates an extension that replaces the router component. This blueprint is limited to use by the app plugin.
|
||||
*
|
||||
* @public
|
||||
*/ | null
|
||||
>)
|
||||
| (() => Promise<(props: IInnerComponentProps) => JSX.Element | null>)
|
||||
: never;
|
||||
}) => ExtensionBlueprintParams<{
|
||||
component: Ref extends SwappableComponentRef<
|
||||
@@ -181,14 +189,7 @@ export const SwappableComponentBlueprint: ExtensionBlueprint<{
|
||||
loader: Ref extends SwappableComponentRef<infer IInnerComponentProps, any>
|
||||
?
|
||||
| (() => (props: IInnerComponentProps) => JSX.Element | null)
|
||||
| (() => Promise<
|
||||
(props: IInnerComponentProps) => JSX.Element
|
||||
/**
|
||||
* Creates an extension that replaces the router component. This blueprint is limited to use by the app plugin.
|
||||
*
|
||||
* @public
|
||||
*/ | null
|
||||
>)
|
||||
| (() => Promise<(props: IInnerComponentProps) => JSX.Element | null>)
|
||||
: never;
|
||||
}>;
|
||||
output: ExtensionDataRef<
|
||||
|
||||
+24
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
* 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.
|
||||
@@ -20,7 +20,8 @@ import {
|
||||
coreExtensionData,
|
||||
createExtension,
|
||||
createExtensionInput,
|
||||
} from '../wiring';
|
||||
createFrontendModule,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { renderTestApp } from '@backstage/frontend-test-utils';
|
||||
|
||||
describe('AppRootWrapperBlueprint', () => {
|
||||
@@ -63,7 +64,11 @@ describe('AppRootWrapperBlueprint', () => {
|
||||
},
|
||||
});
|
||||
|
||||
renderTestApp({ extensions: [extension] });
|
||||
renderTestApp({
|
||||
features: [
|
||||
createFrontendModule({ pluginId: 'app', extensions: [extension] }),
|
||||
],
|
||||
});
|
||||
|
||||
await waitFor(() => expect(screen.getByText('Hello')).toBeInTheDocument());
|
||||
});
|
||||
@@ -95,20 +100,28 @@ describe('AppRootWrapperBlueprint', () => {
|
||||
});
|
||||
|
||||
renderTestApp({
|
||||
extensions: [
|
||||
extension,
|
||||
createExtension({
|
||||
name: 'test-child',
|
||||
attachTo: { id: 'app-root-wrapper:test', input: 'children' },
|
||||
output: [coreExtensionData.reactElement],
|
||||
factory: () => [coreExtensionData.reactElement(<div>Its Me</div>)],
|
||||
extensions: [],
|
||||
features: [
|
||||
createFrontendModule({
|
||||
pluginId: 'app',
|
||||
extensions: [
|
||||
extension,
|
||||
createExtension({
|
||||
name: 'test-child',
|
||||
attachTo: extension.inputs.children,
|
||||
output: [coreExtensionData.reactElement],
|
||||
factory: () => [
|
||||
coreExtensionData.reactElement(<div>Its Me</div>),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
config: {
|
||||
app: {
|
||||
extensions: [
|
||||
{
|
||||
'app-root-wrapper:test': { config: { name: 'Robin' } },
|
||||
'app-root-wrapper:app': { config: { name: 'Robin' } },
|
||||
},
|
||||
],
|
||||
},
|
||||
+6
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
* 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.
|
||||
@@ -15,7 +15,10 @@
|
||||
*/
|
||||
|
||||
import { ReactNode } from 'react';
|
||||
import { createExtensionBlueprint, createExtensionDataRef } from '../wiring';
|
||||
import {
|
||||
createExtensionBlueprint,
|
||||
createExtensionDataRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
const componentDataRef = createExtensionDataRef<
|
||||
(props: { children: ReactNode }) => JSX.Element | null
|
||||
@@ -24,12 +27,9 @@ const componentDataRef = createExtensionDataRef<
|
||||
/**
|
||||
* Creates a extensions that render a React wrapper at the app root, enclosing
|
||||
* the app layout. This is useful for example for adding global React contexts
|
||||
* and similar.
|
||||
* and similar. This blueprint is limited to use by the app plugin.
|
||||
*
|
||||
* @public
|
||||
* @deprecated Use {@link @backstage/plugin-app-react#AppRootWrapperBlueprint} instead.
|
||||
* If you were using this blueprint to provide a context for your plugin,
|
||||
* use `PluginWrapperBlueprint` from `@backstage/frontend-plugin-api/alpha` instead.
|
||||
*/
|
||||
export const AppRootWrapperBlueprint = createExtensionBlueprint({
|
||||
kind: 'app-root-wrapper',
|
||||
+8
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
* 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.
|
||||
@@ -14,16 +14,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { IconComponent } from '../icons';
|
||||
import { createExtensionBlueprint, createExtensionDataRef } from '../wiring';
|
||||
import { IconComponent } from '@backstage/frontend-plugin-api';
|
||||
import {
|
||||
createExtensionBlueprint,
|
||||
createExtensionDataRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
const iconsDataRef = createExtensionDataRef<{
|
||||
[key in string]: IconComponent;
|
||||
}>().with({ id: 'core.icons' });
|
||||
|
||||
/**
|
||||
* Creates an extension that adds icon bundles to your app. This blueprint is limited to use by the app plugin.
|
||||
*
|
||||
* @public
|
||||
* @deprecated Use {@link @backstage/plugin-app-react#IconBundleBlueprint} instead.
|
||||
*/
|
||||
export const IconBundleBlueprint = createExtensionBlueprint({
|
||||
kind: 'icon-bundle',
|
||||
+7
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
* 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.
|
||||
@@ -15,7 +15,10 @@
|
||||
*/
|
||||
|
||||
import { IconComponent, RouteRef } from '@backstage/frontend-plugin-api';
|
||||
import { createExtensionBlueprint, createExtensionDataRef } from '../wiring';
|
||||
import {
|
||||
createExtensionBlueprint,
|
||||
createExtensionDataRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
/**
|
||||
* The props for the {@link NavContentComponent}.
|
||||
@@ -25,7 +28,7 @@ import { createExtensionBlueprint, createExtensionDataRef } from '../wiring';
|
||||
export interface NavContentComponentProps {
|
||||
/**
|
||||
* The nav items available to the component. These are all the items created
|
||||
* with the {@link NavItemBlueprint} in the app.
|
||||
* with the {@link @backstage/frontend-plugin-api#NavItemBlueprint} in the app.
|
||||
*
|
||||
* In addition to the original properties from the nav items, these also
|
||||
* include a resolved route path as `to`, and duplicated `title` as `text` to
|
||||
@@ -57,10 +60,9 @@ const componentDataRef = createExtensionDataRef<NavContentComponent>().with({
|
||||
});
|
||||
|
||||
/**
|
||||
* Creates an extension that replaces the entire nav bar with your own component.
|
||||
* Creates an extension that replaces the entire nav bar with your own component. This blueprint is limited to use by the app plugin.
|
||||
*
|
||||
* @public
|
||||
* @deprecated Use {@link @backstage/plugin-app-react#NavContentBlueprint} instead.
|
||||
*/
|
||||
export const NavContentBlueprint = createExtensionBlueprint({
|
||||
kind: 'nav-content',
|
||||
+1
-1
@@ -20,7 +20,7 @@ import {
|
||||
coreExtensionData,
|
||||
createExtension,
|
||||
createExtensionInput,
|
||||
} from '../wiring';
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { createExtensionTester } from '@backstage/frontend-test-utils';
|
||||
|
||||
describe('RouterBlueprint', () => {
|
||||
+7
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
* 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.
|
||||
@@ -15,15 +15,19 @@
|
||||
*/
|
||||
|
||||
import { ReactNode } from 'react';
|
||||
import { createExtensionBlueprint, createExtensionDataRef } from '../wiring';
|
||||
import {
|
||||
createExtensionBlueprint,
|
||||
createExtensionDataRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
const componentDataRef = createExtensionDataRef<
|
||||
(props: { children: ReactNode }) => JSX.Element | null
|
||||
>().with({ id: 'app.router.wrapper' });
|
||||
|
||||
/**
|
||||
* Creates an extension that replaces the router component. This blueprint is limited to use by the app plugin.
|
||||
*
|
||||
* @public
|
||||
* @deprecated Use {@link @backstage/plugin-app-react#RouterBlueprint} instead.
|
||||
*/
|
||||
export const RouterBlueprint = createExtensionBlueprint({
|
||||
kind: 'app-router-component',
|
||||
+8
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
* 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.
|
||||
@@ -15,9 +15,12 @@
|
||||
*/
|
||||
|
||||
import { ComponentType, lazy, ReactNode } from 'react';
|
||||
import { createExtensionBlueprint, createExtensionDataRef } from '../wiring';
|
||||
import { ExtensionBoundary } from '../components';
|
||||
import { IdentityApi } from '../apis';
|
||||
import {
|
||||
createExtensionBlueprint,
|
||||
createExtensionDataRef,
|
||||
ExtensionBoundary,
|
||||
IdentityApi,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
/**
|
||||
* Props for the `SignInPage` component.
|
||||
@@ -41,10 +44,9 @@ const componentDataRef = createExtensionDataRef<
|
||||
>().with({ id: 'core.sign-in-page.component' });
|
||||
|
||||
/**
|
||||
* Creates an extension that replaces the sign in page.
|
||||
* Creates an extension that replaces the sign in page. This blueprint is limited to use by the app plugin.
|
||||
*
|
||||
* @public
|
||||
* @deprecated Use {@link @backstage/plugin-app-react#SignInPageBlueprint} instead.
|
||||
*/
|
||||
export const SignInPageBlueprint = createExtensionBlueprint({
|
||||
kind: 'sign-in-page',
|
||||
+4
-2
@@ -14,9 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { renderTestApp } from '@backstage/frontend-test-utils';
|
||||
import { createSwappableComponent } from '../components';
|
||||
import {
|
||||
createSwappableComponent,
|
||||
PageBlueprint,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { SwappableComponentBlueprint } from './SwappableComponentBlueprint';
|
||||
import { PageBlueprint } from './PageBlueprint';
|
||||
import { screen } from '@testing-library/react';
|
||||
|
||||
describe('SwappableComponentBlueprint', () => {
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
* 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.
|
||||
@@ -13,12 +13,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { SwappableComponentRef } from '../components';
|
||||
|
||||
import { SwappableComponentRef } from '@backstage/frontend-plugin-api';
|
||||
import {
|
||||
createExtensionBlueprint,
|
||||
createExtensionBlueprintParams,
|
||||
createExtensionDataRef,
|
||||
} from '../wiring';
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
export const componentDataRef = createExtensionDataRef<{
|
||||
ref: SwappableComponentRef;
|
||||
@@ -28,10 +29,9 @@ export const componentDataRef = createExtensionDataRef<{
|
||||
}>().with({ id: 'core.swappableComponent' });
|
||||
|
||||
/**
|
||||
* Blueprint for creating swappable components from a SwappableComponentRef and a loader
|
||||
* Blueprint for creating swappable components from a SwappableComponentRef and a loader. This blueprint is limited to use by the app plugin.
|
||||
*
|
||||
* @public
|
||||
* @deprecated Use {@link @backstage/plugin-app-react#SwappableComponentBlueprint} instead.
|
||||
*/
|
||||
export const SwappableComponentBlueprint = createExtensionBlueprint({
|
||||
kind: 'component',
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { AppTheme } from '../apis/definitions/AppThemeApi';
|
||||
import { AppTheme } from '@backstage/frontend-plugin-api';
|
||||
import { ThemeBlueprint } from './ThemeBlueprint';
|
||||
import { createExtensionTester } from '@backstage/frontend-test-utils';
|
||||
|
||||
+7
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
* 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.
|
||||
@@ -14,18 +14,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { AppTheme } from '../apis/definitions/AppThemeApi';
|
||||
import { createExtensionBlueprint, createExtensionDataRef } from '../wiring';
|
||||
import { AppTheme } from '@backstage/frontend-plugin-api';
|
||||
import {
|
||||
createExtensionBlueprint,
|
||||
createExtensionDataRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
const themeDataRef = createExtensionDataRef<AppTheme>().with({
|
||||
id: 'core.theme.theme',
|
||||
});
|
||||
|
||||
/**
|
||||
* Creates an extension that adds/replaces an app theme.
|
||||
* Creates an extension that adds/replaces an app theme. This blueprint is limited to use by the app plugin.
|
||||
*
|
||||
* @public
|
||||
* @deprecated Use {@link @backstage/plugin-app-react#ThemeBlueprint} instead.
|
||||
*/
|
||||
export const ThemeBlueprint = createExtensionBlueprint({
|
||||
kind: 'theme',
|
||||
+1
-1
@@ -17,7 +17,7 @@ import { createExtensionTester } from '@backstage/frontend-test-utils';
|
||||
import {
|
||||
createTranslationMessages,
|
||||
createTranslationRef,
|
||||
} from '../translation';
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { TranslationBlueprint } from './TranslationBlueprint';
|
||||
|
||||
describe('TranslationBlueprint', () => {
|
||||
+8
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
* 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.
|
||||
@@ -14,18 +14,21 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createExtensionBlueprint, createExtensionDataRef } from '../wiring';
|
||||
import { TranslationMessages, TranslationResource } from '../translation';
|
||||
import {
|
||||
createExtensionBlueprint,
|
||||
createExtensionDataRef,
|
||||
TranslationMessages,
|
||||
TranslationResource,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
const translationDataRef = createExtensionDataRef<
|
||||
TranslationResource | TranslationMessages
|
||||
>().with({ id: 'core.translation.translation' });
|
||||
|
||||
/**
|
||||
* Creates an extension that adds translations to your app.
|
||||
* Creates an extension that adds translations to your app. This blueprint is limited to use by the app plugin.
|
||||
*
|
||||
* @public
|
||||
* @deprecated Use {@link @backstage/plugin-app-react#TranslationBlueprint} instead.
|
||||
*/
|
||||
export const TranslationBlueprint = createExtensionBlueprint({
|
||||
kind: 'translation',
|
||||
@@ -14,94 +14,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
AppRootWrapperBlueprint as _AppRootWrapperBlueprint,
|
||||
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 renders a React wrapper at the app root, enclosing
|
||||
* the app layout. This blueprint is limited to use by the app plugin.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const AppRootWrapperBlueprint = _AppRootWrapperBlueprint;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
export { AppRootWrapperBlueprint } from './AppRootWrapperBlueprint';
|
||||
export { IconBundleBlueprint } from './IconBundleBlueprint';
|
||||
export { NavContentBlueprint } from './NavContentBlueprint';
|
||||
export type {
|
||||
NavContentComponent,
|
||||
NavContentComponentProps,
|
||||
} from './NavContentBlueprint';
|
||||
export { RouterBlueprint } from './RouterBlueprint';
|
||||
export { SignInPageBlueprint } from './SignInPageBlueprint';
|
||||
export type { SignInPageProps } from './SignInPageBlueprint';
|
||||
export { SwappableComponentBlueprint } from './SwappableComponentBlueprint';
|
||||
export { ThemeBlueprint } from './ThemeBlueprint';
|
||||
export { TranslationBlueprint } from './TranslationBlueprint';
|
||||
|
||||
@@ -21,7 +21,6 @@ import {
|
||||
coreExtensionData,
|
||||
ApiBlueprint,
|
||||
NavItemBlueprint,
|
||||
ThemeBlueprint,
|
||||
useApi,
|
||||
routeResolutionApiRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
@@ -79,7 +78,6 @@ const getOutputColor = createOutputColorGenerator(
|
||||
[coreExtensionData.routePath.id]: '#ffeb3b',
|
||||
[coreExtensionData.routeRef.id]: '#9c27b0',
|
||||
[ApiBlueprint.dataRefs.factory.id]: '#2196f3',
|
||||
[ThemeBlueprint.dataRefs.theme.id]: '#cddc39',
|
||||
[NavItemBlueprint.dataRefs.target.id]: '#ff9800',
|
||||
},
|
||||
|
||||
@@ -330,7 +328,6 @@ const legendMap = {
|
||||
'Route Path': coreExtensionData.routePath,
|
||||
'Route Ref': coreExtensionData.routeRef,
|
||||
'Nav Target': NavItemBlueprint.dataRefs.target,
|
||||
Theme: ThemeBlueprint.dataRefs.theme,
|
||||
};
|
||||
|
||||
function Legend() {
|
||||
|
||||
@@ -15,12 +15,12 @@ import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionInput } from '@backstage/frontend-plugin-api';
|
||||
import { IconComponent } from '@backstage/frontend-plugin-api';
|
||||
import { JSX as JSX_2 } from 'react';
|
||||
import { NavContentComponent } from '@backstage/frontend-plugin-api';
|
||||
import { NavContentComponent } from '@backstage/plugin-app-react';
|
||||
import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api';
|
||||
import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api';
|
||||
import { ReactNode } from 'react';
|
||||
import { RouteRef } from '@backstage/frontend-plugin-api';
|
||||
import { SignInPageProps } from '@backstage/frontend-plugin-api';
|
||||
import { SignInPageProps } from '@backstage/plugin-app-react';
|
||||
import { SwappableComponentRef } from '@backstage/frontend-plugin-api';
|
||||
import { TranslationMessages } from '@backstage/frontend-plugin-api';
|
||||
import { TranslationResource } from '@backstage/frontend-plugin-api';
|
||||
@@ -40,6 +40,7 @@ const appPlugin: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: true;
|
||||
optional: false;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
};
|
||||
@@ -57,6 +58,7 @@ const appPlugin: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: true;
|
||||
optional: false;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
content: ExtensionInput<
|
||||
@@ -64,6 +66,7 @@ const appPlugin: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: true;
|
||||
optional: false;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
};
|
||||
@@ -89,6 +92,7 @@ const appPlugin: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: false;
|
||||
optional: false;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
content: ExtensionInput<
|
||||
@@ -100,6 +104,7 @@ const appPlugin: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: true;
|
||||
optional: true;
|
||||
internal: true;
|
||||
}
|
||||
>;
|
||||
};
|
||||
@@ -121,6 +126,7 @@ const appPlugin: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: true;
|
||||
optional: true;
|
||||
internal: true;
|
||||
}
|
||||
>;
|
||||
signInPage: ExtensionInput<
|
||||
@@ -132,6 +138,7 @@ const appPlugin: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: true;
|
||||
optional: true;
|
||||
internal: true;
|
||||
}
|
||||
>;
|
||||
children: ExtensionInput<
|
||||
@@ -139,6 +146,7 @@ const appPlugin: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: true;
|
||||
optional: false;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
elements: ExtensionInput<
|
||||
@@ -146,6 +154,7 @@ const appPlugin: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: false;
|
||||
optional: false;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
wrappers: ExtensionInput<
|
||||
@@ -157,6 +166,7 @@ const appPlugin: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: false;
|
||||
optional: false;
|
||||
internal: true;
|
||||
}
|
||||
>;
|
||||
};
|
||||
@@ -182,6 +192,7 @@ const appPlugin: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: false;
|
||||
optional: false;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
};
|
||||
@@ -218,6 +229,7 @@ const appPlugin: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: false;
|
||||
optional: false;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
};
|
||||
@@ -262,6 +274,7 @@ const appPlugin: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: false;
|
||||
optional: false;
|
||||
internal: true;
|
||||
}
|
||||
>;
|
||||
};
|
||||
@@ -471,6 +484,7 @@ const appPlugin: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: false;
|
||||
optional: false;
|
||||
internal: true;
|
||||
}
|
||||
>;
|
||||
};
|
||||
@@ -592,6 +606,7 @@ const appPlugin: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: false;
|
||||
optional: false;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
};
|
||||
@@ -669,6 +684,7 @@ const appPlugin: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: false;
|
||||
optional: false;
|
||||
internal: true;
|
||||
}
|
||||
>;
|
||||
};
|
||||
@@ -703,6 +719,7 @@ const appPlugin: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: false;
|
||||
optional: false;
|
||||
internal: true;
|
||||
}
|
||||
>;
|
||||
};
|
||||
|
||||
@@ -14,10 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
SignInPageBlueprint,
|
||||
createFrontendModule,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { createFrontendModule } from '@backstage/frontend-plugin-api';
|
||||
import { SignInPageBlueprint } from '@backstage/plugin-app-react';
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import { useEffect } from 'react';
|
||||
import { appModulePublicSignIn } from './appModulePublicSignIn';
|
||||
|
||||
@@ -20,9 +20,9 @@ import {
|
||||
createExtensionInput,
|
||||
createFrontendModule,
|
||||
createSwappableComponent,
|
||||
SwappableComponentBlueprint,
|
||||
swappableComponentsApiRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { SwappableComponentBlueprint } from '@backstage/plugin-app-react';
|
||||
import { DefaultSwappableComponentsApi } from './DefaultSwappableComponentsApi';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { renderInTestApp, renderTestApp } from '@backstage/frontend-test-utils';
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
import {
|
||||
SwappableComponentRef,
|
||||
SwappableComponentsApi,
|
||||
SwappableComponentBlueprint,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { SwappableComponentBlueprint } from '@backstage/plugin-app-react';
|
||||
import { OpaqueSwappableComponentRef } from '@internal/frontend';
|
||||
|
||||
import { lazy } from 'react';
|
||||
|
||||
@@ -19,14 +19,16 @@ import {
|
||||
coreExtensionData,
|
||||
createExtensionInput,
|
||||
NavItemBlueprint,
|
||||
NavContentBlueprint,
|
||||
NavContentComponentProps,
|
||||
routeResolutionApiRef,
|
||||
IconComponent,
|
||||
RouteRef,
|
||||
useApi,
|
||||
NavContentComponent,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import {
|
||||
NavContentBlueprint,
|
||||
NavContentComponent,
|
||||
NavContentComponentProps,
|
||||
} from '@backstage/plugin-app-react';
|
||||
import { Sidebar, SidebarItem } from '@backstage/core-components';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
@@ -90,18 +92,11 @@ export const AppNav = createExtension({
|
||||
content: createExtensionInput([NavContentBlueprint.dataRefs.component], {
|
||||
singleton: true,
|
||||
optional: true,
|
||||
internal: true,
|
||||
}),
|
||||
},
|
||||
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;
|
||||
|
||||
@@ -22,9 +22,6 @@ import {
|
||||
JSX,
|
||||
} from 'react';
|
||||
import {
|
||||
AppRootWrapperBlueprint,
|
||||
RouterBlueprint,
|
||||
SignInPageBlueprint,
|
||||
coreExtensionData,
|
||||
discoveryApiRef,
|
||||
fetchApiRef,
|
||||
@@ -33,6 +30,11 @@ import {
|
||||
createExtensionInput,
|
||||
routeResolutionApiRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import {
|
||||
AppRootWrapperBlueprint,
|
||||
RouterBlueprint,
|
||||
SignInPageBlueprint,
|
||||
} from '@backstage/plugin-app-react';
|
||||
import {
|
||||
DiscoveryApi,
|
||||
ErrorApi,
|
||||
@@ -59,37 +61,26 @@ export const AppRoot = createExtension({
|
||||
router: createExtensionInput([RouterBlueprint.dataRefs.component], {
|
||||
singleton: true,
|
||||
optional: true,
|
||||
internal: true,
|
||||
}),
|
||||
signInPage: createExtensionInput([SignInPageBlueprint.dataRefs.component], {
|
||||
singleton: true,
|
||||
optional: true,
|
||||
internal: true,
|
||||
}),
|
||||
children: createExtensionInput([coreExtensionData.reactElement], {
|
||||
singleton: true,
|
||||
}),
|
||||
elements: createExtensionInput([coreExtensionData.reactElement]),
|
||||
wrappers: createExtensionInput([
|
||||
AppRootWrapperBlueprint.dataRefs.component,
|
||||
]),
|
||||
wrappers: createExtensionInput(
|
||||
[AppRootWrapperBlueprint.dataRefs.component],
|
||||
{
|
||||
internal: true,
|
||||
},
|
||||
),
|
||||
},
|
||||
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) {
|
||||
@@ -117,16 +108,8 @@ export const AppRoot = createExtension({
|
||||
|
||||
for (const wrapper of inputs.wrappers) {
|
||||
const Component = wrapper.get(AppRootWrapperBlueprint.dataRefs.component);
|
||||
const pluginId = wrapper.node.spec.plugin.id;
|
||||
if (Component) {
|
||||
content = <Component>{content}</Component>;
|
||||
if (pluginId !== 'app') {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
`DEPRECATION WARNING: AppRootWrappers 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: ${wrapper.node.spec.id}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,10 +22,10 @@ import DarkIcon from '@material-ui/icons/Brightness2';
|
||||
import LightIcon from '@material-ui/icons/WbSunny';
|
||||
import {
|
||||
createExtensionInput,
|
||||
ThemeBlueprint,
|
||||
ApiBlueprint,
|
||||
appThemeApiRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { ThemeBlueprint } from '@backstage/plugin-app-react';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { AppThemeSelector } from '../../../../packages/core-app-api/src/apis/implementations';
|
||||
|
||||
@@ -37,6 +37,7 @@ export const AppThemeApi = ApiBlueprint.makeWithOverrides({
|
||||
inputs: {
|
||||
themes: createExtensionInput([ThemeBlueprint.dataRefs.theme], {
|
||||
replaces: [{ id: 'app', input: 'themes' }],
|
||||
internal: true,
|
||||
}),
|
||||
},
|
||||
factory: (originalFactory, { inputs }) => {
|
||||
@@ -45,19 +46,6 @@ export const AppThemeApi = ApiBlueprint.makeWithOverrides({
|
||||
api: appThemeApiRef,
|
||||
deps: {},
|
||||
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)),
|
||||
);
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { SignInPageBlueprint } from '@backstage/frontend-plugin-api';
|
||||
import { SignInPageBlueprint } from '@backstage/plugin-app-react';
|
||||
import { SignInPage } from '@backstage/core-components';
|
||||
|
||||
export const DefaultSignInPage = SignInPageBlueprint.make({
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
|
||||
import {
|
||||
createExtensionInput,
|
||||
IconBundleBlueprint,
|
||||
ApiBlueprint,
|
||||
iconsApiRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { IconBundleBlueprint } from '@backstage/plugin-app-react';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { DefaultIconsApi } from '../../../../packages/frontend-app-api/src/apis/implementations/IconsApi';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
@@ -33,6 +33,7 @@ export const IconsApi = ApiBlueprint.makeWithOverrides({
|
||||
inputs: {
|
||||
icons: createExtensionInput([IconBundleBlueprint.dataRefs.icons], {
|
||||
replaces: [{ id: 'app', input: 'icons' }],
|
||||
internal: true,
|
||||
}),
|
||||
},
|
||||
factory: (originalFactory, { inputs }) => {
|
||||
@@ -41,19 +42,6 @@ export const IconsApi = ApiBlueprint.makeWithOverrides({
|
||||
api: iconsApiRef,
|
||||
deps: {},
|
||||
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))
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
SwappableComponentBlueprint,
|
||||
createExtensionInput,
|
||||
ApiBlueprint,
|
||||
swappableComponentsApiRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { SwappableComponentBlueprint } from '@backstage/plugin-app-react';
|
||||
import { DefaultSwappableComponentsApi } from '../apis/SwappableComponentsApi';
|
||||
|
||||
/**
|
||||
@@ -28,9 +28,12 @@ import { DefaultSwappableComponentsApi } from '../apis/SwappableComponentsApi';
|
||||
export const SwappableComponentsApi = ApiBlueprint.makeWithOverrides({
|
||||
name: 'swappable-components',
|
||||
inputs: {
|
||||
components: createExtensionInput([
|
||||
SwappableComponentBlueprint.dataRefs.component,
|
||||
]),
|
||||
components: createExtensionInput(
|
||||
[SwappableComponentBlueprint.dataRefs.component],
|
||||
{
|
||||
internal: true,
|
||||
},
|
||||
),
|
||||
},
|
||||
factory: (originalFactory, { inputs }) => {
|
||||
return originalFactory(defineParams =>
|
||||
@@ -38,25 +41,8 @@ export const SwappableComponentsApi = ApiBlueprint.makeWithOverrides({
|
||||
api: swappableComponentsApiRef,
|
||||
deps: {},
|
||||
factory: () => {
|
||||
const nonAppExtensions = inputs.components.filter(
|
||||
i => i.node.spec.plugin?.id !== 'app',
|
||||
);
|
||||
|
||||
if (nonAppExtensions.length > 0) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
`SwappableComponents should only be installed as an extension in the app plugin. You can either use appPlugin.override(), or provide a module for the app-plugin with the extension there instead. Invalid extensions: ${nonAppExtensions
|
||||
.map(i => i.node.spec.id)
|
||||
.join(', ')}`,
|
||||
);
|
||||
}
|
||||
|
||||
const appExtensions = inputs.components.filter(
|
||||
i => i.node.spec.plugin?.id === 'app',
|
||||
);
|
||||
|
||||
return DefaultSwappableComponentsApi.fromComponents(
|
||||
appExtensions.map(i =>
|
||||
inputs.components.map(i =>
|
||||
i.get(SwappableComponentBlueprint.dataRefs.component),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
*/
|
||||
import {
|
||||
ApiBlueprint,
|
||||
TranslationBlueprint,
|
||||
createExtensionInput,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { TranslationBlueprint } from '@backstage/plugin-app-react';
|
||||
import {
|
||||
appLanguageApiRef,
|
||||
translationApiRef,
|
||||
@@ -34,7 +34,7 @@ export const TranslationsApi = ApiBlueprint.makeWithOverrides({
|
||||
inputs: {
|
||||
translations: createExtensionInput(
|
||||
[TranslationBlueprint.dataRefs.translation],
|
||||
{ replaces: [{ id: 'app', input: 'translations' }] },
|
||||
{ replaces: [{ id: 'app', input: 'translations' }], internal: true },
|
||||
),
|
||||
},
|
||||
factory: (originalFactory, { inputs }) => {
|
||||
@@ -43,19 +43,6 @@ export const TranslationsApi = ApiBlueprint.makeWithOverrides({
|
||||
api: translationApiRef,
|
||||
deps: { languageApi: appLanguageApiRef },
|
||||
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 =>
|
||||
|
||||
@@ -17,9 +17,8 @@ import {
|
||||
NotFoundErrorPage as SwappableNotFoundErrorPage,
|
||||
Progress as SwappableProgress,
|
||||
ErrorDisplay as SwappableErrorDisplay,
|
||||
SwappableComponentBlueprint,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
import { SwappableComponentBlueprint } from '@backstage/plugin-app-react';
|
||||
import {
|
||||
ErrorPage,
|
||||
ErrorPanel,
|
||||
|
||||
@@ -345,6 +345,7 @@ const _default: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: false;
|
||||
optional: false;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
};
|
||||
@@ -794,6 +795,7 @@ const _default: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: false;
|
||||
optional: false;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
cards: ExtensionInput<
|
||||
@@ -822,6 +824,7 @@ const _default: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: false;
|
||||
optional: false;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
};
|
||||
@@ -1000,6 +1003,7 @@ const _default: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: false;
|
||||
optional: false;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
};
|
||||
@@ -1064,6 +1068,7 @@ const _default: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: false;
|
||||
optional: false;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
contents: ExtensionInput<
|
||||
@@ -1105,6 +1110,7 @@ const _default: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: false;
|
||||
optional: false;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
contextMenuItems: ExtensionInput<
|
||||
@@ -1119,6 +1125,7 @@ const _default: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: false;
|
||||
optional: false;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
};
|
||||
|
||||
@@ -92,6 +92,7 @@ const _default: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: false;
|
||||
optional: true;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
};
|
||||
|
||||
@@ -85,6 +85,7 @@ const _default: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: true;
|
||||
optional: true;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
};
|
||||
|
||||
@@ -219,6 +219,7 @@ export const formFieldsApi: OverridableExtensionDefinition<{
|
||||
{
|
||||
singleton: false;
|
||||
optional: false;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
};
|
||||
|
||||
@@ -91,6 +91,7 @@ const _default: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: false;
|
||||
optional: false;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
};
|
||||
@@ -118,6 +119,7 @@ const _default: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: false;
|
||||
optional: false;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
};
|
||||
@@ -218,6 +220,7 @@ const _default: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: false;
|
||||
optional: false;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
};
|
||||
@@ -411,6 +414,7 @@ export const formDecoratorsApi: OverridableExtensionDefinition<{
|
||||
{
|
||||
singleton: false;
|
||||
optional: false;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
};
|
||||
|
||||
@@ -97,6 +97,7 @@ const _default: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: false;
|
||||
optional: false;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
resultTypes: ExtensionInput<
|
||||
@@ -112,6 +113,7 @@ const _default: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: false;
|
||||
optional: false;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
searchFilters: ExtensionInput<
|
||||
@@ -125,6 +127,7 @@ const _default: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: false;
|
||||
optional: false;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
};
|
||||
@@ -215,6 +218,7 @@ export const searchPage: OverridableExtensionDefinition<{
|
||||
{
|
||||
singleton: false;
|
||||
optional: false;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
resultTypes: ExtensionInput<
|
||||
@@ -230,6 +234,7 @@ export const searchPage: OverridableExtensionDefinition<{
|
||||
{
|
||||
singleton: false;
|
||||
optional: false;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
searchFilters: ExtensionInput<
|
||||
@@ -243,6 +248,7 @@ export const searchPage: OverridableExtensionDefinition<{
|
||||
{
|
||||
singleton: false;
|
||||
optional: false;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
};
|
||||
|
||||
@@ -84,6 +84,7 @@ const _default: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: boolean;
|
||||
optional: boolean;
|
||||
internal?: boolean;
|
||||
}
|
||||
>;
|
||||
};
|
||||
@@ -146,6 +147,7 @@ const _default: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: false;
|
||||
optional: false;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
emptyState: ExtensionInput<
|
||||
@@ -159,6 +161,7 @@ const _default: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: true;
|
||||
optional: true;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
};
|
||||
@@ -290,6 +293,7 @@ const _default: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: false;
|
||||
optional: false;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
};
|
||||
|
||||
@@ -66,6 +66,7 @@ const _default: OverridableFrontendPlugin<
|
||||
{
|
||||
singleton: true;
|
||||
optional: true;
|
||||
internal: false;
|
||||
}
|
||||
>;
|
||||
};
|
||||
|
||||
@@ -3571,6 +3571,7 @@ __metadata:
|
||||
"@backstage/frontend-app-api": "workspace:^"
|
||||
"@backstage/frontend-plugin-api": "workspace:^"
|
||||
"@backstage/frontend-test-utils": "workspace:^"
|
||||
"@backstage/plugin-app-react": "workspace:^"
|
||||
"@backstage/plugin-catalog": "workspace:^"
|
||||
"@backstage/plugin-catalog-react": "workspace:^"
|
||||
"@backstage/test-utils": "workspace:^"
|
||||
@@ -3861,6 +3862,7 @@ __metadata:
|
||||
"@backstage/frontend-app-api": "workspace:^"
|
||||
"@backstage/frontend-plugin-api": "workspace:^"
|
||||
"@backstage/plugin-app": "workspace:^"
|
||||
"@backstage/plugin-app-react": "workspace:^"
|
||||
"@backstage/test-utils": "workspace:^"
|
||||
"@react-hookz/web": "npm:^24.0.0"
|
||||
"@testing-library/jest-dom": "npm:^6.0.0"
|
||||
@@ -30697,6 +30699,7 @@ __metadata:
|
||||
"@backstage/integration-react": "workspace:^"
|
||||
"@backstage/plugin-api-docs": "workspace:^"
|
||||
"@backstage/plugin-app": "workspace:^"
|
||||
"@backstage/plugin-app-react": "workspace:^"
|
||||
"@backstage/plugin-app-visualizer": "workspace:^"
|
||||
"@backstage/plugin-auth": "workspace:^"
|
||||
"@backstage/plugin-auth-react": "workspace:^"
|
||||
|
||||
Reference in New Issue
Block a user