@@ -9,12 +9,17 @@ import { useCallback, useMemo } from 'react';
9
9
import { Grid } from '@mui/material' ;
10
10
import { type DateOrTimeView } from '@mui/x-date-pickers' ;
11
11
import { useIntl } from 'react-intl' ;
12
- import { SubmitButton , useSnackMessage } from '@gridsuite/commons-ui' ;
13
- import yup from '../../utils/yup-config' ;
12
+ import { SubmitButton , useSnackMessage , yupConfig as yup } from '@gridsuite/commons-ui' ;
14
13
import { type InferType } from 'yup' ;
15
14
import { type SubmitHandler , useForm } from 'react-hook-form' ;
16
15
import { yupResolver } from '@hookform/resolvers/yup' ;
17
- import { FormContainer , SelectElement , TextareaAutosizeElement } from 'react-hook-form-mui' ;
16
+ import {
17
+ FormContainer ,
18
+ FormErrorProvider ,
19
+ type FormErrorProviderProps ,
20
+ SelectElement ,
21
+ TextareaAutosizeElement ,
22
+ } from 'react-hook-form-mui' ;
18
23
import { DateTimePickerElement , type DateTimePickerElementProps } from 'react-hook-form-mui/date-pickers' ;
19
24
import { UserAdminSrv } from '../../services' ;
20
25
import { getErrorMessage , handleAnnouncementCreationErrors } from '../../utils/error' ;
@@ -28,10 +33,12 @@ export type AddAnnouncementFormProps = {
28
33
onAnnouncementCreated ?: ( ) => void ;
29
34
} ;
30
35
36
+ const MESSAGE_MAX_LENGTH = 200 ;
37
+
31
38
const formSchema = yup
32
39
. object ( )
33
40
. shape ( {
34
- [ MESSAGE ] : yup . string ( ) . nullable ( ) . trim ( ) . min ( 1 ) . required ( ) ,
41
+ [ MESSAGE ] : yup . string ( ) . nullable ( ) . trim ( ) . min ( 1 , 'YupRequired' ) . max ( MESSAGE_MAX_LENGTH ) . required ( ) ,
35
42
[ START_DATE ] : yup . string ( ) . nullable ( ) . datetime ( ) . required ( ) ,
36
43
[ END_DATE ] : yup
37
44
. string ( )
@@ -41,7 +48,7 @@ const formSchema = yup
41
48
. when ( START_DATE , ( startDate , schema ) =>
42
49
schema . test (
43
50
'is-after-start' ,
44
- 'End date must be after start date ' ,
51
+ 'announcements.form.errForm.startDateAfterEndDateErr ' ,
45
52
( endDate ) => ! startDate || ! endDate || new Date ( endDate ) > new Date ( startDate as unknown as string )
46
53
)
47
54
) ,
@@ -55,7 +62,13 @@ const formSchema = yup
55
62
type FormSchema = InferType < typeof formSchema > ;
56
63
57
64
const datetimePickerTransform : NonNullable < DateTimePickerElementProps < FormSchema > [ 'transform' ] > = {
58
- input : ( value ) => ( value ? new Date ( value ) : null ) ,
65
+ input : ( value ) => {
66
+ try {
67
+ return value ? new Date ( value ) : null ;
68
+ } catch {
69
+ return null ; // RangeError: invalid date
70
+ }
71
+ } ,
59
72
output : ( value ) => value ?. toISOString ( ) ?? '' ,
60
73
} ;
61
74
const pickerView = [ 'year' , 'month' , 'day' , 'hours' , 'minutes' ] as const satisfies readonly DateOrTimeView [ ] ;
@@ -76,6 +89,9 @@ export default function AddAnnouncementForm({ onAnnouncementCreated }: Readonly<
76
89
// @ts -expect-error: nullable() is called, so null is accepted as default value
77
90
[ SEVERITY ] : null ,
78
91
} ,
92
+ criteriaMode : 'all' ,
93
+ mode : 'all' ,
94
+ reValidateMode : 'onBlur' , // TODO 'onChange'?
79
95
} ) ;
80
96
const { getValues } = formContext ;
81
97
const startDateValue = getValues ( START_DATE ) ;
@@ -99,68 +115,77 @@ export default function AddAnnouncementForm({ onAnnouncementCreated }: Readonly<
99
115
[ onAnnouncementCreated , snackError ]
100
116
) ;
101
117
118
+ // TODO remove when yupConfig has been rework
119
+ const onErrorIntl = useCallback < FormErrorProviderProps [ 'onError' ] > (
120
+ ( error ) =>
121
+ error ?. message ?. includes ( ' ' ) // if it's not a token
122
+ ? error . message
123
+ : intl . formatMessage ( { id : error . message , defaultMessage : error . message } ) ,
124
+ [ intl ]
125
+ ) ;
126
+
102
127
return (
103
128
< FormContainer < FormSchema >
104
129
formContext = { formContext }
105
130
onSuccess = { onSubmit }
106
- //criteriaMode="all" ?
107
- mode = "onChange" // or maybe mode "all"?
108
- reValidateMode = "onChange"
109
131
FormProps = { { style : { height : '100%' } } }
110
132
>
111
- < Grid container direction = "column" spacing = { 1.5 } height = "100%" >
112
- < Grid item container xs = "auto" spacing = { 1 } >
113
- < Grid item xs = { 12 } lg = { 6 } >
114
- < DateTimePickerElement < FormSchema >
115
- name = { START_DATE }
116
- label = { intl . formatMessage ( { id : 'announcements.table.startDate' } ) }
117
- transform = { datetimePickerTransform }
118
- timezone = "system"
119
- views = { pickerView }
120
- timeSteps = { { hours : 1 , minutes : 1 , seconds : 0 } }
121
- disablePast
133
+ < FormErrorProvider onError = { onErrorIntl } >
134
+ < Grid container direction = "column" spacing = { 1.5 } height = "100%" >
135
+ < Grid item container xs = "auto" spacing = { 1 } >
136
+ < Grid item xs = { 12 } lg = { 6 } >
137
+ < DateTimePickerElement < FormSchema >
138
+ name = { START_DATE }
139
+ label = { intl . formatMessage ( { id : 'announcements.table.startDate' } ) }
140
+ transform = { datetimePickerTransform }
141
+ timezone = "system"
142
+ views = { pickerView }
143
+ timeSteps = { { hours : 1 , minutes : 1 , seconds : 0 } }
144
+ disablePast
145
+ />
146
+ </ Grid >
147
+ < Grid item xs = { 12 } lg = { 6 } >
148
+ < DateTimePickerElement < FormSchema >
149
+ name = { END_DATE }
150
+ label = { intl . formatMessage ( { id : 'announcements.table.endDate' } ) }
151
+ transform = { datetimePickerTransform }
152
+ timezone = "system"
153
+ views = { pickerView }
154
+ timeSteps = { { hours : 1 , minutes : 1 , seconds : 0 } }
155
+ disablePast
156
+ minDateTime = { startDateValue ? new Date ( startDateValue ) : undefined }
157
+ />
158
+ </ Grid >
159
+ </ Grid >
160
+ < Grid item xs = "auto" >
161
+ < SelectElement < FormSchema >
162
+ name = { SEVERITY }
163
+ label = { intl . formatMessage ( { id : 'announcements.severity' } ) }
164
+ options = { useMemo (
165
+ ( ) =>
166
+ Object . values ( UserAdminSrv . AnnouncementSeverity ) . map ( ( value ) => ( {
167
+ id : value ,
168
+ label : intl . formatMessage ( { id : `announcements.severity.${ value } ` } ) ,
169
+ } ) ) ,
170
+ [ intl ]
171
+ ) }
172
+ fullWidth
122
173
/>
123
174
</ Grid >
124
- < Grid item xs = { 12 } lg = { 6 } >
125
- < DateTimePickerElement < FormSchema >
126
- name = { END_DATE }
127
- label = { intl . formatMessage ( { id : 'announcements.table.endDate' } ) }
128
- transform = { datetimePickerTransform }
129
- timezone = "system"
130
- views = { pickerView }
131
- timeSteps = { { hours : 1 , minutes : 1 , seconds : 0 } }
132
- disablePast
133
- minDateTime = { startDateValue ? new Date ( startDateValue ) : undefined }
175
+ < Grid item xs >
176
+ < TextareaAutosizeElement < FormSchema >
177
+ name = { MESSAGE }
178
+ label = { intl . formatMessage ( { id : 'announcements.form.message' } ) }
179
+ rows = { 5 } // why does it do nothing even if the field is set as multiline?!
180
+ fullWidth
181
+ //inputProps={{ maxLength: MESSAGE_MAX_LENGTH } satisfies Partial<HTMLInputElement>}
134
182
/>
135
183
</ Grid >
184
+ < Grid item xs = "auto" >
185
+ < SubmitButton variant = "outlined" type = "submit" fullWidth />
186
+ </ Grid >
136
187
</ Grid >
137
- < Grid item xs = "auto" >
138
- < SelectElement < FormSchema >
139
- name = { SEVERITY }
140
- label = { intl . formatMessage ( { id : 'announcements.severity' } ) }
141
- options = { useMemo (
142
- ( ) =>
143
- Object . values ( UserAdminSrv . AnnouncementSeverity ) . map ( ( value ) => ( {
144
- id : value ,
145
- label : intl . formatMessage ( { id : `announcements.severity.${ value } ` } ) ,
146
- } ) ) ,
147
- [ intl ]
148
- ) }
149
- fullWidth
150
- />
151
- </ Grid >
152
- < Grid item xs >
153
- < TextareaAutosizeElement < FormSchema >
154
- name = { MESSAGE }
155
- label = { intl . formatMessage ( { id : 'announcements.form.message' } ) }
156
- rows = { 5 } // why does it do nothing even if the field is set as multiline?!
157
- fullWidth
158
- />
159
- </ Grid >
160
- < Grid item xs = "auto" >
161
- < SubmitButton variant = "outlined" type = "submit" fullWidth />
162
- </ Grid >
163
- </ Grid >
188
+ </ FormErrorProvider >
164
189
</ FormContainer >
165
190
) ;
166
191
}
0 commit comments