diff --git a/components/Forms/BaseInputField.jsx b/components/Forms/BaseInputField.jsx deleted file mode 100644 index 1f8a1fe6a..000000000 --- a/components/Forms/BaseInputField.jsx +++ /dev/null @@ -1,70 +0,0 @@ -'use strict' - -import { Component, PropTypes } from 'react' -import _ from 'lodash' - -/** - * Base Input field implementation - */ -class BaseInputField extends Component { - - constructor(props) { - super(props) - this.onChange = this.onChange.bind(this) - this.init = this.init.bind(this) - } - - componentWillMount() { - this.init(this.props) - } - - componentWillReceiveProps(nextProps) { - this.init(nextProps) - } - - init(props) { - this.setState({ - dirty: false, - valid: true, // TODO perform validation on component load - value: props.value - }) - } - - shouldComponentUpdate(nextProps, nextState) { - return !_.isEqual(nextState, this.state) - } - - onChange(event) { - const { onFieldChange, validateField, name, validations} = this.props - const newValue = event.target.value - // validate - const results = validateField(newValue, validations) - // TODO - compare against this.props.value to see if value has infact changed - this.setState({ - value: newValue, - dirty: this.state.value !== newValue, // - valid: !results.hasError, - errorMessage: results.errorMessage - }) - onFieldChange(name, newValue, !results.hasError) - } -} - -BaseInputField.propTypes = { - name: PropTypes.string.isRequired, - placeholder: PropTypes.string, - onFieldChange: PropTypes.func.isRequired, - validateField: PropTypes.func.isRequired, - value: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.number - ]).isRequired -} -BaseInputField.defaultProps = { - value: '', - // onFieldChange: () => {}, - validateField: () => { return { hasError: false, errorMessage: null }} -} - - -export default BaseInputField diff --git a/components/Forms/CheckboxGroupInput.jsx b/components/Forms/CheckboxGroupInput.jsx deleted file mode 100644 index 964d5161d..000000000 --- a/components/Forms/CheckboxGroupInput.jsx +++ /dev/null @@ -1,58 +0,0 @@ -'use strict' - -import React, { PropTypes } from 'react' -import _ from 'lodash' -import classNames from 'classnames' -import BaseInputField from './BaseInputField' - -const CheckboxInput = ({index, name, label, value, selectedValue}) => { - const id = [name, 'option', index].join('-') - return ( -
-
- -
- -
- ) -} - -class CheckboxGroupInput extends BaseInputField { - constructor(props) { - super(props) - this.onChange = this.onChange.bind(this) - } - render() { - const { label, name, wrapperClass} = this.props - const { value, valid, dirty, errorMessage } = this.state - const hasError = dirty && !valid - const renderOption = (opt, idx) => { - return ( - - ) - } - const wrapperClasses = classNames(wrapperClass, 'checkbox-group-input') - return ( -
- -
{this.props.options.map(renderOption)}
- { hasError ? (

{errorMessage}

) : null} -
- ) - } -} -CheckboxGroupInput.displayName = 'CheckboxGroupInputField' -CheckboxGroupInput.propTypes = _.assign({}, CheckboxGroupInput.propTypes, { - options: PropTypes.arrayOf(PropTypes.object.isRequired).isRequired -}) - -export default CheckboxGroupInput diff --git a/components/Forms/CheckboxInput.jsx b/components/Forms/CheckboxInput.jsx deleted file mode 100644 index 8240907aa..000000000 --- a/components/Forms/CheckboxInput.jsx +++ /dev/null @@ -1,30 +0,0 @@ -'use strict' - -import React, { PropTypes } from 'react' -import BaseInputField from './BaseInputField' -import _ from 'lodash' - -class CheckboxInput extends BaseInputField { - render() { - const { name, index, label, ...rest } = this.props - const id = [name, 'option', index].join('-') - return ( -
-
- -
- -
- ) - } -} - - -CheckboxInput.displayName = 'CheckboxInputField' -CheckboxInput.propTypes = _.assign({}, CheckboxInput.propTypes, { - label: PropTypes.string.isRequired, - value: PropTypes.any.isRequired -}) - -export default CheckboxInput diff --git a/components/Forms/Fields.jsx b/components/Forms/Fields.jsx deleted file mode 100644 index 1d2175574..000000000 --- a/components/Forms/Fields.jsx +++ /dev/null @@ -1,25 +0,0 @@ -'use strict' - -import SubmitButton from './SubmitButton' -import TextInput from './TextInput' -import TextareaInput from './TextareaInput' -import RadioGroupInput from './RadioGroupInput' -import RadioButton from './RadioButton' -import CheckboxInput from './CheckboxInput' -import CheckboxGroupInput from './CheckboxGroupInput' -import SliderRadioGroupInput from './SliderRadioGroupInput' -import TiledCheckboxInput from './TiledCheckboxInput' - -require('./FormFields.scss') - -export default { - SubmitButton, - TextInput, - TextareaInput, - RadioGroupInput, - RadioButton, - CheckboxInput, - CheckboxGroupInput, - SliderRadioGroupInput, - TiledCheckboxInput -} diff --git a/components/Forms/Form.jsx b/components/Forms/Form.jsx deleted file mode 100644 index 13080bb17..000000000 --- a/components/Forms/Form.jsx +++ /dev/null @@ -1,177 +0,0 @@ -'use strict' - -import React, { PropTypes } from 'react' -import _ from 'lodash' - - -function setValue(path, val, obj) { - const fields = path.split('.') - let result = obj - for (let i = 0, n = fields.length; i < n && result !== undefined; i++) { - const field = fields[i] - if (i === n - 1) { - result[field] = val - } else { - if (typeof result[field] === 'undefined' || !_.isObject(result[field])) { - result[field] = {} - } - result = result[field] - } - } -} - -class Form extends React.Component { - - constructor(props) { - super(props) - this.init = this.init.bind(this) - } - - componentWillMount() { - this.init(this.props) - } - - componentWillReceiveProps(nextProps) { - if (nextProps.resetOnRender) { - this.init(nextProps) - } - } - - init(props) { - const formValue = _.assign({}, props.initialValue || {}) - this.setState({ - fieldValidity: {}, // TODO perform initial validations - dirty: false, - valid: false, - formValue - }) - } - - - updateData(data) { - // TODO should re-perform validations - this.setState(_.assign({}, this.state, { dirty: true, formValue: data})) - } - - onSubmit(event) { - event.preventDefault() - this.props.onSubmit(this.state.formValue) - } - - /** - * perform validations corresponding to field defined in schema - * @param {string} fieldName Name of the field - * @param {string} fieldValue new value - * @return {object} object with hasError(boolean) and error message(strng) fields - */ - validateField(fieldValue, validations) { - const errors = _.map(validations, (v) => { - // message if invalid else null - return v[0](fieldValue) ? false : v[1] - }) - const hasError = !_.isEmpty(validations) && _.every(errors) - const errorMessage = hasError ? errors.join(' ') : null - return { hasError, errorMessage } - } - - /** - * function that updates component state with user entered value - * after validating the input - * @param {string} fieldName name of the field updated - * @param {string} fieldValue updated field value - */ - handleFieldChange(fieldName, fieldValue, isValid) { - // validate - const newState = _.assign({}, this.state) - // setting the form value - setValue(fieldName, fieldValue, newState.formValue) - // update field validity - newState.fieldValidity[fieldName] = isValid - _.merge(newState, { - valid: _.every(newState.fieldValidity), - dirty: true - }) - this.setState(newState) - // call onChange callback if provided - if (this.props.onChange) - this.props.onChange(fieldName, fieldValue, newState) - } - - recursiveCloneChildren(children, disableOnPristine) { - return React.Children.map(children, child => { - let childProps = {} - // string has no props - if (child.props) { - // restrict applying additional properties to "registered field types" - const childType = this.getChildType(child) - - switch(childType) { - case 'InputField': { - // provide additional properties - const value = _.isEmpty(child.props.value) - ? _.get(this.state.formValue, child.props.name, '') - : child.props.value - childProps = { - value: value ? value : '', - validateField: this.validateField.bind(this), - onFieldChange: this.handleFieldChange.bind(this) - } - break - } - case 'SubmitButton': { - // (isDirty && !isValid) || !isDirty - const disableForNonDirty = !this.state.dirty && disableOnPristine - childProps = { - disabled: (!this.state.valid && this.state.dirty) || disableForNonDirty, - onClick: this.onSubmit.bind(this) - } - break - } - default: - childProps = {} - } - childProps.children = this.recursiveCloneChildren(child.props.children, disableOnPristine) - if (_.isEmpty(childProps)) { - return child - } else { - return React.cloneElement(child, childProps) - } - } - return child - }) - } - - render() { - const { disableOnPristine } = this.props - return (
{this.recursiveCloneChildren(this.props.children, disableOnPristine)}
) - } - - /** - * Helper function to help identify form field. - * Returns true if component type is function & displayName constains "Field" - */ - getChildType(elem) { - if (typeof elem.type === 'function') { - const displayName = _.get(elem, 'type.displayName', '') - if (displayName.indexOf('Field') > -1) - return 'InputField' - else if (displayName.indexOf('SubmitButton') > -1) - return 'SubmitButton' - } - return 'UI' - } -} - -Form.defaultProps = { - resetOnRender : false, - disableOnPristine: true -} - -Form.propTypes = { - initalValue: PropTypes.object, - onSubmit: PropTypes.func.isRequired, - onChange: PropTypes.func, - resetOnRender: PropTypes.bool, - disableOnPristine: PropTypes.bool -} -export default Form diff --git a/components/Forms/FormFields.scss b/components/Forms/FormFields.scss deleted file mode 100644 index eb573d32a..000000000 --- a/components/Forms/FormFields.scss +++ /dev/null @@ -1,168 +0,0 @@ -@import 'tc-includes'; - -.row { - margin-bottom: 20px; - &.center{ - display: table; - margin: 0 auto; - label{ - display: inline; - } - input{ - display: inline; - width: 100px; - } - } -} -label{ - display:block; - margin: 10px auto; - text-transform: none; - @include tc-label-md; - color: $tc-gray-70; - font-size: 13px; -} - -.error-message{ - display:block; - margin: 5px auto; - @include roboto; - - color: $tc-gray-70; - font-size: 13px; - line-height:20px; - font-style:italic; - border: 1px solid $tc-red-30; - background: $tc-red-10; - color: $tc-red-70; - padding:10px; - border-radius: 2px; - strong{ - @include roboto-bold; - } -} - -input { - display:block; - margin: 0 auto; - color: $tc-black; - background: $tc-gray-neutral-light; - border-color: $tc-gray-20; - @include tc-label-md; - - &[disabled]{ - color: $tc-gray-20; - background: $tc-white; - } - - &:hover{ - border-color: $tc-gray-40; - background: $tc-gray-neutral-light; - } - - &:focus{ - background: $tc-white!important; - border-color: $tc-dark-blue-90!important; - } - - &.error{ - border-left: 3px solid $tc-red-70!important; - border-bottom: 1px solid $tc-red-70!important; - background: $tc-gray-neutral-light!important; - } -} - -textarea{ - display:block; - margin: 0 auto; - height: 100px; - color: $tc-black; - background: $tc-gray-neutral-light; - border-color: $tc-gray-20; - @include tc-label-md; - font-size:15px; - line-height:20px; - box-shadow: none; - - &[disabled]{ - color: $tc-gray-20; - background: $tc-white; - } - - &:hover{ - border-color: $tc-gray-40; - } - - &:focus{ - background: $tc-white!important; - border-color: $tc-dark-blue-90!important; - } - - &.error{ - border: 1px solid $tc-red-70!important; - background: $tc-gray-neutral-light!important; - } -} - -.checkbox-group-input { -} -.checkbox-group-item { - display: inline-block; - margin-right: 42px; - label { - @include roboto-bold; - color: $tc-gray-80; - margin-right: 0; - } -} - -.radio-group-input { - .radio-group-label { - @include tc-label-md; - margin-right: 20px; - } - .radio-group-options { - display: flex; - flex-direction: row; - .radio { - margin: 0; - input[type="radio"] { - opacity: 0; - display: none; - } - label { - display: inline-block; - line-height: 19px; - padding-left: 26px; - position: relative; - @include tc-label-md; - font-size:14px; - color: $tc-black; - margin-right: 30px; - &:before { - border: 1px solid $tc-gray-20; - border-radius: 50%; - background-color: #fff; - content: ""; - display: inline-block; - height: 19px; - left: 0; - position: absolute; - top: 0; - width: 19px; - } - } - input[type="radio"]:checked + label:after { - background: $tc-dark-blue-90; - border-radius: 50%; - content: ""; - display: inline-block; - left: 4px; - position: absolute; - top: 4px; - height: 11px; - width: 11px; - } - } - } -} diff --git a/components/Forms/RadioButton.jsx b/components/Forms/RadioButton.jsx deleted file mode 100644 index 029b74dd4..000000000 --- a/components/Forms/RadioButton.jsx +++ /dev/null @@ -1,22 +0,0 @@ -'use strict' - -import React, { Component } from 'react' - -class RadioButton extends Component { - render() { - const { name, index, label, selectedValue, value, onChange } = this.props - const id = [name, 'option', index].join('-') - return ( -
- - -
- ) - } -} - -export default RadioButton diff --git a/components/Forms/RadioGroupInput.jsx b/components/Forms/RadioGroupInput.jsx deleted file mode 100644 index b352b89ac..000000000 --- a/components/Forms/RadioGroupInput.jsx +++ /dev/null @@ -1,46 +0,0 @@ -'use strict' - -import React, { PropTypes } from 'react' -import _ from 'lodash' -import classNames from 'classnames' -import BaseInputField from './BaseInputField' -import RadioButton from './RadioButton' - -class RadioGroupInput extends BaseInputField { - constructor(props) { - super(props) - this.onChange = this.onChange.bind(this) - } - render() { - const { label, name, wrapperClass} = this.props - const { value, valid, dirty, errorMessage } = this.state - const hasError = dirty && !valid - const renderOption = (opt, idx) => { - return ( - - ) - } - const wrapperClasses = classNames(wrapperClass, 'radio-group-input') - return ( -
- -
{this.props.options.map(renderOption)}
- { hasError ? (

{errorMessage}

) : null} -
- ) - } -} -RadioGroupInput.displayName = 'RadioGroupInputField' -RadioGroupInput.propTypes = _.assign({}, RadioGroupInput.propTypes, { - options: PropTypes.arrayOf(PropTypes.object.isRequired).isRequired -}) - -export default RadioGroupInput diff --git a/components/Forms/SliderRadioGroupInput.jsx b/components/Forms/SliderRadioGroupInput.jsx deleted file mode 100644 index 067286274..000000000 --- a/components/Forms/SliderRadioGroupInput.jsx +++ /dev/null @@ -1,118 +0,0 @@ -'use strict' - -import React, { PropTypes } from 'react' -import classNames from 'classnames' -import _ from 'lodash' -import BaseInputField from './BaseInputField' - -class SliderRadioGroupInput extends BaseInputField { - constructor(props) { - super(props) - this.onSlide = this.onSlide.bind(this) - } - - componentWillMount() { - const idx = Math.max(this.getIndexFromValue(this.props.value), 0) - this.setState({ - dirty: false, - valid: true, - value: this.props.options[idx].value - }) - } - - onChange(idx) { - idx = parseInt(idx) - const {onFieldChange, name, options} = this.props - const newValue = options[idx].value - this.setState({dirty: true, value: newValue, valid: true}) - onFieldChange(name, newValue, true) - } - - noOp() {} - - onSlide(event) { - this.onChange(event.target.value) - } - - getIndexFromValue(val) { - return _.findIndex(this.props.options, (t) => t.value === val) - } - - render() { - const { options, min, max, step} = this.props - const { value } = this.state - const valueIdx = this.getIndexFromValue(value) - // creating a function to render each type title + desc - const itemFunc = (item, index) => { - // handle active class - const itemClassnames = classNames( 'selector', { - active: value === item.value - }) - const idx = this.getIndexFromValue(item.value) - const handleClick = this.onChange.bind(this, idx) - return ( -
-

{item.title}

- {item.desc} -
- ) - } - - // function to render item info - const itemInfoFunc = (item, index) => { - // handle active class - const itemClassnames = classNames({active: value === item.value}) - const idx = this.getIndexFromValue(item.value) - const handleClick = this.onChange.bind(this, idx) - return ( - - ) - } - return ( - /** - * TODO Using onInput trigger instead of onChange. - * onChange is showing some funky behavior at least in Chrome. - * This functionality should be tested in other browsers - * onChange={this.noOp} - */ -
- -
- -
- -
- {options.map(itemFunc)} -
- -
- {options.map(itemInfoFunc)} -
- -
- ) - } -} - -SliderRadioGroupInput.displayName = 'SliderRadioGroupInputField' -SliderRadioGroupInput.propTypes = _.assign({}, SliderRadioGroupInput.propTypes, { - options: PropTypes.arrayOf(PropTypes.object.isRequired).isRequired, - min: PropTypes.number.isRequired, - max: PropTypes.number.isRequired, - step: PropTypes.number.isRequired -}) - -export default SliderRadioGroupInput diff --git a/components/Forms/SubmitButton.jsx b/components/Forms/SubmitButton.jsx deleted file mode 100644 index 7e2530444..000000000 --- a/components/Forms/SubmitButton.jsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react' - -class SubmitButton extends React.Component { - render() { - return