Merge pull request #23486 from backstage/blam/fix-avatar
core-components: Move inline styles to classes for Avatar component
This commit is contained in:
@@ -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`
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/core-components': patch
|
||||
---
|
||||
|
||||
Deprecate the `customStyles` prop for the `Avatar` component in favour of the `classes` prop.
|
||||
@@ -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)}
|
||||
|
||||
+5
-3
@@ -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 }}
|
||||
|
||||
+24
-10
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user