@planning-inspectorate/dynamic-forms 3.3.3 → 3.4.0

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 (272) hide show
  1. package/.azure/pipelines/azure-pipelines-pr.yml +4 -0
  2. package/.azure/pipelines/release.yml +4 -0
  3. package/.gitattributes +2 -0
  4. package/README.md +20 -9
  5. package/package.json +13 -5
  6. package/src/components/address/question.js +1 -10
  7. package/src/components/boolean/question.js +1 -1
  8. package/src/components/checkbox/question.js +2 -6
  9. package/src/components/date/question.js +1 -8
  10. package/src/components/date-period/question.js +1 -8
  11. package/src/components/email/question.js +4 -4
  12. package/src/components/manage-list/question.test.js +4 -19
  13. package/src/components/multi-field-input/question.js +3 -3
  14. package/src/components/multi-field-input/question.test.js +1 -1
  15. package/src/components/number-entry/question.js +1 -5
  16. package/src/components/radio/question.js +1 -1
  17. package/src/components/select/question.js +1 -1
  18. package/src/components/single-line-input/question.js +0 -8
  19. package/src/components/text-entry/question.js +4 -4
  20. package/src/components/text-entry-redact/index.njk +15 -11
  21. package/src/components/text-entry-redact/question.js +6 -16
  22. package/src/components/unit-option-entry/question.js +4 -11
  23. package/src/components/utils/component-types.js +18 -0
  24. package/src/index.js +72 -25
  25. package/src/middleware/redirect-to-unanswered-question.test.js +1 -1
  26. package/src/questions/options-question.js +5 -12
  27. package/src/questions/question-types.d.ts +3 -3
  28. package/src/questions/question.js +6 -6
  29. package/src/questions/questions.js +1 -1
  30. package/test/check-your-answers.test.js +3 -8
  31. package/test/conditional-journey.test.js +431 -0
  32. package/test/questions.test.js +15 -28
  33. package/test/snapshots/secret-wish.html +15 -11
  34. package/test/utils/question-test-utils.js +52 -4
  35. package/test/utils/test-server.js +1 -0
  36. package/test/utils/utils.js +16 -1
  37. package/tsconfig.json +9 -1
  38. package/types/commitlint.config.d.ts +7 -0
  39. package/types/commitlint.config.d.ts.map +1 -0
  40. package/types/eslint.config.d.ts +95 -0
  41. package/types/eslint.config.d.ts.map +1 -0
  42. package/types/src/components/address/question.d.ts +31 -0
  43. package/types/src/components/address/question.d.ts.map +1 -0
  44. package/types/src/components/address/question.test.d.ts +2 -0
  45. package/types/src/components/address/question.test.d.ts.map +1 -0
  46. package/types/src/components/boolean/question.d.ts +47 -0
  47. package/types/src/components/boolean/question.d.ts.map +1 -0
  48. package/types/src/components/boolean/question.test.d.ts +2 -0
  49. package/types/src/components/boolean/question.test.d.ts.map +1 -0
  50. package/types/src/components/checkbox/question.d.ts +49 -0
  51. package/types/src/components/checkbox/question.d.ts.map +1 -0
  52. package/types/src/components/checkbox/question.test.d.ts +2 -0
  53. package/types/src/components/checkbox/question.test.d.ts.map +1 -0
  54. package/types/src/components/date/question.d.ts +22 -0
  55. package/types/src/components/date/question.d.ts.map +1 -0
  56. package/types/src/components/date/question.test.d.ts +2 -0
  57. package/types/src/components/date/question.test.d.ts.map +1 -0
  58. package/types/src/components/date-period/question.d.ts +29 -0
  59. package/types/src/components/date-period/question.d.ts.map +1 -0
  60. package/types/src/components/date-period/question.test.d.ts +2 -0
  61. package/types/src/components/date-period/question.test.d.ts.map +1 -0
  62. package/types/src/components/date-time/question.d.ts +24 -0
  63. package/types/src/components/date-time/question.d.ts.map +1 -0
  64. package/types/src/components/date-time/question.test.d.ts +2 -0
  65. package/types/src/components/date-time/question.test.d.ts.map +1 -0
  66. package/types/src/components/email/question.d.ts +30 -0
  67. package/types/src/components/email/question.d.ts.map +1 -0
  68. package/types/src/components/email/question.test.d.ts +2 -0
  69. package/types/src/components/email/question.test.d.ts.map +1 -0
  70. package/types/src/components/identifier/question.d.ts +15 -0
  71. package/types/src/components/identifier/question.d.ts.map +1 -0
  72. package/types/src/components/identifier/question.test.d.ts +2 -0
  73. package/types/src/components/identifier/question.test.d.ts.map +1 -0
  74. package/types/src/components/manage-list/manage-list-section.d.ts +16 -0
  75. package/types/src/components/manage-list/manage-list-section.d.ts.map +1 -0
  76. package/types/src/components/manage-list/manage-list-section.test.d.ts +2 -0
  77. package/types/src/components/manage-list/manage-list-section.test.d.ts.map +1 -0
  78. package/types/src/components/manage-list/question.d.ts +41 -0
  79. package/types/src/components/manage-list/question.d.ts.map +1 -0
  80. package/types/src/components/manage-list/question.test.d.ts +2 -0
  81. package/types/src/components/manage-list/question.test.d.ts.map +1 -0
  82. package/types/src/components/manage-list/utils.d.ts +18 -0
  83. package/types/src/components/manage-list/utils.d.ts.map +1 -0
  84. package/types/src/components/manage-list/utils.test.d.ts +2 -0
  85. package/types/src/components/manage-list/utils.test.d.ts.map +1 -0
  86. package/types/src/components/multi-field-input/question.d.ts +106 -0
  87. package/types/src/components/multi-field-input/question.d.ts.map +1 -0
  88. package/types/src/components/multi-field-input/question.test.d.ts +2 -0
  89. package/types/src/components/multi-field-input/question.test.d.ts.map +1 -0
  90. package/types/src/components/number-entry/question.d.ts +37 -0
  91. package/types/src/components/number-entry/question.d.ts.map +1 -0
  92. package/types/src/components/radio/question.d.ts +53 -0
  93. package/types/src/components/radio/question.d.ts.map +1 -0
  94. package/types/src/components/radio/question.test.d.ts +2 -0
  95. package/types/src/components/radio/question.test.d.ts.map +1 -0
  96. package/types/src/components/select/question.d.ts +51 -0
  97. package/types/src/components/select/question.d.ts.map +1 -0
  98. package/types/src/components/select/question.test.d.ts +2 -0
  99. package/types/src/components/select/question.test.d.ts.map +1 -0
  100. package/types/src/components/single-line-input/question.d.ts +18 -0
  101. package/types/src/components/single-line-input/question.d.ts.map +1 -0
  102. package/types/src/components/single-line-input/question.test.d.ts +2 -0
  103. package/types/src/components/single-line-input/question.test.d.ts.map +1 -0
  104. package/types/src/components/text-entry/question.d.ts +34 -0
  105. package/types/src/components/text-entry/question.d.ts.map +1 -0
  106. package/types/src/components/text-entry/question.test.d.ts +2 -0
  107. package/types/src/components/text-entry/question.test.d.ts.map +1 -0
  108. package/types/src/components/text-entry-redact/question.d.ts +42 -0
  109. package/types/src/components/text-entry-redact/question.d.ts.map +1 -0
  110. package/types/src/components/text-entry-redact/question.test.d.ts +2 -0
  111. package/types/src/components/text-entry-redact/question.test.d.ts.map +1 -0
  112. package/types/src/components/unit-option-entry/question.d.ts +83 -0
  113. package/types/src/components/unit-option-entry/question.d.ts.map +1 -0
  114. package/types/src/components/unit-option-entry/question.test.d.ts +2 -0
  115. package/types/src/components/unit-option-entry/question.test.d.ts.map +1 -0
  116. package/types/src/components/utils/component-types.d.ts +19 -0
  117. package/types/src/components/utils/component-types.d.ts.map +1 -0
  118. package/types/src/components/utils/persisted-number-answer.d.ts +2 -0
  119. package/types/src/components/utils/persisted-number-answer.d.ts.map +1 -0
  120. package/types/src/components/utils/question-has-answer.d.ts +17 -0
  121. package/types/src/components/utils/question-has-answer.d.ts.map +1 -0
  122. package/types/src/components/utils/question-has-answer.test.d.ts +2 -0
  123. package/types/src/components/utils/question-has-answer.test.d.ts.map +1 -0
  124. package/types/src/components/utils/question-utils.d.ts +15 -0
  125. package/types/src/components/utils/question-utils.d.ts.map +1 -0
  126. package/types/src/components/utils/question-utils.test.d.ts +2 -0
  127. package/types/src/components/utils/question-utils.test.d.ts.map +1 -0
  128. package/types/src/controller.d.ts +78 -0
  129. package/types/src/controller.d.ts.map +1 -0
  130. package/types/src/controller.test.d.ts +2 -0
  131. package/types/src/controller.test.d.ts.map +1 -0
  132. package/types/src/index.d.ts +54 -0
  133. package/types/src/index.d.ts.map +1 -0
  134. package/types/src/journey/journey-response.d.ts +32 -0
  135. package/types/src/journey/journey-response.d.ts.map +1 -0
  136. package/types/src/journey/journey-response.test.d.ts +2 -0
  137. package/types/src/journey/journey-response.test.d.ts.map +1 -0
  138. package/types/src/journey/journey.d.ts +144 -0
  139. package/types/src/journey/journey.d.ts.map +1 -0
  140. package/types/src/journey/journey.test.d.ts +2 -0
  141. package/types/src/journey/journey.test.d.ts.map +1 -0
  142. package/types/src/lib/address-utils.d.ts +6 -0
  143. package/types/src/lib/address-utils.d.ts.map +1 -0
  144. package/types/src/lib/address-utils.test.d.ts +2 -0
  145. package/types/src/lib/address-utils.test.d.ts.map +1 -0
  146. package/types/src/lib/address.d.ts +43 -0
  147. package/types/src/lib/address.d.ts.map +1 -0
  148. package/types/src/lib/address.test.d.ts +2 -0
  149. package/types/src/lib/address.test.d.ts.map +1 -0
  150. package/types/src/lib/date-utils.d.ts +60 -0
  151. package/types/src/lib/date-utils.d.ts.map +1 -0
  152. package/types/src/lib/date-utils.test.d.ts +2 -0
  153. package/types/src/lib/date-utils.test.d.ts.map +1 -0
  154. package/types/src/lib/session-answer-store.d.ts +84 -0
  155. package/types/src/lib/session-answer-store.d.ts.map +1 -0
  156. package/types/src/lib/session-answer-store.test.d.ts +2 -0
  157. package/types/src/lib/session-answer-store.test.d.ts.map +1 -0
  158. package/types/src/lib/utils.d.ts +19 -0
  159. package/types/src/lib/utils.d.ts.map +1 -0
  160. package/types/src/lib/utils.test.d.ts +2 -0
  161. package/types/src/lib/utils.test.d.ts.map +1 -0
  162. package/types/src/lib/uuid.d.ts +7 -0
  163. package/types/src/lib/uuid.d.ts.map +1 -0
  164. package/types/src/lib/uuid.test.d.ts +2 -0
  165. package/types/src/lib/uuid.test.d.ts.map +1 -0
  166. package/types/src/middleware/build-get-journey.d.ts +6 -0
  167. package/types/src/middleware/build-get-journey.d.ts.map +1 -0
  168. package/types/src/middleware/redirect-to-unanswered-question.d.ts +12 -0
  169. package/types/src/middleware/redirect-to-unanswered-question.d.ts.map +1 -0
  170. package/types/src/middleware/redirect-to-unanswered-question.test.d.ts +2 -0
  171. package/types/src/middleware/redirect-to-unanswered-question.test.d.ts.map +1 -0
  172. package/types/src/questions/create-questions.d.ts +21 -0
  173. package/types/src/questions/create-questions.d.ts.map +1 -0
  174. package/types/src/questions/options-question.d.ts +74 -0
  175. package/types/src/questions/options-question.d.ts.map +1 -0
  176. package/types/src/questions/options-question.test.d.ts +2 -0
  177. package/types/src/questions/options-question.test.d.ts.map +1 -0
  178. package/types/src/questions/question.d.ts +274 -0
  179. package/types/src/questions/question.d.ts.map +1 -0
  180. package/types/src/questions/question.test.d.ts +2 -0
  181. package/types/src/questions/question.test.d.ts.map +1 -0
  182. package/types/src/questions/questions.d.ts +3 -0
  183. package/types/src/questions/questions.d.ts.map +1 -0
  184. package/types/src/section.d.ts +107 -0
  185. package/types/src/section.d.ts.map +1 -0
  186. package/types/src/section.test.d.ts +2 -0
  187. package/types/src/section.test.d.ts.map +1 -0
  188. package/types/src/validator/address-validator.d.ts +41 -0
  189. package/types/src/validator/address-validator.d.ts.map +1 -0
  190. package/types/src/validator/address-validator.test.d.ts +2 -0
  191. package/types/src/validator/address-validator.test.d.ts.map +1 -0
  192. package/types/src/validator/base-validator.d.ts +11 -0
  193. package/types/src/validator/base-validator.d.ts.map +1 -0
  194. package/types/src/validator/base-validator.test.d.ts +2 -0
  195. package/types/src/validator/base-validator.test.d.ts.map +1 -0
  196. package/types/src/validator/conditional-required-validator.d.ts +24 -0
  197. package/types/src/validator/conditional-required-validator.d.ts.map +1 -0
  198. package/types/src/validator/conditional-required-validator.test.d.ts +2 -0
  199. package/types/src/validator/conditional-required-validator.test.d.ts.map +1 -0
  200. package/types/src/validator/confirmation-checkbox-validator.d.ts +27 -0
  201. package/types/src/validator/confirmation-checkbox-validator.d.ts.map +1 -0
  202. package/types/src/validator/confirmation-checkbox-validator.test.d.ts +2 -0
  203. package/types/src/validator/confirmation-checkbox-validator.test.d.ts.map +1 -0
  204. package/types/src/validator/coordinates-validator.d.ts +22 -0
  205. package/types/src/validator/coordinates-validator.d.ts.map +1 -0
  206. package/types/src/validator/coordinates-validator.test.d.ts +2 -0
  207. package/types/src/validator/coordinates-validator.test.d.ts.map +1 -0
  208. package/types/src/validator/date-period-validator.d.ts +63 -0
  209. package/types/src/validator/date-period-validator.d.ts.map +1 -0
  210. package/types/src/validator/date-period-validator.test.d.ts +2 -0
  211. package/types/src/validator/date-period-validator.test.d.ts.map +1 -0
  212. package/types/src/validator/date-time-validator.d.ts +26 -0
  213. package/types/src/validator/date-time-validator.d.ts.map +1 -0
  214. package/types/src/validator/date-time-validator.test.d.ts +2 -0
  215. package/types/src/validator/date-time-validator.test.d.ts.map +1 -0
  216. package/types/src/validator/date-validator.d.ts +48 -0
  217. package/types/src/validator/date-validator.d.ts.map +1 -0
  218. package/types/src/validator/date-validator.test.d.ts +2 -0
  219. package/types/src/validator/date-validator.test.d.ts.map +1 -0
  220. package/types/src/validator/document-upload-validator.d.ts +10 -0
  221. package/types/src/validator/document-upload-validator.d.ts.map +1 -0
  222. package/types/src/validator/document-upload-validator.test.d.ts +2 -0
  223. package/types/src/validator/document-upload-validator.test.d.ts.map +1 -0
  224. package/types/src/validator/email-validator.d.ts +73 -0
  225. package/types/src/validator/email-validator.d.ts.map +1 -0
  226. package/types/src/validator/email-validator.test.d.ts +2 -0
  227. package/types/src/validator/email-validator.test.d.ts.map +1 -0
  228. package/types/src/validator/multi-field-input-validator.d.ts +70 -0
  229. package/types/src/validator/multi-field-input-validator.d.ts.map +1 -0
  230. package/types/src/validator/multi-field-input-validator.test.d.ts +2 -0
  231. package/types/src/validator/multi-field-input-validator.test.d.ts.map +1 -0
  232. package/types/src/validator/numeric-validator.d.ts +49 -0
  233. package/types/src/validator/numeric-validator.d.ts.map +1 -0
  234. package/types/src/validator/numeric-validator.test.d.ts +2 -0
  235. package/types/src/validator/numeric-validator.test.d.ts.map +1 -0
  236. package/types/src/validator/required-validator.d.ts +22 -0
  237. package/types/src/validator/required-validator.d.ts.map +1 -0
  238. package/types/src/validator/required-validator.test.d.ts +2 -0
  239. package/types/src/validator/required-validator.test.d.ts.map +1 -0
  240. package/types/src/validator/same-answer-validator.d.ts +18 -0
  241. package/types/src/validator/same-answer-validator.d.ts.map +1 -0
  242. package/types/src/validator/same-answer-validator.test.d.ts +2 -0
  243. package/types/src/validator/same-answer-validator.test.d.ts.map +1 -0
  244. package/types/src/validator/string-validator.d.ts +58 -0
  245. package/types/src/validator/string-validator.d.ts.map +1 -0
  246. package/types/src/validator/string-validator.test.d.ts +2 -0
  247. package/types/src/validator/string-validator.test.d.ts.map +1 -0
  248. package/types/src/validator/unit-option-entry-validator.d.ts +40 -0
  249. package/types/src/validator/unit-option-entry-validator.d.ts.map +1 -0
  250. package/types/src/validator/unit-option-entry-validator.test.d.ts +2 -0
  251. package/types/src/validator/unit-option-entry-validator.test.d.ts.map +1 -0
  252. package/types/src/validator/valid-option-validator.d.ts +22 -0
  253. package/types/src/validator/valid-option-validator.d.ts.map +1 -0
  254. package/types/src/validator/valid-option-validator.test.d.ts +2 -0
  255. package/types/src/validator/valid-option-validator.test.d.ts.map +1 -0
  256. package/types/src/validator/validation-error-handler.d.ts +20 -0
  257. package/types/src/validator/validation-error-handler.d.ts.map +1 -0
  258. package/types/src/validator/validation-error-handler.test.d.ts +2 -0
  259. package/types/src/validator/validation-error-handler.test.d.ts.map +1 -0
  260. package/types/src/validator/validator.d.ts +5 -0
  261. package/types/src/validator/validator.d.ts.map +1 -0
  262. package/types/src/validator/validator.test.d.ts +2 -0
  263. package/types/src/validator/validator.test.d.ts.map +1 -0
  264. package/types/test/mock/journey.d.ts +3 -0
  265. package/types/test/mock/journey.d.ts.map +1 -0
  266. package/types/test/mock/uuid.d.ts +7 -0
  267. package/types/test/mock/uuid.d.ts.map +1 -0
  268. package/types/test/utils/nunjucks.d.ts +2 -0
  269. package/types/test/utils/nunjucks.d.ts.map +1 -0
  270. package/types/test/utils/utils.d.ts +32 -0
  271. package/types/test/utils/utils.d.ts.map +1 -0
  272. package/src/lib/address.d.ts +0 -7
@@ -58,3 +58,7 @@ extends:
58
58
  parameters:
59
59
  nodeVersion: 22
60
60
  script: npm run test-coverage
61
+ - template: ../steps/node_script.yml
62
+ parameters:
63
+ nodeVersion: 22
64
+ script: npm run generate-types
@@ -46,6 +46,10 @@ jobs:
46
46
  parameters:
47
47
  nodeVersion: 22
48
48
  script: npm ci
49
+ - template: steps/node_script.yml@templates
50
+ parameters:
51
+ nodeVersion: 22
52
+ script: npm run generate-types
49
53
  - template: steps/node_script.yml@templates
50
54
  parameters:
51
55
  nodeVersion: 22
package/.gitattributes ADDED
@@ -0,0 +1,2 @@
1
+ # snapshots always use \n
2
+ test/snapshots/** text eol=lf
package/README.md CHANGED
@@ -146,7 +146,7 @@ to add conditions to questions in multiple ways.
146
146
  The simplest condition applies to just one question:
147
147
 
148
148
  ```javascript
149
- import { whenQuestionHasAnswer } from '@pins/dynamic-forms/src/components/utils/question-has-answer';
149
+ import { whenQuestionHasAnswer } from '@planning-inspectorate/dynamic-forms';
150
150
 
151
151
  new Section('section-key', 'Section title')
152
152
  .addQuestion(questions.q1)
@@ -206,10 +206,12 @@ Email validation is provided through the `EmailValidator` class and `EmailQuesti
206
206
  #### Basic Usage
207
207
 
208
208
  ```javascript
209
- import { COMPONENT_TYPES } from '@planning-inspectorate/dynamic-forms/src/index.js';
210
- import { createQuestions } from '@planning-inspectorate/dynamic-forms/src/questions/create-questions.js';
211
- import { questionClasses } from '@planning-inspectorate/dynamic-forms/src/questions/questions.js';
212
- import EmailValidator from '@planning-inspectorate/dynamic-forms/src/validator/email-validator.js';
209
+ import {
210
+ COMPONENT_TYPES,
211
+ createQuestions,
212
+ questionClasses,
213
+ EmailValidator
214
+ } from '@planning-inspectorate/dynamic-forms';
213
215
 
214
216
  // Define question configuration
215
217
  const questionProps = {
@@ -260,10 +262,12 @@ const businessEmailValidator = new EmailValidator({
260
262
  You can also configure a single line input with email-specific attributes:
261
263
 
262
264
  ```javascript
263
- import { COMPONENT_TYPES } from '@planning-inspectorate/dynamic-forms/src/index.js';
264
- import { createQuestions } from '@planning-inspectorate/dynamic-forms/src/questions/create-questions.js';
265
- import { questionClasses } from '@planning-inspectorate/dynamic-forms/src/questions/questions.js';
266
- import EmailValidator from '@planning-inspectorate/dynamic-forms/src/validator/email-validator.js';
265
+ import {
266
+ COMPONENT_TYPES,
267
+ createQuestions,
268
+ questionClasses,
269
+ EmailValidator
270
+ } from '@planning-inspectorate/dynamic-forms';
267
271
 
268
272
  const questionProps = {
269
273
  contactEmail: {
@@ -290,6 +294,13 @@ Commits must follow conventional commits, and the commit types will be used by s
290
294
 
291
295
  The package will be released automatically using semantic-release, on merge to main. This will include a git tag for the release, and publishing to NPM.
292
296
 
297
+ ### Type safety
298
+ We use JSDocs to describe the types used. This is helpful for the JavaScript developers, and *crucial* for this project being compatible with TypeScript. Type declarations for TypeScript users are auto-generated by `tsc` to create type declarations. So please:
299
+ 1. Add JSDocs annotations to your code, use `@import` or an inline import, i.e. `@param {import('#section').Section} section`. Avoid a top-level import, e.g. `@typedef {import('../validator/base-validator.js')} BaseValidator` - this seems to create confusion when the compiler auto-generates the type declarations.
300
+ 2. Maintain `index.js`. If you're adding code that users of this module will import, please make it is being exported in `index.js`.
301
+
302
+ Thank you for your co-operation and contributions!
303
+
293
304
  ### Tests
294
305
 
295
306
  There are some lightweight tests in the `test` directory which sets up a basic journey and checks the rendering for each question as well as redirect logic.
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "@planning-inspectorate/dynamic-forms",
3
- "version": "3.3.3",
3
+ "version": "3.4.0",
4
4
  "description": "This package is for building GDS forms using a configuration-based approach.",
5
5
  "main": "./src/index.js",
6
6
  "type": "module",
7
+ "types": "./types/src/index.d.ts",
7
8
  "repository": {
8
9
  "type": "git",
9
10
  "url": "git+https://github.com/Planning-Inspectorate/dynamic-forms.git"
@@ -13,6 +14,7 @@
13
14
  "lint": "npx eslint .",
14
15
  "format": "prettier --write .",
15
16
  "format-check": "prettier --check .",
17
+ "generate-types": "npx tsc --project tsconfig.json",
16
18
  "prepare": "node .husky/install.mjs",
17
19
  "test": "node --test",
18
20
  "test-coverage": "node --test --experimental-test-coverage"
@@ -26,21 +28,27 @@
26
28
  },
27
29
  "imports": {
28
30
  "#question-types": "./src/questions/question-types.d.ts",
29
- "#src/*": "./src/*",
30
31
  "#test/*": "./test/*",
31
- "#question": "./src/questions/question.js"
32
+ "#base-validator": "./src/validator/base-validator.js",
33
+ "#journey": "./src/journey/journey.js",
34
+ "#journey-response": "./src/journey/journey-response.js",
35
+ "#question": "./src/questions/question.js",
36
+ "#section": "./src/section.js",
37
+ "#src/*": "./src/*"
32
38
  },
33
39
  "devDependencies": {
34
40
  "@commitlint/cli": "^20.0.0",
35
41
  "@commitlint/config-conventional": "^20.0.0",
42
+ "@eslint/js": "^10.0.1",
36
43
  "body-parser": "^2.2.0",
37
44
  "conventional-changelog-conventionalcommits": "^9.1.0",
38
45
  "cookie-parser": "^1.4.7",
39
- "eslint": "^9.29.0",
46
+ "eslint": "^10.0.0",
40
47
  "eslint-config-prettier": "^10.1.5",
41
48
  "express": "^5.1.0",
42
49
  "express-session": "^1.18.2",
43
- "govuk-frontend": "^5.11.0",
50
+ "globals": "^17.3.0",
51
+ "govuk-frontend": "^6.0.0",
44
52
  "husky": "^9.1.7",
45
53
  "lint-staged": "^16.1.2",
46
54
  "prettier": "^3.6.2",
@@ -5,15 +5,6 @@ import { Address } from '../../lib/address.js';
5
5
  import { nl2br } from '../../lib/utils.js';
6
6
  import AddressValidator from '../../validator/address-validator.js';
7
7
 
8
- /**
9
- * @typedef {import('../../journey/journey-response.js').JourneyResponse} JourneyResponse
10
- * @typedef {import('../../journey/journey.js').Journey} Journey
11
- * @typedef {import('../../section.js').Section} Section
12
- * @typedef {import('../../questions/question.js').QuestionViewModel} QuestionViewModel
13
- * @typedef {import('appeals-service-api').Api.SubmissionAddress} SubmissionAddress
14
-
15
- */
16
-
17
8
  export default class AddressQuestion extends Question {
18
9
  /**
19
10
  * @param {import('#question-types').QuestionParameters} params
@@ -74,7 +65,7 @@ export default class AddressQuestion extends Question {
74
65
  /**
75
66
  * Get the data to save from the request, returns an object of answers
76
67
  * @param {import('express').Request} req
77
- * @param {JourneyResponse} journeyResponse
68
+ * @param {import('#journey-response').JourneyResponse} journeyResponse
78
69
  * @returns {Promise<{answers: Record<string, unknown>}>}
79
70
  */ //eslint-disable-next-line no-unused-vars -- journeyResponse kept for other questions to use
80
71
  async getDataToSave(req, journeyResponse) {
@@ -44,7 +44,7 @@ export default class BooleanQuestion extends RadioQuestion {
44
44
  * @param {string} [params.html]
45
45
  * @param {string} [params.interfaceType]
46
46
  * @param {Array.<import('../../questions/options-question.js').Option>} [params.options]
47
- * @param {Array.<import('../../questions/question.js').BaseValidator>} [params.validators]
47
+ * @param {Array.<import('#base-validator').BaseValidator>} [params.validators]
48
48
  * @param {boolean} [params.editable]
49
49
  */
50
50
  constructor({
@@ -3,10 +3,6 @@ import questionUtils from '../utils/question-utils.js';
3
3
 
4
4
  const defaultOptionJoinString = ',';
5
5
 
6
- /**
7
- * @typedef {import('../../journey/journey.js').Journey} Journey
8
- */
9
-
10
6
  /**
11
7
  * @typedef ConditionalAnswerObject
12
8
  * @type {object}
@@ -24,7 +20,7 @@ export default class CheckboxQuestion extends OptionsQuestion {
24
20
  * @param {string} [params.pageTitle]
25
21
  * @param {string} [params.description]
26
22
  * @param {Array.<import('../../questions/options-question.js').Option>} params.options
27
- * @param {Array.<import('../../questions/question.js').BaseValidator>} [params.validators]
23
+ * @param {Array.<import('#base-validator').BaseValidator>} [params.validators]
28
24
  */
29
25
  constructor({ title, question, fieldName, url, pageTitle, description, options, validators }) {
30
26
  super({
@@ -45,7 +41,7 @@ export default class CheckboxQuestion extends OptionsQuestion {
45
41
  /**
46
42
  * returns the formatted answers values to be used to build task list elements
47
43
  * @param {string | ConditionalAnswerObject } answer will be a single value string, a comma-separated string representing multiple values (one of which may be a conditional) or a single ConditionalAnswerObject
48
- * @param {Journey} journey
44
+ * @param {import('#journey').Journey} journey
49
45
  * @param {String} sectionSegment
50
46
  * @returns {Array.<Object>}
51
47
  */
@@ -1,13 +1,6 @@
1
1
  import { formatDateForDisplay, parseDateInput } from '../../lib/date-utils.js';
2
2
  import { Question } from '../../questions/question.js';
3
3
 
4
- /**
5
- * @typedef {import('../../questions/question.js').QuestionViewModel} QuestionViewModel
6
- * @typedef {import('../../journey/journey.js').Journey} Journey
7
- * @typedef {import('../../journey/journey-response.js').JourneyResponse} JourneyResponse
8
- * @typedef {import('../../section').Section} Section
9
- */
10
-
11
4
  const DEFAULT_DATE_FORMAT = 'd MMMM yyyy';
12
5
 
13
6
  /**
@@ -29,7 +22,7 @@ export default class DateQuestion extends Question {
29
22
  /**
30
23
  * Get the data to save from the request, returns an object of answers
31
24
  * @param {import('express').Request} req
32
- * @param {JourneyResponse} journeyResponse - current journey response, modified with the new answers
25
+ * @param {import('#journey-response').JourneyResponse} journeyResponse - current journey response, modified with the new answers
33
26
  * @returns {Promise.<Object>}
34
27
  */ //eslint-disable-next-line no-unused-vars -- journeyResponse kept for other questions to use
35
28
  async getDataToSave(req, journeyResponse) {
@@ -3,13 +3,6 @@ import { Question } from '#question';
3
3
  import { nl2br } from '../../lib/utils.js';
4
4
  import escape from 'escape-html';
5
5
 
6
- /**
7
- * @typedef {import('../../questions/question.js').QuestionViewModel} QuestionViewModel
8
- * @typedef {import('../../journey/journey.js').Journey} Journey
9
- * @typedef {import('../../journey/journey-response.js').JourneyResponse} JourneyResponse
10
- * @typedef {import('../../section').Section} Section
11
- */
12
-
13
6
  const DEFAULT_DATE_FORMAT = 'HH:mm d MMMM yyyy';
14
7
 
15
8
  /**
@@ -38,7 +31,7 @@ export default class DatePeriodQuestion extends Question {
38
31
  /**
39
32
  * Get the data to save from the request, returns an object of answers
40
33
  * @param {import('express').Request} req
41
- * @param {JourneyResponse} journeyResponse - current journey response, modified with the new answers
34
+ * @param {import('#journey-response').JourneyResponse} journeyResponse - current journey response, modified with the new answers
42
35
  * @returns {Promise.<Object>}
43
36
  */ //eslint-disable-next-line no-unused-vars -- journeyResponse kept for other questions to use
44
37
  async getDataToSave(req, journeyResponse) {
@@ -1,9 +1,9 @@
1
1
  import SingleLineInputQuestion from '../single-line-input/question.js';
2
2
 
3
3
  /**
4
- * @typedef {import('../../questions/question.js').QuestionViewModel} QuestionViewModel
5
- * @typedef {import('../../journey/journey.js').Journey} Journey
6
- * @typedef {import('../../journey/journey-response.js').JourneyResponse} JourneyResponse
4
+ * @typedef {import('#question').QuestionViewModel} QuestionViewModel
5
+ * @typedef {import('#journey').Journey} Journey
6
+ * @typedef {import('#journey-response').JourneyResponse} JourneyResponse
7
7
  * @typedef {import('../../section').Section} Section
8
8
  * @typedef {import('../../validator/base-validator')} BaseValidator
9
9
  */
@@ -44,7 +44,7 @@ export default class EmailQuestion extends SingleLineInputQuestion {
44
44
  * Override formatAnswerForSummary to prevent capitalization of email addresses
45
45
  * Email addresses should remain in their original case (typically lowercase)
46
46
  * @param {string} sectionSegment
47
- * @param {import('../../journey/journey.js').Journey} journey
47
+ * @param {import('#journey').Journey} journey
48
48
  * @param {string} answer
49
49
  * @returns {Array}
50
50
  */
@@ -5,8 +5,7 @@ import { ManageListSection } from '#src/components/manage-list/manage-list-secti
5
5
  import { mockJourney } from '#test/mock/journey.js';
6
6
  import { mockRandomUUID } from '#test/mock/uuid.js';
7
7
  import { configureNunjucksTestEnv } from '#test/utils/nunjucks.js';
8
- import path from 'path';
9
- import { snapshotsDir } from '#test/utils/utils.js';
8
+ import { assertSnapshot } from '#test/utils/utils.js';
10
9
 
11
10
  describe('components/manage-list/question', () => {
12
11
  const TITLE = 'Things';
@@ -96,13 +95,7 @@ describe('components/manage-list/question', () => {
96
95
  const { q } = questionWithManageQuestions(ctx, { showAnswersInSummary: true });
97
96
  const answerForSummary = q.formatAnswerForSummary('section-1', mockJourney(), [{}, {}, {}]);
98
97
  assert.strictEqual(answerForSummary.length, 1);
99
- ctx.assert.fileSnapshot(
100
- answerForSummary[0].value.replaceAll('\r\n', '\n'),
101
- path.join(snapshotsDir(), 'manage-list-answer-summary.html'),
102
- {
103
- serializers: [(v) => v]
104
- }
105
- );
98
+ assertSnapshot(ctx, answerForSummary[0].value, 'manage-list-answer-summary.html');
106
99
  });
107
100
 
108
101
  it('should render with answers', (ctx) => {
@@ -114,9 +107,7 @@ describe('components/manage-list/question', () => {
114
107
  };
115
108
  q.renderAction(res, viewModel);
116
109
  assert.strictEqual(res.render.mock.callCount(), 1);
117
- ctx.assert.fileSnapshot(res.render.mock.calls[0].result, path.join(snapshotsDir(), 'manage-list-render.html'), {
118
- serializers: [(v) => v]
119
- });
110
+ assertSnapshot(ctx, res.render.mock.calls[0].result, 'manage-list-render.html');
120
111
  });
121
112
 
122
113
  it('should render with answers & questions', (ctx) => {
@@ -130,13 +121,7 @@ describe('components/manage-list/question', () => {
130
121
  };
131
122
  q.renderAction(res, viewModel);
132
123
  assert.strictEqual(res.render.mock.callCount(), 1);
133
- ctx.assert.fileSnapshot(
134
- res.render.mock.calls[0].result,
135
- path.join(snapshotsDir(), 'manage-list-render-questions.html'),
136
- {
137
- serializers: [(v) => v]
138
- }
139
- );
124
+ assertSnapshot(ctx, res.render.mock.calls[0].result, 'manage-list-render-questions.html');
140
125
  });
141
126
 
142
127
  describe('getDataToSave', () => {
@@ -3,9 +3,9 @@ import escape from 'escape-html';
3
3
  import { nl2br } from '../../lib/utils.js';
4
4
 
5
5
  /**
6
- * @typedef {import('../../questions/question.js').QuestionViewModel} QuestionViewModel
7
- * @typedef {import('../../journey/journey.js').Journey} Journey
8
- * @typedef {import('../../journey/journey-response.js').JourneyResponse} JourneyResponse
6
+ * @typedef {import('#question').QuestionViewModel} QuestionViewModel
7
+ * @typedef {import('#journey').Journey} Journey
8
+ * @typedef {import('#journey-response').JourneyResponse} JourneyResponse
9
9
  * @typedef {import('../../section').Section} Section
10
10
  * @typedef {import('../../validator/base-validator')} BaseValidator
11
11
  */
@@ -1,7 +1,7 @@
1
1
  import { describe, it } from 'node:test';
2
2
  import assert from 'node:assert';
3
3
  import MultiFieldInputQuestion from './question.js';
4
- import { Journey } from '../../journey/journey.js';
4
+ import { Journey } from '#journey';
5
5
 
6
6
  const TITLE = 'title';
7
7
  const QUESTION = 'Question?';
@@ -1,10 +1,6 @@
1
1
  import { Question } from '#question';
2
2
  import { getPersistedNumberAnswer } from '../utils/persisted-number-answer.js';
3
3
 
4
- /**
5
- * @typedef {import('../../journey/journey.js').Journey} Journey
6
- */
7
-
8
4
  export default class NumberEntryQuestion extends Question {
9
5
  /**
10
6
  * @param {import('#question-types').QuestionParameters} params
@@ -36,7 +32,7 @@ export default class NumberEntryQuestion extends Question {
36
32
  /**
37
33
  * returns the formatted answers values to be used to build task list elements
38
34
  * @param {Object} answer
39
- * @param {Journey} journey
35
+ * @param {import('#journey').Journey} journey
40
36
  * @param {String} sectionSegment
41
37
  * @returns {Array<{
42
38
  * key: string;
@@ -15,7 +15,7 @@ export default class RadioQuestion extends OptionsQuestion {
15
15
  * @param {string} [params.html]
16
16
  * @param {string} [params.legend] - optional legend, used instead of h1
17
17
  * @param {Array.<import('../../questions/options-question.js').Option>} params.options
18
- * @param {Array.<import('../../questions/question.js').BaseValidator>} [params.validators]
18
+ * @param {Array.<import('#base-validator').BaseValidator>} [params.validators]
19
19
  * @param {boolean} [params.editable]
20
20
  * @param {Object<string, any>} [params.viewData]
21
21
  * @param {import('../../questions/question-types.js').ActionLink} [params.actionLink]
@@ -16,7 +16,7 @@ export default class SelectQuestion extends OptionsQuestion {
16
16
  * @param {string} [params.legend] - optional legend, used instead of h1
17
17
  * @param {Array.<import('../../questions/options-question.js').Option>} params.options
18
18
  * @param {Object<string, any>} [params.viewData]
19
- * @param {Array.<import('../../questions/question.js').BaseValidator>} [params.validators]
19
+ * @param {Array.<import('#base-validator').BaseValidator>} [params.validators]
20
20
  */
21
21
  constructor({
22
22
  title,
@@ -1,13 +1,5 @@
1
1
  import { Question } from '#question';
2
2
 
3
- /**
4
- * @typedef {import('../../questions/question.js').QuestionViewModel} QuestionViewModel
5
- * @typedef {import('../../journey/journey.js').Journey} Journey
6
- * @typedef {import('../../journey/journey-response.js').JourneyResponse} JourneyResponse
7
- * @typedef {import('../../section').Section} Section
8
- * @typedef {import('../../validator/base-validator')} BaseValidator
9
- */
10
-
11
3
  /**
12
4
  * @class
13
5
  */
@@ -1,9 +1,9 @@
1
1
  import { Question } from '#question';
2
2
  /**
3
- * @typedef {import('../../questions/question.js').QuestionViewModel} QuestionViewModel
4
- * @typedef {import('../../journey/journey.js').Journey} Journey
5
- * @typedef {import('../../journey/journey-response.js').JourneyResponse} JourneyResponse
6
- * @typedef {import('../../section').Section} Section
3
+ * @import {QuestionViewModel} from '#question'
4
+ * @import {Journey} from '#journey'
5
+ * @import {JourneyResponse} from '#journey-response'
6
+ * @import {Section} from '#section'
7
7
  */
8
8
 
9
9
  /**
@@ -123,7 +123,7 @@
123
123
  }) }}
124
124
 
125
125
  {{ govukButton({
126
- text: "Undo all changes",
126
+ text: "Remove all redactions",
127
127
  classes: "govuk-button--secondary",
128
128
  id: "redact-undo-all",
129
129
  type: "button"
@@ -192,16 +192,20 @@
192
192
  showError('No changes to undo');
193
193
  }
194
194
  });
195
- redactUndoAll.addEventListener('click', () => {
196
- const unredated = unredactedText.dataset.original;
197
- if (history.length === 0 && textArea.value === unredated) {
198
- showError('No changes to undo');
199
- return;
200
- }
201
- hideError();
202
- textArea.value = unredated || '';
203
- history.length = 0;
204
- });
195
+ redactUndoAll.addEventListener('click', () => {
196
+ const unredated = unredactedText.dataset.original;
197
+ if (history.length === 0 && textArea.value === unredated) {
198
+ showError('No changes to undo');
199
+ return;
200
+ }
201
+ hideError();
202
+ textArea.value = unredated || '';
203
+ history.length = 0;
204
+
205
+ for (const rejectLink of rejectLinks) {
206
+ rejectLink.text = 'Accept';
207
+ }
208
+ });
205
209
  for (const rejectLink of rejectLinks) {
206
210
  rejectLink.addEventListener('click', () => {
207
211
  const index = parseInt(rejectLink.getAttribute('data-redact').replace('suggestion-reject-', ''));
@@ -1,21 +1,6 @@
1
1
  import { Question } from '#question';
2
2
  import { nl2br } from '../../lib/utils.js';
3
3
 
4
- /**
5
- * @typedef {import('../../questions/question.js').QuestionViewModel} QuestionViewModel
6
- * @typedef {import('../../journey/journey.js').Journey} Journey
7
- * @typedef {import('../../journey/journey-response.js').JourneyResponse} JourneyResponse
8
- * @typedef {import('../../section').Section} Section
9
- */
10
-
11
- /**
12
- * @typedef {Object} TextEntryCheckbox
13
- * @property {string} header
14
- * @property {string} text
15
- * @property {string} name
16
- * @property {string} [errorMessage]
17
- */
18
-
19
4
  export const REDACT_CHARACTER = '█';
20
5
  export const TRUNCATED_MAX_LENGTH = 500;
21
6
 
@@ -25,7 +10,7 @@ export const TRUNCATED_MAX_LENGTH = 500;
25
10
  export default class TextEntryRedactQuestion extends Question {
26
11
  /**
27
12
  * @param {import('#question-types').QuestionParameters} params
28
- * @param {TextEntryCheckbox} [params.textEntryCheckbox]
13
+ * @param {import('../text-entry/question.js').TextEntryCheckbox} [params.textEntryCheckbox]
29
14
  * @param {string|undefined} [params.label] if defined this show as a label for the input and the question will just be a standard h1
30
15
  * @param {boolean} [params.onlyShowRedactedValueForSummary] whether to only show redacted value for summary
31
16
  * @param {boolean} [params.useRedactedFieldNameForSave] whether to use the redacted field name when saving answers
@@ -57,6 +42,11 @@ export default class TextEntryRedactQuestion extends Question {
57
42
  this.shouldTruncateSummary = shouldTruncateSummary;
58
43
  }
59
44
 
45
+ /**
46
+ * @param {import('express').Request} req
47
+ * @param {import('#journey-response').JourneyResponse} journeyResponse
48
+ * @returns {Promise<{answers: Record<string, unknown>}>}
49
+ */
60
50
  async getDataToSave(req, journeyResponse) {
61
51
  if (this.useRedactedFieldNameForSave) {
62
52
  const fieldName = this.fieldName + 'Redacted';
@@ -4,13 +4,6 @@ import { conditionalIsJustHTML } from '../utils/question-utils.js';
4
4
 
5
5
  const defaultOptionJoinString = ',';
6
6
 
7
- /**
8
- * @typedef {import('../../questions/question.js').QuestionViewModel} QuestionViewModel
9
- * @typedef {import('../../journey/journey.js').Journey} Journey
10
- * @typedef {import('../../journey/journey-response.js').JourneyResponse} JourneyResponse
11
- * @typedef {import('../../section').Section} Section
12
- */
13
-
14
7
  /**
15
8
  * UnitOptions are the options displayed in the radio format - in this case the value
16
9
  * represents the unit.
@@ -67,13 +60,13 @@ export default class UnitOptionEntryQuestion extends Question {
67
60
 
68
61
  /**
69
62
  * gets the view model for this question
70
- * @param {Section} section - the current section
71
- * @param {Journey} journey - the journey we are in
63
+ * @param {import('../../section').Section} section - the current section
64
+ * @param {import('#journey').Journey} journey - the journey we are in
72
65
  * @param {Record<string, unknown>} [customViewData] additional data to send to view
73
66
  * @param {Record<string, unknown>} [payload]
74
67
  * @param {import('#src/questions/question-types.d.ts').PrepQuestionForRenderingOptions} options
75
- * @returns {QuestionViewModel & {
76
- * question: QuestionViewModel['question'] & {
68
+ * @returns {import('#question').QuestionViewModel & {
69
+ * question: import('#question').QuestionViewModel['question'] & {
77
70
  * options:UnitOption[]
78
71
  * }
79
72
  * }}
@@ -0,0 +1,18 @@
1
+ export const COMPONENT_TYPES = Object.freeze({
2
+ CHECKBOX: 'checkbox',
3
+ BOOLEAN: 'boolean',
4
+ RADIO: 'radio',
5
+ DATE: 'date',
6
+ DATE_PERIOD: 'date-period',
7
+ DATE_TIME: 'date-time',
8
+ TEXT_ENTRY: 'text-entry',
9
+ TEXT_ENTRY_REDACT: 'text-entry-redact',
10
+ SELECT: 'select',
11
+ SINGLE_LINE_INPUT: 'single-line-input',
12
+ MULTI_FIELD_INPUT: 'multi-field-input',
13
+ NUMBER: 'number',
14
+ ADDRESS: 'site-address',
15
+ UNIT_OPTION: 'unit-option',
16
+ EMAIL: 'email',
17
+ MANAGE_LIST: 'manage-list'
18
+ });