5
5
6
6
import React , { ReactNode , useEffect , useState } from 'react' ;
7
7
import { Field , FieldProps , getIn , useFormikContext } from 'formik' ;
8
+ import { isEmpty } from 'lodash' ;
8
9
import {
9
10
EuiCodeEditor ,
10
11
EuiCompressedFormRow ,
@@ -70,7 +71,19 @@ export function JsonLinesField(props: JsonLinesFieldProps) {
70
71
) : undefined
71
72
}
72
73
helpText = { props . helpText || undefined }
73
- error = { validate ? customErrMsg : undefined }
74
+ error = {
75
+ validate ? (
76
+ < >
77
+ { customErrMsg ?. split ( '\n' ) ?. map ( ( errMsg , idx ) => {
78
+ return (
79
+ < EuiText key = { idx } color = "danger" size = "s" >
80
+ { errMsg }
81
+ </ EuiText >
82
+ ) ;
83
+ } ) }
84
+ </ >
85
+ ) : undefined
86
+ }
74
87
isInvalid = {
75
88
validate
76
89
? getIn ( errors , field . name ) && getIn ( touched , field . name )
@@ -91,32 +104,37 @@ export function JsonLinesField(props: JsonLinesFieldProps) {
91
104
onBlur = { ( ) => {
92
105
form . setFieldTouched ( field . name ) ;
93
106
let finalJsonStr = '' ;
94
- let curIdx = - 1 ;
107
+ let errs = [ ] as string [ ] ;
95
108
try {
96
109
const lines = jsonStr ?. split ( '\n' ) ;
97
110
lines . forEach ( ( line : string , idx ) => {
98
- curIdx = idx ;
99
111
if ( line . trim ( ) !== '' ) {
100
- finalJsonStr +=
101
- customStringifySingleLine ( JSON . parse ( line ) ) + '\n' ;
112
+ let parsedLine = { } ;
113
+ try {
114
+ parsedLine = JSON . parse ( line ) ;
115
+ } catch ( error ) {
116
+ errs . push (
117
+ getFormattedErrorMsg ( error as Error , idx + 1 )
118
+ ) ;
119
+ }
120
+ if ( ! isEmpty ( parsedLine ) ) {
121
+ finalJsonStr +=
122
+ customStringifySingleLine ( JSON . parse ( line ) ) + '\n' ;
123
+ }
102
124
}
103
125
} ) ;
104
126
// remove trailing newline
105
127
if ( finalJsonStr !== '' ) {
106
128
finalJsonStr = finalJsonStr . slice ( 0 , - 1 ) ;
107
129
}
108
- form . setFieldValue ( field . name , finalJsonStr ) ;
109
- } catch ( error ) {
110
- setCustomErrMsg (
111
- `Error on line ${ curIdx + 1 } : ${ getIn (
112
- error ,
113
- 'message' ,
114
- 'Invalid JSON'
115
- )
116
- . replace ( / ^ ( .* ?) \s + i n J S O N .* / , '$1' )
117
- . replace ( / ^ ( .* ?) \s + a f t e r J S O N .* / , '$1' ) } `
118
- ) ;
119
- }
130
+
131
+ if ( errs ?. length > 0 ) {
132
+ setCustomErrMsg ( getFormattedErrorMsgList ( errs ) ) ;
133
+ } else {
134
+ form . setFieldValue ( field . name , finalJsonStr ) ;
135
+ setCustomErrMsg ( undefined ) ;
136
+ }
137
+ } catch ( error ) { }
120
138
} }
121
139
readOnly = { props . readOnly || false }
122
140
setOptions = { {
@@ -135,3 +153,28 @@ export function JsonLinesField(props: JsonLinesFieldProps) {
135
153
</ Field >
136
154
) ;
137
155
}
156
+
157
+ function getFormattedErrorMsg ( error : Error , idx : number ) : string {
158
+ return `Error on line ${ idx } : ${ getIn ( error , 'message' , 'Invalid JSON' )
159
+ . replace ( / ^ ( .* ?) \s + i n J S O N .* / , '$1' )
160
+ . replace ( / ^ ( .* ?) \s + a f t e r J S O N .* / , '$1' ) } `;
161
+ }
162
+
163
+ // Verbosely display a few error messages, list the count of remaining ones.
164
+ function getFormattedErrorMsgList ( errors : string [ ] ) : string {
165
+ let finalMsg = '' ;
166
+ const verboseErrors = errors . slice ( 0 , 3 ) ;
167
+ const nonVerboseErrorCount = errors . length - 3 ;
168
+ verboseErrors . forEach ( ( error ) => {
169
+ finalMsg += error + '\n' ;
170
+ } ) ;
171
+ if ( nonVerboseErrorCount > 0 ) {
172
+ finalMsg += `${ nonVerboseErrorCount } more error${
173
+ nonVerboseErrorCount > 1 ? 's' : ''
174
+ } `;
175
+ } else if ( finalMsg !== '' ) {
176
+ // remove trailing newline
177
+ finalMsg = finalMsg . slice ( 0 , - 1 ) ;
178
+ }
179
+ return finalMsg ;
180
+ }
0 commit comments