diff --git a/spec/pivotal-ui-react/helpers/helpers_spec.js b/spec/pivotal-ui-react/helpers/helpers_spec.js new file mode 100644 index 000000000..af5829935 --- /dev/null +++ b/spec/pivotal-ui-react/helpers/helpers_spec.js @@ -0,0 +1,138 @@ +require('../spec_helper'); +import {mergeProps} from '../../../src/pivotal-ui-react/helpers/helpers'; + +describe('mergeProps', function() { + let subject; + let defaultProps; + beforeEach(function() { + subject = {props: {}}; + }); + + function mergedProps() { + return mergeProps(subject.props, defaultProps); + } + + describe('when default props are provided', function() { + beforeEach(function() { + // provide default props + defaultProps = { + className: 'class1', + id: 'id1', + style: { + display: 'block', + background: 'blue' + }, + randomKey1: 'preamble' + }; + + }); + + describe('when overriding props are provided', function() { + beforeEach(function() { + subject.props = { + className: 'class2', + id: 'id2', + style: {display: 'inline'}, + randomKey2: 'stuff', + randomKey3: 'things' + }; + }); + + it('overrides the id', function() { + expect(mergedProps().id).toEqual(subject.props.id); + }); + + it('overrides keys under style that are provided', function() { + expect(mergedProps().style.display).toEqual(subject.props.style.display); + }); + + it('does not override keys under style that are not provided', function() { + expect(mergedProps().style.background).toEqual(defaultProps.style.background); + }); + + it('combines classNames', function() { + let classNames = mergedProps().className.split(/\s+/); + expect(classNames).toContain('class1'); + expect(classNames).toContain('class2'); + expect(classNames.length).toEqual(2); + }); + + it('combines the remainingProps', function() { + expect(mergedProps().randomKey1).toEqual(defaultProps.randomKey1); + expect(mergedProps().randomKey2).toEqual(subject.props.randomKey2); + expect(mergedProps().randomKey3).toEqual(subject.props.randomKey3); + }); + }); + + describe('when overriding props are not provided', function() { + it('uses the default id', function() { + expect(mergedProps().id).toEqual(defaultProps.id); + }); + + it('uses the default style', function() { + expect(mergedProps().style).toEqual(defaultProps.style); + }); + + it('uses the default className', function() { + expect(mergedProps().className).toEqual(defaultProps.className); + }); + + it('uses the default remainingProps', function() { + expect(mergedProps().randomKey1).toEqual(defaultProps.randomKey1); + }); + }); + }); + + describe('when default props are not provided', function() { + describe('when overriding props are provided', function() { + beforeEach(function() { + defaultProps = {}; + subject.props = { + className: 'class2', + id: 'id2', + style: {display: 'inline'}, + randomKey2: 'stuff', + randomKey3: 'things' + }; + }); + + it('uses the overridden id', function() { + expect(mergedProps().id).toEqual(subject.props.id); + }); + + it('uses the overridden style', function() { + expect(mergedProps().style).toEqual(subject.props.style); + }); + + + it('uses the overridden classNames', function() { + expect(mergedProps().className).toEqual(subject.props.className); + }); + + it('uses the overridden remainingProps', function() { + expect(mergedProps().randomKey2).toEqual(subject.props.randomKey2); + expect(mergedProps().randomKey3).toEqual(subject.props.randomKey3); + }); + }); + + describe('when overriding props are not provided', function() { + beforeEach(function() { + defaultProps = {}; + subject.props = {}; + }); + + it('uses an undefined id', function() { + expect(mergedProps().id).toEqual(undefined); + }); + + it('uses an empty style', function() { + expect(mergedProps().style).toEqual({}); + }); + + + it('uses an empty string for className', function() { + expect(mergedProps().className).toEqual(''); + }); + }); + }); +}); diff --git a/src/pivotal-ui-react/helpers/helpers.js b/src/pivotal-ui-react/helpers/helpers.js new file mode 100644 index 000000000..5f6133c31 --- /dev/null +++ b/src/pivotal-ui-react/helpers/helpers.js @@ -0,0 +1,49 @@ +import classnames from 'classnames'; + +/** + * @component mergeProps + * @description A helper function that merges default props and provided props + * + * @param reactInstanceProps properties passed into the component. Typically + * `this.props` + * + * @param defaultProps default values for the react component + * + * @return a merged hash of props, giving precedence to the `reactInstanceProps`. + * If `className` is defined by both sets of props, the resultant `className` + * will be a combination of the two. + * If `style` is defined by both, the resultant `style` hash will be a merge of + * the two style hashes, with precedence given to `reactInstanceProps`'s style. + * + * @example ```js + * var {mergeProps} = require('pui-react-helpers'); + * + * var Ribbon = React.createClass({ + * render() { + * var {children, ...others} = this.props; + * var props = mergeProps(others, {className: 'ribbon', style: {height: '50px', color: 'blue'}, id: 'default-ribbon-id'}); + * return
{children}
; + * } + * }); + * + * React.render(, myNode); + * // Resultant props: {className: 'ribbon my-ribbon', style: {height: '25px', color: 'blue'}, id: 'unique-ribbon-id'} + * ``` + */ + +export function mergeProps(reactInstanceProps, defaultProps) { + let {className, id, style, ...remainingProps} = reactInstanceProps; + let { + className: defaultClassName, + id: defaultId, + style: defaultStyle={}, + ...remainingDefaultProps + } = defaultProps; + + className = classnames(defaultClassName, className); + style = {...defaultStyle, ...style}; + id = id || defaultId; + remainingProps = {...remainingDefaultProps, ...remainingProps}; + + return {className, id, style, ...remainingProps}; +} diff --git a/src/pivotal-ui-react/helpers/package.json b/src/pivotal-ui-react/helpers/package.json new file mode 100644 index 000000000..60b580f6c --- /dev/null +++ b/src/pivotal-ui-react/helpers/package.json @@ -0,0 +1,7 @@ +{ + "version": "0.0.2", + "description": "A collection of helpers used by pivotal-ui-react components", + "dependencies": { + "classnames": "^2.1.2" + } +}