From 3866c073f840e3184ce304a4568b057b46b1a21f Mon Sep 17 00:00:00 2001 From: Chathan Driehuys Date: Fri, 13 Oct 2017 15:52:29 -0400 Subject: [PATCH 1/3] Add custom form input component. --- src/components/Input.jsx | 20 ++++++++++ src/components/RegistrationForm.jsx | 5 ++- src/components/__tests__/Input.spec.jsx | 39 +++++++++++++++++++ .../__tests__/RegistrationForm.spec.jsx | 4 +- 4 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 src/components/Input.jsx create mode 100644 src/components/__tests__/Input.spec.jsx diff --git a/src/components/Input.jsx b/src/components/Input.jsx new file mode 100644 index 0000000..acc616f --- /dev/null +++ b/src/components/Input.jsx @@ -0,0 +1,20 @@ +import PropTypes from 'prop-types'; +import React from 'react'; + + +/** + * Component for accepting text input. + * + * @param {string} props.name The name to give the input field. + * @param {...object} [props.extraProps] Any additional props to pass to the input field. + */ +const Input = ({ ...extraProps }) => ( + +); + +Input.propTypes = { + name: PropTypes.string.isRequired, +}; + + +export default Input; diff --git a/src/components/RegistrationForm.jsx b/src/components/RegistrationForm.jsx index 76c2f69..e29208c 100644 --- a/src/components/RegistrationForm.jsx +++ b/src/components/RegistrationForm.jsx @@ -4,6 +4,7 @@ import { connect } from 'react-redux'; import { Redirect } from 'react-router-dom'; import { register } from '../actionCreators'; +import Input from './Input'; import { getRegistrationComplete, getRegistrationErrors, getRegistrationLoading } from '../selectors'; @@ -40,7 +41,7 @@ export class RegistrationForm extends React.Component { return (
- - { + const props = { + ...rest, + name, + }; + const wrapper = shallow(); + + return { + props, + wrapper, + }; +}; + + +describe('Input Component', () => { + it('should render a standard text input', () => { + const { props, wrapper } = setup(); + const input = wrapper.find('input'); + + expect(input).toHaveLength(1); + expect(input.prop('name')).toBe(props.name); + }); + + it('should render any additional props passed to it', () => { + const extraProps = { foo: 'bar', bar: 'baz' }; + const { wrapper } = setup(extraProps); + const input = wrapper.find('input'); + + Object.keys(extraProps).forEach((key) => { + expect(input.prop(key)).toBe(extraProps[key]); + }); + }); +}); diff --git a/src/components/__tests__/RegistrationForm.spec.jsx b/src/components/__tests__/RegistrationForm.spec.jsx index c083934..ac25971 100644 --- a/src/components/__tests__/RegistrationForm.spec.jsx +++ b/src/components/__tests__/RegistrationForm.spec.jsx @@ -101,7 +101,7 @@ describe('RegistrationForm', () => { describe('input handlers', () => { it('should have a handler to update email state', () => { const { wrapper } = setup(); - const emailWrapper = wrapper.find('input[name="email"]'); + const emailWrapper = wrapper.find('Input[name="email"]'); const newEmail = 'test@example.com'; @@ -117,7 +117,7 @@ describe('RegistrationForm', () => { it('should have a handler to update password state', () => { const { wrapper } = setup(); - const passwordWrapper = wrapper.find('input[name="password"]'); + const passwordWrapper = wrapper.find('Input[name="password"]'); const newPassword = 'password'; From ec850a102ad5d99cfff16e4a0301e9910afa2492 Mon Sep 17 00:00:00 2001 From: Chathan Driehuys Date: Fri, 13 Oct 2017 17:08:42 -0400 Subject: [PATCH 2/3] Add form field component. A field encapsulates a label and text input for a form. --- src/components/Field.jsx | 43 ++++++++++ src/components/Input.jsx | 4 +- src/components/RegistrationForm.jsx | 27 ++---- src/components/__tests__/Field.spec.jsx | 85 +++++++++++++++++++ .../__tests__/RegistrationForm.spec.jsx | 73 +++++++--------- 5 files changed, 170 insertions(+), 62 deletions(-) create mode 100644 src/components/Field.jsx create mode 100644 src/components/__tests__/Field.spec.jsx diff --git a/src/components/Field.jsx b/src/components/Field.jsx new file mode 100644 index 0000000..86745f9 --- /dev/null +++ b/src/components/Field.jsx @@ -0,0 +1,43 @@ +import PropTypes from 'prop-types'; +import React from 'react'; + +import Input from './Input'; + + +/** + * A wrapper around a form input and label. + * + * @param {string} props.label A descriptive label for the field. + * @param {string} props.name The input field's name. + * @param {string[]} [props.errors] A list of errors for the field. + * @param {string} [props.id] The ID to give the input. Defaults to the input's name. + * @param {...object} [props.extraProps] Additional props to pass to the input component. + */ +const Field = ({ errors, id, label, name, ...extraProps }) => ( +
+ + + {errors.length > 0 && ( +
    + {errors.map(error => ( +
  • {error}
  • + ))} +
+ )} +
+); + +Field.defaultProps = { + errors: [], + id: '', +}; + +Field.propTypes = { + errors: PropTypes.arrayOf(PropTypes.string), + id: PropTypes.string, + label: PropTypes.string.isRequired, + name: PropTypes.string.isRequired, +}; + + +export default Field; diff --git a/src/components/Input.jsx b/src/components/Input.jsx index acc616f..8f21db4 100644 --- a/src/components/Input.jsx +++ b/src/components/Input.jsx @@ -8,8 +8,8 @@ import React from 'react'; * @param {string} props.name The name to give the input field. * @param {...object} [props.extraProps] Any additional props to pass to the input field. */ -const Input = ({ ...extraProps }) => ( - +const Input = ({ name, ...extraProps }) => ( + ); Input.propTypes = { diff --git a/src/components/RegistrationForm.jsx b/src/components/RegistrationForm.jsx index e29208c..5cd0544 100644 --- a/src/components/RegistrationForm.jsx +++ b/src/components/RegistrationForm.jsx @@ -4,7 +4,7 @@ import { connect } from 'react-redux'; import { Redirect } from 'react-router-dom'; import { register } from '../actionCreators'; -import Input from './Input'; +import Field from './Field'; import { getRegistrationComplete, getRegistrationErrors, getRegistrationLoading } from '../selectors'; @@ -40,33 +40,24 @@ export class RegistrationForm extends React.Component { return ( - - -
    - {this.props.errors.email && this.props.errors.email.map(error => ( -
  • {error}
  • - ))} -
- - - -
    - {this.props.errors.password && this.props.errors.password.map(error => ( -
  • {error}
  • - ))} -