@defra/forms-engine-plugin 0.0.4 → 0.0.6

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 (260) hide show
  1. package/.server/server/index.js +0 -4
  2. package/.server/server/index.js.map +1 -1
  3. package/.server/server/plugins/engine/helpers.js +3 -0
  4. package/.server/server/plugins/engine/helpers.js.map +1 -1
  5. package/.server/server/plugins/engine/index.js +27 -1
  6. package/.server/server/plugins/engine/index.js.map +1 -1
  7. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js +2 -4
  8. package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js.map +1 -1
  9. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js +4 -10
  10. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -1
  11. package/.server/server/plugins/engine/pageControllers/StatusPageController.js +2 -3
  12. package/.server/server/plugins/engine/pageControllers/StatusPageController.js.map +1 -1
  13. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js +2 -4
  14. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js.map +1 -1
  15. package/.server/server/plugins/engine/plugin.js +65 -6
  16. package/.server/server/plugins/engine/plugin.js.map +1 -1
  17. package/.server/server/plugins/engine/types.js.map +1 -1
  18. package/.server/server/{views → plugins/engine/views}/components/service-banner/template.test.js +1 -1
  19. package/.server/server/plugins/engine/views/components/service-banner/template.test.js.map +1 -0
  20. package/.server/server/{views → plugins/engine/views}/components/tag-env/template.test.js +1 -1
  21. package/.server/server/plugins/engine/views/components/tag-env/template.test.js.map +1 -0
  22. package/.server/server/services/cacheService.js +5 -2
  23. package/.server/server/services/cacheService.js.map +1 -1
  24. package/.server/typings/hapi/index.d.js.map +1 -1
  25. package/README.md +215 -4
  26. package/package.json +3 -3
  27. package/src/client/javascripts/application.js +87 -0
  28. package/src/client/javascripts/file-upload.js +386 -0
  29. package/src/client/stylesheets/_code.scss +33 -0
  30. package/src/client/stylesheets/_govuk-frontend.scss +4 -0
  31. package/src/client/stylesheets/_prose.scss +56 -0
  32. package/src/client/stylesheets/_service-banner.scss +24 -0
  33. package/src/client/stylesheets/_summary-list.scss +28 -0
  34. package/src/client/stylesheets/_tag-env.scss +24 -0
  35. package/src/client/stylesheets/application.scss +14 -0
  36. package/src/common/cookies.js +58 -0
  37. package/src/common/cookies.test.js +23 -0
  38. package/src/common/types.js +5 -0
  39. package/src/config/index.ts +271 -0
  40. package/src/index.ts +31 -0
  41. package/src/server/common/helpers/logging/logger-options.test.ts +50 -0
  42. package/src/server/common/helpers/logging/logger-options.ts +46 -0
  43. package/src/server/common/helpers/logging/logger.ts +7 -0
  44. package/src/server/common/helpers/logging/request-logger.ts +9 -0
  45. package/src/server/common/helpers/logging/request-tracing.js +10 -0
  46. package/src/server/common/helpers/redis-client.js +70 -0
  47. package/src/server/constants.js +1 -0
  48. package/src/server/forms/README.md +10 -0
  49. package/src/server/forms/components.json +1015 -0
  50. package/src/server/forms/report-a-terrorist.json +270 -0
  51. package/src/server/forms/runner-components-test.json +365 -0
  52. package/src/server/forms/test.json +581 -0
  53. package/src/server/index.test.ts +582 -0
  54. package/src/server/index.ts +135 -0
  55. package/src/server/plugins/blankie.test.ts +73 -0
  56. package/src/server/plugins/blankie.ts +48 -0
  57. package/src/server/plugins/crumb.ts +20 -0
  58. package/src/server/plugins/engine/README.md +87 -0
  59. package/src/server/plugins/engine/components/AutocompleteField.test.ts +294 -0
  60. package/src/server/plugins/engine/components/AutocompleteField.ts +49 -0
  61. package/src/server/plugins/engine/components/CheckboxesField.test.ts +379 -0
  62. package/src/server/plugins/engine/components/CheckboxesField.ts +106 -0
  63. package/src/server/plugins/engine/components/ComponentBase.ts +97 -0
  64. package/src/server/plugins/engine/components/ComponentCollection.ts +278 -0
  65. package/src/server/plugins/engine/components/DatePartsField.test.ts +822 -0
  66. package/src/server/plugins/engine/components/DatePartsField.ts +264 -0
  67. package/src/server/plugins/engine/components/Details.test.ts +49 -0
  68. package/src/server/plugins/engine/components/Details.ts +30 -0
  69. package/src/server/plugins/engine/components/EmailAddressField.test.ts +395 -0
  70. package/src/server/plugins/engine/components/EmailAddressField.ts +55 -0
  71. package/src/server/plugins/engine/components/FileUploadField.test.ts +778 -0
  72. package/src/server/plugins/engine/components/FileUploadField.ts +262 -0
  73. package/src/server/plugins/engine/components/FormComponent.ts +249 -0
  74. package/src/server/plugins/engine/components/Html.test.ts +48 -0
  75. package/src/server/plugins/engine/components/Html.ts +29 -0
  76. package/src/server/plugins/engine/components/InsetText.test.ts +48 -0
  77. package/src/server/plugins/engine/components/InsetText.ts +27 -0
  78. package/src/server/plugins/engine/components/List.test.ts +76 -0
  79. package/src/server/plugins/engine/components/List.ts +72 -0
  80. package/src/server/plugins/engine/components/ListFormComponent.ts +140 -0
  81. package/src/server/plugins/engine/components/MonthYearField.test.ts +567 -0
  82. package/src/server/plugins/engine/components/MonthYearField.ts +222 -0
  83. package/src/server/plugins/engine/components/MultilineTextField.test.ts +558 -0
  84. package/src/server/plugins/engine/components/MultilineTextField.ts +138 -0
  85. package/src/server/plugins/engine/components/NumberField.test.ts +701 -0
  86. package/src/server/plugins/engine/components/NumberField.ts +163 -0
  87. package/src/server/plugins/engine/components/RadiosField.test.ts +288 -0
  88. package/src/server/plugins/engine/components/RadiosField.ts +24 -0
  89. package/src/server/plugins/engine/components/SelectField.test.ts +288 -0
  90. package/src/server/plugins/engine/components/SelectField.ts +47 -0
  91. package/src/server/plugins/engine/components/SelectionControlField.ts +43 -0
  92. package/src/server/plugins/engine/components/TelephoneNumberField.test.ts +356 -0
  93. package/src/server/plugins/engine/components/TelephoneNumberField.ts +67 -0
  94. package/src/server/plugins/engine/components/TextField.test.ts +489 -0
  95. package/src/server/plugins/engine/components/TextField.ts +96 -0
  96. package/src/server/plugins/engine/components/UkAddressField.test.ts +623 -0
  97. package/src/server/plugins/engine/components/UkAddressField.ts +172 -0
  98. package/src/server/plugins/engine/components/YesNoField.test.ts +248 -0
  99. package/src/server/plugins/engine/components/YesNoField.ts +31 -0
  100. package/src/server/plugins/engine/components/constants.ts +1 -0
  101. package/src/server/plugins/engine/components/helpers.ts +330 -0
  102. package/src/server/plugins/engine/components/index.ts +24 -0
  103. package/src/server/plugins/engine/components/types.ts +117 -0
  104. package/src/server/plugins/engine/configureEnginePlugin.ts +47 -0
  105. package/src/server/plugins/engine/helpers.test.ts +791 -0
  106. package/src/server/plugins/engine/helpers.ts +384 -0
  107. package/src/server/plugins/engine/index.ts +47 -0
  108. package/src/server/plugins/engine/models/FormModel.test.ts +42 -0
  109. package/src/server/plugins/engine/models/FormModel.ts +443 -0
  110. package/src/server/plugins/engine/models/RepeatingSummaryViewModel.ts +0 -0
  111. package/src/server/plugins/engine/models/Section.ts +0 -0
  112. package/src/server/plugins/engine/models/SummaryViewModel.test.ts +209 -0
  113. package/src/server/plugins/engine/models/SummaryViewModel.ts +220 -0
  114. package/src/server/plugins/engine/models/index.ts +2 -0
  115. package/src/server/plugins/engine/models/types.ts +114 -0
  116. package/src/server/plugins/engine/outputFormatters/human/v1.test.ts +143 -0
  117. package/src/server/plugins/engine/outputFormatters/human/v1.ts +73 -0
  118. package/src/server/plugins/engine/outputFormatters/index.test.ts +17 -0
  119. package/src/server/plugins/engine/outputFormatters/index.ts +44 -0
  120. package/src/server/plugins/engine/outputFormatters/machine/v1.test.ts +229 -0
  121. package/src/server/plugins/engine/outputFormatters/machine/v1.ts +140 -0
  122. package/src/server/plugins/engine/outputFormatters/machine/v2.test.ts +229 -0
  123. package/src/server/plugins/engine/outputFormatters/machine/v2.ts +153 -0
  124. package/src/server/plugins/engine/pageControllers/FileUploadPageController.test.ts +1116 -0
  125. package/src/server/plugins/engine/pageControllers/FileUploadPageController.ts +447 -0
  126. package/src/server/plugins/engine/pageControllers/PageController.test.ts +205 -0
  127. package/src/server/plugins/engine/pageControllers/PageController.ts +176 -0
  128. package/src/server/plugins/engine/pageControllers/QuestionPageController.test.ts +1264 -0
  129. package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +565 -0
  130. package/src/server/plugins/engine/pageControllers/README.md +28 -0
  131. package/src/server/plugins/engine/pageControllers/RepeatPageController.test.ts +264 -0
  132. package/src/server/plugins/engine/pageControllers/RepeatPageController.ts +458 -0
  133. package/src/server/plugins/engine/pageControllers/StartPageController.ts +18 -0
  134. package/src/server/plugins/engine/pageControllers/StatusPageController.ts +51 -0
  135. package/src/server/plugins/engine/pageControllers/SummaryPageController.ts +262 -0
  136. package/src/server/plugins/engine/pageControllers/TerminalController.test.ts +28 -0
  137. package/src/server/plugins/engine/pageControllers/TerminalPageController.ts +19 -0
  138. package/src/server/plugins/engine/pageControllers/helpers.test.ts +198 -0
  139. package/src/server/plugins/engine/pageControllers/helpers.ts +101 -0
  140. package/src/server/plugins/engine/pageControllers/index.ts +10 -0
  141. package/src/server/plugins/engine/pageControllers/validationOptions.ts +89 -0
  142. package/src/server/plugins/engine/plugin.ts +753 -0
  143. package/src/server/plugins/engine/services/formSubmissionService.js +46 -0
  144. package/src/server/plugins/engine/services/formsService.js +46 -0
  145. package/src/server/plugins/engine/services/formsService.test.js +90 -0
  146. package/src/server/plugins/engine/services/index.js +3 -0
  147. package/src/server/plugins/engine/services/notifyService.test.ts +132 -0
  148. package/src/server/plugins/engine/services/notifyService.ts +64 -0
  149. package/src/server/plugins/engine/services/uploadService.js +60 -0
  150. package/src/server/plugins/engine/types.ts +317 -0
  151. package/src/server/plugins/engine/views/components/autocompletefield.html +5 -0
  152. package/src/server/plugins/engine/views/components/checkboxesfield.html +5 -0
  153. package/src/server/plugins/engine/views/components/datepartsfield.html +5 -0
  154. package/src/server/plugins/engine/views/components/debug/macro.njk +3 -0
  155. package/src/server/plugins/engine/views/components/debug/template.njk +13 -0
  156. package/src/server/plugins/engine/views/components/details.html +6 -0
  157. package/src/server/plugins/engine/views/components/emailaddressfield.html +5 -0
  158. package/src/server/plugins/engine/views/components/fileuploadfield-key.html +8 -0
  159. package/src/server/plugins/engine/views/components/fileuploadfield-value.html +3 -0
  160. package/src/server/plugins/engine/views/components/fileuploadfield.html +24 -0
  161. package/src/server/plugins/engine/views/components/html.html +3 -0
  162. package/src/server/plugins/engine/views/components/insettext.html +7 -0
  163. package/src/server/plugins/engine/views/components/list.html +36 -0
  164. package/src/server/plugins/engine/views/components/monthyearfield.html +5 -0
  165. package/src/server/plugins/engine/views/components/multilinetextfield.html +10 -0
  166. package/src/server/plugins/engine/views/components/numberfield.html +5 -0
  167. package/src/server/plugins/engine/views/components/radiosfield.html +5 -0
  168. package/src/server/plugins/engine/views/components/selectfield.html +5 -0
  169. package/src/server/plugins/engine/views/components/service-banner/macro.njk +3 -0
  170. package/src/server/plugins/engine/views/components/service-banner/template.njk +20 -0
  171. package/src/server/plugins/engine/views/components/service-banner/template.test.js +43 -0
  172. package/src/server/plugins/engine/views/components/tag-env/macro.njk +3 -0
  173. package/src/server/plugins/engine/views/components/tag-env/template.njk +30 -0
  174. package/src/server/plugins/engine/views/components/tag-env/template.test.js +66 -0
  175. package/src/server/plugins/engine/views/components/telephonenumberfield.html +5 -0
  176. package/src/server/plugins/engine/views/components/textfield.html +5 -0
  177. package/src/server/plugins/engine/views/components/ukaddressfield.html +25 -0
  178. package/src/server/plugins/engine/views/components/yesnofield.html +5 -0
  179. package/src/server/plugins/engine/views/confirmation.html +19 -0
  180. package/src/server/plugins/engine/views/file-upload.html +45 -0
  181. package/src/server/plugins/engine/views/index.html +39 -0
  182. package/src/server/plugins/engine/views/item-delete.html +56 -0
  183. package/src/server/plugins/engine/views/layout.html +199 -0
  184. package/src/server/plugins/engine/views/partials/components.html +6 -0
  185. package/src/server/plugins/engine/views/partials/conditional-components.html +3 -0
  186. package/src/server/plugins/engine/views/partials/debug.html +44 -0
  187. package/src/server/plugins/engine/views/partials/form.html +15 -0
  188. package/src/server/plugins/engine/views/partials/heading.html +16 -0
  189. package/src/server/plugins/engine/views/partials/preview-banner.html +32 -0
  190. package/src/server/plugins/engine/views/partials/preview-banner.test.js +122 -0
  191. package/src/server/plugins/engine/views/partials/warn-missing-notification-email.html +10 -0
  192. package/src/server/plugins/engine/views/repeat-list-summary.html +53 -0
  193. package/src/server/plugins/engine/views/summary.html +50 -0
  194. package/src/server/plugins/errorPages.ts +58 -0
  195. package/src/server/plugins/nunjucks/context.js +88 -0
  196. package/src/server/plugins/nunjucks/context.test.js +142 -0
  197. package/src/server/plugins/nunjucks/enviroment.test.js +201 -0
  198. package/src/server/plugins/nunjucks/environment.js +116 -0
  199. package/src/server/plugins/nunjucks/filters/answer.js +27 -0
  200. package/src/server/plugins/nunjucks/filters/answer.test.js +89 -0
  201. package/src/server/plugins/nunjucks/filters/evaluate.js +21 -0
  202. package/src/server/plugins/nunjucks/filters/field.js +28 -0
  203. package/src/server/plugins/nunjucks/filters/field.test.js +75 -0
  204. package/src/server/plugins/nunjucks/filters/highlight.js +11 -0
  205. package/src/server/plugins/nunjucks/filters/href.js +30 -0
  206. package/src/server/plugins/nunjucks/filters/href.test.js +80 -0
  207. package/src/server/plugins/nunjucks/filters/index.js +8 -0
  208. package/src/server/plugins/nunjucks/filters/inspect.js +15 -0
  209. package/src/server/plugins/nunjucks/filters/page.js +24 -0
  210. package/src/server/plugins/nunjucks/filters/page.test.js +65 -0
  211. package/src/server/plugins/nunjucks/index.js +3 -0
  212. package/src/server/plugins/nunjucks/plugin.js +40 -0
  213. package/src/server/plugins/nunjucks/render.js +42 -0
  214. package/src/server/plugins/nunjucks/types.js +40 -0
  215. package/src/server/plugins/pulse.ts +11 -0
  216. package/src/server/plugins/router.ts +201 -0
  217. package/src/server/plugins/session.ts +28 -0
  218. package/src/server/routes/health.js +13 -0
  219. package/src/server/routes/health.test.js +35 -0
  220. package/src/server/routes/index.test.ts +125 -0
  221. package/src/server/routes/index.ts +2 -0
  222. package/src/server/routes/public.ts +47 -0
  223. package/src/server/routes/types.ts +48 -0
  224. package/src/server/schemas/index.ts +34 -0
  225. package/src/server/secure-context.js +43 -0
  226. package/src/server/services/cacheService.test.ts +277 -0
  227. package/src/server/services/cacheService.ts +138 -0
  228. package/src/server/services/httpService.test.js +491 -0
  229. package/src/server/services/httpService.ts +50 -0
  230. package/src/server/services/index.ts +1 -0
  231. package/src/server/types.ts +54 -0
  232. package/src/server/utils/notify.test.ts +37 -0
  233. package/src/server/utils/notify.ts +50 -0
  234. package/src/server/utils/secure-context/get-trust-store-certs.js +11 -0
  235. package/src/server/utils/secure-context/get-trust-store-certs.test.js +19 -0
  236. package/src/server/utils/utils.js +24 -0
  237. package/src/server/utils/utils.test.js +54 -0
  238. package/src/server/views/404.html +16 -0
  239. package/src/server/views/500.html +19 -0
  240. package/src/server/views/help/accessibility-statement.html +58 -0
  241. package/src/server/views/help/cookie-preferences.html +57 -0
  242. package/src/server/views/help/cookies.html +71 -0
  243. package/src/server/views/help/get-support.html +37 -0
  244. package/src/server/views/help/privacy-notice.html +68 -0
  245. package/src/server/views/help/terms-and-conditions.html +83 -0
  246. package/src/typings/hapi/index.d.ts +87 -0
  247. package/src/typings/hapi-tracing/index.d.ts +6 -0
  248. package/src/typings/index.d.ts +3 -0
  249. package/src/typings/joi/index.d.ts +22 -0
  250. package/.server/server/views/components/service-banner/template.test.js.map +0 -1
  251. package/.server/server/views/components/tag-env/template.test.js.map +0 -1
  252. /package/.server/server/{views → plugins/engine/views}/components/debug/macro.njk +0 -0
  253. /package/.server/server/{views → plugins/engine/views}/components/debug/template.njk +0 -0
  254. /package/.server/server/{views → plugins/engine/views}/components/service-banner/macro.njk +0 -0
  255. /package/.server/server/{views → plugins/engine/views}/components/service-banner/template.njk +0 -0
  256. /package/.server/server/{views → plugins/engine/views}/components/tag-env/macro.njk +0 -0
  257. /package/.server/server/{views → plugins/engine/views}/components/tag-env/template.njk +0 -0
  258. /package/.server/server/{views → plugins/engine/views}/confirmation.html +0 -0
  259. /package/.server/server/{views → plugins/engine/views}/layout.html +0 -0
  260. /package/.server/server/{views → plugins/engine/views}/summary.html +0 -0
@@ -0,0 +1,317 @@
1
+ import {
2
+ type ComponentDef,
3
+ type Item,
4
+ type List,
5
+ type Page
6
+ } from '@defra/forms-model'
7
+ import { type ValidationErrorItem } from 'joi'
8
+
9
+ import { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js'
10
+ import { type Component } from '~/src/server/plugins/engine/components/helpers.js'
11
+ import {
12
+ type BackLink,
13
+ type ComponentText,
14
+ type ComponentViewModel
15
+ } from '~/src/server/plugins/engine/components/types.js'
16
+ import { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'
17
+ import { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers.js'
18
+ import { type ViewContext } from '~/src/server/plugins/nunjucks/types.js'
19
+ import { type FormAction, type FormRequest } from '~/src/server/routes/types.js'
20
+
21
+ /**
22
+ * Form submission state stores the following in Redis:
23
+ * Props containing user's submitted values as `{ [inputId]: value }` or as `{ [sectionName]: { [inputName]: value } }`
24
+ * a) . e.g:
25
+ * ```ts
26
+ * {
27
+ * _C9PRHmsgt: 'Ben',
28
+ * WfLk9McjzX: 'Music',
29
+ * IK7jkUFCBL: 'Royal Academy of Music'
30
+ * }
31
+ * ```
32
+ *
33
+ * b)
34
+ * ```ts
35
+ * {
36
+ * checkBeforeYouStart: { ukPassport: true },
37
+ * applicantDetails: {
38
+ * numberOfApplicants: 1,
39
+ * phoneNumber: '77777777',
40
+ * emailAddress: 'aaa@aaa.com'
41
+ * },
42
+ * applicantOneDetails: {
43
+ * firstName: 'a',
44
+ * middleName: 'a',
45
+ * lastName: 'a',
46
+ * address: { addressLine1: 'a', addressLine2: 'a', town: 'a', postcode: 'a' }
47
+ * }
48
+ * }
49
+ * ```
50
+ */
51
+
52
+ /**
53
+ * Form submission state
54
+ */
55
+ export type FormSubmissionState = {
56
+ upload?: Record<string, TempFileState>
57
+ } & FormState
58
+
59
+ export interface FormSubmissionError
60
+ extends Pick<ValidationErrorItem, 'context' | 'path'> {
61
+ href: string // e.g: '#dateField__day'
62
+ name: string // e.g: 'dateField__day'
63
+ text: string // e.g: 'Date field must be a real date'
64
+ }
65
+
66
+ export interface FormParams {
67
+ action?: FormAction
68
+ confirm?: true
69
+ crumb?: string
70
+ itemId?: string
71
+ }
72
+
73
+ /**
74
+ * Form POST for question pages
75
+ * (after Joi has converted value types)
76
+ */
77
+ export type FormPayload = FormParams & Partial<Record<string, FormValue>>
78
+
79
+ export type FormValue =
80
+ | Item['value']
81
+ | Item['value'][]
82
+ | UploadState
83
+ | RepeatListState
84
+ | undefined
85
+
86
+ export type FormState = Partial<Record<string, FormStateValue>>
87
+ export type FormStateValue = Exclude<FormValue, undefined> | null
88
+
89
+ export interface FormValidationResult<
90
+ ValueType extends FormPayload | FormSubmissionState
91
+ > {
92
+ value: ValueType
93
+ errors: FormSubmissionError[] | undefined
94
+ }
95
+
96
+ export interface FormContext {
97
+ /**
98
+ * Evaluation form state only (filtered by visited paths),
99
+ * with values formatted for condition evaluation using
100
+ * {@link FormComponent.getContextValueFromState}
101
+ */
102
+ evaluationState: FormState
103
+
104
+ /**
105
+ * Relevant form state only (filtered by visited paths)
106
+ */
107
+ relevantState: FormState
108
+
109
+ /**
110
+ * Relevant pages only (filtered by visited paths)
111
+ */
112
+ relevantPages: PageControllerClass[]
113
+
114
+ /**
115
+ * Form submission payload (single page)
116
+ */
117
+ payload: FormPayload
118
+
119
+ /**
120
+ * Form submission state (entire form)
121
+ */
122
+ state: FormSubmissionState
123
+
124
+ /**
125
+ * Validation errors (entire form)
126
+ */
127
+ errors?: FormSubmissionError[]
128
+
129
+ /**
130
+ * Visited paths evaluated from form state
131
+ */
132
+ paths: string[]
133
+
134
+ /**
135
+ * Preview URL direct access is allowed
136
+ */
137
+ isForceAccess: boolean
138
+
139
+ /**
140
+ * Miscellaneous extra data from event responses
141
+ */
142
+ data: object
143
+
144
+ pageDefMap: Map<string, Page>
145
+ listDefMap: Map<string, List>
146
+ componentDefMap: Map<string, ComponentDef>
147
+ pageMap: Map<string, PageControllerClass>
148
+ componentMap: Map<string, Component>
149
+ }
150
+
151
+ export type FormContextRequest = (
152
+ | {
153
+ method: 'get'
154
+ payload?: undefined
155
+ }
156
+ | {
157
+ method: 'post'
158
+ payload: FormPayload
159
+ }
160
+ | {
161
+ method: FormRequest['method']
162
+ payload?: object | undefined
163
+ }
164
+ ) &
165
+ Pick<FormRequest, 'app' | 'method' | 'params' | 'path' | 'query' | 'url'>
166
+
167
+ export interface UploadInitiateResponse {
168
+ uploadId: string
169
+ uploadUrl: string
170
+ statusUrl: string
171
+ }
172
+
173
+ export enum UploadStatus {
174
+ initiated = 'initiated',
175
+ pending = 'pending',
176
+ ready = 'ready'
177
+ }
178
+
179
+ export enum FileStatus {
180
+ complete = 'complete',
181
+ rejected = 'rejected',
182
+ pending = 'pending'
183
+ }
184
+
185
+ export type UploadState = FileState[]
186
+
187
+ export type FileUpload = {
188
+ fileId: string
189
+ filename: string
190
+ contentLength: number
191
+ } & (
192
+ | {
193
+ fileStatus: FileStatus.complete | FileStatus.rejected | FileStatus.pending
194
+ errorMessage?: string
195
+ }
196
+ | {
197
+ fileStatus: FileStatus.complete
198
+ errorMessage?: undefined
199
+ }
200
+ )
201
+
202
+ export interface FileUploadMetadata {
203
+ retrievalKey: string
204
+ }
205
+
206
+ export type UploadStatusResponse =
207
+ | {
208
+ uploadStatus: UploadStatus.initiated
209
+ metadata: FileUploadMetadata
210
+ form: { file?: undefined }
211
+ }
212
+ | {
213
+ uploadStatus: UploadStatus.pending | UploadStatus.ready
214
+ metadata: FileUploadMetadata
215
+ form: { file: FileUpload }
216
+ numberOfRejectedFiles?: number
217
+ }
218
+ | {
219
+ uploadStatus: UploadStatus.ready
220
+ metadata: FileUploadMetadata
221
+ form: { file: FileUpload }
222
+ numberOfRejectedFiles: 0
223
+ }
224
+
225
+ export type UploadStatusFileResponse = Exclude<
226
+ UploadStatusResponse,
227
+ { uploadStatus: UploadStatus.initiated }
228
+ >
229
+
230
+ export interface FileState {
231
+ uploadId: string
232
+ status: UploadStatusFileResponse
233
+ }
234
+
235
+ export interface TempFileState {
236
+ upload?: UploadInitiateResponse
237
+ files: UploadState
238
+ }
239
+
240
+ export interface RepeatItemState extends FormPayload {
241
+ itemId: string
242
+ }
243
+
244
+ export type RepeatListState = RepeatItemState[]
245
+
246
+ export interface CheckAnswers {
247
+ title?: ComponentText
248
+ summaryList: SummaryList
249
+ }
250
+
251
+ export interface SummaryList {
252
+ classes?: string
253
+ rows: SummaryListRow[]
254
+ }
255
+
256
+ export interface SummaryListRow {
257
+ key: ComponentText
258
+ value: ComponentText
259
+ actions?: { items: SummaryListAction[] }
260
+ }
261
+
262
+ export type SummaryListAction = ComponentText & {
263
+ href: string
264
+ visuallyHiddenText: string
265
+ }
266
+
267
+ export interface PageViewModelBase extends Partial<ViewContext> {
268
+ page: PageController
269
+ name?: string
270
+ pageTitle: string
271
+ sectionTitle?: string
272
+ showTitle: boolean
273
+ isStartPage: boolean
274
+ backLink?: BackLink
275
+ feedbackLink?: string
276
+ serviceUrl: string
277
+ phaseTag?: string
278
+ }
279
+
280
+ export interface ItemDeletePageViewModel extends PageViewModelBase {
281
+ context: FormContext
282
+ itemTitle: string
283
+ confirmation?: ComponentText
284
+ buttonConfirm: ComponentText
285
+ buttonCancel: ComponentText
286
+ }
287
+
288
+ export interface FormPageViewModel extends PageViewModelBase {
289
+ components: ComponentViewModel[]
290
+ context: FormContext
291
+ errors?: FormSubmissionError[]
292
+ hasMissingNotificationEmail?: boolean
293
+ }
294
+
295
+ export interface RepeaterSummaryPageViewModel extends PageViewModelBase {
296
+ context: FormContext
297
+ errors?: FormSubmissionError[]
298
+ checkAnswers: CheckAnswers[]
299
+ repeatTitle: string
300
+ }
301
+
302
+ export interface FeaturedFormPageViewModel extends FormPageViewModel {
303
+ formAction?: string
304
+ formComponent: ComponentViewModel
305
+ componentsBefore: ComponentViewModel[]
306
+ uploadId: string | undefined
307
+ proxyUrl: string | null
308
+ }
309
+
310
+ export type PageViewModel =
311
+ | PageViewModelBase
312
+ | ItemDeletePageViewModel
313
+ | FormPageViewModel
314
+ | RepeaterSummaryPageViewModel
315
+ | FeaturedFormPageViewModel
316
+
317
+ export type FilterFunction = (value: unknown) => unknown
@@ -0,0 +1,5 @@
1
+ {% from "govuk/components/select/macro.njk" import govukSelect %}
2
+
3
+ {% macro AutocompleteField(component) %}
4
+ {{ govukSelect(component.model) }}
5
+ {% endmacro %}
@@ -0,0 +1,5 @@
1
+ {% from "govuk/components/checkboxes/macro.njk" import govukCheckboxes %}
2
+
3
+ {% macro CheckboxesField(component) %}
4
+ {{ govukCheckboxes(component.model) }}
5
+ {% endmacro %}
@@ -0,0 +1,5 @@
1
+ {% from "govuk/components/date-input/macro.njk" import govukDateInput %}
2
+
3
+ {% macro DatePartsField(component) %}
4
+ {{ govukDateInput(component.model) }}
5
+ {% endmacro %}
@@ -0,0 +1,3 @@
1
+ {% macro appDebug(params) %}
2
+ {%- include "./template.njk" -%}
3
+ {% endmacro %}
@@ -0,0 +1,13 @@
1
+ {%- macro _code(params) -%}
2
+ {{- params.content | inspect | safe -}}
3
+ {%- endmacro -%}
4
+
5
+ {%- macro _container(params) -%}
6
+ <code tabindex="0" class="app-code__container">
7
+ {{- _code(params) | highlight('js') | safe -}}
8
+ </code>
9
+ {%- endmacro -%}
10
+
11
+ <pre class="app-code">
12
+ {{- _container(params) -}}
13
+ </pre>
@@ -0,0 +1,6 @@
1
+ {% from "govuk/components/details/macro.njk" import govukDetails %}
2
+
3
+ {% macro Details(component) %}
4
+ {# {{ getContext(component) | dump | safe }} #}
5
+ {{ govukDetails(component.model)}}
6
+ {% endmacro %}
@@ -0,0 +1,5 @@
1
+ {% from "components/textfield.html" import TextField %}
2
+
3
+ {% macro EmailAddressField(component) %}
4
+ {{ TextField(component) }}
5
+ {% endmacro %}
@@ -0,0 +1,8 @@
1
+ {% if params.errorMessage %}
2
+ <div class="govuk-form-group govuk-form-group--error govuk-!-margin-bottom-0">
3
+ <div class="govuk-!-margin-bottom-3">{{ params.name }}</div>
4
+ <p class="govuk-error-message">{{ params.errorMessage }}</p>
5
+ </div>
6
+ {% else %}
7
+ {{ params.name }}
8
+ {% endif %}
@@ -0,0 +1,3 @@
1
+ {% from "govuk/components/tag/macro.njk" import govukTag -%}
2
+
3
+ {{ govukTag(params.tag) }}
@@ -0,0 +1,24 @@
1
+ {% from "govuk/components/summary-list/macro.njk" import govukSummaryList -%}
2
+
3
+ {% macro FileUploadField(component) %}
4
+ {% set upload = component.model.upload %}
5
+
6
+ <div id="uploadedFilesContainer">
7
+ <h2 class="govuk-heading-m">Uploaded files</h2>
8
+ {% if upload.count %}
9
+ <p class="govuk-body">{{upload.count}} file{{ "s" if upload.count > 1}} uploaded</p>
10
+ <p class="govuk-body">
11
+ </p>
12
+
13
+ <div class="govuk-visually-hidden" aria-live="polite" id="statusInformation">
14
+ {{upload.count}} file{{ "s" if upload.count > 1}} uploaded
15
+ </div>
16
+
17
+ {% if upload.summaryList.rows | length %}
18
+ {{ govukSummaryList(upload.summaryList) }}
19
+ {% endif %}
20
+ {% else %}
21
+ <p class="govuk-body">{{upload.count}} files uploaded</p>
22
+ {% endif %}
23
+ </div>
24
+ {% endmacro %}
@@ -0,0 +1,3 @@
1
+ {% macro Html(component) %}
2
+ {{ component.model.content | safe }}
3
+ {% endmacro %}
@@ -0,0 +1,7 @@
1
+ {% from "govuk/components/inset-text/macro.njk" import govukInsetText %}
2
+
3
+ {% macro InsetText(component) %}
4
+ {{ govukInsetText({
5
+ html: component.model.content
6
+ }) }}
7
+ {% endmacro %}
@@ -0,0 +1,36 @@
1
+ {% macro List(component) %}
2
+ {% set params = component.model %}
3
+
4
+ <div class="govuk-form-group">
5
+ {% if params.content.title %}
6
+ <h2 class="govuk-heading-m govuk-!-margin-bottom-3">
7
+ {{ params.content.title }}
8
+ </h2>
9
+ {% endif %}
10
+
11
+ {% if params.content.text %}
12
+ <p class="govuk-body">
13
+ {{ params.content.text }}
14
+ </p>
15
+ {% endif %}
16
+
17
+ {% if params.type == 'numbered' %}
18
+ <ol class="govuk-list govuk-list--number {%- if params.classes %} {{ params.classes }}{% endif %}">
19
+ {% else %}
20
+ <ul class="govuk-list govuk-list--bullet {%- if params.classes %} {{ params.classes }}{% endif %}">
21
+ {% endif %}
22
+ {% for item in params.items %}
23
+ <li>
24
+ {{ item.text }}
25
+ {% if item.hint.text | length %}
26
+ <br><span class="govuk-hint govuk-!-display-inline-block">{{ item.hint.text }}</span>
27
+ {% endif %}
28
+ </li>
29
+ {% endfor %}
30
+ {% if params.type == 'numbered' %}
31
+ </ol>
32
+ {% else %}
33
+ </ul>
34
+ {% endif %}
35
+ </div>
36
+ {% endmacro %}
@@ -0,0 +1,5 @@
1
+ {% from "govuk/components/date-input/macro.njk" import govukDateInput %}
2
+
3
+ {% macro MonthYearField(component) %}
4
+ {{ govukDateInput(component.model) }}
5
+ {% endmacro %}
@@ -0,0 +1,10 @@
1
+ {% from "govuk/components/textarea/macro.njk" import govukTextarea %}
2
+ {% from "govuk/components/character-count/macro.njk" import govukCharacterCount %}
3
+
4
+ {% macro MultilineTextField(component) %}
5
+ {% if component.model.isCharacterOrWordCount == true %}
6
+ {{ govukCharacterCount(component.model) }}
7
+ {% else %}
8
+ {{ govukTextarea(component.model) }}
9
+ {% endif %}
10
+ {% endmacro %}
@@ -0,0 +1,5 @@
1
+ {% from "components/textfield.html" import TextField %}
2
+
3
+ {% macro NumberField(component) %}
4
+ {{ TextField(component) }}
5
+ {% endmacro %}
@@ -0,0 +1,5 @@
1
+ {% from "govuk/components/radios/macro.njk" import govukRadios %}
2
+
3
+ {% macro RadiosField(component) %}
4
+ {{ govukRadios(component.model) }}
5
+ {% endmacro %}
@@ -0,0 +1,5 @@
1
+ {% from "govuk/components/select/macro.njk" import govukSelect %}
2
+
3
+ {% macro SelectField(component) %}
4
+ {{ govukSelect(component.model) }}
5
+ {% endmacro %}
@@ -0,0 +1,3 @@
1
+ {% macro appServiceBanner(params) %}
2
+ {%- include "./template.njk" -%}
3
+ {% endmacro %}
@@ -0,0 +1,20 @@
1
+ {% from "govuk/components/warning-text/macro.njk" import govukWarningText -%}
2
+
3
+ {%- macro _message(params) %}
4
+ <span class="govuk-!-font-weight-bold" aria-hidden="true">{{ params.title }}:</span>
5
+ <span class="app-service-banner__text">{{ params.text if params.text else params.html | safe }}</span>
6
+ {% endmacro -%}
7
+
8
+ {%- macro _iconFallback(params) %}
9
+ <span id="app-service-banner-description">{{ params.title }}</span>
10
+ {% endmacro -%}
11
+
12
+ <aside class="app-service-banner" aria-labelledby="app-service-banner-description" aria-live="polite">
13
+ <div class="govuk-width-container">
14
+ {{ govukWarningText({
15
+ html: _message(params) | trim | indent(2),
16
+ iconFallbackText: _iconFallback(params) | safe | trim,
17
+ classes: "app-service-banner__content"
18
+ }) | trim | indent(4) }}
19
+ </div>
20
+ </aside>
@@ -0,0 +1,43 @@
1
+ import { renderMacro } from '~/test/helpers/component-helpers.js'
2
+
3
+ describe('Service banner component', () => {
4
+ let $component = /** @type {HTMLElement | null} */ (null)
5
+ let $content = /** @type {HTMLElement | null} */ (null)
6
+ let $text = /** @type {HTMLElement | null} */ (null)
7
+
8
+ beforeEach(() => {
9
+ const { container, document } = renderMacro(
10
+ 'appServiceBanner',
11
+ 'components/service-banner/macro.njk',
12
+ {
13
+ params: {
14
+ title: 'Service status',
15
+ text: 'This is a service status message'
16
+ }
17
+ }
18
+ )
19
+
20
+ $component = container.getByRole('complementary')
21
+ $content = document.querySelector('.app-service-banner__content')
22
+ $text = document.querySelector('.app-service-banner__text')
23
+ })
24
+
25
+ it('should render contents', () => {
26
+ expect($component).toBeInTheDocument()
27
+ expect($component).toHaveClass('app-service-banner')
28
+ expect($component).toContainElement($content)
29
+ expect($content).toContainElement($text)
30
+ })
31
+
32
+ it('should announce service message', () => {
33
+ expect($component).toHaveAttribute('aria-live', 'polite')
34
+ })
35
+
36
+ it('should have accessible name', () => {
37
+ expect($component).toHaveAccessibleName('Service status')
38
+ })
39
+
40
+ it('should have text content', () => {
41
+ expect($text).toHaveTextContent('This is a service status message')
42
+ })
43
+ })
@@ -0,0 +1,3 @@
1
+ {% macro appTagEnv(params) %}
2
+ {%- include "./template.njk" -%}
3
+ {% endmacro %}
@@ -0,0 +1,30 @@
1
+ {%- from "govuk/components/tag/macro.njk" import govukTag -%}
2
+
3
+ {%- switch params.env %}
4
+ {% case "local" %}
5
+ {% set text = "Local" %}
6
+ {% set classes = "govuk-tag--green" %}
7
+ {% case "dev" %}
8
+ {% set text = "Development" %}
9
+ {% set classes = "govuk-tag--grey" %}
10
+ {% case "test" %}
11
+ {% set text = "Test" %}
12
+ {% set classes = "govuk-tag--yellow" %}
13
+ {% case "ext-test" %}
14
+ {% set text = "External test" %}
15
+ {% set classes = "govuk-tag--yellow" %}
16
+ {% case "perf-test" %}
17
+ {% set text = "Performance test" %}
18
+ {% set classes = "govuk-tag--yellow" %}
19
+ {% case "prod" %}
20
+ {% set text = "Production" %}
21
+ {% set classes = "govuk-tag--red" %}
22
+ {% default %}
23
+ {% set text = params.env | replace("-", " ") | capitalize %}
24
+ {% set classes = "govuk-tag--grey" %}
25
+ {% endswitch -%}
26
+
27
+ {{ govukTag({
28
+ text: text,
29
+ classes: classes + " app-tag--env"
30
+ }) }}
@@ -0,0 +1,66 @@
1
+ import { renderMacro } from '~/test/helpers/component-helpers.js'
2
+
3
+ describe('Tag environment component', () => {
4
+ describe.each([
5
+ {
6
+ text: 'Local',
7
+ env: 'local',
8
+ colour: 'green'
9
+ },
10
+ {
11
+ text: 'Development',
12
+ env: 'dev',
13
+ colour: 'grey'
14
+ },
15
+ {
16
+ text: 'Test',
17
+ env: 'test',
18
+ colour: 'yellow'
19
+ },
20
+ {
21
+ text: 'External test',
22
+ env: 'ext-test',
23
+ colour: 'yellow'
24
+ },
25
+ {
26
+ text: 'Performance test',
27
+ env: 'perf-test',
28
+ colour: 'yellow'
29
+ },
30
+ {
31
+ text: 'Production',
32
+ env: 'prod',
33
+ colour: 'red'
34
+ },
35
+ {
36
+ text: 'Unknown environment',
37
+ env: 'unknown-environment',
38
+ colour: 'grey'
39
+ }
40
+ ])('Environment: $text', ({ text, env, colour }) => {
41
+ let $component = /** @type {HTMLElement | null} */ (null)
42
+
43
+ beforeEach(() => {
44
+ const { container } = renderMacro(
45
+ 'appTagEnv',
46
+ 'components/tag-env/macro.njk',
47
+ { params: { env } }
48
+ )
49
+
50
+ $component = container.getByRole('strong')
51
+ })
52
+
53
+ it('should render contents', () => {
54
+ expect($component).toBeInTheDocument()
55
+ expect($component).toHaveClass('govuk-tag')
56
+ })
57
+
58
+ it('should have text content', () => {
59
+ expect($component).toHaveTextContent(text)
60
+ })
61
+
62
+ it('should use environment colour', () => {
63
+ expect($component).toHaveClass(`govuk-tag--${colour}`)
64
+ })
65
+ })
66
+ })
@@ -0,0 +1,5 @@
1
+ {% from "components/textfield.html" import TextField %}
2
+
3
+ {% macro TelephoneNumberField(component) %}
4
+ {{ TextField(component) }}
5
+ {% endmacro %}