From 0edc67f4cc8e34bcb64a89ee1dd495bfeff00b6d Mon Sep 17 00:00:00 2001 From: Praveenkumar Kalidass Date: Tue, 19 Jan 2021 17:14:55 +0530 Subject: [PATCH] [Avatar] Migrate AvatarGroup to emotion (#24452) --- docs/pages/api-docs/avatar-group.json | 3 +- .../api-docs/avatar-group/avatar-group.json | 1 + .../src/AvatarGroup/AvatarGroup.d.ts | 7 +- .../src/AvatarGroup/AvatarGroup.js | 98 +++++++++++++++---- .../src/AvatarGroup/AvatarGroup.test.js | 30 +++--- .../src/AvatarGroup/avatarGroupClasses.d.ts | 10 ++ .../src/AvatarGroup/avatarGroupClasses.js | 9 ++ .../material-ui/src/AvatarGroup/index.d.ts | 2 + packages/material-ui/src/AvatarGroup/index.js | 2 + 9 files changed, 130 insertions(+), 32 deletions(-) create mode 100644 packages/material-ui/src/AvatarGroup/avatarGroupClasses.d.ts create mode 100644 packages/material-ui/src/AvatarGroup/avatarGroupClasses.js diff --git a/docs/pages/api-docs/avatar-group.json b/docs/pages/api-docs/avatar-group.json index f267333fde1d97..a82432ee0a6a33 100644 --- a/docs/pages/api-docs/avatar-group.json +++ b/docs/pages/api-docs/avatar-group.json @@ -10,6 +10,7 @@ }, "default": "'medium'" }, + "sx": { "type": { "name": "object" } }, "variant": { "type": { "name": "union", @@ -25,5 +26,5 @@ "filename": "/packages/material-ui/src/AvatarGroup/AvatarGroup.js", "inheritance": null, "demos": "", - "styledComponent": false + "styledComponent": true } diff --git a/docs/translations/api-docs/avatar-group/avatar-group.json b/docs/translations/api-docs/avatar-group/avatar-group.json index 0fc78564fc67ce..cf50f9faa15e67 100644 --- a/docs/translations/api-docs/avatar-group/avatar-group.json +++ b/docs/translations/api-docs/avatar-group/avatar-group.json @@ -5,6 +5,7 @@ "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", "max": "Max avatars to show before +x.", "spacing": "Spacing between avatars.", + "sx": "The system prop that allows defining system overrides as well as additional CSS styles. See the `sx` page for more details.", "variant": "The variant to use." }, "classDescriptions": { diff --git a/packages/material-ui/src/AvatarGroup/AvatarGroup.d.ts b/packages/material-ui/src/AvatarGroup/AvatarGroup.d.ts index 2a71f2b4319026..ad82cb1e187772 100644 --- a/packages/material-ui/src/AvatarGroup/AvatarGroup.d.ts +++ b/packages/material-ui/src/AvatarGroup/AvatarGroup.d.ts @@ -1,6 +1,7 @@ import * as React from 'react'; -import { InternalStandardProps as StandardProps } from '@material-ui/core'; +import { InternalStandardProps as StandardProps, Theme } from '@material-ui/core'; import { OverridableStringUnion } from '@material-ui/types'; +import { SxProps } from '@material-ui/system'; export interface AvatarGroupPropsVariantOverrides {} export type AvatarGroupVariantDefaults = Record<'circular' | 'rounded' | 'square', true>; @@ -29,6 +30,10 @@ export interface AvatarGroupProps extends StandardProps; /** * The variant to use. * @default 'circular' diff --git a/packages/material-ui/src/AvatarGroup/AvatarGroup.js b/packages/material-ui/src/AvatarGroup/AvatarGroup.js index 7597deb044bb50..9c2761718dc9a4 100644 --- a/packages/material-ui/src/AvatarGroup/AvatarGroup.js +++ b/packages/material-ui/src/AvatarGroup/AvatarGroup.js @@ -2,23 +2,45 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { isFragment } from 'react-is'; import clsx from 'clsx'; -import { chainPropTypes } from '@material-ui/utils'; -import { withStyles } from '../styles'; +import { chainPropTypes, deepmerge } from '@material-ui/utils'; +import { unstable_composeClasses as composeClasses } from '@material-ui/unstyled'; +import experimentalStyled from '../styles/experimentalStyled'; +import useThemeProps from '../styles/useThemeProps'; import Avatar from '../Avatar'; +import avatarGroupClasses, { getAvatarGroupUtilityClass } from './avatarGroupClasses'; const SPACINGS = { small: -16, medium: null, }; -export const styles = (theme) => ({ - /* Styles applied to the root element. */ - root: { - display: 'flex', - flexDirection: 'row-reverse', +const overridesResolver = (props, styles) => { + return deepmerge(styles.root || {}, { + [`& .${avatarGroupClasses.avatar}`]: styles.avatar, + }); +}; + +const useUtilityClasses = (styleProps) => { + const { classes } = styleProps; + + const slots = { + root: ['root'], + avatar: ['avatar'], + }; + + return composeClasses(slots, getAvatarGroupUtilityClass, classes); +}; + +const AvatarGroupRoot = experimentalStyled( + 'div', + {}, + { + name: 'MuiAvatarGroup', + slot: 'Root', + overridesResolver, }, - /* Styles applied to the avatar elements. */ - avatar: { +)(({ theme }) => ({ + [`& .MuiAvatar-root`]: { border: `2px solid ${theme.palette.background.default}`, boxSizing: 'content-box', marginLeft: -8, @@ -26,12 +48,35 @@ export const styles = (theme) => ({ marginLeft: 0, }, }, -}); + /* Styles applied to the root element. */ + display: 'flex', + flexDirection: 'row-reverse', +})); + +const AvatarGroupAvatar = experimentalStyled( + Avatar, + {}, + { + name: 'MuiAvatarGroup', + slot: 'Avatar', + }, +)(({ theme }) => ({ + border: `2px solid ${theme.palette.background.default}`, + boxSizing: 'content-box', + marginLeft: -8, + '&:last-child': { + marginLeft: 0, + }, +})); + +const AvatarGroup = React.forwardRef(function AvatarGroup(inProps, ref) { + const props = useThemeProps({ + props: inProps, + name: 'MuiAvatarGroup', + }); -const AvatarGroup = React.forwardRef(function AvatarGroup(props, ref) { const { children: childrenProp, - classes, className, max = 5, spacing = 'medium', @@ -40,6 +85,15 @@ const AvatarGroup = React.forwardRef(function AvatarGroup(props, ref) { } = props; const clampedMax = max < 2 ? 2 : max; + const styleProps = { + ...other, + max, + spacing, + variant, + }; + + const classes = useUtilityClasses(styleProps); + const children = React.Children.toArray(childrenProp).filter((child) => { if (process.env.NODE_ENV !== 'production') { if (isFragment(child)) { @@ -60,9 +114,15 @@ const AvatarGroup = React.forwardRef(function AvatarGroup(props, ref) { const marginLeft = spacing && SPACINGS[spacing] !== undefined ? SPACINGS[spacing] : -spacing; return ( -
+ {extraAvatars ? ( - +{extraAvatars} - + ) : null} {children .slice(0, children.length - extraAvatars) @@ -85,7 +145,7 @@ const AvatarGroup = React.forwardRef(function AvatarGroup(props, ref) { variant: child.props.variant || variant, }); })} -
+ ); }); @@ -125,6 +185,10 @@ AvatarGroup.propTypes = { * @default 'medium' */ spacing: PropTypes.oneOfType([PropTypes.oneOf(['medium', 'small']), PropTypes.number]), + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx: PropTypes.object, /** * The variant to use. * @default 'circular' @@ -135,4 +199,4 @@ AvatarGroup.propTypes = { ]), }; -export default withStyles(styles, { name: 'MuiAvatarGroup' })(AvatarGroup); +export default AvatarGroup; diff --git a/packages/material-ui/src/AvatarGroup/AvatarGroup.test.js b/packages/material-ui/src/AvatarGroup/AvatarGroup.test.js index 7e5e1e9af1a155..3ad757e6982a09 100644 --- a/packages/material-ui/src/AvatarGroup/AvatarGroup.test.js +++ b/packages/material-ui/src/AvatarGroup/AvatarGroup.test.js @@ -1,25 +1,29 @@ import * as React from 'react'; import { expect } from 'chai'; -import { getClasses, createMount, describeConformance, createClientRender } from 'test/utils'; +import { createMount, describeConformanceV5, createClientRender } from 'test/utils'; import Avatar from '@material-ui/core/Avatar'; +import classes from './avatarGroupClasses'; import AvatarGroup from './AvatarGroup'; describe('', () => { const mount = createMount(); - let classes; const render = createClientRender(); - before(() => { - classes = getClasses(); - }); - - describeConformance(, () => ({ - classes, - inheritComponent: 'div', - mount, - refInstanceof: window.HTMLDivElement, - skip: ['componentProp'], - })); + describeConformanceV5( + +
+ , + () => ({ + classes, + inheritComponent: 'div', + mount, + muiName: 'MuiAvatarGroup', + refInstanceof: window.HTMLDivElement, + testDeepOverrides: { slotName: 'avatar', slotClassName: classes.avatar }, + testVariantProps: { max: 10, spacing: 'small', variant: 'square' }, + skip: ['componentsProp'], + }), + ); it('should display all the avatars', () => { const { container } = render( diff --git a/packages/material-ui/src/AvatarGroup/avatarGroupClasses.d.ts b/packages/material-ui/src/AvatarGroup/avatarGroupClasses.d.ts new file mode 100644 index 00000000000000..4efd6a90d75b93 --- /dev/null +++ b/packages/material-ui/src/AvatarGroup/avatarGroupClasses.d.ts @@ -0,0 +1,10 @@ +export interface AvatarGroupClasses { + root: string; + avatar: string; +} + +declare const avatarGroupClasses: AvatarGroupClasses; + +export function getAvatarGroupUtilityClass(slot: string): string; + +export default avatarGroupClasses; diff --git a/packages/material-ui/src/AvatarGroup/avatarGroupClasses.js b/packages/material-ui/src/AvatarGroup/avatarGroupClasses.js new file mode 100644 index 00000000000000..6a7507cb0589c2 --- /dev/null +++ b/packages/material-ui/src/AvatarGroup/avatarGroupClasses.js @@ -0,0 +1,9 @@ +import { generateUtilityClass, generateUtilityClasses } from '@material-ui/unstyled'; + +export function getAvatarGroupUtilityClass(slot) { + return generateUtilityClass('MuiAvatarGroup', slot); +} + +const avatarGroupClasses = generateUtilityClasses('MuiAvatarGroup', ['root', 'avatar']); + +export default avatarGroupClasses; diff --git a/packages/material-ui/src/AvatarGroup/index.d.ts b/packages/material-ui/src/AvatarGroup/index.d.ts index a691e2cb2eff92..1e0859d6667974 100644 --- a/packages/material-ui/src/AvatarGroup/index.d.ts +++ b/packages/material-ui/src/AvatarGroup/index.d.ts @@ -1,2 +1,4 @@ export { default } from './AvatarGroup'; export * from './AvatarGroup'; +export { default as avatarGroupClasses } from './avatarGroupClasses'; +export * from './avatarGroupClasses'; diff --git a/packages/material-ui/src/AvatarGroup/index.js b/packages/material-ui/src/AvatarGroup/index.js index 20c5443b297112..d3a39cb2673cf5 100644 --- a/packages/material-ui/src/AvatarGroup/index.js +++ b/packages/material-ui/src/AvatarGroup/index.js @@ -1 +1,3 @@ export { default } from './AvatarGroup'; +export { default as avatarGroupClasses } from './avatarGroupClasses'; +export * from './avatarGroupClasses';