From 31cd78d5c10e78ae18e8c16f87735333ae522e67 Mon Sep 17 00:00:00 2001 From: Andrius Bil <> Date: Mon, 25 Sep 2017 10:46:29 +0300 Subject: [PATCH] [CardMedia] Add `component` property --- docs/src/pages/customization/api.md | 19 +++++++---- pages/api/card-media.md | 5 ++- pages/api/linear-progress.md | 2 +- pages/api/typography.md | 3 +- src/Card/CardMedia.d.ts | 4 ++- src/Card/CardMedia.js | 50 ++++++++++++++++++++++++++--- src/Card/CardMedia.spec.js | 22 +++++++++++++ test/typescript/components.spec.tsx | 2 +- 8 files changed, 91 insertions(+), 16 deletions(-) diff --git a/docs/src/pages/customization/api.md b/docs/src/pages/customization/api.md index e1feca6326d4ab..f1eee0189dac06 100644 --- a/docs/src/pages/customization/api.md +++ b/docs/src/pages/customization/api.md @@ -26,9 +26,11 @@ Aside from the above composition trade-off, we enforce the following rules: ### Spread - Undocumented properties supplied are spread to the root element. -Let's say you want to disable the ripples on the `MenuItem`. -You can take advantage of this behavior: +Undocumented properties supplied are spread to the root element. +For instance, the `className` property is applied to the root. + +Now, let's say you want to disable the ripples on the `MenuItem`. +You can take advantage of the spread behavior: ```jsx ``` @@ -47,13 +49,16 @@ All the components accept a `classes` property to customize the styles. Internal components have: - their own `xxxProps` property when users might need to tweak internal render method's components. For instance, we expose a `inputProps` and a `InputProps` properties. - their own `xxxClassName` property when `classes` isn't enough. -- their own flattened properties when they are key to the abstraction. For instance, we expose a `value` property. -- their own `xxxRef` property when user might need to perform so imperative action. -For instance, we expose a `inputRef` property. +- their own flattened properties when they are key to the abstraction. + For instance, we expose a `value` property. +- their own `xxxRef` property when user might need to perform imperative actions. + For instance, we expose a `inputRef` property to access the native `input` on the `Input` component. + You fill often find a `rootRef` property, this property is applied as a `ref` to the root element of the component ### Property naming -The name of the boolean properties should be chosen based on the default value. We are following the HTML specification. For instance, the `disabled` attribute on an input element. This choice allows the shorthand notation. +The name of the boolean properties should be chosen based on the default value. We are following the HTML specification. +For instance, the `disabled` attribute on an input element. This choice allows the shorthand notation. ### Controllable components diff --git a/pages/api/card-media.md b/pages/api/card-media.md index 5dc35de408082f..8702afd80e7599 100644 --- a/pages/api/card-media.md +++ b/pages/api/card-media.md @@ -8,7 +8,9 @@ | Name | Type | Default | Description | |:-----|:-----|:--------|:------------| | classes | Object | | Useful to extend the style applied to components. | -| image * | string | | Image to be displayed as a background image. Note that caller must specify height otherwise the image will not be visible. | +| component | ElementType | 'div' | Component for rendering image. | +| image | string | | 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 | string | | An alias for `image` property. Available only with media components. Media components: `video`, `audio`, `picture`, `iframe`, `img`. | Any other properties supplied will be [spread to the root element](/customization/api#spread). @@ -17,6 +19,7 @@ Any other properties supplied will be [spread to the root element](/customizatio You can override all the class names injected by Material-UI thanks to the `classes` property. This property accepts the following keys: - `root` +- `rootMedia` Have a look at [overriding with classes](/customization/overrides#overriding-with-classes) section for more detail. diff --git a/pages/api/linear-progress.md b/pages/api/linear-progress.md index ed09f3ebff72c9..8eb3f7b8b91a88 100644 --- a/pages/api/linear-progress.md +++ b/pages/api/linear-progress.md @@ -10,7 +10,7 @@ | classes | Object | | Useful to extend the style applied to components. | | color | union: 'primary'
 'accent'
| 'primary' | The color of the component. It's using the theme palette when that makes sense. | | mode | union: 'determinate'
 'indeterminate'
 'buffer'
 'query'
| 'indeterminate' | The mode of show your progress, indeterminate for when there is no value for progress. | -| value | number | 0 | The value of progress, only works in determinate and buffer mode. Value between 0 and 100. | +| value | number | | The value of progress, only works in determinate and buffer mode. Value between 0 and 100. | | valueBuffer | number | | The value of buffer, only works in buffer mode. Value between 0 and 100. | Any other properties supplied will be [spread to the root element](/customization/api#spread). diff --git a/pages/api/typography.md b/pages/api/typography.md index 1acb78bed1045a..e0af6f3c1379ba 100644 --- a/pages/api/typography.md +++ b/pages/api/typography.md @@ -10,7 +10,7 @@ | align | union: 'inherit', 'left', 'center', 'right', 'justify'
| 'inherit' | | | children | Node | | | | classes | Object | | Useful to extend the style applied to components. | -| color | union: 'inherit'
 'secondary'
 'accent'
 'default'
| 'default' | The color of the component. It's using the theme palette when that makes sense. | +| color | union: 'inherit', 'primary', 'secondary', 'accent', 'default'
| 'default' | The color of the component. It's using the theme palette when that makes sense. | | component | ElementType | | The component used for the root node. Either a string to use a DOM element or a component. By default we map the type to a good default headline component. | | gutterBottom | boolean | false | If `true`, the text will have a bottom margin. | | headlineMapping | signature | { display4: 'h1', display3: 'h1', display2: 'h1', display1: 'h1', headline: 'h1', title: 'h2', subheading: 'h3', body2: 'aside', body1: 'p',} | We are empirically mapping the type property to a range of different DOM element type. For instance, h1 to h6. If you wish to change that mapping, you can provide your own. Alternatively, you can use the `component` property. | @@ -44,6 +44,7 @@ This property accepts the following keys: - `gutterBottom` - `paragraph` - `colorInherit` +- `colorPrimary` - `colorSecondary` - `colorAccent` diff --git a/src/Card/CardMedia.d.ts b/src/Card/CardMedia.d.ts index 4d53a16d4703de..9f57f34f6fa871 100644 --- a/src/Card/CardMedia.d.ts +++ b/src/Card/CardMedia.d.ts @@ -2,7 +2,9 @@ import * as React from 'react'; import { StyledComponent } from '..'; export interface CardMediaProps extends React.HTMLAttributes { - image: string; + image?: string; + src?: string; + component?: React.ReactType; } declare const CardMedia: StyledComponent; diff --git a/src/Card/CardMedia.js b/src/Card/CardMedia.js index e6e5522a76a041..97ccebdaf25825 100644 --- a/src/Card/CardMedia.js +++ b/src/Card/CardMedia.js @@ -2,6 +2,8 @@ import React from 'react'; import classNames from 'classnames'; +import warning from 'warning'; +import type { ElementType } from 'react'; import withStyles from '../styles/withStyles'; export const styles = { @@ -10,10 +12,16 @@ export const styles = { backgroundRepeat: 'no-repeat', backgroundPosition: 'center', }, + rootMedia: { + width: '100%', + }, }; +const mediaComponents = ['video', 'audio', 'picture', 'iframe', 'img']; + type DefaultProps = { classes: Object, + component: ElementType, }; export type Props = { @@ -27,20 +35,54 @@ export type Props = { className?: string, /** * 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. */ - image: string, + image?: string, + /** + * An alias for `image` property. + * Available only with media components. + * Media components: `video`, `audio`, `picture`, `iframe`, `img`. + */ + src?: string, /** * @ignore */ style?: Object, + /** + * Component for rendering image. + */ + component?: ElementType, }; function CardMedia(props: DefaultProps & Props) { - const { classes, className, image, style, ...other } = props; - const composedStyle = { backgroundImage: `url(${image})`, ...style }; + const { classes, className, image, style, src, component: ComponentProp, ...other } = props; + + warning(image || src, 'Material-UI: either `image` or `src` property must be specified.'); + + const isMediaComponent = mediaComponents.indexOf(ComponentProp) !== -1; + const composedStyle = + !isMediaComponent && image ? { backgroundImage: `url(${image})`, ...style } : style; + const composedClassName = classNames( + { + [classes.root]: !isMediaComponent, + [classes.rootMedia]: isMediaComponent, + }, + className, + ); - return
; + return ( + + ); } +CardMedia.defaultProps = { + component: 'div', +}; + export default withStyles(styles, { name: 'MuiCardMedia' })(CardMedia); diff --git a/src/Card/CardMedia.spec.js b/src/Card/CardMedia.spec.js index 318b3a195efbe8..bd6787ab4d0a0a 100644 --- a/src/Card/CardMedia.spec.js +++ b/src/Card/CardMedia.spec.js @@ -46,4 +46,26 @@ describe('', () => { ); assert.strictEqual(wrapper.prop('style').backgroundImage, 'url(/bar.jpg)'); }); + + describe('prop: component', () => { + it('should render `img` component when `img` specified', () => { + const wrapper = shallow(); + assert.isTrue(wrapper.is('img')); + }); + + it('should have `src` prop when media component specified', () => { + const wrapper = shallow(); + assert.strictEqual(wrapper.prop('src'), '/foo.jpg'); + }); + + it('should not have default inline style when media component specified', () => { + const wrapper = shallow(); + assert.strictEqual(wrapper.prop('style'), undefined); + }); + + it('should not have `src` prop if not media component specified', () => { + const wrapper = shallow(); + assert.strictEqual(wrapper.prop('src'), undefined); + }); + }); }); diff --git a/test/typescript/components.spec.tsx b/test/typescript/components.spec.tsx index a5b354c09e5df3..901ca92eab4a66 100644 --- a/test/typescript/components.spec.tsx +++ b/test/typescript/components.spec.tsx @@ -158,7 +158,7 @@ const CardMediaTest = () => title="Shrimp and Chorizo Paella" subheader="September 14, 2016" /> - + Contemplative Reptile