@planning-inspectorate/dynamic-forms 3.1.0 → 3.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planning-inspectorate/dynamic-forms",
3
- "version": "3.1.0",
3
+ "version": "3.2.0",
4
4
  "description": "This package is for building GDS forms using a configuration-based approach.",
5
5
  "main": "./src/index.js",
6
6
  "type": "module",
@@ -86,6 +86,13 @@ export default class ManageListQuestion extends Question {
86
86
  });
87
87
  }
88
88
 
89
+ async getDataToSave(req, journeyResponse) {
90
+ let responseToSave = { answers: {} };
91
+ const data = journeyResponse.answers[this.fieldName];
92
+ responseToSave.answers[this.fieldName] = data || [];
93
+ return responseToSave;
94
+ }
95
+
89
96
  formatAnswerForSummary(sectionSegment, journey, answer) {
90
97
  let formattedAnswer = this.notStartedText;
91
98
  if (answer && Array.isArray(answer)) {
package/src/controller.js CHANGED
@@ -184,6 +184,8 @@ export async function question(req, res) {
184
184
  * @property {import('express').Response} res
185
185
  * @property {string} journeyId
186
186
  * @property {string} referenceId
187
+ * @property {boolean} isManageListItem
188
+ * @property {string} [manageListQuestionFieldName]
187
189
  * @property {Object<string, any>} data
188
190
  */
189
191
 
@@ -234,6 +236,8 @@ export function buildSave(saveData, redirectToTaskListOnSuccess) {
234
236
  res,
235
237
  journeyId: journeyResponse.journeyId,
236
238
  referenceId: journeyResponse.referenceId,
239
+ isManageListItem: question.isInManageListSection,
240
+ manageListQuestionFieldName: manageListQuestion?.fieldName,
237
241
  data
238
242
  });
239
243
 
@@ -386,6 +386,8 @@ describe('dynamic-form/controller', () => {
386
386
  res,
387
387
  journeyId: journeyParams.journeyId,
388
388
  referenceId: journeyParams.referenceId,
389
+ isManageListItem: undefined,
390
+ manageListQuestionFieldName: undefined,
389
391
  data: undefined
390
392
  }
391
393
  ]);
@@ -443,7 +445,9 @@ describe('dynamic-form/controller', () => {
443
445
  res,
444
446
  journeyId: journeyParams.journeyId,
445
447
  referenceId: journeyParams.referenceId,
446
- data: undefined
448
+ data: undefined,
449
+ isManageListItem: undefined,
450
+ manageListQuestionFieldName: undefined
447
451
  }
448
452
  ],
449
453
  'save data args'
@@ -35,11 +35,12 @@ import { booleanToYesNoValue } from '../components/boolean/question.js';
35
35
  *
36
36
  *
37
37
  * @param {Object} opts
38
- * @param {string} [opts.reqParam] - optional request parameter to use as a key
38
+ * @param {string} [opts.reqParam]
39
+ * - optional request parameter to use as a key
39
40
  * @returns {import('../controller').SaveDataFn}
40
41
  */
41
42
  export function buildSaveDataToSession({ reqParam } = {}) {
42
- return async ({ req, journeyId, data }) => {
43
+ return async ({ req, journeyId, data, isManageListItem, manageListQuestionFieldName }) => {
43
44
  if (!req.session) {
44
45
  throw new Error('request session required');
45
46
  }
@@ -50,8 +51,16 @@ export function buildSaveDataToSession({ reqParam } = {}) {
50
51
  // key by a further param
51
52
  forms = forms[reqParamValue] || (forms[reqParamValue] = {});
52
53
  }
53
- const answers = forms[journeyId] || (forms[journeyId] = {});
54
+ let answers = forms[journeyId] || (forms[journeyId] = {});
55
+ if (isManageListItem) {
56
+ const answersList = answers[manageListQuestionFieldName] || (answers[manageListQuestionFieldName] = []);
57
+ answers = answersList.find((item) => item.id === req.params.manageListItemId);
54
58
 
59
+ if (!answers) {
60
+ answers = { id: req.params.manageListItemId };
61
+ answersList.push(answers);
62
+ }
63
+ }
55
64
  for (const [k, v] of Object.entries(data?.answers || {})) {
56
65
  answers[k] = v;
57
66
  }
@@ -80,6 +80,48 @@ describe('session-answer-store', () => {
80
80
  }
81
81
  });
82
82
  });
83
+ it('should add a new managed list item when not present in the session', async () => {
84
+ const req = mockReq();
85
+ req.params = { manageListItemId: '12345' };
86
+ req.session = {};
87
+ const journeyId = 'j-1';
88
+ const data = { answers: { q1: 'a1' } };
89
+ const isManageListItem = true;
90
+ const manageListQuestionFieldName = 'myManagedList';
91
+ const saveDataToSession = buildSaveDataToSession();
92
+ await saveDataToSession({ req, journeyId, data, isManageListItem, manageListQuestionFieldName });
93
+ assert.deepStrictEqual(req.session, {
94
+ forms: {
95
+ 'j-1': {
96
+ myManagedList: [{ id: '12345', q1: 'a1' }]
97
+ }
98
+ }
99
+ });
100
+ });
101
+ it('should add to an existing managed list item using the id when present in the session', async () => {
102
+ const req = mockReq();
103
+ req.params = { manageListItemId: '12345' };
104
+ req.session = {
105
+ forms: {
106
+ 'j-1': {
107
+ myManagedList: [{ id: '12345', q1: 'old value' }]
108
+ }
109
+ }
110
+ };
111
+ const journeyId = 'j-1';
112
+ const data = { answers: { q2: 'a1' } };
113
+ const isManageListItem = true;
114
+ const manageListQuestionFieldName = 'myManagedList';
115
+ const saveDataToSession = buildSaveDataToSession();
116
+ await saveDataToSession({ req, journeyId, data, isManageListItem, manageListQuestionFieldName });
117
+ assert.deepStrictEqual(req.session, {
118
+ forms: {
119
+ 'j-1': {
120
+ myManagedList: [{ id: '12345', q1: 'old value', q2: 'a1' }]
121
+ }
122
+ }
123
+ });
124
+ });
83
125
 
84
126
  it('should overwrite existing data by reqParam & journeyId', async () => {
85
127
  const req = mockReq();
@@ -1,9 +1,10 @@
1
1
  import { describe, it } from 'node:test';
2
2
  import assert from 'assert';
3
- import { questionsInOrder } from './questions.js';
3
+ import { manageListQuestions, questionsInOrder } from './questions.js';
4
4
  import path from 'path';
5
5
  import { escapeForRegExp, snapshotsDir } from './utils/utils.js';
6
6
  import { createAppWithQuestions, mockAnswer, mockAnswerBody } from './questions.test.js';
7
+ import { COMPONENT_TYPES } from '#src/index.js';
7
8
 
8
9
  describe('check-your-answers', () => {
9
10
  it(`should render all question rows with no answers`, async (ctx) => {
@@ -39,6 +40,23 @@ describe('check-your-answers', () => {
39
40
 
40
41
  // 'answer' each question
41
42
  for (const q of questionsInOrder) {
43
+ // Save happens on subquestion for manage list questions
44
+ if (q.type === COMPONENT_TYPES.MANAGE_LIST) {
45
+ // Get the list of subquestions for this manage list question
46
+ const manageListQuestion = manageListQuestions.find(
47
+ (question) => question.manageListQuestion.fieldName === q.fieldName
48
+ );
49
+ const subQuestionAnswers = mockAnswerBody(q)[manageListQuestion.manageListQuestion.fieldName];
50
+ // Post to them individually
51
+ for (const answer of subQuestionAnswers) {
52
+ const id = answer.id;
53
+ for (const subQuestion of manageListQuestion.questions) {
54
+ const key = subQuestion.fieldName;
55
+ const body = { [key]: answer[key] };
56
+ await testServer.post(`/questions/${q.url}/add/${id}/${subQuestion.url}`, body, { redirect: 'manual' });
57
+ }
58
+ }
59
+ }
42
60
  await testServer.post(`/questions/${q.url}`, mockAnswerBody(q), { redirect: 'manual' });
43
61
  }
44
62
 
@@ -257,9 +257,9 @@ export function mockAnswerBody(q) {
257
257
  // manage list questions not available here so just hard coded
258
258
  return {
259
259
  [q.fieldName]: [
260
- { travelCompanionName: 'Companion 1', travelCompanionEmail: 'compaion-1@example.com' },
261
- { travelCompanionName: 'Companion 2', travelCompanionEmail: 'compaion-2@example.com' },
262
- { travelCompanionName: 'Companion 3', travelCompanionEmail: 'compaion-3@example.com' }
260
+ { id: '1234', travelCompanionName: 'Companion 1', travelCompanionEmail: 'companion-1@example.com' },
261
+ { id: '4567', travelCompanionName: 'Companion 2', travelCompanionEmail: 'companion-2@example.com' },
262
+ { id: '8901', travelCompanionName: 'Companion 3', travelCompanionEmail: 'companion-3@example.com' }
263
263
  ]
264
264
  };
265
265
  }