), validity of all components in the group as single boolen true or false can be determined. False means at least one component is invalid,
59 |
60 | - **errorStyle**: Object specifying your custimized style to apply on the error message,
61 |
62 | - **tagName**: Usefull while uglifying - provide the tagName of the component to prevent failue while uglifying,
63 |
64 | - **closures**: an object set with variables, where object key is variable name, and value is the value of the variable. You must use this when component is dependent of value coming from its closure.
65 |
66 |
67 |
68 | ***Real Code Example***
69 | ```jsx
70 | import {Validation, fieldValidatorCore} from "react-validation-framework";
71 | import validator from "validator";
72 | !validator.isEmpty(val),
78 | errorMessage: "Cannot be left empty"
79 | }, {
80 | validator: (val) => validator.isNumeric(val),
81 | errorMessage: "Should be a numeric number"
82 | }, {
83 | validator: (val) => {
84 | if (parseInt(val) > 100){
85 | return false;
86 | } else {
87 | return true;
88 | }
89 | },
90 | errorMessage: "Must be any number less than 100"
91 | }
92 | ]}>
93 | {
100 | console.log("you have typed: ", evt.target.value);
101 | console.log("this value is defined in differenet scope and hence added to closures prop", area);
102 | }
103 | }/>
104 |
105 |
106 | ```
107 | ***Notes***
108 |
109 | **1-** Validation accepts an array of validators functions, each with their respective error messages.
110 | The order is important and the field is validated as per the order. For validator function third-party library can be used like - [validator](http://github.com/chriso/validator.js/),
111 | as like above, which has whole bunch of well tested regex, like isEmpty, isEmail,
112 | etc or we can supply our own function for specific case to validate.
113 |
114 | **2-** “group” prop can be added to define the group in which the filed belongs.
115 | Later the group name can be used to find whether a group of filed is valid or not like this :
116 | ```jsx
117 | handleSubmit(){
118 | let checkFieldTestResult = fieldValidatorCore.checkGroup("myGroup1");
119 | if (checkFieldTestResult.isValid){
120 | console.log("All fields with Gropu prop value as myGroup1 is valid"
121 | } else {
122 | console.log("Some of fields with group as "myGroup1" are invalid");
123 | console.log("Field which are invalid are ", checkFieldTestResult.inValidComponents);
124 | console.log("Fields which are valid are ", checkFieldTestResult.validComponents
125 | }
126 | }
127 | ```
128 |
129 | or simply add a ref to the Validation tag and call the isValid method to find if the field is valid.
130 |
131 | **3-** To add a new component (This is required just one time. It could be somewhere in your index.js)
132 |
133 | Signature -
134 | `fieldValidatorCore.addSupport(name, getValueFromChangeEvent, changeCallBackCaller, errorPropName, helperPropName)`
135 |
136 | **name** - string - Tag name of you component
137 |
138 | **getValueFromChangeEvent** - function - (arg)=>{}
139 |
140 | **changeCallBackCaller** - function - (callback, args)=>{}
141 |
142 | **errorPropName**- string - if your component has a prop for error, ex. some* *material-ui components have "error"
143 |
144 | **helperPropName**- string - if your component has a prop for helperText, ex. some* *material-ui components have "helperText"
145 |
146 | add the component before your page component mounts, like
147 | ```jsx
148 | componentWillMount(){
149 | fieldValidatorCore.addSupport("TextField", (args)=>{
150 | return args[0].target.value;
151 | }, (callback, args)=>{
152 | callback(args[0]);
153 | }, "error", "helperText");
154 | }
155 | ```
156 | **4-** (with 2.3.0), If any other prop other than value prop of your field component has value derived from upper scope/closure, ex - 'area' in onChange, it is better to added those closures in a prop called closures, otherwise, if the value changes you field componenet will still be having old value
157 |
158 | ```jsx
159 |
160 | {
164 | fooFun(area, evt.target.value)
165 | }
166 | }
167 | onKeyUp={
168 | (evt)=>{
169 | if (evt.keyCode === enterKeyCode){
170 | barFun(area, evt.target.value)
171 | }
172 |
173 | }
174 | }
175 | />
176 |
177 | ```
178 | **5-** (with 4.0.0), set tagName prop as the tagName of your component to avoid failures while uglifying.
179 |
180 |
181 | **Final Note**
182 | Example usages are put in the examples directory.
183 | Contributions are welcomed.
184 |
185 |
186 |
--------------------------------------------------------------------------------
/examples/CustomHighOrderComponentExample.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from "react";
2 | import MyCustomHighOrderComponent from "../MyCustomHighOrderComponent";
3 | import {Validation, fieldValidatorCore} from "react-validation-framework";
4 |
5 | //support for MyCustomHighOrderComponent is needs just once, better place for it would be at the start of your app
6 | fieldValidatorCore.addSupport(
7 | "MyCustomHighOrderComponent",
8 | (args) => {
9 | return args[1].suggestion;
10 | },
11 | (callback, args) => {
12 | return callback(...args);
13 | },
14 | "error"
15 | );
16 |
17 |
18 | class MyCustomHighOrderComponentExample extends Component {
19 | constructor(props, context){
20 | super(props, context);
21 | }
22 |
23 | onSumbit(){
24 | if (fieldValidatorCore.checkGroup("somegroup").isValid === true){
25 | //proceed
26 | }
27 | //or to check a specific field
28 | if (this.refs.MyCustomHighOrderComponentREF.isValid === true){
29 | //proceed
30 | }
31 | }
32 |
33 | render(){
34 | return (
35 | {
43 | return !validator.isEmpty(val);
44 | },
45 | errorPropValue: true,
46 | errorMessage: "Please enter a value"
47 | }
48 | ]}>
49 | {
53 | console.log(value);
54 | }}
55 | />
56 |
57 |
)
58 | }
59 | }
60 |
61 |
62 | MyCustomHighOrderComponentExample.propTypes = {};
63 |
64 | export default MyCustomHighOrderComponentExample;
65 |
--------------------------------------------------------------------------------
/examples/MaterialUiTextFieldExample.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from "react";
2 | import TextField from "material-ui/TextField";
3 | import {Validation, fieldValidatorCore} from "react-validation-framework";
4 |
5 | //support for material-ui textfield is needs just once, better place for it would be at the start of your app
6 | fieldValidatorCore.addSupport(
7 | "TextField",
8 | (event) => event[0].target.value,
9 | (callback, args) => callback(args[0], undefined, args[0].target.value),
10 | "error"
11 | );
12 |
13 | class UserDetailsFormComponent extends Component {
14 | constructor(props, context){
15 | super(props, context);
16 | }
17 |
18 | onSumbit(){
19 | if (fieldValidatorCore.checkGroup("userdetails").isValid === true){
20 | //proceed
21 | }
22 | //or to check a specific field
23 | if (this.refs.TextFieldREF.isValid === true){
24 | //proceed
25 | }
26 | }
27 |
28 | render(){
29 | return (
30 | !val === false ,
37 | errorPropValue: true,
38 | errorMessage: "Please enter a value"
39 | },
40 | {
41 | validator: (val) => {
42 | return new RegExp(/^[a-zA-Z]+(([',. -][a-zA-Z ])?[a-zA-Z]*)*\\.?$/).test(val);
43 | },
44 | errorPropValue: true,
45 | errorMessage: ""
46 | }]}>
47 | {
55 | dispatcher.publish(Actions.Action, value);
56 | }}
57 | SelectProps={{
58 | MenuProps: {
59 | className: classes.menu,
60 | },
61 | }}
62 | helperText=""
63 | margin="normal">
64 |
65 |
66 |
)
67 | }
68 | }
69 |
70 |
71 | UserDetailsFormComponent.propTypes = {};
72 |
73 | export default UserDetailsFormComponent;
74 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | (function webpackUniversalModuleDefinition(root, factory) {
2 | if(typeof exports === 'object' && typeof module === 'object')
3 | module.exports = factory(require("react"), require("prop-types"), require("lodash"));
4 | else if(typeof define === 'function' && define.amd)
5 | define(["react", "prop-types", "lodash"], factory);
6 | else if(typeof exports === 'object')
7 | exports["index"] = factory(require("react"), require("prop-types"), require("lodash"));
8 | else
9 | root["index"] = factory(root["React"], root["PropTypes"], root["_"]);
10 | })(this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_3__) {
11 | return /******/ (function(modules) { // webpackBootstrap
12 | /******/ // The module cache
13 | /******/ var installedModules = {};
14 |
15 | /******/ // The require function
16 | /******/ function __webpack_require__(moduleId) {
17 |
18 | /******/ // Check if module is in cache
19 | /******/ if(installedModules[moduleId])
20 | /******/ return installedModules[moduleId].exports;
21 |
22 | /******/ // Create a new module (and put it into the cache)
23 | /******/ var module = installedModules[moduleId] = {
24 | /******/ exports: {},
25 | /******/ id: moduleId,
26 | /******/ loaded: false
27 | /******/ };
28 |
29 | /******/ // Execute the module function
30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
31 |
32 | /******/ // Flag the module as loaded
33 | /******/ module.loaded = true;
34 |
35 | /******/ // Return the exports of the module
36 | /******/ return module.exports;
37 | /******/ }
38 |
39 |
40 | /******/ // expose the modules object (__webpack_modules__)
41 | /******/ __webpack_require__.m = modules;
42 |
43 | /******/ // expose the module cache
44 | /******/ __webpack_require__.c = installedModules;
45 |
46 | /******/ // __webpack_public_path__
47 | /******/ __webpack_require__.p = "";
48 |
49 | /******/ // Load entry module and return exports
50 | /******/ return __webpack_require__(0);
51 | /******/ })
52 | /************************************************************************/
53 | /******/ ([
54 | /* 0 */
55 | /***/ (function(module, exports, __webpack_require__) {
56 |
57 | "use strict";
58 |
59 | Object.defineProperty(exports, "__esModule", {
60 | value: true
61 | });
62 | exports.fieldValidatorCore = exports.Validation = undefined;
63 |
64 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
65 |
66 | var _react = __webpack_require__(1);
67 |
68 | var _react2 = _interopRequireDefault(_react);
69 |
70 | var _propTypes = __webpack_require__(2);
71 |
72 | var _propTypes2 = _interopRequireDefault(_propTypes);
73 |
74 | var _lodash = __webpack_require__(3);
75 |
76 | var _lodash2 = _interopRequireDefault(_lodash);
77 |
78 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
79 |
80 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
81 |
82 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
83 |
84 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
85 |
86 | function generateUUID() {
87 | var basex = 16;
88 | var d = new Date().getTime();
89 | if (window.performance && typeof window.performance.now === "function") {
90 | d += performance.now(); //use high-precision timer if available
91 | }
92 | var uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
93 | var r = (d + Math.random() * basex) % basex | 0;
94 | d = Math.floor(d / basex);
95 | var sr = 0x3;
96 | var srx = r & sr;
97 | var srx1 = 0x8;
98 | return (c === "x" ? r : srx | srx1).toString(basex);
99 | });
100 | return uuid;
101 | }
102 |
103 | var groups = {};
104 | var userAddedComponents = {};
105 |
106 | var internalSupportedComponents = {
107 | TextField: {
108 | getValueFromChangeEvent: function getValueFromChangeEvent(args) {
109 | return args[0].target.value;
110 | },
111 | changeCallBackCaller: function changeCallBackCaller(callback, args) {
112 | callback(args[0]);
113 | },
114 | errorPropName: "errorText",
115 | helperPropName: "helperText"
116 | },
117 | SelectField: {
118 | getValueFromChangeEvent: function getValueFromChangeEvent(args) {
119 | return args[1];
120 | },
121 | changeCallBackCaller: function changeCallBackCaller(callback, args) {
122 | callback(args[0], args[1], args[2]);
123 | },
124 | errorPropName: "errorText"
125 | },
126 | DateRangePicker: {
127 | getValueFromChangeEvent: function getValueFromChangeEvent(args) {
128 | return args[0];
129 | },
130 | changeCallBackCaller: function changeCallBackCaller(callback, args) {
131 | callback(args[0]);
132 | },
133 | errorPropName: "errorText"
134 | },
135 | DatePicker: {
136 | getValueFromChangeEvent: function getValueFromChangeEvent(args) {
137 | return args[1];
138 | },
139 | changeCallBackCaller: function changeCallBackCaller(callback, args) {
140 | callback(args, args[1]);
141 | }
142 | },
143 | Select: {
144 | getValueFromChangeEvent: function getValueFromChangeEvent(args) {
145 | return args[0];
146 | },
147 | changeCallBackCaller: function changeCallBackCaller(callback, args) {
148 | callback(args[0]);
149 | }
150 | }
151 | };
152 |
153 | function getAllSupportedComponent() {
154 | return Object.assign({}, internalSupportedComponents, userAddedComponents);
155 | }
156 |
157 | var fieldValidatorCore = {
158 | addSupport: function addSupport(name, getValueFromChangeEvent, changeCallBackCaller, errorPropName, helperPropName) {
159 | userAddedComponents[name] = {
160 | getValueFromChangeEvent: getValueFromChangeEvent,
161 | changeCallBackCaller: changeCallBackCaller,
162 | errorPropName: errorPropName,
163 | helperPropName: helperPropName
164 | };
165 | },
166 | removeSupport: function removeSupport(name) {
167 | if (userAddedComponents[name] !== undefined) {
168 | delete userAddedComponents.name;
169 | } else {
170 | console.info("Field-Validator", "removeComponent: didn't find the component");
171 | }
172 | },
173 | getAllCurrentlySupported: function getAllCurrentlySupported() {
174 | return getAllSupportedComponent();
175 | },
176 | checkGroup: function checkGroup(groupName) {
177 | var valid = true;
178 | var validityRes = {
179 | isValid: true,
180 | validCompponents: [],
181 | inValidComponents: []
182 | };
183 | var allCompsInGroup = groups[groupName];
184 | if (allCompsInGroup === undefined) {
185 | valid = true;
186 | } else {
187 | allCompsInGroup.forEach(function (v1) {
188 | var v = v1.component;
189 | if (v.isValid === false) {
190 | valid = false;
191 | validityRes.inValidComponents.push(v.props.children);
192 | } else {
193 | validityRes.validCompponents.push(v.props.children);
194 | }
195 | });
196 | }
197 | validityRes.isValid = valid;
198 | return validityRes;
199 | }
200 | };
201 |
202 | var Validation = function (_Component) {
203 | _inherits(Validation, _Component);
204 |
205 | function Validation(props) {
206 | _classCallCheck(this, Validation);
207 |
208 | var _this = _possibleConstructorReturn(this, (Validation.__proto__ || Object.getPrototypeOf(Validation)).call(this, props));
209 |
210 | _this.state = {
211 | childComponentToRender: null,
212 | unControlledChild: true,
213 | isValid: true,
214 | id: generateUUID()
215 | };
216 | _this.typeOfCompnent = _this.props.componentTag ? _this.props.componentTag : _this.props.children.type.displayName ? _this.props.children.type.displayName : _this.props.children.type.name;
217 | _this.testValidity = _this.testValidity.bind(_this);
218 | return _this;
219 | }
220 |
221 | _createClass(Validation, [{
222 | key: "componentWillReceiveProps",
223 | value: function componentWillReceiveProps(props) {
224 | var freshRendered = false;
225 | if (this.state.unsupported !== true) {
226 | if (this.state.unControlledChild === false) {
227 | var isDerivedValueComing = false;
228 | if (!_lodash2.default.isEqual(this.originalVal, props.children.props[this.props.valueProp])) {
229 | isDerivedValueComing = true;
230 | }
231 | if (this.childModified === true || isDerivedValueComing) {
232 | if (!_lodash2.default.isEqual(this.currentChildValue, props.children.props[this.props.valueProp])) {
233 | this.baseProps[this.props.valueProp] = props.children.props[this.props.valueProp];
234 | this.currentChildValue = props.children.props[this.props.valueProp];
235 | freshRendered = true;
236 | this.testValidity(this.currentChildValue);
237 | }
238 | }
239 | }
240 | }
241 | if (Object.keys(this.closureValues).length > 0 && freshRendered === false) {
242 | //match closures
243 | var requireRender = false;
244 | _lodash2.default.forOwn(this.closureValues, function (cVariableValue, cVariable) {
245 | if (!_lodash2.default.isEqual(cVariableValue, props.closures[cVariable])) {
246 | requireRender = true;
247 | }
248 | });
249 | if (requireRender) {
250 | this.mountingSetup(getAllSupportedComponent()[this.typeOfCompnent].getValueFromChangeEvent, getAllSupportedComponent()[this.typeOfCompnent].changeCallBackCaller, false, props);
251 | //also test validity if closure changes -- added if any validation dependes on closure values
252 | this.testValidity(this.currentChildValue);
253 | }
254 | }
255 | }
256 | }, {
257 | key: "componentDidMount",
258 | value: function componentDidMount() {
259 | if (userAddedComponents[this.typeOfCompnent] !== undefined) {
260 | this.mountingSetup(userAddedComponents[this.typeOfCompnent].getValueFromChangeEvent, userAddedComponents[this.typeOfCompnent].changeCallBackCaller);
261 | } else {
262 | if (internalSupportedComponents[this.typeOfCompnent] !== undefined) {
263 | this.mountingSetup(internalSupportedComponents[this.typeOfCompnent].getValueFromChangeEvent, internalSupportedComponents[this.typeOfCompnent].changeCallBackCaller);
264 | } else {
265 | console.error("Field-Validator", this.typeOfCompnent + " is currently not supported by field-validator,\n Please use fieldValidatorCore.addSupport to add support for the component, For more information please refer to docs");
266 | console.info("Field-Validator", "Ignoring " + this.typeOfCompnent + ", and it will work as if it was not wraped with Validation tag");
267 | this.mountingSetup(null, null, true);
268 | }
269 | }
270 | if (this.props.group && this.state.unsupported !== true) {
271 | if (groups[this.props.group] === undefined) {
272 | groups[this.props.group] = [];
273 | }
274 | groups[this.props.group].push({
275 | id: this.state.id,
276 | component: this
277 | });
278 | }
279 | }
280 | }, {
281 | key: "mountingSetup",
282 | value: function mountingSetup(valueFromArgs, argsToPassToActualHandler, unsupportedFlag, nextProps) {
283 | var _this2 = this;
284 |
285 | var toUseProps = nextProps ? nextProps : this.props;
286 | if (unsupportedFlag === true) {
287 | this.setState({
288 | childComponentToRender: toUseProps.children,
289 | unsupported: unsupportedFlag
290 | });
291 | } else {
292 | this.closureValues = {};
293 | if (Object.keys(toUseProps.closures).length > 0) {
294 | _lodash2.default.forOwn(toUseProps.closures, function (cVariableValue, cVariable) {
295 | _this2.closureValues[cVariable] = cVariableValue;
296 | });
297 | }
298 | this.baseProps = _lodash2.default.cloneDeep(toUseProps.children.props);
299 | var isUncontrolled = true;
300 | if (this.baseProps.hasOwnProperty(toUseProps.valueProp)) {
301 | isUncontrolled = false;
302 | if (nextProps !== true) {
303 | this.originalVal = this.baseProps[toUseProps.valueProp];
304 | }
305 | this.currentChildValue = this.baseProps[toUseProps.valueProp];
306 | } else {
307 | //try with default prop
308 | if (this.baseProps.hasOwnProperty(toUseProps.defaultValueProp)) {
309 | if (nextProps !== true) {
310 | this.originalVal = this.baseProps[toUseProps.defaultValueProp];
311 | }
312 | this.currentChildValue = this.baseProps[toUseProps.defaultValueProp];
313 | }
314 | }
315 |
316 | var oldOnChange = this.baseProps[toUseProps.onChangeCallback];
317 | this.baseProps[toUseProps.onChangeCallback] = function () {
318 | for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
319 | args[_key] = arguments[_key];
320 | }
321 |
322 | var rArgs = valueFromArgs(args);
323 | _this2.childModified = true;
324 | if (!_this2.absorbing) {
325 | _this2.absorbing = true;
326 | try {
327 | _this2.baseProps[toUseProps.valueProp] = rArgs;
328 | _this2.currentChildValue = rArgs;
329 | _this2.testValidity(rArgs);
330 | if (oldOnChange) {
331 | argsToPassToActualHandler(oldOnChange, args);
332 | }
333 | } catch (er) {
334 | _this2.absorbing = false;
335 | }
336 | _this2.absorbing = false;
337 | }
338 | };
339 | var theComponent = _react2.default.cloneElement(toUseProps.children, this.baseProps);
340 | this.setState({
341 | childComponentToRender: theComponent,
342 | unControlledChild: isUncontrolled
343 | });
344 | }
345 | }
346 | }, {
347 | key: "testValidity",
348 | value: function testValidity(val) {
349 | var res = {
350 | isValid: true,
351 | helperMessage: null,
352 | errorMessage: null,
353 | errorPropValue: null
354 | };
355 | try {
356 | this.props.validators.every(function (v) {
357 | if (v.validator(val) === false) {
358 | res.isValid = false;
359 | res.helperMessage = v.errorMessage;
360 | res.errorMessage = v.errorMessage;
361 | res.errorPropValue = v.errorPropValue ? v.errorPropValue : v.errorMessage;
362 | return false;
363 | } else {
364 | return true;
365 | }
366 | });
367 | } catch (err) {
368 | console.error(err);
369 | }
370 | if (res.isValid === false) {
371 | if (getAllSupportedComponent()[this.typeOfCompnent].errorPropName) {
372 | this.baseProps[getAllSupportedComponent()[this.typeOfCompnent].errorPropName] = res.errorPropValue;
373 | this.baseProps[getAllSupportedComponent()[this.typeOfCompnent].helperPropName] = res.helperMessage;
374 | }
375 | this.setState({
376 | childComponentToRender: _react2.default.cloneElement(this.props.children, this.baseProps),
377 | isValid: false,
378 | errorText: res.errorMessage,
379 | helperText: res.errorMessage
380 | });
381 | } else {
382 | if (getAllSupportedComponent()[this.typeOfCompnent].errorPropName) {
383 | this.baseProps[getAllSupportedComponent()[this.typeOfCompnent].errorPropName] = null;
384 | this.baseProps[getAllSupportedComponent()[this.typeOfCompnent].helperPropName] = null;
385 | }
386 | this.setState({
387 | childComponentToRender: _react2.default.cloneElement(this.props.children, this.baseProps),
388 | isValid: true,
389 | errorText: null,
390 | helperText: null
391 | });
392 | }
393 | return res;
394 | }
395 | }, {
396 | key: "componentWillUnmount",
397 | value: function componentWillUnmount() {
398 | var _this3 = this;
399 |
400 | if (this.props.group) {
401 | _lodash2.default.remove(groups[this.props.group], function (v) {
402 | return v.id === _this3.state.id;
403 | });
404 | }
405 | }
406 | }, {
407 | key: "render",
408 | value: function render() {
409 | if (this.state.unsupported === true) {
410 | return this.props.children;
411 | } else {
412 | return _react2.default.createElement(
413 | "span",
414 | null,
415 | this.state.childComponentToRender ? this.state.childComponentToRender : "",
416 | !getAllSupportedComponent()[this.typeOfCompnent].errorPropName && this.state.isValid === false ? _react2.default.createElement(
417 | "div",
418 | { style: Object.assign({}, { color: "red", fontSize: "12px", position: "absolute" }, this.props.errorStyle) },
419 | this.state.errorText
420 | ) : ""
421 | );
422 | }
423 | }
424 | }, {
425 | key: "isValid",
426 | get: function get() {
427 | return this.testValidity(this.currentChildValue).isValid;
428 | }
429 | }, {
430 | key: "errorMessage",
431 | get: function get() {
432 | return this.testValidity(this.currentChildValue).errorMessage;
433 | }
434 | }, {
435 | key: "errorPropValue",
436 | get: function get() {
437 | return this.testValidity(this.currentChildValue).errorPropValue;
438 | }
439 | }, {
440 | key: "isModified",
441 | get: function get() {
442 | return this.childModified;
443 | }
444 | }]);
445 |
446 | return Validation;
447 | }(_react.Component);
448 |
449 | Validation.propTypes = {
450 | children: _propTypes2.default.oneOfType([_propTypes2.default.element]),
451 | validators: _propTypes2.default.array.isRequired,
452 | onChangeCallback: _propTypes2.default.string,
453 | group: _propTypes2.default.string,
454 | valueProp: _propTypes2.default.string,
455 | defaultValueProp: _propTypes2.default.string,
456 | errorStyle: _propTypes2.default.object,
457 | closures: _propTypes2.default.object,
458 | componentTag: _propTypes2.default.string
459 | };
460 |
461 | Validation.defaultProps = {
462 | onChangeCallback: "onChange",
463 | valueProp: "value",
464 | defaultValueProp: "defaultValue",
465 | errorStyle: {},
466 | closures: {}
467 | };
468 |
469 | exports.Validation = Validation;
470 | exports.fieldValidatorCore = fieldValidatorCore;
471 |
472 | /***/ }),
473 | /* 1 */
474 | /***/ (function(module, exports) {
475 |
476 | module.exports = __WEBPACK_EXTERNAL_MODULE_1__;
477 |
478 | /***/ }),
479 | /* 2 */
480 | /***/ (function(module, exports) {
481 |
482 | module.exports = __WEBPACK_EXTERNAL_MODULE_2__;
483 |
484 | /***/ }),
485 | /* 3 */
486 | /***/ (function(module, exports) {
487 |
488 | module.exports = __WEBPACK_EXTERNAL_MODULE_3__;
489 |
490 | /***/ })
491 | /******/ ])
492 | });
493 | ;
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-validation-framework",
3 | "version": "5.1.0",
4 | "description": "Framework to validate fields, supports all components including material-ui components",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "webpack"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/vishalvisd/react-validator.git"
13 | },
14 | "keywords": [
15 | "React",
16 | "Validator",
17 | "material-ui",
18 | "html"
19 | ],
20 | "author": "Vishal Daga",
21 | "license": "ISC",
22 | "bugs": {
23 | "url": "https://github.com/vishalvisd/react-validator/issues"
24 | },
25 | "homepage": "https://github.com/vishalvisd/react-validator#readme",
26 | "dependencies": {
27 | "prop-types": "^15.6.1",
28 | "react": "16.1.1"
29 | },
30 | "peerDependencies": {
31 | "lodash": "^4.17.4"
32 | },
33 | "devDependencies": {
34 | "babel-core": "^6.21.0",
35 | "babel-loader": "^6.2.10",
36 | "babel-plugin-transform-object-rest-spread": "^6.20.2",
37 | "babel-preset-es2015": "6.18.0",
38 | "babel-preset-react": "6.16.0",
39 | "babel-preset-stage-0": "6.16.0",
40 | "webpack": "^1.14.0"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import PropTypes from "prop-types";
3 | import _ from "lodash";
4 |
5 | function generateUUID() {
6 | var basex = 16;
7 | var d = new Date().getTime();
8 | if (window.performance && typeof window.performance.now === "function"){
9 | d += performance.now(); //use high-precision timer if available
10 | }
11 | var uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
12 | var r = (d + Math.random()*basex)%basex | 0;
13 | d = Math.floor(d/basex);
14 | var sr = 0x3;
15 | var srx = r&sr;
16 | var srx1 = 0x8;
17 | return (c==="x" ? r : (srx|srx1)).toString(basex);
18 | });
19 | return uuid;
20 | }
21 |
22 | let groups = {};
23 | let userAddedComponents = {};
24 |
25 | let internalSupportedComponents = {
26 | TextField: {
27 | getValueFromChangeEvent: (args)=>{
28 | return args[0].target.value;
29 | },
30 | changeCallBackCaller: (callback, args)=>{
31 | callback(args[0]);
32 | },
33 | errorPropName: "errorText",
34 | helperPropName: "helperText"
35 | },
36 | SelectField: {
37 | getValueFromChangeEvent: (args)=>{
38 | return args[1];
39 | },
40 | changeCallBackCaller: (callback, args)=>{
41 | callback(args[0], args[1], args[2]);
42 | },
43 | errorPropName: "errorText"
44 | },
45 | DateRangePicker: {
46 | getValueFromChangeEvent: (args)=>{
47 | return args[0];
48 | },
49 | changeCallBackCaller: (callback, args)=>{
50 | callback(args[0]);
51 | },
52 | errorPropName: "errorText"
53 | },
54 | DatePicker: {
55 | getValueFromChangeEvent: (args)=>{
56 | return args[1];
57 | },
58 | changeCallBackCaller: (callback, args)=>{
59 | callback(args, args[1]);
60 | }
61 | },
62 | Select: {
63 | getValueFromChangeEvent: (args)=>{
64 | return args[0];
65 | },
66 | changeCallBackCaller: (callback, args)=>{
67 | callback(args[0]);
68 | }
69 | }
70 | };
71 |
72 | function getAllSupportedComponent(){
73 | return Object.assign({}, internalSupportedComponents, userAddedComponents);
74 | }
75 |
76 | let fieldValidatorCore = {
77 | addSupport: (name, getValueFromChangeEvent, changeCallBackCaller, errorPropName, helperPropName) => {
78 | userAddedComponents[name] = {
79 | getValueFromChangeEvent: getValueFromChangeEvent,
80 | changeCallBackCaller: changeCallBackCaller,
81 | errorPropName: errorPropName,
82 | helperPropName: helperPropName
83 | };
84 | },
85 | removeSupport: (name) => {
86 | if (userAddedComponents[name] !== undefined){
87 | delete userAddedComponents.name;
88 | } else {
89 | console.info("Field-Validator", "removeComponent: didn't find the component");
90 | }
91 | },
92 | getAllCurrentlySupported: ()=>{
93 | return getAllSupportedComponent();
94 | },
95 | checkGroup: (groupName) => {
96 | let valid = true;
97 | let validityRes = {
98 | isValid: true,
99 | validCompponents: [],
100 | inValidComponents: []
101 | };
102 | let allCompsInGroup = groups[groupName];
103 | if (allCompsInGroup === undefined) {
104 | valid = true;
105 | } else {
106 | allCompsInGroup.forEach((v1) => {
107 | let v = v1.component;
108 | if (v.isValid === false){
109 | valid = false;
110 | validityRes.inValidComponents.push(v.props.children);
111 | } else {
112 | validityRes.validCompponents.push(v.props.children);
113 | }
114 | });
115 | }
116 | validityRes.isValid = valid;
117 | return validityRes;
118 | }
119 | };
120 |
121 | class Validation extends Component {
122 |
123 | constructor(props) {
124 | super(props);
125 |
126 | this.state = {
127 | childComponentToRender: null,
128 | unControlledChild: true,
129 | isValid: true,
130 | id: generateUUID()
131 | };
132 | this.typeOfCompnent = this.props.componentTag ? this.props.componentTag : (this.props.children.type.displayName ? this.props.children.type.displayName : this.props.children.type.name);
133 | this.testValidity = this.testValidity.bind(this);
134 | }
135 | get isValid() {
136 | return this.testValidity(this.currentChildValue).isValid;
137 | }
138 |
139 | get errorMessage() {
140 | return this.testValidity(this.currentChildValue).errorMessage;
141 | }
142 |
143 | get errorPropValue() {
144 | return this.testValidity(this.currentChildValue).errorPropValue;
145 | }
146 |
147 | get isModified() {
148 | return this.childModified;
149 | }
150 |
151 | componentWillReceiveProps(props){
152 | let freshRendered = false;
153 | if (this.state.unsupported !== true){
154 | if (this.state.unControlledChild === false){
155 | let isDerivedValueComing = false;
156 | if (!(_.isEqual(this.originalVal, props.children.props[this.props.valueProp]))){
157 | isDerivedValueComing = true;
158 | }
159 | if (this.childModified === true || isDerivedValueComing){
160 | if (!(_.isEqual(this.currentChildValue, props.children.props[this.props.valueProp]))){
161 | this.baseProps[this.props.valueProp] = props.children.props[this.props.valueProp];
162 | this.currentChildValue = props.children.props[this.props.valueProp];
163 | freshRendered = true;
164 | this.testValidity(this.currentChildValue);
165 | }
166 | }
167 | }
168 | }
169 | if (Object.keys(this.closureValues).length > 0 && freshRendered === false){
170 | //match closures
171 | let requireRender = false;
172 | _.forOwn(this.closureValues, (cVariableValue, cVariable)=>{
173 | if (!(_.isEqual(cVariableValue, props.closures[cVariable]))){
174 | requireRender = true;
175 | }
176 | });
177 | if (requireRender){
178 | this.mountingSetup(getAllSupportedComponent()[this.typeOfCompnent].getValueFromChangeEvent, getAllSupportedComponent()[this.typeOfCompnent].changeCallBackCaller, false, props);
179 | //also test validity if closure changes -- added if any validation dependes on closure values
180 | this.testValidity(this.currentChildValue);
181 | }
182 | }
183 | }
184 |
185 | componentDidMount(){
186 | if (userAddedComponents[this.typeOfCompnent] !== undefined){
187 | this.mountingSetup(userAddedComponents[this.typeOfCompnent].getValueFromChangeEvent, userAddedComponents[this.typeOfCompnent].changeCallBackCaller);
188 | } else {
189 | if (internalSupportedComponents[this.typeOfCompnent] !== undefined){
190 | this.mountingSetup(internalSupportedComponents[this.typeOfCompnent].getValueFromChangeEvent, internalSupportedComponents[this.typeOfCompnent].changeCallBackCaller);
191 | } else {
192 | console.error("Field-Validator",
193 | `${this.typeOfCompnent} is currently not supported by field-validator,
194 | Please use fieldValidatorCore.addSupport to add support for the component, For more information please refer to docs`);
195 | console.info("Field-Validator", `Ignoring ${this.typeOfCompnent}, and it will work as if it was not wraped with Validation tag`);
196 | this.mountingSetup(null, null, true);
197 | }
198 | }
199 | if (this.props.group && this.state.unsupported !== true){
200 | if (groups[this.props.group] === undefined){
201 | groups[this.props.group] = [];
202 | }
203 | groups[this.props.group].push({
204 | id: this.state.id,
205 | component: this
206 | });
207 | }
208 | }
209 |
210 | mountingSetup(valueFromArgs, argsToPassToActualHandler, unsupportedFlag, nextProps){
211 | let toUseProps = nextProps ? nextProps : this.props;
212 | if (unsupportedFlag === true){
213 | this.setState({
214 | childComponentToRender: toUseProps.children,
215 | unsupported: unsupportedFlag
216 | });
217 | } else {
218 | this.closureValues = {};
219 | if (Object.keys(toUseProps.closures).length > 0) {
220 | _.forOwn(toUseProps.closures, (cVariableValue, cVariable) => {
221 | this.closureValues[cVariable] = cVariableValue;
222 | });
223 | }
224 | this.baseProps = _.cloneDeep(toUseProps.children.props);
225 | let isUncontrolled = true;
226 | if (this.baseProps.hasOwnProperty(toUseProps.valueProp)){
227 | isUncontrolled = false;
228 | if (nextProps !== true){
229 | this.originalVal = this.baseProps[toUseProps.valueProp];
230 | }
231 | this.currentChildValue = this.baseProps[toUseProps.valueProp];
232 | } else {
233 | //try with default prop
234 | if (this.baseProps.hasOwnProperty(toUseProps.defaultValueProp)){
235 | if (nextProps !== true){
236 | this.originalVal = this.baseProps[toUseProps.defaultValueProp];
237 | }
238 | this.currentChildValue = this.baseProps[toUseProps.defaultValueProp];
239 | }
240 | }
241 |
242 | let oldOnChange = this.baseProps[toUseProps.onChangeCallback];
243 | this.baseProps[toUseProps.onChangeCallback] = (...args)=>{
244 | let rArgs = valueFromArgs(args);
245 | this.childModified = true;
246 | if (!this.absorbing){
247 | this.absorbing = true;
248 | try {
249 | this.baseProps[toUseProps.valueProp] = rArgs;
250 | this.currentChildValue = rArgs;
251 | this.testValidity(rArgs);
252 | if (oldOnChange) {
253 | argsToPassToActualHandler(oldOnChange, args);
254 | }
255 | } catch (er){
256 | this.absorbing = false;
257 | }
258 | this.absorbing = false;
259 | }
260 | };
261 | let theComponent = React.cloneElement(toUseProps.children, this.baseProps);
262 | this.setState({
263 | childComponentToRender: theComponent,
264 | unControlledChild: isUncontrolled
265 | });
266 | }
267 | }
268 |
269 | testValidity(val){
270 | let res = {
271 | isValid: true,
272 | helperMessage: null,
273 | errorMessage: null,
274 | errorPropValue: null
275 | };
276 | try {
277 | this.props.validators.every((v)=>{
278 | if (v.validator(val) === false){
279 | res.isValid = false;
280 | res.helperMessage = v.errorMessage;
281 | res.errorMessage = v.errorMessage;
282 | res.errorPropValue = v.errorPropValue ? v.errorPropValue : v.errorMessage;
283 | return false;
284 | } else {
285 | return true;
286 | }
287 | });
288 | } catch (err) {
289 | console.error(err);
290 | }
291 | if (res.isValid === false){
292 | if (getAllSupportedComponent()[this.typeOfCompnent].errorPropName){
293 | this.baseProps[getAllSupportedComponent()[this.typeOfCompnent].errorPropName] = res.errorPropValue;
294 | this.baseProps[getAllSupportedComponent()[this.typeOfCompnent].helperPropName] = res.helperMessage;
295 | }
296 | this.setState({
297 | childComponentToRender: React.cloneElement(this.props.children, this.baseProps),
298 | isValid: false,
299 | errorText: res.errorMessage,
300 | helperText: res.errorMessage
301 | });
302 | } else {
303 | if (getAllSupportedComponent()[this.typeOfCompnent].errorPropName){
304 | this.baseProps[getAllSupportedComponent()[this.typeOfCompnent].errorPropName] = null;
305 | this.baseProps[getAllSupportedComponent()[this.typeOfCompnent].helperPropName] = null;
306 | }
307 | this.setState({
308 | childComponentToRender: React.cloneElement(this.props.children, this.baseProps),
309 | isValid: true,
310 | errorText: null,
311 | helperText: null
312 | });
313 | }
314 | return res;
315 | }
316 |
317 | componentWillUnmount(){
318 | if (this.props.group){
319 | _.remove(groups[this.props.group], (v)=>{
320 | return v.id === this.state.id;
321 | });
322 | }
323 | }
324 |
325 | render() {
326 | if (this.state.unsupported === true){
327 | return this.props.children;
328 | } else {
329 | return (
330 |
331 | {
332 | this.state.childComponentToRender ? this.state.childComponentToRender : ""
333 | }{
334 | (!(getAllSupportedComponent()[this.typeOfCompnent].errorPropName)) && this.state.isValid === false ?
335 |
336 | {
337 | this.state.errorText
338 | }
339 |
: ""
340 | }
341 |
342 | );
343 | }
344 | }
345 | }
346 |
347 | Validation.propTypes = {
348 | children: PropTypes.oneOfType([
349 | PropTypes.element
350 | ]),
351 | validators: PropTypes.array.isRequired,
352 | onChangeCallback: PropTypes.string,
353 | group: PropTypes.string,
354 | valueProp: PropTypes.string,
355 | defaultValueProp: PropTypes.string,
356 | errorStyle: PropTypes.object,
357 | closures: PropTypes.object,
358 | componentTag: PropTypes.string
359 | };
360 |
361 | Validation.defaultProps = {
362 | onChangeCallback: "onChange",
363 | valueProp: "value",
364 | defaultValueProp: "defaultValue",
365 | errorStyle: {},
366 | closures: {}
367 | };
368 |
369 | export {Validation, fieldValidatorCore};
370 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var webpack = require("webpack");
2 | module.exports = [{
3 | entry: {
4 | index: "./src.js"
5 | },
6 | output: {
7 | filename: "[name].js",
8 | libraryTarget: "umd",
9 | library: "[name]"
10 | },
11 | module: {
12 | loaders: [
13 | {
14 | test: /\.js$/,
15 | loader: "babel?presets[]=es2015,presets[]=react,presets[]=stage-0,plugins[]=transform-object-rest-spread"
16 | }
17 | ]
18 | },
19 | externals: {
20 | lodash: {
21 | amd: "lodash",
22 | commonjs: "lodash",
23 | commonjs2: "lodash",
24 | root: "_"
25 | },
26 | "prop-types": {
27 | amd: "prop-types",
28 | commonjs2: "prop-types",
29 | commonjs: "prop-types",
30 | root: "PropTypes"
31 | },
32 | react: {
33 | amd: "react",
34 | commonjs2: "react",
35 | commonjs: "react",
36 | root: "React"
37 | }
38 | }
39 | }];
--------------------------------------------------------------------------------