@defra/forms-engine-plugin 5.0.0-alpha.2 → 5.0.0-alpha.21

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 (346) 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.d.ts +153 -5
  11. package/.server/client/javascripts/geospatial-map.js +157 -77
  12. package/.server/client/javascripts/geospatial-map.js.map +1 -1
  13. package/.server/client/javascripts/govuk.js +2 -1
  14. package/.server/client/javascripts/govuk.js.map +1 -1
  15. package/.server/client/javascripts/location-map.d.ts +44 -0
  16. package/.server/client/javascripts/location-map.js +54 -5
  17. package/.server/client/javascripts/location-map.js.map +1 -1
  18. package/.server/client/javascripts/map-constants.d.ts +3 -0
  19. package/.server/client/javascripts/map-constants.js +4 -0
  20. package/.server/client/javascripts/map-constants.js.map +1 -0
  21. package/.server/client/javascripts/map.d.ts +10 -0
  22. package/.server/client/javascripts/map.js +42 -0
  23. package/.server/client/javascripts/map.js.map +1 -1
  24. package/.server/client/javascripts/maps.d.ts +5 -0
  25. package/.server/client/javascripts/maps.js +6 -0
  26. package/.server/client/javascripts/maps.js.map +1 -0
  27. package/.server/client/javascripts/shared.d.ts +1 -4
  28. package/.server/client/javascripts/shared.js +1 -4
  29. package/.server/client/javascripts/shared.js.map +1 -1
  30. package/.server/client/javascripts/sssi-dataset.d.ts +18 -20
  31. package/.server/client/javascripts/sssi-dataset.js +37 -39
  32. package/.server/client/javascripts/sssi-dataset.js.map +1 -1
  33. package/.server/client/stylesheets/maps.scss +4 -0
  34. package/.server/client/stylesheets/shared.scss +1 -5
  35. package/.server/server/constants.d.ts +2 -0
  36. package/.server/server/constants.js +2 -0
  37. package/.server/server/constants.js.map +1 -1
  38. package/.server/server/plugins/engine/__stubs__/definitions.d.ts +29 -0
  39. package/.server/server/plugins/engine/__stubs__/definitions.js +125 -0
  40. package/.server/server/plugins/engine/__stubs__/definitions.js.map +1 -0
  41. package/.server/server/plugins/engine/components/AutocompleteField.d.ts +9 -3
  42. package/.server/server/plugins/engine/components/AutocompleteField.js +12 -0
  43. package/.server/server/plugins/engine/components/AutocompleteField.js.map +1 -1
  44. package/.server/server/plugins/engine/components/CheckboxesField.d.ts +21 -0
  45. package/.server/server/plugins/engine/components/CheckboxesField.js +30 -5
  46. package/.server/server/plugins/engine/components/CheckboxesField.js.map +1 -1
  47. package/.server/server/plugins/engine/components/ComponentBase.d.ts +1 -0
  48. package/.server/server/plugins/engine/components/ComponentBase.js +2 -0
  49. package/.server/server/plugins/engine/components/ComponentBase.js.map +1 -1
  50. package/.server/server/plugins/engine/components/ComponentCollection.js +11 -6
  51. package/.server/server/plugins/engine/components/ComponentCollection.js.map +1 -1
  52. package/.server/server/plugins/engine/components/DatePartsField.d.ts +6 -3
  53. package/.server/server/plugins/engine/components/DatePartsField.js +12 -7
  54. package/.server/server/plugins/engine/components/DatePartsField.js.map +1 -1
  55. package/.server/server/plugins/engine/components/DeclarationField.d.ts +5 -2
  56. package/.server/server/plugins/engine/components/DeclarationField.js +6 -4
  57. package/.server/server/plugins/engine/components/DeclarationField.js.map +1 -1
  58. package/.server/server/plugins/engine/components/Details.d.ts +1 -0
  59. package/.server/server/plugins/engine/components/EastingNorthingField.d.ts +33 -2
  60. package/.server/server/plugins/engine/components/EastingNorthingField.js +110 -54
  61. package/.server/server/plugins/engine/components/EastingNorthingField.js.map +1 -1
  62. package/.server/server/plugins/engine/components/EmailAddressField.d.ts +4 -1
  63. package/.server/server/plugins/engine/components/EmailAddressField.js +2 -1
  64. package/.server/server/plugins/engine/components/EmailAddressField.js.map +1 -1
  65. package/.server/server/plugins/engine/components/FileUploadField.d.ts +21 -0
  66. package/.server/server/plugins/engine/components/FileUploadField.js +35 -3
  67. package/.server/server/plugins/engine/components/FileUploadField.js.map +1 -1
  68. package/.server/server/plugins/engine/components/FormComponent.d.ts +5 -1
  69. package/.server/server/plugins/engine/components/FormComponent.js +5 -2
  70. package/.server/server/plugins/engine/components/FormComponent.js.map +1 -1
  71. package/.server/server/plugins/engine/components/GeospatialField.d.ts +23 -0
  72. package/.server/server/plugins/engine/components/GeospatialField.js +33 -9
  73. package/.server/server/plugins/engine/components/GeospatialField.js.map +1 -1
  74. package/.server/server/plugins/engine/components/Html.d.ts +1 -0
  75. package/.server/server/plugins/engine/components/InsetText.d.ts +1 -0
  76. package/.server/server/plugins/engine/components/LatLongField.d.ts +32 -2
  77. package/.server/server/plugins/engine/components/LatLongField.js +85 -48
  78. package/.server/server/plugins/engine/components/LatLongField.js.map +1 -1
  79. package/.server/server/plugins/engine/components/List.d.ts +1 -0
  80. package/.server/server/plugins/engine/components/ListFormComponent.d.ts +1 -0
  81. package/.server/server/plugins/engine/components/LocationFieldBase.d.ts +8 -2
  82. package/.server/server/plugins/engine/components/LocationFieldBase.js +25 -6
  83. package/.server/server/plugins/engine/components/LocationFieldBase.js.map +1 -1
  84. package/.server/server/plugins/engine/components/LocationFieldHelpers.d.ts +12 -3
  85. package/.server/server/plugins/engine/components/LocationFieldHelpers.js +21 -13
  86. package/.server/server/plugins/engine/components/LocationFieldHelpers.js.map +1 -1
  87. package/.server/server/plugins/engine/components/Markdown.d.ts +1 -0
  88. package/.server/server/plugins/engine/components/Markdown.js +1 -1
  89. package/.server/server/plugins/engine/components/Markdown.js.map +1 -1
  90. package/.server/server/plugins/engine/components/MonthYearField.d.ts +5 -2
  91. package/.server/server/plugins/engine/components/MonthYearField.js +13 -9
  92. package/.server/server/plugins/engine/components/MonthYearField.js.map +1 -1
  93. package/.server/server/plugins/engine/components/MultilineTextField.d.ts +1 -0
  94. package/.server/server/plugins/engine/components/NationalGridFieldNumberField.d.ts +2 -1
  95. package/.server/server/plugins/engine/components/NationalGridFieldNumberField.js +4 -3
  96. package/.server/server/plugins/engine/components/NationalGridFieldNumberField.js.map +1 -1
  97. package/.server/server/plugins/engine/components/NotificationBanner.d.ts +1 -0
  98. package/.server/server/plugins/engine/components/NumberField.d.ts +5 -0
  99. package/.server/server/plugins/engine/components/NumberField.js +16 -0
  100. package/.server/server/plugins/engine/components/NumberField.js.map +1 -1
  101. package/.server/server/plugins/engine/components/OsGridRefField.d.ts +103 -1
  102. package/.server/server/plugins/engine/components/OsGridRefField.js +13 -3
  103. package/.server/server/plugins/engine/components/OsGridRefField.js.map +1 -1
  104. package/.server/server/plugins/engine/components/PaymentField.d.ts +3 -0
  105. package/.server/server/plugins/engine/components/PaymentField.js +17 -6
  106. package/.server/server/plugins/engine/components/PaymentField.js.map +1 -1
  107. package/.server/server/plugins/engine/components/SelectField.d.ts +1 -0
  108. package/.server/server/plugins/engine/components/SelectionControlField.d.ts +1 -0
  109. package/.server/server/plugins/engine/components/TelephoneNumberField.d.ts +1 -0
  110. package/.server/server/plugins/engine/components/UkAddressField.d.ts +1 -0
  111. package/.server/server/plugins/engine/components/UkAddressField.js +23 -8
  112. package/.server/server/plugins/engine/components/UkAddressField.js.map +1 -1
  113. package/.server/server/plugins/engine/components/YesNoField.d.ts +5 -2
  114. package/.server/server/plugins/engine/components/YesNoField.js +17 -8
  115. package/.server/server/plugins/engine/components/YesNoField.js.map +1 -1
  116. package/.server/server/plugins/engine/components/helpers/components.js +3 -3
  117. package/.server/server/plugins/engine/components/helpers/components.js.map +1 -1
  118. package/.server/server/plugins/engine/components/helpers/geospatial.d.ts +8 -1
  119. package/.server/server/plugins/engine/components/helpers/geospatial.js +56 -15
  120. package/.server/server/plugins/engine/components/helpers/geospatial.js.map +1 -1
  121. package/.server/server/plugins/engine/components/helpers/index.d.ts +7 -0
  122. package/.server/server/plugins/engine/components/helpers/index.js +13 -1
  123. package/.server/server/plugins/engine/components/helpers/index.js.map +1 -1
  124. package/.server/server/plugins/engine/components/types.d.ts +1 -0
  125. package/.server/server/plugins/engine/components/types.js.map +1 -1
  126. package/.server/server/plugins/engine/errors.d.ts +49 -0
  127. package/.server/server/plugins/engine/errors.js +75 -0
  128. package/.server/server/plugins/engine/errors.js.map +1 -0
  129. package/.server/server/plugins/engine/form-availability.d.ts +3 -2
  130. package/.server/server/plugins/engine/form-availability.js +3 -2
  131. package/.server/server/plugins/engine/form-availability.js.map +1 -1
  132. package/.server/server/plugins/engine/form-context.js +2 -2
  133. package/.server/server/plugins/engine/form-context.js.map +1 -1
  134. package/.server/server/plugins/engine/helpers.d.ts +22 -2
  135. package/.server/server/plugins/engine/helpers.js +44 -5
  136. package/.server/server/plugins/engine/helpers.js.map +1 -1
  137. package/.server/server/plugins/engine/i18n/buildValidationMessages.d.ts +4 -1
  138. package/.server/server/plugins/engine/i18n/buildValidationMessages.js +31 -7
  139. package/.server/server/plugins/engine/i18n/buildValidationMessages.js.map +1 -1
  140. package/.server/server/plugins/engine/i18n/createFormTranslator.d.ts +4 -4
  141. package/.server/server/plugins/engine/i18n/createFormTranslator.js +33 -4
  142. package/.server/server/plugins/engine/i18n/createFormTranslator.js.map +1 -1
  143. package/.server/server/plugins/engine/i18n/extractBaseTranslations.d.ts +1 -1
  144. package/.server/server/plugins/engine/i18n/extractBaseTranslations.js +9 -0
  145. package/.server/server/plugins/engine/i18n/extractBaseTranslations.js.map +1 -1
  146. package/.server/server/plugins/engine/i18n/form.d.ts +13 -0
  147. package/.server/server/plugins/engine/i18n/form.js +35 -0
  148. package/.server/server/plugins/engine/i18n/form.js.map +1 -0
  149. package/.server/server/plugins/engine/i18n/languages.d.ts +10 -0
  150. package/.server/server/plugins/engine/i18n/languages.js +19 -0
  151. package/.server/server/plugins/engine/i18n/languages.js.map +1 -0
  152. package/.server/server/plugins/engine/i18n/translations/cy.json +192 -9
  153. package/.server/server/plugins/engine/i18n/translations/en-GB.json +125 -7
  154. package/.server/server/plugins/engine/i18n/types.d.ts +3 -0
  155. package/.server/server/plugins/engine/i18n/types.js.map +1 -1
  156. package/.server/server/plugins/engine/index.d.ts +4 -0
  157. package/.server/server/plugins/engine/index.js +4 -0
  158. package/.server/server/plugins/engine/index.js.map +1 -1
  159. package/.server/server/plugins/engine/models/FormModel.d.ts +0 -4
  160. package/.server/server/plugins/engine/models/FormModel.js +35 -23
  161. package/.server/server/plugins/engine/models/FormModel.js.map +1 -1
  162. package/.server/server/plugins/engine/models/SummaryViewModel.js +9 -4
  163. package/.server/server/plugins/engine/models/SummaryViewModel.js.map +1 -1
  164. package/.server/server/plugins/engine/models/types.d.ts +28 -1
  165. package/.server/server/plugins/engine/models/types.js.map +1 -1
  166. package/.server/server/plugins/engine/models/unavailable-view-model.d.ts +11 -2
  167. package/.server/server/plugins/engine/models/unavailable-view-model.js +23 -4
  168. package/.server/server/plugins/engine/models/unavailable-view-model.js.map +1 -1
  169. package/.server/server/plugins/engine/outputFormatters/adapter/v1.js +14 -6
  170. package/.server/server/plugins/engine/outputFormatters/adapter/v1.js.map +1 -1
  171. package/.server/server/plugins/engine/outputFormatters/human/v1.js +2 -1
  172. package/.server/server/plugins/engine/outputFormatters/human/v1.js.map +1 -1
  173. package/.server/server/plugins/engine/outputFormatters/machine/v2.d.ts +2 -0
  174. package/.server/server/plugins/engine/outputFormatters/machine/v2.js +2 -0
  175. package/.server/server/plugins/engine/outputFormatters/machine/v2.js.map +1 -1
  176. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.d.ts +7 -1
  177. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js +56 -10
  178. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js.map +1 -1
  179. package/.server/server/plugins/engine/pageControllers/QuestionPageController.d.ts +1 -1
  180. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js +7 -3
  181. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -1
  182. package/.server/server/plugins/engine/pageControllers/RepeatPageController.js +3 -4
  183. package/.server/server/plugins/engine/pageControllers/RepeatPageController.js.map +1 -1
  184. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js +10 -4
  185. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js.map +1 -1
  186. package/.server/server/plugins/engine/pageControllers/helpers/pages.js +2 -1
  187. package/.server/server/plugins/engine/pageControllers/helpers/pages.js.map +1 -1
  188. package/.server/server/plugins/engine/pageControllers/helpers/submission.d.ts +35 -2
  189. package/.server/server/plugins/engine/pageControllers/helpers/submission.js +100 -3
  190. package/.server/server/plugins/engine/pageControllers/helpers/submission.js.map +1 -1
  191. package/.server/server/plugins/engine/pageControllers/validationOptions.d.ts +1 -1
  192. package/.server/server/plugins/engine/pageControllers/validationOptions.js +24 -3
  193. package/.server/server/plugins/engine/pageControllers/validationOptions.js.map +1 -1
  194. package/.server/server/plugins/engine/types/enums.d.ts +3 -3
  195. package/.server/server/plugins/engine/types/enums.js +5 -4
  196. package/.server/server/plugins/engine/types/enums.js.map +1 -1
  197. package/.server/server/plugins/engine/types/schema.js +10 -3
  198. package/.server/server/plugins/engine/types/schema.js.map +1 -1
  199. package/.server/server/plugins/engine/types.d.ts +18 -2
  200. package/.server/server/plugins/engine/types.js.map +1 -1
  201. package/.server/server/plugins/engine/unavailable-response.js +5 -2
  202. package/.server/server/plugins/engine/unavailable-response.js.map +1 -1
  203. package/.server/server/plugins/engine/views/components/_location-field-base.html +4 -2
  204. package/.server/server/plugins/engine/views/components/fileuploadfield.html +1 -1
  205. package/.server/server/plugins/engine/views/components/geospatialfield.html +1 -0
  206. package/.server/server/plugins/engine/views/components/multilinetextfield.html +12 -1
  207. package/.server/server/plugins/engine/views/components/osgridreffield.html +4 -1
  208. package/.server/server/plugins/engine/views/components/paymentfield.html +1 -1
  209. package/.server/server/plugins/engine/views/components/ukaddressfield.html +1 -1
  210. package/.server/server/plugins/engine/views/file-upload.html +1 -1
  211. package/.server/server/plugins/engine/views/partials/preview-banner.html +3 -1
  212. package/.server/server/plugins/engine/views/unavailable.html +14 -11
  213. package/.server/server/plugins/nunjucks/filters/applyUrlParam.d.ts +9 -0
  214. package/.server/server/plugins/nunjucks/filters/applyUrlParam.js +17 -0
  215. package/.server/server/plugins/nunjucks/filters/applyUrlParam.js.map +1 -0
  216. package/.server/server/plugins/nunjucks/filters/applyUrlParam.test.js +17 -0
  217. package/.server/server/plugins/nunjucks/filters/applyUrlParam.test.js.map +1 -0
  218. package/.server/server/plugins/nunjucks/filters/field.d.ts +1 -1
  219. package/.server/server/plugins/nunjucks/filters/index.d.ts +1 -0
  220. package/.server/server/plugins/nunjucks/filters/index.js +1 -0
  221. package/.server/server/plugins/nunjucks/filters/index.js.map +1 -1
  222. package/.server/server/plugins/payment/helper.d.ts +11 -1
  223. package/.server/server/plugins/payment/helper.js +18 -2
  224. package/.server/server/plugins/payment/helper.js.map +1 -1
  225. package/.server/server/plugins/payment/helper.test.js +2 -1
  226. package/.server/server/plugins/payment/helper.test.js.map +1 -1
  227. package/.server/server/plugins/payment/service.d.ts +2 -1
  228. package/.server/server/plugins/payment/service.js +5 -2
  229. package/.server/server/plugins/payment/service.js.map +1 -1
  230. package/.server/server/plugins/payment/service.test.js +7 -3
  231. package/.server/server/plugins/payment/service.test.js.map +1 -1
  232. package/.server/server/plugins/payment/types.d.ts +4 -0
  233. package/.server/server/plugins/payment/types.js +1 -0
  234. package/.server/server/plugins/payment/types.js.map +1 -1
  235. package/.server/server/plugins/postcode-lookup/models/index.d.ts +249 -3
  236. package/.server/server/plugins/postcode-lookup/models/index.js +87 -33
  237. package/.server/server/plugins/postcode-lookup/models/index.js.map +1 -1
  238. package/.server/server/plugins/postcode-lookup/routes/index.d.ts +15 -1
  239. package/.server/server/plugins/postcode-lookup/routes/index.js +47 -15
  240. package/.server/server/plugins/postcode-lookup/routes/index.js.map +1 -1
  241. package/.server/server/plugins/postcode-lookup/routes/index.test.js +84 -0
  242. package/.server/server/plugins/postcode-lookup/routes/index.test.js.map +1 -0
  243. package/.server/server/plugins/postcode-lookup/types.d.ts +12 -0
  244. package/.server/server/plugins/postcode-lookup/types.js +6 -1
  245. package/.server/server/plugins/postcode-lookup/types.js.map +1 -1
  246. package/.server/server/plugins/postcode-lookup/views/postcode-lookup-details.html +3 -3
  247. package/.server/server/utils/type-utils.d.ts +1 -2
  248. package/.server/server/utils/type-utils.js +0 -3
  249. package/.server/server/utils/type-utils.js.map +1 -1
  250. package/.server/server/utils/utils.d.ts +6 -0
  251. package/.server/server/utils/utils.js +18 -1
  252. package/.server/server/utils/utils.js.map +1 -1
  253. package/.server/server/utils/utils.test.js +21 -1
  254. package/.server/server/utils/utils.test.js.map +1 -1
  255. package/package.json +20 -5
  256. package/src/client/javascripts/file-upload.js +11 -3
  257. package/src/client/javascripts/geospatial-map.js +176 -81
  258. package/src/client/javascripts/govuk.js +2 -0
  259. package/src/client/javascripts/location-map.js +60 -4
  260. package/src/client/javascripts/map-constants.js +3 -0
  261. package/src/client/javascripts/map.js +47 -0
  262. package/src/client/javascripts/maps.js +5 -0
  263. package/src/client/javascripts/shared.js +1 -4
  264. package/src/client/javascripts/sssi-dataset.js +39 -41
  265. package/src/client/stylesheets/maps.scss +4 -0
  266. package/src/client/stylesheets/shared.scss +1 -5
  267. package/src/server/constants.js +2 -0
  268. package/src/server/plugins/engine/__stubs__/definitions.ts +155 -0
  269. package/src/server/plugins/engine/components/AutocompleteField.ts +13 -0
  270. package/src/server/plugins/engine/components/CheckboxesField.ts +38 -5
  271. package/src/server/plugins/engine/components/ComponentBase.ts +2 -0
  272. package/src/server/plugins/engine/components/ComponentCollection.ts +16 -11
  273. package/src/server/plugins/engine/components/DatePartsField.ts +20 -9
  274. package/src/server/plugins/engine/components/DeclarationField.ts +7 -6
  275. package/src/server/plugins/engine/components/EastingNorthingField.ts +185 -89
  276. package/src/server/plugins/engine/components/EmailAddressField.ts +2 -1
  277. package/src/server/plugins/engine/components/FileUploadField.ts +44 -3
  278. package/src/server/plugins/engine/components/FormComponent.ts +6 -5
  279. package/src/server/plugins/engine/components/GeospatialField.ts +47 -9
  280. package/src/server/plugins/engine/components/LatLongField.ts +128 -58
  281. package/src/server/plugins/engine/components/LocationFieldBase.ts +30 -7
  282. package/src/server/plugins/engine/components/LocationFieldHelpers.ts +28 -14
  283. package/src/server/plugins/engine/components/Markdown.ts +2 -3
  284. package/src/server/plugins/engine/components/MonthYearField.ts +20 -15
  285. package/src/server/plugins/engine/components/NationalGridFieldNumberField.ts +9 -3
  286. package/src/server/plugins/engine/components/NumberField.ts +21 -1
  287. package/src/server/plugins/engine/components/OsGridRefField.ts +22 -3
  288. package/src/server/plugins/engine/components/PaymentField.ts +15 -5
  289. package/src/server/plugins/engine/components/UkAddressField.ts +30 -7
  290. package/src/server/plugins/engine/components/YesNoField.ts +17 -9
  291. package/src/server/plugins/engine/components/helpers/components.ts +3 -3
  292. package/src/server/plugins/engine/components/helpers/geospatial.ts +84 -21
  293. package/src/server/plugins/engine/components/helpers/index.ts +19 -0
  294. package/src/server/plugins/engine/components/types.ts +1 -0
  295. package/src/server/plugins/engine/errors.ts +78 -0
  296. package/src/server/plugins/engine/form-availability.ts +8 -2
  297. package/src/server/plugins/engine/form-context.ts +4 -2
  298. package/src/server/plugins/engine/helpers.ts +52 -5
  299. package/src/server/plugins/engine/i18n/buildValidationMessages.ts +16 -8
  300. package/src/server/plugins/engine/i18n/createFormTranslator.ts +62 -11
  301. package/src/server/plugins/engine/i18n/extractBaseTranslations.ts +7 -1
  302. package/src/server/plugins/engine/i18n/form.ts +54 -0
  303. package/src/server/plugins/engine/i18n/languages.ts +19 -0
  304. package/src/server/plugins/engine/i18n/translations/cy.json +192 -9
  305. package/src/server/plugins/engine/i18n/translations/en-GB.json +125 -7
  306. package/src/server/plugins/engine/i18n/types.ts +3 -0
  307. package/src/server/plugins/engine/index.ts +7 -0
  308. package/src/server/plugins/engine/models/FormModel.ts +46 -23
  309. package/src/server/plugins/engine/models/SummaryViewModel.ts +6 -5
  310. package/src/server/plugins/engine/models/types.ts +31 -0
  311. package/src/server/plugins/engine/models/unavailable-view-model.ts +26 -5
  312. package/src/server/plugins/engine/outputFormatters/adapter/v1.ts +18 -7
  313. package/src/server/plugins/engine/outputFormatters/human/v1.ts +2 -1
  314. package/src/server/plugins/engine/outputFormatters/machine/v2.ts +3 -0
  315. package/src/server/plugins/engine/pageControllers/FileUploadPageController.ts +60 -9
  316. package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +10 -5
  317. package/src/server/plugins/engine/pageControllers/RepeatPageController.ts +2 -2
  318. package/src/server/plugins/engine/pageControllers/SummaryPageController.ts +15 -4
  319. package/src/server/plugins/engine/pageControllers/helpers/pages.ts +2 -1
  320. package/src/server/plugins/engine/pageControllers/helpers/submission.ts +143 -4
  321. package/src/server/plugins/engine/pageControllers/validationOptions.ts +29 -4
  322. package/src/server/plugins/engine/types/enums.ts +4 -3
  323. package/src/server/plugins/engine/types/schema.ts +17 -4
  324. package/src/server/plugins/engine/types.ts +19 -1
  325. package/src/server/plugins/engine/unavailable-response.ts +6 -2
  326. package/src/server/plugins/engine/views/components/_location-field-base.html +4 -2
  327. package/src/server/plugins/engine/views/components/fileuploadfield.html +1 -1
  328. package/src/server/plugins/engine/views/components/geospatialfield.html +1 -0
  329. package/src/server/plugins/engine/views/components/multilinetextfield.html +12 -1
  330. package/src/server/plugins/engine/views/components/osgridreffield.html +4 -1
  331. package/src/server/plugins/engine/views/components/paymentfield.html +1 -1
  332. package/src/server/plugins/engine/views/components/ukaddressfield.html +1 -1
  333. package/src/server/plugins/engine/views/file-upload.html +1 -1
  334. package/src/server/plugins/engine/views/partials/preview-banner.html +3 -1
  335. package/src/server/plugins/engine/views/unavailable.html +14 -11
  336. package/src/server/plugins/nunjucks/filters/applyUrlParam.js +17 -0
  337. package/src/server/plugins/nunjucks/filters/index.js +1 -0
  338. package/src/server/plugins/payment/helper.js +22 -2
  339. package/src/server/plugins/payment/service.js +6 -2
  340. package/src/server/plugins/payment/types.js +1 -0
  341. package/src/server/plugins/postcode-lookup/models/index.js +75 -43
  342. package/src/server/plugins/postcode-lookup/routes/index.js +73 -16
  343. package/src/server/plugins/postcode-lookup/types.js +6 -1
  344. package/src/server/plugins/postcode-lookup/views/postcode-lookup-details.html +3 -3
  345. package/src/server/utils/type-utils.ts +1 -11
  346. package/src/server/utils/utils.js +19 -1
@@ -13,11 +13,13 @@ export class PaymentField extends FormComponent {
13
13
  constructor(def, props) {
14
14
  super(def, props);
15
15
  this.options = def.options;
16
+ this.def = def;
16
17
  const paymentStateSchema = joi.object({
17
18
  paymentId: joi.string().required(),
18
19
  reference: joi.string().required(),
19
20
  amount: joi.number().required(),
20
21
  description: joi.string().required(),
22
+ descriptionInEng: joi.string().optional(),
21
23
  uuid: joi.string().uuid().required(),
22
24
  formId: joi.string().required(),
23
25
  isLivePayment: joi.boolean().required(),
@@ -52,6 +54,12 @@ export class PaymentField extends FormComponent {
52
54
  payload
53
55
  } = context;
54
56
  const viewModel = super.getViewModel(context);
57
+ const {
58
+ translator
59
+ } = context;
60
+ const {
61
+ tComponent
62
+ } = translator;
55
63
 
56
64
  // Payload is pre-populated from state if a payment has already been made
57
65
  const paymentState = this.isPaymentState(payload[this.name]) ? payload[this.name] : undefined;
@@ -63,7 +71,7 @@ export class PaymentField extends FormComponent {
63
71
  return {
64
72
  ...viewModel,
65
73
  amount: formatCurrency(amount),
66
- description: this.options.description,
74
+ description: tComponent(this.def, 'paymentDescription') || this.options.description,
67
75
  paymentState
68
76
  };
69
77
  }
@@ -157,10 +165,11 @@ export class PaymentField extends FormComponent {
157
165
  const {
158
166
  model
159
167
  } = args.controller;
160
- const language = resolveLanguage(request);
161
168
  const {
162
- t
163
- } = model.createTranslator(language);
169
+ language,
170
+ t,
171
+ tComponent
172
+ } = model.createTranslator(resolveLanguage(request));
164
173
  const state = await args.controller.getState(request);
165
174
  const {
166
175
  baseUrl
@@ -182,7 +191,7 @@ export class PaymentField extends FormComponent {
182
191
  if (resolvedAmount === 0) {
183
192
  return h.redirect(summaryUrl).code(StatusCodes.SEE_OTHER);
184
193
  }
185
- const description = options.description;
194
+ const description = tComponent(args.component, 'paymentDescription') || options.description;
186
195
  const slug = `/${model.basePath}`;
187
196
  const payCallbackUrl = `${baseUrl}/payment-callback?uuid=${uuid}`;
188
197
  const paymentPageUrl = args.sourceUrl;
@@ -194,7 +203,8 @@ export class PaymentField extends FormComponent {
194
203
  const amountInPence = Math.round(resolvedAmount * 100);
195
204
  const payment = await paymentService.createPayment(amountInPence, description, payCallbackUrl, reference, isLivePayment, {
196
205
  formId,
197
- slug
206
+ slug,
207
+ language
198
208
  }, prefilledEmail);
199
209
  if (!payment) {
200
210
  const message = isLivePayment ? t('components.paymentField.cannotTakePayment') : t('components.paymentField.testApiKey');
@@ -208,6 +218,7 @@ export class PaymentField extends FormComponent {
208
218
  reference,
209
219
  amount: resolvedAmount,
210
220
  description,
221
+ descriptionInEng: options.description,
211
222
  paymentId: payment.paymentId,
212
223
  componentName,
213
224
  returnUrl: summaryUrl,
@@ -1 +1 @@
1
- {"version":3,"file":"PaymentField.js","names":["randomUUID","StatusCodes","joi","logger","COMPONENT_STATE_ERROR","FormComponent","createError","getPluginOptions","PaymentErrorTypes","PaymentPreAuthError","PaymentSubmissionError","createPaymentService","formatCurrency","resolveLanguage","PaymentField","isAppendageStateSingleObject","constructor","def","props","options","paymentStateSchema","object","paymentId","string","required","reference","amount","number","description","uuid","formId","isLivePayment","boolean","preAuth","status","valid","createdAt","isoDate","unknown","label","formSchema","stateSchema","getPaymentStateFromState","state","value","name","isPaymentState","undefined","getDisplayStringFromState","_translator","getViewModel","context","payload","viewModel","paymentState","Array","isArray","isState","getFormValue","getContextValueFromState","getAllPossibleErrors","baseErrors","type","template","advancedSettingsErrors","resolveAmount","model","conditionalAmounts","length","condition","conditions","warn","fn","dispatcher","request","h","args","componentName","component","controller","language","t","createTranslator","getState","baseUrl","server","summaryUrl","basePath","existingPaymentState","redirect","code","SEE_OTHER","isLive","isPreview","formsService","services","paymentService","$$__referenceNumber","resolvedAmount","slug","payCallbackUrl","paymentPageUrl","sourceUrl","prefilledEmail","userConfirmationEmail","userConfirmationEmailAddress","amountInPence","Math","round","payment","createPayment","message","govukError","yar","flash","url","pathname","search","sessionData","returnUrl","failureUrl","set","paymentUrl","onSubmit","_metadata","PaymentIncomplete","capture","getPaymentStatus","checkPaymentAmount","markPaymentCaptured","PaymentExpired","captured","capturePayment","updatedState","Date","toISOString","page","currentState","mergeState"],"sources":["../../../../../src/server/plugins/engine/components/PaymentField.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto'\n\nimport {\n type FormMetadata,\n type PaymentFieldComponent\n} from '@defra/forms-model'\nimport { StatusCodes } from 'http-status-codes'\nimport joi, { type ObjectSchema } from 'joi'\n\nimport { logger } from '~/src/server/common/helpers/logging/logger.js'\nimport { COMPONENT_STATE_ERROR } from '~/src/server/constants.js'\nimport { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { type PaymentState } from '~/src/server/plugins/engine/components/PaymentField.types.js'\nimport { type RenderContext } from '~/src/server/plugins/engine/components/types.js'\nimport {\n createError,\n getPluginOptions\n} from '~/src/server/plugins/engine/helpers.js'\nimport { type Translator } from '~/src/server/plugins/engine/i18n/types.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport {\n PaymentErrorTypes,\n PaymentPreAuthError,\n PaymentSubmissionError\n} from '~/src/server/plugins/engine/pageControllers/errors.js'\nimport {\n type FormContext,\n type FormRequestPayload,\n type FormResponseToolkit\n} from '~/src/server/plugins/engine/types/index.js'\nimport {\n type ErrorMessageTemplateList,\n type FormState,\n type FormStateValue,\n type FormSubmissionState,\n type PaymentExternalArgs\n} from '~/src/server/plugins/engine/types.js'\nimport {\n createPaymentService,\n formatCurrency\n} from '~/src/server/plugins/payment/helper.js'\nimport { resolveLanguage } from '~/src/server/utils/utils.js'\n\nexport class PaymentField extends FormComponent {\n declare options: PaymentFieldComponent['options']\n declare formSchema: ObjectSchema\n declare stateSchema: ObjectSchema\n isAppendageStateSingleObject = true\n\n constructor(\n def: PaymentFieldComponent,\n props: ConstructorParameters<typeof FormComponent>[1]\n ) {\n super(def, props)\n\n this.options = def.options\n\n const paymentStateSchema = joi\n .object({\n paymentId: joi.string().required(),\n reference: joi.string().required(),\n amount: joi.number().required(),\n description: joi.string().required(),\n uuid: joi.string().uuid().required(),\n formId: joi.string().required(),\n isLivePayment: joi.boolean().required(),\n preAuth: joi\n .object({\n status: joi\n .string()\n .valid('success', 'failed', 'started')\n .required(),\n createdAt: joi.string().isoDate().required()\n })\n .required()\n })\n .unknown(true)\n .label(this.label)\n\n this.formSchema = paymentStateSchema\n // 'required()' forces the payment page to be invalid until we have valid payment state\n // i.e. the user will automatically be directed back to the payment page\n // if they attempt to access future pages when no payment entered yet\n this.stateSchema = paymentStateSchema.required()\n }\n\n /**\n * Gets the PaymentState from form submission state\n */\n getPaymentStateFromState(\n state: FormSubmissionState\n ): PaymentState | undefined {\n const value = state[this.name]\n return this.isPaymentState(value) ? value : undefined\n }\n\n getDisplayStringFromState(\n state: FormSubmissionState,\n _translator: Translator\n ): string {\n const value = this.getPaymentStateFromState(state)\n\n if (!value) {\n return ''\n }\n\n return `${formatCurrency(value.amount)} - ${value.description}`\n }\n\n getViewModel(context: RenderContext) {\n const { payload } = context\n const viewModel = super.getViewModel(context)\n\n // Payload is pre-populated from state if a payment has already been made\n const paymentState = this.isPaymentState(payload[this.name] as unknown)\n ? (payload[this.name] as unknown as PaymentState)\n : undefined\n\n // Use payment state amount if pre-authorized, otherwise use default.\n // The page controller overrides this with the resolved conditional amount\n // using the full form state (which getViewModel doesn't have access to).\n const amount = paymentState?.amount ?? this.options.amount\n\n return {\n ...viewModel,\n amount: formatCurrency(amount),\n description: this.options.description,\n paymentState\n }\n }\n\n /**\n * Type guard to check if value is PaymentState\n */\n isPaymentState(value: unknown): value is PaymentState {\n return PaymentField.isPaymentState(value)\n }\n\n /**\n * Static type guard to check if value is PaymentState\n */\n static isPaymentState(value: unknown): value is PaymentState {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return false\n }\n\n const state = value as PaymentState\n return (\n typeof state.paymentId === 'string' &&\n typeof state.amount === 'number' &&\n typeof state.description === 'string'\n )\n }\n\n /**\n * Override base isState to validate PaymentState\n */\n isState(value?: FormStateValue | FormState): value is FormState {\n return this.isPaymentState(value)\n }\n\n getFormValue(value?: FormStateValue | FormState) {\n return this.isPaymentState(value)\n ? (value as unknown as NonNullable<FormStateValue>)\n : undefined\n }\n\n getContextValueFromState(state: FormSubmissionState) {\n return this.isPaymentState(state)\n ? `Reference: ${state.reference}\\nAmount: ${formatCurrency(state.amount)}`\n : ''\n }\n\n /**\n * For error preview page that shows all possible errors on a component\n */\n getAllPossibleErrors(): ErrorMessageTemplateList {\n return PaymentField.getAllPossibleErrors()\n }\n\n /**\n * Static version of getAllPossibleErrors that doesn't require a component instance.\n */\n static getAllPossibleErrors(): ErrorMessageTemplateList {\n return {\n baseErrors: [\n {\n type: 'paymentRequired',\n template: 'Complete the payment to continue'\n }\n ],\n advancedSettingsErrors: []\n }\n }\n\n /**\n * Resolves the payment amount from conditional amounts configuration.\n * Evaluates conditions in order; first true condition wins.\n * Falls back to the default options.amount.\n */\n static resolveAmount(\n options: PaymentFieldComponent['options'],\n model: FormModel,\n state: FormState\n ): number {\n const { conditionalAmounts } = options\n\n if (!conditionalAmounts?.length) {\n return options.amount\n }\n\n for (const { condition, amount } of conditionalAmounts) {\n if (!model.conditions[condition]) {\n logger.warn(\n `[payment] Condition '${condition}' not found in form conditions. Skipping.`\n )\n continue\n }\n if (model.conditions[condition].fn(state)) {\n return amount\n }\n }\n\n return options.amount\n }\n\n /**\n * Dispatcher for external redirect to GOV.UK Pay\n */\n static async dispatcher(\n request: FormRequestPayload,\n h: FormResponseToolkit,\n args: PaymentExternalArgs\n ): Promise<unknown> {\n const { options, name: componentName } = args.component\n const { model } = args.controller\n const language = resolveLanguage(request)\n const { t } = model.createTranslator(language)\n const state = await args.controller.getState(request)\n const { baseUrl } = getPluginOptions(request.server)\n const summaryUrl = `${baseUrl}/${model.basePath}/summary`\n\n const existingPaymentState = state[componentName]\n if (\n PaymentField.isPaymentState(existingPaymentState) &&\n existingPaymentState.preAuth?.status === 'success'\n ) {\n return h.redirect(summaryUrl).code(StatusCodes.SEE_OTHER)\n }\n\n const isLivePayment = args.isLive && !args.isPreview\n const formId = args.controller.model.formId\n const formsService = model.services.formsService\n const paymentService = await createPaymentService(\n isLivePayment,\n formId,\n formsService\n )\n\n const uuid = randomUUID()\n\n const reference = state.$$__referenceNumber as string\n const resolvedAmount = PaymentField.resolveAmount(options, model, state)\n\n // Zero-amount safety net (page skip should prevent this, but defensive)\n if (resolvedAmount === 0) {\n return h.redirect(summaryUrl).code(StatusCodes.SEE_OTHER)\n }\n\n const description = options.description\n\n const slug = `/${model.basePath}`\n\n const payCallbackUrl = `${baseUrl}/payment-callback?uuid=${uuid}`\n const paymentPageUrl = args.sourceUrl\n\n let prefilledEmail: string | undefined\n const userConfirmationEmail = state.userConfirmationEmailAddress\n if (typeof userConfirmationEmail === 'string' && userConfirmationEmail) {\n prefilledEmail = userConfirmationEmail\n }\n\n const amountInPence = Math.round(resolvedAmount * 100)\n const payment = await paymentService.createPayment(\n amountInPence,\n description,\n payCallbackUrl,\n reference,\n isLivePayment,\n { formId, slug },\n prefilledEmail\n )\n\n if (!payment) {\n const message = isLivePayment\n ? t('components.paymentField.cannotTakePayment')\n : t('components.paymentField.testApiKey')\n const govukError = createError(componentName, message)\n request.yar.flash(COMPONENT_STATE_ERROR, govukError, true)\n return h\n .redirect(`${request.url.pathname}${request.url.search}`)\n .code(StatusCodes.SEE_OTHER)\n }\n\n const sessionData: PaymentSessionData = {\n uuid,\n formId,\n reference,\n amount: resolvedAmount,\n description,\n paymentId: payment.paymentId,\n componentName,\n returnUrl: summaryUrl,\n failureUrl: paymentPageUrl,\n isLivePayment\n }\n\n request.yar.set(`payment-${uuid}`, sessionData)\n\n return h.redirect(payment.paymentUrl).code(StatusCodes.SEE_OTHER)\n }\n\n /**\n * Called on form submission to capture the payment\n * @see https://docs.payments.service.gov.uk/delayed_capture/#delay-taking-a-payment\n */\n async onSubmit(\n request: FormRequestPayload,\n _metadata: FormMetadata,\n context: FormContext\n ): Promise<void> {\n const language = resolveLanguage(request)\n const { t } = this.model.createTranslator(language)\n\n // Zero-amount bypass — no capture needed\n const resolvedAmount = PaymentField.resolveAmount(\n this.options,\n this.model,\n context.state\n )\n if (resolvedAmount === 0) {\n return\n }\n\n const paymentState = this.getPaymentStateFromState(context.state)\n\n if (!paymentState) {\n throw new PaymentPreAuthError(\n this,\n t('components.paymentField.completePayment'),\n true,\n PaymentErrorTypes.PaymentIncomplete\n )\n }\n\n if (paymentState.capture?.status === 'success') {\n return\n }\n\n const { paymentId, isLivePayment, formId } = paymentState\n const formsService = this.model.services.formsService\n const paymentService = await createPaymentService(\n isLivePayment,\n formId,\n formsService\n )\n\n /**\n * @see https://docs.payments.service.gov.uk/api_reference/#payment-status-lifecycle\n */\n const status = await paymentService.getPaymentStatus(\n paymentId,\n isLivePayment\n )\n\n PaymentSubmissionError.checkPaymentAmount(\n status.amount,\n resolvedAmount,\n this,\n t\n )\n\n if (status.state.status === 'success') {\n await this.markPaymentCaptured(request, paymentState)\n return\n }\n\n if (status.state.status !== 'capturable') {\n throw new PaymentPreAuthError(\n this,\n t('components.paymentField.paymentExpired'),\n true,\n PaymentErrorTypes.PaymentExpired\n )\n }\n\n const captured = await paymentService.capturePayment(\n paymentId,\n status.amount\n )\n\n if (!captured) {\n throw new PaymentPreAuthError(\n this,\n t('components.paymentField.submissionFailed'),\n false\n )\n }\n\n await this.markPaymentCaptured(request, paymentState)\n }\n\n /**\n * Updates payment state to mark capture as successful\n * This ensures we don't try to re-capture on submission retry\n */\n private async markPaymentCaptured(\n request: FormRequestPayload,\n paymentState: PaymentState\n ): Promise<void> {\n const updatedState: PaymentState = {\n ...paymentState,\n capture: {\n status: 'success',\n createdAt: new Date().toISOString()\n }\n }\n\n if (this.page) {\n const currentState = await this.page.getState(request)\n await this.page.mergeState(request, currentState, {\n [this.name]: updatedState\n })\n }\n }\n}\n\n/**\n * Session data stored when dispatching to GOV.UK Pay\n */\nexport interface PaymentSessionData {\n uuid: string\n formId: string\n reference: string\n amount: number\n description: string\n paymentId: string\n componentName: string\n returnUrl: string\n failureUrl: string\n isLivePayment: boolean\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"PaymentField.js","names":["randomUUID","StatusCodes","joi","logger","COMPONENT_STATE_ERROR","FormComponent","createError","getPluginOptions","PaymentErrorTypes","PaymentPreAuthError","PaymentSubmissionError","createPaymentService","formatCurrency","resolveLanguage","PaymentField","isAppendageStateSingleObject","constructor","def","props","options","paymentStateSchema","object","paymentId","string","required","reference","amount","number","description","descriptionInEng","optional","uuid","formId","isLivePayment","boolean","preAuth","status","valid","createdAt","isoDate","unknown","label","formSchema","stateSchema","getPaymentStateFromState","state","value","name","isPaymentState","undefined","getDisplayStringFromState","_translator","getViewModel","context","payload","viewModel","translator","tComponent","paymentState","Array","isArray","isState","getFormValue","getContextValueFromState","getAllPossibleErrors","baseErrors","type","template","advancedSettingsErrors","resolveAmount","model","conditionalAmounts","length","condition","conditions","warn","fn","dispatcher","request","h","args","componentName","component","controller","language","t","createTranslator","getState","baseUrl","server","summaryUrl","basePath","existingPaymentState","redirect","code","SEE_OTHER","isLive","isPreview","formsService","services","paymentService","$$__referenceNumber","resolvedAmount","slug","payCallbackUrl","paymentPageUrl","sourceUrl","prefilledEmail","userConfirmationEmail","userConfirmationEmailAddress","amountInPence","Math","round","payment","createPayment","message","govukError","yar","flash","url","pathname","search","sessionData","returnUrl","failureUrl","set","paymentUrl","onSubmit","_metadata","PaymentIncomplete","capture","getPaymentStatus","checkPaymentAmount","markPaymentCaptured","PaymentExpired","captured","capturePayment","updatedState","Date","toISOString","page","currentState","mergeState"],"sources":["../../../../../src/server/plugins/engine/components/PaymentField.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto'\n\nimport {\n type FormMetadata,\n type PaymentFieldComponent\n} from '@defra/forms-model'\nimport { StatusCodes } from 'http-status-codes'\nimport joi, { type ObjectSchema } from 'joi'\n\nimport { logger } from '~/src/server/common/helpers/logging/logger.js'\nimport { COMPONENT_STATE_ERROR } from '~/src/server/constants.js'\nimport { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { type PaymentState } from '~/src/server/plugins/engine/components/PaymentField.types.js'\nimport { type RenderContext } from '~/src/server/plugins/engine/components/types.js'\nimport {\n createError,\n getPluginOptions\n} from '~/src/server/plugins/engine/helpers.js'\nimport { type Translator } from '~/src/server/plugins/engine/i18n/types.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport {\n PaymentErrorTypes,\n PaymentPreAuthError,\n PaymentSubmissionError\n} from '~/src/server/plugins/engine/pageControllers/errors.js'\nimport {\n type FormContext,\n type FormRequestPayload,\n type FormResponseToolkit\n} from '~/src/server/plugins/engine/types/index.js'\nimport {\n type ErrorMessageTemplateList,\n type FormState,\n type FormStateValue,\n type FormSubmissionState,\n type PaymentExternalArgs\n} from '~/src/server/plugins/engine/types.js'\nimport {\n createPaymentService,\n formatCurrency\n} from '~/src/server/plugins/payment/helper.js'\nimport { resolveLanguage } from '~/src/server/utils/utils.js'\n\nexport class PaymentField extends FormComponent {\n declare options: PaymentFieldComponent['options']\n declare formSchema: ObjectSchema\n declare stateSchema: ObjectSchema\n declare def: PaymentFieldComponent\n isAppendageStateSingleObject = true\n\n constructor(\n def: PaymentFieldComponent,\n props: ConstructorParameters<typeof FormComponent>[1]\n ) {\n super(def, props)\n\n this.options = def.options\n this.def = def\n\n const paymentStateSchema = joi\n .object({\n paymentId: joi.string().required(),\n reference: joi.string().required(),\n amount: joi.number().required(),\n description: joi.string().required(),\n descriptionInEng: joi.string().optional(),\n uuid: joi.string().uuid().required(),\n formId: joi.string().required(),\n isLivePayment: joi.boolean().required(),\n preAuth: joi\n .object({\n status: joi\n .string()\n .valid('success', 'failed', 'started')\n .required(),\n createdAt: joi.string().isoDate().required()\n })\n .required()\n })\n .unknown(true)\n .label(this.label)\n\n this.formSchema = paymentStateSchema\n // 'required()' forces the payment page to be invalid until we have valid payment state\n // i.e. the user will automatically be directed back to the payment page\n // if they attempt to access future pages when no payment entered yet\n this.stateSchema = paymentStateSchema.required()\n }\n\n /**\n * Gets the PaymentState from form submission state\n */\n getPaymentStateFromState(\n state: FormSubmissionState\n ): PaymentState | undefined {\n const value = state[this.name]\n return this.isPaymentState(value) ? value : undefined\n }\n\n getDisplayStringFromState(\n state: FormSubmissionState,\n _translator: Translator\n ): string {\n const value = this.getPaymentStateFromState(state)\n\n if (!value) {\n return ''\n }\n\n return `${formatCurrency(value.amount)} - ${value.description}`\n }\n\n getViewModel(context: RenderContext) {\n const { payload } = context\n const viewModel = super.getViewModel(context)\n const { translator } = context\n const { tComponent } = translator\n\n // Payload is pre-populated from state if a payment has already been made\n const paymentState = this.isPaymentState(payload[this.name] as unknown)\n ? (payload[this.name] as unknown as PaymentState)\n : undefined\n\n // Use payment state amount if pre-authorized, otherwise use default.\n // The page controller overrides this with the resolved conditional amount\n // using the full form state (which getViewModel doesn't have access to).\n const amount = paymentState?.amount ?? this.options.amount\n\n return {\n ...viewModel,\n amount: formatCurrency(amount),\n description:\n tComponent(this.def, 'paymentDescription') || this.options.description,\n paymentState\n }\n }\n\n /**\n * Type guard to check if value is PaymentState\n */\n isPaymentState(value: unknown): value is PaymentState {\n return PaymentField.isPaymentState(value)\n }\n\n /**\n * Static type guard to check if value is PaymentState\n */\n static isPaymentState(value: unknown): value is PaymentState {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return false\n }\n\n const state = value as PaymentState\n return (\n typeof state.paymentId === 'string' &&\n typeof state.amount === 'number' &&\n typeof state.description === 'string'\n )\n }\n\n /**\n * Override base isState to validate PaymentState\n */\n isState(value?: FormStateValue | FormState): value is FormState {\n return this.isPaymentState(value)\n }\n\n getFormValue(value?: FormStateValue | FormState) {\n return this.isPaymentState(value)\n ? (value as unknown as NonNullable<FormStateValue>)\n : undefined\n }\n\n getContextValueFromState(state: FormSubmissionState) {\n return this.isPaymentState(state)\n ? `Reference: ${state.reference}\\nAmount: ${formatCurrency(state.amount)}`\n : ''\n }\n\n /**\n * For error preview page that shows all possible errors on a component\n */\n getAllPossibleErrors(): ErrorMessageTemplateList {\n return PaymentField.getAllPossibleErrors()\n }\n\n /**\n * Static version of getAllPossibleErrors that doesn't require a component instance.\n */\n static getAllPossibleErrors(): ErrorMessageTemplateList {\n return {\n baseErrors: [\n {\n type: 'paymentRequired',\n template: 'Complete the payment to continue'\n }\n ],\n advancedSettingsErrors: []\n }\n }\n\n /**\n * Resolves the payment amount from conditional amounts configuration.\n * Evaluates conditions in order; first true condition wins.\n * Falls back to the default options.amount.\n */\n static resolveAmount(\n options: PaymentFieldComponent['options'],\n model: FormModel,\n state: FormState\n ): number {\n const { conditionalAmounts } = options\n\n if (!conditionalAmounts?.length) {\n return options.amount\n }\n\n for (const { condition, amount } of conditionalAmounts) {\n if (!model.conditions[condition]) {\n logger.warn(\n `[payment] Condition '${condition}' not found in form conditions. Skipping.`\n )\n continue\n }\n if (model.conditions[condition].fn(state)) {\n return amount\n }\n }\n\n return options.amount\n }\n\n /**\n * Dispatcher for external redirect to GOV.UK Pay\n */\n static async dispatcher(\n request: FormRequestPayload,\n h: FormResponseToolkit,\n args: PaymentExternalArgs\n ): Promise<unknown> {\n const { options, name: componentName } = args.component\n const { model } = args.controller\n const { language, t, tComponent } = model.createTranslator(\n resolveLanguage(request)\n )\n const state = await args.controller.getState(request)\n const { baseUrl } = getPluginOptions(request.server)\n const summaryUrl = `${baseUrl}/${model.basePath}/summary`\n\n const existingPaymentState = state[componentName]\n if (\n PaymentField.isPaymentState(existingPaymentState) &&\n existingPaymentState.preAuth?.status === 'success'\n ) {\n return h.redirect(summaryUrl).code(StatusCodes.SEE_OTHER)\n }\n\n const isLivePayment = args.isLive && !args.isPreview\n const formId = args.controller.model.formId\n const formsService = model.services.formsService\n const paymentService = await createPaymentService(\n isLivePayment,\n formId,\n formsService\n )\n\n const uuid = randomUUID()\n\n const reference = state.$$__referenceNumber as string\n const resolvedAmount = PaymentField.resolveAmount(options, model, state)\n\n // Zero-amount safety net (page skip should prevent this, but defensive)\n if (resolvedAmount === 0) {\n return h.redirect(summaryUrl).code(StatusCodes.SEE_OTHER)\n }\n\n const description =\n tComponent(args.component, 'paymentDescription') || options.description\n\n const slug = `/${model.basePath}`\n\n const payCallbackUrl = `${baseUrl}/payment-callback?uuid=${uuid}`\n const paymentPageUrl = args.sourceUrl\n\n let prefilledEmail: string | undefined\n const userConfirmationEmail = state.userConfirmationEmailAddress\n if (typeof userConfirmationEmail === 'string' && userConfirmationEmail) {\n prefilledEmail = userConfirmationEmail\n }\n\n const amountInPence = Math.round(resolvedAmount * 100)\n const payment = await paymentService.createPayment(\n amountInPence,\n description,\n payCallbackUrl,\n reference,\n isLivePayment,\n { formId, slug, language },\n prefilledEmail\n )\n\n if (!payment) {\n const message = isLivePayment\n ? t('components.paymentField.cannotTakePayment')\n : t('components.paymentField.testApiKey')\n const govukError = createError(componentName, message)\n request.yar.flash(COMPONENT_STATE_ERROR, govukError, true)\n return h\n .redirect(`${request.url.pathname}${request.url.search}`)\n .code(StatusCodes.SEE_OTHER)\n }\n\n const sessionData: PaymentSessionData = {\n uuid,\n formId,\n reference,\n amount: resolvedAmount,\n description,\n descriptionInEng: options.description,\n paymentId: payment.paymentId,\n componentName,\n returnUrl: summaryUrl,\n failureUrl: paymentPageUrl,\n isLivePayment\n }\n\n request.yar.set(`payment-${uuid}`, sessionData)\n\n return h.redirect(payment.paymentUrl).code(StatusCodes.SEE_OTHER)\n }\n\n /**\n * Called on form submission to capture the payment\n * @see https://docs.payments.service.gov.uk/delayed_capture/#delay-taking-a-payment\n */\n async onSubmit(\n request: FormRequestPayload,\n _metadata: FormMetadata,\n context: FormContext\n ): Promise<void> {\n const language = resolveLanguage(request)\n const { t } = this.model.createTranslator(language)\n\n // Zero-amount bypass — no capture needed\n const resolvedAmount = PaymentField.resolveAmount(\n this.options,\n this.model,\n context.state\n )\n if (resolvedAmount === 0) {\n return\n }\n\n const paymentState = this.getPaymentStateFromState(context.state)\n\n if (!paymentState) {\n throw new PaymentPreAuthError(\n this,\n t('components.paymentField.completePayment'),\n true,\n PaymentErrorTypes.PaymentIncomplete\n )\n }\n\n if (paymentState.capture?.status === 'success') {\n return\n }\n\n const { paymentId, isLivePayment, formId } = paymentState\n const formsService = this.model.services.formsService\n const paymentService = await createPaymentService(\n isLivePayment,\n formId,\n formsService\n )\n\n /**\n * @see https://docs.payments.service.gov.uk/api_reference/#payment-status-lifecycle\n */\n const status = await paymentService.getPaymentStatus(\n paymentId,\n isLivePayment\n )\n\n PaymentSubmissionError.checkPaymentAmount(\n status.amount,\n resolvedAmount,\n this,\n t\n )\n\n if (status.state.status === 'success') {\n await this.markPaymentCaptured(request, paymentState)\n return\n }\n\n if (status.state.status !== 'capturable') {\n throw new PaymentPreAuthError(\n this,\n t('components.paymentField.paymentExpired'),\n true,\n PaymentErrorTypes.PaymentExpired\n )\n }\n\n const captured = await paymentService.capturePayment(\n paymentId,\n status.amount\n )\n\n if (!captured) {\n throw new PaymentPreAuthError(\n this,\n t('components.paymentField.submissionFailed'),\n false\n )\n }\n\n await this.markPaymentCaptured(request, paymentState)\n }\n\n /**\n * Updates payment state to mark capture as successful\n * This ensures we don't try to re-capture on submission retry\n */\n private async markPaymentCaptured(\n request: FormRequestPayload,\n paymentState: PaymentState\n ): Promise<void> {\n const updatedState: PaymentState = {\n ...paymentState,\n capture: {\n status: 'success',\n createdAt: new Date().toISOString()\n }\n }\n\n if (this.page) {\n const currentState = await this.page.getState(request)\n await this.page.mergeState(request, currentState, {\n [this.name]: updatedState\n })\n }\n }\n}\n\n/**\n * Session data stored when dispatching to GOV.UK Pay\n */\nexport interface PaymentSessionData {\n uuid: string\n formId: string\n reference: string\n amount: number\n description: string\n descriptionInEng?: string\n paymentId: string\n componentName: string\n returnUrl: string\n failureUrl: string\n isLivePayment: boolean\n}\n"],"mappings":"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","ignoreList":[]}
@@ -51,6 +51,7 @@ export declare class SelectField extends ListFormComponent {
51
51
  upload?: {
52
52
  count: number;
53
53
  summaryList: import("../types.js").SummaryList;
54
+ uploadingLabel?: string;
54
55
  };
55
56
  };
56
57
  }
@@ -52,6 +52,7 @@ export declare class SelectionControlField extends ListFormComponent {
52
52
  upload?: {
53
53
  count: number;
54
54
  summaryList: import("../../../../server/plugins/engine/types.js").SummaryList;
55
+ uploadingLabel?: string;
55
56
  };
56
57
  };
57
58
  /**
@@ -55,6 +55,7 @@ export declare class TelephoneNumberField extends FormComponent {
55
55
  upload?: {
56
56
  count: number;
57
57
  summaryList: import("../../../../server/plugins/engine/types.js").SummaryList;
58
+ uploadingLabel?: string;
58
59
  };
59
60
  };
60
61
  /**
@@ -69,6 +69,7 @@ export declare class UkAddressField extends FormComponent {
69
69
  upload?: {
70
70
  count: number;
71
71
  summaryList: import("../../../../server/plugins/engine/types.js").SummaryList;
72
+ uploadingLabel?: string;
72
73
  };
73
74
  };
74
75
  isState(value?: FormStateValue | FormState): value is UkAddressState;
@@ -1,8 +1,10 @@
1
- import { ComponentType } from '@defra/forms-model';
1
+ import { ComponentType, FormStatus } from '@defra/forms-model';
2
2
  import lowerFirst from 'lodash/lowerFirst.js';
3
3
  import { ComponentCollection } from "./ComponentCollection.js";
4
4
  import { FormComponent, isFormState } from "./FormComponent.js";
5
5
  import { TextField } from "./TextField.js";
6
+ import { getCachedPluginTranslator } from "../i18n/form.js";
7
+ import { getAvailableLanguages } from "../i18n/languages.js";
6
8
  import { dispatch } from "../../postcode-lookup/routes/index.js";
7
9
  import { resolveLanguage } from "../../../utils/utils.js";
8
10
  export class UkAddressField extends FormComponent {
@@ -164,7 +166,7 @@ export class UkAddressField extends FormComponent {
164
166
  text: label.text,
165
167
  /**
166
168
  * For screen readers, only hide legend visually. This can be overridden
167
- * by single component {@link QuestionPageController | `showTitle` handling}
169
+ * by single component (see QuestionPageController and showTitle handling)
168
170
  */
169
171
  classes: options.hideTitle ? 'govuk-visually-hidden' : 'govuk-fieldset__legend--m'
170
172
  }
@@ -235,22 +237,35 @@ export class UkAddressField extends FormComponent {
235
237
  static isUkAddress(value) {
236
238
  return isFormState(value) && TextField.isText(value.addressLine1) && TextField.isText(value.town) && TextField.isText(value.postcode);
237
239
  }
238
-
239
- // eslint-disable-next-line @typescript-eslint/require-await
240
240
  static async dispatcher(request, h, args) {
241
241
  const {
242
242
  controller,
243
243
  component
244
244
  } = args;
245
245
  const language = resolveLanguage(request);
246
+ const availableLanguages = getAvailableLanguages(controller.model.def);
247
+ const languages = availableLanguages.length ? {
248
+ languages: availableLanguages
249
+ } : {};
250
+ const translator = await getCachedPluginTranslator(request, {
251
+ id: controller.model.formId
252
+ }, args.isLive ? FormStatus.Live : FormStatus.Draft, language);
253
+ const {
254
+ tComponent,
255
+ tForm
256
+ } = translator;
246
257
  return dispatch(request, h, {
247
- formName: controller.model.name,
258
+ formName: tForm('title'),
259
+ formId: controller.model.formId,
260
+ formStatus: args.isLive ? FormStatus.Live : FormStatus.Draft,
261
+ componentId: component.id ?? 'unknown',
248
262
  componentName: component.name,
249
- componentHint: component.hint,
250
- componentTitle: component.title || controller.title,
263
+ componentHint: tComponent(component, 'hint'),
264
+ componentTitle: tComponent(component, 'title') || controller.title,
251
265
  step: args.actionArgs.step,
252
266
  sourceUrl: args.sourceUrl,
253
- language
267
+ language,
268
+ ...languages
254
269
  });
255
270
  }
256
271
  }
@@ -1 +1 @@
1
- {"version":3,"file":"UkAddressField.js","names":["ComponentType","lowerFirst","ComponentCollection","FormComponent","isFormState","TextField","dispatch","resolveLanguage","UkAddressField","constructor","def","props","name","options","isRequired","required","hideOptional","optionalText","hideTitle","collection","type","title","schema","classes","max","autocomplete","regex","parent","formSchema","stateSchema","getFormValueFromState","state","value","isState","undefined","getContextValueFromFormValue","Object","entries","filter","key","Boolean","map","getContextValueFromState","getDisplayStringFromFormValue","_translator","join","getDisplayStringFromState","translator","getViewErrors","errors","t","uniqueErrors","getErrors","error","index","self","findIndex","err","shouldUsePostcodeLookup","length","label","path","href","text","shortDescription","getViewModel","context","payload","viewModel","fieldset","hint","legend","id","attributes","components","uprn","at","Error","model","formGroup","usePostcodeLookup","isUkAddress","getAllPossibleErrors","ordnanceSurveyApiKey","baseErrors","template","advancedSettingsErrors","isText","addressLine1","town","postcode","dispatcher","request","h","args","controller","component","language","formName","componentName","componentHint","componentTitle","step","actionArgs","sourceUrl"],"sources":["../../../../../src/server/plugins/engine/components/UkAddressField.ts"],"sourcesContent":["import { ComponentType, type UkAddressFieldComponent } from '@defra/forms-model'\nimport { type ObjectSchema } from 'joi'\nimport lowerFirst from 'lodash/lowerFirst.js'\n\nimport { ComponentCollection } from '~/src/server/plugins/engine/components/ComponentCollection.js'\nimport {\n FormComponent,\n isFormState\n} from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { TextField } from '~/src/server/plugins/engine/components/TextField.js'\nimport { type RenderContext } from '~/src/server/plugins/engine/components/types.js'\nimport { type Translator } from '~/src/server/plugins/engine/i18n/types.js'\nimport {\n type FormRequestPayload,\n type FormResponseToolkit\n} from '~/src/server/plugins/engine/types/index.js'\nimport {\n type ErrorMessageTemplateList,\n type FormPayload,\n type FormState,\n type FormStateValue,\n type FormSubmissionError,\n type FormSubmissionState,\n type PostcodeLookupExternalArgs\n} from '~/src/server/plugins/engine/types.js'\nimport { dispatch } from '~/src/server/plugins/postcode-lookup/routes/index.js'\nimport { resolveLanguage } from '~/src/server/utils/utils.js'\n\nexport class UkAddressField extends FormComponent {\n declare options: UkAddressFieldComponent['options']\n declare formSchema: ObjectSchema<FormPayload>\n declare stateSchema: ObjectSchema<FormState>\n declare collection: ComponentCollection\n\n constructor(\n def: UkAddressFieldComponent,\n props: ConstructorParameters<typeof FormComponent>[1]\n ) {\n super(def, props)\n\n const { name, options } = def\n\n const isRequired = options.required !== false\n const hideOptional = !!options.optionalText\n const hideTitle = !!options.hideTitle\n this.collection = new ComponentCollection(\n [\n {\n type: ComponentType.TextField,\n name: `${name}__uprn`,\n title: 'components.addressField.uprn',\n schema: {},\n options: {\n required: false,\n classes: 'hidden'\n }\n },\n {\n type: ComponentType.TextField,\n name: `${name}__addressLine1`,\n title: 'components.addressField.line1',\n schema: { max: 100 },\n options: {\n autocomplete: 'address-line1',\n required: isRequired,\n optionalText: !isRequired && (hideOptional || !hideTitle)\n }\n },\n {\n type: ComponentType.TextField,\n name: `${name}__addressLine2`,\n title: 'components.addressField.line2',\n schema: { max: 100 },\n options: {\n autocomplete: 'address-line2',\n required: false,\n optionalText: !isRequired && (hideOptional || !hideTitle)\n }\n },\n {\n type: ComponentType.TextField,\n name: `${name}__town`,\n title: 'components.addressField.town',\n schema: { max: 100 },\n options: {\n autocomplete: 'address-level2',\n classes: 'govuk-!-width-two-thirds',\n required: isRequired,\n optionalText: !isRequired && (hideOptional || !hideTitle)\n }\n },\n {\n type: ComponentType.TextField,\n name: `${name}__county`,\n title: 'components.addressField.county',\n schema: { max: 100 },\n options: {\n autocomplete: 'address-level1',\n required: false,\n optionalText: !isRequired && (hideOptional || !hideTitle)\n }\n },\n {\n type: ComponentType.TextField,\n name: `${name}__postcode`,\n title: 'components.addressField.postcode',\n schema: {\n regex: '^[a-zA-Z]{1,2}\\\\d[a-zA-Z\\\\d]?\\\\s?\\\\d[a-zA-Z]{2}$'\n },\n options: {\n autocomplete: 'postal-code',\n classes: 'govuk-input--width-10',\n required: isRequired,\n optionalText: !isRequired && (hideOptional || !hideTitle)\n }\n }\n ],\n { ...props, parent: this }\n )\n\n this.options = options\n this.formSchema = this.collection.formSchema\n this.stateSchema = this.collection.stateSchema\n }\n\n getFormValueFromState(state: FormSubmissionState) {\n const value = super.getFormValueFromState(state)\n return this.isState(value) ? value : undefined\n }\n\n getContextValueFromFormValue(value: UkAddressState | undefined) {\n if (!value) {\n return null\n }\n\n return Object.entries(value)\n .filter(([key, value]) => key !== 'uprn' && Boolean(value))\n .map(([, value]) => value)\n }\n\n getContextValueFromState(state: FormSubmissionState) {\n const value = this.getFormValueFromState(state)\n\n return this.getContextValueFromFormValue(value)\n }\n\n getDisplayStringFromFormValue(\n value: UkAddressState | undefined,\n _translator: Translator\n ): string {\n return this.getContextValueFromFormValue(value)?.join(', ') ?? ''\n }\n\n getDisplayStringFromState(\n state: FormSubmissionState,\n translator: Translator\n ) {\n const value = this.getFormValueFromState(state)\n\n return this.getDisplayStringFromFormValue(value, translator)\n }\n\n /**\n * Returns one error per child field\n */\n getViewErrors(\n translator: Translator,\n errors?: FormSubmissionError[]\n ): FormSubmissionError[] | undefined {\n const { t } = translator\n\n const uniqueErrors = this.getErrors(translator, errors)?.filter(\n (error, index, self) =>\n index === self.findIndex((err) => err.name === error.name)\n )\n\n // When using postcode lookup, the address fields are hidden\n // so we replace any individual validation messages with a single one\n if (this.shouldUsePostcodeLookup() && uniqueErrors?.length) {\n const { name, label } = this\n\n return [\n {\n name,\n path: [name],\n href: `#${name}`,\n text: t('components.addressField.enterAddress', {\n shortDescription: lowerFirst(label)\n })\n }\n ]\n }\n\n return uniqueErrors\n }\n\n getViewModel(context: RenderContext) {\n const { payload, translator } = context\n const { collection, name, options } = this\n\n const viewModel = super.getViewModel(context)\n let { fieldset, hint, label } = viewModel\n\n fieldset ??= {\n legend: {\n text: label.text,\n\n /**\n * For screen readers, only hide legend visually. This can be overridden\n * by single component {@link QuestionPageController | `showTitle` handling}\n */\n classes: options.hideTitle\n ? 'govuk-visually-hidden'\n : 'govuk-fieldset__legend--m'\n }\n }\n\n if (hint) {\n hint.id ??= `${name}-hint`\n fieldset.attributes ??= {\n 'aria-describedby': hint.id\n }\n }\n\n const components = collection.getViewModel(context)\n\n // Hide UPRN\n const uprn = components.at(0)\n\n if (!uprn) {\n throw new Error('No UPRN')\n }\n\n uprn.model.formGroup = { classes: 'app-hidden' }\n\n // Postcode lookup\n const usePostcodeLookup = this.shouldUsePostcodeLookup()\n\n const value = usePostcodeLookup\n ? this.getDisplayStringFromState(payload, translator)\n : undefined\n\n return {\n ...viewModel,\n value,\n fieldset,\n components,\n usePostcodeLookup\n }\n }\n\n isState(value?: FormStateValue | FormState): value is UkAddressState {\n return UkAddressField.isUkAddress(value)\n }\n\n /**\n * For error preview page that shows all possible errors on a component\n */\n getAllPossibleErrors(): ErrorMessageTemplateList {\n return UkAddressField.getAllPossibleErrors()\n }\n\n private shouldUsePostcodeLookup() {\n return !!(this.options.usePostcodeLookup && this.model.ordnanceSurveyApiKey)\n }\n\n /**\n * Static version of getAllPossibleErrors that doesn't require a component instance.\n */\n static getAllPossibleErrors(): ErrorMessageTemplateList {\n return {\n baseErrors: [\n { type: 'required', template: 'Enter address line 1' },\n { type: 'required', template: 'Enter town or city' },\n { type: 'required', template: 'Enter postcode' },\n { type: 'format', template: 'Enter valid postcode' }\n ],\n advancedSettingsErrors: []\n }\n }\n\n static isUkAddress(\n value?: FormStateValue | FormState\n ): value is UkAddressState {\n return (\n isFormState(value) &&\n TextField.isText(value.addressLine1) &&\n TextField.isText(value.town) &&\n TextField.isText(value.postcode)\n )\n }\n\n // eslint-disable-next-line @typescript-eslint/require-await\n static async dispatcher(\n request: FormRequestPayload,\n h: FormResponseToolkit,\n args: PostcodeLookupExternalArgs\n ) {\n const { controller, component } = args\n const language = resolveLanguage(request)\n\n return dispatch(request, h, {\n formName: controller.model.name,\n componentName: component.name,\n componentHint: component.hint,\n componentTitle: component.title || controller.title,\n step: args.actionArgs.step,\n sourceUrl: args.sourceUrl,\n language\n })\n }\n}\n\nexport interface UkAddressState extends Record<string, string> {\n uprn: string\n addressLine1: string\n addressLine2: string\n town: string\n county: string\n postcode: string\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"UkAddressField.js","names":["ComponentType","FormStatus","lowerFirst","ComponentCollection","FormComponent","isFormState","TextField","getCachedPluginTranslator","getAvailableLanguages","dispatch","resolveLanguage","UkAddressField","constructor","def","props","name","options","isRequired","required","hideOptional","optionalText","hideTitle","collection","type","title","schema","classes","max","autocomplete","regex","parent","formSchema","stateSchema","getFormValueFromState","state","value","isState","undefined","getContextValueFromFormValue","Object","entries","filter","key","Boolean","map","getContextValueFromState","getDisplayStringFromFormValue","_translator","join","getDisplayStringFromState","translator","getViewErrors","errors","t","uniqueErrors","getErrors","error","index","self","findIndex","err","shouldUsePostcodeLookup","length","label","path","href","text","shortDescription","getViewModel","context","payload","viewModel","fieldset","hint","legend","id","attributes","components","uprn","at","Error","model","formGroup","usePostcodeLookup","isUkAddress","getAllPossibleErrors","ordnanceSurveyApiKey","baseErrors","template","advancedSettingsErrors","isText","addressLine1","town","postcode","dispatcher","request","h","args","controller","component","language","availableLanguages","languages","formId","isLive","Live","Draft","tComponent","tForm","formName","formStatus","componentId","componentName","componentHint","componentTitle","step","actionArgs","sourceUrl"],"sources":["../../../../../src/server/plugins/engine/components/UkAddressField.ts"],"sourcesContent":["import {\n ComponentType,\n FormStatus,\n type FormMetadata,\n type UkAddressFieldComponent\n} from '@defra/forms-model'\nimport { type ObjectSchema } from 'joi'\nimport lowerFirst from 'lodash/lowerFirst.js'\n\nimport { ComponentCollection } from '~/src/server/plugins/engine/components/ComponentCollection.js'\nimport {\n FormComponent,\n isFormState\n} from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { TextField } from '~/src/server/plugins/engine/components/TextField.js'\nimport { type RenderContext } from '~/src/server/plugins/engine/components/types.js'\nimport { getCachedPluginTranslator } from '~/src/server/plugins/engine/i18n/form.js'\nimport { getAvailableLanguages } from '~/src/server/plugins/engine/i18n/languages.js'\nimport { type Translator } from '~/src/server/plugins/engine/i18n/types.js'\nimport {\n type FormRequestPayload,\n type FormResponseToolkit\n} from '~/src/server/plugins/engine/types/index.js'\nimport {\n type ErrorMessageTemplateList,\n type FormPayload,\n type FormState,\n type FormStateValue,\n type FormSubmissionError,\n type FormSubmissionState,\n type PostcodeLookupExternalArgs\n} from '~/src/server/plugins/engine/types.js'\nimport { dispatch } from '~/src/server/plugins/postcode-lookup/routes/index.js'\nimport { resolveLanguage } from '~/src/server/utils/utils.js'\n\nexport class UkAddressField extends FormComponent {\n declare options: UkAddressFieldComponent['options']\n declare formSchema: ObjectSchema<FormPayload>\n declare stateSchema: ObjectSchema<FormState>\n declare collection: ComponentCollection\n\n constructor(\n def: UkAddressFieldComponent,\n props: ConstructorParameters<typeof FormComponent>[1]\n ) {\n super(def, props)\n\n const { name, options } = def\n\n const isRequired = options.required !== false\n const hideOptional = !!options.optionalText\n const hideTitle = !!options.hideTitle\n this.collection = new ComponentCollection(\n [\n {\n type: ComponentType.TextField,\n name: `${name}__uprn`,\n title: 'components.addressField.uprn',\n schema: {},\n options: {\n required: false,\n classes: 'hidden'\n }\n },\n {\n type: ComponentType.TextField,\n name: `${name}__addressLine1`,\n title: 'components.addressField.line1',\n schema: { max: 100 },\n options: {\n autocomplete: 'address-line1',\n required: isRequired,\n optionalText: !isRequired && (hideOptional || !hideTitle)\n }\n },\n {\n type: ComponentType.TextField,\n name: `${name}__addressLine2`,\n title: 'components.addressField.line2',\n schema: { max: 100 },\n options: {\n autocomplete: 'address-line2',\n required: false,\n optionalText: !isRequired && (hideOptional || !hideTitle)\n }\n },\n {\n type: ComponentType.TextField,\n name: `${name}__town`,\n title: 'components.addressField.town',\n schema: { max: 100 },\n options: {\n autocomplete: 'address-level2',\n classes: 'govuk-!-width-two-thirds',\n required: isRequired,\n optionalText: !isRequired && (hideOptional || !hideTitle)\n }\n },\n {\n type: ComponentType.TextField,\n name: `${name}__county`,\n title: 'components.addressField.county',\n schema: { max: 100 },\n options: {\n autocomplete: 'address-level1',\n required: false,\n optionalText: !isRequired && (hideOptional || !hideTitle)\n }\n },\n {\n type: ComponentType.TextField,\n name: `${name}__postcode`,\n title: 'components.addressField.postcode',\n schema: {\n regex: '^[a-zA-Z]{1,2}\\\\d[a-zA-Z\\\\d]?\\\\s?\\\\d[a-zA-Z]{2}$'\n },\n options: {\n autocomplete: 'postal-code',\n classes: 'govuk-input--width-10',\n required: isRequired,\n optionalText: !isRequired && (hideOptional || !hideTitle)\n }\n }\n ],\n { ...props, parent: this }\n )\n\n this.options = options\n this.formSchema = this.collection.formSchema\n this.stateSchema = this.collection.stateSchema\n }\n\n getFormValueFromState(state: FormSubmissionState) {\n const value = super.getFormValueFromState(state)\n return this.isState(value) ? value : undefined\n }\n\n getContextValueFromFormValue(value: UkAddressState | undefined) {\n if (!value) {\n return null\n }\n\n return Object.entries(value)\n .filter(([key, value]) => key !== 'uprn' && Boolean(value))\n .map(([, value]) => value)\n }\n\n getContextValueFromState(state: FormSubmissionState) {\n const value = this.getFormValueFromState(state)\n\n return this.getContextValueFromFormValue(value)\n }\n\n getDisplayStringFromFormValue(\n value: UkAddressState | undefined,\n _translator: Translator\n ): string {\n return this.getContextValueFromFormValue(value)?.join(', ') ?? ''\n }\n\n getDisplayStringFromState(\n state: FormSubmissionState,\n translator: Translator\n ) {\n const value = this.getFormValueFromState(state)\n\n return this.getDisplayStringFromFormValue(value, translator)\n }\n\n /**\n * Returns one error per child field\n */\n getViewErrors(\n translator: Translator,\n errors?: FormSubmissionError[]\n ): FormSubmissionError[] | undefined {\n const { t } = translator\n\n const uniqueErrors = this.getErrors(translator, errors)?.filter(\n (error, index, self) =>\n index === self.findIndex((err) => err.name === error.name)\n )\n\n // When using postcode lookup, the address fields are hidden\n // so we replace any individual validation messages with a single one\n if (this.shouldUsePostcodeLookup() && uniqueErrors?.length) {\n const { name, label } = this\n\n return [\n {\n name,\n path: [name],\n href: `#${name}`,\n text: t('components.addressField.enterAddress', {\n shortDescription: lowerFirst(label)\n })\n }\n ]\n }\n\n return uniqueErrors\n }\n\n getViewModel(context: RenderContext) {\n const { payload, translator } = context\n const { collection, name, options } = this\n\n const viewModel = super.getViewModel(context)\n let { fieldset, hint, label } = viewModel\n\n fieldset ??= {\n legend: {\n text: label.text,\n\n /**\n * For screen readers, only hide legend visually. This can be overridden\n * by single component (see QuestionPageController and showTitle handling)\n */\n classes: options.hideTitle\n ? 'govuk-visually-hidden'\n : 'govuk-fieldset__legend--m'\n }\n }\n\n if (hint) {\n hint.id ??= `${name}-hint`\n fieldset.attributes ??= {\n 'aria-describedby': hint.id\n }\n }\n\n const components = collection.getViewModel(context)\n\n // Hide UPRN\n const uprn = components.at(0)\n\n if (!uprn) {\n throw new Error('No UPRN')\n }\n\n uprn.model.formGroup = { classes: 'app-hidden' }\n\n // Postcode lookup\n const usePostcodeLookup = this.shouldUsePostcodeLookup()\n\n const value = usePostcodeLookup\n ? this.getDisplayStringFromState(payload, translator)\n : undefined\n\n return {\n ...viewModel,\n value,\n fieldset,\n components,\n usePostcodeLookup\n }\n }\n\n isState(value?: FormStateValue | FormState): value is UkAddressState {\n return UkAddressField.isUkAddress(value)\n }\n\n /**\n * For error preview page that shows all possible errors on a component\n */\n getAllPossibleErrors(): ErrorMessageTemplateList {\n return UkAddressField.getAllPossibleErrors()\n }\n\n private shouldUsePostcodeLookup() {\n return !!(this.options.usePostcodeLookup && this.model.ordnanceSurveyApiKey)\n }\n\n /**\n * Static version of getAllPossibleErrors that doesn't require a component instance.\n */\n static getAllPossibleErrors(): ErrorMessageTemplateList {\n return {\n baseErrors: [\n { type: 'required', template: 'Enter address line 1' },\n { type: 'required', template: 'Enter town or city' },\n { type: 'required', template: 'Enter postcode' },\n { type: 'format', template: 'Enter valid postcode' }\n ],\n advancedSettingsErrors: []\n }\n }\n\n static isUkAddress(\n value?: FormStateValue | FormState\n ): value is UkAddressState {\n return (\n isFormState(value) &&\n TextField.isText(value.addressLine1) &&\n TextField.isText(value.town) &&\n TextField.isText(value.postcode)\n )\n }\n\n static async dispatcher(\n request: FormRequestPayload,\n h: FormResponseToolkit,\n args: PostcodeLookupExternalArgs\n ) {\n const { controller, component } = args\n const language = resolveLanguage(request)\n\n const availableLanguages = getAvailableLanguages(controller.model.def)\n const languages = availableLanguages.length\n ? { languages: availableLanguages }\n : {}\n\n const translator = await getCachedPluginTranslator(\n request,\n { id: controller.model.formId } as unknown as FormMetadata,\n args.isLive ? FormStatus.Live : FormStatus.Draft,\n language\n )\n const { tComponent, tForm } = translator\n\n return dispatch(request, h, {\n formName: tForm('title'),\n formId: controller.model.formId,\n formStatus: args.isLive ? FormStatus.Live : FormStatus.Draft,\n componentId: component.id ?? 'unknown',\n componentName: component.name,\n componentHint: tComponent(component, 'hint'),\n componentTitle: tComponent(component, 'title') || controller.title,\n step: args.actionArgs.step,\n sourceUrl: args.sourceUrl,\n language,\n ...languages\n })\n }\n}\n\nexport interface UkAddressState extends Record<string, string> {\n uprn: string\n addressLine1: string\n addressLine2: string\n town: string\n county: string\n postcode: string\n}\n"],"mappings":"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","ignoreList":[]}
@@ -1,5 +1,4 @@
1
1
  import { type YesNoFieldComponent } from '@defra/forms-model';
2
- import { type LanguageMessages } from 'joi';
3
2
  import { SelectionControlField } from '../../../../server/plugins/engine/components/SelectionControlField.js';
4
3
  import { type Translator } from '../../../../server/plugins/engine/i18n/types.js';
5
4
  import { type ErrorMessageTemplateList } from '../../../../server/plugins/engine/types.js';
@@ -10,7 +9,11 @@ import { type ErrorMessageTemplateList } from '../../../../server/plugins/engine
10
9
  export declare class YesNoField extends SelectionControlField {
11
10
  options: YesNoFieldComponent['options'];
12
11
  constructor(def: YesNoFieldComponent, props: ConstructorParameters<typeof SelectionControlField>[1]);
13
- getValidationMessagesOverride(translator: Translator): LanguageMessages;
12
+ getValidationMessagesOverride(translator: Translator): {
13
+ [x: string]: {
14
+ 'any.required': string;
15
+ };
16
+ };
14
17
  /**
15
18
  * For error preview page that shows all possible errors on a component
16
19
  */
@@ -3,8 +3,6 @@ import { SelectionControlField } from "./SelectionControlField.js";
3
3
  import { addClassOptionIfNone } from "./helpers/index.js";
4
4
  import { buildValidationMessages } from "../i18n/buildValidationMessages.js";
5
5
  import { messageTemplate } from "../pageControllers/validationOptions.js";
6
- import { convertToLanguageMessages } from "../../../utils/type-utils.js";
7
-
8
6
  /**
9
7
  * @description
10
8
  * YesNoField is a radiosField with predefined values.
@@ -25,9 +23,18 @@ export class YesNoField extends SelectionControlField {
25
23
  if (options.required === false) {
26
24
  formSchema = formSchema.optional();
27
25
  }
28
- formSchema = formSchema.messages(convertToLanguageMessages({
29
- 'any.required': messageTemplate.selectYesNoRequired
30
- }));
26
+ if (options.customValidationMessage) {
27
+ const message = options.customValidationMessage;
28
+ formSchema = formSchema.messages({
29
+ 'any.required': message
30
+ });
31
+ } else if (options.customValidationMessages) {
32
+ formSchema = formSchema.messages(options.customValidationMessages);
33
+ } else {
34
+ formSchema = formSchema.messages({
35
+ 'any.required': messageTemplate.selectYesNoRequired
36
+ });
37
+ }
31
38
  this.formSchema = formSchema;
32
39
  this.options = options;
33
40
  }
@@ -35,9 +42,11 @@ export class YesNoField extends SelectionControlField {
35
42
  const {
36
43
  selectYesNoRequired
37
44
  } = buildValidationMessages(translator.t);
38
- return convertToLanguageMessages({
39
- 'any.required': selectYesNoRequired
40
- });
45
+ return {
46
+ [this.name]: {
47
+ 'any.required': selectYesNoRequired
48
+ }
49
+ };
41
50
  }
42
51
 
43
52
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"YesNoField.js","names":["SchemaVersion","yesNoListId","yesNoListName","SelectionControlField","addClassOptionIfNone","buildValidationMessages","messageTemplate","convertToLanguageMessages","YesNoField","constructor","def","props","list","model","schemaVersion","V1","options","formSchema","required","optional","messages","selectYesNoRequired","getValidationMessagesOverride","translator","t","getAllPossibleErrors","baseErrors","type","template","advancedSettingsErrors"],"sources":["../../../../../src/server/plugins/engine/components/YesNoField.ts"],"sourcesContent":["import {\n SchemaVersion,\n yesNoListId,\n yesNoListName,\n type YesNoFieldComponent\n} from '@defra/forms-model'\nimport { type LanguageMessages } from 'joi'\n\nimport { SelectionControlField } from '~/src/server/plugins/engine/components/SelectionControlField.js'\nimport { addClassOptionIfNone } from '~/src/server/plugins/engine/components/helpers/index.js'\nimport { buildValidationMessages } from '~/src/server/plugins/engine/i18n/buildValidationMessages.js'\nimport { type Translator } from '~/src/server/plugins/engine/i18n/types.js'\nimport { messageTemplate } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport { type ErrorMessageTemplateList } from '~/src/server/plugins/engine/types.js'\nimport { convertToLanguageMessages } from '~/src/server/utils/type-utils.js'\n\n/**\n * @description\n * YesNoField is a radiosField with predefined values.\n */\nexport class YesNoField extends SelectionControlField {\n declare options: YesNoFieldComponent['options']\n\n constructor(\n def: YesNoFieldComponent,\n props: ConstructorParameters<typeof SelectionControlField>[1]\n ) {\n super(\n {\n ...def,\n list:\n props.model.schemaVersion === SchemaVersion.V1\n ? yesNoListName\n : yesNoListId\n },\n props\n )\n\n const { options } = def\n let { formSchema } = this\n\n addClassOptionIfNone(options, 'govuk-radios--inline')\n\n if (options.required === false) {\n formSchema = formSchema.optional()\n }\n\n formSchema = formSchema.messages(\n convertToLanguageMessages({\n 'any.required': messageTemplate.selectYesNoRequired\n })\n )\n\n this.formSchema = formSchema\n this.options = options\n }\n\n getValidationMessagesOverride(translator: Translator): LanguageMessages {\n const { selectYesNoRequired } = buildValidationMessages(translator.t)\n return convertToLanguageMessages({\n 'any.required': selectYesNoRequired\n })\n }\n\n /**\n * For error preview page that shows all possible errors on a component\n */\n getAllPossibleErrors(): ErrorMessageTemplateList {\n return YesNoField.getAllPossibleErrors()\n }\n\n /**\n * Static version of getAllPossibleErrors that doesn't require a component instance.\n */\n static getAllPossibleErrors(): ErrorMessageTemplateList {\n return {\n baseErrors: [\n {\n type: 'selectYesNoRequired',\n template: messageTemplate.selectYesNoRequired\n }\n ],\n advancedSettingsErrors: []\n }\n }\n}\n"],"mappings":"AAAA,SACEA,aAAa,EACbC,WAAW,EACXC,aAAa,QAER,oBAAoB;AAG3B,SAASC,qBAAqB;AAC9B,SAASC,oBAAoB;AAC7B,SAASC,uBAAuB;AAEhC,SAASC,eAAe;AAExB,SAASC,yBAAyB;;AAElC;AACA;AACA;AACA;AACA,OAAO,MAAMC,UAAU,SAASL,qBAAqB,CAAC;EAGpDM,WAAWA,CACTC,GAAwB,EACxBC,KAA6D,EAC7D;IACA,KAAK,CACH;MACE,GAAGD,GAAG;MACNE,IAAI,EACFD,KAAK,CAACE,KAAK,CAACC,aAAa,KAAKd,aAAa,CAACe,EAAE,GAC1Cb,aAAa,GACbD;IACR,CAAC,EACDU,KACF,CAAC;IAED,MAAM;MAAEK;IAAQ,CAAC,GAAGN,GAAG;IACvB,IAAI;MAAEO;IAAW,CAAC,GAAG,IAAI;IAEzBb,oBAAoB,CAACY,OAAO,EAAE,sBAAsB,CAAC;IAErD,IAAIA,OAAO,CAACE,QAAQ,KAAK,KAAK,EAAE;MAC9BD,UAAU,GAAGA,UAAU,CAACE,QAAQ,CAAC,CAAC;IACpC;IAEAF,UAAU,GAAGA,UAAU,CAACG,QAAQ,CAC9Bb,yBAAyB,CAAC;MACxB,cAAc,EAAED,eAAe,CAACe;IAClC,CAAC,CACH,CAAC;IAED,IAAI,CAACJ,UAAU,GAAGA,UAAU;IAC5B,IAAI,CAACD,OAAO,GAAGA,OAAO;EACxB;EAEAM,6BAA6BA,CAACC,UAAsB,EAAoB;IACtE,MAAM;MAAEF;IAAoB,CAAC,GAAGhB,uBAAuB,CAACkB,UAAU,CAACC,CAAC,CAAC;IACrE,OAAOjB,yBAAyB,CAAC;MAC/B,cAAc,EAAEc;IAClB,CAAC,CAAC;EACJ;;EAEA;AACF;AACA;EACEI,oBAAoBA,CAAA,EAA6B;IAC/C,OAAOjB,UAAU,CAACiB,oBAAoB,CAAC,CAAC;EAC1C;;EAEA;AACF;AACA;EACE,OAAOA,oBAAoBA,CAAA,EAA6B;IACtD,OAAO;MACLC,UAAU,EAAE,CACV;QACEC,IAAI,EAAE,qBAAqB;QAC3BC,QAAQ,EAAEtB,eAAe,CAACe;MAC5B,CAAC,CACF;MACDQ,sBAAsB,EAAE;IAC1B,CAAC;EACH;AACF","ignoreList":[]}
1
+ {"version":3,"file":"YesNoField.js","names":["SchemaVersion","yesNoListId","yesNoListName","SelectionControlField","addClassOptionIfNone","buildValidationMessages","messageTemplate","YesNoField","constructor","def","props","list","model","schemaVersion","V1","options","formSchema","required","optional","customValidationMessage","message","messages","customValidationMessages","selectYesNoRequired","getValidationMessagesOverride","translator","t","name","getAllPossibleErrors","baseErrors","type","template","advancedSettingsErrors"],"sources":["../../../../../src/server/plugins/engine/components/YesNoField.ts"],"sourcesContent":["import {\n SchemaVersion,\n yesNoListId,\n yesNoListName,\n type YesNoFieldComponent\n} from '@defra/forms-model'\n\nimport { SelectionControlField } from '~/src/server/plugins/engine/components/SelectionControlField.js'\nimport { addClassOptionIfNone } from '~/src/server/plugins/engine/components/helpers/index.js'\nimport { buildValidationMessages } from '~/src/server/plugins/engine/i18n/buildValidationMessages.js'\nimport { type Translator } from '~/src/server/plugins/engine/i18n/types.js'\nimport { messageTemplate } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport { type ErrorMessageTemplateList } from '~/src/server/plugins/engine/types.js'\n\n/**\n * @description\n * YesNoField is a radiosField with predefined values.\n */\nexport class YesNoField extends SelectionControlField {\n declare options: YesNoFieldComponent['options']\n\n constructor(\n def: YesNoFieldComponent,\n props: ConstructorParameters<typeof SelectionControlField>[1]\n ) {\n super(\n {\n ...def,\n list:\n props.model.schemaVersion === SchemaVersion.V1\n ? yesNoListName\n : yesNoListId\n },\n props\n )\n\n const { options } = def\n let { formSchema } = this\n\n addClassOptionIfNone(options, 'govuk-radios--inline')\n\n if (options.required === false) {\n formSchema = formSchema.optional()\n }\n\n if (options.customValidationMessage) {\n const message = options.customValidationMessage\n\n formSchema = formSchema.messages({\n 'any.required': message\n })\n } else if (options.customValidationMessages) {\n formSchema = formSchema.messages(options.customValidationMessages)\n } else {\n formSchema = formSchema.messages({\n 'any.required': messageTemplate.selectYesNoRequired\n })\n }\n\n this.formSchema = formSchema\n this.options = options\n }\n\n getValidationMessagesOverride(translator: Translator) {\n const { selectYesNoRequired } = buildValidationMessages(translator.t)\n return {\n [this.name]: {\n 'any.required': selectYesNoRequired\n }\n }\n }\n\n /**\n * For error preview page that shows all possible errors on a component\n */\n getAllPossibleErrors(): ErrorMessageTemplateList {\n return YesNoField.getAllPossibleErrors()\n }\n\n /**\n * Static version of getAllPossibleErrors that doesn't require a component instance.\n */\n static getAllPossibleErrors(): ErrorMessageTemplateList {\n return {\n baseErrors: [\n {\n type: 'selectYesNoRequired',\n template: messageTemplate.selectYesNoRequired\n }\n ],\n advancedSettingsErrors: []\n }\n }\n}\n"],"mappings":"AAAA,SACEA,aAAa,EACbC,WAAW,EACXC,aAAa,QAER,oBAAoB;AAE3B,SAASC,qBAAqB;AAC9B,SAASC,oBAAoB;AAC7B,SAASC,uBAAuB;AAEhC,SAASC,eAAe;AAGxB;AACA;AACA;AACA;AACA,OAAO,MAAMC,UAAU,SAASJ,qBAAqB,CAAC;EAGpDK,WAAWA,CACTC,GAAwB,EACxBC,KAA6D,EAC7D;IACA,KAAK,CACH;MACE,GAAGD,GAAG;MACNE,IAAI,EACFD,KAAK,CAACE,KAAK,CAACC,aAAa,KAAKb,aAAa,CAACc,EAAE,GAC1CZ,aAAa,GACbD;IACR,CAAC,EACDS,KACF,CAAC;IAED,MAAM;MAAEK;IAAQ,CAAC,GAAGN,GAAG;IACvB,IAAI;MAAEO;IAAW,CAAC,GAAG,IAAI;IAEzBZ,oBAAoB,CAACW,OAAO,EAAE,sBAAsB,CAAC;IAErD,IAAIA,OAAO,CAACE,QAAQ,KAAK,KAAK,EAAE;MAC9BD,UAAU,GAAGA,UAAU,CAACE,QAAQ,CAAC,CAAC;IACpC;IAEA,IAAIH,OAAO,CAACI,uBAAuB,EAAE;MACnC,MAAMC,OAAO,GAAGL,OAAO,CAACI,uBAAuB;MAE/CH,UAAU,GAAGA,UAAU,CAACK,QAAQ,CAAC;QAC/B,cAAc,EAAED;MAClB,CAAC,CAAC;IACJ,CAAC,MAAM,IAAIL,OAAO,CAACO,wBAAwB,EAAE;MAC3CN,UAAU,GAAGA,UAAU,CAACK,QAAQ,CAACN,OAAO,CAACO,wBAAwB,CAAC;IACpE,CAAC,MAAM;MACLN,UAAU,GAAGA,UAAU,CAACK,QAAQ,CAAC;QAC/B,cAAc,EAAEf,eAAe,CAACiB;MAClC,CAAC,CAAC;IACJ;IAEA,IAAI,CAACP,UAAU,GAAGA,UAAU;IAC5B,IAAI,CAACD,OAAO,GAAGA,OAAO;EACxB;EAEAS,6BAA6BA,CAACC,UAAsB,EAAE;IACpD,MAAM;MAAEF;IAAoB,CAAC,GAAGlB,uBAAuB,CAACoB,UAAU,CAACC,CAAC,CAAC;IACrE,OAAO;MACL,CAAC,IAAI,CAACC,IAAI,GAAG;QACX,cAAc,EAAEJ;MAClB;IACF,CAAC;EACH;;EAEA;AACF;AACA;EACEK,oBAAoBA,CAAA,EAA6B;IAC/C,OAAOrB,UAAU,CAACqB,oBAAoB,CAAC,CAAC;EAC1C;;EAEA;AACF;AACA;EACE,OAAOA,oBAAoBA,CAAA,EAA6B;IACtD,OAAO;MACLC,UAAU,EAAE,CACV;QACEC,IAAI,EAAE,qBAAqB;QAC3BC,QAAQ,EAAEzB,eAAe,CAACiB;MAC5B,CAAC,CACF;MACDS,sBAAsB,EAAE;IAC1B,CAAC;EACH;AACF","ignoreList":[]}
@@ -4,6 +4,7 @@ import { ListFormComponent } from "../ListFormComponent.js";
4
4
  import { escapeMarkdown } from "./index.js";
5
5
  import * as Components from "../index.js";
6
6
  import { markdown } from "../markdownParser.js";
7
+ import { UnknownComponentTypeError } from "../../errors.js";
7
8
 
8
9
  // All component instances
9
10
 
@@ -121,7 +122,7 @@ export function createComponent(def, options) {
121
122
  break;
122
123
  }
123
124
  if (typeof component === 'undefined') {
124
- throw new Error(`Component type ${def.type} does not exist`);
125
+ throw new UnknownComponentTypeError(def.type);
125
126
  }
126
127
  return component;
127
128
  }
@@ -222,8 +223,7 @@ export function getAnswerMarkdown(field, state, translator, options = {
222
223
  // Format UK addresses into new lines
223
224
  answerEscaped = (field.getContextValueFromState(state) ?? []).map(escapeMarkdown).join('\n').concat('\n');
224
225
  } else if (field instanceof Components.EastingNorthingField || field instanceof Components.LatLongField) {
225
- const contextValue = field.getContextValueFromState(state);
226
- answerEscaped = contextValue ? `${contextValue}\n` : '';
226
+ answerEscaped = answer ? `${answer}\n` : '';
227
227
  } else if (field instanceof Components.GeospatialField) {
228
228
  const features = field.getFormValueFromState(state);
229
229
  const value = field.getDisplayStringFromFormValue(features, translator);