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"
/>
-
+