diff --git a/docs.md b/docs.md index d39e5602..7d5662f0 100644 --- a/docs.md +++ b/docs.md @@ -370,6 +370,7 @@ Clicking an unselected checkbox adds its value to this array, and clicking a sel * `input` **[Object][148]** A `redux-form` [input][149] object * `meta` **[Object][148]** A `redux-form` [meta][150] object * `options` **[Array][146]** An array of checkbox values (strings, numbers, or key-value pairs) +* `checkboxInputProps` **[Object][148]** An object of key-value pairs representing props to pass down to all checkbox inputs (optional, default `{}`) ### Examples @@ -396,6 +397,32 @@ function TodoForm ({ handleSubmit, pristine, invalid, submitting }) { export default TodoForm ``` +```javascript +function TodoForm ({ handleSubmit, pristine, invalid, submitting }) { + return ( +
+ + + Submit + + + ) +} + +export default TodoForm +``` + ## CloudinaryFileInput A wrapper around a file input component (defaults to [FileInput][36]) that automatically uploads files to cloudinary via the [cloudinaryUploader][151] HOC. @@ -765,6 +792,7 @@ The value of the entire `RadioGroup` component is the value of the currently sel * `input` **[Object][148]** A `redux-form` [input][149] object * `meta` **[Object][148]** A `redux-form` [meta][150] object * `options` **[Array][146]** An array of radio button values (strings, numbers, booleans, or key-value pairs) +* `radioInputProps` **[Object][148]** An object of key-value pairs representing props to pass down to all radio inputs (optional, default `{}`) ### Examples @@ -791,6 +819,32 @@ function FavoriteFoodForm ({ handleSubmit, pristine, invalid, submitting }) { export default FavoriteFoodForm ``` +```javascript +function FavoriteFoodForm ({ handleSubmit, pristine, invalid, submitting }) { + return ( +
+ + + Submit + + + ) +} + +export default FavoriteFoodForm +``` + ## Select A select input that can be used in a `redux-form`-controlled form. diff --git a/jest.config.js b/jest.config.js index 3ab6d7f6..8720aa31 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,5 +1,8 @@ module.exports = { 'setupFiles': [ './test/setup.js', + ], + "watchPathIgnorePatterns": [ + "/node_modules", ] } \ No newline at end of file diff --git a/migration-guides/v7.0.0.md b/migration-guides/v7.0.0.md index fcf2f0ab..00a4c50d 100644 --- a/migration-guides/v7.0.0.md +++ b/migration-guides/v7.0.0.md @@ -19,7 +19,8 @@ This version contains the following breaking changes: 15. `` no longer overwrites the default modal or overlay class 16. `` targets #root instead of the body 17. ``s dependency on react-datepicker was upgraded from v1 to v4 -18. `@launchpadlab/lp-hoc` was removed as a dependency +18. `` and `` components no longer pass className prop to inputs +19. `@launchpadlab/lp-hoc` was removed as a dependency The required changes for each item are detailed below. @@ -397,7 +398,37 @@ More details: https://github.com/Hacker0x01/react-datepicker/releases. Of these breaking changes, the deprecation of `moment` has the highest likelihood of affecting your application. This will only impact your application if you referenced the `moment` library elsewhere in the codebase **without** installing the package directly as a dependency. -## 18. `@launchpadlab/lp-hoc` was removed as a dependency +## 18. `` and `` components no longer pass className prop to inputs +This change might affect the styles in your application. If you are passing in the `className` prop and your styles rely on targeting both the outer-most fieldset _and_ the inputs, you can either update your scss or you can pass in the same class name via `InputProps`. + +### Before +```jsx + +``` + +```scss +.custom-radio-group { + margin-bottom: 2px; +} +``` +### After +```scss +.custom-radio-group { + margin-bottom: 2px; + input { + margin-bottom: 2px; + } +} +``` +Or, +```jsx + +``` + +## 19. `@launchpadlab/lp-hoc` was removed as a dependency With the introduction of hooks, `lp-hoc` is no longer necessary and will soon be archived. To accommodate this, the dependency was removed from this library. This change only introduced **one** breaking change, which affected the `CloudinaryFileInput`. All other components that relied on `lp-hoc` utilities did not have any changes in interface or functionality but the utilities that these components relied on were removed as exports from this library, which projects may currently rely on. If you're using ``, you will need to pass in an additional required prop: `apiAdapter`. This adapter needs to respond to the `post` method and return a Promise. For typical LPL projects, this will be the `api` adapter which can be imported from `services/api.js`. @@ -408,4 +439,4 @@ If your project relies on any of the following utils imported from `@launchpad/l - sortablePropTypes - toggle - togglePropTypes -- onOutsideClick \ No newline at end of file +- onOutsideClick diff --git a/src/forms/inputs/checkbox-group.js b/src/forms/inputs/checkbox-group.js index f64f35d3..106423ce 100644 --- a/src/forms/inputs/checkbox-group.js +++ b/src/forms/inputs/checkbox-group.js @@ -1,4 +1,5 @@ import React from 'react' +import PropTypes from 'prop-types' import Checkbox from './checkbox' import { checkboxGroupPropTypes, @@ -27,6 +28,7 @@ import { addToArray, removeFromArray, serializeOptions, compose } from '../../ut * @param {Object} input - A `redux-form` [input](http://redux-form.com/6.5.0/docs/api/Field.md/#input-props) object * @param {Object} meta - A `redux-form` [meta](http://redux-form.com/6.5.0/docs/api/Field.md/#meta-props) object * @param {Array} options - An array of checkbox values (strings, numbers, or key-value pairs) + * @param {Object} [checkboxInputProps={}] - An object of key-value pairs representing props to pass down to all checkbox inputs * @example * * function TodoForm ({ handleSubmit, pristine, invalid, submitting }) { @@ -49,15 +51,44 @@ import { addToArray, removeFromArray, serializeOptions, compose } from '../../ut * } * * export default TodoForm + * + * @example + * function TodoForm ({ handleSubmit, pristine, invalid, submitting }) { + * return ( + *
+ * + * + * Submit + * + * + * ) + * } + * + * export default TodoForm */ const propTypes = { ...checkboxGroupPropTypes, + className: PropTypes.string, + checkboxInputProps: PropTypes.object, options: fieldOptionsType } const defaultProps = { - options: [] + className: 'CheckboxGroup', + checkboxInputProps: {}, + options: [], } function CheckboxGroupLegend ({ name, label }) { @@ -72,6 +103,8 @@ function CheckboxGroup (props) { input: { value, onChange, name }, meta, // eslint-disable-line no-unused-vars options, + className, + checkboxInputProps, ...rest } = omitLabelProps(props) const optionObjects = serializeOptions(options) @@ -85,8 +118,8 @@ function CheckboxGroup (props) { } return ( { @@ -102,7 +135,8 @@ function CheckboxGroup (props) { }, meta: {}, label: option.key, - ...rest + ...rest, + ...checkboxInputProps, }} /> ) diff --git a/src/forms/inputs/radio-group.js b/src/forms/inputs/radio-group.js index ec1d027b..c2493bbf 100644 --- a/src/forms/inputs/radio-group.js +++ b/src/forms/inputs/radio-group.js @@ -1,5 +1,5 @@ import React from 'react' -// import PropTypes from 'prop-types' +import PropTypes from 'prop-types' import { convertNameToLabel, radioGroupPropTypes, @@ -26,6 +26,7 @@ import { serializeOptions, filterInvalidDOMProps } from '../../utils' * @param {Object} input - A `redux-form` [input](http://redux-form.com/6.5.0/docs/api/Field.md/#input-props) object * @param {Object} meta - A `redux-form` [meta](http://redux-form.com/6.5.0/docs/api/Field.md/#meta-props) object * @param {Array} options - An array of radio button values (strings, numbers, booleans, or key-value pairs) + * @param {Object} [radioInputProps={}] - An object of key-value pairs representing props to pass down to all radio inputs * @example * * function FavoriteFoodForm ({ handleSubmit, pristine, invalid, submitting }) { @@ -48,15 +49,44 @@ import { serializeOptions, filterInvalidDOMProps } from '../../utils' * } * * export default FavoriteFoodForm + * + * @example + * function FavoriteFoodForm ({ handleSubmit, pristine, invalid, submitting }) { + * return ( + *
+ * + * + * Submit + * + * + * ) + * } + * + * export default FavoriteFoodForm */ const propTypes = { ...radioGroupPropTypes, - options: fieldOptionsType + className: PropTypes.string, + options: fieldOptionsType, + radioInputProps: PropTypes.object, } const defaultProps = { - options: [] + options: [], + className: 'RadioGroup', + radioInputProps: {}, } function RadioGroupLegend ({ label, name }) { @@ -99,12 +129,14 @@ function RadioGroup (props) { input: { value, onChange, name }, meta, // eslint-disable-line no-unused-vars options, + className, + radioInputProps, ...rest } = omitLabelProps(props) const optionObjects = serializeOptions(options) return ( @@ -124,7 +156,8 @@ function RadioGroup (props) { meta: {}, checked: value === option.value, label: option.key, - ...rest + ...rest, + ...radioInputProps, }} /> ) diff --git a/stories/forms/inputs/checkbox-group.story.js b/stories/forms/inputs/checkbox-group.story.js index 2c3c257d..162f6b6a 100644 --- a/stories/forms/inputs/checkbox-group.story.js +++ b/stories/forms/inputs/checkbox-group.story.js @@ -22,6 +22,12 @@ const options = [ { key: 'Third Option', value: '3' } ] +const SpecialLabel = ({ id, label }) => ( + + + +) + storiesOf('CheckboxGroup', module) .add('with default label', () => ( )) + .add('with input props specified', () => ( + + )) diff --git a/stories/forms/inputs/radio-group.story.js b/stories/forms/inputs/radio-group.story.js index e0ecb259..d5db61d8 100644 --- a/stories/forms/inputs/radio-group.story.js +++ b/stories/forms/inputs/radio-group.story.js @@ -22,6 +22,12 @@ const options = [ { key: 'Third Option', value: '3' } ] +const SpecialLabel = ({ id, label }) => ( + + + +) + storiesOf('RadioGroup', module) .add('with default label', () => ( + )) + .add('with input props specified', () => ( + )) \ No newline at end of file diff --git a/test/forms/inputs/checkbox-group.test.js b/test/forms/inputs/checkbox-group.test.js index 9d99481e..3e1d8141 100644 --- a/test/forms/inputs/checkbox-group.test.js +++ b/test/forms/inputs/checkbox-group.test.js @@ -68,3 +68,35 @@ test('CheckboxGroup has a legend with the group\'s label (when provided)', () => expect(legend).toBeTruthy() expect(legend.text()).toEqual('Checkbox Group') }) + +test('CheckboxGroup does not pass class to children', () => { + const props = { + input: { + name: 'testGroup', + value: '', + }, + meta: {}, + options: ['TOGGLED_OPTION'], + className:'custom-class' + } + const wrapper = mount() + expect(wrapper.find('.custom-class').hostNodes().length).toBe(1) +}) + +test('CheckboxGroup passes down props to children', () => { + const props = { + input: { + name: 'testGroup', + value: '', + }, + meta: {}, + options: ['TOGGLED_OPTION'], + className:'custom-group-class', + checkboxInputProps: { + className: 'custom-input-class', + } + } + const wrapper = mount() + expect(wrapper.find('input.custom-group-class').exists()).toBe(false) + expect(wrapper.find('input.custom-input-class').exists()).toBe(true) +}) \ No newline at end of file diff --git a/test/forms/inputs/radio-group.test.js b/test/forms/inputs/radio-group.test.js index dc8309c6..1a243e43 100644 --- a/test/forms/inputs/radio-group.test.js +++ b/test/forms/inputs/radio-group.test.js @@ -20,7 +20,7 @@ test('RadioGroup changes value when buttons are clicked', () => { expect(onChange).toHaveBeenCalledWith('Option 2') }) -test('A RadioGroup\'s inputs all have the same name', () => { +test('RadioGroup\'s inputs all have the same name', () => { const name = "sameName" const props = { input: { @@ -35,7 +35,7 @@ test('A RadioGroup\'s inputs all have the same name', () => { expect(wrapper.find('input').last().prop('name')).toEqual(name) }) -test('A RadioGroup input has a value that matches the corresponding option\'s value', () => { +test('RadioGroup input has a value that matches the corresponding option\'s value', () => { const props = { input: { name: 'test', @@ -49,7 +49,7 @@ test('A RadioGroup input has a value that matches the corresponding option\'s va expect(wrapper.find('input').last().prop('value')).toEqual('Option 2') }) -test('A RadioGroup has a legend with the group\'s name by default', () => { +test('RadioGroup has a legend with the group\'s name by default', () => { const name = "sameName" const props = { input: { @@ -65,7 +65,7 @@ test('A RadioGroup has a legend with the group\'s name by default', () => { expect(legend.text()).toEqual('Same Name') }) -test('A RadioGroup has a legend with the group\'s label (when provided)', () => { +test('RadioGroup has a legend with the group\'s label (when provided)', () => { const name = "sameName" const props = { input: { @@ -82,3 +82,36 @@ test('A RadioGroup has a legend with the group\'s label (when provided)', () => expect(legend.text()).toEqual('Different Name') }) +test('RadioGroup does not pass down class name', () => { + const onChange = jest.fn() + const props = { + input: { + name: 'test', + value: '', + onChange, + }, + meta: {}, + options: ['Option 1', 'Option 2'], + className: 'custom-radio' + } + const wrapper = mount() + expect(wrapper.find('.custom-radio').hostNodes().length).toEqual(1) +}) + +test('RadioGroup passes down props to children', () => { + const onChange = jest.fn() + const props = { + input: { + name: 'test', + value: '', + onChange, + }, + meta: {}, + options: ['Option 1', 'Option 2'], + className: 'custom-radio-group', + radioInputProps: { className: 'custom-radio-input' }, + } + const wrapper = mount() + expect(wrapper.find('input.custom-radio-group').exists()).toBe(false) + expect(wrapper.find('input.custom-radio-input').exists()).toBe(true) +})