@defra/forms-engine-plugin 5.0.0-alpha.1 → 5.0.0-alpha.11

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 (301) hide show
  1. package/.public/javascripts/application.min.js +1 -1
  2. package/.public/javascripts/application.min.js.LICENSE.txt +7 -0
  3. package/.public/javascripts/application.min.js.map +1 -1
  4. package/.public/javascripts/shared.min.js +1 -1
  5. package/.public/javascripts/shared.min.js.LICENSE.txt +7 -0
  6. package/.public/javascripts/shared.min.js.map +1 -1
  7. package/.public/stylesheets/application.min.css.map +1 -1
  8. package/.server/client/javascripts/file-upload.js +6 -3
  9. package/.server/client/javascripts/file-upload.js.map +1 -1
  10. package/.server/client/javascripts/geospatial-map.js +10 -31
  11. package/.server/client/javascripts/geospatial-map.js.map +1 -1
  12. package/.server/client/javascripts/govuk.js +2 -1
  13. package/.server/client/javascripts/govuk.js.map +1 -1
  14. package/.server/client/javascripts/location-map.js +11 -2
  15. package/.server/client/javascripts/location-map.js.map +1 -1
  16. package/.server/client/javascripts/map.d.ts +6 -0
  17. package/.server/client/javascripts/map.js +37 -0
  18. package/.server/client/javascripts/map.js.map +1 -1
  19. package/.server/client/javascripts/maps.d.ts +5 -0
  20. package/.server/client/javascripts/maps.js +6 -0
  21. package/.server/client/javascripts/maps.js.map +1 -0
  22. package/.server/client/javascripts/shared.d.ts +1 -3
  23. package/.server/client/javascripts/shared.js +1 -3
  24. package/.server/client/javascripts/shared.js.map +1 -1
  25. package/.server/client/javascripts/sssi-dataset.d.ts +18 -20
  26. package/.server/client/javascripts/sssi-dataset.js +37 -39
  27. package/.server/client/javascripts/sssi-dataset.js.map +1 -1
  28. package/.server/client/stylesheets/maps.scss +4 -0
  29. package/.server/client/stylesheets/shared.scss +1 -5
  30. package/.server/server/constants.d.ts +2 -0
  31. package/.server/server/constants.js +2 -0
  32. package/.server/server/constants.js.map +1 -1
  33. package/.server/server/plugins/engine/components/AutocompleteField.d.ts +9 -3
  34. package/.server/server/plugins/engine/components/AutocompleteField.js +12 -0
  35. package/.server/server/plugins/engine/components/AutocompleteField.js.map +1 -1
  36. package/.server/server/plugins/engine/components/CheckboxesField.d.ts +4 -1
  37. package/.server/server/plugins/engine/components/CheckboxesField.js +13 -0
  38. package/.server/server/plugins/engine/components/CheckboxesField.js.map +1 -1
  39. package/.server/server/plugins/engine/components/ComponentBase.d.ts +1 -0
  40. package/.server/server/plugins/engine/components/ComponentBase.js +2 -0
  41. package/.server/server/plugins/engine/components/ComponentBase.js.map +1 -1
  42. package/.server/server/plugins/engine/components/ComponentCollection.js +11 -6
  43. package/.server/server/plugins/engine/components/ComponentCollection.js.map +1 -1
  44. package/.server/server/plugins/engine/components/DatePartsField.d.ts +6 -3
  45. package/.server/server/plugins/engine/components/DatePartsField.js +12 -7
  46. package/.server/server/plugins/engine/components/DatePartsField.js.map +1 -1
  47. package/.server/server/plugins/engine/components/DeclarationField.d.ts +5 -2
  48. package/.server/server/plugins/engine/components/DeclarationField.js +6 -4
  49. package/.server/server/plugins/engine/components/DeclarationField.js.map +1 -1
  50. package/.server/server/plugins/engine/components/Details.d.ts +1 -0
  51. package/.server/server/plugins/engine/components/EastingNorthingField.d.ts +29 -2
  52. package/.server/server/plugins/engine/components/EastingNorthingField.js +110 -54
  53. package/.server/server/plugins/engine/components/EastingNorthingField.js.map +1 -1
  54. package/.server/server/plugins/engine/components/EmailAddressField.d.ts +4 -1
  55. package/.server/server/plugins/engine/components/EmailAddressField.js +2 -1
  56. package/.server/server/plugins/engine/components/EmailAddressField.js.map +1 -1
  57. package/.server/server/plugins/engine/components/FileUploadField.d.ts +12 -0
  58. package/.server/server/plugins/engine/components/FileUploadField.js +23 -3
  59. package/.server/server/plugins/engine/components/FileUploadField.js.map +1 -1
  60. package/.server/server/plugins/engine/components/FormComponent.d.ts +5 -1
  61. package/.server/server/plugins/engine/components/FormComponent.js +5 -2
  62. package/.server/server/plugins/engine/components/FormComponent.js.map +1 -1
  63. package/.server/server/plugins/engine/components/GeospatialField.d.ts +1 -0
  64. package/.server/server/plugins/engine/components/GeospatialField.js +1 -1
  65. package/.server/server/plugins/engine/components/GeospatialField.js.map +1 -1
  66. package/.server/server/plugins/engine/components/Html.d.ts +1 -0
  67. package/.server/server/plugins/engine/components/InsetText.d.ts +1 -0
  68. package/.server/server/plugins/engine/components/LatLongField.d.ts +28 -2
  69. package/.server/server/plugins/engine/components/LatLongField.js +85 -48
  70. package/.server/server/plugins/engine/components/LatLongField.js.map +1 -1
  71. package/.server/server/plugins/engine/components/List.d.ts +1 -0
  72. package/.server/server/plugins/engine/components/ListFormComponent.d.ts +1 -0
  73. package/.server/server/plugins/engine/components/LocationFieldBase.d.ts +8 -2
  74. package/.server/server/plugins/engine/components/LocationFieldBase.js +18 -5
  75. package/.server/server/plugins/engine/components/LocationFieldBase.js.map +1 -1
  76. package/.server/server/plugins/engine/components/LocationFieldHelpers.d.ts +8 -3
  77. package/.server/server/plugins/engine/components/LocationFieldHelpers.js +13 -12
  78. package/.server/server/plugins/engine/components/LocationFieldHelpers.js.map +1 -1
  79. package/.server/server/plugins/engine/components/Markdown.d.ts +1 -0
  80. package/.server/server/plugins/engine/components/Markdown.js +1 -1
  81. package/.server/server/plugins/engine/components/Markdown.js.map +1 -1
  82. package/.server/server/plugins/engine/components/MonthYearField.d.ts +5 -2
  83. package/.server/server/plugins/engine/components/MonthYearField.js +13 -9
  84. package/.server/server/plugins/engine/components/MonthYearField.js.map +1 -1
  85. package/.server/server/plugins/engine/components/MultilineTextField.d.ts +1 -0
  86. package/.server/server/plugins/engine/components/NationalGridFieldNumberField.d.ts +2 -1
  87. package/.server/server/plugins/engine/components/NationalGridFieldNumberField.js +4 -3
  88. package/.server/server/plugins/engine/components/NationalGridFieldNumberField.js.map +1 -1
  89. package/.server/server/plugins/engine/components/NotificationBanner.d.ts +1 -0
  90. package/.server/server/plugins/engine/components/NumberField.d.ts +5 -0
  91. package/.server/server/plugins/engine/components/NumberField.js +16 -0
  92. package/.server/server/plugins/engine/components/NumberField.js.map +1 -1
  93. package/.server/server/plugins/engine/components/OsGridRefField.d.ts +2 -1
  94. package/.server/server/plugins/engine/components/OsGridRefField.js +4 -3
  95. package/.server/server/plugins/engine/components/OsGridRefField.js.map +1 -1
  96. package/.server/server/plugins/engine/components/PaymentField.d.ts +3 -0
  97. package/.server/server/plugins/engine/components/PaymentField.js +17 -6
  98. package/.server/server/plugins/engine/components/PaymentField.js.map +1 -1
  99. package/.server/server/plugins/engine/components/SelectField.d.ts +1 -0
  100. package/.server/server/plugins/engine/components/SelectionControlField.d.ts +1 -0
  101. package/.server/server/plugins/engine/components/TelephoneNumberField.d.ts +1 -0
  102. package/.server/server/plugins/engine/components/UkAddressField.d.ts +1 -0
  103. package/.server/server/plugins/engine/components/UkAddressField.js +23 -8
  104. package/.server/server/plugins/engine/components/UkAddressField.js.map +1 -1
  105. package/.server/server/plugins/engine/components/YesNoField.d.ts +5 -2
  106. package/.server/server/plugins/engine/components/YesNoField.js +17 -8
  107. package/.server/server/plugins/engine/components/YesNoField.js.map +1 -1
  108. package/.server/server/plugins/engine/components/helpers/components.js +1 -2
  109. package/.server/server/plugins/engine/components/helpers/components.js.map +1 -1
  110. package/.server/server/plugins/engine/components/helpers/geospatial.d.ts +3 -1
  111. package/.server/server/plugins/engine/components/helpers/geospatial.js +32 -0
  112. package/.server/server/plugins/engine/components/helpers/geospatial.js.map +1 -1
  113. package/.server/server/plugins/engine/components/helpers/index.d.ts +7 -0
  114. package/.server/server/plugins/engine/components/helpers/index.js +13 -1
  115. package/.server/server/plugins/engine/components/helpers/index.js.map +1 -1
  116. package/.server/server/plugins/engine/components/types.d.ts +1 -0
  117. package/.server/server/plugins/engine/components/types.js.map +1 -1
  118. package/.server/server/plugins/engine/helpers.d.ts +22 -2
  119. package/.server/server/plugins/engine/helpers.js +44 -5
  120. package/.server/server/plugins/engine/helpers.js.map +1 -1
  121. package/.server/server/plugins/engine/i18n/buildValidationMessages.d.ts +3 -0
  122. package/.server/server/plugins/engine/i18n/buildValidationMessages.js +10 -7
  123. package/.server/server/plugins/engine/i18n/buildValidationMessages.js.map +1 -1
  124. package/.server/server/plugins/engine/i18n/createFormTranslator.d.ts +4 -4
  125. package/.server/server/plugins/engine/i18n/createFormTranslator.js +33 -4
  126. package/.server/server/plugins/engine/i18n/createFormTranslator.js.map +1 -1
  127. package/.server/server/plugins/engine/i18n/extractBaseTranslations.d.ts +1 -1
  128. package/.server/server/plugins/engine/i18n/extractBaseTranslations.js +9 -0
  129. package/.server/server/plugins/engine/i18n/extractBaseTranslations.js.map +1 -1
  130. package/.server/server/plugins/engine/i18n/form.d.ts +13 -0
  131. package/.server/server/plugins/engine/i18n/form.js +35 -0
  132. package/.server/server/plugins/engine/i18n/form.js.map +1 -0
  133. package/.server/server/plugins/engine/i18n/languages.d.ts +10 -0
  134. package/.server/server/plugins/engine/i18n/languages.js +19 -0
  135. package/.server/server/plugins/engine/i18n/languages.js.map +1 -0
  136. package/.server/server/plugins/engine/i18n/translations/cy.json +85 -8
  137. package/.server/server/plugins/engine/i18n/translations/en-GB.json +55 -6
  138. package/.server/server/plugins/engine/i18n/types.d.ts +2 -0
  139. package/.server/server/plugins/engine/i18n/types.js.map +1 -1
  140. package/.server/server/plugins/engine/index.d.ts +4 -0
  141. package/.server/server/plugins/engine/index.js +4 -0
  142. package/.server/server/plugins/engine/index.js.map +1 -1
  143. package/.server/server/plugins/engine/models/FormModel.d.ts +0 -4
  144. package/.server/server/plugins/engine/models/FormModel.js +9 -15
  145. package/.server/server/plugins/engine/models/FormModel.js.map +1 -1
  146. package/.server/server/plugins/engine/models/SummaryViewModel.js +9 -4
  147. package/.server/server/plugins/engine/models/SummaryViewModel.js.map +1 -1
  148. package/.server/server/plugins/engine/outputFormatters/adapter/v1.js +3 -5
  149. package/.server/server/plugins/engine/outputFormatters/adapter/v1.js.map +1 -1
  150. package/.server/server/plugins/engine/outputFormatters/human/v1.js +2 -1
  151. package/.server/server/plugins/engine/outputFormatters/human/v1.js.map +1 -1
  152. package/.server/server/plugins/engine/outputFormatters/machine/v2.d.ts +2 -0
  153. package/.server/server/plugins/engine/outputFormatters/machine/v2.js +2 -0
  154. package/.server/server/plugins/engine/outputFormatters/machine/v2.js.map +1 -1
  155. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.d.ts +7 -1
  156. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js +56 -10
  157. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js.map +1 -1
  158. package/.server/server/plugins/engine/pageControllers/QuestionPageController.d.ts +1 -1
  159. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js +7 -3
  160. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -1
  161. package/.server/server/plugins/engine/pageControllers/RepeatPageController.js +3 -4
  162. package/.server/server/plugins/engine/pageControllers/RepeatPageController.js.map +1 -1
  163. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js +10 -4
  164. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js.map +1 -1
  165. package/.server/server/plugins/engine/pageControllers/helpers/submission.d.ts +1 -1
  166. package/.server/server/plugins/engine/pageControllers/helpers/submission.js +3 -3
  167. package/.server/server/plugins/engine/pageControllers/helpers/submission.js.map +1 -1
  168. package/.server/server/plugins/engine/pageControllers/validationOptions.js +23 -2
  169. package/.server/server/plugins/engine/pageControllers/validationOptions.js.map +1 -1
  170. package/.server/server/plugins/engine/types/schema.js +1 -0
  171. package/.server/server/plugins/engine/types/schema.js.map +1 -1
  172. package/.server/server/plugins/engine/types.d.ts +5 -0
  173. package/.server/server/plugins/engine/types.js.map +1 -1
  174. package/.server/server/plugins/engine/views/components/_location-field-base.html +2 -1
  175. package/.server/server/plugins/engine/views/components/fileuploadfield.html +1 -1
  176. package/.server/server/plugins/engine/views/components/multilinetextfield.html +12 -1
  177. package/.server/server/plugins/engine/views/components/paymentfield.html +1 -1
  178. package/.server/server/plugins/engine/views/components/ukaddressfield.html +1 -1
  179. package/.server/server/plugins/engine/views/file-upload.html +1 -1
  180. package/.server/server/plugins/engine/views/partials/preview-banner.html +3 -1
  181. package/.server/server/plugins/nunjucks/filters/applyUrlParam.d.ts +9 -0
  182. package/.server/server/plugins/nunjucks/filters/applyUrlParam.js +17 -0
  183. package/.server/server/plugins/nunjucks/filters/applyUrlParam.js.map +1 -0
  184. package/.server/server/plugins/nunjucks/filters/applyUrlParam.test.js +17 -0
  185. package/.server/server/plugins/nunjucks/filters/applyUrlParam.test.js.map +1 -0
  186. package/.server/server/plugins/nunjucks/filters/field.d.ts +1 -1
  187. package/.server/server/plugins/nunjucks/filters/index.d.ts +1 -0
  188. package/.server/server/plugins/nunjucks/filters/index.js +1 -0
  189. package/.server/server/plugins/nunjucks/filters/index.js.map +1 -1
  190. package/.server/server/plugins/payment/helper.d.ts +11 -1
  191. package/.server/server/plugins/payment/helper.js +18 -2
  192. package/.server/server/plugins/payment/helper.js.map +1 -1
  193. package/.server/server/plugins/payment/helper.test.js +2 -1
  194. package/.server/server/plugins/payment/helper.test.js.map +1 -1
  195. package/.server/server/plugins/payment/service.d.ts +2 -1
  196. package/.server/server/plugins/payment/service.js +5 -2
  197. package/.server/server/plugins/payment/service.js.map +1 -1
  198. package/.server/server/plugins/payment/service.test.js +7 -3
  199. package/.server/server/plugins/payment/service.test.js.map +1 -1
  200. package/.server/server/plugins/payment/types.d.ts +4 -0
  201. package/.server/server/plugins/payment/types.js +1 -0
  202. package/.server/server/plugins/payment/types.js.map +1 -1
  203. package/.server/server/plugins/postcode-lookup/models/index.d.ts +249 -3
  204. package/.server/server/plugins/postcode-lookup/models/index.js +87 -33
  205. package/.server/server/plugins/postcode-lookup/models/index.js.map +1 -1
  206. package/.server/server/plugins/postcode-lookup/routes/index.d.ts +15 -1
  207. package/.server/server/plugins/postcode-lookup/routes/index.js +47 -15
  208. package/.server/server/plugins/postcode-lookup/routes/index.js.map +1 -1
  209. package/.server/server/plugins/postcode-lookup/routes/index.test.js +84 -0
  210. package/.server/server/plugins/postcode-lookup/routes/index.test.js.map +1 -0
  211. package/.server/server/plugins/postcode-lookup/types.d.ts +12 -0
  212. package/.server/server/plugins/postcode-lookup/types.js +6 -1
  213. package/.server/server/plugins/postcode-lookup/types.js.map +1 -1
  214. package/.server/server/plugins/postcode-lookup/views/postcode-lookup-details.html +3 -3
  215. package/.server/server/utils/type-utils.d.ts +1 -2
  216. package/.server/server/utils/type-utils.js +0 -3
  217. package/.server/server/utils/type-utils.js.map +1 -1
  218. package/.server/server/utils/utils.d.ts +6 -0
  219. package/.server/server/utils/utils.js +18 -1
  220. package/.server/server/utils/utils.js.map +1 -1
  221. package/.server/server/utils/utils.test.js +21 -1
  222. package/.server/server/utils/utils.test.js.map +1 -1
  223. package/package.json +9 -2
  224. package/src/client/javascripts/file-upload.js +11 -3
  225. package/src/client/javascripts/geospatial-map.js +7 -34
  226. package/src/client/javascripts/govuk.js +2 -0
  227. package/src/client/javascripts/location-map.js +9 -1
  228. package/src/client/javascripts/map.js +42 -0
  229. package/src/client/javascripts/maps.js +5 -0
  230. package/src/client/javascripts/shared.js +1 -3
  231. package/src/client/javascripts/sssi-dataset.js +39 -41
  232. package/src/client/stylesheets/maps.scss +4 -0
  233. package/src/client/stylesheets/shared.scss +1 -5
  234. package/src/server/constants.js +2 -0
  235. package/src/server/plugins/engine/components/AutocompleteField.ts +13 -0
  236. package/src/server/plugins/engine/components/CheckboxesField.ts +13 -1
  237. package/src/server/plugins/engine/components/ComponentBase.ts +2 -0
  238. package/src/server/plugins/engine/components/ComponentCollection.ts +16 -11
  239. package/src/server/plugins/engine/components/DatePartsField.ts +20 -9
  240. package/src/server/plugins/engine/components/DeclarationField.ts +7 -6
  241. package/src/server/plugins/engine/components/EastingNorthingField.ts +185 -89
  242. package/src/server/plugins/engine/components/EmailAddressField.ts +2 -1
  243. package/src/server/plugins/engine/components/FileUploadField.ts +24 -3
  244. package/src/server/plugins/engine/components/FormComponent.ts +6 -5
  245. package/src/server/plugins/engine/components/GeospatialField.ts +6 -1
  246. package/src/server/plugins/engine/components/LatLongField.ts +128 -58
  247. package/src/server/plugins/engine/components/LocationFieldBase.ts +23 -6
  248. package/src/server/plugins/engine/components/LocationFieldHelpers.ts +20 -13
  249. package/src/server/plugins/engine/components/Markdown.ts +2 -3
  250. package/src/server/plugins/engine/components/MonthYearField.ts +20 -15
  251. package/src/server/plugins/engine/components/NationalGridFieldNumberField.ts +9 -3
  252. package/src/server/plugins/engine/components/NumberField.ts +21 -1
  253. package/src/server/plugins/engine/components/OsGridRefField.ts +9 -3
  254. package/src/server/plugins/engine/components/PaymentField.ts +15 -5
  255. package/src/server/plugins/engine/components/UkAddressField.ts +30 -7
  256. package/src/server/plugins/engine/components/YesNoField.ts +17 -9
  257. package/src/server/plugins/engine/components/helpers/components.ts +1 -2
  258. package/src/server/plugins/engine/components/helpers/geospatial.ts +53 -0
  259. package/src/server/plugins/engine/components/helpers/index.ts +19 -0
  260. package/src/server/plugins/engine/components/types.ts +1 -0
  261. package/src/server/plugins/engine/helpers.ts +52 -5
  262. package/src/server/plugins/engine/i18n/buildValidationMessages.ts +10 -7
  263. package/src/server/plugins/engine/i18n/createFormTranslator.ts +62 -11
  264. package/src/server/plugins/engine/i18n/extractBaseTranslations.ts +7 -1
  265. package/src/server/plugins/engine/i18n/form.ts +54 -0
  266. package/src/server/plugins/engine/i18n/languages.ts +19 -0
  267. package/src/server/plugins/engine/i18n/translations/cy.json +85 -8
  268. package/src/server/plugins/engine/i18n/translations/en-GB.json +55 -6
  269. package/src/server/plugins/engine/i18n/types.ts +2 -0
  270. package/src/server/plugins/engine/index.ts +7 -0
  271. package/src/server/plugins/engine/models/FormModel.ts +9 -15
  272. package/src/server/plugins/engine/models/SummaryViewModel.ts +6 -5
  273. package/src/server/plugins/engine/outputFormatters/adapter/v1.ts +3 -6
  274. package/src/server/plugins/engine/outputFormatters/human/v1.ts +2 -1
  275. package/src/server/plugins/engine/outputFormatters/machine/v2.ts +3 -0
  276. package/src/server/plugins/engine/pageControllers/FileUploadPageController.ts +60 -9
  277. package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +10 -5
  278. package/src/server/plugins/engine/pageControllers/RepeatPageController.ts +2 -2
  279. package/src/server/plugins/engine/pageControllers/SummaryPageController.ts +15 -4
  280. package/src/server/plugins/engine/pageControllers/helpers/submission.ts +6 -3
  281. package/src/server/plugins/engine/pageControllers/validationOptions.ts +27 -2
  282. package/src/server/plugins/engine/types/schema.ts +1 -0
  283. package/src/server/plugins/engine/types.ts +5 -0
  284. package/src/server/plugins/engine/views/components/_location-field-base.html +2 -1
  285. package/src/server/plugins/engine/views/components/fileuploadfield.html +1 -1
  286. package/src/server/plugins/engine/views/components/multilinetextfield.html +12 -1
  287. package/src/server/plugins/engine/views/components/paymentfield.html +1 -1
  288. package/src/server/plugins/engine/views/components/ukaddressfield.html +1 -1
  289. package/src/server/plugins/engine/views/file-upload.html +1 -1
  290. package/src/server/plugins/engine/views/partials/preview-banner.html +3 -1
  291. package/src/server/plugins/nunjucks/filters/applyUrlParam.js +17 -0
  292. package/src/server/plugins/nunjucks/filters/index.js +1 -0
  293. package/src/server/plugins/payment/helper.js +22 -2
  294. package/src/server/plugins/payment/service.js +6 -2
  295. package/src/server/plugins/payment/types.js +1 -0
  296. package/src/server/plugins/postcode-lookup/models/index.js +75 -43
  297. package/src/server/plugins/postcode-lookup/routes/index.js +73 -16
  298. package/src/server/plugins/postcode-lookup/types.js +6 -1
  299. package/src/server/plugins/postcode-lookup/views/postcode-lookup-details.html +3 -3
  300. package/src/server/utils/type-utils.ts +1 -11
  301. package/src/server/utils/utils.js +19 -1
@@ -1,4 +1,9 @@
1
- import { ComponentType, type UkAddressFieldComponent } from '@defra/forms-model'
1
+ import {
2
+ ComponentType,
3
+ FormStatus,
4
+ type FormMetadata,
5
+ type UkAddressFieldComponent
6
+ } from '@defra/forms-model'
2
7
  import { type ObjectSchema } from 'joi'
3
8
  import lowerFirst from 'lodash/lowerFirst.js'
4
9
 
@@ -9,6 +14,8 @@ import {
9
14
  } from './FormComponent.js'
10
15
  import { TextField } from './TextField.js'
11
16
  import { type RenderContext } from './types.js'
17
+ import { getCachedPluginTranslator } from '../i18n/form.js'
18
+ import { getAvailableLanguages } from '../i18n/languages.js'
12
19
  import { type Translator } from '../i18n/types.js'
13
20
  import {
14
21
  type FormRequestPayload,
@@ -207,7 +214,7 @@ export class UkAddressField extends FormComponent {
207
214
 
208
215
  /**
209
216
  * For screen readers, only hide legend visually. This can be overridden
210
- * by single component {@link QuestionPageController | `showTitle` handling}
217
+ * by single component (see QuestionPageController and showTitle handling)
211
218
  */
212
219
  classes: options.hideTitle
213
220
  ? 'govuk-visually-hidden'
@@ -290,7 +297,6 @@ export class UkAddressField extends FormComponent {
290
297
  )
291
298
  }
292
299
 
293
- // eslint-disable-next-line @typescript-eslint/require-await
294
300
  static async dispatcher(
295
301
  request: FormRequestPayload,
296
302
  h: FormResponseToolkit,
@@ -299,14 +305,31 @@ export class UkAddressField extends FormComponent {
299
305
  const { controller, component } = args
300
306
  const language = resolveLanguage(request)
301
307
 
308
+ const availableLanguages = getAvailableLanguages(controller.model.def)
309
+ const languages = availableLanguages.length
310
+ ? { languages: availableLanguages }
311
+ : {}
312
+
313
+ const translator = await getCachedPluginTranslator(
314
+ request,
315
+ { id: controller.model.formId } as unknown as FormMetadata,
316
+ args.isLive ? FormStatus.Live : FormStatus.Draft,
317
+ language
318
+ )
319
+ const { tComponent, tForm } = translator
320
+
302
321
  return dispatch(request, h, {
303
- formName: controller.model.name,
322
+ formName: tForm('title'),
323
+ formId: controller.model.formId,
324
+ formStatus: args.isLive ? FormStatus.Live : FormStatus.Draft,
325
+ componentId: component.id ?? 'unknown',
304
326
  componentName: component.name,
305
- componentHint: component.hint,
306
- componentTitle: component.title || controller.title,
327
+ componentHint: tComponent(component, 'hint'),
328
+ componentTitle: tComponent(component, 'title') || controller.title,
307
329
  step: args.actionArgs.step,
308
330
  sourceUrl: args.sourceUrl,
309
- language
331
+ language,
332
+ ...languages
310
333
  })
311
334
  }
312
335
  }
@@ -4,7 +4,6 @@ import {
4
4
  yesNoListName,
5
5
  type YesNoFieldComponent
6
6
  } from '@defra/forms-model'
7
- import { type LanguageMessages } from 'joi'
8
7
 
9
8
  import { SelectionControlField } from './SelectionControlField.js'
10
9
  import { addClassOptionIfNone } from './helpers/index.js'
@@ -12,7 +11,6 @@ import { buildValidationMessages } from '../i18n/buildValidationMessages.js'
12
11
  import { type Translator } from '../i18n/types.js'
13
12
  import { messageTemplate } from '../pageControllers/validationOptions.js'
14
13
  import { type ErrorMessageTemplateList } from '../types.js'
15
- import { convertToLanguageMessages } from '../../../utils/type-utils.js'
16
14
 
17
15
  /**
18
16
  * @description
@@ -45,21 +43,31 @@ export class YesNoField extends SelectionControlField {
45
43
  formSchema = formSchema.optional()
46
44
  }
47
45
 
48
- formSchema = formSchema.messages(
49
- convertToLanguageMessages({
46
+ if (options.customValidationMessage) {
47
+ const message = options.customValidationMessage
48
+
49
+ formSchema = formSchema.messages({
50
+ 'any.required': message
51
+ })
52
+ } else if (options.customValidationMessages) {
53
+ formSchema = formSchema.messages(options.customValidationMessages)
54
+ } else {
55
+ formSchema = formSchema.messages({
50
56
  'any.required': messageTemplate.selectYesNoRequired
51
57
  })
52
- )
58
+ }
53
59
 
54
60
  this.formSchema = formSchema
55
61
  this.options = options
56
62
  }
57
63
 
58
- getValidationMessagesOverride(translator: Translator): LanguageMessages {
64
+ getValidationMessagesOverride(translator: Translator) {
59
65
  const { selectYesNoRequired } = buildValidationMessages(translator.t)
60
- return convertToLanguageMessages({
61
- 'any.required': selectYesNoRequired
62
- })
66
+ return {
67
+ [this.name]: {
68
+ 'any.required': selectYesNoRequired
69
+ }
70
+ }
63
71
  }
64
72
 
65
73
  /**
@@ -347,8 +347,7 @@ export function getAnswerMarkdown(
347
347
  field instanceof Components.EastingNorthingField ||
348
348
  field instanceof Components.LatLongField
349
349
  ) {
350
- const contextValue = field.getContextValueFromState(state)
351
- answerEscaped = contextValue ? `${contextValue}\n` : ''
350
+ answerEscaped = answer ? `${answer}\n` : ''
352
351
  } else if (field instanceof Components.GeospatialField) {
353
352
  const features = field.getFormValueFromState(state)
354
353
  const value = field.getDisplayStringFromFormValue(features, translator)
@@ -5,8 +5,13 @@ import {
5
5
  } from '@defra/forms-model'
6
6
  import Bourne from '@hapi/bourne'
7
7
  import { booleanWithin } from '@turf/boolean-within'
8
+ import { point } from '@turf/helpers'
9
+ // @ts-expect-error - no types
10
+ import OsGridRef from 'geodesy/osgridref.js'
8
11
  import JoiBase, { type CustomValidator } from 'joi'
9
12
 
13
+ import { type EastingNorthingField } from '../EastingNorthingField.js'
14
+ import { type EastingNorthingState } from '../types.js'
10
15
  import {
11
16
  type Coordinates,
12
17
  type Feature,
@@ -171,3 +176,51 @@ export function getGeospatialSchema(
171
176
  def
172
177
  )
173
178
  }
179
+
180
+ export function getEastingNorthingCountryValidator(
181
+ field: EastingNorthingField,
182
+ country: GeospatialFieldOptionsCountry
183
+ ) {
184
+ const validateCountryBounds: CustomValidator = (value, helpers) => {
185
+ const countryFeature = countries.features.find(
186
+ (feature) => feature.id === country
187
+ )
188
+
189
+ if (!countryFeature) {
190
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
191
+ return value
192
+ }
193
+
194
+ const eastingNorthing = field.getFormValueFromState(
195
+ value as EastingNorthingState
196
+ )
197
+
198
+ if (!eastingNorthing) {
199
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
200
+ return value
201
+ }
202
+
203
+ const gridRef = new OsGridRef(
204
+ eastingNorthing.easting,
205
+ eastingNorthing.northing
206
+ )
207
+ const latLong = gridRef.toLatLon()
208
+ const coord = point([latLong.longitude, latLong.latitude])
209
+ const within = booleanWithin(coord, countryFeature)
210
+
211
+ if (!within) {
212
+ return helpers.error(
213
+ 'any.custom',
214
+ {
215
+ country: countriesDesc[country as GeospatialFieldOptionsCountryEnum]
216
+ },
217
+ { path: [field.name] }
218
+ )
219
+ }
220
+
221
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
222
+ return value
223
+ }
224
+
225
+ return validateCountryBounds
226
+ }
@@ -2,6 +2,8 @@ import { type ComponentDef } from '@defra/forms-model'
2
2
  import joi, { type JoiExpression, type ReferenceOptions } from 'joi'
3
3
  import lowerFirst from 'lodash/lowerFirst.js'
4
4
 
5
+ import { type Translator } from '../../i18n/types.js'
6
+
5
7
  /**
6
8
  * Prevent Markdown formatting
7
9
  * @see {@link https://pandoc.org/chunkedhtml-demo/8.11-backslash-escapes.html}
@@ -66,3 +68,20 @@ export const lowerFirstExpressionOptions = {
66
68
  */
67
69
  export const createLowerFirstExpression = (template: string): JoiExpression =>
68
70
  joi.expression(template, lowerFirstExpressionOptions) as JoiExpression
71
+
72
+ /**
73
+ * Translate a component label, in precedence order of errorDescription, then shortDesciption, then title
74
+ * @param {ComponentDef} component
75
+ * @param {Translator} translator
76
+ */
77
+ export function getTranslatedLabel(
78
+ component: ComponentDef,
79
+ translator: Translator
80
+ ) {
81
+ const { tComponent } = translator
82
+ const translatedLabel =
83
+ tComponent(component, 'errorDescription') ||
84
+ tComponent(component, 'shortDescription') ||
85
+ tComponent(component, 'title')
86
+ return translatedLabel
87
+ }
@@ -124,6 +124,7 @@ export interface ViewModel extends Record<string, unknown> {
124
124
  upload?: {
125
125
  count: number
126
126
  summaryList: SummaryList
127
+ uploadingLabel?: string
127
128
  }
128
129
  }
129
130
 
@@ -8,13 +8,14 @@ import {
8
8
  } from '@defra/forms-model'
9
9
  import Boom from '@hapi/boom'
10
10
  import { type ResponseToolkit, type Server } from '@hapi/hapi'
11
- import { format, parseISO } from 'date-fns'
11
+ import { format, parseISO, type Locale } from 'date-fns'
12
+ import { cy, enGB } from 'date-fns/locale'
12
13
  import { StatusCodes } from 'http-status-codes'
13
14
  import { type Schema, type ValidationErrorItem } from 'joi'
14
15
  import { Liquid } from 'liquidjs'
15
16
 
16
17
  import { logger } from '../../common/helpers/logging/logger.js'
17
- import { FORM_VERSION_METADATA_KEY } from '../../constants.js'
18
+ import { CY, EN_GB, FORM_VERSION_METADATA_KEY } from '../../constants.js'
18
19
  import {
19
20
  getAnswer,
20
21
  type Field
@@ -300,19 +301,24 @@ export function checkEmailAddressForLiveFormSubmission(
300
301
 
301
302
  /**
302
303
  * Parses the errors from {@link Schema.validate} so they can be rendered by govuk-frontend templates
304
+ * @param language - the target language for translations
303
305
  * @param [details] - provided by {@link Schema.validate}
304
306
  */
305
307
  export function getErrors(
308
+ language: string | undefined,
306
309
  details?: ValidationErrorItem[]
307
310
  ): FormSubmissionError[] | undefined {
308
311
  if (!details?.length) {
309
312
  return
310
313
  }
311
314
 
312
- return details.map(getError)
315
+ return details.map((detail) => getError(language, detail))
313
316
  }
314
317
 
315
- export function getError(detail: ValidationErrorItem): FormSubmissionError {
318
+ export function getError(
319
+ language: string | undefined,
320
+ detail: ValidationErrorItem
321
+ ): FormSubmissionError {
316
322
  const { context, message, path } = detail
317
323
 
318
324
  const name = context?.key ?? ''
@@ -320,7 +326,10 @@ export function getError(detail: ValidationErrorItem): FormSubmissionError {
320
326
 
321
327
  const text = message.replace(
322
328
  /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/,
323
- (text) => format(parseISO(text), 'd MMMM yyyy')
329
+ (text) =>
330
+ format(parseISO(text), 'd MMMM yyyy', {
331
+ locale: toLanguageLocale(language)
332
+ })
324
333
  )
325
334
 
326
335
  return {
@@ -410,3 +419,41 @@ export function getFormVersion(
410
419
  | FormVersionMetadata
411
420
  | undefined
412
421
  }
422
+
423
+ const payLanguageCodes = {
424
+ [EN_GB]: 'en',
425
+ [CY]: 'cy'
426
+ } as Record<string, string>
427
+
428
+ const languageLocales = {
429
+ [EN_GB]: enGB,
430
+ [CY]: cy
431
+ } as Record<string, Locale>
432
+
433
+ const defaultLanguageCode = 'en'
434
+ const defaultLanguageLocale = enGB
435
+
436
+ /**
437
+ * Converts a BCP 47 language tag to the ISO 639-1 code GOV.UK Pay accepts.
438
+ * Defaults to English if the language is not supported.
439
+ * @param { string | undefined } language - BCP 47 language code
440
+ * @returns {string} language code required by GOV.UK Pay
441
+ */
442
+ export function toPayLanguageCode(language: string | undefined): string {
443
+ return payLanguageCodes[language ?? 'unknown'] ?? defaultLanguageCode
444
+ }
445
+
446
+ /**
447
+ * Converts a BCP 47 language tag to the date-fns Locale (for date formatting).
448
+ * Defaults to English if the language is not supported.
449
+ * @param { string | undefined } language - BCP 47 language code
450
+ * @returns {Locale} language locale
451
+ */
452
+ export function toLanguageLocale(language: string | undefined): Locale {
453
+ return languageLocales[language ?? 'unknown'] ?? defaultLanguageLocale
454
+ }
455
+
456
+ /**
457
+ * @import { FormsService } from '../../types.js'
458
+ * @import { Locale } from 'date-fns/locale'
459
+ */
@@ -56,13 +56,16 @@ export function buildValidationMessages(t: (key: string) => string) {
56
56
  dateMin: t('components.dateField.validation.dateMin'),
57
57
  dateMax: t('components.dateField.validation.dateMax'),
58
58
 
59
- unicode: 'validation.unicode',
60
- arrayMin: 'validation.array.min',
61
- arrayMax: 'validation.array.max',
62
- arrayLength: 'validation.array.length',
63
- featuresMin: 'validation.features.min',
64
- featuresMax: 'validation.features.max',
65
- featuresLength: 'validation.features.length'
59
+ unicode: t('validation.unicode'),
60
+ arrayMin: t('validation.arrayMin'),
61
+ arrayMax: t('validation.arrayMax'),
62
+ arrayLength: t('validation.arrayLength'),
63
+ featuresMin: t('validation.featuresMin'),
64
+ featuresMax: t('validation.featuresMax'),
65
+ featuresLength: t('validation.featuresLength'),
66
+ filesMin: t('validation.filesMin'),
67
+ filesMax: t('validation.filesMax'),
68
+ filesLength: t('validation.filesLength')
66
69
  }
67
70
  }
68
71
 
@@ -1,26 +1,40 @@
1
- import { type FormDefinition } from '@defra/forms-model'
1
+ import {
2
+ yesNoListNoItemId,
3
+ yesNoListYesItemId,
4
+ type FormDefinition,
5
+ type FormMetadata
6
+ } from '@defra/forms-model'
2
7
  import { type i18n } from 'i18next'
3
8
 
9
+ import { CY } from '../../../constants.js'
4
10
  import { createTranslator } from './createTranslator.js'
5
11
  import { extractBaseTranslations } from './extractBaseTranslations.js'
6
12
  import { createFormI18nInstance } from './index.js'
7
- import {
8
- type FormDefinitionTranslations,
9
- type Translator
10
- } from './types.js'
13
+ import { type FormDefinitionTranslations } from './types.js'
11
14
 
12
15
  export function createFormTranslator(
13
- def: FormDefinition,
16
+ metadata: FormMetadata,
17
+ definition: FormDefinition | undefined,
14
18
  language: string
15
- ): Translator {
16
- const baseTranslations = extractBaseTranslations(def)
19
+ ) {
20
+ const baseTranslations = extractBaseTranslations(definition)
17
21
  const i18nInstance = createFormI18nInstance(baseTranslations)
18
- loadFormTranslations(def, i18nInstance)
22
+ loadFormTranslations(definition, i18nInstance)
23
+ extractMetadataBaseTranslations(metadata, i18nInstance)
24
+
25
+ const translator = createTranslator(i18nInstance, language)
19
26
 
20
- return createTranslator(i18nInstance, language)
27
+ return translator
21
28
  }
22
29
 
23
- export function loadFormTranslations(def: FormDefinition, i18nInstance: i18n) {
30
+ export function loadFormTranslations(
31
+ def: FormDefinition | undefined,
32
+ i18nInstance: i18n
33
+ ) {
34
+ if (!def) {
35
+ return
36
+ }
37
+
24
38
  const formTranslations = def.metadata?.translations as
25
39
  | FormDefinitionTranslations
26
40
  | undefined
@@ -28,6 +42,43 @@ export function loadFormTranslations(def: FormDefinition, i18nInstance: i18n) {
28
42
  if (formTranslations) {
29
43
  for (const [lng, resources] of Object.entries(formTranslations)) {
30
44
  i18nInstance.addResourceBundle(lng, 'form', resources, true, true)
45
+
46
+ // Temporary workaround - until we develop a better solution
47
+ if (lng === CY) {
48
+ i18nInstance.addResource(
49
+ lng,
50
+ 'form',
51
+ `listItems.${yesNoListYesItemId}.text`,
52
+ 'Ie'
53
+ )
54
+ i18nInstance.addResource(
55
+ lng,
56
+ 'form',
57
+ `listItems.${yesNoListNoItemId}.text`,
58
+ 'Nage'
59
+ )
60
+ }
61
+ }
62
+ }
63
+ }
64
+
65
+ export function extractMetadataBaseTranslations(
66
+ metadata: FormMetadata | undefined,
67
+ i18nInstance: i18n
68
+ ) {
69
+ if (metadata) {
70
+ const translations = {
71
+ 'form.title': metadata.title,
72
+ 'form.contact.email.address': metadata.contact?.email?.address ?? '',
73
+ 'form.contact.email.responseTime':
74
+ metadata.contact?.email?.responseTime ?? '',
75
+ 'form.contact.online.url': metadata.contact?.online?.url ?? '',
76
+ 'form.contact.online.text': metadata.contact?.online?.text ?? '',
77
+ 'form.contact.phone': metadata.contact?.phone ?? '',
78
+ 'form.submissionGuidance': metadata.submissionGuidance ?? '',
79
+ 'form.privacyNoticeText': metadata.privacyNoticeText ?? '',
80
+ 'form.privacyNoticeUrl': metadata.privacyNoticeUrl ?? ''
31
81
  }
82
+ i18nInstance.addResourceBundle('en-GB', 'form', translations, true, true)
32
83
  }
33
84
  }
@@ -7,13 +7,19 @@ import { type FormDefinitionTranslations } from './types.js'
7
7
 
8
8
  type BaseTranslations = FormDefinitionTranslations[string]
9
9
 
10
- export function extractBaseTranslations(def: FormDefinition): BaseTranslations {
10
+ export function extractBaseTranslations(
11
+ def: FormDefinition | undefined
12
+ ): BaseTranslations {
11
13
  const pages: BaseTranslations['pages'] = {}
12
14
  const components: BaseTranslations['components'] = {}
13
15
  const sections: BaseTranslations['sections'] = {}
14
16
  const listItems: BaseTranslations['listItems'] = {}
15
17
  const form: BaseTranslations['form'] = {}
16
18
 
19
+ if (!def) {
20
+ return { pages, components, sections, listItems, form }
21
+ }
22
+
17
23
  for (const page of def.pages) {
18
24
  if (page.id && page.title) {
19
25
  pages[page.id] = { title: page.title }
@@ -0,0 +1,54 @@
1
+ import { type FormMetadata, type FormStatus } from '@defra/forms-model'
2
+ import Boom from '@hapi/boom'
3
+ import { LRUCache } from 'lru-cache'
4
+
5
+ import { getPluginOptions } from '../helpers.js'
6
+ import { createFormTranslator } from './createFormTranslator.js'
7
+ import { type Translator } from './types.js'
8
+ import { type AnyFormRequest } from '../types.js'
9
+
10
+ const cache = new LRUCache({
11
+ max: 100,
12
+ ttl: 1000 * 60 * 15 // 15 minutes
13
+ })
14
+
15
+ /**
16
+ * Get translator for plugin, for the current form's metadata (as well as the plugin-specific boilerplate).
17
+ * This is an async call so we can read the form definition inside this call.
18
+ * See docs/features/multi-language.md for the language and translation flow overview.
19
+ * @param {AnyFormRequest} request
20
+ * @param {FormMetadata} metadata - the metadata of the form
21
+ * @param {FormStatus} status - the form status to use when retrieving the definition
22
+ * @param {string} language - the language to use for the translator
23
+ */
24
+ export async function getCachedPluginTranslator(
25
+ request: AnyFormRequest,
26
+ metadata: FormMetadata,
27
+ status: FormStatus,
28
+ language: string
29
+ ) {
30
+ const key = `${metadata.id}-${status}-${language}-plugin-meta`
31
+
32
+ if (cache.has(key)) {
33
+ return cache.get(key) as unknown as Translator
34
+ }
35
+
36
+ const { services } = getPluginOptions(request.server)
37
+
38
+ const definition = await services?.formsService.getFormDefinition(
39
+ metadata.id,
40
+ status
41
+ )
42
+
43
+ if (!definition) {
44
+ throw Boom.notFound(
45
+ `FormDefinition not found for form ${metadata.id} and status ${status}`
46
+ )
47
+ }
48
+
49
+ const translator = createFormTranslator(metadata, definition, language)
50
+
51
+ cache.set(key, translator)
52
+
53
+ return translator
54
+ }
@@ -0,0 +1,19 @@
1
+ import { type FormDefinition } from '@defra/forms-model'
2
+
3
+ /**
4
+ * Return a list of available languages.
5
+ * Empty array if only English, or an array of English and Welsh if translations have been defined.
6
+ * @param {FormDefinition} def
7
+ */
8
+ export function getAvailableLanguages(
9
+ def: FormDefinition
10
+ ): { name: string; code: string }[] {
11
+ // @ts-expect-error - dynamic property
12
+ if (def.metadata?.translations?.cy) {
13
+ return [
14
+ { name: 'English', code: 'en-GB' },
15
+ { name: 'Cymraeg', code: 'cy' }
16
+ ]
17
+ }
18
+ return []
19
+ }