Repository: react-materialize/react-materialize
Pull request #701 (2018-11-23 20:32:32)

Slide component enhancement
# Description

Refactoring the `Slide` component into a functional component, adding a `Caption` component.

This leads to a much more semantic and flexible way to define complex captions.

For example you can now add more component to a `Caption`, like a `Button`:
```javascript
<Slider className="white" options={options}>
     <Slide image={FirstSlide}>
          <Caption placement="left">
               <h4>This is our big Tagline!</h4>
               <h5 className="light grey-text text-lighten-3">
                     Here&apos;s our small slogan.
                </h5>
                <Link to="/whatever">
                     <Button className="hoverable" large>
                          Read More
                     </Button>
                </Link>
           </Caption>
    </Slide>
</Slider>
```
You can now use a [`picture`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture) html element for [art direction](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images#Art_direction) or simply to provide different version of your images (WebP images, JPEG...)

For example 

```javascript

import {
     firstSlideWebP,
     firstSlideJpg,
} from '../../assets/img'

const FirstSlide = (
  <picture>
    <source srcSet={firstSlideWebP} type="image/webp" />
    <source srcSet={firstSlideJpg} type="image/jpg" />
    <img src={firstSlideJpg} alt="" />
  </picture>
)

<Slider className="white" options={options}>
     <Slide image={FirstSlide}>
          <Caption placement="left">
               <h4>This is our big Tagline!</h4>
               <h5 className="light grey-text text-lighten-3">
                     Here&apos;s our small slogan.
                </h5>
                <Link to="/whatever">
                     <Button className="hoverable" large>
                          Read More
                     </Button>
                </Link>
           </Caption>
    </Slide>
</Slider>
```

P.S: As noted also on [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture#Usage_notes) you might need to use `object-fit` and/or `object-position` to correctly display the `img`.
 
P.P.S: You may wish to take a look at [react-responsive-picture](https://github.com/braposo/react-responsive-picture) for the last use case

## Type of change

- [x] New feature (non-breaking change which adds functionality)
- [x] This change requires a documentation update

# How Has This Been Tested?

Add test for `Caption` element.
Updated every snapshots.

# Checklist:

- [x] I have commented my code, particularly in hard-to-understand areas
- [x] My changes generate no new warnings
- [x] I have not generated a new package version. (the maintainers will handle that)



Patch:
diff --git a/src/Caption.js b/src/Caption.js
new file mode 100644
index 0000000..bad99f7
--- /dev/null
+++ b/src/Caption.js
@@ -0,0 +1,28 @@
+import React from 'react';
+import cx from 'classnames';
+import PropTypes from 'prop-types';
+import constants from './constants';
+
+const Caption = ({ className, placement, children, ...props }) => {
+  return (
+    <div className={cx('caption', `${placement}-align`, className)} {...props}>
+      {children}
+    </div>
+  );
+};
+
+Caption.propTypes = {
+  children: PropTypes.node,
+  className: PropTypes.string,
+  /**
+   * Placement of the caption
+   * @default 'center'
+   */
+  placement: PropTypes.oneOf(constants.PLACEMENTS)
+};
+
+Caption.defaultProps = {
+  placement: 'center'
+};
+
+export default Caption;
diff --git a/src/Slide.js b/src/Slide.js
index 18f7729..5b676f9 100644
--- a/src/Slide.js
+++ b/src/Slide.js
@@ -1,64 +1,20 @@
-import React, { Component } from 'react';
+import React from 'react';
 import PropTypes from 'prop-types';
-import cx from 'classnames';
-import constants from './constants';
 
-class Slide extends Component {
-  constructor(props) {
-    super(props);
-    this.renderCaption = this.renderCaption.bind(this);
-  }
-
-  render() {
-    return (
-      <li>
-        <img src={this.props.src} />
-        {this.renderCaption()}
-      </li>
-    );
-  }
-
-  renderCaption() {
-    const { title, className, placement, children, ...props } = this.props;
-
-    delete props.placement;
-
-    const classes = {
-      caption: true,
-      [placement + '-align']: placement
-    };
-
-    if (typeof title !== 'undefined' || typeof children !== 'undefined') {
-      return (
-        <div className={cx(classes)} {...props}>
-          <h3>{title}</h3>
-          <h5 className={className}>{children}</h5>
-        </div>
-      );
-    } else return null;
-  }
-}
+const Slide = ({ image, children }) => (
+  <li>
+    {image}
+    {children}
+  </li>
+);
 
 Slide.propTypes = {
-  /**
-   * Aliment of the caption
-   * @default 'center'
-   */
-  placement: PropTypes.oneOf(constants.PLACEMENTS),
   className: PropTypes.string,
   children: PropTypes.node,
   /**
-   * The tagline of the caption
-   */
-  title: PropTypes.string,
-  /**
-   * The path of the background image
+   * The image that will be used in the Slide
    */
-  src: PropTypes.string.isRequired
-};
-
-Slide.defaultProps = {
-  placement: 'center'
+  image: PropTypes.node.isRequired
 };
 
 export default Slide;


Test patch:
diff --git a/test/Caption.spec.js b/test/Caption.spec.js
new file mode 100644
index 0000000..408e7c1
--- /dev/null
+++ b/test/Caption.spec.js
@@ -0,0 +1,18 @@
+import React from 'react';
+import { shallow } from 'enzyme';
+import Caption from '../src/Caption';
+
+describe('<Caption />', () => {
+  let wrapper = shallow(
+    <Caption placement="left">
+      <h3>This is our big Tagline!</h3>
+      <h5 className="light grey-text text-lighten-3">
+        Here's our small slogan.
+      </h5>
+    </Caption>
+  );
+
+  test('renders', () => {
+    expect(wrapper).toMatchSnapshot();
+  });
+});
diff --git a/test/Slide.spec.js b/test/Slide.spec.js
index 7b9697e..d0f39bd 100644
--- a/test/Slide.spec.js
+++ b/test/Slide.spec.js
@@ -3,7 +3,7 @@ import { shallow } from 'enzyme';
 import Slide from '../src/Slide';
 
 describe('<Slide />', () => {
-  const wrapper = shallow(<Slide src="#" />);
+  let wrapper = shallow(<Slide image={<img src="#" />} />);
 
   test('renders', () => {
     expect(wrapper).toMatchSnapshot();
@@ -11,6 +11,29 @@ describe('<Slide />', () => {
 
   test('should render a Slide', () => {
     expect(wrapper.childAt(0).is('img')).toEqual(true);
-    expect(wrapper.childAt(0).is({ src: '#' })).toEqual(true);
+    expect(wrapper.childAt(0).prop('src')).toEqual('#');
+  });
+
+  test('can render a picture element', () => {
+    const pictureTag = (
+      <picture>
+        <source srcSet="#" type="image/webp" />
+        <source srcSet="#" type="image/jpg" />
+        <img src="#" alt="" />
+      </picture>
+    );
+
+    wrapper = shallow(<Slide image={pictureTag} />);
+
+    expect(wrapper).toMatchSnapshot();
+  });
+
+  test('can render a custom caption', () => {
+    wrapper = shallow(
+      <Slide image={<img src="#" />}>
+        <h2>test</h2>
+      </Slide>
+    );
+    expect(wrapper.childAt(1).is('h2')).toEqual(true);
   });
 });
diff --git a/test/__snapshots__/Caption.spec.js.snap b/test/__snapshots__/Caption.spec.js.snap
new file mode 100644
index 0000000..4e0e8d6
--- /dev/null
+++ b/test/__snapshots__/Caption.spec.js.snap
@@ -0,0 +1,16 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`<Caption /> renders 1`] = `
+<div
+  className="caption left-align"
+>
+  <h3>
+    This is our big Tagline!
+  </h3>
+  <h5
+    className="light grey-text text-lighten-3"
+  >
+    Here's our small slogan.
+  </h5>
+</div>
+`;
diff --git a/test/__snapshots__/Slide.spec.js.snap b/test/__snapshots__/Slide.spec.js.snap
index 20e51cc..b664bb0 100644
--- a/test/__snapshots__/Slide.spec.js.snap
+++ b/test/__snapshots__/Slide.spec.js.snap
@@ -1,5 +1,24 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
+exports[`<Slide /> can render a picture element 1`] = `
+<li>
+  <picture>
+    <source
+      srcSet="#"
+      type="image/webp"
+    />
+    <source
+      srcSet="#"
+      type="image/jpg"
+    />
+    <img
+      alt=""
+      src="#"
+    />
+  </picture>
+</li>
+`;
+
 exports[`<Slide /> renders 1`] = `
 <li>
   <img