@oneblink/apps-react 2.11.0-beta.4 → 2.11.0-beta.6
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/form-elements/FormElementDate.js +4 -10
- package/dist/form-elements/FormElementDate.js.map +1 -1
- package/dist/form-elements/FormElementDateTime.js +4 -10
- package/dist/form-elements/FormElementDateTime.js.map +1 -1
- package/dist/form-elements/FormElementLocation.js +18 -17
- package/dist/form-elements/FormElementLocation.js.map +1 -1
- package/dist/hooks/useConditionalLogic.d.ts +3 -2
- package/dist/hooks/useFormElementDateFromTo.d.ts +4 -2
- package/dist/hooks/useFormElementDateFromTo.js +23 -18
- package/dist/hooks/useFormElementDateFromTo.js.map +1 -1
- package/dist/hooks/useFormValidation.js +21 -2
- package/dist/hooks/useFormValidation.js.map +1 -1
- package/dist/services/form-validation.d.ts +2 -2
- package/dist/services/form-validation.js +354 -388
- package/dist/services/form-validation.js.map +1 -1
- package/dist/services/getDateRangeConfiguration.d.ts +8 -0
- package/dist/services/getDateRangeConfiguration.js +11 -0
- package/dist/services/getDateRangeConfiguration.js.map +1 -0
- package/dist/types/form.d.ts +8 -23
- package/dist/types/form.js +0 -1
- package/dist/types/form.js.map +1 -1
- package/package.json +2 -2
@@ -3,6 +3,8 @@ import { localisationService } from '@oneblink/apps';
|
|
3
3
|
import { parseDateValue } from './generate-default-data';
|
4
4
|
import generateCivicaNameRecordElements from './generateCivicaNameRecordElements';
|
5
5
|
import generateFreshdeskDependentFieldElements from './generateFreshdeskDependentFieldElements';
|
6
|
+
import cleanFormSubmissionModel from './cleanFormSubmissionModel';
|
7
|
+
import getDateRangeConfiguration from './getDateRangeConfiguration';
|
6
8
|
export const lookupValidationMessage = 'Lookup is required';
|
7
9
|
// https://validatejs.org/#validators-datetime
|
8
10
|
// Before using it we must add the parse and format functions
|
@@ -19,10 +21,12 @@ validate.extend(validate.validators.datetime, {
|
|
19
21
|
return options.format(dateValue);
|
20
22
|
},
|
21
23
|
});
|
22
|
-
validate.validators.entries = function (value, { setSchema, entrySchema, }) {
|
24
|
+
validate.validators.entries = function (value, { setSchema, entrySchema: { schema: entrySchema, formElementConditionallyShown }, }) {
|
23
25
|
const entries = Array.isArray(value) ? value : [];
|
24
26
|
const entryErrors = entries.reduce((errorsByIndex, entry, index) => {
|
25
|
-
const entryValidation =
|
27
|
+
const entryValidation = validateSubmission(entrySchema, entry, (formElementConditionallyShown === null || formElementConditionallyShown === void 0 ? void 0 : formElementConditionallyShown.type) === 'repeatableSet'
|
28
|
+
? formElementConditionallyShown.entries[index.toString()]
|
29
|
+
: undefined);
|
26
30
|
if (entryValidation) {
|
27
31
|
errorsByIndex[index] = entryValidation;
|
28
32
|
}
|
@@ -38,8 +42,10 @@ validate.validators.entries = function (value, { setSchema, entrySchema, }) {
|
|
38
42
|
entries: entryErrors,
|
39
43
|
};
|
40
44
|
};
|
41
|
-
validate.validators.nestedElements = function (value, schema) {
|
42
|
-
const errors =
|
45
|
+
validate.validators.nestedElements = function (value, { schema, formElementConditionallyShown }) {
|
46
|
+
const errors = validateSubmission(schema, value, (formElementConditionallyShown === null || formElementConditionallyShown === void 0 ? void 0 : formElementConditionallyShown.type) === 'formElements'
|
47
|
+
? formElementConditionallyShown.formElements
|
48
|
+
: undefined);
|
43
49
|
if (!errors) {
|
44
50
|
return;
|
45
51
|
}
|
@@ -125,418 +131,378 @@ function getCustomRegexFormatConfig(formElement) {
|
|
125
131
|
}
|
126
132
|
: undefined;
|
127
133
|
}
|
128
|
-
export function validateSubmission(schema, submission, formElementsConditionallyShown) {
|
129
|
-
const formElementsValidation = validateSingleMessageError(submission, schema);
|
130
|
-
if (formElementsValidation) {
|
131
|
-
clearValidationMessagesForHiddenElements(formElementsValidation, formElementsConditionallyShown);
|
132
|
-
if (!validate.isEmpty(formElementsValidation)) {
|
133
|
-
return formElementsValidation;
|
134
|
-
}
|
135
|
-
}
|
136
|
-
}
|
137
|
-
const clearValidationMessagesForHiddenElements = (formElementsValidation, formElementsConditionallyShown) => {
|
138
|
-
// If there is no validation to check, there are no invalid elements
|
139
|
-
// If there is no conditionally shown elements, all invalid elements should display validation messages,
|
140
|
-
if (!formElementsValidation || !formElementsConditionallyShown) {
|
141
|
-
return;
|
142
|
-
}
|
143
|
-
for (const key in formElementsValidation) {
|
144
|
-
const formElementValidation = formElementsValidation[key];
|
145
|
-
if (!formElementValidation) {
|
146
|
-
continue;
|
147
|
-
}
|
148
|
-
const formElementConditionallyShown = formElementsConditionallyShown[key];
|
149
|
-
// If the validation is for an element that is being hidden,
|
150
|
-
// we can remove the validation message and move to the next validation
|
151
|
-
if (formElementConditionallyShown === null || formElementConditionallyShown === void 0 ? void 0 : formElementConditionallyShown.isHidden) {
|
152
|
-
delete formElementsValidation[key];
|
153
|
-
continue;
|
154
|
-
}
|
155
|
-
// If the validation is for a single element (not nested elements),
|
156
|
-
// we will always show the validation message
|
157
|
-
if (typeof formElementValidation === 'string') {
|
158
|
-
continue;
|
159
|
-
}
|
160
|
-
// Here we will check to see if the nested elements that are
|
161
|
-
// invalid are being shown, if not, remove validation messages
|
162
|
-
switch (formElementValidation.type) {
|
163
|
-
case 'repeatableSet': {
|
164
|
-
for (const index in formElementValidation.entries) {
|
165
|
-
clearValidationMessagesForHiddenElements(formElementValidation.entries[index], formElementConditionallyShown &&
|
166
|
-
formElementConditionallyShown.type === 'repeatableSet'
|
167
|
-
? formElementConditionallyShown.entries[index]
|
168
|
-
: undefined);
|
169
|
-
if (validate.isEmpty(formElementValidation.entries[index])) {
|
170
|
-
delete formElementValidation.entries[index];
|
171
|
-
}
|
172
|
-
}
|
173
|
-
// Remove the validation if all entries are valid and the set is also valid
|
174
|
-
if (validate.isEmpty(formElementValidation.entries) &&
|
175
|
-
!formElementValidation.set) {
|
176
|
-
delete formElementsValidation[key];
|
177
|
-
}
|
178
|
-
break;
|
179
|
-
}
|
180
|
-
case 'formElements': {
|
181
|
-
clearValidationMessagesForHiddenElements(formElementValidation.formElements, (formElementConditionallyShown === null || formElementConditionallyShown === void 0 ? void 0 : formElementConditionallyShown.type) === 'formElements'
|
182
|
-
? formElementConditionallyShown === null || formElementConditionallyShown === void 0 ? void 0 : formElementConditionallyShown.formElements
|
183
|
-
: undefined);
|
184
|
-
if (validate.isEmpty(formElementValidation.formElements)) {
|
185
|
-
delete formElementsValidation[key];
|
186
|
-
}
|
187
|
-
break;
|
188
|
-
}
|
189
|
-
}
|
190
|
-
}
|
191
|
-
};
|
192
134
|
const presence = ({ required, requiredMessage }, message) => (required ? { message: requiredMessage || message } : false);
|
193
135
|
const escapeElementName = (elementName) => {
|
194
136
|
const escapedName = elementName.replace(/\./g, '\\.');
|
195
137
|
return escapedName;
|
196
138
|
};
|
139
|
+
function getCleanDateRangeConfiguration(options, elements, submission, formElementsConditionallyShown) {
|
140
|
+
if (options.referenceFormElementId && submission) {
|
141
|
+
const { model } = cleanFormSubmissionModel(submission, elements, formElementsConditionallyShown, true);
|
142
|
+
return getDateRangeConfiguration(options, elements, model);
|
143
|
+
}
|
144
|
+
return [options.date, options.daysOffset];
|
145
|
+
}
|
197
146
|
export function generateValidationSchema(elements, elementIdsWithLookupsExecuted) {
|
198
147
|
return elements.reduce((partialSchema, formElement) => {
|
199
|
-
var _a;
|
200
148
|
switch (formElement.type) {
|
149
|
+
// Elements that do not need to be validated
|
201
150
|
case 'summary':
|
202
151
|
case 'calculation':
|
203
152
|
case 'image':
|
204
153
|
case 'html':
|
205
154
|
case 'infoPage':
|
206
155
|
case 'heading': {
|
207
|
-
|
208
|
-
}
|
209
|
-
case 'section':
|
210
|
-
case 'page': {
|
211
|
-
const nestedSchema = generateValidationSchema(formElement.elements, elementIdsWithLookupsExecuted);
|
212
|
-
Object.assign(partialSchema, nestedSchema);
|
213
|
-
break;
|
214
|
-
}
|
215
|
-
case 'draw': {
|
216
|
-
partialSchema[escapeElementName(formElement.name)] = {
|
217
|
-
attachment: true,
|
218
|
-
presence: presence(formElement, 'A saved signature is required'),
|
219
|
-
};
|
220
|
-
break;
|
221
|
-
}
|
222
|
-
case 'camera': {
|
223
|
-
partialSchema[escapeElementName(formElement.name)] = {
|
224
|
-
attachment: true,
|
225
|
-
presence: presence(formElement, 'A photo is required'),
|
226
|
-
};
|
227
|
-
break;
|
228
|
-
}
|
229
|
-
case 'captcha': {
|
230
|
-
partialSchema[escapeElementName(formElement.name)] = {
|
231
|
-
presence: presence({ ...formElement, required: true }, 'Please complete the CAPTCHA successfully'),
|
232
|
-
};
|
233
|
-
break;
|
234
|
-
}
|
235
|
-
case 'location': {
|
236
|
-
partialSchema[escapeElementName(formElement.name)] = {
|
237
|
-
presence: presence(formElement, 'Please select a location'),
|
238
|
-
lookups: {
|
239
|
-
formElement,
|
240
|
-
elementIdsWithLookupsExecuted,
|
241
|
-
},
|
242
|
-
};
|
243
|
-
break;
|
244
|
-
}
|
245
|
-
case 'compliance': {
|
246
|
-
partialSchema[escapeElementName(formElement.name)] = {
|
247
|
-
presence: presence(formElement, 'Required'),
|
248
|
-
lookups: {
|
249
|
-
formElement,
|
250
|
-
elementIdsWithLookupsExecuted,
|
251
|
-
},
|
252
|
-
attachments: true,
|
253
|
-
};
|
254
|
-
break;
|
255
|
-
}
|
256
|
-
case 'checkboxes': {
|
257
|
-
const requiredAllDefaultMessage = 'All options are required';
|
258
|
-
partialSchema[escapeElementName(formElement.name)] = {
|
259
|
-
presence: presence({
|
260
|
-
...formElement,
|
261
|
-
required: formElement.required || !!formElement.requiredAll,
|
262
|
-
}, formElement.requiredAll ? requiredAllDefaultMessage : 'Required'),
|
263
|
-
length: formElement.requiredAll
|
264
|
-
? {
|
265
|
-
is: (_a = formElement.options) === null || _a === void 0 ? void 0 : _a.length,
|
266
|
-
message: formElement.requiredMessage || requiredAllDefaultMessage,
|
267
|
-
}
|
268
|
-
: undefined,
|
269
|
-
lookups: {
|
270
|
-
formElement,
|
271
|
-
elementIdsWithLookupsExecuted,
|
272
|
-
},
|
273
|
-
};
|
274
|
-
break;
|
275
|
-
}
|
276
|
-
case 'abn':
|
277
|
-
case 'geoscapeAddress':
|
278
|
-
case 'pointAddress':
|
279
|
-
case 'civicaStreetName':
|
280
|
-
case 'autocomplete':
|
281
|
-
case 'radio':
|
282
|
-
case 'select': {
|
283
|
-
partialSchema[escapeElementName(formElement.name)] = {
|
284
|
-
presence: presence(formElement, 'Required'),
|
285
|
-
lookups: {
|
286
|
-
formElement,
|
287
|
-
elementIdsWithLookupsExecuted,
|
288
|
-
},
|
289
|
-
};
|
290
|
-
break;
|
291
|
-
}
|
292
|
-
case 'boolean': {
|
293
|
-
partialSchema[escapeElementName(formElement.name)] = {
|
294
|
-
isTrue: formElement.required && 'Required',
|
295
|
-
lookups: {
|
296
|
-
formElement,
|
297
|
-
elementIdsWithLookupsExecuted,
|
298
|
-
},
|
299
|
-
};
|
300
|
-
break;
|
156
|
+
return partialSchema;
|
301
157
|
}
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
format: {
|
310
|
-
pattern: /\d{3}-\d{3}/,
|
311
|
-
message: 'Please enter a valid BSB number',
|
312
|
-
},
|
313
|
-
};
|
314
|
-
break;
|
315
|
-
}
|
316
|
-
case 'barcodeScanner': {
|
317
|
-
partialSchema[escapeElementName(formElement.name)] = {
|
318
|
-
presence: presence(formElement, 'Please scan a barcode or enter a value'),
|
319
|
-
lookups: {
|
320
|
-
formElement,
|
321
|
-
elementIdsWithLookupsExecuted,
|
322
|
-
},
|
323
|
-
format: getCustomRegexFormatConfig(formElement),
|
324
|
-
};
|
325
|
-
break;
|
326
|
-
}
|
327
|
-
case 'text':
|
328
|
-
case 'textarea': {
|
329
|
-
partialSchema[escapeElementName(formElement.name)] = {
|
330
|
-
presence: presence(formElement, 'Please enter a value'),
|
331
|
-
lookups: {
|
332
|
-
formElement,
|
333
|
-
elementIdsWithLookupsExecuted,
|
334
|
-
},
|
335
|
-
length: {
|
336
|
-
minimum: formElement.minLength,
|
337
|
-
tooShort: 'Please enter a value with at least %{count} character(s)',
|
338
|
-
maximum: formElement.maxLength,
|
339
|
-
tooLong: 'Please enter a value with %{count} character(s) or less',
|
340
|
-
},
|
341
|
-
format: getCustomRegexFormatConfig(formElement),
|
342
|
-
};
|
343
|
-
break;
|
344
|
-
}
|
345
|
-
case 'telephone': {
|
346
|
-
partialSchema[escapeElementName(formElement.name)] = {
|
347
|
-
presence: presence(formElement, 'Please enter a phone number'),
|
348
|
-
lookups: {
|
349
|
-
formElement,
|
350
|
-
elementIdsWithLookupsExecuted,
|
351
|
-
},
|
352
|
-
format: getCustomRegexFormatConfig(formElement),
|
353
|
-
};
|
354
|
-
break;
|
355
|
-
}
|
356
|
-
case 'email': {
|
357
|
-
partialSchema[escapeElementName(formElement.name)] = {
|
358
|
-
presence: presence(formElement, 'Please enter an email address'),
|
359
|
-
email: {
|
360
|
-
message: 'Please enter a valid email for this field',
|
361
|
-
},
|
362
|
-
lookups: {
|
363
|
-
formElement,
|
364
|
-
elementIdsWithLookupsExecuted,
|
365
|
-
},
|
366
|
-
format: getCustomRegexFormatConfig(formElement),
|
367
|
-
};
|
368
|
-
break;
|
369
|
-
}
|
370
|
-
case 'time': {
|
371
|
-
partialSchema[escapeElementName(formElement.name)] = {
|
372
|
-
presence: presence(formElement, 'Please select a time'),
|
373
|
-
lookups: {
|
374
|
-
formElement,
|
375
|
-
elementIdsWithLookupsExecuted,
|
376
|
-
},
|
377
|
-
};
|
378
|
-
break;
|
379
|
-
}
|
380
|
-
case 'date': {
|
381
|
-
partialSchema[escapeElementName(formElement.name)] = {
|
382
|
-
presence: presence(formElement, 'Please select a date'),
|
383
|
-
date: {
|
384
|
-
format: (v) => localisationService.formatDate(v),
|
385
|
-
earliest: parseDateValue({
|
386
|
-
dateOnly: true,
|
387
|
-
daysOffset: formElement.fromDateDaysOffset,
|
388
|
-
value: formElement.fromDate,
|
389
|
-
}),
|
390
|
-
latest: parseDateValue({
|
391
|
-
dateOnly: true,
|
392
|
-
daysOffset: formElement.toDateDaysOffset,
|
393
|
-
value: formElement.toDate,
|
394
|
-
}),
|
395
|
-
notValid: 'Please select a valid date',
|
396
|
-
tooEarly: 'Date cannot be before %{date}',
|
397
|
-
tooLate: 'Date cannot be after %{date}',
|
398
|
-
},
|
399
|
-
lookups: {
|
400
|
-
formElement,
|
401
|
-
elementIdsWithLookupsExecuted,
|
402
|
-
},
|
403
|
-
};
|
404
|
-
break;
|
405
|
-
}
|
406
|
-
case 'datetime': {
|
407
|
-
partialSchema[escapeElementName(formElement.name)] = {
|
408
|
-
presence: presence(formElement, 'Please select a date and time'),
|
409
|
-
datetime: {
|
410
|
-
format: (v) => localisationService.formatDatetime(v),
|
411
|
-
earliest: parseDateValue({
|
412
|
-
dateOnly: false,
|
413
|
-
daysOffset: formElement.fromDateDaysOffset,
|
414
|
-
value: formElement.fromDate,
|
415
|
-
}),
|
416
|
-
latest: parseDateValue({
|
417
|
-
dateOnly: false,
|
418
|
-
daysOffset: formElement.toDateDaysOffset,
|
419
|
-
value: formElement.toDate,
|
420
|
-
}),
|
421
|
-
notValid: 'Please select a valid date and time',
|
422
|
-
tooEarly: 'Date and time cannot be before %{date}',
|
423
|
-
tooLate: 'Date and time cannot be after %{date}',
|
424
|
-
},
|
425
|
-
lookups: {
|
426
|
-
formElement,
|
427
|
-
elementIdsWithLookupsExecuted,
|
428
|
-
},
|
429
|
-
};
|
430
|
-
break;
|
158
|
+
}
|
159
|
+
const constraint = (value, submission, propertyName, { formElementsConditionallyShown }) => {
|
160
|
+
var _a;
|
161
|
+
// If the element is current hidden, we do not need to apply validation
|
162
|
+
const formElementConditionallyShown = formElementsConditionallyShown === null || formElementsConditionallyShown === void 0 ? void 0 : formElementsConditionallyShown[formElement.name];
|
163
|
+
if (formElementConditionallyShown === null || formElementConditionallyShown === void 0 ? void 0 : formElementConditionallyShown.isHidden) {
|
164
|
+
return;
|
431
165
|
}
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
maxErrorMessage = `Please enter a number between ${formElement.minNumber} and ${formElement.maxNumber}`;
|
166
|
+
switch (formElement.type) {
|
167
|
+
case 'draw': {
|
168
|
+
return {
|
169
|
+
attachment: true,
|
170
|
+
presence: presence(formElement, 'A saved signature is required'),
|
171
|
+
};
|
439
172
|
}
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
break;
|
458
|
-
}
|
459
|
-
case 'files': {
|
460
|
-
partialSchema[escapeElementName(formElement.name)] = {
|
461
|
-
presence: formElement.minEntries
|
462
|
-
? {
|
463
|
-
message: `Please upload at least ${formElement.minEntries} file${formElement.minEntries === 1 ? '' : 's'}`,
|
464
|
-
}
|
465
|
-
: false,
|
466
|
-
length: {
|
467
|
-
minimum: formElement.minEntries,
|
468
|
-
maximum: formElement.maxEntries,
|
469
|
-
tooLong: 'Cannot upload more than %{count} file(s)',
|
470
|
-
tooShort: 'Please upload at least %{count} file(s)',
|
471
|
-
},
|
472
|
-
type: {
|
473
|
-
type: (files) => {
|
474
|
-
return (!Array.isArray(files) ||
|
475
|
-
files.every((file) => {
|
476
|
-
return checkFileNameIsValid(formElement, file.fileName);
|
477
|
-
}));
|
173
|
+
case 'camera': {
|
174
|
+
return {
|
175
|
+
attachment: true,
|
176
|
+
presence: presence(formElement, 'A photo is required'),
|
177
|
+
};
|
178
|
+
}
|
179
|
+
case 'captcha': {
|
180
|
+
return {
|
181
|
+
presence: presence({ ...formElement, required: true }, 'Please complete the CAPTCHA successfully'),
|
182
|
+
};
|
183
|
+
}
|
184
|
+
case 'location': {
|
185
|
+
return {
|
186
|
+
presence: presence(formElement, 'Please select a location'),
|
187
|
+
lookups: {
|
188
|
+
formElement,
|
189
|
+
elementIdsWithLookupsExecuted,
|
478
190
|
},
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
191
|
+
};
|
192
|
+
}
|
193
|
+
case 'compliance': {
|
194
|
+
return {
|
195
|
+
presence: presence(formElement, 'Required'),
|
196
|
+
lookups: {
|
197
|
+
formElement,
|
198
|
+
elementIdsWithLookupsExecuted,
|
199
|
+
},
|
200
|
+
attachments: true,
|
201
|
+
};
|
202
|
+
}
|
203
|
+
case 'checkboxes': {
|
204
|
+
const requiredAllDefaultMessage = 'All options are required';
|
205
|
+
return {
|
206
|
+
presence: presence({
|
207
|
+
...formElement,
|
208
|
+
required: formElement.required || !!formElement.requiredAll,
|
209
|
+
}, formElement.requiredAll ? requiredAllDefaultMessage : 'Required'),
|
210
|
+
length: formElement.requiredAll
|
211
|
+
? {
|
212
|
+
is: (_a = formElement.options) === null || _a === void 0 ? void 0 : _a.length,
|
213
|
+
message: formElement.requiredMessage || requiredAllDefaultMessage,
|
214
|
+
}
|
215
|
+
: undefined,
|
216
|
+
lookups: {
|
217
|
+
formElement,
|
218
|
+
elementIdsWithLookupsExecuted,
|
219
|
+
},
|
220
|
+
};
|
221
|
+
}
|
222
|
+
case 'abn':
|
223
|
+
case 'geoscapeAddress':
|
224
|
+
case 'pointAddress':
|
225
|
+
case 'civicaStreetName':
|
226
|
+
case 'autocomplete':
|
227
|
+
case 'radio':
|
228
|
+
case 'select': {
|
229
|
+
return {
|
230
|
+
presence: presence(formElement, 'Required'),
|
231
|
+
lookups: {
|
232
|
+
formElement,
|
233
|
+
elementIdsWithLookupsExecuted,
|
234
|
+
},
|
235
|
+
};
|
236
|
+
}
|
237
|
+
case 'boolean': {
|
238
|
+
return {
|
239
|
+
isTrue: formElement.required && 'Required',
|
240
|
+
lookups: {
|
241
|
+
formElement,
|
242
|
+
elementIdsWithLookupsExecuted,
|
243
|
+
},
|
244
|
+
};
|
245
|
+
}
|
246
|
+
case 'bsb': {
|
247
|
+
return {
|
248
|
+
presence: presence(formElement, 'Please enter a BSB number'),
|
249
|
+
lookups: {
|
250
|
+
formElement,
|
251
|
+
elementIdsWithLookupsExecuted,
|
252
|
+
},
|
253
|
+
format: {
|
254
|
+
pattern: /\d{3}-\d{3}/,
|
255
|
+
message: 'Please enter a valid BSB number',
|
256
|
+
},
|
257
|
+
};
|
258
|
+
}
|
259
|
+
case 'barcodeScanner': {
|
260
|
+
return {
|
261
|
+
presence: presence(formElement, 'Please scan a barcode or enter a value'),
|
262
|
+
lookups: {
|
263
|
+
formElement,
|
264
|
+
elementIdsWithLookupsExecuted,
|
265
|
+
},
|
266
|
+
format: getCustomRegexFormatConfig(formElement),
|
267
|
+
};
|
268
|
+
}
|
269
|
+
case 'text':
|
270
|
+
case 'textarea': {
|
271
|
+
return {
|
272
|
+
presence: presence(formElement, 'Please enter a value'),
|
273
|
+
lookups: {
|
274
|
+
formElement,
|
275
|
+
elementIdsWithLookupsExecuted,
|
276
|
+
},
|
277
|
+
length: {
|
278
|
+
minimum: formElement.minLength,
|
279
|
+
tooShort: 'Please enter a value with at least %{count} character(s)',
|
280
|
+
maximum: formElement.maxLength,
|
281
|
+
tooLong: 'Please enter a value with %{count} character(s) or less',
|
282
|
+
},
|
283
|
+
format: getCustomRegexFormatConfig(formElement),
|
284
|
+
};
|
285
|
+
}
|
286
|
+
case 'telephone': {
|
287
|
+
return {
|
288
|
+
presence: presence(formElement, 'Please enter a phone number'),
|
289
|
+
lookups: {
|
290
|
+
formElement,
|
291
|
+
elementIdsWithLookupsExecuted,
|
292
|
+
},
|
293
|
+
format: getCustomRegexFormatConfig(formElement),
|
294
|
+
};
|
295
|
+
}
|
296
|
+
case 'email': {
|
297
|
+
return {
|
298
|
+
presence: presence(formElement, 'Please enter an email address'),
|
299
|
+
email: {
|
300
|
+
message: 'Please enter a valid email for this field',
|
301
|
+
},
|
302
|
+
lookups: {
|
303
|
+
formElement,
|
304
|
+
elementIdsWithLookupsExecuted,
|
305
|
+
},
|
306
|
+
format: getCustomRegexFormatConfig(formElement),
|
307
|
+
};
|
308
|
+
}
|
309
|
+
case 'time': {
|
310
|
+
return {
|
311
|
+
presence: presence(formElement, 'Please select a time'),
|
312
|
+
lookups: {
|
313
|
+
formElement,
|
314
|
+
elementIdsWithLookupsExecuted,
|
315
|
+
},
|
316
|
+
};
|
317
|
+
}
|
318
|
+
case 'date': {
|
319
|
+
const [fromDate, fromDateDaysOffset] = getCleanDateRangeConfiguration({
|
320
|
+
date: formElement.fromDate,
|
321
|
+
daysOffset: formElement.fromDateDaysOffset,
|
322
|
+
referenceFormElementId: formElement.fromDateElementId,
|
323
|
+
}, elements, submission, formElementsConditionallyShown);
|
324
|
+
const [toDate, toDateDaysOffset] = getCleanDateRangeConfiguration({
|
325
|
+
date: formElement.toDate,
|
326
|
+
daysOffset: formElement.toDateDaysOffset,
|
327
|
+
referenceFormElementId: formElement.toDateElementId,
|
328
|
+
}, elements, submission, formElementsConditionallyShown);
|
329
|
+
return {
|
330
|
+
presence: presence(formElement, 'Please select a date'),
|
331
|
+
date: {
|
332
|
+
format: (v) => localisationService.formatDate(v),
|
333
|
+
earliest: parseDateValue({
|
334
|
+
dateOnly: true,
|
335
|
+
daysOffset: fromDateDaysOffset,
|
336
|
+
value: fromDate,
|
337
|
+
}),
|
338
|
+
latest: parseDateValue({
|
339
|
+
dateOnly: true,
|
340
|
+
daysOffset: toDateDaysOffset,
|
341
|
+
value: toDate,
|
342
|
+
}),
|
343
|
+
notValid: 'Please select a valid date',
|
344
|
+
tooEarly: 'Date cannot be before %{date}',
|
345
|
+
tooLate: 'Date cannot be after %{date}',
|
346
|
+
},
|
347
|
+
lookups: {
|
348
|
+
formElement,
|
349
|
+
elementIdsWithLookupsExecuted,
|
350
|
+
},
|
351
|
+
};
|
352
|
+
}
|
353
|
+
case 'datetime': {
|
354
|
+
const [fromDate, fromDateDaysOffset] = getCleanDateRangeConfiguration({
|
355
|
+
date: formElement.fromDate,
|
356
|
+
daysOffset: formElement.fromDateDaysOffset,
|
357
|
+
referenceFormElementId: formElement.fromDateElementId,
|
358
|
+
}, elements, submission, formElementsConditionallyShown);
|
359
|
+
const [toDate, toDateDaysOffset] = getCleanDateRangeConfiguration({
|
360
|
+
date: formElement.toDate,
|
361
|
+
daysOffset: formElement.toDateDaysOffset,
|
362
|
+
referenceFormElementId: formElement.toDateElementId,
|
363
|
+
}, elements, submission, formElementsConditionallyShown);
|
364
|
+
return {
|
365
|
+
presence: presence(formElement, 'Please select a date and time'),
|
366
|
+
datetime: {
|
367
|
+
format: (v) => localisationService.formatDatetime(v),
|
368
|
+
earliest: parseDateValue({
|
369
|
+
dateOnly: true,
|
370
|
+
daysOffset: fromDateDaysOffset,
|
371
|
+
value: fromDate,
|
372
|
+
}),
|
373
|
+
latest: parseDateValue({
|
374
|
+
dateOnly: true,
|
375
|
+
daysOffset: toDateDaysOffset,
|
376
|
+
value: toDate,
|
377
|
+
}),
|
378
|
+
notValid: 'Please select a valid date and time',
|
379
|
+
tooEarly: 'Date and time cannot be before %{date}',
|
380
|
+
tooLate: 'Date and time cannot be after %{date}',
|
381
|
+
},
|
382
|
+
lookups: {
|
383
|
+
formElement,
|
384
|
+
elementIdsWithLookupsExecuted,
|
385
|
+
},
|
386
|
+
};
|
387
|
+
}
|
388
|
+
case 'number': {
|
389
|
+
let minErrorMessage = 'Please enter a number greater than or equal to %{count}';
|
390
|
+
let maxErrorMessage = 'Please enter a number less than or equal to %{count}';
|
391
|
+
if (typeof formElement.minNumber === 'number' &&
|
392
|
+
typeof formElement.maxNumber === 'number') {
|
393
|
+
minErrorMessage =
|
394
|
+
maxErrorMessage = `Please enter a number between ${formElement.minNumber} and ${formElement.maxNumber}`;
|
395
|
+
}
|
396
|
+
return {
|
397
|
+
type: 'number',
|
398
|
+
presence: presence(formElement, 'Please enter a number'),
|
399
|
+
numericality: {
|
400
|
+
greaterThanOrEqualTo: formElement.minNumber,
|
401
|
+
notGreaterThanOrEqualTo: minErrorMessage,
|
402
|
+
lessThanOrEqualTo: formElement.maxNumber,
|
403
|
+
notLessThanOrEqualTo: maxErrorMessage,
|
404
|
+
onlyInteger: formElement.isInteger,
|
405
|
+
notInteger: 'Please enter a whole number',
|
406
|
+
},
|
407
|
+
lookups: {
|
408
|
+
formElement,
|
409
|
+
elementIdsWithLookupsExecuted,
|
410
|
+
},
|
411
|
+
numberRegex: getCustomRegexFormatConfig(formElement),
|
412
|
+
};
|
413
|
+
}
|
414
|
+
case 'files': {
|
415
|
+
return {
|
416
|
+
presence: formElement.minEntries
|
417
|
+
? {
|
418
|
+
message: `Please upload at least ${formElement.minEntries} file${formElement.minEntries === 1 ? '' : 's'}`,
|
419
|
+
}
|
420
|
+
: false,
|
421
|
+
length: {
|
422
|
+
minimum: formElement.minEntries,
|
423
|
+
maximum: formElement.maxEntries,
|
424
|
+
tooLong: 'Cannot upload more than %{count} file(s)',
|
425
|
+
tooShort: 'Please upload at least %{count} file(s)',
|
426
|
+
},
|
427
|
+
type: {
|
428
|
+
type: (files) => {
|
429
|
+
return (!Array.isArray(files) ||
|
430
|
+
files.every((file) => {
|
431
|
+
return checkFileNameIsValid(formElement, file.fileName);
|
432
|
+
}));
|
500
433
|
},
|
434
|
+
message: `Only the following file types are accepted: ${(formElement.restrictedFileTypes || []).join(', ')}`,
|
501
435
|
},
|
502
|
-
|
503
|
-
|
504
|
-
};
|
505
|
-
break;
|
506
|
-
}
|
507
|
-
case 'civicaNameRecord': {
|
508
|
-
const nestedElements = generateCivicaNameRecordElements(formElement, []);
|
509
|
-
partialSchema[escapeElementName(formElement.name)] = {
|
510
|
-
nestedElements: generateValidationSchema(nestedElements, elementIdsWithLookupsExecuted),
|
511
|
-
};
|
512
|
-
break;
|
513
|
-
}
|
514
|
-
case 'form': {
|
515
|
-
if (formElement.elements) {
|
516
|
-
partialSchema[escapeElementName(formElement.name)] = {
|
517
|
-
nestedElements: generateValidationSchema(formElement.elements, elementIdsWithLookupsExecuted),
|
436
|
+
needsExtension: formElement,
|
437
|
+
attachments: true,
|
518
438
|
};
|
519
439
|
}
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
440
|
+
case 'repeatableSet': {
|
441
|
+
return {
|
442
|
+
entries: {
|
443
|
+
setSchema: {
|
444
|
+
presence: formElement.minSetEntries
|
445
|
+
? {
|
446
|
+
message: `Must have at least ${formElement.minSetEntries} ${formElement.minSetEntries === 1 ? 'entry' : 'entries'}`,
|
447
|
+
}
|
448
|
+
: false,
|
449
|
+
length: {
|
450
|
+
minimum: formElement.minSetEntries,
|
451
|
+
maximum: formElement.maxSetEntries,
|
452
|
+
tooLong: 'Cannot have more than %{count} entry/entries',
|
453
|
+
tooShort: 'Must have at least %{count} entry/entries',
|
454
|
+
},
|
455
|
+
},
|
456
|
+
entrySchema: {
|
457
|
+
schema: generateValidationSchema(formElement.elements, elementIdsWithLookupsExecuted),
|
458
|
+
formElementConditionallyShown: formElementsConditionallyShown === null || formElementsConditionallyShown === void 0 ? void 0 : formElementsConditionallyShown[formElement.name],
|
459
|
+
},
|
460
|
+
},
|
461
|
+
};
|
462
|
+
}
|
463
|
+
case 'civicaNameRecord': {
|
464
|
+
const nestedElements = generateCivicaNameRecordElements(formElement, []);
|
465
|
+
return {
|
466
|
+
nestedElements: {
|
467
|
+
schema: generateValidationSchema(nestedElements, elementIdsWithLookupsExecuted),
|
468
|
+
formElementConditionallyShown: formElementsConditionallyShown === null || formElementsConditionallyShown === void 0 ? void 0 : formElementsConditionallyShown[formElement.name],
|
469
|
+
},
|
470
|
+
};
|
471
|
+
}
|
472
|
+
case 'form': {
|
473
|
+
if (formElement.elements) {
|
474
|
+
return {
|
475
|
+
nestedElements: {
|
476
|
+
schema: generateValidationSchema(formElement.elements, elementIdsWithLookupsExecuted),
|
477
|
+
formElementConditionallyShown: formElementsConditionallyShown === null || formElementsConditionallyShown === void 0 ? void 0 : formElementsConditionallyShown[formElement.name],
|
478
|
+
},
|
479
|
+
};
|
480
|
+
}
|
481
|
+
break;
|
482
|
+
}
|
483
|
+
case 'freshdeskDependentField': {
|
484
|
+
const nestedElements = generateFreshdeskDependentFieldElements(formElement, undefined);
|
485
|
+
return {
|
486
|
+
nestedElements: {
|
487
|
+
schema: generateValidationSchema(nestedElements, elementIdsWithLookupsExecuted),
|
488
|
+
formElementConditionallyShown: formElementsConditionallyShown === null || formElementsConditionallyShown === void 0 ? void 0 : formElementsConditionallyShown[formElement.name],
|
489
|
+
},
|
490
|
+
};
|
491
|
+
}
|
492
|
+
default: {
|
493
|
+
console.info('Unsupported form element with validation', formElement);
|
494
|
+
}
|
531
495
|
}
|
532
|
-
}
|
496
|
+
};
|
497
|
+
partialSchema[escapeElementName(formElement.name)] = constraint;
|
533
498
|
return partialSchema;
|
534
499
|
}, {});
|
535
500
|
}
|
536
|
-
|
501
|
+
export function validateSubmission(schema, submission, formElementsConditionallyShown) {
|
537
502
|
const errorsAsArray = validate(submission, schema, {
|
538
503
|
format: 'grouped',
|
539
504
|
fullMessages: false,
|
505
|
+
formElementsConditionallyShown,
|
540
506
|
});
|
541
507
|
if (!errorsAsArray || validate.isEmpty(errorsAsArray)) {
|
542
508
|
return;
|
@@ -552,7 +518,7 @@ const validateSingleMessageError = (submission, schema) => {
|
|
552
518
|
return;
|
553
519
|
}
|
554
520
|
return errors;
|
555
|
-
}
|
521
|
+
}
|
556
522
|
export function checkFileNameIsValid(formElement, fileName) {
|
557
523
|
const extension = fileName.split('.').pop();
|
558
524
|
return (!formElement.restrictedFileTypes ||
|