@defra/forms-engine-plugin 4.0.9 → 4.0.10
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/.server/server/plugins/engine/components/EastingNorthingField.js +30 -10
- package/.server/server/plugins/engine/components/EastingNorthingField.js.map +1 -1
- package/.server/server/plugins/engine/components/LatLongField.js +32 -4
- package/.server/server/plugins/engine/components/LatLongField.js.map +1 -1
- package/.server/server/plugins/engine/components/NumberField.d.ts +18 -0
- package/.server/server/plugins/engine/components/NumberField.js +103 -2
- package/.server/server/plugins/engine/components/NumberField.js.map +1 -1
- package/package.json +2 -2
- package/src/server/plugins/engine/components/EastingNorthingField.test.ts +15 -1
- package/src/server/plugins/engine/components/EastingNorthingField.ts +38 -10
- package/src/server/plugins/engine/components/LatLongField.test.ts +173 -16
- package/src/server/plugins/engine/components/LatLongField.ts +41 -4
- package/src/server/plugins/engine/components/NumberField.test.ts +504 -12
- package/src/server/plugins/engine/components/NumberField.ts +133 -4
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { type NumberFieldComponent } from '@defra/forms-model'
|
|
2
|
-
import joi, {
|
|
2
|
+
import joi, {
|
|
3
|
+
type CustomHelpers,
|
|
4
|
+
type CustomValidator,
|
|
5
|
+
type NumberSchema
|
|
6
|
+
} from 'joi'
|
|
3
7
|
|
|
4
8
|
import {
|
|
5
9
|
FormComponent,
|
|
@@ -161,29 +165,154 @@ export class NumberField extends FormComponent {
|
|
|
161
165
|
}
|
|
162
166
|
}
|
|
163
167
|
|
|
168
|
+
/**
|
|
169
|
+
* Validates string length of a numeric value
|
|
170
|
+
* @param value - The numeric value to validate
|
|
171
|
+
* @param minLength - Minimum required string length
|
|
172
|
+
* @param maxLength - Maximum allowed string length
|
|
173
|
+
* @returns Object with validation result
|
|
174
|
+
*/
|
|
175
|
+
export function validateStringLength(
|
|
176
|
+
value: number,
|
|
177
|
+
minLength?: number,
|
|
178
|
+
maxLength?: number
|
|
179
|
+
): { isValid: boolean; error?: 'minLength' | 'maxLength' } {
|
|
180
|
+
if (typeof minLength !== 'number' && typeof maxLength !== 'number') {
|
|
181
|
+
return { isValid: true }
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const valueStr = String(value)
|
|
185
|
+
|
|
186
|
+
if (typeof minLength === 'number' && valueStr.length < minLength) {
|
|
187
|
+
return { isValid: false, error: 'minLength' }
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (typeof maxLength === 'number' && valueStr.length > maxLength) {
|
|
191
|
+
return { isValid: false, error: 'maxLength' }
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return { isValid: true }
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Validates minimum decimal precision
|
|
199
|
+
* @param value - The numeric value to validate
|
|
200
|
+
* @param minPrecision - Minimum required decimal places
|
|
201
|
+
* @returns true if valid, false if invalid
|
|
202
|
+
*/
|
|
203
|
+
export function validateMinimumPrecision(
|
|
204
|
+
value: number,
|
|
205
|
+
minPrecision: number
|
|
206
|
+
): boolean {
|
|
207
|
+
if (Number.isInteger(value)) {
|
|
208
|
+
return false
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const valueStr = String(value)
|
|
212
|
+
const decimalIndex = valueStr.indexOf('.')
|
|
213
|
+
|
|
214
|
+
if (decimalIndex !== -1) {
|
|
215
|
+
const decimalPlaces = valueStr.length - decimalIndex - 1
|
|
216
|
+
return decimalPlaces >= minPrecision
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return false
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Helper function to handle length validation errors
|
|
224
|
+
* Returns the appropriate error response based on the validation result
|
|
225
|
+
*/
|
|
226
|
+
function handleLengthValidationError(
|
|
227
|
+
lengthCheck: ReturnType<typeof validateStringLength>,
|
|
228
|
+
helpers: CustomHelpers,
|
|
229
|
+
custom: string | undefined,
|
|
230
|
+
minLength: number | undefined,
|
|
231
|
+
maxLength: number | undefined
|
|
232
|
+
) {
|
|
233
|
+
if (!lengthCheck.isValid && lengthCheck.error) {
|
|
234
|
+
const errorType = `number.${lengthCheck.error}`
|
|
235
|
+
|
|
236
|
+
if (custom) {
|
|
237
|
+
// Only pass the relevant length value in context
|
|
238
|
+
const contextData =
|
|
239
|
+
lengthCheck.error === 'minLength'
|
|
240
|
+
? { minLength: minLength ?? 0 }
|
|
241
|
+
: { maxLength: maxLength ?? 0 }
|
|
242
|
+
return helpers.message({ custom }, contextData)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const context =
|
|
246
|
+
lengthCheck.error === 'minLength'
|
|
247
|
+
? { minLength: minLength ?? 0 }
|
|
248
|
+
: { maxLength: maxLength ?? 0 }
|
|
249
|
+
return helpers.error(errorType, context)
|
|
250
|
+
}
|
|
251
|
+
return null
|
|
252
|
+
}
|
|
253
|
+
|
|
164
254
|
export function getValidatorPrecision(component: NumberField) {
|
|
165
255
|
const validator: CustomValidator = (value: number, helpers) => {
|
|
166
256
|
const { options, schema } = component
|
|
167
|
-
|
|
168
257
|
const { customValidationMessage: custom } = options
|
|
169
|
-
const {
|
|
258
|
+
const {
|
|
259
|
+
precision: limit,
|
|
260
|
+
minPrecision,
|
|
261
|
+
minLength,
|
|
262
|
+
maxLength
|
|
263
|
+
} = schema as {
|
|
264
|
+
precision?: number
|
|
265
|
+
minPrecision?: number
|
|
266
|
+
minLength?: number
|
|
267
|
+
maxLength?: number
|
|
268
|
+
}
|
|
170
269
|
|
|
171
270
|
if (!limit || limit <= 0) {
|
|
271
|
+
const lengthCheck = validateStringLength(value, minLength, maxLength)
|
|
272
|
+
const error = handleLengthValidationError(
|
|
273
|
+
lengthCheck,
|
|
274
|
+
helpers,
|
|
275
|
+
custom,
|
|
276
|
+
minLength,
|
|
277
|
+
maxLength
|
|
278
|
+
)
|
|
279
|
+
if (error) return error
|
|
172
280
|
return value
|
|
173
281
|
}
|
|
174
282
|
|
|
283
|
+
// Validate precision (max decimal places)
|
|
175
284
|
const validationSchema = joi
|
|
176
285
|
.number()
|
|
177
286
|
.precision(limit)
|
|
178
287
|
.prefs({ convert: false })
|
|
179
288
|
|
|
180
289
|
try {
|
|
181
|
-
|
|
290
|
+
joi.attempt(value, validationSchema)
|
|
182
291
|
} catch {
|
|
183
292
|
return custom
|
|
184
293
|
? helpers.message({ custom }, { limit })
|
|
185
294
|
: helpers.error('number.precision', { limit })
|
|
186
295
|
}
|
|
296
|
+
|
|
297
|
+
// Validate minimum precision (min decimal places)
|
|
298
|
+
if (typeof minPrecision === 'number' && minPrecision > 0) {
|
|
299
|
+
if (!validateMinimumPrecision(value, minPrecision)) {
|
|
300
|
+
return helpers.error('number.minPrecision', { minPrecision })
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Check string length validation after precision checks
|
|
305
|
+
const lengthCheck = validateStringLength(value, minLength, maxLength)
|
|
306
|
+
const error = handleLengthValidationError(
|
|
307
|
+
lengthCheck,
|
|
308
|
+
helpers,
|
|
309
|
+
custom,
|
|
310
|
+
minLength,
|
|
311
|
+
maxLength
|
|
312
|
+
)
|
|
313
|
+
if (error) return error
|
|
314
|
+
|
|
315
|
+
return value
|
|
187
316
|
}
|
|
188
317
|
|
|
189
318
|
return validator
|