@defra/forms-engine-plugin 4.0.5 → 4.0.7
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/.public/stylesheets/application.min.css +2 -2
- package/.public/stylesheets/application.min.css.map +1 -1
- package/.server/client/stylesheets/_location-input.scss +60 -0
- package/.server/client/stylesheets/application.scss +1 -6
- package/.server/client/stylesheets/shared.scss +8 -0
- package/.server/server/forms/register-as-a-unicorn-breeder.yaml +28 -0
- package/.server/server/plugins/engine/components/ComponentBase.d.ts +1 -1
- package/.server/server/plugins/engine/components/ComponentBase.js.map +1 -1
- package/.server/server/plugins/engine/components/EastingNorthingField.d.ts +121 -0
- package/.server/server/plugins/engine/components/EastingNorthingField.js +166 -0
- package/.server/server/plugins/engine/components/EastingNorthingField.js.map +1 -0
- package/.server/server/plugins/engine/components/LatLongField.d.ts +121 -0
- package/.server/server/plugins/engine/components/LatLongField.js +164 -0
- package/.server/server/plugins/engine/components/LatLongField.js.map +1 -0
- package/.server/server/plugins/engine/components/LocationFieldBase.d.ts +134 -0
- package/.server/server/plugins/engine/components/LocationFieldBase.js +85 -0
- package/.server/server/plugins/engine/components/LocationFieldBase.js.map +1 -0
- package/.server/server/plugins/engine/components/LocationFieldHelpers.d.ts +108 -0
- package/.server/server/plugins/engine/components/LocationFieldHelpers.js +96 -0
- package/.server/server/plugins/engine/components/LocationFieldHelpers.js.map +1 -0
- package/.server/server/plugins/engine/components/NationalGridFieldNumberField.d.ts +19 -0
- package/.server/server/plugins/engine/components/NationalGridFieldNumberField.js +40 -0
- package/.server/server/plugins/engine/components/NationalGridFieldNumberField.js.map +1 -0
- package/.server/server/plugins/engine/components/OsGridRefField.d.ts +19 -0
- package/.server/server/plugins/engine/components/OsGridRefField.js +56 -0
- package/.server/server/plugins/engine/components/OsGridRefField.js.map +1 -0
- package/.server/server/plugins/engine/components/helpers/components.d.ts +3 -4
- package/.server/server/plugins/engine/components/helpers/components.js +21 -29
- package/.server/server/plugins/engine/components/helpers/components.js.map +1 -1
- package/.server/server/plugins/engine/components/index.d.ts +4 -0
- package/.server/server/plugins/engine/components/index.js +4 -0
- package/.server/server/plugins/engine/components/index.js.map +1 -1
- package/.server/server/plugins/engine/components/markdownParser.d.ts +2 -0
- package/.server/server/plugins/engine/components/markdownParser.js +28 -0
- package/.server/server/plugins/engine/components/markdownParser.js.map +1 -0
- package/.server/server/plugins/engine/components/types.d.ts +10 -0
- package/.server/server/plugins/engine/components/types.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/helpers/pages.js +7 -0
- package/.server/server/plugins/engine/pageControllers/helpers/pages.js.map +1 -1
- package/.server/server/plugins/engine/types/index.d.ts +1 -1
- package/.server/server/plugins/engine/types/index.js.map +1 -1
- package/.server/server/plugins/engine/types.d.ts +2 -2
- package/.server/server/plugins/engine/types.js.map +1 -1
- package/.server/server/plugins/engine/views/components/_location-field-base.html +53 -0
- package/.server/server/plugins/engine/views/components/eastingnorthingfield.html +5 -0
- package/.server/server/plugins/engine/views/components/latlongfield.html +5 -0
- package/.server/server/plugins/engine/views/components/nationalgridfieldnumberfield.html +13 -0
- package/.server/server/plugins/engine/views/components/osgridreffield.html +13 -0
- package/.server/server/plugins/nunjucks/filters/field.d.ts +1 -1
- package/package.json +3 -3
- package/src/client/stylesheets/_location-input.scss +60 -0
- package/src/client/stylesheets/application.scss +1 -6
- package/src/client/stylesheets/shared.scss +8 -0
- package/src/server/forms/register-as-a-unicorn-breeder.yaml +28 -0
- package/src/server/plugins/engine/components/ComponentBase.ts +1 -1
- package/src/server/plugins/engine/components/EastingNorthingField.test.ts +665 -0
- package/src/server/plugins/engine/components/EastingNorthingField.ts +224 -0
- package/src/server/plugins/engine/components/LatLongField.test.ts +700 -0
- package/src/server/plugins/engine/components/LatLongField.ts +213 -0
- package/src/server/plugins/engine/components/LocationFieldBase.test.ts +253 -0
- package/src/server/plugins/engine/components/LocationFieldBase.ts +152 -0
- package/src/server/plugins/engine/components/LocationFieldHelpers.test.ts +338 -0
- package/src/server/plugins/engine/components/LocationFieldHelpers.ts +123 -0
- package/src/server/plugins/engine/components/NationalGridFieldNumberField.test.ts +438 -0
- package/src/server/plugins/engine/components/NationalGridFieldNumberField.ts +52 -0
- package/src/server/plugins/engine/components/OsGridRefField.test.ts +469 -0
- package/src/server/plugins/engine/components/OsGridRefField.ts +71 -0
- package/src/server/plugins/engine/components/helpers/components.test.ts +270 -0
- package/src/server/plugins/engine/components/helpers/components.ts +39 -47
- package/src/server/plugins/engine/components/helpers/helpers.test.ts +71 -1
- package/src/server/plugins/engine/components/index.ts +4 -0
- package/src/server/plugins/engine/components/markdownParser.ts +40 -0
- package/src/server/plugins/engine/components/types.ts +14 -0
- package/src/server/plugins/engine/outputFormatters/adapter/v1.location.test.ts +356 -0
- package/src/server/plugins/engine/pageControllers/helpers/helpers.test.ts +4 -0
- package/src/server/plugins/engine/pageControllers/helpers/pages.ts +8 -0
- package/src/server/plugins/engine/types/index.ts +2 -0
- package/src/server/plugins/engine/types.ts +4 -0
- package/src/server/plugins/engine/views/components/_location-field-base.html +53 -0
- package/src/server/plugins/engine/views/components/eastingnorthingfield.html +5 -0
- package/src/server/plugins/engine/views/components/latlongfield.html +5 -0
- package/src/server/plugins/engine/views/components/nationalgridfieldnumberfield.html +13 -0
- package/src/server/plugins/engine/views/components/osgridreffield.html +13 -0
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ComponentType,
|
|
3
|
+
type NationalGridFieldNumberFieldComponent
|
|
4
|
+
} from '@defra/forms-model'
|
|
5
|
+
|
|
6
|
+
import { ComponentCollection } from '~/src/server/plugins/engine/components/ComponentCollection.js'
|
|
7
|
+
import { NationalGridFieldNumberField } from '~/src/server/plugins/engine/components/NationalGridFieldNumberField.js'
|
|
8
|
+
import {
|
|
9
|
+
getAnswer,
|
|
10
|
+
type Field
|
|
11
|
+
} from '~/src/server/plugins/engine/components/helpers/components.js'
|
|
12
|
+
import { FormModel } from '~/src/server/plugins/engine/models/FormModel.js'
|
|
13
|
+
import definition from '~/test/form/definitions/blank.js'
|
|
14
|
+
import { getFormData, getFormState } from '~/test/helpers/component-helpers.js'
|
|
15
|
+
|
|
16
|
+
describe('NationalGridFieldNumberField', () => {
|
|
17
|
+
let model: FormModel
|
|
18
|
+
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
model = new FormModel(definition, {
|
|
21
|
+
basePath: 'test'
|
|
22
|
+
})
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
describe('Defaults', () => {
|
|
26
|
+
let def: NationalGridFieldNumberFieldComponent
|
|
27
|
+
let collection: ComponentCollection
|
|
28
|
+
let field: Field
|
|
29
|
+
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
def = {
|
|
32
|
+
title: 'Example National Grid field number',
|
|
33
|
+
name: 'myComponent',
|
|
34
|
+
type: ComponentType.NationalGridFieldNumberField,
|
|
35
|
+
options: {}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
collection = new ComponentCollection([def], { model })
|
|
39
|
+
field = collection.fields[0]
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
describe('Schema', () => {
|
|
43
|
+
it('uses component title as label as default', () => {
|
|
44
|
+
const { formSchema } = collection
|
|
45
|
+
const { keys } = formSchema.describe()
|
|
46
|
+
|
|
47
|
+
expect(keys).toHaveProperty(
|
|
48
|
+
'myComponent',
|
|
49
|
+
expect.objectContaining({
|
|
50
|
+
flags: expect.objectContaining({
|
|
51
|
+
label: 'Example National Grid field number'
|
|
52
|
+
})
|
|
53
|
+
})
|
|
54
|
+
)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('uses component name as keys', () => {
|
|
58
|
+
const { formSchema } = collection
|
|
59
|
+
const { keys } = formSchema.describe()
|
|
60
|
+
|
|
61
|
+
expect(field.keys).toEqual(['myComponent'])
|
|
62
|
+
expect(field.collection).toBeUndefined()
|
|
63
|
+
|
|
64
|
+
for (const key of field.keys) {
|
|
65
|
+
expect(keys).toHaveProperty(key)
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('is required by default', () => {
|
|
70
|
+
const { formSchema } = collection
|
|
71
|
+
const { keys } = formSchema.describe()
|
|
72
|
+
|
|
73
|
+
expect(keys).toHaveProperty(
|
|
74
|
+
'myComponent',
|
|
75
|
+
expect.objectContaining({
|
|
76
|
+
flags: expect.objectContaining({
|
|
77
|
+
presence: 'required'
|
|
78
|
+
})
|
|
79
|
+
})
|
|
80
|
+
)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('is optional when configured', () => {
|
|
84
|
+
const collectionOptional = new ComponentCollection(
|
|
85
|
+
[{ ...def, options: { required: false } }],
|
|
86
|
+
{ model }
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
const { formSchema } = collectionOptional
|
|
90
|
+
const { keys } = formSchema.describe()
|
|
91
|
+
|
|
92
|
+
expect(keys).toHaveProperty(
|
|
93
|
+
'myComponent',
|
|
94
|
+
expect.objectContaining({ allow: [''] })
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
const result = collectionOptional.validate(getFormData(''))
|
|
98
|
+
expect(result.errors).toBeUndefined()
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it('accepts valid values', () => {
|
|
102
|
+
const result1 = collection.validate(getFormData('NG12345678'))
|
|
103
|
+
const result2 = collection.validate(getFormData('ng12345678'))
|
|
104
|
+
const result3 = collection.validate(getFormData('AB98765432'))
|
|
105
|
+
|
|
106
|
+
expect(result1.errors).toBeUndefined()
|
|
107
|
+
expect(result2.errors).toBeUndefined()
|
|
108
|
+
expect(result3.errors).toBeUndefined()
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
it('formats values with spaces per GDS guidance', () => {
|
|
112
|
+
const result1 = collection.validate(getFormData('NG 1234 5678'))
|
|
113
|
+
const result2 = collection.validate(getFormData('NG12345678'))
|
|
114
|
+
const result3 = collection.validate(getFormData('NG12345,678'))
|
|
115
|
+
|
|
116
|
+
expect(result1.value.myComponent).toBe('NG 1234 5678')
|
|
117
|
+
expect(result2.value.myComponent).toBe('NG 1234 5678')
|
|
118
|
+
expect(result3.value.myComponent).toBe('NG 1234 5678')
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('adds errors for empty value', () => {
|
|
122
|
+
const result = collection.validate(getFormData(''))
|
|
123
|
+
|
|
124
|
+
expect(result.errors).toEqual([
|
|
125
|
+
expect.objectContaining({
|
|
126
|
+
text: 'Enter example National Grid field number'
|
|
127
|
+
})
|
|
128
|
+
])
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
it('adds errors for invalid values', () => {
|
|
132
|
+
const result1 = collection.validate(getFormData('NG1234567'))
|
|
133
|
+
const result2 = collection.validate(getFormData('N123456789'))
|
|
134
|
+
const result3 = collection.validate(getFormData('NGABCDEFGH'))
|
|
135
|
+
|
|
136
|
+
expect(result1.errors).toBeTruthy()
|
|
137
|
+
expect(result2.errors).toBeTruthy()
|
|
138
|
+
expect(result3.errors).toBeTruthy()
|
|
139
|
+
})
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
describe('State', () => {
|
|
143
|
+
it('returns text from state', () => {
|
|
144
|
+
const state1 = getFormState('NG12345678')
|
|
145
|
+
const state2 = getFormState(null)
|
|
146
|
+
|
|
147
|
+
const answer1 = getAnswer(field, state1)
|
|
148
|
+
const answer2 = getAnswer(field, state2)
|
|
149
|
+
|
|
150
|
+
expect(answer1).toBe('NG12345678')
|
|
151
|
+
expect(answer2).toBe('')
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
it('returns payload from state', () => {
|
|
155
|
+
const state1 = getFormState('NG12345678')
|
|
156
|
+
const state2 = getFormState(null)
|
|
157
|
+
|
|
158
|
+
const payload1 = field.getFormDataFromState(state1)
|
|
159
|
+
const payload2 = field.getFormDataFromState(state2)
|
|
160
|
+
|
|
161
|
+
expect(payload1).toEqual(getFormData('NG12345678'))
|
|
162
|
+
expect(payload2).toEqual(getFormData())
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
it('returns value from state', () => {
|
|
166
|
+
const state1 = getFormState('NG12345678')
|
|
167
|
+
const state2 = getFormState(null)
|
|
168
|
+
|
|
169
|
+
const value1 = field.getFormValueFromState(state1)
|
|
170
|
+
const value2 = field.getFormValueFromState(state2)
|
|
171
|
+
|
|
172
|
+
expect(value1).toBe('NG12345678')
|
|
173
|
+
expect(value2).toBeUndefined()
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
it('returns context for conditions and form submission', () => {
|
|
177
|
+
const state1 = getFormState('NG12345678')
|
|
178
|
+
const state2 = getFormState(null)
|
|
179
|
+
|
|
180
|
+
const value1 = field.getContextValueFromState(state1)
|
|
181
|
+
const value2 = field.getContextValueFromState(state2)
|
|
182
|
+
|
|
183
|
+
expect(value1).toBe('NG12345678')
|
|
184
|
+
expect(value2).toBeNull()
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
it('returns state from payload', () => {
|
|
188
|
+
const payload1 = getFormData('NG12345678')
|
|
189
|
+
const payload2 = getFormData()
|
|
190
|
+
|
|
191
|
+
const value1 = field.getStateFromValidForm(payload1)
|
|
192
|
+
const value2 = field.getStateFromValidForm(payload2)
|
|
193
|
+
|
|
194
|
+
expect(value1).toEqual(getFormState('NG12345678'))
|
|
195
|
+
expect(value2).toEqual(getFormState(null))
|
|
196
|
+
})
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
describe('View model', () => {
|
|
200
|
+
it('sets Nunjucks component defaults', () => {
|
|
201
|
+
const viewModel = field.getViewModel(getFormData('NG12345678'))
|
|
202
|
+
|
|
203
|
+
expect(viewModel).toEqual(
|
|
204
|
+
expect.objectContaining({
|
|
205
|
+
label: { text: def.title },
|
|
206
|
+
name: 'myComponent',
|
|
207
|
+
id: 'myComponent',
|
|
208
|
+
value: 'NG12345678'
|
|
209
|
+
})
|
|
210
|
+
)
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
it('includes instruction text when provided', () => {
|
|
214
|
+
const componentWithInstruction = new NationalGridFieldNumberField(
|
|
215
|
+
{
|
|
216
|
+
...def,
|
|
217
|
+
options: { instructionText: 'Enter in format **NG12345678**' }
|
|
218
|
+
},
|
|
219
|
+
{ model }
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
const viewModel = componentWithInstruction.getViewModel(
|
|
223
|
+
getFormData('NG12345678')
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
const instructionText =
|
|
227
|
+
'instructionText' in viewModel ? viewModel.instructionText : undefined
|
|
228
|
+
expect(instructionText).toBeTruthy()
|
|
229
|
+
expect(instructionText).toContain('NG12345678')
|
|
230
|
+
})
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
describe('AllPossibleErrors', () => {
|
|
234
|
+
it('should return errors from instance method', () => {
|
|
235
|
+
const errors = field.getAllPossibleErrors()
|
|
236
|
+
expect(errors.baseErrors).not.toBeEmpty()
|
|
237
|
+
expect(errors.advancedSettingsErrors).toEqual([])
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
it('should return errors from static method', () => {
|
|
241
|
+
const staticErrors = NationalGridFieldNumberField.getAllPossibleErrors()
|
|
242
|
+
expect(staticErrors.baseErrors).not.toBeEmpty()
|
|
243
|
+
expect(staticErrors.advancedSettingsErrors).toEqual([])
|
|
244
|
+
})
|
|
245
|
+
})
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
describe('Validation', () => {
|
|
249
|
+
describe.each([
|
|
250
|
+
{
|
|
251
|
+
description: 'Trim empty spaces',
|
|
252
|
+
component: {
|
|
253
|
+
title: 'Example National Grid field number',
|
|
254
|
+
name: 'myComponent',
|
|
255
|
+
type: ComponentType.NationalGridFieldNumberField,
|
|
256
|
+
options: {}
|
|
257
|
+
},
|
|
258
|
+
assertions: [
|
|
259
|
+
{
|
|
260
|
+
input: getFormData(' NG12345678'),
|
|
261
|
+
output: { value: getFormData('NG 1234 5678') }
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
input: getFormData('NG12345678 '),
|
|
265
|
+
output: { value: getFormData('NG 1234 5678') }
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
input: getFormData(' NG12345678 \n\n'),
|
|
269
|
+
output: { value: getFormData('NG 1234 5678') }
|
|
270
|
+
}
|
|
271
|
+
]
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
description: 'Pattern validation',
|
|
275
|
+
component: {
|
|
276
|
+
title: 'Example National Grid field number',
|
|
277
|
+
name: 'myComponent',
|
|
278
|
+
type: ComponentType.NationalGridFieldNumberField,
|
|
279
|
+
options: {}
|
|
280
|
+
},
|
|
281
|
+
assertions: [
|
|
282
|
+
{
|
|
283
|
+
input: getFormData('NG1234567'),
|
|
284
|
+
output: {
|
|
285
|
+
value: getFormData('NG1234567'),
|
|
286
|
+
errors: expect.arrayContaining([
|
|
287
|
+
expect.objectContaining({
|
|
288
|
+
text: 'Enter a valid National Grid field number for Example National Grid field number like NG 1234 5678'
|
|
289
|
+
})
|
|
290
|
+
])
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
input: getFormData('N123456789'),
|
|
295
|
+
output: {
|
|
296
|
+
value: getFormData('N123456789'),
|
|
297
|
+
errors: expect.arrayContaining([
|
|
298
|
+
expect.objectContaining({
|
|
299
|
+
text: 'Enter a valid National Grid field number for Example National Grid field number like NG 1234 5678'
|
|
300
|
+
})
|
|
301
|
+
])
|
|
302
|
+
}
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
input: getFormData('NGABCDEFGH'),
|
|
306
|
+
output: {
|
|
307
|
+
value: getFormData('NGABCDEFGH'),
|
|
308
|
+
errors: expect.arrayContaining([
|
|
309
|
+
expect.objectContaining({
|
|
310
|
+
text: 'Enter a valid National Grid field number for Example National Grid field number like NG 1234 5678'
|
|
311
|
+
})
|
|
312
|
+
])
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
]
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
description: 'Custom validation message',
|
|
319
|
+
component: {
|
|
320
|
+
title: 'Example National Grid field number',
|
|
321
|
+
name: 'myComponent',
|
|
322
|
+
type: ComponentType.NationalGridFieldNumberField,
|
|
323
|
+
options: {
|
|
324
|
+
customValidationMessage: 'This is a custom error'
|
|
325
|
+
}
|
|
326
|
+
},
|
|
327
|
+
assertions: [
|
|
328
|
+
{
|
|
329
|
+
input: getFormData(''),
|
|
330
|
+
output: {
|
|
331
|
+
value: getFormData(''),
|
|
332
|
+
errors: [
|
|
333
|
+
expect.objectContaining({
|
|
334
|
+
text: 'This is a custom error'
|
|
335
|
+
})
|
|
336
|
+
]
|
|
337
|
+
}
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
input: getFormData('INVALID'),
|
|
341
|
+
output: {
|
|
342
|
+
value: getFormData('INVALID'),
|
|
343
|
+
errors: expect.arrayContaining([
|
|
344
|
+
expect.objectContaining({
|
|
345
|
+
text: 'This is a custom error'
|
|
346
|
+
})
|
|
347
|
+
])
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
]
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
description: 'Custom validation messages (multiple)',
|
|
354
|
+
component: {
|
|
355
|
+
title: 'Example National Grid field number',
|
|
356
|
+
name: 'myComponent',
|
|
357
|
+
type: ComponentType.NationalGridFieldNumberField,
|
|
358
|
+
options: {
|
|
359
|
+
customValidationMessages: {
|
|
360
|
+
'any.required': 'This is a custom required error',
|
|
361
|
+
'string.empty': 'This is a custom empty string error',
|
|
362
|
+
'string.pattern.base': 'This is a custom pattern error'
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
assertions: [
|
|
367
|
+
{
|
|
368
|
+
input: getFormData(),
|
|
369
|
+
output: {
|
|
370
|
+
value: getFormData(''),
|
|
371
|
+
errors: [
|
|
372
|
+
expect.objectContaining({
|
|
373
|
+
text: 'This is a custom required error'
|
|
374
|
+
})
|
|
375
|
+
]
|
|
376
|
+
}
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
input: getFormData(''),
|
|
380
|
+
output: {
|
|
381
|
+
value: getFormData(''),
|
|
382
|
+
errors: [
|
|
383
|
+
expect.objectContaining({
|
|
384
|
+
text: 'This is a custom empty string error'
|
|
385
|
+
})
|
|
386
|
+
]
|
|
387
|
+
}
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
input: getFormData('INVALID'),
|
|
391
|
+
output: {
|
|
392
|
+
value: getFormData('INVALID'),
|
|
393
|
+
errors: expect.arrayContaining([
|
|
394
|
+
expect.objectContaining({
|
|
395
|
+
text: 'This is a custom pattern error'
|
|
396
|
+
})
|
|
397
|
+
])
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
]
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
description: 'Optional field',
|
|
404
|
+
component: {
|
|
405
|
+
title: 'Example National Grid field number',
|
|
406
|
+
name: 'myComponent',
|
|
407
|
+
type: ComponentType.NationalGridFieldNumberField,
|
|
408
|
+
options: {
|
|
409
|
+
required: false
|
|
410
|
+
}
|
|
411
|
+
},
|
|
412
|
+
assertions: [
|
|
413
|
+
{
|
|
414
|
+
input: getFormData(''),
|
|
415
|
+
output: { value: getFormData('') }
|
|
416
|
+
}
|
|
417
|
+
]
|
|
418
|
+
}
|
|
419
|
+
])('$description', ({ component: def, assertions }) => {
|
|
420
|
+
let collection: ComponentCollection
|
|
421
|
+
|
|
422
|
+
beforeEach(() => {
|
|
423
|
+
collection = new ComponentCollection(
|
|
424
|
+
[def as NationalGridFieldNumberFieldComponent],
|
|
425
|
+
{ model }
|
|
426
|
+
)
|
|
427
|
+
})
|
|
428
|
+
|
|
429
|
+
it.each([...assertions])(
|
|
430
|
+
'validates custom example',
|
|
431
|
+
({ input, output }) => {
|
|
432
|
+
const result = collection.validate(input)
|
|
433
|
+
expect(result).toEqual(output)
|
|
434
|
+
}
|
|
435
|
+
)
|
|
436
|
+
})
|
|
437
|
+
})
|
|
438
|
+
})
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type NationalGridFieldNumberFieldComponent } from '@defra/forms-model'
|
|
2
|
+
import type joi from 'joi'
|
|
3
|
+
|
|
4
|
+
import { LocationFieldBase } from '~/src/server/plugins/engine/components/LocationFieldBase.js'
|
|
5
|
+
|
|
6
|
+
export class NationalGridFieldNumberField extends LocationFieldBase {
|
|
7
|
+
declare options: NationalGridFieldNumberFieldComponent['options']
|
|
8
|
+
|
|
9
|
+
protected getValidationConfig() {
|
|
10
|
+
return {
|
|
11
|
+
// Pattern allows spaces and commas in the input since custom validation will clean them
|
|
12
|
+
pattern: /^[A-Z]{2}[\d\s,]*$/i,
|
|
13
|
+
patternErrorMessage: `Enter a valid National Grid field number for ${this.title} like NG 1234 5678`,
|
|
14
|
+
customValidation: (value: string, helpers: joi.CustomHelpers) => {
|
|
15
|
+
// Strip spaces and commas for validation
|
|
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)}`
|
|
27
|
+
|
|
28
|
+
return formattedValue
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
protected getErrorTemplates() {
|
|
34
|
+
return [
|
|
35
|
+
{
|
|
36
|
+
type: 'pattern',
|
|
37
|
+
template:
|
|
38
|
+
'Enter a valid National Grid field number for [short description] like NG 1234 5678'
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Static version of getAllPossibleErrors that doesn't require a component instance.
|
|
45
|
+
*/
|
|
46
|
+
static getAllPossibleErrors() {
|
|
47
|
+
const instance = Object.create(
|
|
48
|
+
NationalGridFieldNumberField.prototype
|
|
49
|
+
) as NationalGridFieldNumberField
|
|
50
|
+
return instance.getAllPossibleErrors()
|
|
51
|
+
}
|
|
52
|
+
}
|