diff --git a/README.md b/README.md index afb09f6f..54c28fb7 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ var UIToolkit = require('ui-toolkit'); + ``` If you would like to cherry pick which components to include in your project, you can use this method: diff --git a/docs/assets/style.css b/docs/assets/style.css index 8d35416a..cb80978c 100755 --- a/docs/assets/style.css +++ b/docs/assets/style.css @@ -131,3 +131,32 @@ article { .toolkit-example button { margin:.25em; } + + +/* Components */ +.toolkit-example .input-group { + width: 100%; +} + +.toolkit-example .input-group-label { + width: 18%; +} + +.toolkit-example .input-group-field { + border: 1px solid #dddddd; + outline: none; + padding: 0 8px; + border-radius: 2px; +} + +.toolkit-example .input-group-error .input-group-field { + border: 1px solid #c7254e; + color: #c7254e; + background-color: #f9f2f4; +} + +.toolkit-example .input-group-error .input-group-span { + color: #c7254e; + display: inline-block; + margin-left: 15px; +} diff --git a/docs/examples/Input.jsx b/docs/examples/Input.jsx new file mode 100644 index 00000000..8ce5bf3d --- /dev/null +++ b/docs/examples/Input.jsx @@ -0,0 +1,43 @@ +var text_validator = /^([a-z]+(-| )?)+$/i; +var text_error_message = 'Invalid Name'; + +var password_validator = /^(?=.*\d)(?=.*[a-zA-Z])(?!.*[\W_\x7B-\xFF]).{6,15}$/; +var password_error_message = 'Password must be 6-20 characters including at least 1 upper or lower alpha, and 1 digit.'; + +var email_validator = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i; +var email_error_message = 'Invalid Email'; + +var example = ( +
+ + + +
+ +); + +React.render(example, mountNode); diff --git a/docs/src/Components.jsx b/docs/src/Components.jsx index 9d8e2382..26205e4c 100644 --- a/docs/src/Components.jsx +++ b/docs/src/Components.jsx @@ -81,6 +81,26 @@ var Components = React.createClass({ +
+

Input

+

Custom Input for Text, Password & Email Types

+ +

Attributes

+
    +
  • type String - Type of Input Field [ text | password | email ]
  • +
  • name String - Optional Name for Input Field
  • +
  • id String - Optional ID for Input Field
  • +
  • label String - Optional Label in front of Input Field
  • +
  • placeholder String - Optional Placeholder for Input Field
  • +
  • defaultValue String - Optional Default Value for Input Field
  • +
  • validator String - Optional Regular Expression to Validate Input
  • +
  • errorMessage String - Optional Error Message to show if validator returns false
  • +
  • disabled Boolean - Whether to Disable the Input Field
  • +
  • readOnly Boolean - Whether to set the Input Field to Read Only
  • +
  • required Boolean - Whether to set the Input Field to be Required
  • +
+
+ ); } diff --git a/docs/src/Nav.jsx b/docs/src/Nav.jsx index 6915932f..c26ab582 100644 --- a/docs/src/Nav.jsx +++ b/docs/src/Nav.jsx @@ -22,6 +22,7 @@ var Nav = React.createClass({
  • Rating
  • Image
  • Tile
  • +
  • Input
  • diff --git a/src/components/input/__tests__/input-test.js b/src/components/input/__tests__/input-test.js new file mode 100644 index 00000000..34555610 --- /dev/null +++ b/src/components/input/__tests__/input-test.js @@ -0,0 +1,67 @@ +/** @jsx React.DOM */ + +var InputView = require('../code/views/inputView.jsx'); + +describe('InputComponent', function() { + + it('should render text input', function() { + var input = TestUtils.renderIntoDocument( + + ); + + var renderedInput = TestUtils.findRenderedDOMComponentWithClass(input, 'component-input'); + assert.isDefined(renderedInput); + + }); + + it('should render password input', function() { + var input = TestUtils.renderIntoDocument( + + ); + + var renderedInput = TestUtils.findRenderedDOMComponentWithClass(input, 'component-input'); + assert.isDefined(renderedInput); + + }); + + it('should render email input', function() { + var input = TestUtils.renderIntoDocument( + + ); + + var renderedInput = TestUtils.findRenderedDOMComponentWithClass(input, 'component-input'); + assert.isDefined(renderedInput); + + }); + + it('should render input with label', function() { + var input = TestUtils.renderIntoDocument( + + ); + + var renderedInput = TestUtils.findRenderedDOMComponentWithClass(input, 'input-group-label'); + assert.isDefined(renderedInput); + + }); + + it('should render input as disabled', function() { + var input = TestUtils.renderIntoDocument( + + ); + + var renderedInput = TestUtils.findRenderedDOMComponentWithClass(input, 'component-input-field'); + assert.equal(renderedInput.getDOMNode().getAttribute('disabled'), ''); + + }); + + it('should render input as readonly', function() { + var input = TestUtils.renderIntoDocument( + + ); + + var renderedInput = TestUtils.findRenderedDOMComponentWithClass(input, 'component-input-field'); + assert.equal(renderedInput.getDOMNode().getAttribute('readonly'), ''); + + }); + +}); diff --git a/src/components/input/code/index.js b/src/components/input/code/index.js new file mode 100644 index 00000000..66d3ebf0 --- /dev/null +++ b/src/components/input/code/index.js @@ -0,0 +1 @@ +module.exports = require('./views/inputView.jsx'); diff --git a/src/components/input/code/templates/inputTemplate.jsx b/src/components/input/code/templates/inputTemplate.jsx new file mode 100644 index 00000000..da51e823 --- /dev/null +++ b/src/components/input/code/templates/inputTemplate.jsx @@ -0,0 +1,49 @@ +var React = require('react'); +var classNames = require('classnames'); + +module.exports = function (component){ + + var classes = classNames('input-group', { + 'component-input': true, + 'input-group-error': component.state.error || false, + 'input-group-disabled': component.props.disabled || false + }); + + // the form label + var label; + + if (component.props.label){ + label = ( ); + } + + /** + * Create the span element used for containing messages + * related to the element. + */ + var span; + + if (component.state.error) + { + span = ( {component.state.error} ); + } + + return ( +
    + {label} + + {span} +
    + ); +}; diff --git a/src/components/input/code/views/inputView.jsx b/src/components/input/code/views/inputView.jsx new file mode 100644 index 00000000..061dfed3 --- /dev/null +++ b/src/components/input/code/views/inputView.jsx @@ -0,0 +1,58 @@ +var React = require('react'); + +module.exports = React.createClass({ + + propTypes: { + label: React.PropTypes.string, + type: React.PropTypes.oneOf(['text', 'password', 'email']), + placeholder: React.PropTypes.string, + defaultValue: React.PropTypes.string, + name: React.PropTypes.string, + id: React.PropTypes.string, + error: React.PropTypes.string, + disabled: React.PropTypes.bool, + readOnly: React.PropTypes.bool, + required: React.PropTypes.bool, + valid: React.PropTypes.bool, + errorMessage: React.PropTypes.string, + onChange: React.PropTypes.func + }, + + getInitialState: function() { + return { + value: this.props.defaultValue || '', + error: null, + valid: true + } + }, + + getDefaultProps: function() { + return { + type: 'text', + disabled: false, + readOnly: false, + required: false, + onChange: this.changeHandler, + errorMessage: null + } + }, + + changeHandler: function(e) { + + var is_valid = true; + + if(typeof this.props.validator !== 'undefined'){ + is_valid = this.props.validator.test(e.target.value); + } + + if( !is_valid){ + this.setState({ error : this.props.errorMessage }); + } + + this.setState({ valid : is_valid }); + }, + + render: function() { + return require('../templates/inputTemplate.jsx')(this); + } +}); diff --git a/src/components/input/index.js b/src/components/input/index.js new file mode 100644 index 00000000..e7dfdfd3 --- /dev/null +++ b/src/components/input/index.js @@ -0,0 +1 @@ +module.exports = require('./code/index'); diff --git a/src/components/input/scripts/build-dev.sh b/src/components/input/scripts/build-dev.sh new file mode 100755 index 00000000..979a68df --- /dev/null +++ b/src/components/input/scripts/build-dev.sh @@ -0,0 +1,7 @@ +#!/bin/bash +if [ -z "$VARIANT" ]; then VARIANT=''; fi +if [ -z "$TEMPLATE_LANG" ]; then TEMPLATE_LANG='en'; fi + +echo "Building package for demo. Variant: '$VARIANT', language: '$TEMPLATE_LANG'." + +browserify -t [ redirectify --dir "$VARIANT" ] -t reactify dev/example.jsx -o dev/ui-component-email.js \ No newline at end of file diff --git a/src/components/input/scripts/build-dist.sh b/src/components/input/scripts/build-dist.sh new file mode 100755 index 00000000..f73aab23 --- /dev/null +++ b/src/components/input/scripts/build-dist.sh @@ -0,0 +1,7 @@ +#!/bin/bash +if [ -z "$VARIANT" ]; then VARIANT=''; fi +if [ -z "$TEMPLATE_LANG" ]; then TEMPLATE_LANG='en'; fi + +echo "Building package for demo. Variant: '$VARIANT', language: '$TEMPLATE_LANG'." + +browserify -t [ redirectify --dir "$VARIANT" ] -t reactify index.js --standalone ui-component-email > dist/ui-component-email-standalone.js \ No newline at end of file diff --git a/src/ui-toolkit.js b/src/ui-toolkit.js index f43c0bfc..82f87ba5 100644 --- a/src/ui-toolkit.js +++ b/src/ui-toolkit.js @@ -7,5 +7,6 @@ UIToolkit.Rating = require('./components/rating'); UIToolkit.Reviews = require('./components/reviews'); UIToolkit.Tile = require('./components/tile'); UIToolkit.Image = require('./components/image'); +UIToolkit.Input = require('./components/input'); module.exports = UIToolkit;