@coxy/react-validator 1.3.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/.eslintignore +1 -1
  2. package/.eslintrc.js +25 -8
  3. package/README.md +10 -0
  4. package/babel.config.js +3 -0
  5. package/dist/context.d.ts +5 -0
  6. package/dist/context.js +5 -0
  7. package/dist/index.d.ts +5 -0
  8. package/dist/index.js +13 -0
  9. package/dist/rules.d.ts +37 -0
  10. package/dist/rules.js +43 -0
  11. package/dist/use-validator.d.ts +3 -0
  12. package/dist/use-validator.js +22 -0
  13. package/dist/validator-field.d.ts +25 -0
  14. package/dist/validator-field.jsx +34 -0
  15. package/dist/validator-wrapper.d.ts +17 -0
  16. package/dist/validator-wrapper.jsx +43 -0
  17. package/dist/validator.d.ts +28 -0
  18. package/dist/validator.js +72 -0
  19. package/example/example.jsx +29 -26
  20. package/example/index.html +2 -0
  21. package/example/webpack.config.js +15 -7
  22. package/jest.config.ts +10 -0
  23. package/package.json +35 -28
  24. package/src/context.test.ts +7 -0
  25. package/src/context.ts +6 -0
  26. package/src/index.test.ts +7 -0
  27. package/src/index.ts +5 -0
  28. package/src/jest.d.ts +1 -0
  29. package/src/rules.test.ts +127 -0
  30. package/src/{rules.js → rules.ts} +26 -15
  31. package/src/use-validator.test.tsx +58 -0
  32. package/src/use-validator.ts +10 -0
  33. package/src/validator-field.test.tsx +161 -0
  34. package/src/validator-field.tsx +71 -0
  35. package/src/validator-wrapper.test.tsx +138 -0
  36. package/src/validator-wrapper.tsx +58 -0
  37. package/src/validator.test.tsx +30 -0
  38. package/src/validator.ts +105 -0
  39. package/tsconfig.build.json +12 -0
  40. package/tsconfig.json +39 -0
  41. package/.babelrc.js +0 -14
  42. package/build/index.js +0 -1
  43. package/jest.config.js +0 -5
  44. package/src/context.js +0 -3
  45. package/src/context.test.js +0 -9
  46. package/src/index.js +0 -11
  47. package/src/index.test.js +0 -9
  48. package/src/rules.test.js +0 -130
  49. package/src/validator-field.jsx +0 -72
  50. package/src/validator-field.test.js +0 -187
  51. package/src/validator-wrapper.jsx +0 -63
  52. package/src/validator-wrapper.test.js +0 -174
  53. package/src/validator.js +0 -82
  54. package/src/validator.test.js +0 -36
  55. package/webpack.config.js +0 -32
package/.eslintignore CHANGED
@@ -1,3 +1,3 @@
1
- build
1
+ dist
2
2
  node_modules
3
3
  example/dist
package/.eslintrc.js CHANGED
@@ -1,11 +1,28 @@
1
1
  module.exports = {
2
- parser: 'babel-eslint',
3
- env: {
4
- browser: true,
5
- },
2
+ root: true,
3
+ parser: '@typescript-eslint/parser',
4
+ plugins: [
5
+ 'react',
6
+ 'import',
7
+ '@typescript-eslint'
8
+ ],
6
9
  rules: {
7
- 'react/destructuring-assignment': [0],
8
- 'import/no-extraneous-dependencies': [0],
10
+ 'no-void': [0],
11
+ 'react/jsx-tag-spacing': ['error', {
12
+ closingSlash: 'never',
13
+ beforeSelfClosing: 'always',
14
+ afterOpening: 'never',
15
+ beforeClosing: 'never'
16
+ }],
17
+ 'import/order': ['error', {
18
+ groups: ['type', 'external', 'internal', 'builtin', 'object', 'index', 'parent', 'sibling'],
19
+ 'newlines-between': 'always'
20
+ }]
9
21
  },
10
- extends: 'airbnb',
11
- };
22
+ extends: [
23
+ 'standard',
24
+ 'eslint:recommended',
25
+ 'plugin:@typescript-eslint/eslint-recommended',
26
+ 'plugin:@typescript-eslint/recommended'
27
+ ]
28
+ }
package/README.md CHANGED
@@ -121,6 +121,16 @@ This component has a default set of rules that you can use right away:
121
121
  required | true | no | The field will be required
122
122
  id | null | no | ID for get field
123
123
 
124
+  
125
+ # React api useValidator
126
+
127
+ ```javascript
128
+
129
+ const [isValid, { errors }] = useValidator('test value', rules.email)
130
+ console.log(isValid, errors) // false
131
+
132
+ ```
133
+
124
134
   
125
135
  # Api for inline validation
126
136
 
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+ presets: ['@babel/preset-env', '@babel/preset-react']
3
+ }
@@ -0,0 +1,5 @@
1
+ /// <reference types="react" />
2
+ export declare const Context: import("react").Context<{
3
+ registerField: (field: string | number) => void;
4
+ unregisterField: (field: string | number) => void;
5
+ }>;
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Context = void 0;
4
+ const react_1 = require("react");
5
+ exports.Context = (0, react_1.createContext)(null);
@@ -0,0 +1,5 @@
1
+ export { rules } from './rules';
2
+ export { ValidatorField } from './validator-field';
3
+ export { ValidatorWrapper } from './validator-wrapper';
4
+ export { Validator } from './validator';
5
+ export { useValidator } from './use-validator';
package/dist/index.js ADDED
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useValidator = exports.Validator = exports.ValidatorWrapper = exports.ValidatorField = exports.rules = void 0;
4
+ var rules_1 = require("./rules");
5
+ Object.defineProperty(exports, "rules", { enumerable: true, get: function () { return rules_1.rules; } });
6
+ var validator_field_1 = require("./validator-field");
7
+ Object.defineProperty(exports, "ValidatorField", { enumerable: true, get: function () { return validator_field_1.ValidatorField; } });
8
+ var validator_wrapper_1 = require("./validator-wrapper");
9
+ Object.defineProperty(exports, "ValidatorWrapper", { enumerable: true, get: function () { return validator_wrapper_1.ValidatorWrapper; } });
10
+ var validator_1 = require("./validator");
11
+ Object.defineProperty(exports, "Validator", { enumerable: true, get: function () { return validator_1.Validator; } });
12
+ var use_validator_1 = require("./use-validator");
13
+ Object.defineProperty(exports, "useValidator", { enumerable: true, get: function () { return use_validator_1.useValidator; } });
@@ -0,0 +1,37 @@
1
+ declare type Fn = (value: any) => string;
2
+ export interface RuleInstance {
3
+ rule: (value: any) => boolean;
4
+ message: string | Fn;
5
+ }
6
+ export declare type ValidatorRules = RuleInstance[];
7
+ export declare const rules: {
8
+ notEmpty: {
9
+ rule: (value: any) => boolean;
10
+ message: string;
11
+ }[];
12
+ bool: {
13
+ rule: (value: any) => boolean;
14
+ message: string;
15
+ }[];
16
+ password: {
17
+ rule: (value: any) => boolean;
18
+ message: string;
19
+ }[];
20
+ email: {
21
+ rule: (value: any) => boolean;
22
+ message: string;
23
+ }[];
24
+ min: (min: any) => {
25
+ rule: (value: any) => boolean;
26
+ message: string;
27
+ }[];
28
+ max: (max: any) => {
29
+ rule: (value: any) => boolean;
30
+ message: string;
31
+ }[];
32
+ length: (min: any, max?: any) => {
33
+ rule: (value: any) => boolean;
34
+ message: string;
35
+ }[];
36
+ };
37
+ export {};
package/dist/rules.js ADDED
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rules = void 0;
4
+ const emailReg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
5
+ exports.rules = {
6
+ notEmpty: [{
7
+ rule: (value) => value !== '' && value.length > 0,
8
+ message: 'Value is required'
9
+ }],
10
+ bool: [{
11
+ rule: (value) => !!value,
12
+ message: 'Value is required'
13
+ }],
14
+ password: [{
15
+ rule: (value) => value.length > 0,
16
+ message: 'Password field cannot be empty'
17
+ }, {
18
+ rule: (value) => value.length > 5,
19
+ message: 'Password field can not be less than 6 characters'
20
+ }],
21
+ email: [{
22
+ rule: (value) => !!value && value !== '' && value.length !== 0,
23
+ message: 'Email is required'
24
+ }, {
25
+ rule: (value) => emailReg.test(String(value).toLowerCase()),
26
+ message: 'Email is invalid'
27
+ }],
28
+ min: (min) => [{
29
+ rule: (value) => parseFloat(value) > min,
30
+ message: `The value must be greater than ${min}`
31
+ }],
32
+ max: (max) => [{
33
+ rule: (value) => parseFloat(value) < max,
34
+ message: `The value must be smaller ${max}`
35
+ }],
36
+ length: (min, max) => [{
37
+ rule: (value) => String(value).length >= min,
38
+ message: `No less than ${min} symbols`
39
+ }, {
40
+ rule: (value) => (max !== undefined ? String(value).length <= max : true),
41
+ message: `No more than ${max} symbols`
42
+ }]
43
+ };
@@ -0,0 +1,3 @@
1
+ import { ValidatorRules } from './rules';
2
+ import { Validity, Value } from './validator-field';
3
+ export declare function useValidator(value: Value, rules: ValidatorRules): [boolean, Pick<Validity, 'message' | 'errors'>];
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.useValidator = void 0;
15
+ const validator_1 = require("./validator");
16
+ function useValidator(value, rules) {
17
+ const validator = new validator_1.Validator();
18
+ validator.addField({ value, rules });
19
+ const _a = validator.validate(), { isValid } = _a, validateObject = __rest(_a, ["isValid"]);
20
+ return [isValid, validateObject];
21
+ }
22
+ exports.useValidator = useValidator;
@@ -0,0 +1,25 @@
1
+ import { ReactNode } from 'react';
2
+ import { ValidatorRules } from './rules';
3
+ export declare type Value = any;
4
+ export interface ErrorMessage {
5
+ message: string;
6
+ isValid: boolean;
7
+ }
8
+ export interface Validity {
9
+ message: string;
10
+ isValid: boolean;
11
+ errors?: ErrorMessage[];
12
+ id?: string | number;
13
+ }
14
+ declare type Fn = (validity: Validity, value: Value) => ReactNode;
15
+ interface Props {
16
+ rules?: ValidatorRules;
17
+ required?: boolean;
18
+ value?: Value;
19
+ id?: string | number;
20
+ children?: ReactNode | Fn;
21
+ unregisterField: (val: any) => void;
22
+ registerField: (val: any) => void;
23
+ }
24
+ export declare function ValidatorField(props: Omit<Props, 'registerField' | 'unregisterField'>): JSX.Element;
25
+ export {};
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ValidatorField = void 0;
4
+ const react_1 = require("react");
5
+ const context_1 = require("./context");
6
+ const validator_1 = require("./validator");
7
+ class ValidationFieldWrapper extends react_1.Component {
8
+ componentWillUnmount() {
9
+ this.props.unregisterField(this);
10
+ }
11
+ componentDidMount() {
12
+ this.props.registerField(this);
13
+ }
14
+ validate() {
15
+ const field = new validator_1.Field({
16
+ rules: this.props.rules,
17
+ required: this.props.required,
18
+ value: this.props.value,
19
+ id: this.props.id
20
+ });
21
+ return field.validate();
22
+ }
23
+ render() {
24
+ const { children, value } = this.props;
25
+ const validity = this.validate();
26
+ return (typeof children === 'function' ? children(validity, value) : children);
27
+ }
28
+ }
29
+ function ValidatorField(props) {
30
+ return (<context_1.Context.Consumer>
31
+ {(data) => (<ValidationFieldWrapper {...props} registerField={data.registerField} unregisterField={data.unregisterField}/>)}
32
+ </context_1.Context.Consumer>);
33
+ }
34
+ exports.ValidatorField = ValidatorField;
@@ -0,0 +1,17 @@
1
+ import { Component, ReactNode, RefObject } from 'react';
2
+ interface ComponentProps {
3
+ children?: ReactNode;
4
+ stopAtFirstError?: boolean;
5
+ ref?: RefObject<any>;
6
+ }
7
+ export declare class ValidatorWrapper extends Component<ComponentProps> {
8
+ fields: any[];
9
+ constructor(props: any, ctx: any);
10
+ componentWillUnmount(): void;
11
+ registerField(field: any): void;
12
+ unregisterField(field: any): void;
13
+ getField(id: any): any;
14
+ validate(): import("./validator-field").Validity;
15
+ render(): JSX.Element;
16
+ }
17
+ export {};
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ValidatorWrapper = void 0;
4
+ const react_1 = require("react");
5
+ const context_1 = require("./context");
6
+ const validator_1 = require("./validator");
7
+ class ValidatorWrapper extends react_1.Component {
8
+ constructor(props, ctx) {
9
+ super(props, ctx);
10
+ this.fields = [];
11
+ this.registerField = this.registerField.bind(this);
12
+ this.unregisterField = this.unregisterField.bind(this);
13
+ }
14
+ componentWillUnmount() {
15
+ this.fields = [];
16
+ }
17
+ registerField(field) {
18
+ if (field && !this.fields.includes(field)) {
19
+ this.fields.push(field);
20
+ }
21
+ }
22
+ unregisterField(field) {
23
+ const index = this.fields.indexOf(field);
24
+ if (index > -1)
25
+ this.fields.splice(index, 1);
26
+ }
27
+ getField(id) {
28
+ return this.fields.find((field) => field.props.id === id) || null;
29
+ }
30
+ validate() {
31
+ const validator = new validator_1.Validator({ stopAtFirstError: this.props.stopAtFirstError });
32
+ this.fields.forEach((comp) => {
33
+ validator.addField(comp.props);
34
+ });
35
+ return validator.validate();
36
+ }
37
+ render() {
38
+ return (<context_1.Context.Provider value={{ registerField: this.registerField, unregisterField: this.unregisterField }}>
39
+ {this.props.children}
40
+ </context_1.Context.Provider>);
41
+ }
42
+ }
43
+ exports.ValidatorWrapper = ValidatorWrapper;
@@ -0,0 +1,28 @@
1
+ import { Validity, Value } from './validator-field';
2
+ import { ValidatorRules } from './rules';
3
+ export interface FieldParams {
4
+ value: Value;
5
+ rules: ValidatorRules;
6
+ required?: boolean;
7
+ id?: string | number;
8
+ }
9
+ export declare class Field {
10
+ private rules;
11
+ private required;
12
+ private value;
13
+ id: string | number;
14
+ constructor({ rules, required, value, id }: FieldParams);
15
+ validate(): Validity;
16
+ }
17
+ export interface ValidatorParams {
18
+ stopAtFirstError: boolean;
19
+ }
20
+ export declare class Validator {
21
+ private fields;
22
+ private params;
23
+ constructor(params?: ValidatorParams);
24
+ addField(params: FieldParams): Field;
25
+ removeField(field: Field): void;
26
+ getField(id: Field['id']): Field;
27
+ validate(): Validity;
28
+ }
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Validator = exports.Field = void 0;
4
+ class Field {
5
+ constructor({ rules, required, value, id }) {
6
+ this.rules = rules;
7
+ this.required = required;
8
+ this.value = value;
9
+ this.id = id;
10
+ }
11
+ validate() {
12
+ let isValid = true;
13
+ let message = '';
14
+ const { rules, value, required, id } = this;
15
+ const isEmptyValue = !value && parseFloat(value) !== 0;
16
+ if (!rules.length || (isEmptyValue && required === false)) {
17
+ return { isValid, message, id };
18
+ }
19
+ rules.forEach((instance) => {
20
+ if (isValid) {
21
+ isValid = instance.rule(value);
22
+ if (!isValid) {
23
+ if (typeof instance.message === 'function') {
24
+ message = instance.message(value);
25
+ }
26
+ else {
27
+ message = instance.message;
28
+ }
29
+ }
30
+ }
31
+ });
32
+ return { isValid, message, id };
33
+ }
34
+ }
35
+ exports.Field = Field;
36
+ class Validator {
37
+ constructor(params) {
38
+ this.params = params || null;
39
+ this.fields = [];
40
+ }
41
+ addField(params) {
42
+ const field = new Field(params);
43
+ this.fields.push(field);
44
+ return field;
45
+ }
46
+ removeField(field) {
47
+ const index = this.fields.indexOf(field);
48
+ if (index > -1)
49
+ this.fields.splice(index, 1);
50
+ }
51
+ getField(id) {
52
+ return this.fields.find((field) => field.id === id) || null;
53
+ }
54
+ validate() {
55
+ let prevResult;
56
+ const statuses = this.fields.map((field) => {
57
+ var _a;
58
+ if (((_a = this.params) === null || _a === void 0 ? void 0 : _a.stopAtFirstError) && prevResult && prevResult.isValid === false) {
59
+ return null;
60
+ }
61
+ prevResult = field.validate();
62
+ return prevResult;
63
+ });
64
+ const errors = statuses.filter((inst) => inst && inst.isValid === false);
65
+ if (errors.length) {
66
+ const { isValid, message } = errors[0];
67
+ return { isValid, message, errors };
68
+ }
69
+ return { isValid: true, message: '' };
70
+ }
71
+ }
72
+ exports.Validator = Validator;
@@ -1,45 +1,46 @@
1
1
  /* eslint no-console: [0] */
2
- import React, { useState } from 'react';
3
- import ReactDOM from 'react-dom';
4
- import ValidatorWrapper, { rules, ValidatorField, Validator } from '../src';
2
+ import { useState, createRef } from 'react'
3
+ import { createRoot } from 'react-dom/client'
5
4
 
6
- function App() {
7
- const [email, setEmail] = useState('');
8
- const jsxValidator = React.createRef();
5
+ import { ValidatorWrapper, rules, ValidatorField, Validator } from '../dist/index'
9
6
 
10
- function handleValidateEmail() {
11
- const { isValid, message, errors } = jsxValidator.current.validate();
7
+ function App () {
8
+ const [email, setEmail] = useState('')
9
+ const jsxValidator = createRef()
10
+
11
+ function handleValidateEmail () {
12
+ const { isValid, message, errors } = jsxValidator.current.validate()
12
13
  if (!isValid) {
13
- console.log(isValid, message, errors);
14
- return;
14
+ console.log(isValid, message, errors)
15
+ return
15
16
  }
16
- console.log('success');
17
+ console.log('success')
17
18
  }
18
19
 
19
- function handleValidateCustomFields() {
20
- const validator = new Validator({ stopAtFirstError: true });
20
+ function handleValidateCustomFields () {
21
+ const validator = new Validator({ stopAtFirstError: true })
21
22
  const fieldPassword = validator.addField({
22
23
  rules: rules.password,
23
24
  value: '',
24
- id: 'for-remove',
25
- });
25
+ id: 'for-remove'
26
+ })
26
27
 
27
- const fieldSearchPassword = validator.getField('for-remove');
28
- console.log(fieldPassword === fieldSearchPassword);
28
+ const fieldSearchPassword = validator.getField('for-remove')
29
+ console.log(fieldPassword === fieldSearchPassword)
29
30
 
30
- validator.removeField(fieldPassword);
31
+ validator.removeField(fieldPassword)
31
32
 
32
33
  validator.addField({
33
34
  rules: rules.password,
34
- value: 'testpassword',
35
- });
35
+ value: 'testpassword'
36
+ })
36
37
 
37
- const { isValid, message, errors } = validator.validate();
38
+ const { isValid, message, errors } = validator.validate()
38
39
  if (!isValid) {
39
- console.log(isValid, message, errors);
40
- return;
40
+ console.log(isValid, message, errors)
41
+ return
41
42
  }
42
- console.log('success');
43
+ console.log('success')
43
44
  }
44
45
 
45
46
  return (
@@ -58,7 +59,9 @@ function App() {
58
59
  <button onClick={handleValidateCustomFields} type="button">Validate custom fields</button>
59
60
  </ValidatorWrapper>
60
61
  </>
61
- );
62
+ )
62
63
  }
63
64
 
64
- ReactDOM.render(<App />, document.getElementById('root'));
65
+ const root = createRoot(document.getElementById('root'))
66
+
67
+ root.render(<App />)
@@ -3,6 +3,8 @@
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
5
  <title>Test template</title>
6
+ <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
7
+ <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
6
8
  </head>
7
9
  <body>
8
10
 
@@ -1,21 +1,29 @@
1
- const path = require('path');
1
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
2
+ const path = require('path')
2
3
 
3
4
  module.exports = {
5
+ target: 'web',
4
6
  entry: [
5
- path.resolve('./example/example.jsx'),
7
+ path.resolve('./example/example.jsx')
6
8
  ],
9
+ mode: 'development',
7
10
  output: {
8
11
  path: path.resolve('./example'),
9
- filename: './dist/example.js',
12
+ filename: './dist/example.js'
10
13
  },
14
+ devtool: 'source-map',
11
15
  module: {
12
16
  rules: [{
13
17
  test: /\.jsx?$/,
14
18
  exclude: /node_modules/,
15
- loader: 'babel-loader',
16
- }],
19
+ loader: 'babel-loader'
20
+ }]
17
21
  },
18
22
  resolve: {
19
- extensions: ['.js', '.jsx'],
23
+ extensions: ['.js', '.jsx']
20
24
  },
21
- };
25
+ externals: {
26
+ react: 'React',
27
+ 'react-dom': 'ReactDOM'
28
+ }
29
+ }
package/jest.config.ts ADDED
@@ -0,0 +1,10 @@
1
+ /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
2
+ globalThis.IS_REACT_ACT_ENVIRONMENT = true
3
+
4
+ export default {
5
+ preset: 'ts-jest',
6
+ testEnvironment: 'node',
7
+ collectCoverage: true,
8
+ coverageDirectory: './coverage/',
9
+ displayName: '@coxy'
10
+ }