From d9895fbd6211c74ed8efe259df4c93ccbf91511e Mon Sep 17 00:00:00 2001 From: Mark Dunphy Date: Tue, 11 Mar 2025 11:14:22 -0400 Subject: [PATCH] add support for dialog api Signed-off-by: Mark Dunphy --- .../EntityContextMenuItemBlueprint.test.tsx | 76 +++++++++++++++++++ .../EntityContextMenuItemBlueprint.tsx | 31 +++++++- 2 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.test.tsx diff --git a/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.test.tsx b/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.test.tsx new file mode 100644 index 0000000000..466256f25b --- /dev/null +++ b/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.test.tsx @@ -0,0 +1,76 @@ +/* + * 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. + */ + +import React from 'react'; +import { EntityContextMenuItemBlueprint } from './EntityContextMenuItemBlueprint'; +import { createRouteRef, useRouteRef } from '@backstage/frontend-plugin-api'; + +describe('EntityContextMenuItemBlueprint', () => { + const routeRef = createRouteRef(); + const data = [ + { + loader: async () =>
  • Test!
  • , + }, + { + title: 'Test', + href: '/somewhere', + icon: Test, + }, + { + title: 'Test', + useHref() { + const r = useRouteRef(routeRef) ?? (() => '/somewhere'); + return r(); + }, + icon: Test, + }, + { + title: 'TestDialog', + dialogLoader: async () => () =>
    test dialog
    , + icon: Test, + }, + ]; + + it.each(data)('should return an extension with sane defaults', params => { + const extension = EntityContextMenuItemBlueprint.make({ + name: 'test', + params, + }); + + expect(extension).toMatchInlineSnapshot(` + { + "$$type": "@backstage/ExtensionDefinition", + "T": undefined, + "attachTo": { + "id": "page:catalog/entity", + "input": "extraContextMenuItems", + }, + "configSchema": undefined, + "disabled": false, + "factory": [Function], + "inputs": {}, + "kind": "entity-context-menu-item", + "name": "test", + "output": [ + [Function], + ], + "override": [Function], + "toString": [Function], + "version": "v2", + } + `); + }); +}); diff --git a/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.tsx b/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.tsx index f2c7657927..bfb3eb17c7 100644 --- a/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.tsx +++ b/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.tsx @@ -16,9 +16,11 @@ import React from 'react'; import { + DialogApiDialog, ExtensionBoundary, coreExtensionData, createExtensionBlueprint, + dialogApiRef, } from '@backstage/frontend-plugin-api'; import MenuItem from '@material-ui/core/MenuItem'; import ListItemIcon from '@material-ui/core/ListItemIcon'; @@ -42,17 +44,44 @@ export type FactoryHrefParams = href: string; }; +/** @alpha */ +export type FactoryDialogParams = { + dialogLoader: () => Promise< + ({ dialog }: { dialog: DialogApiDialog }) => JSX.Element + >; + title: string; + icon: JSX.Element; +}; + +export type EntityContextMenuItemParams = + | FactoryLoaderParams + | FactoryHrefParams + | FactoryDialogParams; + /** @alpha */ export const EntityContextMenuItemBlueprint = createExtensionBlueprint({ kind: 'entity-context-menu-item', attachTo: { id: 'page:catalog/entity', input: 'extraContextMenuItems' }, output: [coreExtensionData.reactElement], - *factory(params: FactoryLoaderParams | FactoryHrefParams, { node }) { + *factory(params: EntityContextMenuItemParams, { node, apis }) { const loaderFactory = () => { if ('loader' in params) { return params.loader; } + if ('dialogLoader' in params) { + const dialogApi = apis.get(dialogApiRef); + return async () => { + const Dialog = await params.dialogLoader(); + return ( + dialogApi?.show(Dialog)}> + {params.icon} + + + ); + }; + } + const useHref = 'useHref' in params ? params.useHref : () => params.href; return async () => {