make context menu items first class

Signed-off-by: Mark Dunphy <markd@spotify.com>
This commit is contained in:
Mark Dunphy
2025-03-21 14:58:02 -04:00
parent bf5e50d638
commit b12ae854b9
6 changed files with 57 additions and 50 deletions
@@ -17,10 +17,8 @@
import React from 'react';
import {
createExtensionBlueprint,
ApiHolder,
createExtensionDataRef,
dialogApiRef,
useApiHolder,
ExtensionBoundary,
} from '@backstage/frontend-plugin-api';
import MenuItem from '@material-ui/core/MenuItem';
import ListItemIcon from '@material-ui/core/ListItemIcon';
@@ -69,11 +67,11 @@ export const EntityContextMenuItemBlueprint = createExtensionBlueprint({
kind: 'entity-context-menu-item',
attachTo: { id: 'page:catalog/entity', input: 'contextMenuItems' },
output: [contextMenuItemComponentDataRef],
*factory(params: EntityContextMenuItemParams, { apis }) {
const loaderFactory = (): ContextMenuItemComponent => {
*factory(params: EntityContextMenuItemParams, { node }) {
const loader = async (): Promise<ContextMenuItemComponent> => {
if ('useOnClick' in params) {
return ({ onClose }) => {
const onClick = params.useOnClick({ apis });
const onClick = params.useOnClick();
const title = params.useTitle();
return (
@@ -105,6 +103,8 @@ export const EntityContextMenuItemBlueprint = createExtensionBlueprint({
};
};
yield contextMenuItemComponentDataRef(loaderFactory());
yield contextMenuItemComponentDataRef(
ExtensionBoundary.lazyComponent(node, loader),
);
},
});
@@ -179,7 +179,7 @@ export function EntityHeader(props: {
UNSTABLE_contextMenuOptions?: {
disableUnregister: boolean | 'visible' | 'hidden' | 'disable';
};
extraMenuItems?: ContextMenuItemComponent[];
contextMenuItems?: ContextMenuItemComponent[];
/**
* An array of relation types used to determine the parent entities in the hierarchy.
* These relations are prioritized in the order provided, allowing for flexible
@@ -197,7 +197,7 @@ export function EntityHeader(props: {
const {
UNSTABLE_extraContextMenuItems,
UNSTABLE_contextMenuOptions,
extraMenuItems,
contextMenuItems,
parentEntityRelations,
title,
subtitle,
@@ -284,7 +284,7 @@ export function EntityHeader(props: {
<EntityContextMenu
UNSTABLE_extraContextMenuItems={UNSTABLE_extraContextMenuItems}
UNSTABLE_contextMenuOptions={UNSTABLE_contextMenuOptions}
extraMenuItems={extraMenuItems}
contextMenuItems={contextMenuItems}
onInspectEntity={openInspectEntityDialog}
onUnregisterEntity={openUnregisterEntityDialog}
/>
@@ -62,7 +62,7 @@ export interface EntityLayoutProps {
UNSTABLE_extraContextMenuItems?: ComponentProps<
typeof EntityHeader
>['UNSTABLE_extraContextMenuItems'];
extraMenuItems?: ComponentProps<typeof EntityHeader>['extraMenuItems'];
contextMenuItems?: ComponentProps<typeof EntityHeader>['contextMenuItems'];
children?: ReactNode;
header?: JSX.Element;
NotFoundComponent?: ReactNode;
@@ -100,7 +100,7 @@ export const EntityLayout = (props: EntityLayoutProps) => {
const {
UNSTABLE_extraContextMenuItems,
UNSTABLE_contextMenuOptions,
extraMenuItems,
contextMenuItems,
children,
header,
NotFoundComponent,
@@ -147,7 +147,7 @@ export const EntityLayout = (props: EntityLayoutProps) => {
parentEntityRelations={parentEntityRelations}
UNSTABLE_contextMenuOptions={UNSTABLE_contextMenuOptions}
UNSTABLE_extraContextMenuItems={UNSTABLE_extraContextMenuItems}
extraMenuItems={extraMenuItems}
contextMenuItems={contextMenuItems}
/>
)}
+2 -2
View File
@@ -101,7 +101,7 @@ export const catalogEntityPage = PageBlueprint.makeWithOverrides({
const header = inputs.header?.get(
EntityHeaderBlueprint.dataRefs.element,
) ?? <EntityHeader extraMenuItems={menuItems} />;
) ?? <EntityHeader contextMenuItems={menuItems} />;
let groups = Object.entries(defaultEntityContentGroups).reduce<Groups>(
(rest, group) => {
@@ -140,7 +140,7 @@ export const catalogEntityPage = PageBlueprint.makeWithOverrides({
const Component = () => {
return (
<AsyncEntityProvider {...useEntityFromUrl()}>
<EntityLayout header={header} extraMenuItems={menuItems}>
<EntityLayout header={header} contextMenuItems={menuItems}>
{Object.values(groups).flatMap(({ title, items }) =>
items.map(output => (
<EntityLayout.Route
@@ -62,7 +62,7 @@ interface ExtraContextMenuItem {
interface EntityContextMenuProps {
UNSTABLE_extraContextMenuItems?: ExtraContextMenuItem[];
UNSTABLE_contextMenuOptions?: UnregisterEntityOptions;
extraMenuItems?: ContextMenuItemComponent[];
contextMenuItems?: ContextMenuItemComponent[];
onUnregisterEntity: () => void;
onInspectEntity: () => void;
}
@@ -71,7 +71,7 @@ export function EntityContextMenu(props: EntityContextMenuProps) {
const {
UNSTABLE_extraContextMenuItems,
UNSTABLE_contextMenuOptions,
extraMenuItems,
contextMenuItems,
onUnregisterEntity,
onInspectEntity,
} = props;
@@ -148,37 +148,46 @@ export function EntityContextMenu(props: EntityContextMenuProps) {
>
<MenuList autoFocusItem={Boolean(anchorEl)}>
{extraItems}
{extraMenuItems?.map(ExtraMenuItem => (
<ExtraMenuItem onClose={onClose} />
))}
<UnregisterEntity
unregisterEntityOptions={UNSTABLE_contextMenuOptions}
isUnregisterAllowed={isAllowed}
onUnregisterEntity={onUnregisterEntity}
onClose={onClose}
/>
<MenuItem
onClick={() => {
onClose();
onInspectEntity();
}}
>
<ListItemIcon>
<BugReportIcon fontSize="small" />
</ListItemIcon>
<ListItemText primary={t('entityContextMenu.inspectMenuTitle')} />
</MenuItem>
<MenuItem
onClick={() => {
onClose();
copyToClipboard(window.location.toString());
}}
>
<ListItemIcon>
<FileCopyTwoToneIcon fontSize="small" />
</ListItemIcon>
<ListItemText primary={t('entityContextMenu.copyURLMenuTitle')} />
</MenuItem>
{contextMenuItems === undefined ? (
<>
<UnregisterEntity
unregisterEntityOptions={UNSTABLE_contextMenuOptions}
isUnregisterAllowed={isAllowed}
onUnregisterEntity={onUnregisterEntity}
onClose={onClose}
/>
<MenuItem
onClick={() => {
onClose();
onInspectEntity();
}}
>
<ListItemIcon>
<BugReportIcon fontSize="small" />
</ListItemIcon>
<ListItemText
primary={t('entityContextMenu.inspectMenuTitle')}
/>
</MenuItem>
<MenuItem
onClick={() => {
onClose();
copyToClipboard(window.location.toString());
}}
>
<ListItemIcon>
<FileCopyTwoToneIcon fontSize="small" />
</ListItemIcon>
<ListItemText
primary={t('entityContextMenu.copyURLMenuTitle')}
/>
</MenuItem>
</>
) : (
contextMenuItems.map(ExtraMenuItem => (
<ExtraMenuItem onClose={onClose} />
))
)}
</MenuList>
</Popover>
</>
@@ -246,7 +246,6 @@ export const EntityLayout = (props: EntityLayoutProps) => {
const {
UNSTABLE_extraContextMenuItems,
UNSTABLE_contextMenuOptions,
extraMenuItems,
children,
NotFoundComponent,
parentEntityRelations,
@@ -362,7 +361,6 @@ export const EntityLayout = (props: EntityLayoutProps) => {
<EntityContextMenu
UNSTABLE_extraContextMenuItems={UNSTABLE_extraContextMenuItems}
UNSTABLE_contextMenuOptions={UNSTABLE_contextMenuOptions}
extraMenuItems={extraMenuItems}
onUnregisterEntity={() => setConfirmationDialogOpen(true)}
onInspectEntity={() => setSearchParams('inspect')}
/>