Merge pull request #23486 from backstage/blam/fix-avatar

core-components: Move inline styles to classes for Avatar component
This commit is contained in:
Fredrik Adelöw
2024-03-19 14:53:17 +01:00
committed by GitHub
10 changed files with 126 additions and 81 deletions
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/plugin-azure-devops': patch
'@backstage/plugin-bazaar': patch
'@backstage/plugin-org': patch
---
Remove the use of the deprecated `customStyles` for `Avatar`
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/core-components': patch
---
Deprecate the `customStyles` prop for the `Avatar` component in favour of the `classes` prop.
+4
View File
@@ -95,6 +95,10 @@ export type AvatarClassKey = 'avatar';
// @public
export interface AvatarProps {
classes?: {
[key in 'avatar' | 'avatarText']?: string;
};
// @deprecated
customStyles?: CSSProperties;
displayName?: string;
picture?: string;
@@ -16,6 +16,15 @@
import React from 'react';
import { Avatar } from './Avatar';
import { makeStyles } from '@material-ui/core/styles';
const useStyles = makeStyles({
avatar: {
width: '24px',
height: '24px',
fontSize: '8px',
},
});
export default {
title: 'Data Display/Avatar',
@@ -34,9 +43,7 @@ export const NameFallback = () => <Avatar displayName="Jenny Doe" />;
export const Empty = () => <Avatar />;
export const CustomStyling = () => (
<Avatar
displayName="Jenny Doe"
customStyles={{ width: '24px', height: '24px', fontSize: '8px' }}
/>
);
export const CustomStyling = () => {
const classes = useStyles();
return <Avatar displayName="Jenny Doe" classes={classes} />;
};
@@ -17,7 +17,6 @@
import { render } from '@testing-library/react';
import React from 'react';
import { Avatar } from './Avatar';
import { stringToColor } from './utils';
describe('<Avatar />', () => {
it('renders without exploding', async () => {
@@ -25,26 +24,4 @@ describe('<Avatar />', () => {
expect(getByText('JD')).toBeInTheDocument();
});
it('generates a background color', async () => {
const bgcolor = stringToColor('John Doe');
const { getByText } = render(<Avatar displayName="John Doe" />);
expect(getByText('JD').parentElement).toHaveStyle(
`background-color: ${bgcolor}`,
);
});
it('does not generate a background color when a picture is given', async () => {
const bgcolor = stringToColor('John Doe');
const { getByAltText } = render(
<Avatar
displayName="John Doe"
picture="https://backstage.io/test/john-doe.png"
/>,
);
expect(getByAltText('John Doe')).not.toHaveStyle(
`background-color: ${bgcolor}`,
);
});
});
@@ -19,6 +19,7 @@ import Typography from '@material-ui/core/Typography';
import React, { CSSProperties } from 'react';
import { extractInitials, stringToColor } from './utils';
import classNames from 'classnames';
/** @public */
export type AvatarClassKey = 'avatar';
@@ -29,6 +30,8 @@ const useStyles = makeStyles(
width: '4rem',
height: '4rem',
color: theme.palette.common.white,
backgroundColor: (props: { backgroundColor?: string }) =>
props.backgroundColor,
},
avatarText: {
fontWeight: theme.typography.fontWeightBold,
@@ -55,8 +58,14 @@ export interface AvatarProps {
picture?: string;
/**
* Custom styles applied to avatar
* @deprecated - use the classes property instead
*/
customStyles?: CSSProperties;
/**
* Custom styles applied to avatar
*/
classes?: { [key in 'avatar' | 'avatarText']?: string };
}
/**
@@ -69,35 +78,41 @@ export interface AvatarProps {
*/
export function Avatar(props: AvatarProps) {
const { displayName, picture, customStyles } = props;
const classes = useStyles();
let styles = { ...customStyles };
const styles = { ...customStyles };
// TODO: Remove this with the customStyles deprecation
const fontStyles = {
fontFamily: styles.fontFamily,
fontSize: styles.fontSize,
fontWeight: styles.fontWeight,
};
// We only calculate the background color if there's not an avatar
// picture. If there is a picture, it might have a transparent
// background and we don't know whether the calculated background
// color will clash.
if (!picture) {
styles = {
backgroundColor: stringToColor(displayName || ''),
...customStyles,
};
}
const classes = useStyles(
!picture ? { backgroundColor: stringToColor(displayName || '') } : {},
);
const avatarClassNames = classNames(props.classes?.avatar, classes.avatar);
const avatarTextClassNames = classNames(
props.classes?.avatarText,
classes.avatarText,
);
return (
<MaterialAvatar
alt={displayName}
src={picture}
className={classes.avatar}
className={avatarClassNames}
style={styles}
>
{displayName && (
<Typography
variant="h6"
component="span"
className={classes.avatarText}
className={avatarTextClassNames}
style={fontStyles}
>
{extractInitials(displayName)}
@@ -51,6 +51,8 @@ const useStyles = makeStyles(
policies: {
flex: 1,
},
avatar: { width: '2.5rem', height: '2.5rem' },
avatarText: { fontSize: '1rem' },
}),
{ name: 'PullRequestCard' },
);
@@ -64,6 +66,8 @@ export const PullRequestCard = ({
pullRequest,
simplified,
}: PullRequestCardProps) => {
const classes = useStyles();
const title = (
<Link to={pullRequest.link ?? ''} title={pullRequest.description}>
{pullRequest.title}
@@ -90,12 +94,10 @@ export const PullRequestCard = ({
<Avatar
displayName={pullRequest.createdBy?.displayName}
picture={pullRequest.createdBy?.imageUrl}
customStyles={{ width: '2.5rem', height: '2.5rem', fontSize: '1rem' }}
classes={{ avatar: classes.avatar, avatarText: classes.avatarText }}
/>
);
const classes = useStyles();
return (
<Card
classes={{ root: classes.card }}
@@ -17,22 +17,36 @@
import { Avatar } from '@backstage/core-components';
import React from 'react';
import { Reviewer } from '@backstage/plugin-azure-devops-common';
import { makeStyles } from '@material-ui/core/styles';
type PullRequestCardReviewerProps = {
reviewer: Reviewer;
};
export const PullRequestCardReviewer = ({
reviewer,
}: PullRequestCardReviewerProps) => (
<Avatar
displayName={reviewer.displayName}
picture={reviewer.imageUrl}
customStyles={{
const useStyles = makeStyles(
{
avatar: {
width: '2.5rem',
height: '2.5rem',
fontSize: '1rem',
border: '0.3rem solid silver',
}}
/>
},
avatarText: {
fontSize: '1rem',
},
},
{ name: 'PullRequestCardReviewer' },
);
export const PullRequestCardReviewer = ({
reviewer,
}: PullRequestCardReviewerProps) => {
const classes = useStyles();
return (
<Avatar
displayName={reviewer.displayName}
picture={reviewer.imageUrl}
classes={classes}
/>
);
};
@@ -19,6 +19,8 @@ import Grid from '@material-ui/core/Grid';
import Card from '@material-ui/core/Card';
import CardContent from '@material-ui/core/CardContent';
import Typography from '@material-ui/core/Typography';
import makeStyles from '@material-ui/core/styles/makeStyles';
import { GridSize } from '@material-ui/core/Grid';
import { parseEntityRef } from '@backstage/catalog-model';
import { Avatar, Link } from '@backstage/core-components';
@@ -35,6 +37,25 @@ type Props = {
membersSize: GridSize;
};
const useStyles = makeStyles(
{
avatar: {
width: '19px',
height: '19px',
float: 'left',
marginRight: '0.3rem',
marginTop: '0rem',
marginBottom: '0rem',
alignItems: 'left',
},
avatarText: {
fontSize: '8px',
textAlign: 'left',
},
},
{ name: 'CardContentFields' },
);
export const CardContentFields = ({
bazaarProject,
members,
@@ -42,7 +63,7 @@ export const CardContentFields = ({
membersSize,
}: Props) => {
const catalogEntityRoute = useRouteRef(entityRouteRef);
const classes = useStyles();
return (
<div>
<Card>
@@ -84,17 +105,7 @@ export const CardContentFields = ({
>
<Avatar
displayName={member.userId}
customStyles={{
width: '19px',
height: '19px',
fontSize: '8px',
float: 'left',
marginRight: '0.3rem',
marginTop: '0rem',
marginBottom: '0rem',
alignItems: 'left',
textAlign: 'left',
}}
classes={classes}
picture={member.picture}
/>
<Link
@@ -50,19 +50,25 @@ import {
import { EntityRefLink } from '@backstage/plugin-catalog-react';
import { EntityRelationAggregation } from '../../types';
const useStyles = makeStyles((theme: Theme) =>
createStyles({
card: {
border: `1px solid ${theme.palette.divider}`,
boxShadow: theme.shadows[2],
borderRadius: '4px',
overflow: 'visible',
position: 'relative',
margin: theme.spacing(4, 1, 1),
flex: '1',
minWidth: '0px',
},
}),
const useStyles = makeStyles(
(theme: Theme) =>
createStyles({
card: {
border: `1px solid ${theme.palette.divider}`,
boxShadow: theme.shadows[2],
borderRadius: '4px',
overflow: 'visible',
position: 'relative',
margin: theme.spacing(4, 1, 1),
flex: '1',
minWidth: '0px',
},
avatar: {
position: 'absolute',
top: '-2rem',
},
}),
{ name: 'MembersListCardComponent' },
);
const MemberComponent = (props: { member: UserEntity }) => {
@@ -85,10 +91,7 @@ const MemberComponent = (props: { member: UserEntity }) => {
<Avatar
displayName={displayName}
picture={profile?.picture}
customStyles={{
position: 'absolute',
top: '-2rem',
}}
classes={classes}
/>
<Box
pt={2}