Merge pull request #11404 from acierto/reconfigure

Make a possibility to configure existing components
This commit is contained in:
Ben Lambert
2022-07-20 13:00:49 +02:00
committed by GitHub
90 changed files with 616 additions and 42 deletions
+8
View File
@@ -0,0 +1,8 @@
---
'@backstage/core-plugin-api': patch
---
Introduced a new experimental feature that allows you to declare plugin-wide options for your plugin by defining
`__experimentalConfigure` in your `createPlugin` options. See https://backstage.io/docs/plugins/customization.md for more information.
This is an experimental feature and it will have breaking changes in the future.
+15
View File
@@ -0,0 +1,15 @@
---
'@backstage/plugin-catalog': minor
---
Plugin catalog has been modified to use an experimental feature where you can customize the title of the create button.
You can modify it by doing:
```typescript jsx
import { catalogPlugin } from '@backstage/plugin-catalog';
catalogPlugin.__experimentalReconfigure({
createButtonTitle: 'New',
});
```
+68
View File
@@ -0,0 +1,68 @@
---
id: customization
title: Customization
description: Documentation on adding a customization logic to the plugin
---
## Overview
The Backstage core logic provides a possibility to make the component customizable in such a way that the application
developer can redefine the labels, icons, elements or even completely replace the component. It's up to each plugin
to decide what can be customized.
## For a plugin developer
When you are creating your plugin, you have a possibility to use a metadata field and define there all
customizable elements. For example
```typescript jsx
const plugin = createPlugin({
id: 'my-plugin',
__experimentalConfigure(
options?: CatalogInputPluginOptions,
): CatalogPluginOptions {
const defaultOptions = {
createButtonTitle: 'Create',
};
return { ...defaultOptions, ...options };
},
});
```
And the rendering part of the exposed component can retrieve that metadata as:
```typescript jsx
export type CatalogPluginOptions = {
createButtonTitle: string;
};
export type CatalogInputPluginOptions = {
createButtonTitle: string;
};
export const useCatalogPluginOptions = () =>
usePluginOptions<CatalogPluginOptions>();
export function DefaultMyPluginWelcomePage() {
const { createButtonTitle } = useCatalogPluginOptions();
return (
<div>
<button>{createButtonTitle}</button>
</div>
);
}
```
## For an application developer using the plugin
The way to reconfigure the default values provided by the plugin you can do it via reconfigure method, defined on the
plugin. Example:
```typescript jsx
import { myPlugin } from '@backstage/my-plugin';
myPlugin.__experimentalReconfigure({
createButtonTitle: 'New',
});
```
+1 -1
View File
@@ -20,7 +20,6 @@
"@backstage/plugin-azure-devops": "^0.1.23",
"@backstage/plugin-apache-airflow": "^0.2.0",
"@backstage/plugin-badges": "^0.2.31",
"@backstage/plugin-catalog": "^1.4.0",
"@backstage/plugin-catalog-common": "^1.0.4",
"@backstage/plugin-catalog-graph": "^0.2.19",
"@backstage/plugin-catalog-import": "^0.8.10",
@@ -70,6 +69,7 @@
"@roadiehq/backstage-plugin-github-insights": "^2.0.0",
"@roadiehq/backstage-plugin-github-pull-requests": "^2.0.0",
"@roadiehq/backstage-plugin-travis-ci": "^2.0.0",
"@internal/plugin-catalog-customized": "0.0.0",
"history": "^5.0.0",
"prop-types": "^15.7.2",
"react": "^17.0.2",
+3 -1
View File
@@ -35,11 +35,13 @@ import {
} from '@backstage/core-components';
import { apiDocsPlugin, ApiExplorerPage } from '@backstage/plugin-api-docs';
import { AzurePullRequestsPage } from '@backstage/plugin-azure-devops';
import {
CatalogEntityPage,
CatalogIndexPage,
catalogPlugin,
} from '@backstage/plugin-catalog';
} from '@internal/plugin-catalog-customized';
import { CatalogGraphPage } from '@backstage/plugin-catalog-graph';
import {
CatalogImportPage,
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { EntityLayout } from '@backstage/plugin-catalog';
import { EntityLayout } from '@internal/plugin-catalog-customized';
import {
EntityProvider,
starredEntitiesApiRef,
@@ -59,7 +59,7 @@ import {
isComponentType,
isKind,
isOrphan,
} from '@backstage/plugin-catalog';
} from '@internal/plugin-catalog-customized';
import {
Direction,
EntityCatalogGraphCard,
@@ -33,7 +33,7 @@ import {
useContent,
} from '@backstage/core-components';
import { useApi, useRouteRef } from '@backstage/core-plugin-api';
import { CatalogSearchResultListItem } from '@backstage/plugin-catalog';
import { CatalogSearchResultListItem } from '@internal/plugin-catalog-customized';
import {
catalogApiRef,
CATALOG_FILTER_EXISTS,
@@ -24,7 +24,7 @@ import {
useSidebarPinState,
} from '@backstage/core-components';
import { useApi } from '@backstage/core-plugin-api';
import { CatalogSearchResultListItem } from '@backstage/plugin-catalog';
import { CatalogSearchResultListItem } from '@internal/plugin-catalog-customized';
import {
catalogApiRef,
CATALOG_FILTER_EXISTS,
+26 -2
View File
@@ -214,6 +214,7 @@ export type BackstageIdentityResponse = {
export type BackstagePlugin<
Routes extends AnyRoutes = {},
ExternalRoutes extends AnyExternalRoutes = {},
PluginInputOptions extends {} = {},
> = {
getId(): string;
getApis(): Iterable<AnyApiFactory>;
@@ -221,6 +222,7 @@ export type BackstagePlugin<
provide<T>(extension: Extension<T>): T;
routes: Routes;
externalRoutes: ExternalRoutes;
__experimentalReconfigure(options: PluginInputOptions): void;
};
// @public
@@ -303,9 +305,10 @@ export function createExternalRouteRef<
export function createPlugin<
Routes extends AnyRoutes = {},
ExternalRoutes extends AnyExternalRoutes = {},
PluginInputOptions extends {} = {},
>(
config: PluginConfig<Routes, ExternalRoutes>,
): BackstagePlugin<Routes, ExternalRoutes>;
config: PluginConfig<Routes, ExternalRoutes, PluginInputOptions>,
): BackstagePlugin<Routes, ExternalRoutes, PluginInputOptions>;
// @public
export function createReactExtension<
@@ -621,12 +624,14 @@ export type PendingOAuthRequest = {
export type PluginConfig<
Routes extends AnyRoutes,
ExternalRoutes extends AnyExternalRoutes,
PluginInputOptions extends {},
> = {
id: string;
apis?: Iterable<AnyApiFactory>;
routes?: Routes;
externalRoutes?: ExternalRoutes;
featureFlags?: PluginFeatureFlagConfig[];
__experimentalConfigure?(options?: PluginInputOptions): {};
};
// @public
@@ -634,6 +639,20 @@ export type PluginFeatureFlagConfig = {
name: string;
};
// @alpha
export interface PluginOptionsProviderProps {
// (undocumented)
children: ReactNode;
// (undocumented)
plugin?: BackstagePlugin;
}
// @alpha
export const PluginProvider: ({
children,
plugin,
}: PluginOptionsProviderProps) => JSX.Element;
// @public
export type ProfileInfo = {
email?: string;
@@ -734,6 +753,11 @@ export function useElementFilter<T>(
dependencies?: any[],
): T;
// @alpha
export function usePluginOptions<
TPluginOptions extends {} = {},
>(): TPluginOptions;
// @public
export function useRouteRef<Optional extends boolean, Params extends AnyParams>(
routeRef: ExternalRouteRef<Params, Optional>,
@@ -15,12 +15,13 @@
*/
import React, { lazy, Suspense } from 'react';
import { AnalyticsContext } from '../analytics/AnalyticsContext';
import { AnalyticsContext } from '../analytics';
import { useApp } from '../app';
import { RouteRef, useRouteRef } from '../routing';
import { attachComponentData } from './componentData';
import { Extension, BackstagePlugin } from '../plugin/types';
import { Extension, BackstagePlugin } from '../plugin';
import { PluginErrorBoundary } from './PluginErrorBoundary';
import { PluginProvider } from '../plugin-options';
/**
* Lazy or synchronous retrieving of extension components.
@@ -245,7 +246,9 @@ export function createReactExtension<
...(mountPoint && { routeRef: mountPoint.id }),
}}
>
<Component {...props} />
<PluginProvider plugin={plugin}>
<Component {...props} />
</PluginProvider>
</AnalyticsContext>
</PluginErrorBoundary>
</Suspense>
+1
View File
@@ -22,6 +22,7 @@
export * from './analytics';
export * from './apis';
export * from './plugin-options';
export * from './app';
export * from './extensions';
export * from './icons';
@@ -0,0 +1,18 @@
/*
* Copyright 2020 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.
*/
export { usePluginOptions, PluginProvider } from './usePluginOptions';
export type { PluginOptionsProviderProps } from './usePluginOptions';
@@ -0,0 +1,53 @@
/*
* Copyright 2021 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 React from 'react';
import { renderHook } from '@testing-library/react-hooks';
import { usePluginOptions, PluginProvider } from './usePluginOptions';
import { createPlugin } from '../plugin';
describe('usePluginOptions', () => {
it('should provide a versioned value to hook', () => {
type TestInputPluginOptions = {
'key-1': string;
};
type TestPluginOptions = {
'key-1': string;
'key-2': string;
};
const plugin = createPlugin({
id: 'my-plugin',
__experimentalConfigure(_: TestInputPluginOptions): TestPluginOptions {
return { 'key-1': 'value-1', 'key-2': 'value-2' };
},
});
const rendered = renderHook(() => usePluginOptions(), {
wrapper: ({ children }) => (
<PluginProvider plugin={plugin}>{children}</PluginProvider>
),
});
const config = rendered.result.current;
expect(config).toEqual({
'key-1': 'value-1',
'key-2': 'value-2',
});
});
});
@@ -0,0 +1,92 @@
/*
* Copyright 2020 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 {
createVersionedContext,
createVersionedValueMap,
useVersionedContext,
} from '@backstage/version-bridge';
import { BackstagePlugin } from '../plugin';
import React, { ReactNode } from 'react';
const contextKey: string = 'plugin-context';
/**
* Properties for the PluginProvider component.
*
* @alpha
*/
export interface PluginOptionsProviderProps {
children: ReactNode;
plugin?: BackstagePlugin;
}
/**
* Contains the plugin configuration.
*
* @alpha
*/
export const PluginProvider = ({
children,
plugin,
}: PluginOptionsProviderProps): JSX.Element => {
const { Provider } = createVersionedContext<{
1: { plugin: BackstagePlugin | undefined };
}>(contextKey);
return (
<Provider
value={createVersionedValueMap({
1: {
plugin,
},
})}
>
{children}
</Provider>
);
};
/**
* Grab the current entity from the context, throws if the entity has not yet been loaded
* or is not available.
*
* @alpha
*/
export function usePluginOptions<
TPluginOptions extends {} = {},
>(): TPluginOptions {
const versionedHolder = useVersionedContext<{ 1: TPluginOptions }>(
contextKey,
);
if (!versionedHolder) {
throw new Error('Plugin Options context is not available');
}
const value = versionedHolder.atVersion(1);
if (!value) {
throw new Error('Plugin Options v1 is not available');
}
return (
value as unknown as {
plugin: {
getPluginOptions(): {};
};
}
).plugin.getPluginOptions() as TPluginOptions;
}
+27 -4
View File
@@ -30,9 +30,18 @@ import { AnyApiFactory } from '../apis';
export class PluginImpl<
Routes extends AnyRoutes,
ExternalRoutes extends AnyExternalRoutes,
> implements BackstagePlugin<Routes, ExternalRoutes>
PluginInputOptions extends {},
> implements BackstagePlugin<Routes, ExternalRoutes, PluginInputOptions>
{
constructor(private readonly config: PluginConfig<Routes, ExternalRoutes>) {}
constructor(
private readonly config: PluginConfig<
Routes,
ExternalRoutes,
PluginInputOptions
>,
) {}
private options: {} | undefined = undefined;
getId(): string {
return this.config.id;
@@ -58,6 +67,19 @@ export class PluginImpl<
return extension.expose(this);
}
__experimentalReconfigure(options: PluginInputOptions): void {
if (this.config.__experimentalConfigure) {
this.options = this.config.__experimentalConfigure(options);
}
}
getPluginOptions(): {} {
if (this.config.__experimentalConfigure && !this.options) {
this.options = this.config.__experimentalConfigure();
}
return this.options ?? {};
}
toString() {
return `plugin{${this.config.id}}`;
}
@@ -72,8 +94,9 @@ export class PluginImpl<
export function createPlugin<
Routes extends AnyRoutes = {},
ExternalRoutes extends AnyExternalRoutes = {},
PluginInputOptions extends {} = {},
>(
config: PluginConfig<Routes, ExternalRoutes>,
): BackstagePlugin<Routes, ExternalRoutes> {
config: PluginConfig<Routes, ExternalRoutes, PluginInputOptions>,
): BackstagePlugin<Routes, ExternalRoutes, PluginInputOptions> {
return new PluginImpl(config);
}
@@ -15,6 +15,7 @@
*/
export { createPlugin } from './Plugin';
export type {
AnyExternalRoutes,
AnyRoutes,
+5 -1
View File
@@ -15,7 +15,7 @@
*/
import { RouteRef, SubRouteRef, ExternalRouteRef } from '../routing';
import { AnyApiFactory } from '../apis/system';
import { AnyApiFactory } from '../apis';
/**
* Plugin extension type.
@@ -52,6 +52,7 @@ export type AnyExternalRoutes = { [name: string]: ExternalRouteRef };
export type BackstagePlugin<
Routes extends AnyRoutes = {},
ExternalRoutes extends AnyExternalRoutes = {},
PluginInputOptions extends {} = {},
> = {
getId(): string;
getApis(): Iterable<AnyApiFactory>;
@@ -62,6 +63,7 @@ export type BackstagePlugin<
provide<T>(extension: Extension<T>): T;
routes: Routes;
externalRoutes: ExternalRoutes;
__experimentalReconfigure(options: PluginInputOptions): void;
};
/**
@@ -82,12 +84,14 @@ export type PluginFeatureFlagConfig = {
export type PluginConfig<
Routes extends AnyRoutes,
ExternalRoutes extends AnyExternalRoutes,
PluginInputOptions extends {},
> = {
id: string;
apis?: Iterable<AnyApiFactory>;
routes?: Routes;
externalRoutes?: ExternalRoutes;
featureFlags?: PluginFeatureFlagConfig[];
__experimentalConfigure?(options?: PluginInputOptions): {};
};
/**
+1
View File
@@ -25,6 +25,7 @@ export const adrPlugin: BackstagePlugin<
{
root: RouteRef<undefined>;
},
{},
{}
>;
+1
View File
@@ -13,6 +13,7 @@ export const airbrakePlugin: BackstagePlugin<
{
root: RouteRef<undefined>;
},
{},
{}
>;
+1
View File
@@ -21,6 +21,7 @@ export const allurePlugin: BackstagePlugin<
{
root: RouteRef<undefined>;
},
{},
{}
>;
+1 -1
View File
@@ -10,7 +10,7 @@ import { Config } from '@backstage/config';
import { IdentityApi } from '@backstage/core-plugin-api';
// @public @deprecated (undocumented)
export const analyticsModuleGA: BackstagePlugin<{}, {}>;
export const analyticsModuleGA: BackstagePlugin<{}, {}, {}>;
// @public
export class GoogleAnalytics implements AnalyticsApi {
+3 -2
View File
@@ -10,8 +10,8 @@ import { RouteRef } from '@backstage/core-plugin-api';
// @public
export const ApacheAirflowDagTable: ({
dagIds,
}: {
dagIds,
}: {
dagIds?: string[] | undefined;
}) => JSX.Element;
@@ -27,6 +27,7 @@ export const apacheAirflowPlugin: BackstagePlugin<
{
root: RouteRef<undefined>;
},
{},
{}
>;
```
+2 -1
View File
@@ -47,7 +47,8 @@ const apiDocsPlugin: BackstagePlugin<
},
{
registerApi: ExternalRouteRef<undefined, true>;
}
},
{}
>;
export { apiDocsPlugin };
export { apiDocsPlugin as plugin };
+1
View File
@@ -41,6 +41,7 @@ export const apolloExplorerPlugin: BackstagePlugin<
{
root: RouteRef<undefined>;
},
{},
{}
>;
+1 -1
View File
@@ -170,7 +170,7 @@ export class AzureDevOpsClient implements AzureDevOpsApi {
// Warning: (ae-missing-release-tag) "azureDevOpsPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const azureDevOpsPlugin: BackstagePlugin<{}, {}>;
export const azureDevOpsPlugin: BackstagePlugin<{}, {}, {}>;
// Warning: (ae-missing-release-tag) "AzurePullRequestsIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
+1 -1
View File
@@ -10,7 +10,7 @@ import { BackstagePlugin } from '@backstage/core-plugin-api';
// Warning: (ae-missing-release-tag) "badgesPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const badgesPlugin: BackstagePlugin<{}, {}>;
export const badgesPlugin: BackstagePlugin<{}, {}, {}>;
// Warning: (ae-missing-release-tag) "EntityBadgesDialog" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
+1
View File
@@ -16,6 +16,7 @@ export const bazaarPlugin: BackstagePlugin<
{
root: RouteRef<undefined>;
},
{},
{}
>;
+1 -1
View File
@@ -11,7 +11,7 @@ import { Entity } from '@backstage/catalog-model';
// Warning: (ae-missing-release-tag) "bitrisePlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const bitrisePlugin: BackstagePlugin<{}, {}>;
export const bitrisePlugin: BackstagePlugin<{}, {}, {}>;
// Warning: (ae-missing-release-tag) "EntityBitriseContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
+1
View File
@@ -0,0 +1 @@
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
+57
View File
@@ -0,0 +1,57 @@
# Backstage Catalog Frontend
This is the React frontend to customize Backstage [software
catalog](http://backstage.io/docs/features/software-catalog/software-catalog-overview).
This package supplies the example how it can be achieved.
## Installation
This `@internal/plugin-catalog-customized` package comes installed by default in example application of
Backstage application.
To check if you already have the package, look under
`packages/app/package.json`, in the `dependencies` block, for
`@internal/plugin-catalog-customized`. The instructions below walk through restoring the
plugin, if you previously removed it.
### Install the package
```bash
# From your Backstage root directory
yarn add --cwd packages/app @internal/plugin-catalog-customized
```
### Add the plugin to your `packages/app`
Add the import to a file where is your plugin catalog is defined:
```diff
// packages/app/src/App.tsx
import from '@internal/plugin-catalog-customized';
...
import {
CatalogIndexPage,
CatalogEntityPage,
} from '@backstage/plugin-catalog';
...
```
## Development
This frontend plugin can be started in a standalone mode from directly in this
package with `yarn start`. However, it will have limited functionality and that
process is most convenient when developing the catalog frontend plugin itself.
To evaluate the catalog and have a greater amount of functionality available,
run the entire Backstage example application from the root folder:
```bash
yarn dev
```
This will launch both frontend and backend in the same window, populated with
some example entities.
+9
View File
@@ -0,0 +1,9 @@
## API Report File for "@internal/plugin-catalog-customized"
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
export * from '@backstage/plugin-catalog';
// (No @packageDocumentation comment for this package)
```
+49
View File
@@ -0,0 +1,49 @@
{
"name": "@internal/plugin-catalog-customized",
"description": "The internal Backstage Customizable plugin for browsing the Backstage catalog",
"version": "0.0.0",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"private": true,
"publishConfig": {
"access": "public",
"main": "dist/index.esm.js",
"types": "dist/index.d.ts"
},
"backstage": {
"role": "frontend-plugin"
},
"homepage": "https://backstage.io",
"repository": {
"type": "git",
"url": "https://github.com/backstage/backstage",
"directory": "plugins/catalog-customized"
},
"keywords": [
"backstage"
],
"scripts": {
"build": "backstage-cli package build",
"start": "backstage-cli package start",
"lint": "backstage-cli package lint",
"test": "backstage-cli package test",
"diff": "backstage-cli plugin:diff",
"prepack": "backstage-cli package prepack",
"postpack": "backstage-cli package postpack",
"clean": "backstage-cli package clean"
},
"dependencies": {
"@backstage/plugin-catalog": "^1.4.0",
"@backstage/plugin-catalog-react": "^1.1.2"
},
"devDependencies": {
"@types/react": "^16.13.1 || ^17.0.0"
},
"peerDependencies": {
"react": "^16.13.1 || ^17.0.0"
},
"files": [
"dist"
]
}
+18
View File
@@ -0,0 +1,18 @@
/*
* Copyright 2020 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 './plugin';
export * from '@backstage/plugin-catalog';
+22
View File
@@ -0,0 +1,22 @@
/*
* Copyright 2020 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 { catalogPlugin } from '@backstage/plugin-catalog';
// id: 'catalog-customized'
catalogPlugin.__experimentalReconfigure({
createButtonTitle: 'New',
});
+2 -1
View File
@@ -48,7 +48,8 @@ export const catalogGraphPlugin: BackstagePlugin<
},
true
>;
}
},
{}
>;
// @public
+1
View File
@@ -133,6 +133,7 @@ const catalogImportPlugin: BackstagePlugin<
{
importPage: RouteRef<undefined>;
},
{},
{}
>;
export { catalogImportPlugin };
+4 -1
View File
@@ -79,6 +79,8 @@ export interface CatalogKindHeaderProps {
initialFilter?: string;
}
// Warning: (ae-forgotten-export) The symbol "CatalogInputPluginOptions" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
export const catalogPlugin: BackstagePlugin<
{
@@ -99,7 +101,8 @@ export const catalogPlugin: BackstagePlugin<
},
true
>;
}
},
CatalogInputPluginOptions
>;
// @public (undocumented)
@@ -23,8 +23,10 @@ import {
} from '@backstage/catalog-model';
import { TableColumn, TableProps } from '@backstage/core-components';
import {
createPlugin,
IdentityApi,
identityApiRef,
PluginProvider,
ProfileInfo,
storageApiRef,
} from '@backstage/core-plugin-api';
@@ -133,6 +135,22 @@ describe('DefaultCatalogPage', () => {
};
const storageApi = MockStorageApi.create();
type TestInputPluginOptions = {
'key-1': string;
};
type TestPluginOptions = {
'key-1': string;
'key-2': string;
};
const plugin = createPlugin({
id: 'my-plugin',
__experimentalConfigure(_: TestInputPluginOptions): TestPluginOptions {
return { 'key-1': 'value-1', 'key-2': 'value-2' };
},
});
const renderWrapped = (children: React.ReactNode) =>
renderWithEffects(
wrapInTestApp(
@@ -144,7 +162,7 @@ describe('DefaultCatalogPage', () => {
[starredEntitiesApiRef, new MockStarredEntitiesApi()],
]}
>
{children}
<PluginProvider plugin={plugin}>{children}</PluginProvider>
</TestApiProvider>,
{
mountedRoutes: {
@@ -39,6 +39,7 @@ import React from 'react';
import { createComponentRouteRef } from '../../routes';
import { CatalogTable, CatalogTableRow } from '../CatalogTable';
import { CatalogKindHeader } from '../CatalogKindHeader';
import { useCatalogPluginOptions } from '../../options';
/**
* Props for root catalog pages.
@@ -65,6 +66,8 @@ export function DefaultCatalogPage(props: DefaultCatalogPageProps) {
useApi(configApiRef).getOptionalString('organization.name') ?? 'Backstage';
const createComponentLink = useRouteRef(createComponentRouteRef);
const { createButtonTitle } = useCatalogPluginOptions();
return (
<PageWithHeader title={`${orgName} Catalog`} themeId="home">
<EntityListProvider>
@@ -73,7 +76,7 @@ export function DefaultCatalogPage(props: DefaultCatalogPageProps) {
titleComponent={<CatalogKindHeader initialFilter={initialKind} />}
>
<CreateButton
title="Create Component"
title={createButtonTitle}
to={createComponentLink && createComponentLink()}
/>
<SupportButton>All your software catalog entities</SupportButton>
+28
View File
@@ -0,0 +1,28 @@
/*
* Copyright 2020 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 { usePluginOptions } from '@backstage/core-plugin-api';
export type CatalogPluginOptions = {
createButtonTitle: string;
};
export type CatalogInputPluginOptions = {
createButtonTitle: string;
};
export const useCatalogPluginOptions = () =>
usePluginOptions<CatalogPluginOptions>();
+9
View File
@@ -43,6 +43,7 @@ import { HasSubcomponentsCardProps } from './components/HasSubcomponentsCard';
import { HasSystemsCardProps } from './components/HasSystemsCard';
import { RelatedEntitiesCardProps } from './components/RelatedEntitiesCard';
import { rootRouteRef } from './routes';
import { CatalogInputPluginOptions, CatalogPluginOptions } from './options';
/** @public */
export const catalogPlugin = createPlugin({
@@ -72,6 +73,14 @@ export const catalogPlugin = createPlugin({
createComponent: createComponentRouteRef,
viewTechDoc: viewTechDocRouteRef,
},
__experimentalConfigure(
options?: CatalogInputPluginOptions,
): CatalogPluginOptions {
const defaultOptions = {
createButtonTitle: 'Create',
};
return { ...defaultOptions, ...options };
},
});
/** @public */
+1
View File
@@ -107,6 +107,7 @@ export const cicdStatisticsPlugin: BackstagePlugin<
{
entityContent: RouteRef<undefined>;
},
{},
{}
>;
+1 -1
View File
@@ -75,7 +75,7 @@ export const circleCIBuildRouteRef: SubRouteRef<PathParams<'/:buildId'>>;
// Warning: (ae-missing-release-tag) "circleCIPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
const circleCIPlugin: BackstagePlugin<{}, {}>;
const circleCIPlugin: BackstagePlugin<{}, {}, {}>;
export { circleCIPlugin };
export { circleCIPlugin as plugin };
+1
View File
@@ -141,6 +141,7 @@ const cloudbuildPlugin: BackstagePlugin<
{
entityContent: RouteRef<undefined>;
},
{},
{}
>;
export { cloudbuildPlugin };
+1
View File
@@ -48,6 +48,7 @@ export const codeClimatePlugin: BackstagePlugin<
{
root: RouteRef<undefined>;
},
{},
{}
>;
+1
View File
@@ -14,6 +14,7 @@ export const codeCoveragePlugin: BackstagePlugin<
{
root: RouteRef<undefined>;
},
{},
{}
>;
+1
View File
@@ -23,6 +23,7 @@ export const codescenePlugin: BackstagePlugin<
projectId: string;
}>;
},
{},
{}
>;
+1
View File
@@ -38,6 +38,7 @@ export const configSchemaPlugin: BackstagePlugin<
{
root: RouteRef<undefined>;
},
{},
{}
>;
+1
View File
@@ -382,6 +382,7 @@ const costInsightsPlugin: BackstagePlugin<
growthAlerts: RouteRef<undefined>;
unlabeledDataflowAlerts: RouteRef<undefined>;
},
{},
{}
>;
export { costInsightsPlugin };
+1 -1
View File
@@ -9,7 +9,7 @@ import { BackstagePlugin } from '@backstage/core-plugin-api';
import { Entity } from '@backstage/catalog-model';
// @public
export const dynatracePlugin: BackstagePlugin<{}, {}>;
export const dynatracePlugin: BackstagePlugin<{}, {}, {}>;
// @public
export const DynatraceTab: () => JSX.Element;
+1
View File
@@ -16,6 +16,7 @@ export const todoListPlugin: BackstagePlugin<
{
root: RouteRef<undefined>;
},
{},
{}
>;
+2 -1
View File
@@ -68,7 +68,8 @@ const explorePlugin: BackstagePlugin<
},
true
>;
}
},
{}
>;
export { explorePlugin };
export { explorePlugin as plugin };
+1
View File
@@ -20,6 +20,7 @@ export const firehydrantPlugin: BackstagePlugin<
{
root: RouteRef<undefined>;
},
{},
{}
>;
```
+1
View File
@@ -40,6 +40,7 @@ export const fossaPlugin: BackstagePlugin<
{
fossaOverview: RouteRef<undefined>;
},
{},
{}
>;
```
+1
View File
@@ -65,6 +65,7 @@ export const gcalendarPlugin: BackstagePlugin<
{
root: RouteRef<undefined>;
},
{},
{}
>;
+1
View File
@@ -57,6 +57,7 @@ const gcpProjectsPlugin: BackstagePlugin<
{
root: RouteRef<undefined>;
},
{},
{}
>;
export { gcpProjectsPlugin };
@@ -203,6 +203,7 @@ export const gitReleaseManagerPlugin: BackstagePlugin<
{
root: RouteRef<undefined>;
},
{},
{}
>;
+1
View File
@@ -198,6 +198,7 @@ const githubActionsPlugin: BackstagePlugin<
{
entityContent: RouteRef<undefined>;
},
{},
{}
>;
export { githubActionsPlugin };
+1 -1
View File
@@ -47,7 +47,7 @@ export const EntityGithubDeploymentsCard: (props: {
// Warning: (ae-missing-release-tag) "githubDeploymentsPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const githubDeploymentsPlugin: BackstagePlugin<{}, {}>;
export const githubDeploymentsPlugin: BackstagePlugin<{}, {}, {}>;
// Warning: (ae-forgotten-export) The symbol "GithubDeploymentsTableProps" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "GithubDeploymentsTable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+1
View File
@@ -149,6 +149,7 @@ const gitopsProfilesPlugin: BackstagePlugin<
}>;
createPage: RouteRef<undefined>;
},
{},
{}
>;
export { gitopsProfilesPlugin };
+1 -1
View File
@@ -15,7 +15,7 @@ export const EntityGoCdContent: () => JSX.Element;
export const GOCD_PIPELINES_ANNOTATION = 'gocd.org/pipelines';
// @public
export const gocdPlugin: BackstagePlugin<{}, {}>;
export const gocdPlugin: BackstagePlugin<{}, {}, {}>;
// @public
export const isGoCdAvailable: (entity: Entity) => boolean;
+1 -1
View File
@@ -39,7 +39,7 @@ export const GraphiQLIcon: IconComponent;
export const GraphiQLPage: () => JSX.Element;
// @public (undocumented)
const graphiqlPlugin: BackstagePlugin<{}, {}>;
const graphiqlPlugin: BackstagePlugin<{}, {}, {}>;
export { graphiqlPlugin };
export { graphiqlPlugin as plugin };
+1
View File
@@ -107,6 +107,7 @@ export const homePlugin: BackstagePlugin<
{
root: RouteRef<undefined>;
},
{},
{}
>;
+1
View File
@@ -249,6 +249,7 @@ const ilertPlugin: BackstagePlugin<
{
root: RouteRef<undefined>;
},
{},
{}
>;
export { ilertPlugin };
+1
View File
@@ -108,6 +108,7 @@ const jenkinsPlugin: BackstagePlugin<
{
entityContent: RouteRef<undefined>;
},
{},
{}
>;
export { jenkinsPlugin };
+1
View File
@@ -34,6 +34,7 @@ const kafkaPlugin: BackstagePlugin<
{
entityContent: RouteRef<undefined>;
},
{},
{}
>;
export { kafkaPlugin };
+1
View File
@@ -353,6 +353,7 @@ const kubernetesPlugin: BackstagePlugin<
{
entityContent: RouteRef<undefined>;
},
{},
{}
>;
export { kubernetesPlugin };
+1
View File
@@ -175,6 +175,7 @@ const lighthousePlugin: BackstagePlugin<
root: RouteRef<undefined>;
entityContent: RouteRef<undefined>;
},
{},
{}
>;
export { lighthousePlugin };
+1
View File
@@ -42,6 +42,7 @@ export const newRelicDashboardPlugin: BackstagePlugin<
{
root: RouteRef<undefined>;
},
{},
{}
>;
+1
View File
@@ -20,6 +20,7 @@ const newRelicPlugin: BackstagePlugin<
{
root: RouteRef<undefined>;
},
{},
{}
>;
export { newRelicPlugin };
+2 -1
View File
@@ -58,7 +58,8 @@ const orgPlugin: BackstagePlugin<
{},
{
catalogIndex: ExternalRouteRef<undefined, false>;
}
},
{}
>;
export { orgPlugin };
export { orgPlugin as plugin };
+1 -1
View File
@@ -166,7 +166,7 @@ export type PagerDutyOnCallsResponse = {
// Warning: (ae-missing-release-tag) "pagerDutyPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
const pagerDutyPlugin: BackstagePlugin<{}, {}>;
const pagerDutyPlugin: BackstagePlugin<{}, {}, {}>;
export { pagerDutyPlugin };
export { pagerDutyPlugin as plugin };
+1 -1
View File
@@ -119,7 +119,7 @@ export class PeriskopClient implements PeriskopApi {
}
// @public (undocumented)
export const periskopPlugin: BackstagePlugin<{}, {}>;
export const periskopPlugin: BackstagePlugin<{}, {}, {}>;
// @public (undocumented)
export interface RequestHeaders {
+1
View File
@@ -91,6 +91,7 @@ const rollbarPlugin: BackstagePlugin<
{
entityContent: RouteRef<undefined>;
},
{},
{}
>;
export { rollbarPlugin as plugin };
+2 -1
View File
@@ -367,7 +367,8 @@ export const scaffolderPlugin: BackstagePlugin<
},
{
registerComponent: ExternalRouteRef<undefined, true>;
}
},
{}
>;
// @public
+1
View File
@@ -75,6 +75,7 @@ const searchPlugin: BackstagePlugin<
{
root: RouteRef<undefined>;
},
{},
{}
>;
export { searchPlugin as plugin };
+1
View File
@@ -145,6 +145,7 @@ const sentryPlugin: BackstagePlugin<
{
root: RouteRef<undefined>;
},
{},
{}
>;
export { sentryPlugin as plugin };
+1 -1
View File
@@ -62,7 +62,7 @@ export const shortcutsApiRef: ApiRef<ShortcutApi>;
// Warning: (ae-missing-release-tag) "shortcutsPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const shortcutsPlugin: BackstagePlugin<{}, {}>;
export const shortcutsPlugin: BackstagePlugin<{}, {}, {}>;
// @public
export interface ShortcutsProps {
+1 -1
View File
@@ -44,7 +44,7 @@ export const SonarQubeCard: ({
// Warning: (ae-missing-release-tag) "sonarQubePlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
const sonarQubePlugin: BackstagePlugin<{}, {}>;
const sonarQubePlugin: BackstagePlugin<{}, {}, {}>;
export { sonarQubePlugin as plugin };
export { sonarQubePlugin };
+1
View File
@@ -98,6 +98,7 @@ const splunkOnCallPlugin: BackstagePlugin<
{
root: RouteRef<undefined>;
},
{},
{}
>;
export { splunkOnCallPlugin as plugin };
+1 -1
View File
@@ -20,7 +20,7 @@ export const HomePageStackOverflowQuestions: (
export const StackOverflowIcon: () => JSX.Element;
// @public
export const stackOverflowPlugin: BackstagePlugin<{}, {}>;
export const stackOverflowPlugin: BackstagePlugin<{}, {}, {}>;
// @public
export type StackOverflowQuestion = {
+1
View File
@@ -112,6 +112,7 @@ export const techInsightsPlugin: BackstagePlugin<
{
root: RouteRef<undefined>;
},
{},
{}
>;
+1
View File
@@ -126,6 +126,7 @@ const techRadarPlugin: BackstagePlugin<
{
root: RouteRef<undefined>;
},
{},
{}
>;
export { techRadarPlugin as plugin };
@@ -33,7 +33,7 @@ export type ReportIssueTemplateBuilder = ({
}) => ReportIssueTemplate;
// @public
export const techdocsModuleAddonsContribPlugin: BackstagePlugin<{}, {}>;
export const techdocsModuleAddonsContribPlugin: BackstagePlugin<{}, {}, {}>;
// @public
export const TextSize: () => JSX.Element | null;
+1
View File
@@ -291,6 +291,7 @@ const techdocsPlugin: BackstagePlugin<
}>;
entityContent: RouteRef<undefined>;
},
{},
{}
>;
export { techdocsPlugin as plugin };
+1
View File
@@ -84,6 +84,7 @@ export const todoPlugin: BackstagePlugin<
{
entityContent: RouteRef<undefined>;
},
{},
{}
>;
```
+1
View File
@@ -102,6 +102,7 @@ const userSettingsPlugin: BackstagePlugin<
{
settingsPage: RouteRef<undefined>;
},
{},
{}
>;
export { userSettingsPlugin as plugin };
+1 -1
View File
@@ -27,7 +27,7 @@ export interface VaultApi {
export const vaultApiRef: ApiRef<VaultApi>;
// @public
export const vaultPlugin: BackstagePlugin<{}, {}>;
export const vaultPlugin: BackstagePlugin<{}, {}, {}>;
// @public
export type VaultSecret = {
+1
View File
@@ -20,6 +20,7 @@ export const xcmetricsPlugin: BackstagePlugin<
{
root: RouteRef<undefined>;
},
{},
{}
>;
```