// dependencies
//
import React from 'react';
import classnames from 'classnames';
import uniqueId from 'lodash/uniqueId';
import isRegExp from 'lodash/isRegExp';
import isFunction from 'lodash/isFunction';

// @TODO pattern = function which returns true if value passes
// @TODO add different validation error messages for failing different rules
// @TODO horizontal form - label & input element widths
// @TODO permitted characters regex
// @TODO parse & format functions for onFocus & onBlur handling of values
// @TODO placeholder

/**
 * The TextInput component
 *
 */
class TextInput extends React.Component {
    /**
     * Construct the component instance
     * @param  {Object} props The component props
     */
    constructor(props) {
        super(props);

        // initialize the state for the component
        //
        this.state = {
            value:   props.value,
            isValid: true,
        };

        // generate a unique ID for this component instance
        //
        this.id = uniqueId('form-textinput-');

        // intialize the validation message for the component
        //
        this.validationMessage = props.validationMessage
            || `${this.props.description} is required`;

        // bind `this` to the component event handlers
        //
        this.onBlur = this.onBlur.bind(this);
        this.onChange = this.onChange.bind(this);
    }

    /**
     * Handle the "component is about to mount" event
     */
    componentWillMount() {
        // call the `onChildValidationEvent` handler once with no error message,
        // just to ensure that the parent knows about this child
        //
        this.context.onChildValidationEvent(this.id, null);
    }

    /**
     * Handle the blur event for the input element
     * @param  {Object} event The event object
     */
    onBlur(event) {
        // get the new value
        //
        const value = event.target.value;

        // determine if it's valid
        //
        const isValid = this.validate(value);

        // set the `isValid` state
        //
        this.setState({ isValid });

        // call the `onChildValidationEvent` handler
        //
        this.context.onChildValidationEvent(this.id, isValid ? null : this.validationMessage);
    }

    /**
     * Handle the change event for the input element
     * @param  {Object} event The event object
     */
    onChange(event) {
        // get the value from the event object
        //
        const value = event.target.value;

        // update the component state
        //
        this.setState({ value });

        // do we have an `onChange` handler? if so call it with the new value
        //
        if (this.props.onChange) {
            this.props.onChange(value);
        }
    }

    /**
     * Validate a value
     * @param  {String} value The value to validate
     * @return {Boolean}      True if the value is valid, false otherwise
     */
    validate(value) {
        // declare a variable to hold the result
        //
        let isValid = true;

        if (!value) {
            isValid = !this.props.required;
        } else {
            // do we have a pattern prop?
            //
            if (this.props.pattern) {
                // is the pattern a regex? is it a function?
                //
                if (isRegExp(this.props.pattern)) {
                    // the pattern is a regex. Test the value against it
                    //
                    isValid = this.props.pattern.test(`${value}`);
                } else if (isFunction(this.props.pattern)) {
                    // the pattern is a function. Pass the value to the function
                    //
                    isValid = this.props.pattern(value);
                }
            } else {
                isValid = true;
            }
        }

        return isValid;
    }

    /**
     * Render the component
     * @return {React.Element} The React element describing the component
     */
    render() {
        return (
            <div className={classnames('form-group', { 'has-error': !this.state.isValid })}>
                {this.props.label ? <label htmlFor={this.id}>{this.props.label}</label> : ''}
                <input
                    type="text"
                    id={this.id}
                    value={this.state.value}
                    className="form-control"
                    onBlur={this.onBlur}
                    onChange={this.onChange}
                />
                {!this.state.isValid
                    ? <span className="help-block">{this.validationMessage}</span>
                    : null
                }
            </div>
        );
    }
}

// set the property types for the component
//
TextInput.propTypes = {
    required:               React.PropTypes.bool,
    value:                  React.PropTypes.string,
    label:                  React.PropTypes.string,
    description:            React.PropTypes.string,
    validationMessage:      React.PropTypes.string,
    pattern:                (props, propName, componentName) => {
        if (props.pattern && !isRegExp(props.pattern) && !isFunction(props.pattern)) {
            return new Error(
                `Invalid prop ${propName} supplied to ${componentName}` +
                ' - should be a regular expression'
            );
        }
        return null;
    },
    onChange:               React.PropTypes.func,
    validationKey:          React.PropTypes.string,
    onChildValidationEvent: React.PropTypes.func,
};

TextInput.contextTypes = {
    onChildValidationEvent: React.PropTypes.func,
};

export default TextInput;

@@ -3068,21 +3068,232 @@
     
-if (!value) %7B
+// do we have a value?%0A        //%0A        if (!value) %7B%0A            // we do not have a value - in this case, whether it's valid or not%0A            // is determined solely by whether a valid is required or not%0A            //
 %0A   
@@ -3357,33 +3357,53 @@
 %0A            // 
-d
+we do have a value. D
 o we have a patt
@@ -3479,24 +3479,53 @@
           //
+ yes, we have a pattern prop.
  is the patt
@@ -3539,15 +3539,36 @@
 egex
-?
+ or
  is it
+%0A                //
  a f
@@ -4091,48 +4091,335 @@
     
-isValid = true;%0A            %7D%0A        %7D%0A
+// we don't have a pattern prop. That means if we get here that%0A                // we have a value and it doesn't matter whether it's required%0A                // or not because we have a value%0A                //%0A                isValid = true;%0A            %7D%0A        %7D%0A%0A        // return the results of the validation%0A        //
 %0A   

comment cleanup