Merge pull request #14129 from jrwpatterson/master

Extend the UserProfileCard and GroupProfileCard to allow for adding links
This commit is contained in:
Patrik Oldsberg
2022-11-02 17:18:21 +01:00
committed by GitHub
9 changed files with 352 additions and 2 deletions
@@ -87,3 +87,44 @@ export default {
),
],
};
const extraDetailsEntity: GroupEntity = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Group',
metadata: {
name: 'team-a',
description: 'Team A',
links: [
{
url: 'slack://user?team=T00000000&id=U00000000',
title: 'Slack',
icon: 'chat',
},
{
url: 'https://www.google.com',
title: 'Google',
},
],
},
spec: {
profile: {
displayName: 'Team A',
email: 'team-a@example.com',
picture:
'https://avatars.dicebear.com/api/identicon/team-a@example.com.svg?background=%23fff&margin=25',
},
type: 'group',
children: [],
},
relations: [dummyDepartment],
};
export const ExtraDetails = () => (
<EntityProvider entity={extraDetailsEntity}>
<Grid container spacing={4}>
<Grid item xs={12} md={4}>
<GroupProfileCard variant="gridItem" />
</Grid>
</Grid>
</EntityProvider>
);
@@ -52,6 +52,7 @@ import {
Link,
} from '@backstage/core-components';
import { alertApiRef, useApi } from '@backstage/core-plugin-api';
import { LinksGroup } from '../../Meta';
const CardTitle = (props: { title: string }) => (
<Box display="flex" alignItems="center">
@@ -76,7 +77,7 @@ export const GroupProfileCard = (props: { variant?: InfoCardVariants }) => {
}
const {
metadata: { name, description, annotations },
metadata: { name, description, annotations, links },
spec: { profile },
} = group;
@@ -190,6 +191,7 @@ export const GroupProfileCard = (props: { variant?: InfoCardVariants }) => {
secondary="Child Groups"
/>
</ListItem>
<LinksGroup links={links} />
</List>
</Grid>
</Grid>
@@ -0,0 +1,66 @@
/*
* 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 { EntityLink } from '@backstage/catalog-model';
import { IconComponent, useApp } from '@backstage/core-plugin-api';
import LanguageIcon from '@material-ui/icons/Language';
import {
ListItem,
ListItemIcon,
ListItemText,
Divider,
} from '@material-ui/core';
import React from 'react';
const WebLink = ({
href,
Icon,
text,
}: {
href: string;
text?: string;
Icon?: IconComponent;
}) => (
<ListItem button component="a" key={href} href={href}>
<ListItemIcon>{Icon ? <Icon /> : <LanguageIcon />}</ListItemIcon>
<ListItemText>{text}</ListItemText>
</ListItem>
);
export const LinksGroup = ({ links }: { links?: EntityLink[] }) => {
const app = useApp();
const iconResolver = (key?: string): IconComponent =>
key ? app.getSystemIcon(key) ?? LanguageIcon : LanguageIcon;
if (links === undefined) {
return null;
}
return (
<>
<Divider />
{links.map(link => {
return (
<WebLink
key={link.url}
href={link.url}
text={link.title}
Icon={iconResolver(link.icon)}
/>
);
})}
</>
);
};
@@ -0,0 +1,16 @@
/*
* 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 * from './LinksGroup';
@@ -102,3 +102,43 @@ export default {
}),
],
};
const extraDetailsEntity: UserEntity = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'User',
metadata: {
name: 'guest',
description: 'Description for guest',
links: [
{
url: 'slack://user?team=T00000000&id=U00000000',
title: 'Slack',
icon: 'chat',
},
{
url: 'https://www.google.com',
title: 'Google',
},
],
},
spec: {
profile: {
displayName: 'Guest User',
email: 'guest@example.com',
picture:
'https://avatars.dicebear.com/api/avataaars/guest@example.com.svg?background=%23fff',
},
memberOf: ['team-a'],
},
relations: [dummyGroup],
};
export const ExtraDetails = () => (
<EntityProvider entity={extraDetailsEntity}>
<Grid container spacing={4}>
<Grid item xs={12} md={4}>
<UserProfileCard variant="gridItem" />
</Grid>
</Grid>
</EntityProvider>
);
@@ -155,4 +155,58 @@ describe('Edit Button', () => {
);
expect(rendered.getByRole('button')).toBeInTheDocument();
});
it('Should show the extra fields if either links or extra profile are filled', async () => {
const annotations: Record<string, string> = {
'backstage.io/edit-url': 'https://example.com/user.yaml',
};
const userEntity: UserEntity = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'User',
metadata: {
name: 'calum.leavy',
description: 'Super awesome human',
annotations,
links: [
{
url: 'slack://user?team=T00000000&id=U00000000',
title: 'Slack',
icon: 'message',
},
{
url: 'https://www.google.com',
title: 'Google',
},
],
},
spec: {
profile: {
displayName: 'Calum Leavy',
email: 'calum-leavy@example.com',
},
memberOf: ['ExampleGroup'],
},
relations: [
{
type: 'memberOf',
targetRef: 'group:default/examplegroup',
},
],
};
const rendered = await renderWithEffects(
wrapInTestApp(
<EntityProvider entity={userEntity}>
<UserProfileCard variant="gridItem" />
</EntityProvider>,
{
mountedRoutes: {
'/catalog/:namespace/:kind/:name': entityRouteRef,
},
},
),
);
expect(rendered.getByText('Slack')).toBeInTheDocument();
expect(rendered.getByText('Google')).toBeInTheDocument();
});
});
@@ -46,6 +46,7 @@ import {
InfoCardVariants,
Link,
} from '@backstage/core-components';
import { LinksGroup } from '../../Meta';
const CardTitle = (props: { title?: string }) =>
props.title ? (
@@ -66,7 +67,7 @@ export const UserProfileCard = (props: { variant?: InfoCardVariants }) => {
user.metadata.annotations?.[ANNOTATION_EDIT_URL];
const {
metadata: { name: metaName, description },
metadata: { name: metaName, description, links },
spec: { profile },
} = user;
const displayName = profile?.displayName ?? metaName;
@@ -128,6 +129,8 @@ export const UserProfileCard = (props: { variant?: InfoCardVariants }) => {
/>
</ListItemText>
</ListItem>
<LinksGroup links={links} />
</List>
</Grid>
</Grid>