@planning-inspectorate/dynamic-forms 3.5.0 → 3.6.1
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/package.json +1 -1
- package/src/components/date-period/index.njk +12 -4
- package/src/components/date-period/question.js +7 -1
- package/src/components/date-period/question.test.js +22 -0
- package/src/components/manage-list/README.md +5 -0
- package/src/components/manage-list/confirm.njk +22 -0
- package/src/components/manage-list/index.njk +2 -2
- package/src/components/manage-list/manage-list-actions.js +9 -0
- package/src/components/manage-list/question.js +23 -0
- package/src/components/manage-list/question.test.js +15 -0
- package/src/controller.js +17 -1
- package/src/controller.test.js +134 -0
- package/src/journey/journey.js +19 -0
- package/src/journey/journey.test.js +33 -0
- package/src/lib/session-answer-store.js +24 -2
- package/src/lib/session-answer-store.test.js +28 -0
- package/test/snapshots/holiday-period.html +2 -0
- package/test/snapshots/manage-list-confirmation-render.html +12 -0
- package/types/src/components/date-period/question.d.ts +6 -1
- package/types/src/components/date-period/question.d.ts.map +1 -1
- package/types/src/components/manage-list/manage-list-actions.d.ts +10 -0
- package/types/src/components/manage-list/manage-list-actions.d.ts.map +1 -0
- package/types/src/components/manage-list/question.d.ts +7 -0
- package/types/src/components/manage-list/question.d.ts.map +1 -1
- package/types/src/controller.d.ts +2 -0
- package/types/src/controller.d.ts.map +1 -1
- package/types/src/journey/journey.d.ts.map +1 -1
- package/types/src/lib/session-answer-store.d.ts +4 -0
- package/types/src/lib/session-answer-store.d.ts.map +1 -1
package/package.json
CHANGED
|
@@ -33,6 +33,14 @@
|
|
|
33
33
|
}
|
|
34
34
|
}) }}
|
|
35
35
|
|
|
36
|
+
{% if question.hint %}
|
|
37
|
+
<div class="govuk-hint">{{ question.hint }}</div>
|
|
38
|
+
{% endif %}
|
|
39
|
+
|
|
40
|
+
{% if question.html %}
|
|
41
|
+
{% include question.html ignore missing %}
|
|
42
|
+
{% endif %}
|
|
43
|
+
|
|
36
44
|
{{ govukDateInput({
|
|
37
45
|
id: startFieldName,
|
|
38
46
|
fieldset: {
|
|
@@ -40,8 +48,8 @@
|
|
|
40
48
|
text: labels.start
|
|
41
49
|
}
|
|
42
50
|
},
|
|
43
|
-
hint: question.
|
|
44
|
-
text: question.
|
|
51
|
+
hint: question.hintStart and {
|
|
52
|
+
text: question.hintStart
|
|
45
53
|
},
|
|
46
54
|
errorMessage: errorSummary and {
|
|
47
55
|
text: errors[question.fieldName].msg or errors[startFieldName].msg or errors[startDayFieldName].msg or errors[startMonthFieldName].msg or errors[startYearFieldName].msg
|
|
@@ -78,8 +86,8 @@
|
|
|
78
86
|
text: labels.end
|
|
79
87
|
}
|
|
80
88
|
},
|
|
81
|
-
hint: question.
|
|
82
|
-
|
|
89
|
+
hint: question.hintEnd and {
|
|
90
|
+
text: question.hintEnd
|
|
83
91
|
},
|
|
84
92
|
errorMessage: errorSummary and {
|
|
85
93
|
text: errors[question.fieldName].msg or errors[endFieldName].msg or errors[endDayFieldName].msg or errors[endMonthFieldName].msg or errors[endYearFieldName].msg
|
|
@@ -14,16 +14,22 @@ export default class DatePeriodQuestion extends Question {
|
|
|
14
14
|
* @param {import('#question-types').QuestionParameters} params
|
|
15
15
|
* @param {string} [params.dateFormat]
|
|
16
16
|
* @param {{start: string, end: string}} [params.labels]
|
|
17
|
+
* @param {string} [params.hint]
|
|
18
|
+
* @param {string} [params.hintStart]
|
|
19
|
+
* @param {string} [params.hintEnd]
|
|
17
20
|
* @param {{hour: number, minute: number, second: number}} [params.startTime]
|
|
18
21
|
* @param {{hour: number, minute: number, second: number}} [params.endTime]
|
|
19
22
|
*/
|
|
20
|
-
constructor({ dateFormat = DEFAULT_DATE_FORMAT, labels, startTime, endTime, ...params }) {
|
|
23
|
+
constructor({ dateFormat = DEFAULT_DATE_FORMAT, hint, labels, hintStart, startTime, hintEnd, endTime, ...params }) {
|
|
21
24
|
super({
|
|
22
25
|
...params,
|
|
23
26
|
viewFolder: 'date-period'
|
|
24
27
|
});
|
|
25
28
|
this.dateFormat = dateFormat;
|
|
26
29
|
this.labels = labels || { start: 'Start', end: 'End' };
|
|
30
|
+
this.hint = hint;
|
|
31
|
+
this.hintStart = hintStart;
|
|
32
|
+
this.hintEnd = hintEnd;
|
|
27
33
|
this.startTime = startTime || { hour: 0, minute: 0, second: 0 };
|
|
28
34
|
this.endTime = endTime || { hour: 0, minute: 0, second: 0 };
|
|
29
35
|
}
|
|
@@ -10,6 +10,8 @@ describe('DatePeriodQuestion', () => {
|
|
|
10
10
|
const FIELDNAME = 'fieldName';
|
|
11
11
|
const HINT = 'hint hint';
|
|
12
12
|
const VALIDATORS = [];
|
|
13
|
+
const HINTSTART = 'Start date hint';
|
|
14
|
+
const HINTEND = 'End date hint';
|
|
13
15
|
|
|
14
16
|
describe('constructor', () => {
|
|
15
17
|
it('should instantiate and inherit from Question', () => {
|
|
@@ -18,6 +20,8 @@ describe('DatePeriodQuestion', () => {
|
|
|
18
20
|
question: QUESTION,
|
|
19
21
|
fieldName: FIELDNAME,
|
|
20
22
|
hint: HINT,
|
|
23
|
+
hintStart: HINTSTART,
|
|
24
|
+
hintEnd: HINTEND,
|
|
21
25
|
validators: VALIDATORS
|
|
22
26
|
});
|
|
23
27
|
assert.strictEqual(dateQuestion instanceof DatePeriodQuestion, true);
|
|
@@ -33,6 +37,8 @@ describe('DatePeriodQuestion', () => {
|
|
|
33
37
|
question: QUESTION,
|
|
34
38
|
fieldName: FIELDNAME,
|
|
35
39
|
hint: HINT,
|
|
40
|
+
hintStart: HINTSTART,
|
|
41
|
+
hintEnd: HINTEND,
|
|
36
42
|
validators: VALIDATORS
|
|
37
43
|
});
|
|
38
44
|
|
|
@@ -63,6 +69,8 @@ describe('DatePeriodQuestion', () => {
|
|
|
63
69
|
question: QUESTION,
|
|
64
70
|
fieldName: FIELDNAME,
|
|
65
71
|
hint: HINT,
|
|
72
|
+
hintStart: HINTSTART,
|
|
73
|
+
hintEnd: HINTEND,
|
|
66
74
|
validators: VALIDATORS,
|
|
67
75
|
startTime: { hour: 3, minute: 24, second: 0 },
|
|
68
76
|
endTime: { hour: 23, minute: 59, second: 0 }
|
|
@@ -98,6 +106,8 @@ describe('DatePeriodQuestion', () => {
|
|
|
98
106
|
question: QUESTION,
|
|
99
107
|
fieldName: FIELDNAME,
|
|
100
108
|
hint: HINT,
|
|
109
|
+
hintStart: HINTSTART,
|
|
110
|
+
hintEnd: HINTEND,
|
|
101
111
|
validators: VALIDATORS
|
|
102
112
|
});
|
|
103
113
|
|
|
@@ -142,6 +152,8 @@ describe('DatePeriodQuestion', () => {
|
|
|
142
152
|
question: QUESTION,
|
|
143
153
|
fieldName: FIELDNAME,
|
|
144
154
|
hint: HINT,
|
|
155
|
+
hintStart: HINTSTART,
|
|
156
|
+
hintEnd: HINTEND,
|
|
145
157
|
validators: VALIDATORS
|
|
146
158
|
});
|
|
147
159
|
|
|
@@ -189,6 +201,8 @@ describe('DatePeriodQuestion', () => {
|
|
|
189
201
|
question: QUESTION,
|
|
190
202
|
fieldName: FIELDNAME,
|
|
191
203
|
hint: HINT,
|
|
204
|
+
hintStart: HINTSTART,
|
|
205
|
+
hintEnd: HINTEND,
|
|
192
206
|
validators: VALIDATORS
|
|
193
207
|
});
|
|
194
208
|
|
|
@@ -231,6 +245,8 @@ describe('DatePeriodQuestion', () => {
|
|
|
231
245
|
question: QUESTION,
|
|
232
246
|
fieldName: FIELDNAME,
|
|
233
247
|
hint: HINT,
|
|
248
|
+
hintStart: HINTSTART,
|
|
249
|
+
hintEnd: HINTEND,
|
|
234
250
|
validators: VALIDATORS
|
|
235
251
|
});
|
|
236
252
|
|
|
@@ -260,6 +276,8 @@ describe('DatePeriodQuestion', () => {
|
|
|
260
276
|
question: QUESTION,
|
|
261
277
|
fieldName: FIELDNAME,
|
|
262
278
|
hint: HINT,
|
|
279
|
+
hintStart: HINTSTART,
|
|
280
|
+
hintEnd: HINTEND,
|
|
263
281
|
validators: VALIDATORS
|
|
264
282
|
});
|
|
265
283
|
|
|
@@ -289,6 +307,8 @@ describe('DatePeriodQuestion', () => {
|
|
|
289
307
|
question: QUESTION,
|
|
290
308
|
fieldName: FIELDNAME,
|
|
291
309
|
hint: HINT,
|
|
310
|
+
hintStart: HINTSTART,
|
|
311
|
+
hintEnd: HINTEND,
|
|
292
312
|
validators: VALIDATORS,
|
|
293
313
|
labels: { start: 'Open', end: 'Close' }
|
|
294
314
|
});
|
|
@@ -320,6 +340,8 @@ describe('DatePeriodQuestion', () => {
|
|
|
320
340
|
question: QUESTION,
|
|
321
341
|
fieldName: FIELDNAME,
|
|
322
342
|
hint: HINT,
|
|
343
|
+
hintStart: HINTSTART,
|
|
344
|
+
hintEnd: HINTEND,
|
|
323
345
|
validators: VALIDATORS
|
|
324
346
|
});
|
|
325
347
|
|
|
@@ -79,6 +79,10 @@ new Section('New Holiday', 'holiday')
|
|
|
79
79
|
|
|
80
80
|
`{/:manageListAction/:manageListItemId/:manageListQuestion}` optional route parameters are added to the get and post routes for questions
|
|
81
81
|
|
|
82
|
+
* manageListAction should be either `add`, `edit` or `remove` to indicate the action being taken in the manage list. These can be imported from `manage-list-actions.js`.
|
|
83
|
+
* manageListItemId should be a unique identifier for the item being edited or removed, for example an id. When adding an item, an ID is generated.
|
|
84
|
+
* manageListQuestion should be the fieldName of the question being added, edited or removed, this is needed to know which question in the manage list journey to show when adding or editing an item.
|
|
85
|
+
|
|
82
86
|
```js
|
|
83
87
|
router.get('/:section/:question{/:manageListAction/:manageListItemId/:manageListQuestion}',
|
|
84
88
|
getJourneyResponse,
|
|
@@ -114,3 +118,4 @@ new Section('New Holiday', 'holiday')
|
|
|
114
118
|
.addQuestion(questions.departureDate)
|
|
115
119
|
.addQuestion(questions.holidayPeriod)
|
|
116
120
|
```
|
|
121
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{% extends layoutTemplate %}
|
|
2
|
+
|
|
3
|
+
{% from "govuk/components/button/macro.njk" import govukButton %}
|
|
4
|
+
|
|
5
|
+
{% block dynQuestionContent %}
|
|
6
|
+
<div class="govuk-grid-row">
|
|
7
|
+
<div class="govuk-grid-column-two-thirds">
|
|
8
|
+
<form action="" method="post" novalidate>
|
|
9
|
+
{% if _csrf %}<input type="hidden" name="_csrf" value="{{ _csrf }}">{% endif %}
|
|
10
|
+
<h1 class="govuk-heading-l">Are you sure you want to remove the following {{ titleSingular }}?</h1>
|
|
11
|
+
{% for item in questionSummary %}
|
|
12
|
+
<p class="govuk-body">{{ item.answer | safe }}</p>
|
|
13
|
+
{% endfor %}
|
|
14
|
+
{{ govukButton({
|
|
15
|
+
text: continueButtonText,
|
|
16
|
+
type: "submit",
|
|
17
|
+
attributes: { "data-cy":"button-save-and-continue"}
|
|
18
|
+
}) }}
|
|
19
|
+
</form>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
{% endblock %}
|
|
@@ -31,12 +31,12 @@
|
|
|
31
31
|
<dd class="govuk-summary-list__actions">
|
|
32
32
|
<ul class="govuk-summary-list__actions-list">
|
|
33
33
|
<li class="govuk-summary-list__actions-list-item">
|
|
34
|
-
<a class="govuk-link" href="{{ util.trimTrailingSlash(originalUrl) }}/edit/{{ item.id }}/{{ question.firstQuestionUrl }}">
|
|
34
|
+
<a class="govuk-link" href="{{ util.trimTrailingSlash(originalUrl) }}/{{ actionParams.edit }}/{{ item.id }}/{{ question.firstQuestionUrl }}">
|
|
35
35
|
Change <span class="govuk-visually-hidden">{{ titleSingular }} {{ loop.index }}</span>
|
|
36
36
|
</a>
|
|
37
37
|
</li>
|
|
38
38
|
<li class="govuk-summary-list__actions-list-item">
|
|
39
|
-
<a class="govuk-link" href="{{ util.trimTrailingSlash(originalUrl) }}/remove/{{ item.id }}/
|
|
39
|
+
<a class="govuk-link" href="{{ util.trimTrailingSlash(originalUrl) }}/{{ actionParams.remove }}/{{ item.id }}/{{ actionParams.confirmRemove }}">
|
|
40
40
|
Remove <span class="govuk-visually-hidden">{{ titleSingular }} {{ loop.index }}</span>
|
|
41
41
|
</a>
|
|
42
42
|
</li>
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { Question } from '#question';
|
|
2
2
|
import { Uuid } from '#src/lib/uuid.js';
|
|
3
3
|
import nunjucks from 'nunjucks';
|
|
4
|
+
import { MANAGE_LIST_ACTIONS } from './manage-list-actions.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* @typedef {Object} ManageListQuestionParameters
|
|
7
8
|
* @property {string} titleSingular - the single name of the list item, e.g. "Holiday activity"
|
|
8
9
|
* @property {boolean} [showManageListQuestions] - whether to show the question titles as well as answers on the manage list summary page
|
|
9
10
|
* @property {boolean} [showAnswersInSummary] - whether to show the answers on the main check-your-answers page (or just a count)
|
|
11
|
+
* @property {string} [confirmationQuestion] - the name of the confirmation question to use when removing an item, default 'confirm'
|
|
10
12
|
*/
|
|
11
13
|
|
|
12
14
|
export default class ManageListQuestion extends Question {
|
|
@@ -14,6 +16,7 @@ export default class ManageListQuestion extends Question {
|
|
|
14
16
|
#section;
|
|
15
17
|
/** @type {boolean} */
|
|
16
18
|
#showAnswersInSummary;
|
|
19
|
+
#confirmationQuestionParam;
|
|
17
20
|
|
|
18
21
|
/**
|
|
19
22
|
* @param {import('#question-types').QuestionParameters & ManageListQuestionParameters} params
|
|
@@ -30,6 +33,7 @@ export default class ManageListQuestion extends Question {
|
|
|
30
33
|
}
|
|
31
34
|
});
|
|
32
35
|
this.#showAnswersInSummary = params.showAnswersInSummary || false;
|
|
36
|
+
this.#confirmationQuestionParam = params.confirmationQuestion || 'confirm';
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
/**
|
|
@@ -44,6 +48,10 @@ export default class ManageListQuestion extends Question {
|
|
|
44
48
|
return true;
|
|
45
49
|
}
|
|
46
50
|
|
|
51
|
+
get confirmationQuestionParam() {
|
|
52
|
+
return this.#confirmationQuestionParam;
|
|
53
|
+
}
|
|
54
|
+
|
|
47
55
|
/**
|
|
48
56
|
* @param {import('#question').QuestionViewModel} viewModel
|
|
49
57
|
*/
|
|
@@ -51,6 +59,11 @@ export default class ManageListQuestion extends Question {
|
|
|
51
59
|
viewModel.question.addAnotherLink = this.#addAnotherLink;
|
|
52
60
|
viewModel.question.firstQuestionUrl = this.#firstQuestionUrl;
|
|
53
61
|
viewModel.question.valueSummary = [];
|
|
62
|
+
viewModel.actionParams = {
|
|
63
|
+
edit: MANAGE_LIST_ACTIONS.EDIT,
|
|
64
|
+
remove: MANAGE_LIST_ACTIONS.REMOVE,
|
|
65
|
+
confirmRemove: this.#confirmationQuestionParam
|
|
66
|
+
};
|
|
54
67
|
if (viewModel.question.value && Array.isArray(viewModel.question.value)) {
|
|
55
68
|
viewModel.question.valueSummary = viewModel.question.value.map((v) => {
|
|
56
69
|
return {
|
|
@@ -126,6 +139,16 @@ export default class ManageListQuestion extends Question {
|
|
|
126
139
|
];
|
|
127
140
|
}
|
|
128
141
|
|
|
142
|
+
renderConfirmationAction(res, itemToRemove, viewModel) {
|
|
143
|
+
viewModel.questionSummary = this.#formatItemAnswers(itemToRemove);
|
|
144
|
+
let view = `components/${this.viewFolder}/${this.confirmationQuestionParam}`;
|
|
145
|
+
if (this.viewFolder.includes('/')) {
|
|
146
|
+
// custom view folder
|
|
147
|
+
view = `${this.viewFolder}/${this.confirmationQuestionParam}`;
|
|
148
|
+
}
|
|
149
|
+
res.render(view, viewModel);
|
|
150
|
+
}
|
|
151
|
+
|
|
129
152
|
/**
|
|
130
153
|
* Create an 'add another' link,
|
|
131
154
|
* to the first question in the manage list section
|
|
@@ -124,6 +124,21 @@ describe('components/manage-list/question', () => {
|
|
|
124
124
|
assertSnapshot(ctx, res.render.mock.calls[0].result, 'manage-list-render-questions.html');
|
|
125
125
|
});
|
|
126
126
|
|
|
127
|
+
it('should render the confirmation view model', (ctx) => {
|
|
128
|
+
const { q, journey } = questionWithManageQuestions(ctx, {
|
|
129
|
+
showManageListQuestions: true
|
|
130
|
+
});
|
|
131
|
+
const itemToRemove = { id: 'id-1', name: 'Test' };
|
|
132
|
+
const viewModel = q.prepQuestionForRendering({}, journey);
|
|
133
|
+
const nunjucks = configureNunjucksTestEnv();
|
|
134
|
+
const res = {
|
|
135
|
+
render: mock.fn((view, data) => nunjucks.render(view + '.njk', data))
|
|
136
|
+
};
|
|
137
|
+
q.renderConfirmationAction(res, itemToRemove, viewModel);
|
|
138
|
+
assert.strictEqual(res.render.mock.callCount(), 1);
|
|
139
|
+
assertSnapshot(ctx, res.render.mock.calls[0].result, 'manage-list-confirmation-render.html');
|
|
140
|
+
});
|
|
141
|
+
|
|
127
142
|
describe('getDataToSave', () => {
|
|
128
143
|
it('should return existing journey answers if present', async () => {
|
|
129
144
|
const q = newQuestion();
|
package/src/controller.js
CHANGED
|
@@ -2,6 +2,7 @@ import { SECTION_STATUS } from './section.js';
|
|
|
2
2
|
import questionUtils from './components/utils/question-utils.js';
|
|
3
3
|
import { answerObjectForManageListSaving } from '#src/components/manage-list/utils.js';
|
|
4
4
|
import { booleanToYesNoValue } from '#src/components/boolean/question.js';
|
|
5
|
+
import { MANAGE_LIST_ACTIONS } from '#src/components/manage-list/manage-list-actions.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* @typedef {import('./journey/journey.js').Journey} Journey
|
|
@@ -177,6 +178,19 @@ export async function question(req, res) {
|
|
|
177
178
|
originalUrl: req.originalUrl
|
|
178
179
|
}
|
|
179
180
|
});
|
|
181
|
+
if (
|
|
182
|
+
question.isManageListQuestion &&
|
|
183
|
+
typeof question.renderConfirmationAction === 'function' &&
|
|
184
|
+
req.params.manageListAction === MANAGE_LIST_ACTIONS.REMOVE
|
|
185
|
+
) {
|
|
186
|
+
const answers = journey.response.answers;
|
|
187
|
+
if (answers && Object.hasOwn(answers, question.fieldName) && Array.isArray(answers[question.fieldName])) {
|
|
188
|
+
const item = answers[question.fieldName].find((i) => i.id === req.params.manageListItemId);
|
|
189
|
+
if (item) return question.renderConfirmationAction(res, item, viewModel);
|
|
190
|
+
}
|
|
191
|
+
// if we can't find the item to remove, redirect to task list rather than erroring out as the session may have expired or been tampered with
|
|
192
|
+
return res.redirect(journey.taskListUrl);
|
|
193
|
+
}
|
|
180
194
|
return question.renderAction(res, viewModel);
|
|
181
195
|
}
|
|
182
196
|
|
|
@@ -188,6 +202,7 @@ export async function question(req, res) {
|
|
|
188
202
|
* @property {string} referenceId
|
|
189
203
|
* @property {boolean} isManageListItem
|
|
190
204
|
* @property {string} [manageListQuestionFieldName]
|
|
205
|
+
* @property {boolean} [manageListItemRemove]
|
|
191
206
|
* @property {Object<string, any>} data
|
|
192
207
|
*/
|
|
193
208
|
|
|
@@ -215,7 +230,7 @@ export function buildSave(saveData, redirectToTaskListOnSuccess) {
|
|
|
215
230
|
}
|
|
216
231
|
|
|
217
232
|
let manageListQuestion;
|
|
218
|
-
if (question.isInManageListSection) {
|
|
233
|
+
if (question.isInManageListSection || req.params.manageListAction === MANAGE_LIST_ACTIONS.REMOVE) {
|
|
219
234
|
// find parent question for the manage list
|
|
220
235
|
manageListQuestion = journey.getQuestionByParams({ section: req.params.section, question: req.params.question });
|
|
221
236
|
if (!manageListQuestion) {
|
|
@@ -240,6 +255,7 @@ export function buildSave(saveData, redirectToTaskListOnSuccess) {
|
|
|
240
255
|
referenceId: journeyResponse.referenceId,
|
|
241
256
|
isManageListItem: question.isInManageListSection,
|
|
242
257
|
manageListQuestionFieldName: manageListQuestion?.fieldName,
|
|
258
|
+
manageListItemRemove: req.params?.manageListAction === MANAGE_LIST_ACTIONS.REMOVE,
|
|
243
259
|
data
|
|
244
260
|
});
|
|
245
261
|
|
package/src/controller.test.js
CHANGED
|
@@ -7,6 +7,7 @@ import { SECTION_STATUS } from './section.js';
|
|
|
7
7
|
|
|
8
8
|
import { mockReq, mockRes } from '#test/utils/utils.js';
|
|
9
9
|
import { BOOLEAN_OPTIONS } from '#src/components/boolean/question.js';
|
|
10
|
+
import { MANAGE_LIST_ACTIONS } from '#src/components/manage-list/manage-list-actions.js';
|
|
10
11
|
|
|
11
12
|
const res = mockRes();
|
|
12
13
|
const mockBaseUrl = '/manage-appeals/questionnaire';
|
|
@@ -351,6 +352,129 @@ describe('dynamic-form/controller', () => {
|
|
|
351
352
|
assert.ok(args?.customViewData?.originalUrl);
|
|
352
353
|
assert.strictEqual(args?.customViewData?.originalUrl, req.originalUrl);
|
|
353
354
|
});
|
|
355
|
+
|
|
356
|
+
it('should redirect to tasklist when manage list action is remove and journey response has no answers', async () => {
|
|
357
|
+
res.redirect.mock.resetCalls();
|
|
358
|
+
|
|
359
|
+
sampleQuestionObj.isManageListQuestion = true;
|
|
360
|
+
sampleQuestionObj.renderConfirmationAction = mock.fn();
|
|
361
|
+
|
|
362
|
+
req.params = {
|
|
363
|
+
referenceId: mockRef,
|
|
364
|
+
section: mockSection.name,
|
|
365
|
+
question: sampleQuestionObj.fieldName,
|
|
366
|
+
manageListAction: MANAGE_LIST_ACTIONS.REMOVE
|
|
367
|
+
};
|
|
368
|
+
const mockAnswer = undefined;
|
|
369
|
+
const mockBackLink = 'back';
|
|
370
|
+
const mockQuestionRendering = 'test';
|
|
371
|
+
|
|
372
|
+
sampleQuestionObj.toViewModel.mock.mockImplementationOnce(() => mockQuestionRendering);
|
|
373
|
+
mockJourney.getQuestionByParams = mock.fn();
|
|
374
|
+
mockJourney.getQuestionByParams.mock.mockImplementation(() => sampleQuestionObj);
|
|
375
|
+
mockJourney.response.answers = mockAnswer;
|
|
376
|
+
mockJourney.getNextQuestionUrl = mock.fn(() => mockBackLink);
|
|
377
|
+
mockJourney.getSection = mock.fn(() => mockSection);
|
|
378
|
+
|
|
379
|
+
await question(req, res);
|
|
380
|
+
|
|
381
|
+
assert.strictEqual(res.redirect.mock.callCount(), 1);
|
|
382
|
+
assert.deepStrictEqual(res.redirect.mock.calls[0].arguments, [mockJourney.taskListUrl]);
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it('should redirect to tasklist when manage list action is remove and the manage list answer is undefined', async () => {
|
|
386
|
+
res.redirect.mock.resetCalls();
|
|
387
|
+
|
|
388
|
+
sampleQuestionObj.isManageListQuestion = true;
|
|
389
|
+
sampleQuestionObj.renderConfirmationAction = mock.fn();
|
|
390
|
+
|
|
391
|
+
req.params = {
|
|
392
|
+
referenceId: mockRef,
|
|
393
|
+
section: mockSection.name,
|
|
394
|
+
question: sampleQuestionObj.fieldName,
|
|
395
|
+
manageListAction: MANAGE_LIST_ACTIONS.REMOVE
|
|
396
|
+
};
|
|
397
|
+
const mockAnswer = {
|
|
398
|
+
wrongFieldName: 'Some value'
|
|
399
|
+
};
|
|
400
|
+
const mockBackLink = 'back';
|
|
401
|
+
const mockQuestionRendering = 'test';
|
|
402
|
+
|
|
403
|
+
sampleQuestionObj.toViewModel.mock.mockImplementationOnce(() => mockQuestionRendering);
|
|
404
|
+
mockJourney.getQuestionByParams = mock.fn();
|
|
405
|
+
mockJourney.getQuestionByParams.mock.mockImplementation(() => sampleQuestionObj);
|
|
406
|
+
mockJourney.response.answers = mockAnswer;
|
|
407
|
+
mockJourney.getNextQuestionUrl = mock.fn(() => mockBackLink);
|
|
408
|
+
mockJourney.getSection = mock.fn(() => mockSection);
|
|
409
|
+
|
|
410
|
+
await question(req, res);
|
|
411
|
+
|
|
412
|
+
assert.strictEqual(res.redirect.mock.callCount(), 1);
|
|
413
|
+
assert.deepStrictEqual(res.redirect.mock.calls[0].arguments, [mockJourney.taskListUrl]);
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
it('should redirect to tasklist when manage list action is remove and the manage list answer is not an array', async () => {
|
|
417
|
+
res.redirect.mock.resetCalls();
|
|
418
|
+
|
|
419
|
+
sampleQuestionObj.isManageListQuestion = true;
|
|
420
|
+
sampleQuestionObj.renderConfirmationAction = mock.fn();
|
|
421
|
+
|
|
422
|
+
req.params = {
|
|
423
|
+
referenceId: mockRef,
|
|
424
|
+
section: mockSection.name,
|
|
425
|
+
question: sampleQuestionObj.fieldName,
|
|
426
|
+
manageListAction: MANAGE_LIST_ACTIONS.REMOVE
|
|
427
|
+
};
|
|
428
|
+
const mockAnswer = {
|
|
429
|
+
sampleFieldName: 'not-an-array'
|
|
430
|
+
};
|
|
431
|
+
const mockBackLink = 'back';
|
|
432
|
+
const mockQuestionRendering = 'test';
|
|
433
|
+
|
|
434
|
+
sampleQuestionObj.toViewModel.mock.mockImplementationOnce(() => mockQuestionRendering);
|
|
435
|
+
mockJourney.getQuestionByParams = mock.fn();
|
|
436
|
+
mockJourney.getQuestionByParams.mock.mockImplementation(() => sampleQuestionObj);
|
|
437
|
+
mockJourney.response.answers = mockAnswer;
|
|
438
|
+
mockJourney.getNextQuestionUrl = mock.fn(() => mockBackLink);
|
|
439
|
+
mockJourney.getSection = mock.fn(() => mockSection);
|
|
440
|
+
|
|
441
|
+
await question(req, res);
|
|
442
|
+
|
|
443
|
+
assert.strictEqual(res.redirect.mock.callCount(), 1);
|
|
444
|
+
assert.deepStrictEqual(res.redirect.mock.calls[0].arguments, [mockJourney.taskListUrl]);
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
it('should render the confirmation page when manage list action is remove and the question has a renderConfirmationAction function', async () => {
|
|
448
|
+
res.redirect.mock.resetCalls();
|
|
449
|
+
|
|
450
|
+
sampleQuestionObj.isManageListQuestion = true;
|
|
451
|
+
|
|
452
|
+
req.params = {
|
|
453
|
+
referenceId: mockRef,
|
|
454
|
+
section: mockSection.name,
|
|
455
|
+
question: sampleQuestionObj.fieldName,
|
|
456
|
+
manageListAction: MANAGE_LIST_ACTIONS.REMOVE,
|
|
457
|
+
manageListItemId: 'item-id-1'
|
|
458
|
+
};
|
|
459
|
+
const mockAnswer = {
|
|
460
|
+
sampleFieldName: [{ id: 'item-id-1', name: 'Item 1' }]
|
|
461
|
+
};
|
|
462
|
+
const mockBackLink = 'back';
|
|
463
|
+
const mockQuestionRendering = 'test';
|
|
464
|
+
|
|
465
|
+
sampleQuestionObj.toViewModel.mock.mockImplementationOnce(() => mockQuestionRendering);
|
|
466
|
+
sampleQuestionObj.renderConfirmationAction = mock.fn();
|
|
467
|
+
mockJourney.getQuestionByParams = mock.fn();
|
|
468
|
+
mockJourney.getQuestionByParams.mock.mockImplementation(() => sampleQuestionObj);
|
|
469
|
+
mockJourney.response.answers = mockAnswer;
|
|
470
|
+
mockJourney.getNextQuestionUrl = mock.fn(() => mockBackLink);
|
|
471
|
+
mockJourney.getSection = mock.fn(() => mockSection);
|
|
472
|
+
|
|
473
|
+
await question(req, res);
|
|
474
|
+
|
|
475
|
+
assert.strictEqual(res.redirect.mock.callCount(), 0);
|
|
476
|
+
assert.strictEqual(sampleQuestionObj.renderConfirmationAction.mock.callCount(), 1);
|
|
477
|
+
});
|
|
354
478
|
});
|
|
355
479
|
|
|
356
480
|
describe('save', () => {
|
|
@@ -389,6 +513,7 @@ describe('dynamic-form/controller', () => {
|
|
|
389
513
|
journeyId: journeyParams.journeyId,
|
|
390
514
|
referenceId: journeyParams.referenceId,
|
|
391
515
|
isManageListItem: undefined,
|
|
516
|
+
manageListItemRemove: false,
|
|
392
517
|
manageListQuestionFieldName: undefined,
|
|
393
518
|
data: undefined
|
|
394
519
|
}
|
|
@@ -449,6 +574,7 @@ describe('dynamic-form/controller', () => {
|
|
|
449
574
|
referenceId: journeyParams.referenceId,
|
|
450
575
|
data: undefined,
|
|
451
576
|
isManageListItem: undefined,
|
|
577
|
+
manageListItemRemove: false,
|
|
452
578
|
manageListQuestionFieldName: undefined
|
|
453
579
|
}
|
|
454
580
|
],
|
|
@@ -633,6 +759,14 @@ describe('dynamic-form/controller', () => {
|
|
|
633
759
|
const params = journey.redirectToNextQuestion.mock.calls[0].arguments[1];
|
|
634
760
|
assert.deepStrictEqual(params, req.params);
|
|
635
761
|
});
|
|
762
|
+
it('should set manageListItemRemove is true when manageListAction is remove', async () => {
|
|
763
|
+
const { journey, req, res } = setupJourney();
|
|
764
|
+
req.params.manageListAction = MANAGE_LIST_ACTIONS.REMOVE;
|
|
765
|
+
const saveData = mock.fn();
|
|
766
|
+
await buildSave(saveData)(req, res, 'journey-1');
|
|
767
|
+
assert.strictEqual(journey.redirectToNextQuestion.mock.callCount(), 1);
|
|
768
|
+
assert.strictEqual(saveData.mock.calls[0].arguments[0].manageListItemRemove, true);
|
|
769
|
+
});
|
|
636
770
|
});
|
|
637
771
|
});
|
|
638
772
|
});
|
package/src/journey/journey.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* instances of this class *
|
|
5
5
|
***********************************************************/
|
|
6
6
|
import { END_OF_SECTION } from '#src/section.js';
|
|
7
|
+
import { MANAGE_LIST_ACTIONS } from '#src/components/manage-list/manage-list-actions.js';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* @typedef {import('./journey-response').JourneyResponse} JourneyResponse
|
|
@@ -174,6 +175,13 @@ export class Journey {
|
|
|
174
175
|
return undefined;
|
|
175
176
|
}
|
|
176
177
|
if (manageListParams && question.isManageListQuestion) {
|
|
178
|
+
if (
|
|
179
|
+
manageListParams.question === question.confirmationQuestionParam &&
|
|
180
|
+
manageListParams.action === MANAGE_LIST_ACTIONS.REMOVE
|
|
181
|
+
) {
|
|
182
|
+
// special case for the delete confirmation page
|
|
183
|
+
return question;
|
|
184
|
+
}
|
|
177
185
|
return question.section.questions.find((q) => matchQuestion(q, manageListParams.question));
|
|
178
186
|
}
|
|
179
187
|
return question;
|
|
@@ -247,6 +255,17 @@ export class Journey {
|
|
|
247
255
|
const numberOfSections = this.sections.length;
|
|
248
256
|
const sectionsStart = reverse ? numberOfSections - 1 : 0;
|
|
249
257
|
const questionFieldName = manageListQuestion ? params.manageListQuestion : params.question;
|
|
258
|
+
if (
|
|
259
|
+
params.manageListAction === MANAGE_LIST_ACTIONS.REMOVE &&
|
|
260
|
+
Object.hasOwn(params, 'manageListItemId') &&
|
|
261
|
+
Object.hasOwn(params, 'manageListQuestion')
|
|
262
|
+
) {
|
|
263
|
+
// If you have just confirmed removal of an item, go back to the parent manage list question
|
|
264
|
+
return this.#buildQuestionUrl({
|
|
265
|
+
section: params.section,
|
|
266
|
+
question: params.question
|
|
267
|
+
});
|
|
268
|
+
}
|
|
250
269
|
|
|
251
270
|
let currentSectionIndex;
|
|
252
271
|
let foundSection = false;
|
|
@@ -2,6 +2,7 @@ import { describe, it, mock, beforeEach } from 'node:test';
|
|
|
2
2
|
import assert from 'node:assert';
|
|
3
3
|
import { Journey } from './journey.js';
|
|
4
4
|
import { Section } from '#src/section.js';
|
|
5
|
+
import { MANAGE_LIST_ACTIONS } from '#src/components/manage-list/manage-list-actions.js';
|
|
5
6
|
|
|
6
7
|
const mockSectionDetails = [
|
|
7
8
|
{
|
|
@@ -229,6 +230,19 @@ describe('Journey class', () => {
|
|
|
229
230
|
|
|
230
231
|
assert.strictEqual(question, undefined);
|
|
231
232
|
});
|
|
233
|
+
|
|
234
|
+
it('should return the parent question if the question is a remove manage list item confirmation', () => {
|
|
235
|
+
const journey = new Journey(constructorArgs);
|
|
236
|
+
journey.sections = mockSections;
|
|
237
|
+
const question = journey.getQuestionByParams({
|
|
238
|
+
section: mockSections[0].segment,
|
|
239
|
+
question: mockSections[0].questions[0].fieldName,
|
|
240
|
+
manageListAction: MANAGE_LIST_ACTIONS.REMOVE,
|
|
241
|
+
manageListQuestion: 'confirm',
|
|
242
|
+
manageListItemId: '123'
|
|
243
|
+
});
|
|
244
|
+
assert.strictEqual(question, mockSections[0].questions[0]);
|
|
245
|
+
});
|
|
232
246
|
});
|
|
233
247
|
|
|
234
248
|
describe('getBackLink', () => {
|
|
@@ -291,6 +305,25 @@ describe('Journey class', () => {
|
|
|
291
305
|
});
|
|
292
306
|
|
|
293
307
|
describe('getNextQuestionUrl', () => {
|
|
308
|
+
it('should return to parent manageList question if removing an answer', () => {
|
|
309
|
+
const section = mockSections[2];
|
|
310
|
+
const name = section.questions[0].fieldName;
|
|
311
|
+
const journey = new Journey(constructorArgs);
|
|
312
|
+
journey.sections = mockSections;
|
|
313
|
+
journey.returnToListing = false;
|
|
314
|
+
const nextQuestionUrl = journey.getNextQuestionUrl({
|
|
315
|
+
section: section.segment,
|
|
316
|
+
question: name,
|
|
317
|
+
manageListAction: MANAGE_LIST_ACTIONS.REMOVE,
|
|
318
|
+
manageListQuestion: 'confirm',
|
|
319
|
+
manageListItemId: '123'
|
|
320
|
+
});
|
|
321
|
+
assert.strictEqual(
|
|
322
|
+
nextQuestionUrl,
|
|
323
|
+
`${constructorArgs.makeBaseUrl()}/${section.segment}/${section.questions[0].fieldName}`
|
|
324
|
+
);
|
|
325
|
+
});
|
|
326
|
+
|
|
294
327
|
for (const returnToListing of [true, false]) {
|
|
295
328
|
it(`should return null if section is not found [${returnToListing}]`, () => {
|
|
296
329
|
const section = 'section3'; // Non-existent section
|
|
@@ -33,6 +33,10 @@ import { booleanToYesNoValue } from '../components/boolean/question.js';
|
|
|
33
33
|
* }
|
|
34
34
|
* }
|
|
35
35
|
*
|
|
36
|
+
* Delete behavior for manage-list items:
|
|
37
|
+
* - When `manageListItemRemove === true`, and both `manageListQuestionFieldName` and `req.params.manageListItemId`
|
|
38
|
+
* are present, the item with a matching `id` is removed from the array stored at `answers[manageListQuestionFieldName]`.
|
|
39
|
+
* - If the list does not exist or is not an array, no action is taken.
|
|
36
40
|
*
|
|
37
41
|
* @param {Object} opts
|
|
38
42
|
* @param {string} [opts.reqParam]
|
|
@@ -40,7 +44,14 @@ import { booleanToYesNoValue } from '../components/boolean/question.js';
|
|
|
40
44
|
* @returns {import('../controller').SaveDataFn}
|
|
41
45
|
*/
|
|
42
46
|
export function buildSaveDataToSession({ reqParam } = {}) {
|
|
43
|
-
return async ({
|
|
47
|
+
return async ({
|
|
48
|
+
req,
|
|
49
|
+
journeyId,
|
|
50
|
+
data,
|
|
51
|
+
isManageListItem,
|
|
52
|
+
manageListQuestionFieldName,
|
|
53
|
+
manageListItemRemove = false
|
|
54
|
+
}) => {
|
|
44
55
|
if (!req.session) {
|
|
45
56
|
throw new Error('request session required');
|
|
46
57
|
}
|
|
@@ -52,14 +63,25 @@ export function buildSaveDataToSession({ reqParam } = {}) {
|
|
|
52
63
|
forms = forms[reqParamValue] || (forms[reqParamValue] = {});
|
|
53
64
|
}
|
|
54
65
|
let answers = forms[journeyId] || (forms[journeyId] = {});
|
|
66
|
+
|
|
55
67
|
if (isManageListItem) {
|
|
56
68
|
const answersList = answers[manageListQuestionFieldName] || (answers[manageListQuestionFieldName] = []);
|
|
57
69
|
answers = answersList.find((item) => item.id === req.params.manageListItemId);
|
|
58
|
-
|
|
59
70
|
if (!answers) {
|
|
60
71
|
answers = { id: req.params.manageListItemId };
|
|
61
72
|
answersList.push(answers);
|
|
62
73
|
}
|
|
74
|
+
} else if (manageListItemRemove && manageListQuestionFieldName && req.params.manageListItemId) {
|
|
75
|
+
const answersList = answers[manageListQuestionFieldName];
|
|
76
|
+
|
|
77
|
+
if (!answersList || !Array.isArray(answersList)) return; // nothing to remove
|
|
78
|
+
|
|
79
|
+
const index = answersList.findIndex((item) => item.id === req.params.manageListItemId);
|
|
80
|
+
if (index > -1) {
|
|
81
|
+
answersList.splice(index, 1);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
return; // item not found, nothing to remove
|
|
63
85
|
}
|
|
64
86
|
for (const [k, v] of Object.entries(data?.answers || {})) {
|
|
65
87
|
answers[k] = v;
|
|
@@ -123,6 +123,34 @@ describe('session-answer-store', () => {
|
|
|
123
123
|
});
|
|
124
124
|
});
|
|
125
125
|
|
|
126
|
+
it('should remove a managed list item using the id when present in the session', async () => {
|
|
127
|
+
const req = mockReq();
|
|
128
|
+
req.params = { manageListItemId: '56789' };
|
|
129
|
+
req.session = {
|
|
130
|
+
forms: {
|
|
131
|
+
'j-1': {
|
|
132
|
+
myManagedList: [
|
|
133
|
+
{ id: '12345', q1: 'keep value' },
|
|
134
|
+
{ id: '56789', q1: 'remove value' }
|
|
135
|
+
]
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
const data = {};
|
|
140
|
+
const journeyId = 'j-1';
|
|
141
|
+
const manageListQuestionFieldName = 'myManagedList';
|
|
142
|
+
const manageListItemRemove = true;
|
|
143
|
+
const saveDataToSession = buildSaveDataToSession();
|
|
144
|
+
await saveDataToSession({ req, journeyId, data, manageListQuestionFieldName, manageListItemRemove });
|
|
145
|
+
assert.deepStrictEqual(req.session, {
|
|
146
|
+
forms: {
|
|
147
|
+
'j-1': {
|
|
148
|
+
myManagedList: [{ id: '12345', q1: 'keep value' }]
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
126
154
|
it('should overwrite existing data by reqParam & journeyId', async () => {
|
|
127
155
|
const req = mockReq();
|
|
128
156
|
req.params = { myParam: 'req-1' };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<div class="govuk-grid-row">
|
|
2
|
+
<div class="govuk-grid-column-two-thirds">
|
|
3
|
+
<form action="" method="post" novalidate>
|
|
4
|
+
<h1 class="govuk-heading-l">Are you sure you want to remove the following Thing?</h1>
|
|
5
|
+
<p class="govuk-body">mock answer</p>
|
|
6
|
+
<p class="govuk-body">mock answer 2</p>
|
|
7
|
+
<button type="submit" class="govuk-button" data-module="govuk-button" data-cy="button-save-and-continue">
|
|
8
|
+
Continue
|
|
9
|
+
</button>
|
|
10
|
+
</form>
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
@@ -7,12 +7,17 @@ export default class DatePeriodQuestion extends Question {
|
|
|
7
7
|
* @param {import('#question-types').QuestionParameters} params
|
|
8
8
|
* @param {string} [params.dateFormat]
|
|
9
9
|
* @param {{start: string, end: string}} [params.labels]
|
|
10
|
+
* @param {string} [params.hint]
|
|
11
|
+
* @param {string} [params.hintStart]
|
|
12
|
+
* @param {string} [params.hintEnd]
|
|
10
13
|
* @param {{hour: number, minute: number, second: number}} [params.startTime]
|
|
11
14
|
* @param {{hour: number, minute: number, second: number}} [params.endTime]
|
|
12
15
|
*/
|
|
13
|
-
constructor({ dateFormat, labels, startTime, endTime, ...params }: import("#question-types").QuestionParameters);
|
|
16
|
+
constructor({ dateFormat, hint, labels, hintStart, startTime, hintEnd, endTime, ...params }: import("#question-types").QuestionParameters);
|
|
14
17
|
dateFormat: any;
|
|
15
18
|
labels: any;
|
|
19
|
+
hintStart: any;
|
|
20
|
+
hintEnd: any;
|
|
16
21
|
startTime: any;
|
|
17
22
|
endTime: any;
|
|
18
23
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"question.d.ts","sourceRoot":"","sources":["../../../../src/components/date-period/question.js"],"names":[],"mappings":"AAOA;;;GAGG;AACH;IACC
|
|
1
|
+
{"version":3,"file":"question.d.ts","sourceRoot":"","sources":["../../../../src/components/date-period/question.js"],"names":[],"mappings":"AAOA;;;GAGG;AACH;IACC;;;;;;;;;OASG;IACH,6FATW,OAAO,iBAAiB,EAAE,kBAAkB,EAqBtD;IAPA,gBAA4B;IAC5B,YAAsD;IAEtD,eAA0B;IAC1B,aAAsB;IACtB,eAA+D;IAC/D,aAA2D;IAG5D;;;;;OAKG,CACH,yCAHW,OAAO,mBAAmB,EAAE,eAAe,GACzC,OAAO,CAAE,MAAM,CAAC,CAgC5B;IAED;;MAuCC;CAuCD;yBAvJwB,WAAW"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manage-list-actions.d.ts","sourceRoot":"","sources":["../../../../src/components/manage-list/manage-list-actions.js"],"names":[],"mappings":"AAAA;;;GAGG;AACH,kCAFU,QAAQ,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAC,CAAC,CAM5D"}
|
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
* @property {string} titleSingular - the single name of the list item, e.g. "Holiday activity"
|
|
4
4
|
* @property {boolean} [showManageListQuestions] - whether to show the question titles as well as answers on the manage list summary page
|
|
5
5
|
* @property {boolean} [showAnswersInSummary] - whether to show the answers on the main check-your-answers page (or just a count)
|
|
6
|
+
* @property {string} [confirmationQuestion] - the name of the confirmation question to use when removing an item, default 'confirm'
|
|
6
7
|
*/
|
|
7
8
|
export default class ManageListQuestion extends Question {
|
|
8
9
|
/**
|
|
9
10
|
* @param {import('#question-types').QuestionParameters & ManageListQuestionParameters} params
|
|
10
11
|
*/
|
|
11
12
|
constructor(params: import("#question-types").QuestionParameters & ManageListQuestionParameters);
|
|
13
|
+
get confirmationQuestionParam(): string;
|
|
12
14
|
getDataToSave(req: any, journeyResponse: any): Promise<{
|
|
13
15
|
answers: {
|
|
14
16
|
[x: string]: any;
|
|
@@ -19,6 +21,7 @@ export default class ManageListQuestion extends Question {
|
|
|
19
21
|
value: string;
|
|
20
22
|
action: import("../../controller.js").ActionView | import("../../controller.js").ActionView[] | undefined;
|
|
21
23
|
}[];
|
|
24
|
+
renderConfirmationAction(res: any, itemToRemove: any, viewModel: any): void;
|
|
22
25
|
set section(section: import("../../section.js").Section);
|
|
23
26
|
get section(): import("../../section.js").Section;
|
|
24
27
|
#private;
|
|
@@ -36,6 +39,10 @@ export type ManageListQuestionParameters = {
|
|
|
36
39
|
* - whether to show the answers on the main check-your-answers page (or just a count)
|
|
37
40
|
*/
|
|
38
41
|
showAnswersInSummary?: boolean | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* - the name of the confirmation question to use when removing an item, default 'confirm'
|
|
44
|
+
*/
|
|
45
|
+
confirmationQuestion?: string | undefined;
|
|
39
46
|
};
|
|
40
47
|
import { Question } from '#question';
|
|
41
48
|
//# sourceMappingURL=question.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"question.d.ts","sourceRoot":"","sources":["../../../../src/components/manage-list/question.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"question.d.ts","sourceRoot":"","sources":["../../../../src/components/manage-list/question.js"],"names":[],"mappings":"AAKA;;;;;;GAMG;AAEH;IAOC;;OAEG;IACH,oBAFW,OAAO,iBAAiB,EAAE,kBAAkB,GAAG,4BAA4B,EAerF;IAcD,wCAEC;IAyDD;;;;OAMC;IAED;;;;QAsBC;IAED,4EAQC;IAoCD,yDAEC;IATD,kDAKC;;CAKD;;;;;mBArLa,MAAM;;;;;;;;;;;;;;yBAPK,WAAW"}
|
|
@@ -24,6 +24,7 @@ export function question(req: any, res: any): Promise<any>;
|
|
|
24
24
|
* @property {string} referenceId
|
|
25
25
|
* @property {boolean} isManageListItem
|
|
26
26
|
* @property {string} [manageListQuestionFieldName]
|
|
27
|
+
* @property {boolean} [manageListItemRemove]
|
|
27
28
|
* @property {Object<string, any>} data
|
|
28
29
|
*/
|
|
29
30
|
/**
|
|
@@ -70,6 +71,7 @@ export type SaveParams = {
|
|
|
70
71
|
referenceId: string;
|
|
71
72
|
isManageListItem: boolean;
|
|
72
73
|
manageListQuestionFieldName?: string | undefined;
|
|
74
|
+
manageListItemRemove?: boolean | undefined;
|
|
73
75
|
data: {
|
|
74
76
|
[x: string]: any;
|
|
75
77
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../src/controller.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../src/controller.js"],"names":[],"mappings":"AAuEA;;;GAGG;AACH,qCAHW,MAAM,OAKhB;AAED;;;;;GAKG;AACH,sDAHW,MAAM,YACN,MAAM,gBA8DhB;AAED;;;;GAIG;AACH,2DA0CC;AAED;;;;;;;;;;GAUG;AAEH;;GAEG;AAEH;;;;GAIG;AACH,oCAJW,UAAU,gCACV,OAAO,OAuFjB;sBAtSY,OAAO,sBAAsB,EAAE,OAAO;uBACtC,OAAO,yBAAyB,EAAE,QAAQ;;;aAMzC,MAAM;YACN,MAAM;UAEjB;QAAiC,IAAI,EAA1B,KAAK,CAAE,OAAO,CAAC;KAC5B;;;SAIa;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE;WAChB;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE;;eAC1B,UAAU,EAAE;;;;UAKrB,MAAM;UACN,MAAM;;;;;;eA0KN,MAAM;iBACN,MAAM;sBACN,OAAO;;;;;;;yBAOR,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"journey.d.ts","sourceRoot":"","sources":["../../../src/journey/journey.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"journey.d.ts","sourceRoot":"","sources":["../../../src/journey/journey.js"],"names":[],"mappings":"AAQA;;;;GAIG;AAEH;;;GAGG;AACH;IAwBC;;;;;;;;;;;;;;OAcG;IACH,qLAZG;QAAwB,SAAS,EAAzB,MAAM;QACyC,WAAW,EAA1D,CAAC,QAAQ,EAAE,eAAe,KAAK,MAAM;QACpB,WAAW;QACH,QAAQ,EAAjC,eAAe;QACC,eAAe,EAA/B,MAAM;QACU,gBAAgB,EAAhC,MAAM;QACW,uBAAuB;QACxB,YAAY,EAA5B,MAAM;QACY,eAAe;QACd,QAAQ,EAA3B,OAAO,EAAE;QACQ,eAAe;KAC1C,EAoDA;IAzFD,8EAA8E;IAC9E,WADW,MAAM,CACP;IACV,qEAAqE;IACrE,UADW,KAAK,CAAE,OAAO,CAAC,CACZ;IACd,mFAAmF;IACnF,UADW,eAAe,CACjB;IACT,wFAAwF;IACxF,SADW,MAAM,CACJ;IACb,8HAA8H;IAC9H,aADW,CAAC,eAAe,EAAE,eAAe,KAAK,MAAM,CAChC;IACvB,kEAAkE;IAClE,aADW,MAAM,CACA;IACjB,uEAAuE;IACvE,iBADW,MAAM,CACI;IACrB,qFAAqF;IACrF,kBADW,MAAM,CACK;IACtB,4GAA4G;IAC5G,yBADW,MAAM,CACY;IAC7B,qFAAqF;IACrF,iBADW,OAAO,CACM;IACxB,2DAA2D;IAC3D,cADU,MAAM,CACH;IAmEZ,+BAA8C;IA2C/C;;;;OAIG;IACH,2BAHW,MAAM,GACJ,OAAO,GAAG,SAAS,CAM/B;IA8BD;;;;OAIG;IACH,4BAHW,OAAO,sBAAsB,EAAE,WAAW,GACxC,QAAQ,GAAG,SAAS,CAiBhC;IAED;;;;;;;OAOG;IACH,4CAJG;QAA4D,MAAM,EAA1D,OAAO,sBAAsB,EAAE,WAAW;QACkB,kBAAkB;KACtF,GAAU,MAAM,GAAC,IAAI,CAWvB;IAED;;;;;;;;OAQG;IACH,yCAJW,OAAO,sBAAsB,EAAE,WAAW,kFAExC,IAAI,CAKhB;IAED;;;;;;;;OAQG;IACH,2BANW,OAAO,sBAAsB,EAAE,WAAW,oCAElD;QAA0B,OAAO;QACmC,kBAAkB;KACtF,GAAU,MAAM,GAAC,IAAI,CAyEvB;IAED;;;;;OAKG;IACH,wBAAyB,gBAJd,MAI4B,EAAE,iBAH9B,MAG6C,KAF3C,MAAM,CAqBjB;IAEF;;;;OAIG;IACH,sCAAuC,iBAH5B,MAG2C,KAFzC,MAAM,CAIjB;IAEF;;;;;;OAMG;IACH,0BAA2B,gBALhB,MAK8B,EAAE,iBAJhC,MAI+C,EAAE,UAHjD,MAGyD,KAFvD,MAAM,CAuBjB;IAEF;;;OAGG;IACH,cAFa,OAAO,CAInB;IAED;;OAEG;IACH,6BAFW,eAAe,QAKzB;;CACD"}
|
|
@@ -28,6 +28,10 @@
|
|
|
28
28
|
* }
|
|
29
29
|
* }
|
|
30
30
|
*
|
|
31
|
+
* Delete behavior for manage-list items:
|
|
32
|
+
* - When `manageListItemRemove === true`, and both `manageListQuestionFieldName` and `req.params.manageListItemId`
|
|
33
|
+
* are present, the item with a matching `id` is removed from the array stored at `answers[manageListQuestionFieldName]`.
|
|
34
|
+
* - If the list does not exist or is not an array, no action is taken.
|
|
31
35
|
*
|
|
32
36
|
* @param {Object} opts
|
|
33
37
|
* @param {string} [opts.reqParam]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-answer-store.d.ts","sourceRoot":"","sources":["../../../src/lib/session-answer-store.js"],"names":[],"mappings":"AAKA
|
|
1
|
+
{"version":3,"file":"session-answer-store.d.ts","sourceRoot":"","sources":["../../../src/lib/session-answer-store.js"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,sDAJG;IAAsB,QAAQ;CAE9B,OA8CF;AASD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,gFANG;IAA0C,GAAG,EAArC,GAAyB;IACV,SAAS,EAAxB,MAAM;IACuB,WAAW;;;IACxB,QAAQ;CAChC,GAAU,IAAI,CAkBhB;AAED;;;;;;GAMG;AACH,8DAJW,MAAM,aACN,MAAM,OA4BhB;AA/ED;;;;GAIG;AACH,oCAA0D"}
|