@planning-inspectorate/dynamic-forms 3.15.6 → 3.17.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.
- package/.gitattributes +3 -0
- package/.prettierrc +1 -0
- package/README.md +123 -4
- package/package.json +6 -4
- package/{types/src → src}/components/address/question.d.ts +6 -2
- package/src/components/address/question.d.ts.map +1 -0
- package/src/components/address/question.js +7 -19
- package/{types/src → src}/components/boolean/question.d.ts +2 -2
- package/src/components/boolean/question.d.ts.map +1 -0
- package/src/components/boolean/question.js +1 -1
- package/src/components/checkbox/question.d.ts +9 -0
- package/src/components/checkbox/question.d.ts.map +1 -0
- package/src/components/checkbox/question.js +37 -54
- package/src/components/checkbox/question.test.js +170 -27
- package/{types/src → src}/components/date/question.d.ts +2 -2
- package/src/components/date/question.d.ts.map +1 -0
- package/src/components/date/question.js +7 -16
- package/{types/src → src}/components/date-period/question.d.ts +12 -4
- package/src/components/date-period/question.d.ts.map +1 -0
- package/src/components/date-period/question.js +2 -3
- package/{types/src → src}/components/date-time/question.d.ts +2 -2
- package/src/components/date-time/question.d.ts.map +1 -0
- package/src/components/date-time/question.js +5 -14
- package/src/components/email/question.d.ts +23 -0
- package/src/components/email/question.d.ts.map +1 -0
- package/src/components/email/question.js +1 -9
- package/src/components/manage-list/answer-summary-list.njk +1 -1
- package/src/components/manage-list/manage-list-actions.d.ts.map +1 -0
- package/src/components/manage-list/manage-list-section.d.ts.map +1 -0
- package/{types/src → src}/components/manage-list/question.d.ts +6 -3
- package/src/components/manage-list/question.d.ts.map +1 -0
- package/src/components/manage-list/question.js +5 -2
- package/{types/src → src}/components/manage-list/utils.d.ts +3 -3
- package/src/components/manage-list/utils.d.ts.map +1 -0
- package/src/components/manage-list/utils.js +2 -2
- package/src/components/multi-field-input/question.d.ts +52 -0
- package/src/components/multi-field-input/question.d.ts.map +1 -0
- package/src/components/multi-field-input/question.js +5 -13
- package/src/components/number-entry/question.d.ts +12 -0
- package/src/components/number-entry/question.d.ts.map +1 -0
- package/src/components/number-entry/question.js +4 -22
- package/src/components/radio/question.d.ts +11 -0
- package/src/components/radio/question.d.ts.map +1 -0
- package/src/components/radio/question.js +30 -51
- package/src/components/radio/question.test.js +139 -60
- package/src/components/select/question.d.ts +12 -0
- package/src/components/select/question.d.ts.map +1 -0
- package/src/components/select/question.js +5 -48
- package/{types/src → src}/components/single-line-input/question.d.ts +2 -2
- package/src/components/single-line-input/question.d.ts.map +1 -0
- package/src/components/single-line-input/question.js +2 -2
- package/{types/src → src}/components/text-entry/question.d.ts +3 -3
- package/src/components/text-entry/question.d.ts.map +1 -0
- package/src/components/text-entry/question.js +2 -2
- package/{types/src → src}/components/text-entry-redact/question.d.ts +8 -9
- package/src/components/text-entry-redact/question.d.ts.map +1 -0
- package/src/components/text-entry-redact/question.js +5 -2
- package/{types/src → src}/components/unit-option-entry/question.d.ts +12 -8
- package/src/components/unit-option-entry/question.d.ts.map +1 -0
- package/src/components/unit-option-entry/question.js +5 -6
- package/src/components/utils/component-types.d.ts.map +1 -0
- package/src/components/utils/persisted-number-answer.d.ts.map +1 -0
- package/src/components/utils/question-has-answer.d.ts.map +1 -0
- package/src/components/utils/question-has-answer.js +1 -1
- package/src/components/utils/question-utils.d.ts +33 -0
- package/src/components/utils/question-utils.d.ts.map +1 -0
- package/src/components/utils/question-utils.js +31 -2
- package/src/components/utils/question-utils.test.js +89 -2
- package/{types/src → src}/controller.d.ts +2 -7
- package/src/controller.d.ts.map +1 -0
- package/src/controller.js +3 -10
- package/{types/src → src}/index.d.ts +4 -3
- package/src/index.d.ts.map +1 -0
- package/src/index.js +6 -4
- package/src/journey/journey-response.d.ts +28 -0
- package/src/journey/journey-response.d.ts.map +1 -0
- package/src/journey/journey-response.js +4 -8
- package/{types/src → src}/journey/journey.d.ts +8 -8
- package/src/journey/journey.d.ts.map +1 -0
- package/src/journey/journey.js +6 -6
- package/src/lib/address-utils.d.ts.map +1 -0
- package/src/lib/address.d.ts.map +1 -0
- package/src/lib/date-utils.d.ts.map +1 -0
- package/src/lib/session-answer-store.d.ts.map +1 -0
- package/src/lib/utils.d.ts.map +1 -0
- package/src/lib/uuid.d.ts.map +1 -0
- package/src/middleware/build-get-journey.d.ts.map +1 -0
- package/src/middleware/redirect-to-unanswered-question.d.ts +12 -0
- package/src/middleware/redirect-to-unanswered-question.d.ts.map +1 -0
- package/src/middleware/redirect-to-unanswered-question.js +1 -1
- package/src/questions/create-questions.d.ts +18 -0
- package/src/questions/create-questions.d.ts.map +1 -0
- package/src/questions/create-questions.js +3 -3
- package/{types/src → src}/questions/options-question.d.ts +7 -7
- package/src/questions/options-question.d.ts.map +1 -0
- package/src/questions/options-question.js +29 -4
- package/src/questions/options-question.test.js +106 -0
- package/{types/src → src}/questions/question.d.ts +48 -79
- package/src/questions/question.d.ts.map +1 -0
- package/src/questions/question.js +83 -60
- package/src/questions/question.test.js +86 -0
- package/src/questions/questions.d.ts +3 -0
- package/src/questions/questions.d.ts.map +1 -0
- package/src/questions/questions.js +1 -1
- package/{types/src → src}/section.d.ts +16 -15
- package/src/section.d.ts.map +1 -0
- package/src/section.js +12 -14
- package/src/validator/address-validator.d.ts.map +1 -0
- package/src/validator/base-validator.d.ts.map +1 -0
- package/src/validator/conditional-required-validator.d.ts.map +1 -0
- package/src/validator/confirmation-checkbox-validator.d.ts.map +1 -0
- package/src/validator/coordinates-validator.d.ts.map +1 -0
- package/src/validator/cross-question-validator.d.ts.map +1 -0
- package/src/validator/date-period-validator.d.ts.map +1 -0
- package/src/validator/date-time-validator.d.ts.map +1 -0
- package/src/validator/date-validator.d.ts.map +1 -0
- package/src/validator/document-upload-validator.d.ts.map +1 -0
- package/src/validator/email-validator.d.ts.map +1 -0
- package/{types/src → src}/validator/multi-field-input-validator.d.ts +3 -3
- package/src/validator/multi-field-input-validator.d.ts.map +1 -0
- package/src/validator/multi-field-input-validator.js +3 -3
- package/{types/src → src}/validator/numeric-validator.d.ts +2 -2
- package/src/validator/numeric-validator.d.ts.map +1 -0
- package/src/validator/numeric-validator.js +2 -2
- package/src/validator/required-validator.d.ts.map +1 -0
- package/src/validator/same-answer-validator.d.ts.map +1 -0
- package/{types/src → src}/validator/string-validator.d.ts +3 -3
- package/src/validator/string-validator.d.ts.map +1 -0
- package/src/validator/string-validator.js +3 -3
- package/src/validator/unit-option-entry-validator.d.ts.map +1 -0
- package/src/validator/valid-option-validator.d.ts.map +1 -0
- package/src/validator/validation-error-handler.d.ts.map +1 -0
- package/src/validator/validator.d.ts.map +1 -0
- package/test/journey.js +2 -0
- package/test/questions.js +51 -1
- package/test/secondary-actions.test.js +1 -1
- package/test/snapshots/check-your-answers-not-started.html +22 -0
- package/test/snapshots/check-your-answers-with-answers.html +22 -0
- package/test/snapshots/favourite-activity-reason.html +54 -0
- package/test/snapshots/travel-requirements.html +48 -0
- package/test/utils/question-test-utils.js +36 -6
- package/tsconfig.json +11 -4
- package/{types/src/questions → typedefs}/question-props.d.ts +28 -26
- package/typedefs/question-types.d.ts +141 -0
- package/{src → typedefs}/section-types.d.ts +1 -1
- package/typedefs/section-types.js +1 -0
- package/move-types.js +0 -12
- package/src/questions/question-props.d.ts +0 -295
- package/src/questions/question-types.d.ts +0 -86
- package/types/commitlint.config.d.ts +0 -7
- package/types/commitlint.config.d.ts.map +0 -1
- package/types/eslint.config.d.ts +0 -97
- package/types/eslint.config.d.ts.map +0 -1
- package/types/move-types.d.ts +0 -2
- package/types/move-types.d.ts.map +0 -1
- package/types/src/components/address/question.d.ts.map +0 -1
- package/types/src/components/boolean/question.d.ts.map +0 -1
- package/types/src/components/checkbox/question.d.ts +0 -33
- package/types/src/components/checkbox/question.d.ts.map +0 -1
- package/types/src/components/date/question.d.ts.map +0 -1
- package/types/src/components/date-period/question.d.ts.map +0 -1
- package/types/src/components/date-time/question.d.ts.map +0 -1
- package/types/src/components/email/question.d.ts +0 -35
- package/types/src/components/email/question.d.ts.map +0 -1
- package/types/src/components/manage-list/manage-list-actions.d.ts.map +0 -1
- package/types/src/components/manage-list/manage-list-section.d.ts.map +0 -1
- package/types/src/components/manage-list/question.d.ts.map +0 -1
- package/types/src/components/manage-list/utils.d.ts.map +0 -1
- package/types/src/components/multi-field-input/question.d.ts +0 -64
- package/types/src/components/multi-field-input/question.d.ts.map +0 -1
- package/types/src/components/number-entry/question.d.ts +0 -36
- package/types/src/components/number-entry/question.d.ts.map +0 -1
- package/types/src/components/radio/question.d.ts +0 -23
- package/types/src/components/radio/question.d.ts.map +0 -1
- package/types/src/components/select/question.d.ts +0 -22
- package/types/src/components/select/question.d.ts.map +0 -1
- package/types/src/components/single-line-input/question.d.ts.map +0 -1
- package/types/src/components/text-entry/question.d.ts.map +0 -1
- package/types/src/components/text-entry-redact/question.d.ts.map +0 -1
- package/types/src/components/unit-option-entry/question.d.ts.map +0 -1
- package/types/src/components/utils/component-types.d.ts.map +0 -1
- package/types/src/components/utils/persisted-number-answer.d.ts.map +0 -1
- package/types/src/components/utils/question-has-answer.d.ts.map +0 -1
- package/types/src/components/utils/question-utils.d.ts +0 -15
- package/types/src/components/utils/question-utils.d.ts.map +0 -1
- package/types/src/controller.d.ts.map +0 -1
- package/types/src/index.d.ts.map +0 -1
- package/types/src/journey/journey-response.d.ts +0 -32
- package/types/src/journey/journey-response.d.ts.map +0 -1
- package/types/src/journey/journey-types.d.ts +0 -28
- package/types/src/journey/journey-types.d.ts.map +0 -1
- package/types/src/journey/journey.d.ts.map +0 -1
- package/types/src/lib/address-utils.d.ts.map +0 -1
- package/types/src/lib/address.d.ts.map +0 -1
- package/types/src/lib/date-utils.d.ts.map +0 -1
- package/types/src/lib/session-answer-store.d.ts.map +0 -1
- package/types/src/lib/utils.d.ts.map +0 -1
- package/types/src/lib/uuid.d.ts.map +0 -1
- package/types/src/middleware/build-get-journey.d.ts.map +0 -1
- package/types/src/middleware/redirect-to-unanswered-question.d.ts +0 -12
- package/types/src/middleware/redirect-to-unanswered-question.d.ts.map +0 -1
- package/types/src/questions/create-questions.d.ts +0 -18
- package/types/src/questions/create-questions.d.ts.map +0 -1
- package/types/src/questions/options-question.d.ts.map +0 -1
- package/types/src/questions/question-props.d.ts.map +0 -1
- package/types/src/questions/question-types.d.ts +0 -86
- package/types/src/questions/question-types.d.ts.map +0 -1
- package/types/src/questions/question.d.ts.map +0 -1
- package/types/src/questions/questions.d.ts +0 -3
- package/types/src/questions/questions.d.ts.map +0 -1
- package/types/src/section.d.ts.map +0 -1
- package/types/src/validator/address-validator.d.ts.map +0 -1
- package/types/src/validator/base-validator.d.ts.map +0 -1
- package/types/src/validator/conditional-required-validator.d.ts.map +0 -1
- package/types/src/validator/confirmation-checkbox-validator.d.ts.map +0 -1
- package/types/src/validator/coordinates-validator.d.ts.map +0 -1
- package/types/src/validator/cross-question-validator.d.ts.map +0 -1
- package/types/src/validator/date-period-validator.d.ts.map +0 -1
- package/types/src/validator/date-time-validator.d.ts.map +0 -1
- package/types/src/validator/date-validator.d.ts.map +0 -1
- package/types/src/validator/document-upload-validator.d.ts.map +0 -1
- package/types/src/validator/email-validator.d.ts.map +0 -1
- package/types/src/validator/multi-field-input-validator.d.ts.map +0 -1
- package/types/src/validator/numeric-validator.d.ts.map +0 -1
- package/types/src/validator/required-validator.d.ts.map +0 -1
- package/types/src/validator/same-answer-validator.d.ts.map +0 -1
- package/types/src/validator/string-validator.d.ts.map +0 -1
- package/types/src/validator/unit-option-entry-validator.d.ts.map +0 -1
- package/types/src/validator/valid-option-validator.d.ts.map +0 -1
- package/types/src/validator/validation-error-handler.d.ts.map +0 -1
- package/types/src/validator/validator.d.ts.map +0 -1
- /package/{types/src → src}/components/manage-list/manage-list-actions.d.ts +0 -0
- /package/{types/src → src}/components/manage-list/manage-list-section.d.ts +0 -0
- /package/{types/src → src}/components/utils/component-types.d.ts +0 -0
- /package/{types/src → src}/components/utils/persisted-number-answer.d.ts +0 -0
- /package/{types/src → src}/components/utils/question-has-answer.d.ts +0 -0
- /package/{types/src → src}/lib/address-utils.d.ts +0 -0
- /package/{types/src → src}/lib/address.d.ts +0 -0
- /package/{types/src → src}/lib/date-utils.d.ts +0 -0
- /package/{types/src → src}/lib/session-answer-store.d.ts +0 -0
- /package/{types/src → src}/lib/utils.d.ts +0 -0
- /package/{types/src → src}/lib/uuid.d.ts +0 -0
- /package/{types/src → src}/middleware/build-get-journey.d.ts +0 -0
- /package/{types/src → src}/validator/address-validator.d.ts +0 -0
- /package/{types/src → src}/validator/base-validator.d.ts +0 -0
- /package/{types/src → src}/validator/conditional-required-validator.d.ts +0 -0
- /package/{types/src → src}/validator/confirmation-checkbox-validator.d.ts +0 -0
- /package/{types/src → src}/validator/coordinates-validator.d.ts +0 -0
- /package/{types/src → src}/validator/cross-question-validator.d.ts +0 -0
- /package/{types/src → src}/validator/date-period-validator.d.ts +0 -0
- /package/{types/src → src}/validator/date-time-validator.d.ts +0 -0
- /package/{types/src → src}/validator/date-validator.d.ts +0 -0
- /package/{types/src → src}/validator/document-upload-validator.d.ts +0 -0
- /package/{types/src → src}/validator/email-validator.d.ts +0 -0
- /package/{types/src → src}/validator/required-validator.d.ts +0 -0
- /package/{types/src → src}/validator/same-answer-validator.d.ts +0 -0
- /package/{types/src → src}/validator/unit-option-entry-validator.d.ts +0 -0
- /package/{types/src → src}/validator/valid-option-validator.d.ts +0 -0
- /package/{types/src → src}/validator/validation-error-handler.d.ts +0 -0
- /package/{types/src → src}/validator/validator.d.ts +0 -0
- /package/{src/journey → typedefs}/journey-types.d.ts +0 -0
- /package/{src/journey → typedefs}/journey-types.js +0 -0
- /package/{src/questions → typedefs}/question-props.js +0 -0
- /package/{src/questions → typedefs}/question-types.js +0 -0
|
@@ -303,4 +303,110 @@ describe('./src/dynamic-forms/question.js', () => {
|
|
|
303
303
|
);
|
|
304
304
|
});
|
|
305
305
|
});
|
|
306
|
+
|
|
307
|
+
describe('formatAnswer', () => {
|
|
308
|
+
it('should return not started text for null answer', () => {
|
|
309
|
+
const question = getTestQuestion({
|
|
310
|
+
options: [{ text: 'Option A', value: 'a' }]
|
|
311
|
+
});
|
|
312
|
+
const result = question.formatAnswer(null);
|
|
313
|
+
assert.strictEqual(result, 'Not started');
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it('should return not started text for undefined answer', () => {
|
|
317
|
+
const question = getTestQuestion({
|
|
318
|
+
options: [{ text: 'Option A', value: 'a' }]
|
|
319
|
+
});
|
|
320
|
+
const result = question.formatAnswer(undefined);
|
|
321
|
+
assert.strictEqual(result, 'Not started');
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
it('should return not started text for empty string answer', () => {
|
|
325
|
+
const question = getTestQuestion({
|
|
326
|
+
options: [{ text: 'Option A', value: 'a' }]
|
|
327
|
+
});
|
|
328
|
+
const result = question.formatAnswer('');
|
|
329
|
+
assert.strictEqual(result, 'Not started');
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
it('should format a single value using option text', () => {
|
|
333
|
+
const question = getTestQuestion({
|
|
334
|
+
options: [
|
|
335
|
+
{ text: 'Option A', value: 'a' },
|
|
336
|
+
{ text: 'Option B', value: 'b' }
|
|
337
|
+
]
|
|
338
|
+
});
|
|
339
|
+
const result = question.formatAnswer('a');
|
|
340
|
+
assert.strictEqual(result, 'Option A');
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it('should format multiple values joined with line breaks', () => {
|
|
344
|
+
const question = getTestQuestion({
|
|
345
|
+
options: [
|
|
346
|
+
{ text: 'Option A', value: 'a' },
|
|
347
|
+
{ text: 'Option B', value: 'b' },
|
|
348
|
+
{ text: 'Option C', value: 'c' }
|
|
349
|
+
]
|
|
350
|
+
});
|
|
351
|
+
const result = question.formatAnswer('a,b');
|
|
352
|
+
assert.strictEqual(result, 'Option A<br>Option B');
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it('should fall back to value when option not found', () => {
|
|
356
|
+
const question = getTestQuestion({
|
|
357
|
+
options: [{ text: 'Option A', value: 'a' }]
|
|
358
|
+
});
|
|
359
|
+
const result = question.formatAnswer('unknown');
|
|
360
|
+
assert.strictEqual(result, 'unknown');
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it('should handle mixed known and unknown values', () => {
|
|
364
|
+
const question = getTestQuestion({
|
|
365
|
+
options: [
|
|
366
|
+
{ text: 'Option A', value: 'a' },
|
|
367
|
+
{ text: 'Option B', value: 'b' }
|
|
368
|
+
]
|
|
369
|
+
});
|
|
370
|
+
const result = question.formatAnswer('a,unknown,b');
|
|
371
|
+
assert.strictEqual(result, 'Option A<br>unknown<br>Option B');
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it('should trim whitespace from values', () => {
|
|
375
|
+
const question = getTestQuestion({
|
|
376
|
+
options: [
|
|
377
|
+
{ text: 'Option A', value: 'a' },
|
|
378
|
+
{ text: 'Option B', value: 'b' }
|
|
379
|
+
]
|
|
380
|
+
});
|
|
381
|
+
const result = question.formatAnswer('a , b');
|
|
382
|
+
assert.strictEqual(result, 'Option A<br>Option B');
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it('should escape HTML in option text', () => {
|
|
386
|
+
const question = getTestQuestion({
|
|
387
|
+
options: [{ text: '<script>alert("xss")</script>', value: 'a' }]
|
|
388
|
+
});
|
|
389
|
+
const result = question.formatAnswer('a');
|
|
390
|
+
assert.strictEqual(result, '<script>alert("xss")</script>');
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
it('should escape HTML in fallback value when option not found', () => {
|
|
394
|
+
const question = getTestQuestion({
|
|
395
|
+
options: [{ text: 'Option A', value: 'a' }]
|
|
396
|
+
});
|
|
397
|
+
const result = question.formatAnswer('<img src=x onerror=alert(1)>');
|
|
398
|
+
assert.strictEqual(result, '<img src=x onerror=alert(1)>');
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
it('should escape HTML in multiple option texts', () => {
|
|
402
|
+
const question = getTestQuestion({
|
|
403
|
+
options: [
|
|
404
|
+
{ text: '<b>Bold</b>', value: 'a' },
|
|
405
|
+
{ text: '<i>Italic</i>', value: 'b' }
|
|
406
|
+
]
|
|
407
|
+
});
|
|
408
|
+
const result = question.formatAnswer('a,b');
|
|
409
|
+
assert.strictEqual(result, '<b>Bold</b><br><i>Italic</i>');
|
|
410
|
+
});
|
|
411
|
+
});
|
|
306
412
|
});
|
|
@@ -1,39 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @import {BaseValidator} from "#base-validator"
|
|
3
|
-
* @import {ActionView} from "../controller.js"
|
|
4
|
-
* @import {ActionLink} from "./question-types.js"
|
|
5
|
-
*/
|
|
6
|
-
/**
|
|
7
|
-
* @typedef {Object} PreppedQuestion
|
|
8
|
-
* @property {Object} value
|
|
9
|
-
* @property {string} question
|
|
10
|
-
* @property {string} fieldName
|
|
11
|
-
* @property {string} pageTitle
|
|
12
|
-
* @property {string} [description]
|
|
13
|
-
* @property {string} [html]
|
|
14
|
-
*/
|
|
15
|
-
/**
|
|
16
|
-
* @typedef {Record<string, any>} QuestionViewModel
|
|
17
|
-
* @property {PreppedQuestion} question
|
|
18
|
-
* @property {string} layoutTemplate
|
|
19
|
-
* @property {string} pageCaption
|
|
20
|
-
* @property {string} [continueButtonText]
|
|
21
|
-
* @property {string} backLink
|
|
22
|
-
* @property {boolean} showBackToListLink
|
|
23
|
-
* @property {string} listLink
|
|
24
|
-
* @property {Object} util
|
|
25
|
-
* @property {function(string): string} util.trimTrailingSlash
|
|
26
|
-
*/
|
|
27
1
|
/**
|
|
28
2
|
* A specific question within a journey which is made up of one (usually) or many (sometimes) components and their required content.
|
|
29
3
|
* @class
|
|
30
4
|
*/
|
|
31
5
|
export class Question {
|
|
32
6
|
/**
|
|
33
|
-
* @param {import('
|
|
7
|
+
* @param {import('#typedefs/question-types.d.ts').QuestionParameters} params
|
|
34
8
|
* @param {Record<string, Function>} [methodOverrides]
|
|
35
9
|
*/
|
|
36
|
-
constructor({ title, question, viewFolder, fieldName, url, pageTitle, description, validators, html, hint, interfaceType, shouldDisplay, autocomplete, editable, actionLink, viewData }: import("
|
|
10
|
+
constructor({ title, question, viewFolder, fieldName, url, pageTitle, description, validators, html, hint, interfaceType, shouldDisplay, autocomplete, editable, actionLink, viewData, formatSummaryValue, capitaliseAnswer }: import("#typedefs/question-types.d.ts").QuestionParameters, methodOverrides?: Record<string, Function>);
|
|
37
11
|
/** @type {string} - html page title, defaults to question if not provided */
|
|
38
12
|
pageTitle: string;
|
|
39
13
|
/** @type {string} - title used in the summary list */
|
|
@@ -48,8 +22,8 @@ export class Question {
|
|
|
48
22
|
fieldName: string;
|
|
49
23
|
/** @type {boolean} if the question should appear in the journey overview task list or not */
|
|
50
24
|
taskList: boolean;
|
|
51
|
-
/** @type {Array.<BaseValidator>} array of validators that a question uses to validate answers */
|
|
52
|
-
validators: Array<BaseValidator>;
|
|
25
|
+
/** @type {Array.<import('../validator/base-validator.js').BaseValidator>} array of validators that a question uses to validate answers */
|
|
26
|
+
validators: Array<import("../validator/base-validator.js").BaseValidator>;
|
|
53
27
|
/** @type {string|undefined} hint text displayed to user */
|
|
54
28
|
hint: string | undefined;
|
|
55
29
|
/** @type {boolean} show return to listing page link after question */
|
|
@@ -60,10 +34,12 @@ export class Question {
|
|
|
60
34
|
html: string | undefined;
|
|
61
35
|
/** @type {string|undefined} optional question type */
|
|
62
36
|
interfaceType: string | undefined;
|
|
63
|
-
/** @type {ActionLink|undefined} override action link */
|
|
64
|
-
actionLink: ActionLink | undefined;
|
|
37
|
+
/** @type {import('#typedefs/question-types.d.ts').ActionLink|undefined} override action link */
|
|
38
|
+
actionLink: import("#typedefs/question-types.d.ts").ActionLink | undefined;
|
|
65
39
|
/** @type {string} 'not started' text to display (if a question has no answer) */
|
|
66
40
|
notStartedText: string;
|
|
41
|
+
/** @type {boolean} whether to capitalize the first letter of the answer in summary */
|
|
42
|
+
capitaliseAnswer: boolean;
|
|
67
43
|
/** @type {string} button text to display */
|
|
68
44
|
continueButtonText: string;
|
|
69
45
|
/** @type {string} text to display for 'change' link */
|
|
@@ -72,11 +48,13 @@ export class Question {
|
|
|
72
48
|
answerActionText: string;
|
|
73
49
|
/** @type {string} text to display for 'add' link */
|
|
74
50
|
addActionText: string;
|
|
51
|
+
/** @type {import('#typedefs/question-types.d.ts').SummaryValueFormatter|undefined} custom function to format the summary display value */
|
|
52
|
+
formatSummaryValue: import("#typedefs/question-types.d.ts").SummaryValueFormatter | undefined;
|
|
75
53
|
/**
|
|
76
54
|
* @param {import('../journey/journey-response.js').JourneyResponse} [response]
|
|
77
55
|
* @returns {boolean}
|
|
78
56
|
*/
|
|
79
|
-
shouldDisplay: () => boolean;
|
|
57
|
+
shouldDisplay: (response?: import("../journey/journey-response.js").JourneyResponse) => boolean;
|
|
80
58
|
details: {
|
|
81
59
|
title: string;
|
|
82
60
|
text: string;
|
|
@@ -93,8 +71,8 @@ export class Question {
|
|
|
93
71
|
editable: boolean;
|
|
94
72
|
viewData: {
|
|
95
73
|
[key: string]: any;
|
|
96
|
-
extraActionButtons?: import("
|
|
97
|
-
secondaryActions?: import("
|
|
74
|
+
extraActionButtons?: import("#typedefs/question-types.d.ts").SecondaryAction[];
|
|
75
|
+
secondaryActions?: import("#typedefs/question-types.d.ts").SecondaryAction[];
|
|
98
76
|
};
|
|
99
77
|
/**
|
|
100
78
|
* Is this question a manage list question?
|
|
@@ -126,22 +104,22 @@ export class Question {
|
|
|
126
104
|
* that prepQuestionForRendering doesn't need
|
|
127
105
|
*
|
|
128
106
|
* @param {Object} options
|
|
129
|
-
* @param {import('
|
|
107
|
+
* @param {import('#typedefs/journey-types.d.ts').RouteParams} options.params
|
|
130
108
|
* @param {import('../components/manage-list/question.js')} [options.manageListQuestion]
|
|
131
109
|
* @param {import('../section.js').Section} options.section - the current section
|
|
132
110
|
* @param {import('../journey/journey.js').Journey} options.journey - the journey we are in
|
|
133
111
|
* @param {Record<string, unknown>} [options.customViewData] additional data to send to view
|
|
134
112
|
* @param {unknown} [options.payload]
|
|
135
|
-
* @returns {QuestionViewModel}
|
|
113
|
+
* @returns {import('#typedefs/question-types.d.ts').QuestionViewModel}
|
|
136
114
|
*/
|
|
137
115
|
toViewModel({ params, manageListQuestion, section, journey, customViewData, payload }: {
|
|
138
|
-
params: import("
|
|
116
|
+
params: import("#typedefs/journey-types.d.ts").RouteParams;
|
|
139
117
|
manageListQuestion?: typeof import("../components/manage-list/question.js") | undefined;
|
|
140
118
|
section: import("../section.js").Section;
|
|
141
119
|
journey: import("../journey/journey.js").Journey;
|
|
142
120
|
customViewData?: Record<string, unknown> | undefined;
|
|
143
121
|
payload?: unknown;
|
|
144
|
-
}): QuestionViewModel;
|
|
122
|
+
}): import("#typedefs/question-types.d.ts").QuestionViewModel;
|
|
145
123
|
/**
|
|
146
124
|
* gets the base view model for this question
|
|
147
125
|
*
|
|
@@ -149,10 +127,10 @@ export class Question {
|
|
|
149
127
|
* @param {import('../journey/journey.js').Journey} journey - the journey we are in
|
|
150
128
|
* @param {Record<string, unknown>} [customViewData] additional data to send to view
|
|
151
129
|
* @param {unknown} [payload]
|
|
152
|
-
* @param {import('
|
|
153
|
-
* @returns {QuestionViewModel}
|
|
130
|
+
* @param {import('#typedefs/question-types.d.ts').PrepQuestionForRenderingOptions} [options] - required to support manage list question
|
|
131
|
+
* @returns {import('#typedefs/question-types.d.ts').QuestionViewModel}
|
|
154
132
|
*/
|
|
155
|
-
prepQuestionForRendering(section: import("../section.js").Section, journey: import("../journey/journey.js").Journey, customViewData?: Record<string, unknown>, payload?: unknown, options?: import("
|
|
133
|
+
prepQuestionForRendering(section: import("../section.js").Section, journey: import("../journey/journey.js").Journey, customViewData?: Record<string, unknown>, payload?: unknown, options?: import("#typedefs/question-types.d.ts").PrepQuestionForRenderingOptions): import("#typedefs/question-types.d.ts").QuestionViewModel;
|
|
156
134
|
/**
|
|
157
135
|
* The answer to this question for use in the viewModel
|
|
158
136
|
*
|
|
@@ -167,32 +145,32 @@ export class Question {
|
|
|
167
145
|
*
|
|
168
146
|
* If possible override this method instead of prepQuestionForRendering for simple changes to the view model
|
|
169
147
|
*
|
|
170
|
-
* @param {QuestionViewModel} viewModel
|
|
171
|
-
*/ addCustomDataToViewModel(viewModel: QuestionViewModel): void;
|
|
148
|
+
* @param {import('#typedefs/question-types.d.ts').QuestionViewModel} viewModel
|
|
149
|
+
*/ addCustomDataToViewModel(viewModel: import("#typedefs/question-types.d.ts").QuestionViewModel): void;
|
|
172
150
|
/**
|
|
173
151
|
* Get the answers object from the journey response, which may be nested in an array for manage list questions
|
|
174
152
|
*
|
|
175
153
|
* @param {import('../journey/journey-response.js').JourneyResponse} response
|
|
176
|
-
* @param {import('
|
|
154
|
+
* @param {import('#typedefs/question-types.d.ts').PrepQuestionForRenderingOptions} [options]
|
|
177
155
|
* @returns {Record<string, any>}
|
|
178
156
|
*/
|
|
179
|
-
answerObjectFromJourneyResponse(response: import("../journey/journey-response.js").JourneyResponse, { params, manageListQuestion }?: import("
|
|
157
|
+
answerObjectFromJourneyResponse(response: import("../journey/journey-response.js").JourneyResponse, { params, manageListQuestion }?: import("#typedefs/question-types.d.ts").PrepQuestionForRenderingOptions): Record<string, any>;
|
|
180
158
|
/**
|
|
181
159
|
* renders the question
|
|
182
160
|
* @param {import('express').Response} res - the express response
|
|
183
|
-
* @param {QuestionViewModel} viewModel additional data to send to view
|
|
161
|
+
* @param {import('#typedefs/question-types.d.ts').QuestionViewModel} viewModel additional data to send to view
|
|
184
162
|
* @returns {void}
|
|
185
163
|
*/
|
|
186
|
-
renderAction(res: import("express").Response, viewModel: QuestionViewModel): void;
|
|
164
|
+
renderAction(res: import("express").Response, viewModel: import("#typedefs/question-types.d.ts").QuestionViewModel): void;
|
|
187
165
|
/**
|
|
188
166
|
* check for validation errors
|
|
189
167
|
* @param {import('express').Request} req
|
|
190
168
|
* @param {import('../journey/journey.js').Journey} journey
|
|
191
169
|
* @param {import('../section.js').Section} section
|
|
192
170
|
* @param {import('../components/manage-list/question.js')} [manageListQuestion]
|
|
193
|
-
* @returns {QuestionViewModel|undefined} returns the view model for displaying the error or undefined if there are no errors
|
|
171
|
+
* @returns {import('#typedefs/question-types.d.ts').QuestionViewModel|undefined} returns the view model for displaying the error or undefined if there are no errors
|
|
194
172
|
*/
|
|
195
|
-
checkForValidationErrors(req: import("express").Request, section: import("../section.js").Section, journey: import("../journey/journey.js").Journey, manageListQuestion?: typeof import("../components/manage-list/question.js")): QuestionViewModel | undefined;
|
|
173
|
+
checkForValidationErrors(req: import("express").Request, section: import("../section.js").Section, journey: import("../journey/journey.js").Journey, manageListQuestion?: typeof import("../components/manage-list/question.js")): import("#typedefs/question-types.d.ts").QuestionViewModel | undefined;
|
|
196
174
|
/**
|
|
197
175
|
* Get the data to save from the request, returns an object of answers
|
|
198
176
|
*
|
|
@@ -207,8 +185,8 @@ export class Question {
|
|
|
207
185
|
* @param {import('express').Request} req
|
|
208
186
|
* @param {import('../journey/journey.js').Journey} journey
|
|
209
187
|
* @param {import('../section.js').Section} sectionObj
|
|
210
|
-
* @returns {QuestionViewModel | undefined} returns the view model for displaying the error or undefined if there are no errors
|
|
211
|
-
*/ checkForSavingErrors(req: import("express").Request, sectionObj: import("../section.js").Section, journey: import("../journey/journey.js").Journey): QuestionViewModel | undefined;
|
|
188
|
+
* @returns {import('#typedefs/question-types.d.ts').QuestionViewModel | undefined} returns the view model for displaying the error or undefined if there are no errors
|
|
189
|
+
*/ checkForSavingErrors(req: import("express").Request, sectionObj: import("../section.js").Section, journey: import("../journey/journey.js").Journey): import("#typedefs/question-types.d.ts").QuestionViewModel | undefined;
|
|
212
190
|
/**
|
|
213
191
|
* Handles redirect after saving. Kept around for backwards compatibility.
|
|
214
192
|
*
|
|
@@ -222,33 +200,35 @@ export class Question {
|
|
|
222
200
|
handleNextQuestion(res: import("express").Response, journey: import("../journey/journey.js").Journey, sectionSegment: string, questionSegment: string): void;
|
|
223
201
|
/**
|
|
224
202
|
* returns the formatted answers values to be used to build task list elements
|
|
225
|
-
* @param {
|
|
203
|
+
* @param {string} sectionSegment
|
|
226
204
|
* @param {import('../journey/journey.js').Journey} journey
|
|
227
205
|
* @param {unknown} answer
|
|
228
|
-
* @
|
|
229
|
-
*
|
|
230
|
-
* value: string | Object;
|
|
231
|
-
* action?: ActionView | ActionView[];
|
|
232
|
-
* }>}
|
|
206
|
+
* @param {boolean} [capitals] - deprecated: use capitaliseAnswer property instead
|
|
207
|
+
* @returns {import('#typedefs/question-types.d.ts').SummaryRow[]}
|
|
233
208
|
*/
|
|
234
|
-
formatAnswerForSummary(sectionSegment: string, journey: import("../journey/journey.js").Journey, answer: unknown, capitals?: boolean):
|
|
235
|
-
key: string;
|
|
236
|
-
value: string | Object;
|
|
237
|
-
action?: ActionView | ActionView[];
|
|
238
|
-
}>;
|
|
209
|
+
formatAnswerForSummary(sectionSegment: string, journey: import("../journey/journey.js").Journey, answer: unknown, capitals?: boolean): import("#typedefs/question-types.d.ts").SummaryRow[];
|
|
239
210
|
/**
|
|
240
211
|
* Returns the action link for the question
|
|
241
|
-
* @param {
|
|
212
|
+
* @param {string} sectionSegment
|
|
242
213
|
* @param {import('../journey/journey.js').Journey} journey
|
|
243
|
-
* @param {
|
|
244
|
-
* @returns {ActionView|ActionView[]|undefined}
|
|
214
|
+
* @param {unknown} answer
|
|
215
|
+
* @returns {import('#typedefs/question-types.d.ts').ActionView | import('#typedefs/question-types.d.ts').ActionView[] | undefined}
|
|
245
216
|
*/
|
|
246
|
-
getAction(sectionSegment: string, journey: import("../journey/journey.js").Journey, answer: unknown): ActionView | ActionView[] | undefined;
|
|
217
|
+
getAction(sectionSegment: string, journey: import("../journey/journey.js").Journey, answer: unknown): import("#typedefs/question-types.d.ts").ActionView | import("#typedefs/question-types.d.ts").ActionView[] | undefined;
|
|
247
218
|
/**
|
|
248
219
|
* @param {unknown} answer
|
|
249
220
|
* @returns {unknown}
|
|
250
221
|
*/
|
|
251
222
|
format(answer: unknown): unknown;
|
|
223
|
+
/**
|
|
224
|
+
* Formats an answer value for display in the summary.
|
|
225
|
+
* Subclasses can override this to provide custom formatting without
|
|
226
|
+
* needing to override the full formatAnswerForSummary method.
|
|
227
|
+
*
|
|
228
|
+
* @param {unknown} answer - the raw answer value
|
|
229
|
+
* @returns {string} the formatted answer for display
|
|
230
|
+
*/
|
|
231
|
+
formatAnswer(answer: unknown): string;
|
|
252
232
|
/**
|
|
253
233
|
* @returns {boolean}
|
|
254
234
|
*/
|
|
@@ -264,17 +244,6 @@ export class Question {
|
|
|
264
244
|
* @returns {boolean}
|
|
265
245
|
*/
|
|
266
246
|
isAnswered(journeyResponse: import("../journey/journey-response.js").JourneyResponse, fieldName?: string): boolean;
|
|
247
|
+
#private;
|
|
267
248
|
}
|
|
268
|
-
export type PreppedQuestion = {
|
|
269
|
-
value: Object;
|
|
270
|
-
question: string;
|
|
271
|
-
fieldName: string;
|
|
272
|
-
pageTitle: string;
|
|
273
|
-
description?: string | undefined;
|
|
274
|
-
html?: string | undefined;
|
|
275
|
-
};
|
|
276
|
-
export type QuestionViewModel = Record<string, any>;
|
|
277
|
-
import type { BaseValidator } from "#base-validator";
|
|
278
|
-
import type { ActionLink } from "./question-types.js";
|
|
279
|
-
import type { ActionView } from "../controller.js";
|
|
280
249
|
//# sourceMappingURL=question.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"question.d.ts","sourceRoot":"","sources":["question.js"],"names":[],"mappings":"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"}
|
|
@@ -3,35 +3,6 @@ import { capitalize, nl2br, trimTrailingSlash } from '../lib/utils.js';
|
|
|
3
3
|
import MultiFieldInputValidator from '../validator/multi-field-input-validator.js';
|
|
4
4
|
import { answerObjectForManageList } from '#src/components/manage-list/utils.js';
|
|
5
5
|
|
|
6
|
-
/**
|
|
7
|
-
* @import {BaseValidator} from "#base-validator"
|
|
8
|
-
* @import {ActionView} from "../controller.js"
|
|
9
|
-
* @import {ActionLink} from "./question-types.js"
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* @typedef {Object} PreppedQuestion
|
|
14
|
-
* @property {Object} value
|
|
15
|
-
* @property {string} question
|
|
16
|
-
* @property {string} fieldName
|
|
17
|
-
* @property {string} pageTitle
|
|
18
|
-
* @property {string} [description]
|
|
19
|
-
* @property {string} [html]
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* @typedef {Record<string, any>} QuestionViewModel
|
|
24
|
-
* @property {PreppedQuestion} question
|
|
25
|
-
* @property {string} layoutTemplate
|
|
26
|
-
* @property {string} pageCaption
|
|
27
|
-
* @property {string} [continueButtonText]
|
|
28
|
-
* @property {string} backLink
|
|
29
|
-
* @property {boolean} showBackToListLink
|
|
30
|
-
* @property {string} listLink
|
|
31
|
-
* @property {Object} util
|
|
32
|
-
* @property {function(string): string} util.trimTrailingSlash
|
|
33
|
-
*/
|
|
34
|
-
|
|
35
6
|
/**
|
|
36
7
|
* A specific question within a journey which is made up of one (usually) or many (sometimes) components and their required content.
|
|
37
8
|
* @class
|
|
@@ -51,7 +22,7 @@ export class Question {
|
|
|
51
22
|
fieldName;
|
|
52
23
|
/** @type {boolean} if the question should appear in the journey overview task list or not */
|
|
53
24
|
taskList = true;
|
|
54
|
-
/** @type {Array.<BaseValidator>} array of validators that a question uses to validate answers */
|
|
25
|
+
/** @type {Array.<import('../validator/base-validator.js').BaseValidator>} array of validators that a question uses to validate answers */
|
|
55
26
|
validators = [];
|
|
56
27
|
/** @type {string|undefined} hint text displayed to user */
|
|
57
28
|
hint;
|
|
@@ -63,11 +34,13 @@ export class Question {
|
|
|
63
34
|
html;
|
|
64
35
|
/** @type {string|undefined} optional question type */
|
|
65
36
|
interfaceType;
|
|
66
|
-
/** @type {ActionLink|undefined} override action link */
|
|
37
|
+
/** @type {import('#typedefs/question-types.d.ts').ActionLink|undefined} override action link */
|
|
67
38
|
actionLink;
|
|
68
39
|
|
|
69
40
|
/** @type {string} 'not started' text to display (if a question has no answer) */
|
|
70
41
|
notStartedText = 'Not started';
|
|
42
|
+
/** @type {boolean} whether to capitalize the first letter of the answer in summary */
|
|
43
|
+
capitaliseAnswer = true;
|
|
71
44
|
/** @type {string} button text to display */
|
|
72
45
|
continueButtonText = 'Continue';
|
|
73
46
|
/** @type {string} text to display for 'change' link */
|
|
@@ -76,12 +49,15 @@ export class Question {
|
|
|
76
49
|
answerActionText = 'Answer';
|
|
77
50
|
/** @type {string} text to display for 'add' link */
|
|
78
51
|
addActionText = 'Add';
|
|
52
|
+
/** @type {import('#typedefs/question-types.d.ts').SummaryValueFormatter|undefined} custom function to format the summary display value */
|
|
53
|
+
formatSummaryValue;
|
|
79
54
|
|
|
80
55
|
/**
|
|
81
56
|
* @param {import('../journey/journey-response.js').JourneyResponse} [response]
|
|
82
57
|
* @returns {boolean}
|
|
83
58
|
*/
|
|
84
|
-
|
|
59
|
+
// eslint-disable-next-line no-unused-vars -- response will be used by extending classes
|
|
60
|
+
shouldDisplay = (response) => true;
|
|
85
61
|
|
|
86
62
|
details = {
|
|
87
63
|
title: '',
|
|
@@ -97,7 +73,7 @@ export class Question {
|
|
|
97
73
|
_isInManageListSection = false;
|
|
98
74
|
|
|
99
75
|
/**
|
|
100
|
-
* @param {import('
|
|
76
|
+
* @param {import('#typedefs/question-types.d.ts').QuestionParameters} params
|
|
101
77
|
* @param {Record<string, Function>} [methodOverrides]
|
|
102
78
|
*/
|
|
103
79
|
constructor(
|
|
@@ -117,7 +93,9 @@ export class Question {
|
|
|
117
93
|
autocomplete,
|
|
118
94
|
editable = true,
|
|
119
95
|
actionLink,
|
|
120
|
-
viewData = {}
|
|
96
|
+
viewData = {},
|
|
97
|
+
formatSummaryValue,
|
|
98
|
+
capitaliseAnswer = true
|
|
121
99
|
},
|
|
122
100
|
methodOverrides
|
|
123
101
|
) {
|
|
@@ -139,6 +117,8 @@ export class Question {
|
|
|
139
117
|
this.editable = editable;
|
|
140
118
|
this.actionLink = actionLink;
|
|
141
119
|
this.viewData = viewData;
|
|
120
|
+
this.formatSummaryValue = formatSummaryValue;
|
|
121
|
+
this.capitaliseAnswer = capitaliseAnswer;
|
|
142
122
|
|
|
143
123
|
if (shouldDisplay) {
|
|
144
124
|
this.shouldDisplay = shouldDisplay;
|
|
@@ -181,6 +161,18 @@ export class Question {
|
|
|
181
161
|
this._isInManageListSection = value;
|
|
182
162
|
}
|
|
183
163
|
|
|
164
|
+
/**
|
|
165
|
+
* Applies custom summary formatting if a formatSummaryValue function is provided
|
|
166
|
+
* @param {import('#typedefs/question-types.d.ts').SummaryFormatterContext} context - the context for formatting
|
|
167
|
+
* @returns {string}
|
|
168
|
+
*/
|
|
169
|
+
#applyCustomSummaryFormatter(context) {
|
|
170
|
+
if (this.formatSummaryValue) {
|
|
171
|
+
return this.formatSummaryValue(context);
|
|
172
|
+
}
|
|
173
|
+
return context.formattedAnswer;
|
|
174
|
+
}
|
|
175
|
+
|
|
184
176
|
/**
|
|
185
177
|
* Gets the body field names used by this question in form submissions.
|
|
186
178
|
* Returns the field names that should be present in req.body when this question is submitted.
|
|
@@ -199,13 +191,13 @@ export class Question {
|
|
|
199
191
|
* that prepQuestionForRendering doesn't need
|
|
200
192
|
*
|
|
201
193
|
* @param {Object} options
|
|
202
|
-
* @param {import('
|
|
194
|
+
* @param {import('#typedefs/journey-types.d.ts').RouteParams} options.params
|
|
203
195
|
* @param {import('../components/manage-list/question.js')} [options.manageListQuestion]
|
|
204
196
|
* @param {import('../section.js').Section} options.section - the current section
|
|
205
197
|
* @param {import('../journey/journey.js').Journey} options.journey - the journey we are in
|
|
206
198
|
* @param {Record<string, unknown>} [options.customViewData] additional data to send to view
|
|
207
199
|
* @param {unknown} [options.payload]
|
|
208
|
-
* @returns {QuestionViewModel}
|
|
200
|
+
* @returns {import('#typedefs/question-types.d.ts').QuestionViewModel}
|
|
209
201
|
*/
|
|
210
202
|
toViewModel({ params, manageListQuestion, section, journey, customViewData, payload }) {
|
|
211
203
|
const viewModel = this.prepQuestionForRendering(section, journey, customViewData, payload, {
|
|
@@ -223,8 +215,8 @@ export class Question {
|
|
|
223
215
|
* @param {import('../journey/journey.js').Journey} journey - the journey we are in
|
|
224
216
|
* @param {Record<string, unknown>} [customViewData] additional data to send to view
|
|
225
217
|
* @param {unknown} [payload]
|
|
226
|
-
* @param {import('
|
|
227
|
-
* @returns {QuestionViewModel}
|
|
218
|
+
* @param {import('#typedefs/question-types.d.ts').PrepQuestionForRenderingOptions} [options] - required to support manage list question
|
|
219
|
+
* @returns {import('#typedefs/question-types.d.ts').QuestionViewModel}
|
|
228
220
|
*/
|
|
229
221
|
prepQuestionForRendering(section, journey, customViewData, payload, options) {
|
|
230
222
|
const answers = payload || this.answerObjectFromJourneyResponse(journey.response, options);
|
|
@@ -283,7 +275,7 @@ export class Question {
|
|
|
283
275
|
*
|
|
284
276
|
* If possible override this method instead of prepQuestionForRendering for simple changes to the view model
|
|
285
277
|
*
|
|
286
|
-
* @param {QuestionViewModel} viewModel
|
|
278
|
+
* @param {import('#typedefs/question-types.d.ts').QuestionViewModel} viewModel
|
|
287
279
|
*/ // eslint-disable-next-line no-unused-vars
|
|
288
280
|
addCustomDataToViewModel(viewModel) {}
|
|
289
281
|
|
|
@@ -291,7 +283,7 @@ export class Question {
|
|
|
291
283
|
* Get the answers object from the journey response, which may be nested in an array for manage list questions
|
|
292
284
|
*
|
|
293
285
|
* @param {import('../journey/journey-response.js').JourneyResponse} response
|
|
294
|
-
* @param {import('
|
|
286
|
+
* @param {import('#typedefs/question-types.d.ts').PrepQuestionForRenderingOptions} [options]
|
|
295
287
|
* @returns {Record<string, any>}
|
|
296
288
|
*/
|
|
297
289
|
answerObjectFromJourneyResponse(response, { params, manageListQuestion } = {}) {
|
|
@@ -311,7 +303,7 @@ export class Question {
|
|
|
311
303
|
/**
|
|
312
304
|
* renders the question
|
|
313
305
|
* @param {import('express').Response} res - the express response
|
|
314
|
-
* @param {QuestionViewModel} viewModel additional data to send to view
|
|
306
|
+
* @param {import('#typedefs/question-types.d.ts').QuestionViewModel} viewModel additional data to send to view
|
|
315
307
|
* @returns {void}
|
|
316
308
|
*/
|
|
317
309
|
renderAction(res, viewModel) {
|
|
@@ -329,7 +321,7 @@ export class Question {
|
|
|
329
321
|
* @param {import('../journey/journey.js').Journey} journey
|
|
330
322
|
* @param {import('../section.js').Section} section
|
|
331
323
|
* @param {import('../components/manage-list/question.js')} [manageListQuestion]
|
|
332
|
-
* @returns {QuestionViewModel|undefined} returns the view model for displaying the error or undefined if there are no errors
|
|
324
|
+
* @returns {import('#typedefs/question-types.d.ts').QuestionViewModel|undefined} returns the view model for displaying the error or undefined if there are no errors
|
|
333
325
|
*/
|
|
334
326
|
checkForValidationErrors(req, section, journey, manageListQuestion) {
|
|
335
327
|
const { body = {} } = req;
|
|
@@ -376,7 +368,7 @@ export class Question {
|
|
|
376
368
|
* @param {import('express').Request} req
|
|
377
369
|
* @param {import('../journey/journey.js').Journey} journey
|
|
378
370
|
* @param {import('../section.js').Section} sectionObj
|
|
379
|
-
* @returns {QuestionViewModel | undefined} returns the view model for displaying the error or undefined if there are no errors
|
|
371
|
+
* @returns {import('#typedefs/question-types.d.ts').QuestionViewModel | undefined} returns the view model for displaying the error or undefined if there are no errors
|
|
380
372
|
*/ //eslint-disable-next-line no-unused-vars
|
|
381
373
|
checkForSavingErrors(req, sectionObj, journey) {
|
|
382
374
|
return;
|
|
@@ -401,34 +393,49 @@ export class Question {
|
|
|
401
393
|
|
|
402
394
|
/**
|
|
403
395
|
* returns the formatted answers values to be used to build task list elements
|
|
404
|
-
* @param {
|
|
396
|
+
* @param {string} sectionSegment
|
|
405
397
|
* @param {import('../journey/journey.js').Journey} journey
|
|
406
398
|
* @param {unknown} answer
|
|
407
|
-
* @
|
|
408
|
-
*
|
|
409
|
-
* value: string | Object;
|
|
410
|
-
* action?: ActionView | ActionView[];
|
|
411
|
-
* }>}
|
|
399
|
+
* @param {boolean} [capitals] - deprecated: use capitaliseAnswer property instead
|
|
400
|
+
* @returns {import('#typedefs/question-types.d.ts').SummaryRow[]}
|
|
412
401
|
*/
|
|
413
|
-
formatAnswerForSummary(sectionSegment, journey, answer, capitals
|
|
414
|
-
|
|
402
|
+
formatAnswerForSummary(sectionSegment, journey, answer, capitals) {
|
|
403
|
+
let formattedAnswer = this.formatAnswer(answer);
|
|
404
|
+
|
|
405
|
+
// Handle deprecated capitals parameter
|
|
406
|
+
// TODO DF-49 fully remove capitals parameter
|
|
407
|
+
if (capitals !== undefined && capitals !== this.capitaliseAnswer) {
|
|
408
|
+
console.warn(
|
|
409
|
+
`[Deprecation Warning] The 'capitals' parameter in formatAnswerForSummary is deprecated. ` +
|
|
410
|
+
`Set 'capitaliseAnswer: ${capitals}' in the question constructor instead.`
|
|
411
|
+
);
|
|
412
|
+
// Temporarily override for backwards compatibility
|
|
413
|
+
const originalCapitalize = this.capitaliseAnswer;
|
|
414
|
+
this.capitaliseAnswer = capitals;
|
|
415
|
+
formattedAnswer = this.formatAnswer(answer);
|
|
416
|
+
this.capitaliseAnswer = originalCapitalize;
|
|
417
|
+
}
|
|
418
|
+
|
|
415
419
|
const action = this.getAction(sectionSegment, journey, answer);
|
|
416
420
|
const key = this.title ?? this.question;
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
421
|
+
|
|
422
|
+
const displayValue = this.#applyCustomSummaryFormatter({
|
|
423
|
+
answer,
|
|
424
|
+
formattedAnswer,
|
|
425
|
+
question: this,
|
|
426
|
+
journey,
|
|
427
|
+
sectionSegment
|
|
422
428
|
});
|
|
423
|
-
|
|
429
|
+
|
|
430
|
+
return [{ key, value: displayValue, action }];
|
|
424
431
|
}
|
|
425
432
|
|
|
426
433
|
/**
|
|
427
434
|
* Returns the action link for the question
|
|
428
|
-
* @param {
|
|
435
|
+
* @param {string} sectionSegment
|
|
429
436
|
* @param {import('../journey/journey.js').Journey} journey
|
|
430
|
-
* @param {
|
|
431
|
-
* @returns {ActionView|ActionView[]|undefined}
|
|
437
|
+
* @param {unknown} answer
|
|
438
|
+
* @returns {import('#typedefs/question-types.d.ts').ActionView | import('#typedefs/question-types.d.ts').ActionView[] | undefined}
|
|
432
439
|
*/
|
|
433
440
|
getAction(sectionSegment, journey, answer) {
|
|
434
441
|
if (this.actionLink) {
|
|
@@ -459,6 +466,22 @@ export class Question {
|
|
|
459
466
|
return answer;
|
|
460
467
|
}
|
|
461
468
|
|
|
469
|
+
/**
|
|
470
|
+
* Formats an answer value for display in the summary.
|
|
471
|
+
* Subclasses can override this to provide custom formatting without
|
|
472
|
+
* needing to override the full formatAnswerForSummary method.
|
|
473
|
+
*
|
|
474
|
+
* @param {unknown} answer - the raw answer value
|
|
475
|
+
* @returns {string} the formatted answer for display
|
|
476
|
+
*/
|
|
477
|
+
formatAnswer(answer) {
|
|
478
|
+
let formatted = answer ?? this.notStartedText;
|
|
479
|
+
if (this.capitaliseAnswer) {
|
|
480
|
+
formatted = capitalize(formatted);
|
|
481
|
+
}
|
|
482
|
+
return nl2br(escape(formatted));
|
|
483
|
+
}
|
|
484
|
+
|
|
462
485
|
/**
|
|
463
486
|
* @returns {boolean}
|
|
464
487
|
*/
|