diff --git a/src/factories.js b/src/factories.js new file mode 100644 index 0000000000..b627669b08 --- /dev/null +++ b/src/factories.js @@ -0,0 +1,64 @@ +import _ from 'lodash' +import cx from 'classnames' +import React, { isValidElement } from 'react' + +import Icon from './elements/Icon/Icon' +import Image from './elements/Image/Image' +import Label from './elements/Label/Label' + +/** + * Merges props and classNames. + * + * @param {object} props A props object + * @param {object} extraProps A props object + * @returns {object} A new props object + */ +const mergePropsAndClassName = (props, extraProps) => { + let className + if (_.has(props, 'className') || _.has(extraProps.className)) { + className = cx(props.className, extraProps.className) // eslint-disable-line react/prop-types + } + return { ...props, ...extraProps, className } +} + +/** + * A more robust React.createElement. + * It can create elements from primitive values. + * + * @param {function|string} Component A ReactClass or string + * @param {function} mapValueToProps A function that maps a primitive value to the Component props + * @param {string|object|function} val The value to create a ReactElement from + * @param {object} extraProps Additional props to add to the final ReactElement + * @returns {function|null} + */ +export function createShorthand(Component, mapValueToProps, val, extraProps = {}) { + // Clone ReactElements + if (isValidElement(val)) { + return React.cloneElement(val, mergePropsAndClassName(val.props, extraProps)) + } + + // Create ReactElements from props objects + if (_.isPlainObject(val)) { + return + } + + // Map values to props and create a ReactElement + if (!_.isNil(val)) { + return + } + + // React requires ReactElements or null be returned + return null +} + +function createShorthandFactory(Component, mapValueToProps) { + return _.partial(createShorthand, Component, mapValueToProps) +} + +// ---------------------------------------- +// Factories +// ---------------------------------------- +export const createIcon = createShorthandFactory(Icon, value => ({ name: value })) +export const createImage = createShorthandFactory(Image, value => ({ src: value })) +export const createImg = createShorthandFactory('img', value => ({ src: value })) +export const createLabel = createShorthandFactory(Label, value => ({ content: value })) diff --git a/src/factories/createFactory.js b/src/factories/createFactory.js deleted file mode 100644 index 9397e49ecf..0000000000 --- a/src/factories/createFactory.js +++ /dev/null @@ -1,47 +0,0 @@ -import _ from 'lodash' -import cx from 'classnames' -import React, { isValidElement } from 'react' - -/** - * The default CreateFactory mergeExtraProps function. Merges props and classNames. - * @param props - * @param extraProps - * @returns {{className: *}} - */ -const mergePropsAndClassName = (props, extraProps) => { - let className - if (_.has(props, 'className') || _.has(extraProps.className)) { - className = cx(props.className, extraProps.className) // eslint-disable-line react/prop-types - } - return { ...props, ...extraProps, className } -} - -/** - * Return a function that produces ReactElements. Similar to React.createFactory with some extras. - * If the returned factory is passed a ReactElement it will be cloned. - * If it passed null or undefined it will do nothing. - * @param {function|string} Component A ReactClass or string - * @param {function} mapValueToProps A function that maps a primitive value to the Component props - * @param {function} [mergeExtraProps=mergePropsAndClassName] - * @returns {function} - */ -const createFactory = (Component, mapValueToProps, mergeExtraProps = mergePropsAndClassName) => { - return function Factory(val, extraProps = {}) { - // Clone ReactElements - if (isValidElement(val)) { - return React.cloneElement(val, mergeExtraProps(val.props, extraProps)) - } - - // Create ReactElements from props objects - if (_.isPlainObject(val)) { - return - } - - // Map values to props and create an ReactElement - if (!_.isNil(val)) { - return - } - } -} - -export default createFactory diff --git a/src/factories/index.js b/src/factories/index.js deleted file mode 100644 index 1df724108f..0000000000 --- a/src/factories/index.js +++ /dev/null @@ -1,41 +0,0 @@ -import createFactory from './createFactory' - -import Icon from '../elements/Icon/Icon' -import Image from '../elements/Image/Image' -import Label from '../elements/Label/Label' - -/** - * Returns an Icon element from an icon name, ReactElement, or props object. - * @type {function} - * @param {string|ReactElement|object} val The value to render. - * @param {object} [props = {}] Optional additional props. - * @returns {ReactElement|undefined} - */ -export const createIcon = createFactory(Icon, value => ({ name: value })) - -/** - * Returns an Image element from an img src, ReactElement, or props object. - * @type {function} - * @param {string|ReactElement|object} val The value to render. - * @param {object} [props = {}] Optional additional props. - * @returns {ReactElement|undefined} - */ -export const createImage = createFactory(Image, value => ({ src: value })) - -/** - * Returns an img element from an img src, ReactElement, or props object. - * @type {function} - * @param {string|ReactElement|object} val The value to render. - * @param {object} [props = {}] Optional additional props. - * @returns {ReactElement|undefined} - */ -export const createImg = createFactory('img', value => ({ src: value })) - -/** - * Returns a Label element from label text, ReactElement, or props object. - * @type {function} - * @param {string|ReactElement|object} val The value to render. - * @param {object} [props = {}] Optional additional props. - * @returns {ReactElement|undefined} - */ -export const createLabel = createFactory(Label, value => ({ content: value }))