Skip to content

Instantly share code, notes, and snippets.

@devdlabs
Forked from insin/contactform.js
Created May 28, 2016 07:32

Revisions

  1. @insin insin revised this gist Jan 14, 2014. 1 changed file with 19 additions and 1 deletion.
    20 changes: 19 additions & 1 deletion contactform.js
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,12 @@
    /** @jsx React.DOM */

    var STATES = [
    'AL', 'AK', 'AS', 'AZ', 'AR', 'CA', 'CO', 'CT', 'DE', 'DC', 'FL', 'GA', 'HI',
    'ID', 'IL', 'IN', 'IA', 'KS', 'KY', 'LA', 'ME', 'MD', 'MA', 'MI', 'MN', 'MS',
    'MO', 'MT', 'NE', 'NV', 'NH', 'NJ', 'NM', 'NY', 'NC', 'ND', 'OH', 'OK', 'OR',
    'PA', 'RI', 'SC', 'SD', 'TN', 'TX', 'UT', 'VT', 'VA', 'WA', 'WV', 'WI', 'WY'
    ]

    var Example = React.createClass({
    getInitialState: function() {
    return {
    @@ -127,7 +134,7 @@ var ContactForm = React.createClass({
    {this.props.question && this.renderTextarea('question', 'Question')}
    {this.renderTextInput('address', 'Address')}
    {this.renderTextInput('city', 'City')}
    {this.renderTextInput('state', 'State')}
    {this.renderSelect('state', 'State', STATES)}
    {this.renderTextInput('zipCode', 'Zip Code')}
    {this.renderRadioInlines('currentCustomer', 'Are you currently a ' + this.props.company + ' Customer?', {
    values: ['Yes', 'No']
    @@ -148,6 +155,17 @@ var ContactForm = React.createClass({
    )
    }

    , renderSelect: function(id, label, values) {
    var options = values.map(function(value) {
    return <option value={value}>{value}</option>
    })
    return this.renderField(id, label,
    <select className="form-control" id={id} ref={id}>
    {options}
    </select>
    )
    }

    , renderRadioInlines: function(id, label, kwargs) {
    var radios = kwargs.values.map(function(value) {
    var defaultChecked = (value == kwargs.defaultCheckedValue)
  2. @insin insin revised this gist Jan 14, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -11,7 +11,7 @@
    <div class="container">
    <div class="page-header">
    <h1><a href="http://facebook.github.io/react/">React</a> Contact Form Example</h1>
    <p>An example of building a reusable contact form using uncontrolled components, extracted from a <a href="http://facebook.github.io/react/">React</a> app I'm working on.</p>
    <p>An example of building a reusable contact form using <a href="http://facebook.github.io/react/docs/forms.html#uncontrolled-components">uncontrolled components</a>, extracted from a <a href="http://facebook.github.io/react/">React</a> app I'm working on.</p>
    <p>Plain old JavaScript functions are used to remove duplication of structure and logic from the form layout.</p>
    <p>The validation is barebones and whole-form-at-a-time for now, as I have plans for <a href="http://github.com/insin/newforms">newforms</a> in this area&hellip;</p>
    </div>
  3. @insin insin revised this gist Jan 14, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion index.html
    Original file line number Diff line number Diff line change
    @@ -13,7 +13,7 @@
    <h1><a href="http://facebook.github.io/react/">React</a> Contact Form Example</h1>
    <p>An example of building a reusable contact form using uncontrolled components, extracted from a <a href="http://facebook.github.io/react/">React</a> app I'm working on.</p>
    <p>Plain old JavaScript functions are used to remove duplication of structure and logic from the form layout.</p>
    <p>The validation is barebones and whole-form-at-a time for now, as I have plans for <a href="http://github.com/insin/newforms">newforms</a> in this area&hellip;</p>
    <p>The validation is barebones and whole-form-at-a-time for now, as I have plans for <a href="http://github.com/insin/newforms">newforms</a> in this area&hellip;</p>
    </div>
    <div id="contactform"></div>
    </div>
  4. @insin insin revised this gist Jan 14, 2014. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -18,5 +18,6 @@ <h1><a href="http://facebook.github.io/react/">React</a> Contact Form Example</h
    <div id="contactform"></div>
    </div>
    <script type="text/jsx" src="contactform.js"></script>
    <a href="https://gist.github.com/insin/8418675"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub"></a>
    </body>
    </html>
  5. @insin insin created this gist Jan 14, 2014.
    197 changes: 197 additions & 0 deletions contactform.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,197 @@
    /** @jsx React.DOM */

    var Example = React.createClass({
    getInitialState: function() {
    return {
    email: true
    , question: true
    , submitted: null
    }
    }

    , render: function() {
    var submitted
    if (this.state.submitted !== null) {
    submitted = <div className="alert alert-success">
    <p>ContactForm data:</p>
    <pre><code>{JSON.stringify(this.state.submitted, null, ' ')}</code></pre>
    </div>
    }

    return <div>
    <div className="panel panel-default">
    <div className="panel-heading clearfix">
    <h3 className="panel-title pull-left">Contact Form</h3>
    <div className="pull-right">
    <label className="checkbox-inline">
    <input type="checkbox"
    checked={this.state.email}
    onChange={this.handleChange.bind(this, 'email')}
    /> Email
    </label>
    <label className="checkbox-inline">
    <input type="checkbox"
    checked={this.state.question}
    onChange={this.handleChange.bind(this, 'question')}
    /> Question
    </label>
    </div>
    </div>
    <div className="panel-body">
    <ContactForm ref="contactForm"
    email={this.state.email}
    question={this.state.question}
    company={this.props.company}
    />
    </div>
    <div className="panel-footer">
    <button type="button" className="btn btn-primary btn-block" onClick={this.handleSubmit}>Submit</button>
    </div>
    </div>
    {submitted}
    </div>
    }

    , handleChange: function(field, e) {
    var nextState = {}
    nextState[field] = e.target.checked
    this.setState(nextState)
    }

    , handleSubmit: function() {
    if (this.refs.contactForm.isValid()) {
    this.setState({submitted: this.refs.contactForm.getFormData()})
    }
    }
    })

    /**
    * A contact form with certain optional fields.
    */
    var ContactForm = React.createClass({
    getDefaultProps: function() {
    return {
    email: true
    , question: false
    }
    }

    , getInitialState: function() {
    return {errors: {}}
    }

    , isValid: function() {
    var fields = ['firstName', 'lastName', 'phoneNumber', 'address', 'city', 'state', 'zipCode']
    if (this.props.email) fields.push('email')
    if (this.props.question) fields.push('question')

    var errors = {}
    fields.forEach(function(field) {
    var value = trim(this.refs[field].getDOMNode().value)
    if (!value) {
    errors[field] = 'This field is required'
    }
    }.bind(this))
    this.setState({errors: errors})

    var isValid = true
    for (var error in errors) {
    isValid = false
    break
    }
    return isValid
    }

    , getFormData: function() {
    var data = {
    firstName: this.refs.firstName.getDOMNode().value
    , lastName: this.refs.lastName.getDOMNode().value
    , phoneNumber: this.refs.phoneNumber.getDOMNode().value
    , address: this.refs.address.getDOMNode().value
    , city: this.refs.city.getDOMNode().value
    , state: this.refs.state.getDOMNode().value
    , zipCode: this.refs.zipCode.getDOMNode().value
    , currentCustomer: this.refs.currentCustomerYes.getDOMNode().checked
    }
    if (this.props.email) data.email = this.refs.email.getDOMNode().value
    if (this.props.question) data.question = this.refs.question.getDOMNode().value
    return data
    }

    , render: function() {
    return <div className="form-horizontal">
    {this.renderTextInput('firstName', 'First Name')}
    {this.renderTextInput('lastName', 'Last Name')}
    {this.renderTextInput('phoneNumber', 'Phone number')}
    {this.props.email && this.renderTextInput('email', 'Email')}
    {this.props.question && this.renderTextarea('question', 'Question')}
    {this.renderTextInput('address', 'Address')}
    {this.renderTextInput('city', 'City')}
    {this.renderTextInput('state', 'State')}
    {this.renderTextInput('zipCode', 'Zip Code')}
    {this.renderRadioInlines('currentCustomer', 'Are you currently a ' + this.props.company + ' Customer?', {
    values: ['Yes', 'No']
    , defaultCheckedValue: 'No'
    })}
    </div>
    }

    , renderTextInput: function(id, label) {
    return this.renderField(id, label,
    <input type="text" className="form-control" id={id} ref={id}/>
    )
    }

    , renderTextarea: function(id, label) {
    return this.renderField(id, label,
    <textarea className="form-control" id={id} ref={id}/>
    )
    }

    , renderRadioInlines: function(id, label, kwargs) {
    var radios = kwargs.values.map(function(value) {
    var defaultChecked = (value == kwargs.defaultCheckedValue)
    return <label className="radio-inline">
    <input type="radio" ref={id + value} name={id} value={value} defaultChecked={defaultChecked}/>
    {value}
    </label>
    })
    return this.renderField(id, label, radios)
    }

    , renderField: function(id, label, field) {
    return <div className={$c('form-group', {'has-error': id in this.state.errors})}>
    <label htmlFor={id} className="col-sm-4 control-label">{label}</label>
    <div className="col-sm-6">
    {field}
    </div>
    </div>
    }
    })

    React.renderComponent(<Example company="FakeCo"/>, document.getElementById('contactform'))

    // Utils

    var trim = function() {
    var TRIM_RE = /^\s+|\s+$/g
    return function trim(string) {
    return string.replace(TRIM_RE, '')
    }
    }()

    function $c(staticClassName, conditionalClassNames) {
    var classNames = []
    if (typeof conditionalClassNames == 'undefined') {
    conditionalClassNames = staticClassName
    }
    else {
    classNames.push(staticClassName)
    }
    for (var className in conditionalClassNames) {
    if (!!conditionalClassNames[className]) {
    classNames.push(className)
    }
    }
    return classNames.join(' ')
    }
    22 changes: 22 additions & 0 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,22 @@
    <!DOCTYPE html>
    <html>
    <head>
    <title>React Contact Form Example</title>
    <script src="http://fb.me/react-0.8.0.js"></script>
    <script src="http://fb.me/JSXTransformer-0.8.0.js"></script>
    <link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css">
    <link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap-theme.min.css">
    </head>
    <body>
    <div class="container">
    <div class="page-header">
    <h1><a href="http://facebook.github.io/react/">React</a> Contact Form Example</h1>
    <p>An example of building a reusable contact form using uncontrolled components, extracted from a <a href="http://facebook.github.io/react/">React</a> app I'm working on.</p>
    <p>Plain old JavaScript functions are used to remove duplication of structure and logic from the form layout.</p>
    <p>The validation is barebones and whole-form-at-a time for now, as I have plans for <a href="http://github.com/insin/newforms">newforms</a> in this area&hellip;</p>
    </div>
    <div id="contactform"></div>
    </div>
    <script type="text/jsx" src="contactform.js"></script>
    </body>
    </html>