├── .babelrc ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── LICENSE.md ├── README.md ├── examples ├── bootstrap │ ├── components │ │ ├── App.js │ │ └── bootstrap │ │ │ ├── Checkbox.js │ │ │ ├── Form.js │ │ │ ├── FormGroup.js │ │ │ └── RadioGroup.js │ ├── index.html │ ├── index.js │ ├── package.json │ ├── server.js │ └── webpack.config.js └── simple │ ├── components │ └── App.js │ ├── index.html │ ├── index.js │ ├── package.json │ ├── server.js │ └── webpack.config.js ├── karma.conf.js ├── package.json ├── src ├── components │ ├── Checkbox.js │ ├── Form.js │ ├── Message.js │ ├── RadioGroup.js │ ├── Select.js │ ├── Text.js │ └── TextArea.js ├── hoc │ ├── connect.js │ ├── connectCheckbox.js │ ├── connectInput.js │ ├── connectMessage.js │ ├── connectSelect.js │ └── createForm.js ├── index.js └── utils │ ├── formShape.js │ ├── makePath.js │ └── shallowEqual.js ├── test.js ├── test ├── .eslintrc ├── components │ ├── Checkbox.spec.js │ ├── Form.js │ ├── RadioGroup.spec.js │ ├── Select.spec.js │ ├── Text.spec.js │ └── TextArea.spec.js └── hoc │ ├── connectInput.spec.js │ └── createMessage.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react", "es2015", "stage-1"] 3 | } 4 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | lib 2 | **/node_modules 3 | **/webpack.config.js 4 | examples/**/server.js 5 | karma.conf.js 6 | tests.webpack.js 7 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": "eslint-config-airbnb", 4 | "env": { 5 | "browser": true, 6 | "mocha": true, 7 | "node": true 8 | }, 9 | "ecmaFeatures": { 10 | "experimentalObjectRestSpread": true 11 | }, 12 | "rules": { 13 | "react/jsx-uses-react": 2, 14 | "react/jsx-uses-vars": 2, 15 | "react/jsx-indent-props": 0, 16 | "react/jsx-no-bind": 0, 17 | "react/jsx-closing-bracket-location": 0, 18 | "react/react-in-jsx-scope": 2, 19 | "react/no-multi-comp": 0, 20 | "react/prefer-es6-class": 0, 21 | "max-len": 0, 22 | "indent": [0, 4], 23 | "new-cap": 0, 24 | "comma-dangle": 0, 25 | "camelcase": 0, 26 | "id-length": 0, 27 | "no-nested-ternary": 0, 28 | "no-param-reassign": 2, 29 | "prefer-arrow-callback": 0, 30 | "arrow-body-style": 0 31 | }, 32 | "plugins": [ 33 | "react" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .DS_Store 4 | dist 5 | lib 6 | coverage 7 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.log 3 | src 4 | test 5 | examples 6 | coverage 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "iojs" 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change log 2 | 3 | All notable changes to this project will be documented in this file. 4 | This project adheres to [Semantic Versioning](http://semver.org/). 5 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. 4 | 5 | We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion. 6 | 7 | Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. 8 | 9 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team. 10 | 11 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. 12 | 13 | This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/) 14 | 15 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Malte Wessel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | react-formalize 2 | ========================= 3 | 4 | [![npm](https://img.shields.io/badge/npm-react--formalize-brightgreen.svg?style=flat-square)]() 5 | [![npm version](https://img.shields.io/npm/v/react-formalize.svg?style=flat-square)](https://www.npmjs.com/package/react-formalize) 6 | [![npm downloads](https://img.shields.io/npm/dm/react-formalize.svg?style=flat-square)](https://www.npmjs.com/package/react-formalize) 7 | 8 | * serialize forms with react 9 | * pass defaults to form or input 10 | * easy two-way data binding 11 | * validation messages 12 | * works great with flux, redux and friends 13 | * fully customizable 14 | 15 | ### Demos 16 | * **[Simple example](http://malte-wessel.github.io/react-formalize/simple.html)** 17 | * **[Bootstrap integration](http://malte-wessel.github.io/react-formalize/bootstrap.html)** 18 | 19 | ## Table of Contents 20 | 21 | - [Installation](#installation) 22 | - [Usage](#usage) 23 | - [Customization](#customization) 24 | - [API](#api) 25 | - [Examples](#examples) 26 | - [License](#license) 27 | 28 | ## Installation 29 | ```bash 30 | npm install react-formalize --save 31 | ``` 32 | 33 | ## Usage 34 | ```javascript 35 | import { Component } from 'react'; 36 | import { Form, Text, Select } from 'react-formalize'; 37 | 38 | export default class MyForm extends Component { 39 | 40 | handleSubmit(values) { 41 | console.info('Submit', values); 42 | // { 43 | // title: 'Lorem ipsum dolor ist', 44 | // category: 'news' 45 | // }; 46 | } 47 | 48 | render() { 49 | const post = { 50 | title: 'Lorem ipsum dolor ist', 51 | category: 'news' 52 | }; 53 | 54 | return ( 55 |
59 |
60 | 61 | 64 |
65 |
66 | 67 | ` 114 | 115 | Input component wrapper, connects to `Form` component, receives and propagates data, **do not use directly**. 116 | 117 | ##### Props 118 | 119 | * `name`: *(String)* name of the input field 120 | * `value`: *(Array|Boolean|Number|Object|String)* value of the input field 121 | * `serialize`: *(Function)* function that extracts the input's data from the change event 122 | * `children`: *(Component)* children components 123 | 124 | **[Input component source](https://github.com/malte-wessel/react-formalize/blob/master/src/components/Form.js)** 125 | 126 | ##### Example 127 | ```javascript 128 | import React, { PropTypes, Component } from 'react'; 129 | import { Input } from 'react-formalize'; 130 | 131 | export default class MyCustomTextField extends Component { 132 | 133 | renderInput(props) { 134 | return ; 135 | } 136 | 137 | render() { 138 | return ( 139 | 140 | {props => } 141 | 142 | ); 143 | } 144 | } 145 | 146 | ``` 147 | 148 | #### `` 149 | 150 | Message component, connects to `Form` component, receives messages 151 | 152 | ##### Props 153 | 154 | * `name`: *(String)* name of the related input field 155 | * `renderMessage`: *(Function)* render a custom message 156 | * `children`: *(Function)* children components 157 | 158 | **[Message component source](https://github.com/malte-wessel/react-formalize/blob/master/src/components/Form.js)** 159 | 160 | ##### Example 161 | ```javascript 162 | 163 | 164 | {message =>

{message}

}
165 | 166 | ``` 167 | 168 | ### Build in input components 169 | 170 | 171 | #### `` 172 | 173 | Native text input component 174 | 175 | ##### Props 176 | 177 | * `name`: *(String)* name of the input field 178 | * `type`: *(String)* One of: `text`, `date`, `datetime`, `datetime-local`, `email`, `month`, `number`, `password`, `tel`, `time`, `search`, `url`, `week`. Default is text 179 | 180 | **[Text component source](https://github.com/malte-wessel/react-formalize/blob/master/src/components/inputs/Text.js)** 181 | 182 | ##### Example 183 | ```javascript 184 |
185 | 186 | 187 |
188 | ``` 189 | 190 | * Text ([Source](https://github.com/malte-wessel/react-formalize/blob/master/src/components/inputs/Text.js)) 191 | * TextArea ([Source](https://github.com/malte-wessel/react-formalize/blob/master/src/components/inputs/TextArea.js)) 192 | * Checkbox ([Source](https://github.com/malte-wessel/react-formalize/blob/master/src/components/inputs/Checkbox.js)) 193 | * RadioGroup ([Source](https://github.com/malte-wessel/react-formalize/blob/master/src/components/inputs/RadioGroup.js)) 194 | * Select ([Source](https://github.com/malte-wessel/react-formalize/blob/master/src/components/inputs/Select.js)) 195 | 196 | ## Examples 197 | 198 | Run the simple example: 199 | ```bash 200 | cd react-formalize 201 | npm install 202 | cd react-formalize/examples/simple 203 | npm install 204 | npm start 205 | ``` 206 | 207 | ## License 208 | 209 | MIT 210 | -------------------------------------------------------------------------------- /examples/bootstrap/components/App.js: -------------------------------------------------------------------------------- 1 | import React, { createClass } from 'react'; 2 | 3 | import { Text, Select, TextArea } from 'react-formalize'; 4 | 5 | import Form from './bootstrap/Form'; 6 | import FormGroup from './bootstrap/FormGroup'; 7 | import Checkbox from './bootstrap/Checkbox'; 8 | import RadioGroup from './bootstrap/RadioGroup'; 9 | 10 | export default createClass({ 11 | 12 | displayName: 'App', 13 | 14 | getInitialState() { 15 | return { 16 | messages: {}, 17 | saving: false, 18 | values: { 19 | categories: ['articles', 'react'] 20 | } 21 | }; 22 | }, 23 | 24 | onChange(values) { 25 | this.setState({ values }); 26 | console.info('onChange', values); 27 | }, 28 | 29 | onSubmit(values) { 30 | console.info('onSubmit', values); 31 | const messages = this.validate(values); 32 | let saving = false; 33 | if (!messages) { 34 | saving = true; 35 | setTimeout(() => this.setState({ saving: false }), 2000); 36 | } 37 | this.setState({ messages, saving }); 38 | }, 39 | 40 | validate(values) { 41 | const { title, categories, text } = values; 42 | const errors = {}; 43 | if (!title) errors.title = 'Title is required'; 44 | if (categories.length < 1) errors.categories = 'Please select at least one category'; 45 | if (!text) errors.text = 'Text is required'; 46 | 47 | if (Object.keys(errors).length > 0) { 48 | return errors; 49 | } 50 | return undefined; 51 | }, 52 | 53 | render() { 54 | const { values, messages, saving } = this.state; 55 | const categories = { 56 | articles: 'Articles', 57 | react: 'React', 58 | reactNative: 'React Native', 59 | flux: 'Flux', 60 | bootstrap: 'Bootstrap' 61 | }; 62 | const ads = { 63 | no: 'Don\'t show ads', 64 | yes: 'Shou ads' 65 | }; 66 | return ( 67 |
68 |
69 |
70 |
71 |

Bootstrap Form Example

72 |
73 |
79 | 80 | 81 | 82 | 83 |