diff --git a/docs/pages/api-docs/card-header.json b/docs/pages/api-docs/card-header.json index ec2825b28cf9eb..38d9e3e97f3c8a 100644 --- a/docs/pages/api-docs/card-header.json +++ b/docs/pages/api-docs/card-header.json @@ -7,6 +7,7 @@ "disableTypography": { "type": { "name": "bool" } }, "subheader": { "type": { "name": "node" } }, "subheaderTypographyProps": { "type": { "name": "object" } }, + "sx": { "type": { "name": "object" } }, "title": { "type": { "name": "node" } }, "titleTypographyProps": { "type": { "name": "object" } } }, @@ -21,6 +22,6 @@ "filename": "/packages/material-ui/src/CardHeader/CardHeader.js", "inheritance": null, "demos": "", - "styledComponent": false, + "styledComponent": true, "cssComponent": false } diff --git a/docs/translations/api-docs/card-header/card-header.json b/docs/translations/api-docs/card-header/card-header.json index 3c285917a4523d..d18739b747626c 100644 --- a/docs/translations/api-docs/card-header/card-header.json +++ b/docs/translations/api-docs/card-header/card-header.json @@ -8,6 +8,7 @@ "disableTypography": "If true, subheader and title won't be wrapped by a Typography component. This can be useful to render an alternative Typography variant by wrapping the title text, and optional subheader text with the Typography component.", "subheader": "The content of the component.", "subheaderTypographyProps": "These props will be forwarded to the subheader (as long as disableTypography is not true).", + "sx": "The system prop that allows defining system overrides as well as additional CSS styles. See the `sx` page for more details.", "title": "The content of the component.", "titleTypographyProps": "These props will be forwarded to the title (as long as disableTypography is not true)." }, diff --git a/packages/material-ui/src/CardHeader/CardHeader.d.ts b/packages/material-ui/src/CardHeader/CardHeader.d.ts index 490cae40d0888f..68404b6fdf9653 100644 --- a/packages/material-ui/src/CardHeader/CardHeader.d.ts +++ b/packages/material-ui/src/CardHeader/CardHeader.d.ts @@ -1,6 +1,8 @@ import * as React from 'react'; +import { SxProps } from '@material-ui/system'; import { TypographyProps } from '../Typography'; import { OverridableComponent, OverrideProps } from '../OverridableComponent'; +import { Theme } from '..'; export interface CardHeaderTypeMap< Props = {}, @@ -54,6 +56,10 @@ export interface CardHeaderTypeMap< SubheaderTypographyComponent, { component?: SubheaderTypographyComponent } >; + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx?: SxProps; /** * The content of the component. */ diff --git a/packages/material-ui/src/CardHeader/CardHeader.js b/packages/material-ui/src/CardHeader/CardHeader.js index f052113ba8e9d9..0f798a0e1a1f17 100644 --- a/packages/material-ui/src/CardHeader/CardHeader.js +++ b/packages/material-ui/src/CardHeader/CardHeader.js @@ -1,47 +1,102 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import clsx from 'clsx'; -import withStyles from '../styles/withStyles'; +import { deepmerge } from '@material-ui/utils'; +import { unstable_composeClasses as composeClasses } from '@material-ui/unstyled'; import Typography from '../Typography'; +import useThemeProps from '../styles/useThemeProps'; +import experimentalStyled from '../styles/experimentalStyled'; +import cardHeaderClasses, { getCardHeaderUtilityClass } from './cardHeaderClasses'; -export const styles = { +const overridesResolver = (props, styles) => { + return deepmerge(styles.root || {}, { + [`& .${cardHeaderClasses.avatar}`]: styles.avatar, + [`& .${cardHeaderClasses.action}`]: styles.action, + [`& .${cardHeaderClasses.content}`]: styles.content, + [`& .${cardHeaderClasses.title}`]: styles.title, + [`& .${cardHeaderClasses.subheader}`]: styles.subheader, + }); +}; + +const useUtilityClasses = (styleProps) => { + const { classes } = styleProps; + + const slots = { + root: ['root'], + avatar: ['avatar'], + action: ['action'], + content: ['content'], + title: ['title'], + subheader: ['subheader'], + }; + + return composeClasses(slots, getCardHeaderUtilityClass, classes); +}; + +const CardHeaderRoot = experimentalStyled( + 'div', + {}, + { + name: 'MuiCardHeader', + slot: 'Root', + overridesResolver, + }, +)({ /* Styles applied to the root element. */ - root: { - display: 'flex', - alignItems: 'center', - padding: 16, + display: 'flex', + alignItems: 'center', + padding: 16, +}); + +const CardHeaderAvatar = experimentalStyled( + 'div', + {}, + { + name: 'MuiCardHeader', + slot: 'Avatar', }, +)({ /* Styles applied to the avatar element. */ - avatar: { - display: 'flex', - flex: '0 0 auto', - marginRight: 16, + display: 'flex', + flex: '0 0 auto', + marginRight: 16, +}); + +const CardHeaderAction = experimentalStyled( + 'div', + {}, + { + name: 'MuiCardHeader', + slot: 'Action', }, +)({ /* Styles applied to the action element. */ - action: { - flex: '0 0 auto', - alignSelf: 'flex-start', - marginTop: -4, - marginRight: -8, - marginBottom: -4, + flex: '0 0 auto', + alignSelf: 'flex-start', + marginTop: -4, + marginRight: -8, + marginBottom: -4, +}); + +const CardHeaderContent = experimentalStyled( + 'div', + {}, + { + name: 'MuiCardHeader', + slot: 'Content', }, +)({ /* Styles applied to the content wrapper element. */ - content: { - flex: '1 1 auto', - }, - /* Styles applied to the title Typography element. */ - title: {}, - /* Styles applied to the subheader Typography element. */ - subheader: {}, -}; + flex: '1 1 auto', +}); -const CardHeader = React.forwardRef(function CardHeader(props, ref) { +const CardHeader = React.forwardRef(function CardHeader(inProps, ref) { + const props = useThemeProps({ props: inProps, name: 'MuiCardHeader' }); const { action, avatar, - classes, className, - component: Component = 'div', + component = 'div', disableTypography = false, subheader: subheaderProp, subheaderTypographyProps, @@ -50,6 +105,14 @@ const CardHeader = React.forwardRef(function CardHeader(props, ref) { ...other } = props; + const styleProps = { + ...props, + component, + disableTypography, + }; + + const classes = useUtilityClasses(styleProps); + let title = titleProp; if (title != null && title.type !== Typography && !disableTypography) { title = ( @@ -82,14 +145,29 @@ const CardHeader = React.forwardRef(function CardHeader(props, ref) { } return ( - - {avatar &&
{avatar}
} -
+ + {avatar && ( + + {avatar} + + )} + + {title} {subheader} -
- {action &&
{action}
} -
+ + {action && ( + + {action} + + )} + ); }); @@ -140,6 +218,10 @@ CardHeader.propTypes = { * (as long as disableTypography is not `true`). */ subheaderTypographyProps: PropTypes.object, + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx: PropTypes.object, /** * The content of the component. */ @@ -151,4 +233,4 @@ CardHeader.propTypes = { titleTypographyProps: PropTypes.object, }; -export default withStyles(styles, { name: 'MuiCardHeader' })(CardHeader); +export default CardHeader; diff --git a/packages/material-ui/src/CardHeader/CardHeader.test.js b/packages/material-ui/src/CardHeader/CardHeader.test.js index 8ed3b31bdbdb31..0a097c73787710 100644 --- a/packages/material-ui/src/CardHeader/CardHeader.test.js +++ b/packages/material-ui/src/CardHeader/CardHeader.test.js @@ -1,24 +1,24 @@ import * as React from 'react'; import { expect } from 'chai'; -import { getClasses, createMount, createClientRender, describeConformance } from 'test/utils'; +import { createMount, createClientRender, describeConformanceV5 } from 'test/utils'; import CardHeader from './CardHeader'; import { typographyClasses } from '../Typography'; +import classes from './cardHeaderClasses'; describe('', () => { const mount = createMount(); - let classes; const render = createClientRender(); - before(() => { - classes = getClasses(); - }); - - describeConformance(, () => ({ + describeConformanceV5(, () => ({ classes, inheritComponent: 'div', mount, + muiName: 'MuiCardHeader', refInstanceof: window.HTMLDivElement, + testDeepOverrides: { slotName: 'content', slotClassName: classes.content }, testComponentPropWith: 'span', + testVariantProps: { variant: 'foo' }, + skip: ['componentsProp'], })); describe('without an avatar', () => { diff --git a/packages/material-ui/src/CardHeader/cardHeaderClasses.d.ts b/packages/material-ui/src/CardHeader/cardHeaderClasses.d.ts new file mode 100644 index 00000000000000..0961c645ed6e54 --- /dev/null +++ b/packages/material-ui/src/CardHeader/cardHeaderClasses.d.ts @@ -0,0 +1,14 @@ +export interface CardHeaderClasses { + root: string; + avatar: string; + action: string; + content: string; + title: string; + subheader: string; +} + +declare const cardHeaderClasses: CardHeaderClasses; + +export function getCardHeaderUtilityClass(slot: string): string; + +export default cardHeaderClasses; diff --git a/packages/material-ui/src/CardHeader/cardHeaderClasses.js b/packages/material-ui/src/CardHeader/cardHeaderClasses.js new file mode 100644 index 00000000000000..3396d8dac56083 --- /dev/null +++ b/packages/material-ui/src/CardHeader/cardHeaderClasses.js @@ -0,0 +1,16 @@ +import { generateUtilityClass, generateUtilityClasses } from '@material-ui/unstyled'; + +export function getCardHeaderUtilityClass(slot) { + return generateUtilityClass('MuiCardHeader', slot); +} + +const cardHeaderClasses = generateUtilityClasses('MuiCardHeader', [ + 'root', + 'avatar', + 'action', + 'content', + 'title', + 'subheader', +]); + +export default cardHeaderClasses; diff --git a/packages/material-ui/src/CardHeader/index.d.ts b/packages/material-ui/src/CardHeader/index.d.ts index d989494e94ab60..c599bc0c40ab71 100644 --- a/packages/material-ui/src/CardHeader/index.d.ts +++ b/packages/material-ui/src/CardHeader/index.d.ts @@ -1,2 +1,5 @@ export { default } from './CardHeader'; export * from './CardHeader'; + +export { default as cardHeaderClasses } from './cardHeaderClasses'; +export * from './cardHeaderClasses'; diff --git a/packages/material-ui/src/CardHeader/index.js b/packages/material-ui/src/CardHeader/index.js index 9879cb20030b62..7f66c751b27942 100644 --- a/packages/material-ui/src/CardHeader/index.js +++ b/packages/material-ui/src/CardHeader/index.js @@ -1 +1,4 @@ export { default } from './CardHeader'; + +export { default as cardHeaderClasses } from './cardHeaderClasses'; +export * from './cardHeaderClasses';