diff --git a/packages/app/package.json b/packages/app/package.json index 9c43f03f49..68fed34421 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -70,6 +70,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": "1.2.1-next.1", "history": "^5.0.0", "prop-types": "^15.7.2", "react": "^17.0.2", diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 35c61f2f16..dc004ef2fd 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -40,6 +40,10 @@ import { CatalogIndexPage, catalogPlugin, } from '@backstage/plugin-catalog'; + +/* Uncomment this import to enable a customized version of a catalog */ +// import '@internal/plugin-catalog-customized'; + import { CatalogGraphPage } from '@backstage/plugin-catalog-graph'; import { CatalogImportPage, diff --git a/packages/core-plugin-api/src/plugin-options/usePluginOptions.tsx b/packages/core-plugin-api/src/plugin-options/usePluginOptions.tsx index 629d0d290c..1e667e09ea 100644 --- a/packages/core-plugin-api/src/plugin-options/usePluginOptions.tsx +++ b/packages/core-plugin-api/src/plugin-options/usePluginOptions.tsx @@ -19,7 +19,7 @@ import { createVersionedValueMap, useVersionedContext, } from '@backstage/version-bridge'; -import { AnyPluginOptions } from '../plugin'; +import { PluginOptions } from '../plugin'; import React, { ReactNode } from 'react'; const contextKey: string = 'plugin-options-context'; @@ -31,7 +31,7 @@ const contextKey: string = 'plugin-options-context'; */ export interface PluginOptionsProviderProps { children: ReactNode; - pluginOptions?: AnyPluginOptions; + pluginOptions?: PluginOptions; } export const PluginOptionsProvider = ({ @@ -39,9 +39,7 @@ export const PluginOptionsProvider = ({ pluginOptions, }: PluginOptionsProviderProps): JSX.Element => { const value = { pluginOptions }; - const { Provider } = createVersionedContext<{ 1: AnyPluginOptions }>( - contextKey, - ); + const { Provider } = createVersionedContext<{ 1: PluginOptions }>(contextKey); return ( {children} @@ -56,7 +54,7 @@ export const PluginOptionsProvider = ({ * @public */ export function usePluginOptions< - TPluginOptions extends AnyPluginOptions = AnyPluginOptions, + TPluginOptions extends PluginOptions = PluginOptions, >(): TPluginOptions { const versionedHolder = useVersionedContext<{ 1: TPluginOptions }>( contextKey, @@ -71,5 +69,5 @@ export function usePluginOptions< throw new Error('Plugin Options v1 is not available'); } - return value.pluginOptions; + return value.pluginOptions as TPluginOptions; } diff --git a/packages/core-plugin-api/src/plugin/index.ts b/packages/core-plugin-api/src/plugin/index.ts index 6826ba37c6..e433e5cc86 100644 --- a/packages/core-plugin-api/src/plugin/index.ts +++ b/packages/core-plugin-api/src/plugin/index.ts @@ -15,6 +15,7 @@ */ export { createPlugin } from './Plugin'; + export type { AnyExternalRoutes, PluginOptions, diff --git a/plugins/catalog-customized/.eslintrc.js b/plugins/catalog-customized/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/catalog-customized/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/catalog-customized/README.md b/plugins/catalog-customized/README.md new file mode 100644 index 0000000000..129d70ad58 --- /dev/null +++ b/plugins/catalog-customized/README.md @@ -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. diff --git a/plugins/catalog-customized/package.json b/plugins/catalog-customized/package.json new file mode 100644 index 0000000000..4ffc82c2b0 --- /dev/null +++ b/plugins/catalog-customized/package.json @@ -0,0 +1,47 @@ +{ + "name": "@internal/plugin-catalog-customized", + "description": "The internal Backstage Customizable plugin for browsing the Backstage catalog", + "version": "1.2.1-next.1", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "private": false, + "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.3.1-next.0", + "@backstage/plugin-catalog-react": "^1.1.2-next.0" + }, + "peerDependencies": { + "@types/react": "^16.13.1 || ^17.0.0", + "react": "^16.13.1 || ^17.0.0" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/catalog-customized/src/components.tsx b/plugins/catalog-customized/src/components.tsx new file mode 100644 index 0000000000..9c9bd2e187 --- /dev/null +++ b/plugins/catalog-customized/src/components.tsx @@ -0,0 +1,33 @@ +/* + * 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 React from 'react'; +import { + EntityTypePickerProps, + UserListPickerProps, +} from '@backstage/plugin-catalog-react'; + +export const EntityOwnerPicker = () => { + return
Customized entity owner picker
; +}; + +export const EntityTypePicker = (_: EntityTypePickerProps) => { + return
Customized entity type picker
; +}; + +export const UserListPicker = (_: UserListPickerProps) => { + return
Customized user lis picker
; +}; diff --git a/plugins/catalog-customized/src/index.ts b/plugins/catalog-customized/src/index.ts new file mode 100644 index 0000000000..0ad4c9ff68 --- /dev/null +++ b/plugins/catalog-customized/src/index.ts @@ -0,0 +1,17 @@ +/* + * 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 { customizedCatalog } from './plugin'; diff --git a/plugins/catalog-customized/src/plugin.ts b/plugins/catalog-customized/src/plugin.ts new file mode 100644 index 0000000000..e46eb17192 --- /dev/null +++ b/plugins/catalog-customized/src/plugin.ts @@ -0,0 +1,33 @@ +/* + * 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'; + +import { + EntityOwnerPicker, + EntityTypePicker, + UserListPicker, +} from './components'; + +catalogPlugin.reconfigure({ + EntityOwnerPicker, + EntityTypePicker, + UserListPicker, + createButtonTitle: 'Maybe Create', + showButtonText: 'Mine catalog entities', +}); + +export const customizedCatalog = catalogPlugin; diff --git a/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.tsx b/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.tsx index 5250dcec0d..a02623118f 100644 --- a/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.tsx +++ b/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.tsx @@ -23,21 +23,24 @@ import { TableColumn, TableProps, } from '@backstage/core-components'; -import { configApiRef, useApi, useRouteRef } from '@backstage/core-plugin-api'; +import { + configApiRef, + useApi, + usePluginOptions, + useRouteRef, +} from '@backstage/core-plugin-api'; import { CatalogFilterLayout, EntityLifecyclePicker, EntityListProvider, - EntityOwnerPicker, EntityTagPicker, - EntityTypePicker, UserListFilterKind, - UserListPicker, } from '@backstage/plugin-catalog-react'; import React from 'react'; import { createComponentRouteRef } from '../../routes'; import { CatalogTable, CatalogTableRow } from '../CatalogTable'; import { CatalogKindHeader } from '../CatalogKindHeader'; +import { CatalogPageOptionsProps } from '../../types'; /** * Props for root catalog pages. @@ -64,6 +67,14 @@ export function DefaultCatalogPage(props: DefaultCatalogPageProps) { useApi(configApiRef).getOptionalString('organization.name') ?? 'Backstage'; const createComponentLink = useRouteRef(createComponentRouteRef); + const { + EntityOwnerPicker, + EntityTypePicker, + UserListPicker, + createButtonTitle, + showButtonText, + } = usePluginOptions(); + return ( @@ -72,10 +83,10 @@ export function DefaultCatalogPage(props: DefaultCatalogPageProps) { titleComponent={} > - All your software catalog entities + ${showButtonText} diff --git a/plugins/catalog/src/plugin.ts b/plugins/catalog/src/plugin.ts index 61612c404b..ce68fafcbc 100644 --- a/plugins/catalog/src/plugin.ts +++ b/plugins/catalog/src/plugin.ts @@ -18,7 +18,10 @@ import { CatalogClient } from '@backstage/catalog-client'; import { Entity } from '@backstage/catalog-model'; import { catalogApiRef, + EntityOwnerPicker, entityRouteRef, + EntityTypePicker, + UserListPicker, starredEntitiesApiRef, } from '@backstage/plugin-catalog-react'; import { createComponentRouteRef, viewTechDocRouteRef } from './routes'; @@ -31,7 +34,6 @@ import { fetchApiRef, storageApiRef, PluginOptions, - PluginInputOptions, } from '@backstage/core-plugin-api'; import { DefaultStarredEntitiesApi } from './apis'; import { AboutCardProps } from './components/AboutCard'; @@ -46,6 +48,8 @@ import { HasSystemsCardProps } from './components/HasSystemsCard'; import { RelatedEntitiesCardProps } from './components/RelatedEntitiesCard'; import { rootRouteRef } from './routes'; +export interface PluginInputOptions {} + /** @public */ export const catalogPlugin = createPlugin({ id: 'catalog', @@ -75,7 +79,13 @@ export const catalogPlugin = createPlugin({ viewTechDoc: viewTechDocRouteRef, }, configure(options?: PluginInputOptions): PluginOptions { - const defaultOptions = { createButtonTitle: 'Create' }; + const defaultOptions = { + EntityOwnerPicker, + EntityTypePicker, + UserListPicker, + createButtonTitle: 'Create', + showButtonText: 'All your software catalog entities', + }; if (!options) { return defaultOptions; } diff --git a/plugins/catalog/src/types.ts b/plugins/catalog/src/types.ts new file mode 100644 index 0000000000..29997d1368 --- /dev/null +++ b/plugins/catalog/src/types.ts @@ -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 { + EntityTypePickerProps, + UserListPickerProps, +} from '@backstage/plugin-catalog-react'; + +export type CatalogPageOptionsProps = { + EntityOwnerPicker: () => JSX.Element | null; + EntityTypePicker: (props: EntityTypePickerProps) => JSX.Element | null; + UserListPicker: (props: UserListPickerProps) => JSX.Element | null; + createButtonTitle: string; + showButtonText: string; +};