@axinom/mosaic-ui 0.33.0-rc.0 → 0.33.0-rc.1
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/dist/components/FormStation/FormStation.d.ts.map +1 -1
- package/dist/components/FormStation/SaveOnNavigate/SaveOnNavigate.d.ts +2 -0
- package/dist/components/FormStation/SaveOnNavigate/SaveOnNavigate.d.ts.map +1 -1
- package/dist/components/FormStation/SaveOnNavigate/handleNavigationAttempt.d.ts +1 -1
- package/dist/components/FormStation/SaveOnNavigate/handleNavigationAttempt.d.ts.map +1 -1
- package/dist/components/FormStation/StationErrorStateType.d.ts +5 -0
- package/dist/components/FormStation/StationErrorStateType.d.ts.map +1 -0
- package/dist/components/FormStation/useValidationError.d.ts +15 -0
- package/dist/components/FormStation/useValidationError.d.ts.map +1 -0
- package/dist/index.es.js +1 -1
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/components/FormStation/FormStation.tsx +30 -16
- package/src/components/FormStation/SaveOnNavigate/SaveOnNavigate.tsx +5 -0
- package/src/components/FormStation/SaveOnNavigate/handleNavigationAttempt.spec.ts +21 -0
- package/src/components/FormStation/SaveOnNavigate/handleNavigationAttempt.ts +2 -0
- package/src/components/FormStation/StationErrorStateType.tsx +5 -0
- package/src/components/FormStation/useValidationError.tsx +59 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axinom/mosaic-ui",
|
|
3
|
-
"version": "0.33.0-rc.
|
|
3
|
+
"version": "0.33.0-rc.1",
|
|
4
4
|
"description": "UI components for building Axinom Mosaic applications",
|
|
5
5
|
"author": "Axinom",
|
|
6
6
|
"license": "PROPRIETARY",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"build-storybook": "storybook build"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@axinom/mosaic-core": "^0.4.6-rc.
|
|
35
|
+
"@axinom/mosaic-core": "^0.4.6-rc.1",
|
|
36
36
|
"@faker-js/faker": "^7.4.0",
|
|
37
37
|
"@popperjs/core": "^2.9.2",
|
|
38
38
|
"clsx": "^1.1.0",
|
|
@@ -102,5 +102,5 @@
|
|
|
102
102
|
"publishConfig": {
|
|
103
103
|
"access": "public"
|
|
104
104
|
},
|
|
105
|
-
"gitHead": "
|
|
105
|
+
"gitHead": "aa98a4a89a48ff7650a40acfec9a5b24c90d9c01"
|
|
106
106
|
}
|
|
@@ -28,10 +28,12 @@ import {
|
|
|
28
28
|
PageHeaderActionType,
|
|
29
29
|
PageHeaderProps,
|
|
30
30
|
} from '../PageHeader';
|
|
31
|
-
import { ErrorType,
|
|
31
|
+
import { ErrorType, StationMessage } from '../models';
|
|
32
32
|
import { FormActionData, InitialFormData } from './FormStation.models';
|
|
33
33
|
import classes from './FormStation.scss';
|
|
34
34
|
import { SaveOnNavigate } from './SaveOnNavigate/SaveOnNavigate';
|
|
35
|
+
import { StationErrorStateType } from './StationErrorStateType';
|
|
36
|
+
import { useValidationError } from './useValidationError';
|
|
35
37
|
|
|
36
38
|
export type ObjectSchemaDefinition<
|
|
37
39
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -103,7 +105,8 @@ interface FormActionProps<T, Y> extends Omit<ActionsProps, 'actions'> {
|
|
|
103
105
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
104
106
|
validationSchema?: OptionalObjectSchema<ObjectSchemaDefinition<any>>;
|
|
105
107
|
actions?: FormActionData<T, Y>[];
|
|
106
|
-
setStationError: (error:
|
|
108
|
+
setStationError: (error: StationErrorStateType) => void;
|
|
109
|
+
setValidationError: () => void;
|
|
107
110
|
submitResponse?: React.MutableRefObject<Y | undefined>;
|
|
108
111
|
alwaysSubmitBeforeAction?: boolean;
|
|
109
112
|
className?: string;
|
|
@@ -131,8 +134,12 @@ export const FormStation = <TValues extends Data, TSubmitResponse = unknown>({
|
|
|
131
134
|
}: PropsWithChildren<
|
|
132
135
|
FormStationProps<TValues, TSubmitResponse>
|
|
133
136
|
>): JSX.Element => {
|
|
134
|
-
const [stationError, setStationError] = useState<
|
|
135
|
-
|
|
137
|
+
const [stationError, setStationError] = useState<StationErrorStateType>();
|
|
138
|
+
|
|
139
|
+
const { setValidationError, validationWatcher } = useValidationError(
|
|
140
|
+
stationError,
|
|
141
|
+
setStationError,
|
|
142
|
+
);
|
|
136
143
|
|
|
137
144
|
const submitResponse = useRef<TSubmitResponse>();
|
|
138
145
|
const [isFormSubmitting, setIsFormSubmitting] = useState<boolean>(false);
|
|
@@ -182,19 +189,20 @@ export const FormStation = <TValues extends Data, TSubmitResponse = unknown>({
|
|
|
182
189
|
(initialData.data === null && !initialData.loading) ||
|
|
183
190
|
initialData.entityNotFound
|
|
184
191
|
) {
|
|
185
|
-
const stationError =
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
192
|
+
const stationError = {
|
|
193
|
+
...ErrorTypeToStationError(
|
|
194
|
+
initialData.error,
|
|
195
|
+
'An error occurred when trying to load data.',
|
|
196
|
+
'Entity not found',
|
|
197
|
+
),
|
|
198
|
+
type: 'loading',
|
|
199
|
+
};
|
|
190
200
|
|
|
191
201
|
setStationError(stationError);
|
|
192
|
-
setIsLoadingError(true);
|
|
193
202
|
} else {
|
|
194
|
-
if (
|
|
203
|
+
if (stationError?.type === 'loading') {
|
|
195
204
|
// Only clear the error if it is a loading error, which now seems to be cleared.
|
|
196
205
|
setStationError(undefined);
|
|
197
|
-
setIsLoadingError(false);
|
|
198
206
|
}
|
|
199
207
|
}
|
|
200
208
|
}, [
|
|
@@ -202,7 +210,7 @@ export const FormStation = <TValues extends Data, TSubmitResponse = unknown>({
|
|
|
202
210
|
initialData.error,
|
|
203
211
|
initialData.entityNotFound,
|
|
204
212
|
initialData.data,
|
|
205
|
-
|
|
213
|
+
stationError?.type,
|
|
206
214
|
]);
|
|
207
215
|
|
|
208
216
|
const getContent = (): JSX.Element | undefined => {
|
|
@@ -223,7 +231,10 @@ export const FormStation = <TValues extends Data, TSubmitResponse = unknown>({
|
|
|
223
231
|
// Loading successful
|
|
224
232
|
return (
|
|
225
233
|
<>
|
|
226
|
-
<SaveOnNavigate
|
|
234
|
+
<SaveOnNavigate
|
|
235
|
+
isSubmitting={isFormSubmitting}
|
|
236
|
+
onNavigationCancelled={setValidationError}
|
|
237
|
+
/>
|
|
227
238
|
<div className={classes.main}>
|
|
228
239
|
<div
|
|
229
240
|
className={clsx(classes.formWrapper, {
|
|
@@ -272,6 +283,7 @@ export const FormStation = <TValues extends Data, TSubmitResponse = unknown>({
|
|
|
272
283
|
enableReinitialize={true}
|
|
273
284
|
>
|
|
274
285
|
<>
|
|
286
|
+
{validationWatcher}
|
|
275
287
|
<FormStationHeader
|
|
276
288
|
titleProperty={titleProperty as string}
|
|
277
289
|
defaultTitle={defaultTitle}
|
|
@@ -296,6 +308,7 @@ export const FormStation = <TValues extends Data, TSubmitResponse = unknown>({
|
|
|
296
308
|
width={actionsWidth}
|
|
297
309
|
validationSchema={validationSchema}
|
|
298
310
|
setStationError={setStationError}
|
|
311
|
+
setValidationError={setValidationError}
|
|
299
312
|
submitResponse={submitResponse}
|
|
300
313
|
alwaysSubmitBeforeAction={alwaysSubmitBeforeAction}
|
|
301
314
|
className={classes.actionsPanel}
|
|
@@ -317,6 +330,7 @@ const FormStationAction = <T, Y>(
|
|
|
317
330
|
actions,
|
|
318
331
|
validationSchema,
|
|
319
332
|
setStationError,
|
|
333
|
+
setValidationError,
|
|
320
334
|
submitResponse,
|
|
321
335
|
alwaysSubmitBeforeAction,
|
|
322
336
|
className = '',
|
|
@@ -352,8 +366,7 @@ const FormStationAction = <T, Y>(
|
|
|
352
366
|
!isValid ||
|
|
353
367
|
(await validationSchema?.isValid(values)) === false
|
|
354
368
|
) {
|
|
355
|
-
|
|
356
|
-
console.log('form invalid, action not performed');
|
|
369
|
+
setValidationError();
|
|
357
370
|
// Making sure that the fields will actually show the validation messages (they won't if the from was not touched yet - e.g. on a create station)
|
|
358
371
|
validateForm();
|
|
359
372
|
return;
|
|
@@ -405,6 +418,7 @@ const FormStationAction = <T, Y>(
|
|
|
405
418
|
isValid,
|
|
406
419
|
resetForm,
|
|
407
420
|
setStationError,
|
|
421
|
+
setValidationError,
|
|
408
422
|
submitForm,
|
|
409
423
|
submitResponse,
|
|
410
424
|
validateForm,
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { useFormikContext } from 'formik';
|
|
2
2
|
import React, { useState } from 'react';
|
|
3
|
+
import { noop } from '../../../helpers/utils';
|
|
3
4
|
import { NavigationAPI, useReactRouterPause } from '../../../hooks';
|
|
4
5
|
import { handleNavigationAttempt } from './handleNavigationAttempt';
|
|
5
6
|
|
|
6
7
|
interface SaveOnNavigateProps {
|
|
7
8
|
/** If set to true, will prevent form submission when navigating away. (default: false) */
|
|
8
9
|
isSubmitting?: boolean;
|
|
10
|
+
/** Callback that will be called when a navigation attempt was cancelled */
|
|
11
|
+
onNavigationCancelled?: () => void;
|
|
9
12
|
}
|
|
10
13
|
|
|
11
14
|
/**
|
|
@@ -13,6 +16,7 @@ interface SaveOnNavigateProps {
|
|
|
13
16
|
*/
|
|
14
17
|
export const SaveOnNavigate: React.FC<SaveOnNavigateProps> = ({
|
|
15
18
|
isSubmitting = false,
|
|
19
|
+
onNavigationCancelled = noop,
|
|
16
20
|
}) => {
|
|
17
21
|
const { dirty, isValid, submitForm } = useFormikContext();
|
|
18
22
|
const [canSubmit, setCanSubmit] = useState<boolean>(true);
|
|
@@ -28,6 +32,7 @@ export const SaveOnNavigate: React.FC<SaveOnNavigateProps> = ({
|
|
|
28
32
|
isSubmitting,
|
|
29
33
|
canSubmit,
|
|
30
34
|
setCanSubmit,
|
|
35
|
+
onNavigationCancelled,
|
|
31
36
|
),
|
|
32
37
|
},
|
|
33
38
|
dirty,
|
|
@@ -20,6 +20,7 @@ describe('handleNavigationAttempt', () => {
|
|
|
20
20
|
const isSubmitting = false;
|
|
21
21
|
const canSubmit = true;
|
|
22
22
|
const setCanSubmit = jest.fn();
|
|
23
|
+
const onNavigationCancelled = jest.fn();
|
|
23
24
|
|
|
24
25
|
// Act
|
|
25
26
|
await handleNavigationAttempt(
|
|
@@ -30,6 +31,7 @@ describe('handleNavigationAttempt', () => {
|
|
|
30
31
|
isSubmitting,
|
|
31
32
|
canSubmit,
|
|
32
33
|
setCanSubmit,
|
|
34
|
+
onNavigationCancelled,
|
|
33
35
|
);
|
|
34
36
|
|
|
35
37
|
// Assert
|
|
@@ -38,6 +40,7 @@ describe('handleNavigationAttempt', () => {
|
|
|
38
40
|
expect(navigation.cancel).not.toHaveBeenCalled();
|
|
39
41
|
expect(submitForm).not.toHaveBeenCalled();
|
|
40
42
|
expect(setCanSubmit).not.toHaveBeenCalled();
|
|
43
|
+
expect(onNavigationCancelled).not.toHaveBeenCalled();
|
|
41
44
|
});
|
|
42
45
|
|
|
43
46
|
it(`blocks navigation when 'isValid' is 'false' by calling 'navigation.cancel()'`, async () => {
|
|
@@ -48,6 +51,7 @@ describe('handleNavigationAttempt', () => {
|
|
|
48
51
|
const isSubmitting = false;
|
|
49
52
|
const canSubmit = true;
|
|
50
53
|
const setCanSubmit = jest.fn();
|
|
54
|
+
const onNavigationCancelled = jest.fn();
|
|
51
55
|
|
|
52
56
|
// Act
|
|
53
57
|
await handleNavigationAttempt(
|
|
@@ -58,6 +62,7 @@ describe('handleNavigationAttempt', () => {
|
|
|
58
62
|
isSubmitting,
|
|
59
63
|
canSubmit,
|
|
60
64
|
setCanSubmit,
|
|
65
|
+
onNavigationCancelled,
|
|
61
66
|
);
|
|
62
67
|
|
|
63
68
|
// Assert
|
|
@@ -66,6 +71,7 @@ describe('handleNavigationAttempt', () => {
|
|
|
66
71
|
expect(navigation.cancel).toHaveBeenCalledTimes(1);
|
|
67
72
|
expect(submitForm).not.toHaveBeenCalled();
|
|
68
73
|
expect(setCanSubmit).not.toHaveBeenCalled();
|
|
74
|
+
expect(onNavigationCancelled).toHaveBeenCalled();
|
|
69
75
|
});
|
|
70
76
|
|
|
71
77
|
it(`pauses further navigation when 'dirty' is 'true' and 'isValid' is 'true' by calling 'navigation.pause()'`, async () => {
|
|
@@ -76,6 +82,7 @@ describe('handleNavigationAttempt', () => {
|
|
|
76
82
|
const isSubmitting = true;
|
|
77
83
|
const canSubmit = true;
|
|
78
84
|
const setCanSubmit = jest.fn();
|
|
85
|
+
const onNavigationCancelled = jest.fn();
|
|
79
86
|
|
|
80
87
|
// Act
|
|
81
88
|
await handleNavigationAttempt(
|
|
@@ -86,6 +93,7 @@ describe('handleNavigationAttempt', () => {
|
|
|
86
93
|
isSubmitting,
|
|
87
94
|
canSubmit,
|
|
88
95
|
setCanSubmit,
|
|
96
|
+
onNavigationCancelled,
|
|
89
97
|
);
|
|
90
98
|
|
|
91
99
|
// Assert
|
|
@@ -94,6 +102,7 @@ describe('handleNavigationAttempt', () => {
|
|
|
94
102
|
expect(navigation.cancel).not.toHaveBeenCalled();
|
|
95
103
|
expect(submitForm).not.toHaveBeenCalled();
|
|
96
104
|
expect(setCanSubmit).not.toHaveBeenCalled();
|
|
105
|
+
expect(onNavigationCancelled).not.toHaveBeenCalled();
|
|
97
106
|
});
|
|
98
107
|
|
|
99
108
|
it(`while 'isSubmitting' is 'true' navigation should continue to be paused by not calling 'navigation.resume()'`, async () => {
|
|
@@ -104,6 +113,7 @@ describe('handleNavigationAttempt', () => {
|
|
|
104
113
|
const isSubmitting = true;
|
|
105
114
|
const canSubmit = true;
|
|
106
115
|
const setCanSubmit = jest.fn();
|
|
116
|
+
const onNavigationCancelled = jest.fn();
|
|
107
117
|
|
|
108
118
|
// Act
|
|
109
119
|
await handleNavigationAttempt(
|
|
@@ -114,6 +124,7 @@ describe('handleNavigationAttempt', () => {
|
|
|
114
124
|
isSubmitting,
|
|
115
125
|
canSubmit,
|
|
116
126
|
setCanSubmit,
|
|
127
|
+
onNavigationCancelled,
|
|
117
128
|
);
|
|
118
129
|
|
|
119
130
|
// Assert
|
|
@@ -122,6 +133,7 @@ describe('handleNavigationAttempt', () => {
|
|
|
122
133
|
expect(navigation.cancel).not.toHaveBeenCalled();
|
|
123
134
|
expect(submitForm).not.toHaveBeenCalled();
|
|
124
135
|
expect(setCanSubmit).not.toHaveBeenCalled();
|
|
136
|
+
expect(onNavigationCancelled).not.toHaveBeenCalled();
|
|
125
137
|
});
|
|
126
138
|
|
|
127
139
|
it(`while 'canSubmit' is 'false' navigation should continue to be paused by not calling 'navigation.resume()'`, async () => {
|
|
@@ -132,6 +144,7 @@ describe('handleNavigationAttempt', () => {
|
|
|
132
144
|
const isSubmitting = false;
|
|
133
145
|
const canSubmit = false;
|
|
134
146
|
const setCanSubmit = jest.fn();
|
|
147
|
+
const onNavigationCancelled = jest.fn();
|
|
135
148
|
|
|
136
149
|
// Act
|
|
137
150
|
await handleNavigationAttempt(
|
|
@@ -142,6 +155,7 @@ describe('handleNavigationAttempt', () => {
|
|
|
142
155
|
isSubmitting,
|
|
143
156
|
canSubmit,
|
|
144
157
|
setCanSubmit,
|
|
158
|
+
onNavigationCancelled,
|
|
145
159
|
);
|
|
146
160
|
|
|
147
161
|
// Assert
|
|
@@ -150,6 +164,7 @@ describe('handleNavigationAttempt', () => {
|
|
|
150
164
|
expect(navigation.cancel).not.toHaveBeenCalled();
|
|
151
165
|
expect(submitForm).not.toHaveBeenCalled();
|
|
152
166
|
expect(setCanSubmit).not.toHaveBeenCalled();
|
|
167
|
+
expect(onNavigationCancelled).not.toHaveBeenCalled();
|
|
153
168
|
});
|
|
154
169
|
|
|
155
170
|
it(`navigation can resume after the submitForm promise was successfully resolved by calling 'navigation.resume()'`, async () => {
|
|
@@ -160,6 +175,7 @@ describe('handleNavigationAttempt', () => {
|
|
|
160
175
|
const isSubmitting = false;
|
|
161
176
|
const canSubmit = true;
|
|
162
177
|
const setCanSubmit = jest.fn();
|
|
178
|
+
const onNavigationCancelled = jest.fn();
|
|
163
179
|
|
|
164
180
|
// Act
|
|
165
181
|
await handleNavigationAttempt(
|
|
@@ -170,6 +186,7 @@ describe('handleNavigationAttempt', () => {
|
|
|
170
186
|
isSubmitting,
|
|
171
187
|
canSubmit,
|
|
172
188
|
setCanSubmit,
|
|
189
|
+
onNavigationCancelled,
|
|
173
190
|
);
|
|
174
191
|
|
|
175
192
|
// Assert
|
|
@@ -179,6 +196,7 @@ describe('handleNavigationAttempt', () => {
|
|
|
179
196
|
expect(submitForm).toHaveBeenCalledTimes(1);
|
|
180
197
|
expect(setCanSubmit).toHaveBeenCalledTimes(1);
|
|
181
198
|
expect(setCanSubmit).toHaveBeenCalledWith(false);
|
|
199
|
+
expect(onNavigationCancelled).not.toHaveBeenCalledWith(false);
|
|
182
200
|
});
|
|
183
201
|
|
|
184
202
|
it(`navigation is cancelled when the 'submitForm' promise was rejected by calling 'navigation.cancel()'`, async () => {
|
|
@@ -191,6 +209,7 @@ describe('handleNavigationAttempt', () => {
|
|
|
191
209
|
const isSubmitting = false;
|
|
192
210
|
const canSubmit = true;
|
|
193
211
|
const setCanSubmit = jest.fn();
|
|
212
|
+
const onNavigationCancelled = jest.fn();
|
|
194
213
|
|
|
195
214
|
// Act
|
|
196
215
|
await handleNavigationAttempt(
|
|
@@ -201,6 +220,7 @@ describe('handleNavigationAttempt', () => {
|
|
|
201
220
|
isSubmitting,
|
|
202
221
|
canSubmit,
|
|
203
222
|
setCanSubmit,
|
|
223
|
+
onNavigationCancelled,
|
|
204
224
|
);
|
|
205
225
|
|
|
206
226
|
// Assert
|
|
@@ -211,5 +231,6 @@ describe('handleNavigationAttempt', () => {
|
|
|
211
231
|
expect(setCanSubmit).toHaveBeenCalledTimes(2);
|
|
212
232
|
expect(setCanSubmit).toHaveBeenNthCalledWith(1, false);
|
|
213
233
|
expect(setCanSubmit).toHaveBeenNthCalledWith(2, true);
|
|
234
|
+
expect(onNavigationCancelled).not.toHaveBeenCalled();
|
|
214
235
|
});
|
|
215
236
|
});
|
|
@@ -10,6 +10,7 @@ export const handleNavigationAttempt = async (
|
|
|
10
10
|
isSubmitting: boolean,
|
|
11
11
|
canSubmit: boolean,
|
|
12
12
|
setCanSubmit: React.Dispatch<React.SetStateAction<boolean>>,
|
|
13
|
+
onNavigationCancelled: () => void,
|
|
13
14
|
): Promise<void> => {
|
|
14
15
|
if (!dirty) {
|
|
15
16
|
// Form values didn't change, just navigate away
|
|
@@ -20,6 +21,7 @@ export const handleNavigationAttempt = async (
|
|
|
20
21
|
// Form is invalid, cancel navigation
|
|
21
22
|
// TODO: Add a message here (probably connected to "Error handling")
|
|
22
23
|
navigation.cancel();
|
|
24
|
+
onNavigationCancelled();
|
|
23
25
|
return;
|
|
24
26
|
}
|
|
25
27
|
// Form values have changed and form is valid, attempt save
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { useFormikContext } from 'formik';
|
|
2
|
+
import React, { useCallback, useEffect, useState } from 'react';
|
|
3
|
+
import { StationErrorStateType } from './StationErrorStateType';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Component that watches for changes in the form validation state
|
|
7
|
+
* and calls the callback with the current state.
|
|
8
|
+
*/
|
|
9
|
+
const ValidationWatcher: React.FC<{
|
|
10
|
+
callback: (isValid: boolean) => void;
|
|
11
|
+
}> = ({ callback: isValid }) => {
|
|
12
|
+
const formik = useFormikContext();
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
isValid(formik.isValid);
|
|
15
|
+
}, [formik.isValid, isValid]);
|
|
16
|
+
return null;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Cares for showing (and removing) validation errors.
|
|
20
|
+
* @param stationError the currently showing error
|
|
21
|
+
* @param setStationError the setter for the currently showing error
|
|
22
|
+
* @returns
|
|
23
|
+
* `setValidationError` - call this method to set the validation error.
|
|
24
|
+
* `validationWatcher` - place this JSX somewhere inside the <Formik /> component children.
|
|
25
|
+
*/
|
|
26
|
+
export function useValidationError(
|
|
27
|
+
stationError: StationErrorStateType | undefined,
|
|
28
|
+
setStationError: React.Dispatch<
|
|
29
|
+
React.SetStateAction<StationErrorStateType | undefined>
|
|
30
|
+
>,
|
|
31
|
+
): {
|
|
32
|
+
setValidationError: () => void;
|
|
33
|
+
validationWatcher: JSX.Element;
|
|
34
|
+
} {
|
|
35
|
+
// Track validation state of the from using the validationWatcher
|
|
36
|
+
const [isValid, setIsValid] = useState<boolean>(true);
|
|
37
|
+
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
// if the form became valid, remove the validation error
|
|
40
|
+
if (isValid && stationError && stationError.type === 'validation') {
|
|
41
|
+
setStationError(undefined);
|
|
42
|
+
}
|
|
43
|
+
}, [isValid, setStationError, stationError]);
|
|
44
|
+
|
|
45
|
+
const setValidationError = useCallback((): void => {
|
|
46
|
+
// set the validation error
|
|
47
|
+
setStationError({
|
|
48
|
+
title: 'Please fix the errors in the form to proceed.',
|
|
49
|
+
type: 'validation',
|
|
50
|
+
});
|
|
51
|
+
}, [setStationError]);
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
setValidationError,
|
|
55
|
+
validationWatcher: (
|
|
56
|
+
<ValidationWatcher callback={(valid) => setIsValid(valid)} />
|
|
57
|
+
),
|
|
58
|
+
};
|
|
59
|
+
}
|