@@ -5,11 +5,10 @@ import {
5
5
import { Field , type FieldConfig , type FieldProps } from "formik"
6
6
import { type FC , useState , useEffect } from "react"
7
7
import {
8
- type ArraySchema ,
9
8
type StringSchema ,
10
9
type ValidateOptions ,
11
10
array as YupArray ,
12
- type Schema ,
11
+ string as YupString ,
13
12
} from "yup"
14
13
15
14
import { schemaToFieldValidator } from "../../utils/form"
@@ -26,65 +25,104 @@ export type TextFieldProps = Omit<
26
25
| "helperText"
27
26
> & {
28
27
name : string
29
- schema : StringSchema
28
+ baseSchema ? : StringSchema
30
29
validateOptions ?: ValidateOptions
31
30
dirty ?: boolean
32
31
split ?: string | RegExp
33
32
unique ?: boolean
34
33
uniqueCaseInsensitive ?: boolean
34
+ allowedChars ?: string
35
+ minChars ?: number
36
+ maxChars ?: number
37
+ minValues ?: number
38
+ maxValues ?: number
35
39
}
36
40
37
41
// https://formik.org/docs/examples/with-material-ui
38
42
const TextField : FC < TextFieldProps > = ( {
39
43
id,
40
44
name,
41
- schema ,
45
+ baseSchema = YupString ( ) ,
42
46
type = "text" ,
43
47
required = false ,
44
48
dirty = false ,
45
49
unique = false ,
46
50
uniqueCaseInsensitive = false ,
51
+ allowedChars,
52
+ minChars,
53
+ maxChars = 1000 ,
47
54
split,
55
+ minValues,
56
+ maxValues = 1000 ,
48
57
validateOptions,
49
58
...otherTextFieldProps
50
59
} ) => {
51
60
const [ initialValue , setInitialValue ] = useState < string | string [ ] > ( "" )
52
61
53
62
const dotPath = name . split ( "." )
54
63
55
- let _schema : Schema = schema
56
- if ( split ) {
57
- _schema = YupArray ( ) . of ( _schema )
58
- if ( unique || uniqueCaseInsensitive ) {
59
- _schema = _schema . test ( {
64
+ function buildSchema ( ) {
65
+ // Build schema for a single string.
66
+ let stringSchema = baseSchema
67
+ // 1: Validate string has min length.
68
+ if ( required || minChars )
69
+ stringSchema = stringSchema . required ( ) . min ( minChars ?? 1 )
70
+ // 2: Validate string has max length.
71
+ stringSchema = stringSchema . max ( maxChars )
72
+ // 3. Validate string has allowed characters.
73
+ if ( allowedChars )
74
+ stringSchema = stringSchema . matches ( new RegExp ( `[${ allowedChars } ]*` ) )
75
+ // 4: Validate string is dirty.
76
+ if ( dirty && ! split )
77
+ stringSchema = stringSchema . notOneOf (
78
+ [ initialValue as string ] ,
79
+ "cannot be initial value" ,
80
+ )
81
+ // Return schema for a single string.
82
+ if ( ! split ) return stringSchema
83
+
84
+ // Build schema for an array of strings.
85
+ let arraySchema = YupArray ( ) . of ( stringSchema )
86
+ // 1: Validate string array has min length.
87
+ if ( required || minValues )
88
+ arraySchema = arraySchema . required ( ) . min ( minValues ?? 1 )
89
+ // 2: Validate string array has max length.
90
+ arraySchema = arraySchema . max ( maxValues )
91
+ // 3: Validate string array has unique values.
92
+ if ( unique || uniqueCaseInsensitive )
93
+ arraySchema = arraySchema . test ( {
60
94
message : "cannot have duplicates" ,
61
95
test : values => {
62
- if ( Array . isArray ( values ) && values . length >= 2 ) {
96
+ if (
97
+ Array . isArray ( values ) &&
98
+ values . length >= 2 &&
99
+ values . every ( value => typeof value === "string" )
100
+ ) {
63
101
return (
64
102
new Set (
65
- uniqueCaseInsensitive && typeof values [ 0 ] === "string"
103
+ uniqueCaseInsensitive
66
104
? values . map ( value => value . toLowerCase ( ) )
67
105
: values ,
68
106
) . size === values . length
69
107
)
70
108
}
71
-
72
109
return true
73
110
} ,
74
111
} )
75
- }
76
- }
77
- if ( required ) {
78
- _schema = _schema . required ( )
79
- if ( split ) _schema = ( _schema as ArraySchema < string [ ] , any > ) . min ( 1 )
112
+ // 4: Validate string array is dirty.
113
+ if ( dirty )
114
+ arraySchema = arraySchema . notOneOf (
115
+ [ initialValue as string [ ] ] ,
116
+ "cannot be initial value" ,
117
+ )
118
+ // Return schema for an array of strings.
119
+ return arraySchema
80
120
}
81
- if ( dirty )
82
- _schema = _schema . notOneOf ( [ initialValue ] , "cannot be initial value" )
83
121
84
122
const fieldConfig : FieldConfig = {
85
123
name,
86
124
type,
87
- validate : schemaToFieldValidator ( _schema , validateOptions ) ,
125
+ validate : schemaToFieldValidator ( buildSchema ( ) , validateOptions ) ,
88
126
}
89
127
90
128
const _Field : FC < FieldProps > = ( { form } ) => {
0 commit comments