diff --git a/.changeset/slimy-ghosts-rescue.md b/.changeset/slimy-ghosts-rescue.md new file mode 100644 index 0000000000..ae01e3f718 --- /dev/null +++ b/.changeset/slimy-ghosts-rescue.md @@ -0,0 +1,9 @@ +--- +'@backstage/plugin-catalog-unprocessed-entities': patch +'@backstage/plugin-devtools-react': minor +'@backstage/plugin-devtools': patch +'@backstage/plugin-devtools-backend': patch +'@backstage/plugin-devtools-common': patch +--- + +Add support for adding `unprocessed-entities` and other tabs to `devtools` when using the New Frontend system diff --git a/packages/app-next/app-config.yaml b/packages/app-next/app-config.yaml index 97e8446fa4..d21e19f0de 100644 --- a/packages/app-next/app-config.yaml +++ b/packages/app-next/app-config.yaml @@ -114,6 +114,11 @@ app: # - entity-content:azure-devops/pull-requests # - entity-content:azure-devops/git-tags + # Enable the catalog-unprocessed-entities tab in devtools + - devtools-content:catalog-unprocessed-entities: true + # Disable the catalog-unprocessed-entities element outside devtools + - page:catalog-unprocessed-entities: false + # scmAuthExtension: >- # createScmAuthExtension({ # id: 'apis.scmAuth.addons.ghe', diff --git a/packages/app-next/package.json b/packages/app-next/package.json index 839e2acc74..42a3b0199e 100644 --- a/packages/app-next/package.json +++ b/packages/app-next/package.json @@ -57,6 +57,7 @@ "@backstage/plugin-catalog-import": "workspace:^", "@backstage/plugin-catalog-react": "workspace:^", "@backstage/plugin-catalog-unprocessed-entities": "workspace:^", + "@backstage/plugin-devtools": "workspace:^", "@backstage/plugin-home": "workspace:^", "@backstage/plugin-kubernetes": "workspace:^", "@backstage/plugin-kubernetes-cluster": "workspace:^", diff --git a/packages/app-next/src/App.tsx b/packages/app-next/src/App.tsx index 6c567f3fe1..47f717c7ee 100644 --- a/packages/app-next/src/App.tsx +++ b/packages/app-next/src/App.tsx @@ -45,6 +45,8 @@ import { convertLegacyPageExtension } from '@backstage/core-compat-api'; import { convertLegacyEntityContentExtension } from '@backstage/plugin-catalog-react/alpha'; import { pluginInfoResolver } from './pluginInfoResolver'; import { appModuleNav } from './modules/appModuleNav'; +import devtoolsPlugin from '@backstage/plugin-devtools/alpha'; +import { unprocessedEntitiesDevToolsContent } from '@backstage/plugin-catalog-unprocessed-entities/alpha'; /* @@ -116,6 +118,11 @@ const notFoundErrorPageModule = createFrontendModule({ extensions: [notFoundErrorPage], }); +const devtoolsPluginUnprocessed = createFrontendModule({ + pluginId: 'catalog-unprocessed-entities', + extensions: [unprocessedEntitiesDevToolsContent], +}); + const collectedLegacyPlugins = convertLegacyAppRoot( } /> @@ -133,6 +140,8 @@ const app = createApp({ notFoundErrorPageModule, appModuleNav, customHomePageModule, + devtoolsPlugin, + devtoolsPluginUnprocessed, ...collectedLegacyPlugins, ], advanced: { diff --git a/plugins/catalog-unprocessed-entities/README.md b/plugins/catalog-unprocessed-entities/README.md index ebadbb7054..b39a4a7ad4 100644 --- a/plugins/catalog-unprocessed-entities/README.md +++ b/plugins/catalog-unprocessed-entities/README.md @@ -52,18 +52,36 @@ Import `catalogUnprocessedEntitiesPlugin` in your `App.tsx` and add it to your a ```typescript import catalogUnprocessedEntitiesPlugin from '@backstage/plugin-catalog-unprocessed-entities'; +import { unprocessedEntitiesDevToolsContent } from '@backstage/plugin-catalog-unprocessed-entities/alpha'; -// ... +// Optionally add unprocessed entities route to devtools +const devtoolsPluginUnprocessed = createFrontendModule({ + pluginId: 'catalog-unprocessed-entities', + extensions: [unprocessedEntitiesDevToolsContent], +}); export const app = createApp({ features: [ // ... catalogUnprocessedEntitiesPlugin, + + // Optionally add unprocessed entities route to devtools + devtoolsPluginUnprocessed, + devtoolsPlugin, // devtools plugin needs to be added too, if autodiscover is disabled // ... ], }); ``` +```yaml +app: + extensions: + # Enable the catalog-unprocessed-entities tab in devtools + - devtools-content:catalog-unprocessed-entities: true + # Disable the catalog-unprocessed-entities element outside devtools including the sidebar + - page:catalog-unprocessed-entities: false +``` + ## Customization If you want to use the provided endpoints in a different way, you can use the ApiRef doing the following: diff --git a/plugins/catalog-unprocessed-entities/package.json b/plugins/catalog-unprocessed-entities/package.json index de923b790d..af8b4a1d13 100644 --- a/plugins/catalog-unprocessed-entities/package.json +++ b/plugins/catalog-unprocessed-entities/package.json @@ -50,11 +50,13 @@ "test": "backstage-cli package test" }, "dependencies": { + "@backstage/core-compat-api": "workspace:^", "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", "@backstage/errors": "workspace:^", "@backstage/frontend-plugin-api": "workspace:^", "@backstage/plugin-catalog-unprocessed-entities-common": "workspace:^", + "@backstage/plugin-devtools-react": "workspace:^", "@material-ui/core": "^4.9.13", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "^4.0.0-alpha.60", diff --git a/plugins/catalog-unprocessed-entities/report-alpha.api.md b/plugins/catalog-unprocessed-entities/report-alpha.api.md index 50bc0c22fa..d3bd6dd28f 100644 --- a/plugins/catalog-unprocessed-entities/report-alpha.api.md +++ b/plugins/catalog-unprocessed-entities/report-alpha.api.md @@ -6,6 +6,7 @@ import { AnyApiFactory } from '@backstage/frontend-plugin-api'; import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; import { ApiFactory } from '@backstage/frontend-plugin-api'; +import { DevToolsContentBlueprintParams } from '@backstage/plugin-devtools-react'; import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api'; import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; import { IconComponent } from '@backstage/frontend-plugin-api'; @@ -89,5 +90,32 @@ const _default: OverridableFrontendPlugin< >; export default _default; +// @alpha +export const unprocessedEntitiesDevToolsContent: OverridableExtensionDefinition<{ + kind: 'devtools-content'; + name: undefined; + config: { + path: string | undefined; + title: string | undefined; + }; + configInput: { + title?: string | undefined; + path?: string | undefined; + }; + output: + | ExtensionDataRef + | ExtensionDataRef + | ExtensionDataRef< + RouteRef_2, + 'core.routing.ref', + { + optional: true; + } + > + | ExtensionDataRef; + inputs: {}; + params: DevToolsContentBlueprintParams; +}>; + // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/catalog-unprocessed-entities/src/alpha/devToolsContent.tsx b/plugins/catalog-unprocessed-entities/src/alpha/devToolsContent.tsx new file mode 100644 index 0000000000..3dcdc0ad6b --- /dev/null +++ b/plugins/catalog-unprocessed-entities/src/alpha/devToolsContent.tsx @@ -0,0 +1,36 @@ +/* + * Copyright 2024 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 { DevToolsContentBlueprint } from '@backstage/plugin-devtools-react'; + +/** + * DevTools content for catalog unprocessed entities. + * + * @alpha + */ +export const unprocessedEntitiesDevToolsContent = DevToolsContentBlueprint.make( + { + disabled: true, + params: { + path: 'unprocessed-entities', + title: 'Unprocessed Entities', + loader: () => + import('../components/UnprocessedEntities').then(m => ( + + )), + }, + }, +); diff --git a/plugins/catalog-unprocessed-entities/src/alpha/index.ts b/plugins/catalog-unprocessed-entities/src/alpha/index.ts index 2f137f09ee..001fa77643 100644 --- a/plugins/catalog-unprocessed-entities/src/alpha/index.ts +++ b/plugins/catalog-unprocessed-entities/src/alpha/index.ts @@ -15,3 +15,4 @@ */ export { default } from './plugin'; +export { unprocessedEntitiesDevToolsContent } from './devToolsContent'; diff --git a/plugins/devtools-backend/package.json b/plugins/devtools-backend/package.json index 6dd6f19d62..789dc77641 100644 --- a/plugins/devtools-backend/package.json +++ b/plugins/devtools-backend/package.json @@ -7,7 +7,8 @@ "pluginPackages": [ "@backstage/plugin-devtools", "@backstage/plugin-devtools-backend", - "@backstage/plugin-devtools-common" + "@backstage/plugin-devtools-common", + "@backstage/plugin-devtools-react" ] }, "publishConfig": { diff --git a/plugins/devtools-common/package.json b/plugins/devtools-common/package.json index 21d99333c2..7ece3371fa 100644 --- a/plugins/devtools-common/package.json +++ b/plugins/devtools-common/package.json @@ -8,7 +8,8 @@ "pluginPackages": [ "@backstage/plugin-devtools", "@backstage/plugin-devtools-backend", - "@backstage/plugin-devtools-common" + "@backstage/plugin-devtools-common", + "@backstage/plugin-devtools-react" ] }, "publishConfig": { diff --git a/plugins/devtools-react/.eslintrc.js b/plugins/devtools-react/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/devtools-react/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/devtools-react/README.md b/plugins/devtools-react/README.md new file mode 100644 index 0000000000..fe08b182a7 --- /dev/null +++ b/plugins/devtools-react/README.md @@ -0,0 +1,5 @@ +# @backstage/plugin-devtools-react + +Welcome to the web library package for the `devtools` plugin! + +_This plugin was created through the Backstage CLI_ diff --git a/plugins/devtools-react/catalog-info.yaml b/plugins/devtools-react/catalog-info.yaml new file mode 100644 index 0000000000..98b847bee3 --- /dev/null +++ b/plugins/devtools-react/catalog-info.yaml @@ -0,0 +1,10 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: backstage-plugin-devtools-react + title: '@backstage/plugin-devtools-react' + description: Web library for the devtools plugin +spec: + lifecycle: experimental + type: backstage-web-library + owner: maintainers diff --git a/plugins/devtools-react/package.json b/plugins/devtools-react/package.json new file mode 100644 index 0000000000..bec1564d80 --- /dev/null +++ b/plugins/devtools-react/package.json @@ -0,0 +1,67 @@ +{ + "name": "@backstage/plugin-devtools-react", + "version": "0.0.0", + "description": "Web library for the devtools plugin", + "backstage": { + "role": "web-library", + "pluginId": "devtools", + "pluginPackages": [ + "@backstage/plugin-devtools", + "@backstage/plugin-devtools-backend", + "@backstage/plugin-devtools-common", + "@backstage/plugin-devtools-react" + ] + }, + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/devtools-react" + }, + "license": "Apache-2.0", + "sideEffects": false, + "main": "src/index.ts", + "types": "src/index.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "backstage-cli package build", + "clean": "backstage-cli package clean", + "lint": "backstage-cli package lint", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack", + "start": "backstage-cli package start", + "test": "backstage-cli package test" + }, + "dependencies": { + "@backstage/core-plugin-api": "workspace:^", + "@backstage/frontend-plugin-api": "workspace:^", + "@material-ui/core": "^4.9.13" + }, + "devDependencies": { + "@backstage/cli": "workspace:^", + "@backstage/test-utils": "workspace:^", + "@testing-library/jest-dom": "^6.0.0", + "@testing-library/react": "^14.0.0", + "@types/react": "^18.0.0", + "react": "^18.0.2", + "react-dom": "^18.0.2", + "react-router-dom": "^6.3.0" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0", + "react-router-dom": "^6.3.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } +} diff --git a/plugins/devtools-react/report.api.md b/plugins/devtools-react/report.api.md new file mode 100644 index 0000000000..d1b1a42250 --- /dev/null +++ b/plugins/devtools-react/report.api.md @@ -0,0 +1,50 @@ +## API Report File for "@backstage/plugin-devtools-react" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; +import { ExtensionBlueprint } from '@backstage/frontend-plugin-api'; +import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; +import { JSX as JSX_2 } from 'react'; +import { RouteRef } from '@backstage/frontend-plugin-api'; + +// @public +export const DevToolsContentBlueprint: ExtensionBlueprint<{ + kind: 'devtools-content'; + params: DevToolsContentBlueprintParams; + output: + | ExtensionDataRef + | ExtensionDataRef + | ExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + > + | ExtensionDataRef; + inputs: {}; + config: { + path: string | undefined; + title: string | undefined; + }; + configInput: { + title?: string | undefined; + path?: string | undefined; + }; + dataRefs: never; +}>; + +// @public +export interface DevToolsContentBlueprintParams { + // (undocumented) + loader: () => Promise; + // (undocumented) + path: string; + // (undocumented) + routeRef?: RouteRef; + // (undocumented) + title: string; +} +``` diff --git a/plugins/devtools-react/src/devToolsContentBlueprint.tsx b/plugins/devtools-react/src/devToolsContentBlueprint.tsx new file mode 100644 index 0000000000..c10d8dade0 --- /dev/null +++ b/plugins/devtools-react/src/devToolsContentBlueprint.tsx @@ -0,0 +1,87 @@ +/* + * Copyright 2024 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 { + coreExtensionData, + createExtensionBlueprint, + ExtensionBoundary, + RouteRef, +} from '@backstage/frontend-plugin-api'; +import { JSX } from 'react'; + +/** + * Parameters for creating a DevTools route extension + * @public + */ +export interface DevToolsContentBlueprintParams { + path: string; + title: string; + loader: () => Promise; + routeRef?: RouteRef; +} + +/** + * Extension blueprint for creating DevTools content pages (appearing as tabs) + * + * @example + * ```tsx + * const myDevToolsContent = DevToolsContentBlueprint.make({ + * { + * params: { + * path: 'my-dev-tools', + * title: 'My DevTools', + * loader: () => + * import('../components/MyDevTools').then(m => + * compatWrapper(), + * ), + * }, + * }, + * }); + * ``` + * @public + */ +export const DevToolsContentBlueprint = createExtensionBlueprint({ + kind: 'devtools-content', + attachTo: { id: 'page:devtools', input: 'contents' }, + output: [ + coreExtensionData.reactElement, + coreExtensionData.routePath, + coreExtensionData.routeRef.optional(), + coreExtensionData.title, + ], + config: { + schema: { + path: z => z.string().optional(), + title: z => z.string().optional(), + }, + }, + *factory(params: DevToolsContentBlueprintParams, { node, config }) { + const path = config.path ?? params.path; + const title = config.title ?? params.title; + + yield coreExtensionData.reactElement( + ExtensionBoundary.lazy(node, params.loader), + ); + + yield coreExtensionData.routePath(path); + + yield coreExtensionData.title(title); + + if (params.routeRef) { + yield coreExtensionData.routeRef(params.routeRef); + } + }, +}); diff --git a/plugins/devtools-react/src/index.ts b/plugins/devtools-react/src/index.ts new file mode 100644 index 0000000000..70b4915ae8 --- /dev/null +++ b/plugins/devtools-react/src/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Web library for the devtools plugin. + * + * @packageDocumentation + */ +export { + type DevToolsContentBlueprintParams, + DevToolsContentBlueprint, +} from './devToolsContentBlueprint'; diff --git a/plugins/devtools/README.md b/plugins/devtools/README.md index 07374db5da..b23c35d4dd 100644 --- a/plugins/devtools/README.md +++ b/plugins/devtools/README.md @@ -166,6 +166,39 @@ You can also add tabs to show content from other plugins that fit well with the #### Catalog Unprocessed Entities Tab +##### New Frontend System + +Create an extension and/or load a 3rd party extension to add additional tabs. + +```shell +yarn --cwd plugins/ add @backstage/plugin-devtools-react +``` + +```tsx +import { DevToolsContentBlueprint } from '@backstage/plugin-devtools-react'; + +export const unprocessedEntitiesDevToolsContent = DevToolsContentBlueprint.make( + { + disabled: true, + params: { + path: 'unprocessed-entities', + title: 'Unprocessed Entities', + loader: () => + import('../components/UnprocessedEntities').then(m => ( + + )), + }, + }, +); + +const appFeature = createFrontendModule({ + pluginId: 'catalog-unprocessed-entities', + extensions: [unprocessedEntitiesDevToolsContent], +}); +``` + +##### Old System + Here's how to add the Catalog Unprocessed Entities tab: 1. Install and setup the [Catalog Unprocessed Entities plugin](https://github.com/backstage/backstage/tree/master/plugins/catalog-unprocessed-entities) as per its documentation diff --git a/plugins/devtools/package.json b/plugins/devtools/package.json index fb9deadfb9..ef4d9eef3f 100644 --- a/plugins/devtools/package.json +++ b/plugins/devtools/package.json @@ -7,7 +7,8 @@ "pluginPackages": [ "@backstage/plugin-devtools", "@backstage/plugin-devtools-backend", - "@backstage/plugin-devtools-common" + "@backstage/plugin-devtools-common", + "@backstage/plugin-devtools-react" ] }, "publishConfig": { @@ -58,6 +59,7 @@ "@backstage/errors": "workspace:^", "@backstage/frontend-plugin-api": "workspace:^", "@backstage/plugin-devtools-common": "workspace:^", + "@backstage/plugin-devtools-react": "workspace:^", "@backstage/plugin-permission-react": "workspace:^", "@material-ui/core": "^4.9.13", "@material-ui/icons": "^4.9.1", diff --git a/plugins/devtools/report-alpha.api.md b/plugins/devtools/report-alpha.api.md index 9640158e53..3f78a90587 100644 --- a/plugins/devtools/report-alpha.api.md +++ b/plugins/devtools/report-alpha.api.md @@ -6,8 +6,10 @@ import { AnyApiFactory } from '@backstage/frontend-plugin-api'; import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; import { ApiFactory } from '@backstage/frontend-plugin-api'; +import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api'; 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 { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api'; @@ -59,8 +61,6 @@ const _default: OverridableFrontendPlugin< }; }>; 'page:devtools': OverridableExtensionDefinition<{ - kind: 'page'; - name: undefined; config: { path: string | undefined; }; @@ -77,7 +77,26 @@ const _default: OverridableFrontendPlugin< optional: true; } >; - inputs: {}; + inputs: { + contents: ExtensionInput< + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef_2, + 'core.routing.ref', + { + optional: true; + } + >, + { + singleton: false; + optional: true; + } + >; + }; + kind: 'page'; + name: undefined; params: { defaultPath?: [Error: `Use the 'path' param instead`]; path: string; diff --git a/plugins/devtools/report.api.md b/plugins/devtools/report.api.md index 80297f299b..804e678e89 100644 --- a/plugins/devtools/report.api.md +++ b/plugins/devtools/report.api.md @@ -6,6 +6,7 @@ import { BackstagePlugin } from '@backstage/core-plugin-api'; import { ElementType } from 'react'; import { JSX as JSX_2 } from 'react/jsx-runtime'; +import { ReactElement } from 'react'; import { ReactNode } from 'react'; import { RouteRef } from '@backstage/core-plugin-api'; import { TabProps } from '@material-ui/core/Tab'; @@ -28,7 +29,23 @@ export type DevToolsLayoutProps = { }; // @public (undocumented) -export const DevToolsPage: () => JSX_2.Element; +export const DevToolsPage: ({ contents }: DevToolsPageProps) => JSX_2.Element; + +// @public (undocumented) +export interface DevToolsPageContent { + // (undocumented) + children: ReactElement; + // (undocumented) + path: string; + // (undocumented) + title: string; +} + +// @public (undocumented) +export interface DevToolsPageProps { + // (undocumented) + contents?: DevToolsPageContent[]; +} // @public (undocumented) export const devToolsPlugin: BackstagePlugin< diff --git a/plugins/devtools/src/alpha/plugin.tsx b/plugins/devtools/src/alpha/plugin.tsx index 85021e9857..edeaae5723 100644 --- a/plugins/devtools/src/alpha/plugin.tsx +++ b/plugins/devtools/src/alpha/plugin.tsx @@ -21,6 +21,8 @@ import { ApiBlueprint, PageBlueprint, NavItemBlueprint, + createExtensionInput, + coreExtensionData, } from '@backstage/frontend-plugin-api'; import { devToolsApiRef, DevToolsClient } from '../api'; @@ -42,12 +44,35 @@ export const devToolsApi = ApiBlueprint.make({ }); /** @alpha */ -export const devToolsPage = PageBlueprint.make({ - params: { - path: '/devtools', - routeRef: rootRouteRef, - loader: () => - import('../components/DevToolsPage').then(m => ), +export const devToolsPage = PageBlueprint.makeWithOverrides({ + inputs: { + contents: createExtensionInput( + [ + coreExtensionData.reactElement, + coreExtensionData.routePath, + coreExtensionData.routeRef.optional(), + coreExtensionData.title, + ], + { + optional: true, + }, + ), + }, + factory(originalFactory, { inputs }) { + return originalFactory({ + path: '/devtools', + routeRef: rootRouteRef, + loader: () => { + const contents = inputs.contents.map(content => ({ + path: content.get(coreExtensionData.routePath), + title: content.get(coreExtensionData.title), + children: content.get(coreExtensionData.reactElement), + })); + return import('../components/DevToolsPage').then(m => ( + + )); + }, + }); }, }); diff --git a/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx b/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx index d706fe44e7..efbed90737 100644 --- a/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx +++ b/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx @@ -18,15 +18,17 @@ import { devToolsConfigReadPermission, devToolsInfoReadPermission, } from '@backstage/plugin-devtools-common'; + +import { ConfigContent } from '../Content'; import { devToolsTaskSchedulerReadPermission } from '@backstage/plugin-devtools-common/alpha'; -import { ConfigContent } from '../Content/ConfigContent'; import { DevToolsLayout } from '../DevToolsLayout'; -import { InfoContent } from '../Content/InfoContent'; +import { InfoContent } from '../Content'; import { RequirePermission } from '@backstage/plugin-permission-react'; import { ScheduledTasksContent } from '../Content/ScheduledTasksContent'; +import { DevToolsPageProps } from '../DevToolsPage'; /** @public */ -export const DefaultDevToolsPage = () => ( +export const DefaultDevToolsPage = ({ contents }: DevToolsPageProps) => ( @@ -43,5 +45,14 @@ export const DefaultDevToolsPage = () => ( + {contents?.map((content, index) => ( + + {content.children} + + ))} ); diff --git a/plugins/devtools/src/components/DevToolsPage/DevToolsPage.tsx b/plugins/devtools/src/components/DevToolsPage/DevToolsPage.tsx index 07a549f8eb..89305b2c1a 100644 --- a/plugins/devtools/src/components/DevToolsPage/DevToolsPage.tsx +++ b/plugins/devtools/src/components/DevToolsPage/DevToolsPage.tsx @@ -16,9 +16,26 @@ import { useOutlet } from 'react-router-dom'; import { DefaultDevToolsPage } from '../DefaultDevToolsPage'; +import { ReactElement } from 'react'; -export const DevToolsPage = () => { +/** + @public + */ +export interface DevToolsPageProps { + contents?: DevToolsPageContent[]; +} + +/** + @public + */ +export interface DevToolsPageContent { + title: string; + path: string; + children: ReactElement; +} + +export const DevToolsPage = ({ contents }: DevToolsPageProps) => { const outlet = useOutlet(); - return <>{outlet || }; + return <>{outlet || }; }; diff --git a/plugins/devtools/src/components/DevToolsPage/index.ts b/plugins/devtools/src/components/DevToolsPage/index.ts index 88d8bc06fa..a0a4028d10 100644 --- a/plugins/devtools/src/components/DevToolsPage/index.ts +++ b/plugins/devtools/src/components/DevToolsPage/index.ts @@ -14,4 +14,8 @@ * limitations under the License. */ -export { DevToolsPage } from './DevToolsPage'; +export { + DevToolsPage, + type DevToolsPageProps, + type DevToolsPageContent, +} from './DevToolsPage'; diff --git a/plugins/devtools/src/components/index.ts b/plugins/devtools/src/components/index.ts index 9938866bcc..9744a4b801 100644 --- a/plugins/devtools/src/components/index.ts +++ b/plugins/devtools/src/components/index.ts @@ -16,3 +16,7 @@ export * from './Content'; export * from './DevToolsLayout'; +export { + type DevToolsPageProps, + type DevToolsPageContent, +} from './DevToolsPage'; diff --git a/yarn.lock b/yarn.lock index a7e7f41022..e240d6a443 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5444,12 +5444,14 @@ __metadata: resolution: "@backstage/plugin-catalog-unprocessed-entities@workspace:plugins/catalog-unprocessed-entities" dependencies: "@backstage/cli": "workspace:^" + "@backstage/core-compat-api": "workspace:^" "@backstage/core-components": "workspace:^" "@backstage/core-plugin-api": "workspace:^" "@backstage/dev-utils": "workspace:^" "@backstage/errors": "workspace:^" "@backstage/frontend-plugin-api": "workspace:^" "@backstage/plugin-catalog-unprocessed-entities-common": "workspace:^" + "@backstage/plugin-devtools-react": "workspace:^" "@material-ui/core": "npm:^4.9.13" "@material-ui/icons": "npm:^4.9.1" "@material-ui/lab": "npm:^4.0.0-alpha.60" @@ -5611,6 +5613,32 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-devtools-react@workspace:^, @backstage/plugin-devtools-react@workspace:plugins/devtools-react": + version: 0.0.0-use.local + resolution: "@backstage/plugin-devtools-react@workspace:plugins/devtools-react" + dependencies: + "@backstage/cli": "workspace:^" + "@backstage/core-plugin-api": "workspace:^" + "@backstage/frontend-plugin-api": "workspace:^" + "@backstage/test-utils": "workspace:^" + "@material-ui/core": "npm:^4.9.13" + "@testing-library/jest-dom": "npm:^6.0.0" + "@testing-library/react": "npm:^14.0.0" + "@types/react": "npm:^18.0.0" + react: "npm:^18.0.2" + react-dom: "npm:^18.0.2" + react-router-dom: "npm:^6.3.0" + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + react-router-dom: ^6.3.0 + peerDependenciesMeta: + "@types/react": + optional: true + languageName: unknown + linkType: soft + "@backstage/plugin-devtools@workspace:^, @backstage/plugin-devtools@workspace:plugins/devtools": version: 0.0.0-use.local resolution: "@backstage/plugin-devtools@workspace:plugins/devtools" @@ -5623,6 +5651,7 @@ __metadata: "@backstage/errors": "workspace:^" "@backstage/frontend-plugin-api": "workspace:^" "@backstage/plugin-devtools-common": "workspace:^" + "@backstage/plugin-devtools-react": "workspace:^" "@backstage/plugin-permission-react": "workspace:^" "@material-ui/core": "npm:^4.9.13" "@material-ui/icons": "npm:^4.9.1" @@ -20169,6 +20198,22 @@ __metadata: languageName: node linkType: hard +"@testing-library/dom@npm:^9.0.0": + version: 9.3.4 + resolution: "@testing-library/dom@npm:9.3.4" + dependencies: + "@babel/code-frame": "npm:^7.10.4" + "@babel/runtime": "npm:^7.12.5" + "@types/aria-query": "npm:^5.0.1" + aria-query: "npm:5.1.3" + chalk: "npm:^4.1.0" + dom-accessibility-api: "npm:^0.5.9" + lz-string: "npm:^1.5.0" + pretty-format: "npm:^27.0.2" + checksum: 10/510da752ea76f4a10a0a4e3a77917b0302cf03effe576cd3534cab7e796533ee2b0e9fb6fb11b911a1ebd7c70a0bb6f235bf4f816c9b82b95b8fe0cddfd10975 + languageName: node + linkType: hard + "@testing-library/jest-dom@npm:^6.0.0, @testing-library/jest-dom@npm:^6.6.3": version: 6.9.1 resolution: "@testing-library/jest-dom@npm:6.9.1" @@ -20205,6 +20250,20 @@ __metadata: languageName: node linkType: hard +"@testing-library/react@npm:^14.0.0": + version: 14.3.1 + resolution: "@testing-library/react@npm:14.3.1" + dependencies: + "@babel/runtime": "npm:^7.12.5" + "@testing-library/dom": "npm:^9.0.0" + "@types/react-dom": "npm:^18.0.0" + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + checksum: 10/83359dcdf9eaf067839f34604e1a181cbc14fc09f3a07672403700fcc6a900c4b8054ad1114fc24b4b9f89d84e2a09e1b7c9afce2306b1d4b4c9e30eb1cb12de + languageName: node + linkType: hard + "@testing-library/react@npm:^16.0.0": version: 16.3.1 resolution: "@testing-library/react@npm:16.3.1" @@ -24713,6 +24772,15 @@ __metadata: languageName: node linkType: hard +"aria-query@npm:5.1.3": + version: 5.1.3 + resolution: "aria-query@npm:5.1.3" + dependencies: + deep-equal: "npm:^2.0.5" + checksum: 10/e5da608a7c4954bfece2d879342b6c218b6b207e2d9e5af270b5e38ef8418f02d122afdc948b68e32649b849a38377785252059090d66fa8081da95d1609c0d2 + languageName: node + linkType: hard + "aria-query@npm:5.3.0": version: 5.3.0 resolution: "aria-query@npm:5.3.0" @@ -24729,7 +24797,7 @@ __metadata: languageName: node linkType: hard -"array-buffer-byte-length@npm:^1.0.1, array-buffer-byte-length@npm:^1.0.2": +"array-buffer-byte-length@npm:^1.0.0, array-buffer-byte-length@npm:^1.0.1, array-buffer-byte-length@npm:^1.0.2": version: 1.0.2 resolution: "array-buffer-byte-length@npm:1.0.2" dependencies: @@ -25013,6 +25081,20 @@ __metadata: languageName: node linkType: hard +"async-function@npm:^1.0.0": + version: 1.0.0 + resolution: "async-function@npm:1.0.0" + checksum: 10/1a09379937d846f0ce7614e75071c12826945d4e417db634156bf0e4673c495989302f52186dfa9767a1d9181794554717badd193ca2bbab046ef1da741d8efd + languageName: node + linkType: hard + +"async-generator-function@npm:^1.0.0": + version: 1.0.0 + resolution: "async-generator-function@npm:1.0.0" + checksum: 10/3d49e7acbeee9e84537f4cb0e0f91893df8eba976759875ae8ee9e3d3c82f6ecdebdb347c2fad9926b92596d93cdfc78ecc988bcdf407e40433e8e8e6fe5d78e + languageName: node + linkType: hard + "async-lock@npm:^1.4.1": version: 1.4.1 resolution: "async-lock@npm:1.4.1" @@ -28491,6 +28573,32 @@ __metadata: languageName: node linkType: hard +"deep-equal@npm:^2.0.5": + version: 2.2.3 + resolution: "deep-equal@npm:2.2.3" + dependencies: + array-buffer-byte-length: "npm:^1.0.0" + call-bind: "npm:^1.0.5" + es-get-iterator: "npm:^1.1.3" + get-intrinsic: "npm:^1.2.2" + is-arguments: "npm:^1.1.1" + is-array-buffer: "npm:^3.0.2" + is-date-object: "npm:^1.0.5" + is-regex: "npm:^1.1.4" + is-shared-array-buffer: "npm:^1.0.2" + isarray: "npm:^2.0.5" + object-is: "npm:^1.1.5" + object-keys: "npm:^1.1.1" + object.assign: "npm:^4.1.4" + regexp.prototype.flags: "npm:^1.5.1" + side-channel: "npm:^1.0.4" + which-boxed-primitive: "npm:^1.0.2" + which-collection: "npm:^1.0.1" + which-typed-array: "npm:^1.1.13" + checksum: 10/1ce49d0b71d0f14d8ef991a742665eccd488dfc9b3cada069d4d7a86291e591c92d2589c832811dea182b4015736b210acaaebce6184be356c1060d176f5a05f + languageName: node + linkType: hard + "deep-equal@npm:~1.0.1": version: 1.0.1 resolution: "deep-equal@npm:1.0.1" @@ -29686,7 +29794,7 @@ __metadata: languageName: node linkType: hard -"es-get-iterator@npm:^1.0.2": +"es-get-iterator@npm:^1.0.2, es-get-iterator@npm:^1.1.3": version: 1.1.3 resolution: "es-get-iterator@npm:1.1.3" dependencies: @@ -30700,6 +30808,7 @@ __metadata: "@backstage/plugin-catalog-import": "workspace:^" "@backstage/plugin-catalog-react": "workspace:^" "@backstage/plugin-catalog-unprocessed-entities": "workspace:^" + "@backstage/plugin-devtools": "workspace:^" "@backstage/plugin-home": "workspace:^" "@backstage/plugin-kubernetes": "workspace:^" "@backstage/plugin-kubernetes-cluster": "workspace:^" @@ -32256,6 +32365,13 @@ __metadata: languageName: node linkType: hard +"generator-function@npm:^2.0.0": + version: 2.0.1 + resolution: "generator-function@npm:2.0.1" + checksum: 10/eb7e7eb896c5433f3d40982b2ccacdb3dd990dd3499f14040e002b5d54572476513be8a2e6f9609f6e41ab29f2c4469307611ddbfc37ff4e46b765c326663805 + languageName: node + linkType: hard + "generic-names@npm:^2.0.1": version: 2.0.1 resolution: "generic-names@npm:2.0.1" @@ -32303,21 +32419,24 @@ __metadata: languageName: node linkType: hard -"get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6, get-intrinsic@npm:^1.3.0": - version: 1.3.0 - resolution: "get-intrinsic@npm:1.3.0" +"get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.2, get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6, get-intrinsic@npm:^1.3.0": + version: 1.3.1 + resolution: "get-intrinsic@npm:1.3.1" dependencies: + async-function: "npm:^1.0.0" + async-generator-function: "npm:^1.0.0" call-bind-apply-helpers: "npm:^1.0.2" es-define-property: "npm:^1.0.1" es-errors: "npm:^1.3.0" es-object-atoms: "npm:^1.1.1" function-bind: "npm:^1.1.2" + generator-function: "npm:^2.0.0" get-proto: "npm:^1.0.1" gopd: "npm:^1.2.0" has-symbols: "npm:^1.1.0" hasown: "npm:^2.0.2" math-intrinsics: "npm:^1.1.0" - checksum: 10/6e9dd920ff054147b6f44cb98104330e87caafae051b6d37b13384a45ba15e71af33c3baeac7cb630a0aaa23142718dcf25b45cfdd86c184c5dcb4e56d953a10 + checksum: 10/bb579dda84caa4a3a41611bdd483dade7f00f246f2a7992eb143c5861155290df3fdb48a8406efa3dfb0b434e2c8fafa4eebd469e409d0439247f85fc3fa2cc1 languageName: node linkType: hard @@ -34407,7 +34526,7 @@ __metadata: languageName: node linkType: hard -"is-array-buffer@npm:^3.0.4, is-array-buffer@npm:^3.0.5": +"is-array-buffer@npm:^3.0.2, is-array-buffer@npm:^3.0.4, is-array-buffer@npm:^3.0.5": version: 3.0.5 resolution: "is-array-buffer@npm:3.0.5" dependencies: @@ -34880,7 +34999,7 @@ __metadata: languageName: node linkType: hard -"is-regex@npm:^1.2.1": +"is-regex@npm:^1.1.4, is-regex@npm:^1.2.1": version: 1.2.1 resolution: "is-regex@npm:1.2.1" dependencies: @@ -34929,7 +35048,7 @@ __metadata: languageName: node linkType: hard -"is-shared-array-buffer@npm:^1.0.4": +"is-shared-array-buffer@npm:^1.0.2, is-shared-array-buffer@npm:^1.0.4": version: 1.0.4 resolution: "is-shared-array-buffer@npm:1.0.4" dependencies: @@ -44711,7 +44830,7 @@ __metadata: languageName: node linkType: hard -"regexp.prototype.flags@npm:^1.5.3, regexp.prototype.flags@npm:^1.5.4": +"regexp.prototype.flags@npm:^1.5.1, regexp.prototype.flags@npm:^1.5.3, regexp.prototype.flags@npm:^1.5.4": version: 1.5.4 resolution: "regexp.prototype.flags@npm:1.5.4" dependencies: @@ -46279,7 +46398,7 @@ __metadata: languageName: node linkType: hard -"side-channel@npm:^1.1.0": +"side-channel@npm:^1.0.4, side-channel@npm:^1.1.0": version: 1.1.0 resolution: "side-channel@npm:1.1.0" dependencies: @@ -50503,7 +50622,7 @@ __metadata: languageName: node linkType: hard -"which-boxed-primitive@npm:^1.1.0, which-boxed-primitive@npm:^1.1.1": +"which-boxed-primitive@npm:^1.0.2, which-boxed-primitive@npm:^1.1.0, which-boxed-primitive@npm:^1.1.1": version: 1.1.1 resolution: "which-boxed-primitive@npm:1.1.1" dependencies: @@ -50537,7 +50656,7 @@ __metadata: languageName: node linkType: hard -"which-collection@npm:^1.0.2": +"which-collection@npm:^1.0.1, which-collection@npm:^1.0.2": version: 1.0.2 resolution: "which-collection@npm:1.0.2" dependencies: @@ -50559,7 +50678,7 @@ __metadata: languageName: node linkType: hard -"which-typed-array@npm:^1.1.16, which-typed-array@npm:^1.1.19, which-typed-array@npm:^1.1.2": +"which-typed-array@npm:^1.1.13, which-typed-array@npm:^1.1.16, which-typed-array@npm:^1.1.19, which-typed-array@npm:^1.1.2": version: 1.1.19 resolution: "which-typed-array@npm:1.1.19" dependencies: