remove entity ref link popover
This also: - adds stories for the entity ref links componet - and moves some of the components into another file Signed-off-by: Brian Fletcher <brian@roadie.io>
This commit is contained in:
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2022 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 { Button, Tooltip } from '@material-ui/core';
|
||||
import EmailIcon from '@material-ui/icons/Email';
|
||||
import React from 'react';
|
||||
|
||||
/**
|
||||
* Email Card action link
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
export const EmailCardAction = ({ email }: { email: string }) => {
|
||||
return (
|
||||
<Tooltip title={`Email ${email}`}>
|
||||
<Button target="_blank" href={`mailto:${email}`} size="small">
|
||||
<EmailIcon color="action" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2022 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 { entityRouteRef } from '../../../routes';
|
||||
import { Tooltip } from '@material-ui/core';
|
||||
import InfoIcon from '@material-ui/icons/Info';
|
||||
import React from 'react';
|
||||
import { useRouteRef } from '@backstage/core-plugin-api';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { Link } from '@backstage/core-components';
|
||||
|
||||
/**
|
||||
* Card actions that show for all entities
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
export const EntityCardActions = ({ entity }: { entity: Entity }) => {
|
||||
const entityRoute = useRouteRef(entityRouteRef);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tooltip title="Show details">
|
||||
<Link
|
||||
component="button"
|
||||
to={entityRoute({
|
||||
name: entity.metadata.name,
|
||||
namespace: entity.metadata.namespace || 'default',
|
||||
kind: entity.kind.toLocaleLowerCase('en-US'),
|
||||
})}
|
||||
>
|
||||
<InfoIcon color="action" />
|
||||
</Link>
|
||||
</Tooltip>
|
||||
</>
|
||||
);
|
||||
};
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2022 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 { EmailCardAction } from './EmailCardAction';
|
||||
import React from 'react';
|
||||
import { GroupEntity } from '@backstage/catalog-model';
|
||||
|
||||
/**
|
||||
* Card actions that show for a group
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
export const GroupCardActions = ({ entity }: { entity: GroupEntity }) => {
|
||||
return (
|
||||
<>
|
||||
{entity.spec.profile?.email && (
|
||||
<EmailCardAction email={entity.spec.profile.email} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2022 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 { EmailCardAction } from './EmailCardAction';
|
||||
import React from 'react';
|
||||
import { UserEntity } from '@backstage/catalog-model';
|
||||
|
||||
/**
|
||||
* Card actions that show for a user
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
export const UserCardActions = ({ entity }: { entity: UserEntity }) => {
|
||||
return (
|
||||
<>
|
||||
{entity.spec.profile?.email && (
|
||||
<EmailCardAction email={entity.spec.profile.email} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2022 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.
|
||||
*/
|
||||
export { EntityCardActions } from './EntityCardActions';
|
||||
export { GroupCardActions } from './GroupCardActions';
|
||||
export { UserCardActions } from './UserCardActions';
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2022 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 { Card, CardContent } from '@material-ui/core';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import React from 'react';
|
||||
|
||||
/**
|
||||
* Entity not found card
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
export const EntityNotFoundCard = ({
|
||||
entityRef,
|
||||
error,
|
||||
}: {
|
||||
entityRef: string;
|
||||
error?: Error;
|
||||
}) => {
|
||||
return (
|
||||
<Card>
|
||||
<CardContent>
|
||||
<Alert severity="warning">
|
||||
{entityRef} was not found {error?.message}
|
||||
</Alert>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
+9
-85
@@ -13,7 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { entityRouteRef } from '../../routes';
|
||||
import useAsyncFn from 'react-use/lib/useAsyncFn';
|
||||
import { catalogApiRef } from '../../api';
|
||||
import React, { PropsWithChildren, useEffect } from 'react';
|
||||
@@ -25,7 +24,6 @@ import {
|
||||
} from 'material-ui-popup-state/hooks';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Card,
|
||||
CardActions,
|
||||
CardContent,
|
||||
@@ -34,19 +32,19 @@ import {
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from '@material-ui/core';
|
||||
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 { useApiHolder } from '@backstage/core-plugin-api';
|
||||
import {
|
||||
isUserEntity,
|
||||
isGroupEntity,
|
||||
UserEntity,
|
||||
GroupEntity,
|
||||
Entity,
|
||||
isUserEntity,
|
||||
parseEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import { Link, Progress } from '@backstage/core-components';
|
||||
import { Progress } from '@backstage/core-components';
|
||||
import {
|
||||
EntityCardActions,
|
||||
UserCardActions,
|
||||
GroupCardActions,
|
||||
} from './CardActionComponents';
|
||||
import { EntityNotFoundCard } from './EntityNotFoundCard';
|
||||
|
||||
/**
|
||||
* Properties for an entity popover on hover of a component.
|
||||
@@ -77,80 +75,6 @@ const useStyles = makeStyles(() => {
|
||||
|
||||
const maxTagChips = 4;
|
||||
|
||||
const EmailCardAction = ({ email }: { email: string }) => {
|
||||
return (
|
||||
<Tooltip title={`Email ${email}`}>
|
||||
<Button target="_blank" href={`mailto:${email}`} size="small">
|
||||
<EmailIcon color="action" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
const UserCardActions = ({ entity }: { entity: UserEntity }) => {
|
||||
return (
|
||||
<>
|
||||
{entity.spec.profile?.email && (
|
||||
<EmailCardAction email={entity.spec.profile.email} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const GroupCardActions = ({ entity }: { entity: GroupEntity }) => {
|
||||
return (
|
||||
<>
|
||||
{entity.spec.profile?.email && (
|
||||
<EmailCardAction email={entity.spec.profile.email} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows an entity popover on hover of a component.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
const EntityCardActions = ({ entity }: { entity: Entity }) => {
|
||||
const entityRoute = useRouteRef(entityRouteRef);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tooltip title="Show details">
|
||||
<Link
|
||||
component="button"
|
||||
to={entityRoute({
|
||||
name: entity.metadata.name,
|
||||
namespace: entity.metadata.namespace || 'default',
|
||||
kind: entity.kind.toLocaleLowerCase('en-US'),
|
||||
})}
|
||||
>
|
||||
<InfoIcon color="action" />
|
||||
</Link>
|
||||
</Tooltip>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const EntityNotFoundCard = ({
|
||||
entityRef,
|
||||
error,
|
||||
}: {
|
||||
entityRef: string;
|
||||
error?: Error;
|
||||
}) => {
|
||||
return (
|
||||
<Card>
|
||||
<CardContent>
|
||||
<Alert severity="warning">
|
||||
{entityRef} was not found {error?.message}
|
||||
</Alert>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows an entity popover on hover of a component.
|
||||
*
|
||||
|
||||
@@ -16,64 +16,8 @@
|
||||
|
||||
import React, { ComponentType } from 'react';
|
||||
import { EntityRefLink, EntityRefLinkProps } from './EntityRefLink';
|
||||
import { wrapInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import { catalogApiRef } from '../../api';
|
||||
import { CompoundEntityRef } from '@backstage/catalog-model';
|
||||
import { wrapInTestApp } from '@backstage/test-utils';
|
||||
import { entityRouteRef } from '../../routes';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
|
||||
const mockCatalogApi = {
|
||||
getEntityByRef: async (entityRef: CompoundEntityRef) => {
|
||||
if (
|
||||
entityRef.namespace === 'default' &&
|
||||
entityRef.name === 'playback' &&
|
||||
entityRef.kind === 'component'
|
||||
) {
|
||||
return {
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
name: 'playback',
|
||||
namespace: 'default',
|
||||
description: 'Details about the playback service',
|
||||
},
|
||||
};
|
||||
}
|
||||
if (
|
||||
entityRef.namespace === 'default' &&
|
||||
entityRef.name === 'fname.lname' &&
|
||||
entityRef.kind === 'user'
|
||||
) {
|
||||
return {
|
||||
kind: 'User',
|
||||
metadata: {
|
||||
name: 'fname.lname',
|
||||
namespace: 'default',
|
||||
},
|
||||
spec: {
|
||||
profile: {
|
||||
email: 'fname.lname@example.com',
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
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: 'component:default/playback',
|
||||
@@ -83,20 +27,11 @@ export default {
|
||||
title: 'Catalog /EntityRefLink',
|
||||
decorators: [
|
||||
(Story: ComponentType<{}>) =>
|
||||
wrapInTestApp(
|
||||
<>
|
||||
<TestApiProvider
|
||||
apis={[[catalogApiRef, mockCatalogApi as any as CatalogApi]]}
|
||||
>
|
||||
<Story />
|
||||
</TestApiProvider>
|
||||
</>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/catalog/:namespace/:kind/:name': entityRouteRef,
|
||||
},
|
||||
wrapInTestApp(<Story />, {
|
||||
mountedRoutes: {
|
||||
'/catalog/:namespace/:kind/:name': entityRouteRef,
|
||||
},
|
||||
),
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
||||
@@ -104,22 +39,3 @@ export const Default = (args: EntityRefLinkProps) => (
|
||||
<EntityRefLink {...args} />
|
||||
);
|
||||
Default.args = defaultArgs;
|
||||
|
||||
export const User = (args: EntityRefLinkProps) => <EntityRefLink {...args} />;
|
||||
User.args = {
|
||||
entityRef: 'user:default/fname.lname',
|
||||
};
|
||||
|
||||
export const NotFound = (args: EntityRefLinkProps) => (
|
||||
<EntityRefLink {...args} />
|
||||
);
|
||||
NotFound.args = {
|
||||
entityRef: 'user:default/doesnt.exist',
|
||||
};
|
||||
|
||||
export const SlowCatalogItem = (args: EntityRefLinkProps) => (
|
||||
<EntityRefLink {...args} />
|
||||
);
|
||||
SlowCatalogItem.args = {
|
||||
entityRef: 'component:default/slow.catalog.item',
|
||||
};
|
||||
|
||||
@@ -25,7 +25,8 @@ import { entityRouteRef } from '../../routes';
|
||||
import { humanizeEntityRef } from './humanize';
|
||||
import { Link, LinkProps } from '@backstage/core-components';
|
||||
import { useRouteRef } from '@backstage/core-plugin-api';
|
||||
import { EntityPeekAheadPopover } from '../EntityPeekAheadPopover';
|
||||
import { Tooltip } from '@material-ui/core';
|
||||
|
||||
/**
|
||||
* Props for {@link EntityRefLink}.
|
||||
*
|
||||
@@ -76,13 +77,17 @@ export const EntityRefLink = forwardRef<any, EntityRefLinkProps>(
|
||||
{ defaultKind },
|
||||
);
|
||||
|
||||
return (
|
||||
<EntityPeekAheadPopover entityRef={`${kind}:${namespace}/${name}`}>
|
||||
<Link {...linkProps} ref={ref} to={entityRoute(routeParams)}>
|
||||
{children}
|
||||
{!children && (title ?? formattedEntityRefTitle)}
|
||||
</Link>
|
||||
</EntityPeekAheadPopover>
|
||||
const link = (
|
||||
<Link {...linkProps} ref={ref} to={entityRoute(routeParams)}>
|
||||
{children}
|
||||
{!children && (title ?? formattedEntityRefTitle)}
|
||||
</Link>
|
||||
);
|
||||
|
||||
return title ? (
|
||||
<Tooltip title={formattedEntityRefTitle}>{link}</Tooltip>
|
||||
) : (
|
||||
link
|
||||
);
|
||||
},
|
||||
) as (props: EntityRefLinkProps) => JSX.Element;
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2022 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, { ComponentType } from 'react';
|
||||
import { EntityRefLinks, EntityRefLinksProps } from './EntityRefLinks';
|
||||
import { wrapInTestApp } from '@backstage/test-utils';
|
||||
import { CompoundEntityRef } from '@backstage/catalog-model';
|
||||
import { entityRouteRef } from '../../routes';
|
||||
|
||||
const defaultArgs = {
|
||||
entityRefs: ['component:default/playback', 'user:default/fname.lname'],
|
||||
};
|
||||
|
||||
export default {
|
||||
title: 'Catalog /EntityRefLinks',
|
||||
decorators: [
|
||||
(Story: ComponentType<{}>) =>
|
||||
wrapInTestApp(<Story />, {
|
||||
mountedRoutes: {
|
||||
'/catalog/:namespace/:kind/:name': entityRouteRef,
|
||||
},
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
||||
export const Default = (
|
||||
args: EntityRefLinksProps<string | CompoundEntityRef>,
|
||||
) => <EntityRefLinks {...args} />;
|
||||
Default.args = defaultArgs;
|
||||
Reference in New Issue
Block a user