add entity peek ahead component

Signed-off-by: Brian Fletcher <brian@roadie.io>
This commit is contained in:
Brian Fletcher
2022-12-05 09:10:58 +00:00
parent 8512673a8e
commit 248ed0f7d8
2 changed files with 149 additions and 11 deletions
+1
View File
@@ -48,6 +48,7 @@
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.57",
"material-ui-popup-state": "^1.9.3",
"classnames": "^2.2.6",
"jwt-decode": "^3.1.0",
"lodash": "^4.17.21",
@@ -19,13 +19,34 @@ import {
CompoundEntityRef,
DEFAULT_NAMESPACE,
parseEntityRef,
isUserEntity,
isGroupEntity,
} from '@backstage/catalog-model';
import React, { forwardRef } from 'react';
import React, { ForwardedRef, forwardRef } from 'react';
import { entityRouteRef } from '../../routes';
import { humanizeEntityRef } from './humanize';
import { Link, LinkProps } from '@backstage/core-components';
import { useRouteRef } from '@backstage/core-plugin-api';
import { Tooltip } from '@material-ui/core';
import { useApi, useRouteRef } from '@backstage/core-plugin-api';
import {
Button,
Tooltip,
Typography,
CardContent,
Card,
CardActions,
makeStyles,
} from '@material-ui/core';
import {
usePopupState,
bindPopover,
bindHover,
PopupState,
} from 'material-ui-popup-state/hooks';
import HoverPopover from 'material-ui-popup-state/HoverPopover';
import EmailIcon from '@material-ui/icons/Email';
import InfoIcon from '@material-ui/icons/Info';
import useAsync from 'react-use/lib/useAsync';
import { catalogApiRef } from '../../api';
/**
* Props for {@link EntityRefLink}.
@@ -39,6 +60,101 @@ export type EntityRefLinkProps = {
children?: React.ReactNode;
} & Omit<LinkProps, 'to'>;
type PeekAheadPopoverProps = {
popupState: PopupState;
entityRef: CompoundEntityRef;
ref: ForwardedRef<any>;
};
const useStyles = makeStyles(() => {
return {
popover: {
width: '80em',
minWidth: '80em',
maxWidth: '80em',
},
card: {
width: '100%',
},
};
});
export const PeekAheadPopover = ({
popupState,
entityRef,
ref,
}: PeekAheadPopoverProps) => {
const catalogApi = useApi(catalogApiRef);
const entityRoute = useRouteRef(entityRouteRef);
const classes = useStyles();
const { value, loading, error } = useAsync(async () => {
if (popupState.isOpen) {
return catalogApi.getEntityByRef(entityRef);
}
return undefined;
}, [popupState]);
if (loading) {
return null;
}
return (
<HoverPopover
{...bindPopover(popupState)}
className={classes.popover}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'center',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'center',
}}
>
<Card className={classes.card}>
<CardContent>
<Typography gutterBottom>{entityRef.namespace}</Typography>
<Typography variant="h5" component="div">
{entityRef.name}
</Typography>
<Typography>{entityRef.kind}</Typography>
<Typography variant="body2">
{error && error.message}
{value && (
<>
{value.metadata.description}
<br />
<br />
{value.spec?.type}
</>
)}
</Typography>
</CardContent>
<CardActions>
{value &&
(isUserEntity(value) || isGroupEntity(value)) &&
value.spec.profile?.email && (
<Tooltip title={`Email ${value.spec.profile.email}`}>
<Button
target="_blank"
href={`mailto:${value.spec.profile.email}`}
size="small"
>
<EmailIcon color="action" />
</Button>
</Tooltip>
)}
<Tooltip title="Show details">
<Link component="button" to={entityRoute(entityRef)}>
<InfoIcon color="action" />
</Link>
</Tooltip>
</CardActions>
</Card>
</HoverPopover>
);
};
/**
* Shows a clickable link to an entity.
*
@@ -48,6 +164,10 @@ export const EntityRefLink = forwardRef<any, EntityRefLinkProps>(
(props, ref) => {
const { entityRef, defaultKind, title, children, ...linkProps } = props;
const entityRoute = useRouteRef(entityRouteRef);
const popupState = usePopupState({
variant: 'popover',
popupId: 'entity-peek-ahead',
});
let kind;
let namespace;
@@ -78,16 +198,33 @@ export const EntityRefLink = forwardRef<any, EntityRefLinkProps>(
);
const link = (
<Link {...linkProps} ref={ref} to={entityRoute(routeParams)}>
{children}
{!children && (title ?? formattedEntityRefTitle)}
</Link>
<>
<Link
{...bindHover(popupState)}
{...linkProps}
ref={ref}
to={entityRoute(routeParams)}
>
{children}
{!children && (title ?? formattedEntityRefTitle)}
</Link>
</>
);
return title ? (
<Tooltip title={formattedEntityRefTitle}>{link}</Tooltip>
) : (
link
return (
<>
{title ? (
<Tooltip title={formattedEntityRefTitle}>{link}</Tooltip>
) : (
link
)}
<PeekAheadPopover
ref={ref}
popupState={popupState}
entityRef={{ kind, namespace, name }}
/>
</>
);
},
) as (props: EntityRefLinkProps) => JSX.Element;