@defra/forms-engine-plugin 4.0.11 → 4.0.13
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/forms/register-as-a-unicorn-breeder.yaml +2 -2
- package/.server/server/plugins/engine/components/EastingNorthingField.js +4 -24
- package/.server/server/plugins/engine/components/EastingNorthingField.js.map +1 -1
- package/.server/server/plugins/engine/components/LatLongField.js +8 -31
- package/.server/server/plugins/engine/components/LatLongField.js.map +1 -1
- package/.server/server/plugins/engine/components/LocationFieldBase.d.ts +1 -3
- package/.server/server/plugins/engine/components/LocationFieldBase.js +1 -8
- package/.server/server/plugins/engine/components/LocationFieldBase.js.map +1 -1
- package/.server/server/plugins/engine/components/NationalGridFieldNumberField.d.ts +0 -2
- package/.server/server/plugins/engine/components/NationalGridFieldNumberField.js +7 -18
- package/.server/server/plugins/engine/components/NationalGridFieldNumberField.js.map +1 -1
- package/.server/server/plugins/engine/components/NumberField.d.ts +0 -18
- package/.server/server/plugins/engine/components/NumberField.js +2 -103
- package/.server/server/plugins/engine/components/NumberField.js.map +1 -1
- package/.server/server/plugins/engine/components/OsGridRefField.d.ts +0 -2
- package/.server/server/plugins/engine/components/OsGridRefField.js +4 -32
- package/.server/server/plugins/engine/components/OsGridRefField.js.map +1 -1
- package/.server/server/plugins/engine/models/FormModel.d.ts +2 -2
- package/.server/server/plugins/engine/models/FormModel.js +1 -1
- package/.server/server/plugins/engine/models/FormModel.js.map +1 -1
- package/.server/server/plugins/engine/models/types.d.ts +1 -1
- package/.server/server/plugins/engine/models/types.js.map +1 -1
- package/package.json +2 -2
- package/src/server/forms/register-as-a-unicorn-breeder.yaml +2 -2
- package/src/server/plugins/engine/components/EastingNorthingField.test.ts +0 -14
- package/src/server/plugins/engine/components/EastingNorthingField.ts +4 -24
- package/src/server/plugins/engine/components/LatLongField.test.ts +4 -24
- package/src/server/plugins/engine/components/LatLongField.ts +8 -33
- package/src/server/plugins/engine/components/LocationFieldBase.ts +1 -15
- package/src/server/plugins/engine/components/NationalGridFieldNumberField.test.ts +22 -8
- package/src/server/plugins/engine/components/NationalGridFieldNumberField.ts +9 -20
- package/src/server/plugins/engine/components/NumberField.test.ts +12 -504
- package/src/server/plugins/engine/components/NumberField.ts +4 -133
- package/src/server/plugins/engine/components/OsGridRefField.test.ts +11 -33
- package/src/server/plugins/engine/components/OsGridRefField.ts +5 -38
- package/src/server/plugins/engine/models/FormModel.ts +1 -1
- package/src/server/plugins/engine/models/types.ts +1 -1
|
@@ -26,19 +26,6 @@ import { convertToLanguageMessages } from '~/src/server/utils/type-utils.js'
|
|
|
26
26
|
// Precision constants
|
|
27
27
|
// UK latitude/longitude requires high precision for accurate location (within ~11mm)
|
|
28
28
|
const DECIMAL_PRECISION = 7 // 7 decimal places
|
|
29
|
-
const MIN_DECIMAL_PLACES = 1 // At least 1 decimal place required
|
|
30
|
-
|
|
31
|
-
// Latitude length constraints
|
|
32
|
-
// Min: 3 chars for values like "52.1" (2 digits + decimal + 1 decimal place)
|
|
33
|
-
// Max: 10 chars for values like "59.1234567" (2 digits + decimal + 7 decimal places)
|
|
34
|
-
const LATITUDE_MIN_LENGTH = 3
|
|
35
|
-
const LATITUDE_MAX_LENGTH = 10
|
|
36
|
-
|
|
37
|
-
// Longitude length constraints
|
|
38
|
-
// Min: 2 chars for values like "-1" or single digit with decimal (needs min decimal places)
|
|
39
|
-
// Max: 10 chars for values like "-1.1234567" (minus + 1 digit + decimal + 7 decimal places)
|
|
40
|
-
const LONGITUDE_MIN_LENGTH = 2
|
|
41
|
-
const LONGITUDE_MAX_LENGTH = 10
|
|
42
29
|
|
|
43
30
|
export class LatLongField extends FormComponent {
|
|
44
31
|
declare options: LatLongFieldComponent['options']
|
|
@@ -57,10 +44,10 @@ export class LatLongField extends FormComponent {
|
|
|
57
44
|
const isRequired = options.required !== false
|
|
58
45
|
|
|
59
46
|
// Read schema values from def.schema with fallback defaults
|
|
60
|
-
const latitudeMin = schema?.latitude?.min ?? 49
|
|
61
|
-
const latitudeMax = schema?.latitude?.max ?? 60
|
|
62
|
-
const longitudeMin = schema?.longitude?.min ?? -
|
|
63
|
-
const longitudeMax = schema?.longitude?.max ??
|
|
47
|
+
const latitudeMin = schema?.latitude?.min ?? 49.85
|
|
48
|
+
const latitudeMax = schema?.latitude?.max ?? 60.859
|
|
49
|
+
const longitudeMin = schema?.longitude?.min ?? -13.687
|
|
50
|
+
const longitudeMax = schema?.longitude?.max ?? 1.767
|
|
64
51
|
|
|
65
52
|
const customValidationMessages: LanguageMessages =
|
|
66
53
|
convertToLanguageMessages({
|
|
@@ -68,8 +55,6 @@ export class LatLongField extends FormComponent {
|
|
|
68
55
|
'number.base': messageTemplate.objectMissing,
|
|
69
56
|
'number.precision':
|
|
70
57
|
'{{#label}} must have no more than 7 decimal places',
|
|
71
|
-
'number.minPrecision':
|
|
72
|
-
'{{#label}} must have at least {{#minPrecision}} decimal place',
|
|
73
58
|
'number.unsafe': '{{#label}} must be a valid number'
|
|
74
59
|
})
|
|
75
60
|
|
|
@@ -77,18 +62,14 @@ export class LatLongField extends FormComponent {
|
|
|
77
62
|
...customValidationMessages,
|
|
78
63
|
'number.base': `Enter a valid latitude for ${this.title} like 51.519450`,
|
|
79
64
|
'number.min': `Latitude for ${this.title} must be between ${latitudeMin} and ${latitudeMax}`,
|
|
80
|
-
'number.max': `Latitude for ${this.title} must be between ${latitudeMin} and ${latitudeMax}
|
|
81
|
-
'number.minLength': `Latitude for ${this.title} must be between 3 and 10 characters`,
|
|
82
|
-
'number.maxLength': `Latitude for ${this.title} must be between 3 and 10 characters`
|
|
65
|
+
'number.max': `Latitude for ${this.title} must be between ${latitudeMin} and ${latitudeMax}`
|
|
83
66
|
})
|
|
84
67
|
|
|
85
68
|
const longitudeMessages: LanguageMessages = convertToLanguageMessages({
|
|
86
69
|
...customValidationMessages,
|
|
87
70
|
'number.base': `Enter a valid longitude for ${this.title} like -0.127758`,
|
|
88
71
|
'number.min': `Longitude for ${this.title} must be between ${longitudeMin} and ${longitudeMax}`,
|
|
89
|
-
'number.max': `Longitude for ${this.title} must be between ${longitudeMin} and ${longitudeMax}
|
|
90
|
-
'number.minLength': `Longitude for ${this.title} must be between 2 and 10 characters`,
|
|
91
|
-
'number.maxLength': `Longitude for ${this.title} must be between 2 and 10 characters`
|
|
72
|
+
'number.max': `Longitude for ${this.title} must be between ${longitudeMin} and ${longitudeMax}`
|
|
92
73
|
})
|
|
93
74
|
|
|
94
75
|
this.collection = new ComponentCollection(
|
|
@@ -100,10 +81,7 @@ export class LatLongField extends FormComponent {
|
|
|
100
81
|
schema: {
|
|
101
82
|
min: latitudeMin,
|
|
102
83
|
max: latitudeMax,
|
|
103
|
-
precision: DECIMAL_PRECISION
|
|
104
|
-
minPrecision: MIN_DECIMAL_PLACES,
|
|
105
|
-
minLength: LATITUDE_MIN_LENGTH,
|
|
106
|
-
maxLength: LATITUDE_MAX_LENGTH
|
|
84
|
+
precision: DECIMAL_PRECISION
|
|
107
85
|
},
|
|
108
86
|
options: {
|
|
109
87
|
required: isRequired,
|
|
@@ -120,10 +98,7 @@ export class LatLongField extends FormComponent {
|
|
|
120
98
|
schema: {
|
|
121
99
|
min: longitudeMin,
|
|
122
100
|
max: longitudeMax,
|
|
123
|
-
precision: DECIMAL_PRECISION
|
|
124
|
-
minPrecision: MIN_DECIMAL_PLACES,
|
|
125
|
-
minLength: LONGITUDE_MIN_LENGTH,
|
|
126
|
-
maxLength: LONGITUDE_MAX_LENGTH
|
|
101
|
+
precision: DECIMAL_PRECISION
|
|
127
102
|
},
|
|
128
103
|
options: {
|
|
129
104
|
required: isRequired,
|
|
@@ -28,11 +28,6 @@ interface LocationFieldOptions {
|
|
|
28
28
|
interface ValidationConfig {
|
|
29
29
|
pattern: RegExp
|
|
30
30
|
patternErrorMessage: string
|
|
31
|
-
customValidation?: (
|
|
32
|
-
value: string,
|
|
33
|
-
helpers: joi.CustomHelpers
|
|
34
|
-
) => string | joi.ErrorReport
|
|
35
|
-
additionalMessages?: LanguageMessages
|
|
36
31
|
}
|
|
37
32
|
|
|
38
33
|
/**
|
|
@@ -71,14 +66,9 @@ export abstract class LocationFieldBase extends FormComponent {
|
|
|
71
66
|
.required()
|
|
72
67
|
.pattern(config.pattern)
|
|
73
68
|
.messages({
|
|
74
|
-
'string.pattern.base': config.patternErrorMessage
|
|
75
|
-
...config.additionalMessages
|
|
69
|
+
'string.pattern.base': config.patternErrorMessage
|
|
76
70
|
})
|
|
77
71
|
|
|
78
|
-
if (config.customValidation) {
|
|
79
|
-
formSchema = formSchema.custom(config.customValidation)
|
|
80
|
-
}
|
|
81
|
-
|
|
82
72
|
if (locationOptions.required === false) {
|
|
83
73
|
formSchema = formSchema.allow('')
|
|
84
74
|
}
|
|
@@ -91,10 +81,6 @@ export abstract class LocationFieldBase extends FormComponent {
|
|
|
91
81
|
'string.pattern.base'
|
|
92
82
|
]
|
|
93
83
|
|
|
94
|
-
if (config.additionalMessages) {
|
|
95
|
-
messageKeys.push(...Object.keys(config.additionalMessages))
|
|
96
|
-
}
|
|
97
|
-
|
|
98
84
|
const messages = messageKeys.reduce<LanguageMessages>((acc, key) => {
|
|
99
85
|
acc[key] = message
|
|
100
86
|
return acc
|
|
@@ -99,13 +99,27 @@ describe('NationalGridFieldNumberField', () => {
|
|
|
99
99
|
})
|
|
100
100
|
|
|
101
101
|
it('accepts valid values', () => {
|
|
102
|
-
|
|
103
|
-
const
|
|
104
|
-
const
|
|
102
|
+
// Test 8-digit parcel ID format (2x4)
|
|
103
|
+
const result1 = collection.validate(getFormData('TQ12345678'))
|
|
104
|
+
const result2 = collection.validate(getFormData('TQ 1234 5678'))
|
|
105
|
+
|
|
106
|
+
// Test 10-digit OS grid reference format (2x5)
|
|
107
|
+
const result3 = collection.validate(getFormData('SU1234567890'))
|
|
108
|
+
const result4 = collection.validate(getFormData('SU 12345 67890'))
|
|
109
|
+
|
|
110
|
+
expect(result1.errors).toBeUndefined()
|
|
111
|
+
expect(result2.errors).toBeUndefined()
|
|
112
|
+
expect(result3.errors).toBeUndefined()
|
|
113
|
+
expect(result4.errors).toBeUndefined()
|
|
114
|
+
|
|
115
|
+
// Test case-insensitive
|
|
116
|
+
const result5 = collection.validate(getFormData('nt12345678'))
|
|
105
117
|
|
|
106
118
|
expect(result1.errors).toBeUndefined()
|
|
107
119
|
expect(result2.errors).toBeUndefined()
|
|
108
120
|
expect(result3.errors).toBeUndefined()
|
|
121
|
+
expect(result4.errors).toBeUndefined()
|
|
122
|
+
expect(result5.errors).toBeUndefined()
|
|
109
123
|
})
|
|
110
124
|
|
|
111
125
|
it('formats values with spaces per GDS guidance', () => {
|
|
@@ -114,8 +128,8 @@ describe('NationalGridFieldNumberField', () => {
|
|
|
114
128
|
const result3 = collection.validate(getFormData('NG12345,678'))
|
|
115
129
|
|
|
116
130
|
expect(result1.value.myComponent).toBe('NG 1234 5678')
|
|
117
|
-
expect(result2.value.myComponent).toBe('
|
|
118
|
-
expect(result3.value.myComponent).toBe('
|
|
131
|
+
expect(result2.value.myComponent).toBe('NG12345678')
|
|
132
|
+
expect(result3.value.myComponent).toBe('NG12345,678')
|
|
119
133
|
})
|
|
120
134
|
|
|
121
135
|
it('adds errors for empty value', () => {
|
|
@@ -258,15 +272,15 @@ describe('NationalGridFieldNumberField', () => {
|
|
|
258
272
|
assertions: [
|
|
259
273
|
{
|
|
260
274
|
input: getFormData(' NG12345678'),
|
|
261
|
-
output: { value: getFormData('
|
|
275
|
+
output: { value: getFormData('NG12345678') }
|
|
262
276
|
},
|
|
263
277
|
{
|
|
264
278
|
input: getFormData('NG12345678 '),
|
|
265
|
-
output: { value: getFormData('
|
|
279
|
+
output: { value: getFormData('NG12345678') }
|
|
266
280
|
},
|
|
267
281
|
{
|
|
268
282
|
input: getFormData(' NG12345678 \n\n'),
|
|
269
|
-
output: { value: getFormData('
|
|
283
|
+
output: { value: getFormData('NG12345678') }
|
|
270
284
|
}
|
|
271
285
|
]
|
|
272
286
|
},
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { type NationalGridFieldNumberFieldComponent } from '@defra/forms-model'
|
|
2
|
-
import type joi from 'joi'
|
|
3
2
|
|
|
4
3
|
import { LocationFieldBase } from '~/src/server/plugins/engine/components/LocationFieldBase.js'
|
|
5
4
|
|
|
@@ -7,26 +6,16 @@ export class NationalGridFieldNumberField extends LocationFieldBase {
|
|
|
7
6
|
declare options: NationalGridFieldNumberFieldComponent['options']
|
|
8
7
|
|
|
9
8
|
protected getValidationConfig() {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const cleanValue = value.replace(/[\s,]/g, '')
|
|
17
|
-
|
|
18
|
-
// Check if it matches the exact pattern after cleaning
|
|
19
|
-
if (!/^[A-Z]{2}\d{8}$/i.test(cleanValue)) {
|
|
20
|
-
return helpers.error('string.pattern.base')
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Format with spaces per GDS guidance: NG 1234 5678
|
|
24
|
-
const letters = cleanValue.substring(0, 2)
|
|
25
|
-
const numbers = cleanValue.substring(2)
|
|
26
|
-
const formattedValue = `${letters} ${numbers.substring(0, 4)} ${numbers.substring(4)}`
|
|
9
|
+
// Regex for OS grid references and parcel IDs
|
|
10
|
+
// Validates specific valid OS grid letter combinations with:
|
|
11
|
+
// - 2 letters & 8 digits in 2 blocks of 4 (parcel ID) e.g., ST 6789 6789
|
|
12
|
+
// - 2 letters & 10 digits in 2 blocks of 5 (OS grid reference) e.g., SO 12345 12345
|
|
13
|
+
const pattern =
|
|
14
|
+
/^((([sS]|[nN])[a-hA-Hj-zJ-Z])|(([tT]|[oO])[abfglmqrvwABFGLMQRVW])|([hH][l-zL-Z])|([jJ][lmqrvwLMQRVW]))\s?(([0-9]{4})\s?([0-9]{4})|([0-9]{5})\s?([0-9]{5}))$/
|
|
27
15
|
|
|
28
|
-
|
|
29
|
-
|
|
16
|
+
return {
|
|
17
|
+
pattern,
|
|
18
|
+
patternErrorMessage: `Enter a valid National Grid field number for ${this.title} like NG 1234 5678`
|
|
30
19
|
}
|
|
31
20
|
}
|
|
32
21
|
|