@coxy/react-validator 1.3.4 → 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.
- package/.eslintignore +1 -1
- package/.eslintrc.js +25 -8
- package/babel.config.js +3 -0
- package/dist/context.d.ts +5 -0
- package/dist/context.js +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +13 -0
- package/dist/rules.d.ts +37 -0
- package/dist/rules.js +43 -0
- package/dist/use-validator.d.ts +3 -0
- package/dist/use-validator.js +22 -0
- package/dist/validator-field.d.ts +25 -0
- package/dist/validator-field.jsx +34 -0
- package/dist/validator-wrapper.d.ts +17 -0
- package/dist/validator-wrapper.jsx +43 -0
- package/dist/validator.d.ts +28 -0
- package/dist/validator.js +72 -0
- package/example/example.jsx +29 -26
- package/example/index.html +2 -0
- package/example/webpack.config.js +15 -7
- package/jest.config.ts +10 -0
- package/package.json +34 -27
- package/src/context.test.ts +7 -0
- package/src/context.ts +6 -0
- package/src/index.test.ts +7 -0
- package/src/index.ts +5 -0
- package/src/jest.d.ts +1 -0
- package/src/rules.test.ts +127 -0
- package/src/{rules.js → rules.ts} +26 -15
- package/src/use-validator.test.tsx +58 -0
- package/src/use-validator.ts +10 -0
- package/src/validator-field.test.tsx +161 -0
- package/src/validator-field.tsx +71 -0
- package/src/validator-wrapper.test.tsx +138 -0
- package/src/validator-wrapper.tsx +58 -0
- package/src/validator.test.tsx +30 -0
- package/src/validator.ts +105 -0
- package/tsconfig.build.json +12 -0
- package/tsconfig.json +39 -0
- package/.babelrc.js +0 -14
- package/build/index.js +0 -1
- package/jest.config.js +0 -5
- package/src/context.js +0 -3
- package/src/context.test.js +0 -13
- package/src/index.js +0 -13
- package/src/index.test.js +0 -13
- package/src/rules.test.js +0 -134
- package/src/use-validator.js +0 -8
- package/src/use-validator.test.js +0 -56
- package/src/validator-field.jsx +0 -72
- package/src/validator-field.test.js +0 -190
- package/src/validator-wrapper.jsx +0 -63
- package/src/validator-wrapper.test.js +0 -177
- package/src/validator.js +0 -82
- package/src/validator.test.js +0 -40
- package/webpack.config.js +0 -32
package/src/index.ts
ADDED
package/src/jest.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
declare let shallow: any
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { rules } from './rules'
|
|
2
|
+
|
|
3
|
+
it('check rule email', () => {
|
|
4
|
+
expect(rules.email.length).toBe(2)
|
|
5
|
+
|
|
6
|
+
let result
|
|
7
|
+
// email.length > 0
|
|
8
|
+
result = rules.email[0].rule('test')
|
|
9
|
+
expect(result).toBe(true)
|
|
10
|
+
|
|
11
|
+
// email check regexp
|
|
12
|
+
result = rules.email[1].rule('test')
|
|
13
|
+
expect(result).toBe(false)
|
|
14
|
+
|
|
15
|
+
// email check regexp
|
|
16
|
+
result = rules.email[1].rule('test@gmail.com')
|
|
17
|
+
expect(result).toBe(true)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('check rule password', () => {
|
|
21
|
+
expect(rules.password.length).toBe(2)
|
|
22
|
+
|
|
23
|
+
let result
|
|
24
|
+
// password.length > 0
|
|
25
|
+
result = rules.password[0].rule('test')
|
|
26
|
+
expect(result).toBe(true)
|
|
27
|
+
|
|
28
|
+
// password.length > 5
|
|
29
|
+
result = rules.password[1].rule('test')
|
|
30
|
+
expect(result).toBe(false)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('check rule bool', () => {
|
|
34
|
+
expect(rules.bool.length).toBe(1)
|
|
35
|
+
|
|
36
|
+
const result = rules.bool[0].rule(true)
|
|
37
|
+
expect(result).toBe(true)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('check rule notEmpty', () => {
|
|
41
|
+
expect(rules.notEmpty.length).toBe(1)
|
|
42
|
+
|
|
43
|
+
let result
|
|
44
|
+
result = rules.notEmpty[0].rule('')
|
|
45
|
+
expect(result).toBe(false)
|
|
46
|
+
|
|
47
|
+
result = rules.notEmpty[0].rule('test')
|
|
48
|
+
expect(result).toBe(true)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('check rule min', () => {
|
|
52
|
+
expect(rules.min(1).length).toBe(1)
|
|
53
|
+
expect(typeof rules.min).toBe('function')
|
|
54
|
+
|
|
55
|
+
let result
|
|
56
|
+
result = rules.min(10)[0].rule('')
|
|
57
|
+
expect(result).toBe(false)
|
|
58
|
+
|
|
59
|
+
result = rules.min(9)[0].rule('testtesttest')
|
|
60
|
+
expect(result).toBe(false)
|
|
61
|
+
|
|
62
|
+
result = rules.min(9)[0].rule('11')
|
|
63
|
+
expect(result).toBe(true)
|
|
64
|
+
|
|
65
|
+
result = rules.min(9)[0].rule(10)
|
|
66
|
+
expect(result).toBe(true)
|
|
67
|
+
|
|
68
|
+
result = rules.min(9)[0].rule('8')
|
|
69
|
+
expect(result).toBe(false)
|
|
70
|
+
|
|
71
|
+
result = rules.min(9)[0].rule(7)
|
|
72
|
+
expect(result).toBe(false)
|
|
73
|
+
|
|
74
|
+
result = rules.min(-1)[0].rule('')
|
|
75
|
+
expect(result).toBe(false)
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('check rule max', () => {
|
|
79
|
+
let result
|
|
80
|
+
result = rules.max(10)[0].rule('')
|
|
81
|
+
expect(result).toBe(false)
|
|
82
|
+
|
|
83
|
+
result = rules.max(9)[0].rule('testtesttest')
|
|
84
|
+
expect(result).toBe(false)
|
|
85
|
+
|
|
86
|
+
result = rules.max(9)[0].rule('11')
|
|
87
|
+
expect(result).toBe(false)
|
|
88
|
+
|
|
89
|
+
result = rules.max(9)[0].rule(10)
|
|
90
|
+
expect(result).toBe(false)
|
|
91
|
+
|
|
92
|
+
result = rules.max(9)[0].rule('5')
|
|
93
|
+
expect(result).toBe(true)
|
|
94
|
+
|
|
95
|
+
result = rules.max(9)[0].rule(5)
|
|
96
|
+
expect(result).toBe(true)
|
|
97
|
+
|
|
98
|
+
result = rules.max(-1)[0].rule('')
|
|
99
|
+
expect(result).toBe(false)
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('check rule length', () => {
|
|
103
|
+
let result
|
|
104
|
+
result = rules.length(1)[0].rule('')
|
|
105
|
+
expect(result).toBe(false)
|
|
106
|
+
|
|
107
|
+
result = rules.length(1)[0].rule('1')
|
|
108
|
+
expect(result).toBe(true)
|
|
109
|
+
|
|
110
|
+
result = rules.length(1, 10)[0].rule('testtesttest')
|
|
111
|
+
expect(result).toBe(true)
|
|
112
|
+
|
|
113
|
+
result = rules.length(1, 10)[1].rule('testtesttest')
|
|
114
|
+
expect(result).toBe(false)
|
|
115
|
+
|
|
116
|
+
result = rules.length(1, 10)[0].rule('lol')
|
|
117
|
+
expect(result).toBe(true)
|
|
118
|
+
|
|
119
|
+
result = rules.length(1, 10)[1].rule('lol')
|
|
120
|
+
expect(result).toBe(true)
|
|
121
|
+
|
|
122
|
+
result = rules.length(1)[1].rule('test undefined 2 param')
|
|
123
|
+
expect(result).toBe(true)
|
|
124
|
+
|
|
125
|
+
result = rules.length(10)[0].rule('tes')
|
|
126
|
+
expect(result).toBe(false)
|
|
127
|
+
})
|
|
@@ -1,48 +1,59 @@
|
|
|
1
|
+
import { Value } from './validator-field'
|
|
2
|
+
|
|
1
3
|
// eslint-disable-next-line
|
|
2
|
-
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,}))
|
|
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
|
+
|
|
6
|
+
type Fn = (value: Value) => string
|
|
7
|
+
|
|
8
|
+
export interface RuleInstance {
|
|
9
|
+
rule: (value: Value) => boolean
|
|
10
|
+
message: string | Fn
|
|
11
|
+
}
|
|
3
12
|
|
|
4
|
-
export
|
|
13
|
+
export type ValidatorRules = RuleInstance[]
|
|
14
|
+
|
|
15
|
+
export const rules = {
|
|
5
16
|
notEmpty: [{
|
|
6
17
|
rule: (value) => value !== '' && value.length > 0,
|
|
7
|
-
message: 'Value is required'
|
|
18
|
+
message: 'Value is required'
|
|
8
19
|
}],
|
|
9
20
|
|
|
10
21
|
bool: [{
|
|
11
22
|
rule: (value) => !!value,
|
|
12
|
-
message: 'Value is required'
|
|
23
|
+
message: 'Value is required'
|
|
13
24
|
}],
|
|
14
25
|
|
|
15
26
|
password: [{
|
|
16
27
|
rule: (value) => value.length > 0,
|
|
17
|
-
message: 'Password field cannot be empty'
|
|
28
|
+
message: 'Password field cannot be empty'
|
|
18
29
|
}, {
|
|
19
30
|
rule: (value) => value.length > 5,
|
|
20
|
-
message: 'Password field can not be less than 6 characters'
|
|
31
|
+
message: 'Password field can not be less than 6 characters'
|
|
21
32
|
}],
|
|
22
33
|
|
|
23
34
|
email: [{
|
|
24
35
|
rule: (value) => !!value && value !== '' && value.length !== 0,
|
|
25
|
-
message: 'Email is required'
|
|
36
|
+
message: 'Email is required'
|
|
26
37
|
}, {
|
|
27
38
|
rule: (value) => emailReg.test(String(value).toLowerCase()),
|
|
28
|
-
message: 'Email is invalid'
|
|
39
|
+
message: 'Email is invalid'
|
|
29
40
|
}],
|
|
30
41
|
|
|
31
42
|
min: (min) => [{
|
|
32
43
|
rule: (value) => parseFloat(value) > min,
|
|
33
|
-
message: `The value must be greater than ${min}
|
|
44
|
+
message: `The value must be greater than ${min}`
|
|
34
45
|
}],
|
|
35
46
|
|
|
36
47
|
max: (max) => [{
|
|
37
48
|
rule: (value) => parseFloat(value) < max,
|
|
38
|
-
message: `The value must be smaller ${max}
|
|
49
|
+
message: `The value must be smaller ${max}`
|
|
39
50
|
}],
|
|
40
51
|
|
|
41
|
-
length: (min, max) => [{
|
|
52
|
+
length: (min, max?) => [{
|
|
42
53
|
rule: (value) => String(value).length >= min,
|
|
43
|
-
message: `No less than ${min} symbols
|
|
54
|
+
message: `No less than ${min} symbols`
|
|
44
55
|
}, {
|
|
45
56
|
rule: (value) => (max !== undefined ? String(value).length <= max : true),
|
|
46
|
-
message: `No more than ${max} symbols
|
|
47
|
-
}]
|
|
48
|
-
}
|
|
57
|
+
message: `No more than ${max} symbols`
|
|
58
|
+
}]
|
|
59
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { unmountComponentAtNode } from 'react-dom'
|
|
6
|
+
import { render, screen } from '@testing-library/react'
|
|
7
|
+
import { act } from 'react-dom/test-utils'
|
|
8
|
+
import { useEffect, useState } from 'react'
|
|
9
|
+
|
|
10
|
+
import { rules } from './rules'
|
|
11
|
+
import { useValidator } from './use-validator'
|
|
12
|
+
|
|
13
|
+
let container
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
container = document.createElement('div')
|
|
16
|
+
document.body.appendChild(container)
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
unmountComponentAtNode(container)
|
|
21
|
+
container.remove()
|
|
22
|
+
container = null
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
jest.useFakeTimers()
|
|
26
|
+
|
|
27
|
+
it('check state change and hide field', () => {
|
|
28
|
+
function Comp () {
|
|
29
|
+
const [value, setValue] = useState(false)
|
|
30
|
+
const [isValid, validateObject] = useValidator(value, rules.bool)
|
|
31
|
+
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
setTimeout(() => {
|
|
34
|
+
act(() => {
|
|
35
|
+
setValue(true)
|
|
36
|
+
})
|
|
37
|
+
}, 100)
|
|
38
|
+
}, [])
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<>
|
|
42
|
+
<span data-testid="test1">{isValid ? 'true' : 'false'}</span>
|
|
43
|
+
<span data-testid="test2">{validateObject.message || 'true'}</span>
|
|
44
|
+
</>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
act(() => {
|
|
48
|
+
render(<Comp />)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
expect(screen.getByTestId('test1').textContent).toContain('false')
|
|
52
|
+
expect(screen.getByTestId('test2').textContent).toContain('Value is required')
|
|
53
|
+
|
|
54
|
+
jest.runAllTimers()
|
|
55
|
+
|
|
56
|
+
expect(screen.getByTestId('test1').textContent).toContain('true')
|
|
57
|
+
expect(screen.getByTestId('test2').textContent).toContain('true')
|
|
58
|
+
})
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Validator } from './validator'
|
|
2
|
+
import { ValidatorRules } from './rules'
|
|
3
|
+
import { Validity, Value } from './validator-field'
|
|
4
|
+
|
|
5
|
+
export function useValidator (value: Value, rules: ValidatorRules): [boolean, Pick<Validity, 'message' | 'errors'>] {
|
|
6
|
+
const validator = new Validator()
|
|
7
|
+
validator.addField({ value, rules })
|
|
8
|
+
const { isValid, ...validateObject } = validator.validate()
|
|
9
|
+
return [isValid, validateObject]
|
|
10
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { createRef, useEffect, useState } from 'react'
|
|
6
|
+
import { render, act } from '@testing-library/react'
|
|
7
|
+
|
|
8
|
+
import { ValidatorWrapper } from './validator-wrapper'
|
|
9
|
+
import { ValidatorField } from './validator-field'
|
|
10
|
+
import { rules } from './rules'
|
|
11
|
+
|
|
12
|
+
it('render without wrapper', () => {
|
|
13
|
+
expect(() => shallow(<ValidatorField />)).toThrowError()
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('normal render', () => {
|
|
17
|
+
render((
|
|
18
|
+
<ValidatorWrapper>
|
|
19
|
+
<ValidatorField rules={[]} value="" />
|
|
20
|
+
</ValidatorWrapper>
|
|
21
|
+
))
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('check context validator', () => {
|
|
25
|
+
const validator = createRef<ValidatorWrapper>()
|
|
26
|
+
render((
|
|
27
|
+
<ValidatorWrapper ref={validator}>
|
|
28
|
+
<ValidatorField rules={[]} />
|
|
29
|
+
</ValidatorWrapper>
|
|
30
|
+
))
|
|
31
|
+
|
|
32
|
+
const validateResult = validator.current.validate()
|
|
33
|
+
|
|
34
|
+
expect(validateResult.isValid).toBe(true)
|
|
35
|
+
expect(validateResult.message).toBe('')
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('check failed validation', () => {
|
|
39
|
+
const validator1 = createRef<ValidatorWrapper>()
|
|
40
|
+
const validator2 = createRef<ValidatorWrapper>()
|
|
41
|
+
|
|
42
|
+
render((
|
|
43
|
+
<>
|
|
44
|
+
<ValidatorWrapper ref={validator1}>
|
|
45
|
+
<ValidatorField rules={rules.email} value="test" />
|
|
46
|
+
</ValidatorWrapper>
|
|
47
|
+
<ValidatorWrapper ref={validator2}>
|
|
48
|
+
<ValidatorField rules={rules.email} value="" />
|
|
49
|
+
</ValidatorWrapper>
|
|
50
|
+
</>
|
|
51
|
+
))
|
|
52
|
+
|
|
53
|
+
act(() => {
|
|
54
|
+
const validateResult1 = validator1.current.validate()
|
|
55
|
+
|
|
56
|
+
expect(validateResult1.isValid).toBe(false)
|
|
57
|
+
expect(validateResult1.message).toBe('Email is invalid')
|
|
58
|
+
expect(validateResult1.errors.length).toBe(1)
|
|
59
|
+
|
|
60
|
+
const validateResult2 = validator2.current.validate()
|
|
61
|
+
|
|
62
|
+
expect(validateResult2.isValid).toBe(false)
|
|
63
|
+
expect(validateResult2.message).toBe('Email is required')
|
|
64
|
+
expect(validateResult2.errors.length).toBe(1)
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
jest.useFakeTimers()
|
|
69
|
+
|
|
70
|
+
it('check state change and hide field', () => {
|
|
71
|
+
const validator1 = createRef<ValidatorWrapper>()
|
|
72
|
+
|
|
73
|
+
function Comp () {
|
|
74
|
+
const [st, setSt] = useState(true)
|
|
75
|
+
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
setTimeout(() => {
|
|
78
|
+
act(() => {
|
|
79
|
+
setSt(false)
|
|
80
|
+
})
|
|
81
|
+
}, 100)
|
|
82
|
+
}, [])
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<ValidatorWrapper ref={validator1}>
|
|
86
|
+
<ValidatorField rules={rules.email} value="test" />
|
|
87
|
+
{st && (
|
|
88
|
+
<ValidatorField rules={rules.email} value="" />
|
|
89
|
+
)}
|
|
90
|
+
</ValidatorWrapper>
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
render(<Comp />)
|
|
95
|
+
|
|
96
|
+
jest.runAllTimers()
|
|
97
|
+
|
|
98
|
+
const validateResult1 = validator1.current.validate()
|
|
99
|
+
|
|
100
|
+
expect(validateResult1.isValid).toBe(false)
|
|
101
|
+
expect(validateResult1.message).toBe('Email is invalid')
|
|
102
|
+
expect(validateResult1.errors.length).toBe(1)
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('check success validation', () => {
|
|
106
|
+
const validator = createRef<ValidatorWrapper>()
|
|
107
|
+
render((
|
|
108
|
+
<ValidatorWrapper ref={validator}>
|
|
109
|
+
<ValidatorField rules={rules.email} value="email@email.com" />
|
|
110
|
+
</ValidatorWrapper>
|
|
111
|
+
))
|
|
112
|
+
|
|
113
|
+
const validateResult = validator.current.validate()
|
|
114
|
+
|
|
115
|
+
expect(validateResult.isValid).toBe(true)
|
|
116
|
+
expect(validateResult.message).toBe('')
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it('check success validation fot child function', () => {
|
|
120
|
+
const validator = createRef<ValidatorWrapper>()
|
|
121
|
+
render((
|
|
122
|
+
<ValidatorWrapper ref={validator}>
|
|
123
|
+
<ValidatorField rules={rules.email} value="email@email.com">
|
|
124
|
+
{({ isValid, message }) => (
|
|
125
|
+
<>
|
|
126
|
+
{!isValid && <div>{message}</div>}
|
|
127
|
+
</>
|
|
128
|
+
)}
|
|
129
|
+
</ValidatorField>
|
|
130
|
+
</ValidatorWrapper>
|
|
131
|
+
))
|
|
132
|
+
|
|
133
|
+
const validateResult = validator.current.validate()
|
|
134
|
+
|
|
135
|
+
expect(validateResult.isValid).toBe(true)
|
|
136
|
+
expect(validateResult.message).toBe('')
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it('check custom rule message function', () => {
|
|
140
|
+
const validator = createRef<ValidatorWrapper>()
|
|
141
|
+
const rule = [{
|
|
142
|
+
rule: (value) => value !== 'test',
|
|
143
|
+
message: (value) => `test message ${value}`
|
|
144
|
+
}]
|
|
145
|
+
render((
|
|
146
|
+
<ValidatorWrapper ref={validator}>
|
|
147
|
+
<ValidatorField rules={rule} value="test">
|
|
148
|
+
{({ isValid, message }) => (
|
|
149
|
+
<>
|
|
150
|
+
{!isValid && <div>{message}</div>}
|
|
151
|
+
</>
|
|
152
|
+
)}
|
|
153
|
+
</ValidatorField>
|
|
154
|
+
</ValidatorWrapper>
|
|
155
|
+
))
|
|
156
|
+
|
|
157
|
+
const validateResult = validator.current.validate()
|
|
158
|
+
|
|
159
|
+
expect(validateResult.isValid).toBe(false)
|
|
160
|
+
expect(validateResult.message).toBe('test message test')
|
|
161
|
+
})
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Component, ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
import { Context } from './context'
|
|
4
|
+
import { Field } from './validator'
|
|
5
|
+
import { ValidatorRules } from './rules'
|
|
6
|
+
|
|
7
|
+
export type Value = any
|
|
8
|
+
|
|
9
|
+
export interface ErrorMessage {
|
|
10
|
+
message: string
|
|
11
|
+
isValid: boolean
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface Validity {
|
|
15
|
+
message: string
|
|
16
|
+
isValid: boolean
|
|
17
|
+
errors?: ErrorMessage[]
|
|
18
|
+
id?: string | number
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
type Fn = (validity: Validity, value: Value) => ReactNode
|
|
22
|
+
|
|
23
|
+
interface Props {
|
|
24
|
+
rules?: ValidatorRules,
|
|
25
|
+
required?: boolean,
|
|
26
|
+
value?: Value
|
|
27
|
+
id?: string | number
|
|
28
|
+
children?: ReactNode | Fn
|
|
29
|
+
unregisterField: (val: any) => void
|
|
30
|
+
registerField: (val: any) => void
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
class ValidationFieldWrapper extends Component<Props> {
|
|
34
|
+
componentWillUnmount () {
|
|
35
|
+
this.props.unregisterField(this)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
componentDidMount () {
|
|
39
|
+
this.props.registerField(this)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
validate (): Validity {
|
|
43
|
+
const field = new Field({
|
|
44
|
+
rules: this.props.rules,
|
|
45
|
+
required: this.props.required,
|
|
46
|
+
value: this.props.value,
|
|
47
|
+
id: this.props.id
|
|
48
|
+
})
|
|
49
|
+
return field.validate()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
render () {
|
|
53
|
+
const { children, value } = this.props
|
|
54
|
+
const validity = this.validate()
|
|
55
|
+
return (typeof children === 'function' ? children(validity, value) : children)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function ValidatorField (props: Omit<Props, 'registerField' | 'unregisterField'>) {
|
|
60
|
+
return (
|
|
61
|
+
<Context.Consumer>
|
|
62
|
+
{(data) => (
|
|
63
|
+
<ValidationFieldWrapper
|
|
64
|
+
{...props}
|
|
65
|
+
registerField={data.registerField}
|
|
66
|
+
unregisterField={data.unregisterField}
|
|
67
|
+
/>
|
|
68
|
+
)}
|
|
69
|
+
</Context.Consumer>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { createRef } from 'react'
|
|
6
|
+
import { render } from '@testing-library/react'
|
|
7
|
+
|
|
8
|
+
import { ValidatorWrapper } from './validator-wrapper'
|
|
9
|
+
import { ValidatorField } from './validator-field'
|
|
10
|
+
import { rules } from './rules'
|
|
11
|
+
|
|
12
|
+
it('render without child', () => {
|
|
13
|
+
expect(() => shallow(<ValidatorWrapper />)).toThrowError()
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('check wrapper validator', () => {
|
|
17
|
+
const validator = createRef<ValidatorWrapper>()
|
|
18
|
+
render((
|
|
19
|
+
<ValidatorWrapper ref={validator}>
|
|
20
|
+
<ValidatorField rules={[]} />
|
|
21
|
+
<ValidatorField rules={[]} />
|
|
22
|
+
<ValidatorField rules={[]} />
|
|
23
|
+
<ValidatorField rules={[]} />
|
|
24
|
+
<ValidatorField rules={[]} />
|
|
25
|
+
<ValidatorField rules={[]} />
|
|
26
|
+
</ValidatorWrapper>
|
|
27
|
+
))
|
|
28
|
+
|
|
29
|
+
expect(typeof validator.current).toBe('object')
|
|
30
|
+
expect(typeof validator.current.validate).toBe('function')
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('check getField validator', () => {
|
|
34
|
+
const validator = createRef<ValidatorWrapper>()
|
|
35
|
+
render((
|
|
36
|
+
<ValidatorWrapper ref={validator}>
|
|
37
|
+
<ValidatorField rules={[]} id="test" />
|
|
38
|
+
<ValidatorField rules={[]} id="test-fields" />
|
|
39
|
+
</ValidatorWrapper>
|
|
40
|
+
))
|
|
41
|
+
expect(typeof validator.current.getField).toBe('function')
|
|
42
|
+
const field = validator.current.getField('test')
|
|
43
|
+
|
|
44
|
+
expect(typeof field.validate).toBe('function')
|
|
45
|
+
const fieldValidate = field.validate()
|
|
46
|
+
|
|
47
|
+
expect(fieldValidate.isValid).toBe(true)
|
|
48
|
+
expect(fieldValidate.message).toBe('')
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('check getField undefined field', () => {
|
|
52
|
+
const validator = createRef<ValidatorWrapper>()
|
|
53
|
+
render((
|
|
54
|
+
<ValidatorWrapper ref={validator}>
|
|
55
|
+
<ValidatorField rules={[]} id="test-empty-field" />
|
|
56
|
+
</ValidatorWrapper>
|
|
57
|
+
))
|
|
58
|
+
|
|
59
|
+
const field = validator.current.getField('8')
|
|
60
|
+
expect(field).toBe(null)
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('check stopAtFirstError validator', () => {
|
|
64
|
+
const validator = createRef<ValidatorWrapper>()
|
|
65
|
+
render((
|
|
66
|
+
<ValidatorWrapper ref={validator} stopAtFirstError>
|
|
67
|
+
<ValidatorField rules={[]} value="test" />
|
|
68
|
+
<ValidatorField rules={rules.email} value="test" />
|
|
69
|
+
<ValidatorField rules={rules.password} value="" />
|
|
70
|
+
</ValidatorWrapper>
|
|
71
|
+
))
|
|
72
|
+
const fieldValidate = validator.current.validate()
|
|
73
|
+
expect(fieldValidate.isValid).toBe(false)
|
|
74
|
+
expect(fieldValidate.message).toBe('Email is invalid')
|
|
75
|
+
expect(fieldValidate.errors.length).toBe(1)
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('check unregisterField, registerField', () => {
|
|
79
|
+
const validator = createRef<ValidatorWrapper>()
|
|
80
|
+
render((
|
|
81
|
+
<ValidatorWrapper ref={validator}>
|
|
82
|
+
<ValidatorField rules={[]} id="test-register-field" />
|
|
83
|
+
</ValidatorWrapper>
|
|
84
|
+
))
|
|
85
|
+
|
|
86
|
+
expect(typeof validator.current.registerField).toBe('function')
|
|
87
|
+
expect(typeof validator.current.unregisterField).toBe('function')
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('check filed in field', () => {
|
|
91
|
+
const validator = createRef<ValidatorWrapper>()
|
|
92
|
+
render(
|
|
93
|
+
<ValidatorWrapper ref={validator}>
|
|
94
|
+
<ValidatorField rules={[]}>
|
|
95
|
+
<ValidatorField rules={[]} id="check-validate-field-1" />
|
|
96
|
+
<ValidatorField rules={[]} id="check-validate-field-2" />
|
|
97
|
+
</ValidatorField>
|
|
98
|
+
</ValidatorWrapper>
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
expect(typeof validator.current).toBe('object')
|
|
102
|
+
expect(typeof validator.current.validate).toBe('function')
|
|
103
|
+
const result = validator.current.validate()
|
|
104
|
+
expect(result.isValid).toBe(true)
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it('check wrapper in wrapper', () => {
|
|
108
|
+
const validatorOut = createRef<ValidatorWrapper>()
|
|
109
|
+
const validatorIn = createRef<ValidatorWrapper>()
|
|
110
|
+
render((
|
|
111
|
+
<ValidatorWrapper ref={validatorOut}>
|
|
112
|
+
<ValidatorField rules={rules.email} value="" />
|
|
113
|
+
<ValidatorWrapper ref={validatorIn}>
|
|
114
|
+
<ValidatorField rules={rules.password} value="successpasswword" />
|
|
115
|
+
</ValidatorWrapper>
|
|
116
|
+
</ValidatorWrapper>
|
|
117
|
+
))
|
|
118
|
+
expect(validatorIn.current.validate().isValid).toBe(true)
|
|
119
|
+
expect(validatorOut.current.validate().isValid).toBe(false)
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('check two validators', () => {
|
|
123
|
+
const validatorSuccess = createRef<ValidatorWrapper>()
|
|
124
|
+
const validatorFailed = createRef<ValidatorWrapper>()
|
|
125
|
+
render((
|
|
126
|
+
<>
|
|
127
|
+
<ValidatorWrapper ref={validatorSuccess}>
|
|
128
|
+
<ValidatorField rules={rules.password} value="successpasswword" />
|
|
129
|
+
</ValidatorWrapper>
|
|
130
|
+
<ValidatorWrapper ref={validatorFailed}>
|
|
131
|
+
<ValidatorField rules={rules.email} value="" />
|
|
132
|
+
</ValidatorWrapper>
|
|
133
|
+
</>
|
|
134
|
+
))
|
|
135
|
+
|
|
136
|
+
expect(validatorFailed.current.validate().isValid).toBe(false)
|
|
137
|
+
expect(validatorSuccess.current.validate().isValid).toBe(true)
|
|
138
|
+
})
|