Update profile cards to display more information if relevant

Signed-off-by: Joe Patterson <jrwpatterson@gmail.com>
This commit is contained in:
Joe Patterson
2022-10-14 14:42:51 +10:00
parent 2bb7fa54d7
commit 2e138c95ca
11 changed files with 279 additions and 13 deletions
@@ -87,3 +87,46 @@ 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: 'message',
},
{
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',
Telephone: '123456789',
Location: 'London',
},
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>
);
@@ -37,10 +37,13 @@ import {
ListItemText,
Tooltip,
IconButton,
Divider,
} from '@material-ui/core';
import Icon from '@material-ui/core/Icon';
import AccountTreeIcon from '@material-ui/icons/AccountTree';
import EmailIcon from '@material-ui/icons/Email';
import GroupIcon from '@material-ui/icons/Group';
import LinkIcon from '@material-ui/icons/Link';
import EditIcon from '@material-ui/icons/Edit';
import CachedIcon from '@material-ui/icons/Cached';
import Alert from '@material-ui/lab/Alert';
@@ -53,6 +56,8 @@ import {
} from '@backstage/core-components';
import { alertApiRef, useApi } from '@backstage/core-plugin-api';
const staticProfileKeys = ['displayName', 'email', 'picture'];
const CardTitle = (props: { title: string }) => (
<Box display="flex" alignItems="center">
<GroupIcon fontSize="inherit" />
@@ -76,7 +81,7 @@ export const GroupProfileCard = (props: { variant?: InfoCardVariants }) => {
}
const {
metadata: { name, description, annotations },
metadata: { name, description, annotations, links },
spec: { profile },
} = group;
@@ -94,6 +99,11 @@ export const GroupProfileCard = (props: { variant?: InfoCardVariants }) => {
const entityMetadataEditUrl =
group.metadata.annotations?.[ANNOTATION_EDIT_URL];
const profileKeys =
profile !== undefined
? Object.keys(profile).filter(key => !staticProfileKeys.includes(key))
: [];
const displayName = profile?.displayName ?? name;
const emailHref = profile?.email ? `mailto:${profile.email}` : '#';
const infoCardAction = entityMetadataEditUrl ? (
@@ -190,6 +200,42 @@ export const GroupProfileCard = (props: { variant?: InfoCardVariants }) => {
secondary="Child Groups"
/>
</ListItem>
{links !== undefined && <Divider />}
{links !== undefined &&
links.map(link => {
return (
<ListItem button component="a" key={link.url} href={link.url}>
{link.icon ? (
<ListItemIcon>
<Tooltip title={link.icon}>
<Icon>{link.icon}</Icon>
</Tooltip>
</ListItemIcon>
) : (
<ListItemIcon>
<LinkIcon />
</ListItemIcon>
)}
<ListItemText>{link.title}</ListItemText>
</ListItem>
);
})}
{profile !== undefined && profileKeys.length > 0 && <Divider />}
{profile !== undefined &&
profileKeys.length > 0 &&
profileKeys.map(key => {
const value = profile[key];
return (
<ListItem key={key}>
<ListItemText style={{ width: '25%', flexGrow: 0 }}>
{key}
</ListItemText>
<ListItemText>{value}</ListItemText>
</ListItem>
);
})}
</List>
</Grid>
</Grid>
@@ -102,3 +102,46 @@ 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: 'message',
},
{
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',
'Job Title': 'Software Engineer',
Department: 'Engineering',
Location: 'San Francisco, CA',
},
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,65 @@ 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',
'Job Title': 'Software Engineer',
Department: 'Engineering',
Location: 'San Francisco, CA',
},
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('Software Engineer')).toBeInTheDocument();
expect(rendered.getByText('Department')).toBeInTheDocument();
expect(rendered.getByText('San Francisco, CA')).toBeInTheDocument();
expect(rendered.getByText('Location')).toBeInTheDocument();
expect(rendered.getByText('Slack')).toBeInTheDocument();
expect(rendered.getByText('Google')).toBeInTheDocument();
});
});
@@ -33,12 +33,15 @@ import {
ListItemIcon,
ListItemText,
Tooltip,
Divider,
} from '@material-ui/core';
import EditIcon from '@material-ui/icons/Edit';
import EmailIcon from '@material-ui/icons/Email';
import GroupIcon from '@material-ui/icons/Group';
import PersonIcon from '@material-ui/icons/Person';
import LinkIcon from '@material-ui/icons/Link';
import Alert from '@material-ui/lab/Alert';
import Icon from '@material-ui/core/Icon';
import React from 'react';
import {
Avatar,
@@ -47,6 +50,8 @@ import {
Link,
} from '@backstage/core-components';
const staticProfileKeys = ['displayName', 'email', 'picture'];
const CardTitle = (props: { title?: string }) =>
props.title ? (
<Box display="flex" alignItems="center">
@@ -66,7 +71,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;
@@ -75,6 +80,11 @@ export const UserProfileCard = (props: { variant?: InfoCardVariants }) => {
kind: 'Group',
});
const profileKeys =
profile !== undefined
? Object.keys(profile).filter(key => !staticProfileKeys.includes(key))
: [];
return (
<InfoCard
title={<CardTitle title={displayName} />}
@@ -128,6 +138,44 @@ export const UserProfileCard = (props: { variant?: InfoCardVariants }) => {
/>
</ListItemText>
</ListItem>
{links !== undefined && <Divider />}
{links !== undefined &&
links.map(link => {
return (
<ListItem button component="a" key={link.url} href={link.url}>
{link.icon ? (
<ListItemIcon>
<Tooltip title={link.icon}>
<Icon>{link.icon}</Icon>
</Tooltip>
</ListItemIcon>
) : (
<ListItemIcon>
<LinkIcon />
</ListItemIcon>
)}
<ListItemText>{link.title}</ListItemText>
</ListItem>
);
})}
{profile !== undefined && profileKeys.length > 0 && <Divider />}
{profile !== undefined &&
profileKeys.length > 0 &&
profileKeys.map(key => {
const value = profile[key];
return (
<ListItem key={key}>
<ListItemText style={{ width: '100px', flexGrow: 0 }}>
{key}
</ListItemText>
<ListItemText>{value}</ListItemText>
</ListItem>
);
})}
</List>
</Grid>
</Grid>