@defra/forms-engine-plugin 5.0.0-alpha.8 → 5.0.0-alpha.9

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.
Files changed (162) hide show
  1. package/.server/server/constants.d.ts +1 -0
  2. package/.server/server/constants.js +2 -1
  3. package/.server/server/constants.js.map +1 -1
  4. package/.server/server/plugins/engine/components/ComponentCollection.js +10 -6
  5. package/.server/server/plugins/engine/components/ComponentCollection.js.map +1 -1
  6. package/.server/server/plugins/engine/components/DatePartsField.d.ts +4 -2
  7. package/.server/server/plugins/engine/components/DatePartsField.js +14 -11
  8. package/.server/server/plugins/engine/components/DatePartsField.js.map +1 -1
  9. package/.server/server/plugins/engine/components/DeclarationField.d.ts +4 -2
  10. package/.server/server/plugins/engine/components/DeclarationField.js +5 -3
  11. package/.server/server/plugins/engine/components/DeclarationField.js.map +1 -1
  12. package/.server/server/plugins/engine/components/EastingNorthingField.d.ts +24 -2
  13. package/.server/server/plugins/engine/components/EastingNorthingField.js +93 -52
  14. package/.server/server/plugins/engine/components/EastingNorthingField.js.map +1 -1
  15. package/.server/server/plugins/engine/components/FormComponent.d.ts +4 -1
  16. package/.server/server/plugins/engine/components/FormComponent.js +3 -0
  17. package/.server/server/plugins/engine/components/FormComponent.js.map +1 -1
  18. package/.server/server/plugins/engine/components/LatLongField.d.ts +24 -2
  19. package/.server/server/plugins/engine/components/LatLongField.js +85 -46
  20. package/.server/server/plugins/engine/components/LatLongField.js.map +1 -1
  21. package/.server/server/plugins/engine/components/LocationFieldHelpers.d.ts +3 -2
  22. package/.server/server/plugins/engine/components/LocationFieldHelpers.js +10 -11
  23. package/.server/server/plugins/engine/components/LocationFieldHelpers.js.map +1 -1
  24. package/.server/server/plugins/engine/components/MonthYearField.d.ts +4 -2
  25. package/.server/server/plugins/engine/components/MonthYearField.js +16 -13
  26. package/.server/server/plugins/engine/components/MonthYearField.js.map +1 -1
  27. package/.server/server/plugins/engine/components/PaymentField.d.ts +1 -0
  28. package/.server/server/plugins/engine/components/PaymentField.js +9 -5
  29. package/.server/server/plugins/engine/components/PaymentField.js.map +1 -1
  30. package/.server/server/plugins/engine/components/UkAddressField.js +22 -7
  31. package/.server/server/plugins/engine/components/UkAddressField.js.map +1 -1
  32. package/.server/server/plugins/engine/components/YesNoField.d.ts +3 -2
  33. package/.server/server/plugins/engine/components/YesNoField.js +5 -3
  34. package/.server/server/plugins/engine/components/YesNoField.js.map +1 -1
  35. package/.server/server/plugins/engine/components/helpers/components.js +1 -2
  36. package/.server/server/plugins/engine/components/helpers/components.js.map +1 -1
  37. package/.server/server/plugins/engine/components/helpers/index.d.ts +7 -0
  38. package/.server/server/plugins/engine/components/helpers/index.js +13 -1
  39. package/.server/server/plugins/engine/components/helpers/index.js.map +1 -1
  40. package/.server/server/plugins/engine/helpers.d.ts +21 -2
  41. package/.server/server/plugins/engine/helpers.js +43 -5
  42. package/.server/server/plugins/engine/helpers.js.map +1 -1
  43. package/.server/server/plugins/engine/i18n/createFormTranslator.d.ts +4 -4
  44. package/.server/server/plugins/engine/i18n/createFormTranslator.js +27 -5
  45. package/.server/server/plugins/engine/i18n/createFormTranslator.js.map +1 -1
  46. package/.server/server/plugins/engine/i18n/extractBaseTranslations.d.ts +1 -1
  47. package/.server/server/plugins/engine/i18n/extractBaseTranslations.js +9 -0
  48. package/.server/server/plugins/engine/i18n/extractBaseTranslations.js.map +1 -1
  49. package/.server/server/plugins/engine/i18n/form.d.ts +13 -0
  50. package/.server/server/plugins/engine/i18n/form.js +35 -0
  51. package/.server/server/plugins/engine/i18n/form.js.map +1 -0
  52. package/.server/server/plugins/engine/i18n/languages.d.ts +10 -0
  53. package/.server/server/plugins/engine/i18n/languages.js +19 -0
  54. package/.server/server/plugins/engine/i18n/languages.js.map +1 -0
  55. package/.server/server/plugins/engine/i18n/translations/cy.json +3 -1
  56. package/.server/server/plugins/engine/i18n/translations/en-GB.json +3 -1
  57. package/.server/server/plugins/engine/index.d.ts +3 -0
  58. package/.server/server/plugins/engine/index.js +3 -0
  59. package/.server/server/plugins/engine/index.js.map +1 -1
  60. package/.server/server/plugins/engine/models/FormModel.d.ts +0 -4
  61. package/.server/server/plugins/engine/models/FormModel.js +9 -15
  62. package/.server/server/plugins/engine/models/FormModel.js.map +1 -1
  63. package/.server/server/plugins/engine/models/SummaryViewModel.js +2 -1
  64. package/.server/server/plugins/engine/models/SummaryViewModel.js.map +1 -1
  65. package/.server/server/plugins/engine/outputFormatters/human/v1.js +2 -1
  66. package/.server/server/plugins/engine/outputFormatters/human/v1.js.map +1 -1
  67. package/.server/server/plugins/engine/outputFormatters/machine/v2.d.ts +2 -0
  68. package/.server/server/plugins/engine/outputFormatters/machine/v2.js +2 -0
  69. package/.server/server/plugins/engine/outputFormatters/machine/v2.js.map +1 -1
  70. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.d.ts +1 -1
  71. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js +2 -2
  72. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js.map +1 -1
  73. package/.server/server/plugins/engine/pageControllers/QuestionPageController.d.ts +1 -1
  74. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js +2 -2
  75. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -1
  76. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js +6 -3
  77. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js.map +1 -1
  78. package/.server/server/plugins/engine/pageControllers/helpers/submission.d.ts +1 -1
  79. package/.server/server/plugins/engine/pageControllers/helpers/submission.js +3 -3
  80. package/.server/server/plugins/engine/pageControllers/helpers/submission.js.map +1 -1
  81. package/.server/server/plugins/engine/types/schema.js +1 -0
  82. package/.server/server/plugins/engine/types/schema.js.map +1 -1
  83. package/.server/server/plugins/engine/types.d.ts +1 -0
  84. package/.server/server/plugins/engine/types.js.map +1 -1
  85. package/.server/server/plugins/engine/views/components/ukaddressfield.html +1 -1
  86. package/.server/server/plugins/nunjucks/filters/applyUrlParam.d.ts +9 -0
  87. package/.server/server/plugins/nunjucks/filters/applyUrlParam.js +17 -0
  88. package/.server/server/plugins/nunjucks/filters/applyUrlParam.js.map +1 -0
  89. package/.server/server/plugins/nunjucks/filters/applyUrlParam.test.js +17 -0
  90. package/.server/server/plugins/nunjucks/filters/applyUrlParam.test.js.map +1 -0
  91. package/.server/server/plugins/nunjucks/filters/field.d.ts +1 -1
  92. package/.server/server/plugins/nunjucks/filters/index.d.ts +1 -0
  93. package/.server/server/plugins/nunjucks/filters/index.js +1 -0
  94. package/.server/server/plugins/nunjucks/filters/index.js.map +1 -1
  95. package/.server/server/plugins/payment/helper.d.ts +11 -1
  96. package/.server/server/plugins/payment/helper.js +18 -2
  97. package/.server/server/plugins/payment/helper.js.map +1 -1
  98. package/.server/server/plugins/payment/helper.test.js +2 -1
  99. package/.server/server/plugins/payment/helper.test.js.map +1 -1
  100. package/.server/server/plugins/payment/service.d.ts +2 -1
  101. package/.server/server/plugins/payment/service.js +5 -2
  102. package/.server/server/plugins/payment/service.js.map +1 -1
  103. package/.server/server/plugins/payment/service.test.js +7 -3
  104. package/.server/server/plugins/payment/service.test.js.map +1 -1
  105. package/.server/server/plugins/payment/types.d.ts +4 -0
  106. package/.server/server/plugins/payment/types.js +1 -0
  107. package/.server/server/plugins/payment/types.js.map +1 -1
  108. package/.server/server/plugins/postcode-lookup/models/index.d.ts +247 -3
  109. package/.server/server/plugins/postcode-lookup/models/index.js +86 -33
  110. package/.server/server/plugins/postcode-lookup/models/index.js.map +1 -1
  111. package/.server/server/plugins/postcode-lookup/routes/index.d.ts +15 -1
  112. package/.server/server/plugins/postcode-lookup/routes/index.js +47 -15
  113. package/.server/server/plugins/postcode-lookup/routes/index.js.map +1 -1
  114. package/.server/server/plugins/postcode-lookup/routes/index.test.js +84 -0
  115. package/.server/server/plugins/postcode-lookup/routes/index.test.js.map +1 -0
  116. package/.server/server/plugins/postcode-lookup/types.d.ts +12 -0
  117. package/.server/server/plugins/postcode-lookup/types.js +6 -1
  118. package/.server/server/plugins/postcode-lookup/types.js.map +1 -1
  119. package/.server/server/plugins/postcode-lookup/views/postcode-lookup-details.html +3 -3
  120. package/package.json +2 -1
  121. package/src/server/constants.js +2 -1
  122. package/src/server/plugins/engine/components/ComponentCollection.ts +15 -9
  123. package/src/server/plugins/engine/components/DatePartsField.ts +16 -12
  124. package/src/server/plugins/engine/components/DeclarationField.ts +6 -4
  125. package/src/server/plugins/engine/components/EastingNorthingField.ts +156 -88
  126. package/src/server/plugins/engine/components/FormComponent.ts +4 -1
  127. package/src/server/plugins/engine/components/LatLongField.ts +129 -57
  128. package/src/server/plugins/engine/components/LocationFieldHelpers.ts +14 -11
  129. package/src/server/plugins/engine/components/MonthYearField.ts +16 -11
  130. package/src/server/plugins/engine/components/PaymentField.ts +9 -4
  131. package/src/server/plugins/engine/components/UkAddressField.ts +29 -6
  132. package/src/server/plugins/engine/components/YesNoField.ts +6 -5
  133. package/src/server/plugins/engine/components/helpers/components.ts +1 -2
  134. package/src/server/plugins/engine/components/helpers/index.ts +19 -0
  135. package/src/server/plugins/engine/helpers.ts +51 -5
  136. package/src/server/plugins/engine/i18n/createFormTranslator.ts +43 -12
  137. package/src/server/plugins/engine/i18n/extractBaseTranslations.ts +7 -1
  138. package/src/server/plugins/engine/i18n/form.ts +54 -0
  139. package/src/server/plugins/engine/i18n/languages.ts +19 -0
  140. package/src/server/plugins/engine/i18n/translations/cy.json +3 -1
  141. package/src/server/plugins/engine/i18n/translations/en-GB.json +3 -1
  142. package/src/server/plugins/engine/index.ts +6 -0
  143. package/src/server/plugins/engine/models/FormModel.ts +9 -15
  144. package/src/server/plugins/engine/models/SummaryViewModel.ts +2 -1
  145. package/src/server/plugins/engine/outputFormatters/human/v1.ts +2 -1
  146. package/src/server/plugins/engine/outputFormatters/machine/v2.ts +3 -0
  147. package/src/server/plugins/engine/pageControllers/FileUploadPageController.ts +2 -2
  148. package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +2 -2
  149. package/src/server/plugins/engine/pageControllers/SummaryPageController.ts +9 -3
  150. package/src/server/plugins/engine/pageControllers/helpers/submission.ts +6 -3
  151. package/src/server/plugins/engine/types/schema.ts +1 -0
  152. package/src/server/plugins/engine/types.ts +1 -0
  153. package/src/server/plugins/engine/views/components/ukaddressfield.html +1 -1
  154. package/src/server/plugins/nunjucks/filters/applyUrlParam.js +17 -0
  155. package/src/server/plugins/nunjucks/filters/index.js +1 -0
  156. package/src/server/plugins/payment/helper.js +22 -2
  157. package/src/server/plugins/payment/service.js +6 -2
  158. package/src/server/plugins/payment/types.js +1 -0
  159. package/src/server/plugins/postcode-lookup/models/index.js +75 -43
  160. package/src/server/plugins/postcode-lookup/routes/index.js +73 -16
  161. package/src/server/plugins/postcode-lookup/types.js +6 -1
  162. package/src/server/plugins/postcode-lookup/views/postcode-lookup-details.html +3 -3
@@ -15,7 +15,10 @@ import {
15
15
  getLocationFieldViewModel
16
16
  } from './LocationFieldHelpers.js'
17
17
  import { NumberField } from './NumberField.js'
18
- import { createLowerFirstExpression } from './helpers/index.js'
18
+ import {
19
+ createLowerFirstExpression,
20
+ getTranslatedLabel
21
+ } from './helpers/index.js'
19
22
  import {
20
23
  type EastingNorthingState,
21
24
  type RenderContext
@@ -55,89 +58,18 @@ export class EastingNorthingField extends FormComponent {
55
58
 
56
59
  const isRequired = options.required !== false
57
60
 
58
- const eastingMin = schema?.easting?.min ?? DEFAULT_EASTING_MIN
59
- const eastingMax = schema?.easting?.max ?? DEFAULT_EASTING_MAX
60
- const northingMin = schema?.northing?.min ?? DEFAULT_NORTHING_MIN
61
- const northingMax = schema?.northing?.max ?? DEFAULT_NORTHING_MAX
62
-
63
- const fieldLabel = lowerFirst(this.label)
61
+ const { eastingMin, eastingMax, northingMin, northingMax } =
62
+ EastingNorthingField.getMinMax(schema)
64
63
 
65
- const eastingDigitsMessage = tPlugin(
66
- 'components.eastingNorthingField.eastingDigits',
67
- 'en-GB',
68
- { fieldLabel }
69
- )
70
- const northingDigitsMessage = tPlugin(
71
- 'components.eastingNorthingField.northingDigits',
72
- 'en-GB',
73
- { fieldLabel }
74
- )
75
-
76
- const customValidationMessages: LanguageMessages =
77
- convertToLanguageMessages({
78
- 'any.required': tPlugin(
79
- 'components.eastingNorthingField.eastingRequired',
80
- 'en-GB'
81
- ),
82
- 'number.base': tPlugin(
83
- 'components.eastingNorthingField.eastingRequired',
84
- 'en-GB'
85
- ),
86
- 'number.min': tPlugin(
87
- 'components.eastingNorthingField.eastingRange',
88
- 'en-GB',
89
- {
90
- fieldLabel,
91
- min: eastingMin,
92
- max: eastingMax
93
- }
94
- ),
95
- 'number.max': tPlugin(
96
- 'components.eastingNorthingField.eastingRange',
97
- 'en-GB',
98
- {
99
- fieldLabel,
100
- min: eastingMin,
101
- max: eastingMax
102
- }
103
- ),
104
- 'number.precision': eastingDigitsMessage,
105
- 'number.integer': eastingDigitsMessage,
106
- 'number.unsafe': eastingDigitsMessage
107
- })
108
-
109
- const northingValidationMessages: LanguageMessages =
110
- convertToLanguageMessages({
111
- 'any.required': tPlugin(
112
- 'components.eastingNorthingField.northingRequired',
113
- 'en-GB'
114
- ),
115
- 'number.base': tPlugin(
116
- 'components.eastingNorthingField.northingRequired',
117
- 'en-GB'
118
- ),
119
- 'number.min': tPlugin(
120
- 'components.eastingNorthingField.northingRange',
121
- 'en-GB',
122
- {
123
- fieldLabel,
124
- min: northingMin,
125
- max: northingMax
126
- }
127
- ),
128
- 'number.max': tPlugin(
129
- 'components.eastingNorthingField.northingRange',
130
- 'en-GB',
131
- {
132
- fieldLabel,
133
- min: northingMin,
134
- max: northingMax
135
- }
136
- ),
137
- 'number.precision': northingDigitsMessage,
138
- 'number.integer': northingDigitsMessage,
139
- 'number.unsafe': northingDigitsMessage
140
- })
64
+ const { eastingValidationMessages, northingValidationMessages } =
65
+ EastingNorthingField.buildErrorMessages(
66
+ this.label,
67
+ eastingMin,
68
+ eastingMax,
69
+ northingMin,
70
+ northingMax,
71
+ 'en-GB'
72
+ )
141
73
 
142
74
  this.collection = new ComponentCollection(
143
75
  [
@@ -154,7 +86,7 @@ export class EastingNorthingField extends FormComponent {
154
86
  required: isRequired,
155
87
  optionalText: true,
156
88
  classes: 'govuk-input--width-10',
157
- customValidationMessages
89
+ customValidationMessages: eastingValidationMessages
158
90
  }
159
91
  },
160
92
  {
@@ -192,14 +124,15 @@ export class EastingNorthingField extends FormComponent {
192
124
 
193
125
  getDisplayStringFromFormValue(
194
126
  value: EastingNorthingState | undefined,
195
- _translator: Translator
127
+ translator: Translator
196
128
  ): string {
197
129
  if (!value) {
198
130
  return ''
199
131
  }
200
132
 
201
- // CYA page format: <<eastingvalue, northingvalue>>
202
- return `${value.easting}, ${value.northing}`
133
+ const { t } = translator
134
+
135
+ return `${t('components.eastingNorthingField.easting')}: ${value.easting}\n${t('components.eastingNorthingField.northing')}: ${value.northing}`
203
136
  }
204
137
 
205
138
  getDisplayStringFromState(
@@ -218,7 +151,7 @@ export class EastingNorthingField extends FormComponent {
218
151
  return null
219
152
  }
220
153
 
221
- return `Easting: ${value.easting}\nNorthing: ${value.northing}`
154
+ return `${value.easting}, ${value.northing}`
222
155
  }
223
156
 
224
157
  getContextValueFromState(state: FormSubmissionState) {
@@ -244,6 +177,26 @@ export class EastingNorthingField extends FormComponent {
244
177
  return EastingNorthingField.isEastingNorthing(value)
245
178
  }
246
179
 
180
+ getValidationMessagesOverride(translator: Translator) {
181
+ const def = this.def as EastingNorthingFieldComponent
182
+ const translatedLabel = getTranslatedLabel(def, translator)
183
+ const { eastingMin, eastingMax, northingMin, northingMax } =
184
+ EastingNorthingField.getMinMax(def.schema)
185
+ const { eastingValidationMessages, northingValidationMessages } =
186
+ EastingNorthingField.buildErrorMessages(
187
+ translatedLabel,
188
+ eastingMin,
189
+ eastingMax,
190
+ northingMin,
191
+ northingMax,
192
+ translator.language
193
+ )
194
+ return {
195
+ [`${this.name}__easting`]: eastingValidationMessages,
196
+ [`${this.name}__northing`]: northingValidationMessages
197
+ }
198
+ }
199
+
247
200
  /**
248
201
  * For error preview page that shows all possible errors on a component
249
202
  */
@@ -309,4 +262,119 @@ export class EastingNorthingField extends FormComponent {
309
262
  NumberField.isNumber(value.northing)
310
263
  )
311
264
  }
265
+
266
+ static buildErrorMessages(
267
+ label: string,
268
+ eastingMin: number,
269
+ eastingMax: number,
270
+ northingMin: number,
271
+ northingMax: number,
272
+ language: string
273
+ ) {
274
+ const fieldLabel = lowerFirst(label)
275
+
276
+ const eastingDigitsMessage = tPlugin(
277
+ 'components.eastingNorthingField.eastingDigits',
278
+ language,
279
+ { fieldLabel }
280
+ )
281
+ const northingDigitsMessage = tPlugin(
282
+ 'components.eastingNorthingField.northingDigits',
283
+ language,
284
+ { fieldLabel }
285
+ )
286
+
287
+ const eastingValidationMessages: LanguageMessages =
288
+ convertToLanguageMessages({
289
+ 'any.required': tPlugin(
290
+ 'components.eastingNorthingField.eastingRequired',
291
+ language
292
+ ),
293
+ 'number.base': tPlugin(
294
+ 'components.eastingNorthingField.eastingRequired',
295
+ language
296
+ ),
297
+ 'number.min': tPlugin(
298
+ 'components.eastingNorthingField.eastingRange',
299
+ language,
300
+ {
301
+ fieldLabel,
302
+ min: eastingMin,
303
+ max: eastingMax
304
+ }
305
+ ),
306
+ 'number.max': tPlugin(
307
+ 'components.eastingNorthingField.eastingRange',
308
+ language,
309
+ {
310
+ fieldLabel,
311
+ min: eastingMin,
312
+ max: eastingMax
313
+ }
314
+ ),
315
+ 'number.precision': eastingDigitsMessage,
316
+ 'number.integer': eastingDigitsMessage,
317
+ 'number.unsafe': eastingDigitsMessage
318
+ })
319
+
320
+ const northingValidationMessages: LanguageMessages =
321
+ convertToLanguageMessages({
322
+ 'any.required': tPlugin(
323
+ 'components.eastingNorthingField.northingRequired',
324
+ language
325
+ ),
326
+ 'number.base': tPlugin(
327
+ 'components.eastingNorthingField.northingRequired',
328
+ language
329
+ ),
330
+ 'number.min': tPlugin(
331
+ 'components.eastingNorthingField.northingRange',
332
+ language,
333
+ {
334
+ fieldLabel,
335
+ min: northingMin,
336
+ max: northingMax
337
+ }
338
+ ),
339
+ 'number.max': tPlugin(
340
+ 'components.eastingNorthingField.northingRange',
341
+ language,
342
+ {
343
+ fieldLabel,
344
+ min: northingMin,
345
+ max: northingMax
346
+ }
347
+ ),
348
+ 'number.precision': northingDigitsMessage,
349
+ 'number.integer': northingDigitsMessage,
350
+ 'number.unsafe': northingDigitsMessage
351
+ })
352
+ return {
353
+ eastingValidationMessages,
354
+ northingValidationMessages
355
+ }
356
+ }
357
+
358
+ // Read schema values from def.schema with fallback defaults
359
+ static getMinMax(
360
+ schema:
361
+ | {
362
+ easting?: {
363
+ min?: number
364
+ max?: number
365
+ }
366
+ northing?: {
367
+ min?: number
368
+ max?: number
369
+ }
370
+ }
371
+ | undefined
372
+ ) {
373
+ return {
374
+ eastingMin: schema?.easting?.min ?? DEFAULT_EASTING_MIN,
375
+ eastingMax: schema?.easting?.max ?? DEFAULT_EASTING_MAX,
376
+ northingMin: schema?.northing?.min ?? DEFAULT_NORTHING_MIN,
377
+ northingMax: schema?.northing?.max ?? DEFAULT_NORTHING_MAX
378
+ }
379
+ }
312
380
  }
@@ -157,10 +157,13 @@ export class FormComponent extends ComponentBase {
157
157
  * Override in composite fields to return translated Joi message overrides for
158
158
  * their sub-fields, applied at validation time so schema-level English messages
159
159
  * are replaced with the correct language. Return null to skip message patching.
160
+ * The return object may contain message overrides at component-level, or sub-field-level.
161
+ * This would allow different error message overrides per field within a collection e.g.
162
+ * for EastingNothingField
160
163
  */
161
164
  getValidationMessagesOverride(
162
165
  _translator: Translator
163
- ): LanguageMessages | null {
166
+ ): Record<string, LanguageMessages> | null {
164
167
  return null
165
168
  }
166
169
 
@@ -12,7 +12,10 @@ import {
12
12
  getLocationFieldViewModel
13
13
  } from './LocationFieldHelpers.js'
14
14
  import { NumberField } from './NumberField.js'
15
- import { createLowerFirstExpression } from './helpers/index.js'
15
+ import {
16
+ createLowerFirstExpression,
17
+ getTranslatedLabel
18
+ } from './helpers/index.js'
16
19
  import {
17
20
  type LatLongState,
18
21
  type RenderContext
@@ -50,59 +53,18 @@ export class LatLongField extends FormComponent {
50
53
 
51
54
  const isRequired = options.required !== false
52
55
 
53
- // Read schema values from def.schema with fallback defaults
54
- const latitudeMin = schema?.latitude?.min ?? 49.85
55
- const latitudeMax = schema?.latitude?.max ?? 60.859
56
- const longitudeMin = schema?.longitude?.min ?? -13.687
57
- const longitudeMax = schema?.longitude?.max ?? 1.767
58
-
59
- const fieldLabel = lowerFirst(this.label)
60
-
61
- const customValidationMessages: LanguageMessages =
62
- convertToLanguageMessages({
63
- 'number.precision': tPlugin(
64
- 'components.latLongField.precision',
65
- 'en-GB'
66
- ),
67
- 'number.unsafe': tPlugin('components.latLongField.notANumber', 'en-GB')
68
- })
69
-
70
- const latitudeRangeMessage = tPlugin(
71
- 'components.latLongField.latitudeRange',
72
- 'en-GB',
73
- { fieldLabel, min: latitudeMin, max: latitudeMax }
74
- )
75
- const longitudeRangeMessage = tPlugin(
76
- 'components.latLongField.longitudeRange',
77
- 'en-GB',
78
- { fieldLabel, min: longitudeMin, max: longitudeMax }
79
- )
80
-
81
- const latitudeMessages: LanguageMessages = convertToLanguageMessages({
82
- ...customValidationMessages,
83
- 'any.required': tPlugin(
84
- 'components.latLongField.latitudeRequired',
85
- 'en-GB'
86
- ),
87
- 'number.base': tPlugin('components.latLongField.latitudeBase', 'en-GB', {
88
- fieldLabel
89
- }),
90
- 'number.min': latitudeRangeMessage,
91
- 'number.max': latitudeRangeMessage
92
- })
56
+ const { latitudeMin, latitudeMax, longitudeMin, longitudeMax } =
57
+ LatLongField.getMinMax(schema)
93
58
 
94
- const longitudeMessages: LanguageMessages = convertToLanguageMessages({
95
- ...customValidationMessages,
96
- 'any.required': tPlugin(
97
- 'components.latLongField.longitudeRequired',
59
+ const { latitudeMessages, longitudeMessages } =
60
+ LatLongField.buildErrorMessages(
61
+ this.label,
62
+ latitudeMin,
63
+ latitudeMax,
64
+ longitudeMin,
65
+ longitudeMax,
98
66
  'en-GB'
99
- ),
100
- 'number.base': tPlugin('components.latLongField.longitudeBase', 'en-GB', {
101
- fieldLabel
102
- }),
103
- 'number.min': longitudeRangeMessage,
104
- 'number.max': longitudeRangeMessage
105
- })
67
+ )
106
68
 
107
69
  this.collection = new ComponentCollection(
108
70
  [
@@ -159,14 +121,16 @@ export class LatLongField extends FormComponent {
159
121
 
160
122
  getDisplayStringFromFormValue(
161
123
  value: LatLongState | undefined,
162
- _translator: Translator
124
+ translator: Translator
163
125
  ): string {
164
126
  if (!value) {
165
127
  return ''
166
128
  }
167
129
 
168
- // CYA page format: <<latvalue, langvalue>>
169
- return `${value.latitude}, ${value.longitude}`
130
+ const { t } = translator
131
+
132
+ // Output format: Latitude: <<entry>>\nLongitude: <<entry>>
133
+ return `${t('components.latLongField.latitude')}: ${value.latitude}\n${t('components.latLongField.longitude')}: ${value.longitude}`
170
134
  }
171
135
 
172
136
  getDisplayStringFromState(
@@ -183,8 +147,7 @@ export class LatLongField extends FormComponent {
183
147
  return null
184
148
  }
185
149
 
186
- // Output format: Latitude: <<entry>>\nLongitude: <<entry>>
187
- return `Latitude: ${value.latitude}\nLongitude: ${value.longitude}`
150
+ return `${value.latitude}, ${value.longitude}`
188
151
  }
189
152
 
190
153
  getContextValueFromState(state: FormSubmissionState) {
@@ -210,6 +173,26 @@ export class LatLongField extends FormComponent {
210
173
  return LatLongField.isLatLong(value)
211
174
  }
212
175
 
176
+ getValidationMessagesOverride(translator: Translator) {
177
+ const def = this.def as LatLongFieldComponent
178
+ const translatedLabel = getTranslatedLabel(def, translator)
179
+ const { latitudeMin, latitudeMax, longitudeMin, longitudeMax } =
180
+ LatLongField.getMinMax(def.schema)
181
+ const { latitudeMessages, longitudeMessages } =
182
+ LatLongField.buildErrorMessages(
183
+ translatedLabel,
184
+ latitudeMin,
185
+ latitudeMax,
186
+ longitudeMin,
187
+ longitudeMax,
188
+ translator.language
189
+ )
190
+ return {
191
+ [`${this.name}__latitude`]: latitudeMessages,
192
+ [`${this.name}__longitude`]: longitudeMessages
193
+ }
194
+ }
195
+
213
196
  /**
214
197
  * For error preview page that shows all possible errors on a component
215
198
  */
@@ -273,4 +256,93 @@ export class LatLongField extends FormComponent {
273
256
  NumberField.isNumber(value.longitude)
274
257
  )
275
258
  }
259
+
260
+ static buildErrorMessages(
261
+ label: string,
262
+ latitudeMin: number,
263
+ latitudeMax: number,
264
+ longitudeMin: number,
265
+ longitudeMax: number,
266
+ language: string
267
+ ) {
268
+ const fieldLabel = lowerFirst(label)
269
+
270
+ const customValidationMessages: LanguageMessages =
271
+ convertToLanguageMessages({
272
+ 'number.precision': tPlugin(
273
+ 'components.latLongField.precision',
274
+ language
275
+ ),
276
+ 'number.unsafe': tPlugin('components.latLongField.notANumber', language)
277
+ })
278
+
279
+ const latitudeRangeMessage = tPlugin(
280
+ 'components.latLongField.latitudeRange',
281
+ language,
282
+ { fieldLabel, min: latitudeMin, max: latitudeMax }
283
+ )
284
+ const longitudeRangeMessage = tPlugin(
285
+ 'components.latLongField.longitudeRange',
286
+ language,
287
+ { fieldLabel, min: longitudeMin, max: longitudeMax }
288
+ )
289
+
290
+ const latitudeMessages: LanguageMessages = convertToLanguageMessages({
291
+ ...customValidationMessages,
292
+ 'any.required': tPlugin(
293
+ 'components.latLongField.latitudeRequired',
294
+ language
295
+ ),
296
+ 'number.base': tPlugin('components.latLongField.latitudeBase', language, {
297
+ fieldLabel
298
+ }),
299
+ 'number.min': latitudeRangeMessage,
300
+ 'number.max': latitudeRangeMessage
301
+ })
302
+
303
+ const longitudeMessages: LanguageMessages = convertToLanguageMessages({
304
+ ...customValidationMessages,
305
+ 'any.required': tPlugin(
306
+ 'components.latLongField.longitudeRequired',
307
+ language
308
+ ),
309
+ 'number.base': tPlugin(
310
+ 'components.latLongField.longitudeBase',
311
+ language,
312
+ {
313
+ fieldLabel
314
+ }
315
+ ),
316
+ 'number.min': longitudeRangeMessage,
317
+ 'number.max': longitudeRangeMessage
318
+ })
319
+
320
+ return {
321
+ latitudeMessages,
322
+ longitudeMessages
323
+ }
324
+ }
325
+
326
+ // Read schema values from def.schema with fallback defaults
327
+ static getMinMax(
328
+ schema:
329
+ | {
330
+ latitude?: {
331
+ min?: number
332
+ max?: number
333
+ }
334
+ longitude?: {
335
+ min?: number
336
+ max?: number
337
+ }
338
+ }
339
+ | undefined
340
+ ) {
341
+ return {
342
+ latitudeMin: schema?.latitude?.min ?? 49.85,
343
+ latitudeMax: schema?.latitude?.max ?? 60.859,
344
+ longitudeMin: schema?.longitude?.min ?? -13.687,
345
+ longitudeMax: schema?.longitude?.max ?? 1.767
346
+ }
347
+ }
276
348
  }
@@ -11,6 +11,7 @@ import {
11
11
  type RenderContext,
12
12
  type ViewModel
13
13
  } from './types.js'
14
+ import { type Translator } from '../i18n/types.js'
14
15
  import {
15
16
  type FormPayload,
16
17
  type FormSubmissionError,
@@ -29,25 +30,25 @@ export type LocationField =
29
30
  * - "latitude must be..." -> "latitude must be..."
30
31
  */
31
32
  function lowercaseMessageStart(message: string): string {
32
- if (message.startsWith('Enter ')) {
33
- return 'enter' + message.slice(5)
34
- }
35
-
36
- // Lowercase first character for any other message
33
+ // Lowercase first character for a message
37
34
  return message.charAt(0).toLowerCase() + message.slice(1)
38
35
  }
39
36
 
40
- export function joinWithAnd(items: string[]): string {
37
+ export function joinWithAnd(items: string[], translator: Translator): string {
38
+ const { t } = translator
41
39
  if (items.length === 2) {
42
- return `${items[0]} and ${items[1]}`
40
+ return `${items[0]} ${t('common.and')} ${items[1]}`
43
41
  }
44
42
 
45
43
  const leading = items.slice(0, -1).join(', ')
46
44
  const last = items[items.length - 1]
47
- return `${leading} and ${last}`
45
+ return `${leading} ${t('common.and')} ${last}`
48
46
  }
49
47
 
50
- export function formatErrorList(messages: string[]): string {
48
+ export function formatErrorList(
49
+ messages: string[],
50
+ translator: Translator
51
+ ): string {
51
52
  if (!messages.length) {
52
53
  return ''
53
54
  }
@@ -60,7 +61,7 @@ export function formatErrorList(messages: string[]): string {
60
61
  index === 0 ? msg : lowercaseMessageStart(msg)
61
62
  )
62
63
 
63
- return joinWithAnd(formattedMessages)
64
+ return joinWithAnd(formattedMessages, translator)
64
65
  }
65
66
 
66
67
  export function mergeCssClasses(...classNames: (string | undefined)[]) {
@@ -172,7 +173,9 @@ export function getLocationFieldViewModel(
172
173
  if (hasFieldErrors) {
173
174
  viewModel.errorMessage = {
174
175
  text:
175
- fieldErrors.length === 1 ? fieldErrors[0] : formatErrorList(fieldErrors)
176
+ fieldErrors.length === 1
177
+ ? fieldErrors[0]
178
+ : formatErrorList(fieldErrors, context.translator)
176
179
  }
177
180
  }
178
181
 
@@ -30,6 +30,7 @@ import {
30
30
  type FormStateValue,
31
31
  type FormSubmissionState
32
32
  } from '../types.js'
33
+ import { formatDateForLanguage } from '../../payment/helper.js'
33
34
  import { convertToLanguageMessages } from '../../../utils/type-utils.js'
34
35
 
35
36
  export class MonthYearField extends FormComponent {
@@ -105,16 +106,18 @@ export class MonthYearField extends FormComponent {
105
106
 
106
107
  getDisplayStringFromFormValue(
107
108
  value: MonthYearState | undefined,
108
- _translator: Translator
109
+ translator: Translator
109
110
  ): string {
110
111
  if (!value) {
111
112
  return ''
112
113
  }
113
114
 
114
115
  const date = new Date()
116
+ // Force to first day of month in case current date was say 31st and specified month has fewer than 31 days
117
+ date.setDate(1)
115
118
  date.setMonth(value.month - 1)
116
119
 
117
- const monthString = date.toLocaleString('default', { month: 'long' })
120
+ const monthString = formatDateForLanguage(date, 'LLLL', translator.language)
118
121
  return `${monthString} ${value.year}`
119
122
  }
120
123
 
@@ -155,15 +158,17 @@ export class MonthYearField extends FormComponent {
155
158
 
156
159
  getValidationMessagesOverride(translator: Translator) {
157
160
  const { t } = translator
158
- return convertToLanguageMessages({
159
- 'any.required': buildValidationMessages(t).objectMissing,
160
- 'number.base': buildValidationMessages(t).objectMissing,
161
- 'number.precision': buildValidationMessages(t).dateFormat,
162
- 'number.integer': buildValidationMessages(t).dateFormat,
163
- 'number.unsafe': buildValidationMessages(t).dateFormat,
164
- 'number.min': buildValidationMessages(t).dateFormat,
165
- 'number.max': buildValidationMessages(t).dateFormat
166
- })
161
+ return {
162
+ [this.name]: convertToLanguageMessages({
163
+ 'any.required': buildValidationMessages(t).objectMissing,
164
+ 'number.base': buildValidationMessages(t).objectMissing,
165
+ 'number.precision': buildValidationMessages(t).dateFormat,
166
+ 'number.integer': buildValidationMessages(t).dateFormat,
167
+ 'number.unsafe': buildValidationMessages(t).dateFormat,
168
+ 'number.min': buildValidationMessages(t).dateFormat,
169
+ 'number.max': buildValidationMessages(t).dateFormat
170
+ })
171
+ }
167
172
  }
168
173
 
169
174
  getViewModel(context: RenderContext) {