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({
+
+
+ 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;