Skip to content

Commit

Permalink
[Avatar] Migrate to emotion (#24114)
Browse files Browse the repository at this point in the history
  • Loading branch information
oliviertassinari committed Dec 24, 2020
1 parent 7a3752e commit d72eb5f
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 101 deletions.
2 changes: 1 addition & 1 deletion docs/pages/api-docs/avatar.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@
"filename": "/packages/material-ui/src/Avatar/Avatar.js",
"inheritance": null,
"demos": "<ul><li><a href=\"/components/avatars/\">Avatars</a></li></ul>",
"styledComponent": false
"styledComponent": true
}
186 changes: 112 additions & 74 deletions packages/material-ui/src/Avatar/Avatar.js
Original file line number Diff line number Diff line change
@@ -1,60 +1,105 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import { useThemeVariants } from '@material-ui/styles';
import withStyles from '../styles/withStyles';
import experimentalStyled from '../styles/experimentalStyled';
import useThemeProps from '../styles/useThemeProps';
import Person from '../internal/svg-icons/Person';
import avatarClasses, { getAvatarUtilityClass } from './avatarClasses';

export const styles = (theme) => ({
/* Styles applied to the root element. */
root: {
position: 'relative',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexShrink: 0,
width: 40,
height: 40,
fontFamily: theme.typography.fontFamily,
fontSize: theme.typography.pxToRem(20),
lineHeight: 1,
borderRadius: '50%',
overflow: 'hidden',
userSelect: 'none',
},
/* Styles applied to the root element if not `src` or `srcSet`. */
colorDefault: {
color: theme.palette.background.default,
backgroundColor:
theme.palette.mode === 'light' ? theme.palette.grey[400] : theme.palette.grey[600],
},
/* Styles applied to the root element if `variant="circular"`. */
circular: {},
/* Styles applied to the root element if `variant="rounded"`. */
rounded: {
borderRadius: theme.shape.borderRadius,
const overridesResolver = (props, styles) => {
const { variant = 'circular' } = props;

const styleOverrides = {
...styles.root,
...styles[variant],
[`&.${avatarClasses.colorDefault}`]: styles.colorDefault,
[`&.${avatarClasses.img}`]: styles.img,
[`&.${avatarClasses.fallback}`]: styles.fallback,
};

return styleOverrides;
};

const useAvatarClasses = (props) => {
const { classes = {}, variant, colorDefault } = props;

const utilityClasses = {
root: clsx(avatarClasses.root, classes.root, getAvatarUtilityClass(variant), {
[avatarClasses.colorDefault]: colorDefault,
}),
img: clsx(avatarClasses.img, classes.img),
fallback: clsx(avatarClasses.fallback, classes.fallback),
};

return utilityClasses;
};

const AvatarRoot = experimentalStyled(
'div',
{},
{
name: 'Avatar',
slot: 'Root',
overridesResolver,
},
/* Styles applied to the root element if `variant="square"`. */
square: {
)((props) => ({
position: 'relative',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexShrink: 0,
width: 40,
height: 40,
fontFamily: props.theme.typography.fontFamily,
fontSize: props.theme.typography.pxToRem(20),
lineHeight: 1,
borderRadius: '50%',
overflow: 'hidden',
userSelect: 'none',
...(props.styleProps.variant === 'rounded' && {
borderRadius: props.theme.shape.borderRadius,
}),
...(props.styleProps.variant === 'square' && {
borderRadius: 0,
}),
...(props.styleProps.colorDefault && {
color: props.theme.palette.background.default,
backgroundColor:
props.theme.palette.mode === 'light'
? props.theme.palette.grey[400]
: props.theme.palette.grey[600],
}),
}));

const AvatarImg = experimentalStyled(
'img',
{},
{
name: 'Avatar',
slot: 'Img',
},
/* Styles applied to the img element if either `src` or `srcSet` is defined. */
img: {
width: '100%',
height: '100%',
textAlign: 'center',
// Handle non-square image. The property isn't supported by IE11.
objectFit: 'cover',
// Hide alt text.
color: 'transparent',
// Hide the image broken icon, only works on Chrome.
textIndent: 10000,
},
/* Styles applied to the fallback icon */
fallback: {
width: '75%',
height: '75%',
)({
width: '100%',
height: '100%',
textAlign: 'center',
// Handle non-square image. The property isn't supported by IE11.
objectFit: 'cover',
// Hide alt text.
color: 'transparent',
// Hide the image broken icon, only works on Chrome.
textIndent: 10000,
});

const AvatarFallback = experimentalStyled(
Person,
{},
{
name: 'Avatar',
slot: 'Fallback',
},
)({
width: '75%',
height: '75%',
});

function useLoaded({ src, srcSet }) {
Expand Down Expand Up @@ -94,11 +139,12 @@ function useLoaded({ src, srcSet }) {
return loaded;
}

const Avatar = React.forwardRef(function Avatar(props, ref) {
const Avatar = React.forwardRef(function Avatar(inProps, ref) {
const props = useThemeProps({ props: inProps, name: 'MuiAvatar' });

const {
alt,
children: childrenProp,
classes,
className,
component: Component = 'div',
imgProps,
Expand All @@ -109,25 +155,24 @@ const Avatar = React.forwardRef(function Avatar(props, ref) {
...other
} = props;

const themeVariantsClasses = useThemeVariants(
{
...props,
component: Component,
variant,
},
'MuiAvatar',
);

let children = null;

// Use a hook instead of onError on the img element to support server-side rendering.
const loaded = useLoaded({ src, srcSet });
const hasImg = src || srcSet;
const hasImgNotFailing = hasImg && loaded !== 'error';

const stateAndProps = {
...props,
variant,
colorDefault: !hasImgNotFailing,
};

const classes = useAvatarClasses(stateAndProps);

if (hasImgNotFailing) {
children = (
<img
<AvatarImg
alt={alt}
src={src}
srcSet={srcSet}
Expand All @@ -141,26 +186,19 @@ const Avatar = React.forwardRef(function Avatar(props, ref) {
} else if (hasImg && alt) {
children = alt[0];
} else {
children = <Person className={classes.fallback} />;
children = <AvatarFallback className={classes.fallback} />;
}

return (
<Component
className={clsx(
classes.root,
classes.system,
classes[variant],
{
[classes.colorDefault]: !hasImgNotFailing,
},
themeVariantsClasses,
className,
)}
<AvatarRoot
as={Component}
styleProps={stateAndProps}
className={clsx(classes.root, className)}
ref={ref}
{...other}
>
{children}
</Component>
</AvatarRoot>
);
});

Expand Down Expand Up @@ -220,4 +258,4 @@ Avatar.propTypes = {
]),
};

export default withStyles(styles, { name: 'MuiAvatar' })(Avatar);
export default Avatar;
19 changes: 6 additions & 13 deletions packages/material-ui/src/Avatar/Avatar.test.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,24 @@
import * as React from 'react';
import { expect } from 'chai';
import {
createClientRender,
fireEvent,
getClasses,
createMount,
describeConformance,
} from 'test/utils';
import { createClientRender, fireEvent, createMount, describeConformanceV5 } from 'test/utils';
import { spy } from 'sinon';
import CancelIcon from '../internal/svg-icons/Cancel';
import Avatar from './Avatar';
import classes from './avatarClasses';

describe('<Avatar />', () => {
const mount = createMount();
let classes;
const render = createClientRender();

before(() => {
classes = getClasses(<Avatar />);
});

describeConformance(<Avatar />, () => ({
describeConformanceV5(<Avatar />, () => ({
classes,
inheritComponent: 'div',
mount,
refInstanceof: window.HTMLDivElement,
testComponentPropWith: 'span',
muiName: 'MuiAvatar',
testVariantProps: { variant: 'foo' },
skip: ['componentsProp'],
}));

describe('image avatar', () => {
Expand Down
15 changes: 15 additions & 0 deletions packages/material-ui/src/Avatar/avatarClasses.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export interface AvatarClasses {
root: string;
colorDefault: string;
circular: string;
rounded: string;
square: string;
img: string;
fallback: string;
}

declare const AvatarClasses: AvatarClasses;

export function getAvatarUtilityClass(part: string): string;

export default AvatarClasses;
15 changes: 15 additions & 0 deletions packages/material-ui/src/Avatar/avatarClasses.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export function getAvatarUtilityClass(name) {
return `MuiAvatar-${name}`;
}

const avatarClasses = {
root: getAvatarUtilityClass('root'),
colorDefault: getAvatarUtilityClass('colorDefault'),
circular: getAvatarUtilityClass('circular'),
rounded: getAvatarUtilityClass('rounded'),
square: getAvatarUtilityClass('square'),
img: getAvatarUtilityClass('img'),
fallback: getAvatarUtilityClass('fallback'),
};

export default avatarClasses;
2 changes: 2 additions & 0 deletions packages/material-ui/src/Avatar/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export { default } from './Avatar';
export * from './Avatar';
export { default as avatarClasses } from './avatarClasses';
export * from './avatarClasses';
2 changes: 2 additions & 0 deletions packages/material-ui/src/Avatar/index.js
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export { default } from './Avatar';
export { default as avatarClasses } from './avatarClasses';
export * from './avatarClasses';
26 changes: 13 additions & 13 deletions packages/material-ui/src/Button/Button.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ const useButtonClasses = (props) => {

const utilityClasses = {
root: clsx(
buttonClasses['root'],
classes['root'],
buttonClasses.root,
classes.root,
getButtonUtilityClass(variant),
classes[variant],
getButtonUtilityClass(`${variant}${capitalize(color)}`),
Expand All @@ -56,23 +56,23 @@ const useButtonClasses = (props) => {
getButtonUtilityClass(`${variant}Size${capitalize(size)}`),
classes[`${variant}Size${capitalize(size)}`],
{
[buttonClasses['colorInherit']]: color === 'inherit',
[classes['colorInherit']]: color === 'inherit',
[buttonClasses['disableElevation']]: disableElevation,
[classes['disableElevation']]: disableElevation,
[buttonClasses['fullWidth']]: fullWidth,
[classes['fullWidth']]: fullWidth,
[buttonClasses.colorInherit]: color === 'inherit',
[classes.colorInherit]: color === 'inherit',
[buttonClasses.disableElevation]: disableElevation,
[classes.disableElevation]: disableElevation,
[buttonClasses.fullWidth]: fullWidth,
[classes.fullWidth]: fullWidth,
},
),
label: clsx(buttonClasses['label'], classes['label']),
label: clsx(buttonClasses.label, classes.label),
startIcon: clsx(
buttonClasses['startIcon'],
classes['startIcon'],
buttonClasses.startIcon,
classes.startIcon,
getButtonUtilityClass(`iconSize${capitalize(size)}`),
),
endIcon: clsx(
buttonClasses['endIcon'],
classes['endIcon'],
buttonClasses.endIcon,
classes.endIcon,
getButtonUtilityClass(`iconSize${capitalize(size)}`),
),
};
Expand Down

0 comments on commit d72eb5f

Please sign in to comment.