diff --git a/docs/pages/api-docs/card-media.json b/docs/pages/api-docs/card-media.json index 6d6a6a01c294fc..528452a3976a72 100644 --- a/docs/pages/api-docs/card-media.json +++ b/docs/pages/api-docs/card-media.json @@ -4,7 +4,8 @@ "classes": { "type": { "name": "object" } }, "component": { "type": { "name": "elementType" } }, "image": { "type": { "name": "string" } }, - "src": { "type": { "name": "string" } } + "src": { "type": { "name": "string" } }, + "sx": { "type": { "name": "object" } } }, "name": "CardMedia", "styles": { "classes": ["root", "media", "img"], "globalClasses": {}, "name": "MuiCardMedia" }, @@ -13,6 +14,6 @@ "filename": "/packages/material-ui/src/CardMedia/CardMedia.js", "inheritance": null, "demos": "", - "styledComponent": false, + "styledComponent": true, "cssComponent": false } diff --git a/docs/translations/api-docs/card-media/card-media.json b/docs/translations/api-docs/card-media/card-media.json index 549ce3ec7b894f..92515bce5e0bfc 100644 --- a/docs/translations/api-docs/card-media/card-media.json +++ b/docs/translations/api-docs/card-media/card-media.json @@ -5,7 +5,8 @@ "classes": "Override or extend the styles applied to the component. See CSS API below for more details.", "component": "The component used for the root node. Either a string to use a HTML element or a component.", "image": "Image to be displayed as a background image. Either image or src prop must be specified. Note that caller must specify height otherwise the image will not be visible.", - "src": "An alias for image property. Available only with media components. Media components: video, audio, picture, iframe, img." + "src": "An alias for image property. Available only with media components. Media components: video, audio, picture, iframe, img.", + "sx": "The system prop that allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." }, "classDescriptions": { "root": { "description": "Styles applied to the root element." }, diff --git a/packages/material-ui/src/CardMedia/CardMedia.d.ts b/packages/material-ui/src/CardMedia/CardMedia.d.ts index 7d5a6299c820fb..3ec63fbf834e55 100644 --- a/packages/material-ui/src/CardMedia/CardMedia.d.ts +++ b/packages/material-ui/src/CardMedia/CardMedia.d.ts @@ -1,4 +1,6 @@ +import { SxProps } from '@material-ui/system'; import * as React from 'react'; +import { Theme } from '..'; import { OverridableComponent, OverrideProps } from '../OverridableComponent'; export interface CardMediaTypeMap { @@ -30,6 +32,10 @@ export interface CardMediaTypeMap { * Media components: `video`, `audio`, `picture`, `iframe`, `img`. */ src?: string; + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx?: SxProps; }; defaultComponent: D; } diff --git a/packages/material-ui/src/CardMedia/CardMedia.js b/packages/material-ui/src/CardMedia/CardMedia.js index d28619ff1980e4..8e6e2fbd35bceb 100644 --- a/packages/material-ui/src/CardMedia/CardMedia.js +++ b/packages/material-ui/src/CardMedia/CardMedia.js @@ -1,63 +1,89 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import clsx from 'clsx'; -import { chainPropTypes } from '@material-ui/utils'; -import withStyles from '../styles/withStyles'; +import { chainPropTypes, deepmerge } from '@material-ui/utils'; +import { unstable_composeClasses as composeClasses } from '@material-ui/unstyled'; +import useThemeProps from '../styles/useThemeProps'; +import experimentalStyled from '../styles/experimentalStyled'; +import { getCardMediaUtilityClass } from './cardMediaClasses'; -export const styles = { - /* Styles applied to the root element. */ - root: { - display: 'block', - backgroundSize: 'cover', - backgroundRepeat: 'no-repeat', - backgroundPosition: 'center', +const overridesResolver = (props, styles) => { + const { styleProps } = props; + const { isMediaComponent, isImageComponent } = styleProps; + + return deepmerge(styles.root || {}, { + ...(isMediaComponent && styles.media), + ...(isImageComponent && styles.img), + }); +}; + +const useUtilityClasses = (styleProps) => { + const { classes, isMediaComponent, isImageComponent } = styleProps; + + const slots = { + root: ['root', isMediaComponent && 'media', isImageComponent && 'img'], + }; + + return composeClasses(slots, getCardMediaUtilityClass, classes); +}; + +const CardMediaRoot = experimentalStyled( + 'div', + {}, + { + name: 'MuiCardMedia', + slot: 'Root', + overridesResolver, }, +)(({ styleProps }) => ({ + /* Styles applied to the root element. */ + display: 'block', + backgroundSize: 'cover', + backgroundRepeat: 'no-repeat', + backgroundPosition: 'center', /* Styles applied to the root element if `component="video, audio, picture, iframe, or img"`. */ - media: { + ...(styleProps.isMediaComponent && { width: '100%', - }, + }), /* Styles applied to the root element if `component="picture or img"`. */ - img: { + ...(styleProps.isImageComponent && { // ⚠️ object-fit is not supported by IE11. objectFit: 'cover', - }, -}; + }), +})); const MEDIA_COMPONENTS = ['video', 'audio', 'picture', 'iframe', 'img']; +const IMAGE_COMPONENTS = ['picture', 'img']; -const CardMedia = React.forwardRef(function CardMedia(props, ref) { - const { - children, - classes, - className, - component: Component = 'div', - image, - src, - style, - ...other - } = props; +const CardMedia = React.forwardRef(function CardMedia(inProps, ref) { + const props = useThemeProps({ props: inProps, name: 'MuiCardMedia' }); + const { children, className, component = 'div', image, src, style, ...other } = props; - const isMediaComponent = MEDIA_COMPONENTS.indexOf(Component) !== -1; + const isMediaComponent = MEDIA_COMPONENTS.indexOf(component) !== -1; const composedStyle = !isMediaComponent && image ? { backgroundImage: `url("${image}")`, ...style } : style; + const styleProps = { + ...props, + component, + isMediaComponent, + isImageComponent: IMAGE_COMPONENTS.indexOf(component) !== -1, + }; + + const classes = useUtilityClasses(styleProps); + return ( - {children} - + ); }); @@ -106,6 +132,10 @@ CardMedia.propTypes = { * @ignore */ style: PropTypes.object, + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx: PropTypes.object, }; -export default withStyles(styles, { name: 'MuiCardMedia' })(CardMedia); +export default CardMedia; diff --git a/packages/material-ui/src/CardMedia/CardMedia.test.js b/packages/material-ui/src/CardMedia/CardMedia.test.js index 2d6e8c93306dda..8cbba8334d5094 100644 --- a/packages/material-ui/src/CardMedia/CardMedia.test.js +++ b/packages/material-ui/src/CardMedia/CardMedia.test.js @@ -1,23 +1,23 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { expect } from 'chai'; -import { getClasses, createMount, createClientRender, describeConformance } from 'test/utils'; +import { createMount, createClientRender, describeConformanceV5 } from 'test/utils'; import CardMedia from './CardMedia'; +import classes from './cardMediaClasses'; describe('', () => { const mount = createMount(); - let classes; const render = createClientRender(); - before(() => { - classes = getClasses(); - }); - describeConformance(, () => ({ + describeConformanceV5(, () => ({ classes, inheritComponent: 'div', mount, + muiName: 'MuiCardMedia', refInstanceof: window.HTMLDivElement, testComponentPropWith: 'span', + testVariantProps: { variant: 'foo' }, + skip: ['componentsProp'], })); it('should have the backgroundImage specified', () => { @@ -79,7 +79,7 @@ describe('', () => { it('warns when neither `children`, nor `image`, nor `src`, nor `component` are provided', () => { expect(() => { - PropTypes.checkPropTypes(CardMedia.Naked.propTypes, { classes: {} }, 'prop', 'MockedName'); + PropTypes.checkPropTypes(CardMedia.propTypes, { classes: {} }, 'prop', 'MockedName'); }).toErrorDev( 'Material-UI: Either `children`, `image`, `src` or `component` prop must be specified.', ); diff --git a/packages/material-ui/src/CardMedia/cardMediaClasses.d.ts b/packages/material-ui/src/CardMedia/cardMediaClasses.d.ts new file mode 100644 index 00000000000000..4bf7f3f94b9a98 --- /dev/null +++ b/packages/material-ui/src/CardMedia/cardMediaClasses.d.ts @@ -0,0 +1,11 @@ +export interface CardMediaClasses { + root: string; + media: string; + img: string; +} + +declare const cardMediaClasses: CardMediaClasses; + +export function getCardMediaUtilityClass(slot: string): string; + +export default cardMediaClasses; diff --git a/packages/material-ui/src/CardMedia/cardMediaClasses.js b/packages/material-ui/src/CardMedia/cardMediaClasses.js new file mode 100644 index 00000000000000..5a1f4587377c8b --- /dev/null +++ b/packages/material-ui/src/CardMedia/cardMediaClasses.js @@ -0,0 +1,9 @@ +import { generateUtilityClass, generateUtilityClasses } from '@material-ui/unstyled'; + +export function getCardMediaUtilityClass(slot) { + return generateUtilityClass('MuiCardMedia', slot); +} + +const cardMediaClasses = generateUtilityClasses('MuiCardMedia', ['root', 'media', 'img']); + +export default cardMediaClasses; diff --git a/packages/material-ui/src/CardMedia/index.d.ts b/packages/material-ui/src/CardMedia/index.d.ts index e01400dab1b601..c61f21cc6b4d84 100644 --- a/packages/material-ui/src/CardMedia/index.d.ts +++ b/packages/material-ui/src/CardMedia/index.d.ts @@ -1,2 +1,5 @@ export { default } from './CardMedia'; export * from './CardMedia'; + +export { default as cardMediaClasses } from './cardMediaClasses'; +export * from './cardMediaClasses'; diff --git a/packages/material-ui/src/CardMedia/index.js b/packages/material-ui/src/CardMedia/index.js index 52c81c17a543a3..b20f3170c16b8a 100644 --- a/packages/material-ui/src/CardMedia/index.js +++ b/packages/material-ui/src/CardMedia/index.js @@ -1 +1,4 @@ export { default } from './CardMedia'; + +export { default as cardMediaClasses } from './cardMediaClasses'; +export * from './cardMediaClasses';