diff --git a/plugins/catalog-react/api-report.md b/plugins/catalog-react/api-report.md
index 9d30d84bb2..82f8326a17 100644
--- a/plugins/catalog-react/api-report.md
+++ b/plugins/catalog-react/api-report.md
@@ -255,14 +255,13 @@ export class EntityOwnerFilter implements EntityFilter {
export const EntityOwnerPicker: () => JSX.Element | null;
// @public
-export const EntityPeekAheadPopover: ({
- entityRef,
- children,
-}: EntityPeekAheadPopoverProps) => JSX.Element;
+export const EntityPeekAheadPopover: (
+ props: EntityPeekAheadPopoverProps,
+) => JSX.Element;
// @public
export type EntityPeekAheadPopoverProps = PropsWithChildren<{
- entityRef: CompoundEntityRef;
+ entityRef: string;
}>;
// @public (undocumented)
diff --git a/plugins/catalog-react/src/components/EntityPeekAheadPopover/EntityPeekAheadPopover.stories.tsx b/plugins/catalog-react/src/components/EntityPeekAheadPopover/EntityPeekAheadPopover.stories.tsx
index c31e5320ef..c44a31a743 100644
--- a/plugins/catalog-react/src/components/EntityPeekAheadPopover/EntityPeekAheadPopover.stories.tsx
+++ b/plugins/catalog-react/src/components/EntityPeekAheadPopover/EntityPeekAheadPopover.stories.tsx
@@ -60,16 +60,27 @@ const mockCatalogApi = {
},
};
}
+ if (
+ entityRef.namespace === 'default' &&
+ entityRef.name === 'slow.catalog.item' &&
+ entityRef.kind === 'component'
+ ) {
+ await new Promise(resolve => setTimeout(resolve, 3000));
+ return {
+ kind: 'Component',
+ metadata: {
+ name: 'slow.catalog.item',
+ namespace: 'default',
+ description: 'Details about the slow.catalog.item service',
+ },
+ };
+ }
return undefined;
},
};
const defaultArgs = {
- entityRef: {
- namespace: 'default',
- name: 'playback',
- kind: 'component',
- },
+ entityRef: 'component:default/playback',
};
export default {
@@ -106,9 +117,23 @@ export const User = (args: EntityPeekAheadPopoverProps) => (
);
User.args = {
- entityRef: {
- kind: 'user',
- namespace: 'default',
- name: 'fname.lname',
- },
+ entityRef: 'user:default/fname.lname',
+};
+
+export const NotFound = (args: EntityPeekAheadPopoverProps) => (
+
+
+
+);
+NotFound.args = {
+ entityRef: 'user:default/doesnt.exist',
+};
+
+export const SlowCatalogItem = (args: EntityPeekAheadPopoverProps) => (
+
+
+
+);
+SlowCatalogItem.args = {
+ entityRef: 'component:default/slow.catalog.item',
};
diff --git a/plugins/catalog-react/src/components/EntityPeekAheadPopover/EntityPeekAheadPopover.test.tsx b/plugins/catalog-react/src/components/EntityPeekAheadPopover/EntityPeekAheadPopover.test.tsx
index 7053969a09..f7c4d1cffa 100644
--- a/plugins/catalog-react/src/components/EntityPeekAheadPopover/EntityPeekAheadPopover.test.tsx
+++ b/plugins/catalog-react/src/components/EntityPeekAheadPopover/EntityPeekAheadPopover.test.tsx
@@ -14,22 +14,25 @@
* limitations under the License.
*/
-import { fireEvent, render, screen } from '@testing-library/react';
+import { fireEvent, screen } from '@testing-library/react';
import React from 'react';
import { EntityPeekAheadPopover } from './EntityPeekAheadPopover';
import { ApiProvider } from '@backstage/core-app-api';
-import { TestApiRegistry } from '@backstage/test-utils';
+import { TestApiRegistry, renderInTestApp } from '@backstage/test-utils';
import { catalogApiRef } from '../../api';
import { CompoundEntityRef, Entity } from '@backstage/catalog-model';
import { CatalogApi } from '@backstage/catalog-client';
+import { Button } from '@material-ui/core';
+import { entityRouteRef } from '../../routes';
const catalogApi: Partial = {
getEntityByRef: async (
entityRef: CompoundEntityRef,
): Promise => {
if (
- entityRef ===
- { name: 'service1', namespace: 'default', kind: 'component ' }
+ entityRef.name === 'service1' &&
+ entityRef.namespace === 'default' &&
+ entityRef.kind === 'component'
) {
return {
apiVersion: '',
@@ -51,22 +54,31 @@ const apis = TestApiRegistry.from([catalogApiRef, catalogApi]);
describe('', () => {
it('renders all owners', async () => {
- render(
+ renderInTestApp(
-
- asdf
+
+
+
+
+
,
+ {
+ mountedRoutes: {
+ '/catalog/:namespace/:kind/:name': entityRouteRef,
+ },
+ },
);
- expect(screen.getByText('asdf')).toBeInTheDocument();
+ expect(screen.getByText('s1')).toBeInTheDocument();
expect(screen.queryByText('service1')).toBeNull();
- fireEvent.mouseOver(screen.getByTestId('popover'));
- expect(screen.getByText('service1')).toBeInTheDocument();
+ fireEvent.mouseOver(screen.getByTestId('popover1'));
+ expect(await screen.findByText('service1')).toBeInTheDocument();
+
+ expect(screen.getByText('s2')).toBeInTheDocument();
+ expect(screen.queryByText('service2')).toBeNull();
+ fireEvent.mouseOver(screen.getByTestId('popover2'));
+ expect(
+ await screen.findByText(/service2 was not found/),
+ ).toBeInTheDocument();
});
});
diff --git a/plugins/catalog-react/src/components/EntityPeekAheadPopover/EntityPeekAheadPopover.tsx b/plugins/catalog-react/src/components/EntityPeekAheadPopover/EntityPeekAheadPopover.tsx
index bd41dd5b92..6e5b75ed58 100644
--- a/plugins/catalog-react/src/components/EntityPeekAheadPopover/EntityPeekAheadPopover.tsx
+++ b/plugins/catalog-react/src/components/EntityPeekAheadPopover/EntityPeekAheadPopover.tsx
@@ -34,17 +34,17 @@ import {
Tooltip,
Typography,
} from '@material-ui/core';
-import { Alert, Skeleton } from '@material-ui/lab';
+import { Alert } from '@material-ui/lab';
import EmailIcon from '@material-ui/icons/Email';
import InfoIcon from '@material-ui/icons/Info';
import { useApiHolder, useRouteRef } from '@backstage/core-plugin-api';
import {
- CompoundEntityRef,
isUserEntity,
isGroupEntity,
UserEntity,
GroupEntity,
Entity,
+ parseEntityRef,
} from '@backstage/catalog-model';
import { Link, Progress } from '@backstage/core-components';
@@ -54,11 +54,14 @@ import { Link, Progress } from '@backstage/core-components';
* @public
*/
export type EntityPeekAheadPopoverProps = PropsWithChildren<{
- entityRef: CompoundEntityRef;
+ entityRef: string;
}>;
const useStyles = makeStyles(() => {
return {
+ trigger: {
+ display: 'inline-block',
+ },
popoverPaper: {
width: '30em',
},
@@ -130,33 +133,53 @@ const EntityCardActions = ({ entity }: { entity: Entity }) => {
);
};
+const EntityNotFoundCard = ({
+ entityRef,
+ error,
+}: {
+ entityRef: string;
+ error?: Error;
+}) => {
+ return (
+
+
+
+ {entityRef} was not found {error?.message}
+
+
+
+ );
+};
+
/**
* Shows an entity popover on hover of a component.
*
* @public
*/
-export const EntityPeekAheadPopover = ({
- entityRef,
- children,
-}: EntityPeekAheadPopoverProps) => {
+export const EntityPeekAheadPopover = (props: EntityPeekAheadPopoverProps) => {
+ const { entityRef, children } = props;
+
const classes = useStyles();
const apiHolder = useApiHolder();
const popupState = usePopupState({
variant: 'popover',
popupId: 'entity-peek-ahead',
});
+ const compoundEntityRef = parseEntityRef(entityRef);
const [{ loading, error, value: entity }, load] = useAsyncFn(async () => {
const catalogApi = apiHolder.get(catalogApiRef);
if (catalogApi) {
- const retrievedEntity = await catalogApi.getEntityByRef(entityRef);
+ const retrievedEntity = await catalogApi.getEntityByRef(
+ compoundEntityRef,
+ );
if (!retrievedEntity) {
- throw new Error(`${entityRef.name} was not found`);
+ throw new Error(`${compoundEntityRef.name} was not found`);
}
return retrievedEntity;
}
return undefined;
- }, [apiHolder, entityRef]);
+ }, [apiHolder, compoundEntityRef]);
useEffect(() => {
if (popupState.isOpen && !entity && !error && !loading) {
@@ -166,9 +189,9 @@ export const EntityPeekAheadPopover = ({
return (
<>
-
+
{children}
-
+
-
+ <>
{loading && }
-
- {entityRef.namespace}
-
- {entityRef.name}
-
- {error && {error.message}}
- {entity ? (
- <>
+ {!entity && !loading && (
+
+ )}
+ {entity && (
+
+
+
+ {compoundEntityRef.namespace}
+
+
+ {compoundEntityRef.name}
+
{entity.kind}
{entity.metadata.description}
@@ -202,35 +229,28 @@ export const EntityPeekAheadPopover = ({
{(entity.metadata.tags || [])
.slice(0, maxTagChips)
.map(tag => {
- return ;
+ return ;
})}
{entity.metadata.tags?.length &&
entity.metadata.tags?.length > maxTagChips && (
-
+
)}
- >
- ) : (
- <>
-
-
-
-
- >
- )}
-
-
- {entity && (
- <>
- {isUserEntity(entity) && }
- {isGroupEntity(entity) && }
-
- >
- )}
-
-
+
+
+ <>
+ {isUserEntity(entity) && }
+ {isGroupEntity(entity) && (
+
+ )}
+
+ >
+
+
+ )}
+ >
>
);
diff --git a/plugins/catalog-react/src/components/EntityRefLink/EntityRefLink.tsx b/plugins/catalog-react/src/components/EntityRefLink/EntityRefLink.tsx
index 813195cb70..de94866bb6 100644
--- a/plugins/catalog-react/src/components/EntityRefLink/EntityRefLink.tsx
+++ b/plugins/catalog-react/src/components/EntityRefLink/EntityRefLink.tsx
@@ -77,7 +77,7 @@ export const EntityRefLink = forwardRef(
);
return (
-
+
{children}
{!children && (title ?? formattedEntityRefTitle)}