@planning-inspectorate/dynamic-forms 3.23.0 → 3.24.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/README.md CHANGED
@@ -39,6 +39,54 @@ All exports are named exports from the root module:
39
39
 
40
40
  `import {COMPONENT_TYPES} from '@planning-inspectorate/dynamic-forms`
41
41
 
42
+ ### Custom answer types
43
+
44
+ TypeScript consumers can supply an answer interface without an index signature. Both
45
+ `JourneyResponse` and `Journey` default to `JourneyAnswers` when no answer type is specified.
46
+
47
+ ```typescript
48
+ import { Journey, JourneyResponse } from '@planning-inspectorate/dynamic-forms';
49
+ import type { JourneyResponseLike } from '@planning-inspectorate/dynamic-forms';
50
+
51
+ interface HolidayAppViewModel {
52
+ holidayDestination: string;
53
+ holidayActivities: string[];
54
+ }
55
+
56
+ const answers: HolidayAppViewModel = {
57
+ holidayDestination: 'beach',
58
+ holidayActivities: ['swimming', 'sightseeing']
59
+ };
60
+ const response = new JourneyResponse<HolidayAppViewModel>('holiday-journey', 'ref', answers);
61
+ const journey = new Journey<HolidayAppViewModel>({
62
+ journeyId: 'holiday-journey',
63
+ response,
64
+ makeBaseUrl: (currentResponse) => `/journey/${currentResponse.referenceId}`,
65
+ journeyTemplate: 'views/layout-journey.njk',
66
+ taskListTemplate: 'views/layout-check-your-answers.njk',
67
+ journeyTitle: 'Holiday Booking',
68
+ sections: []
69
+ });
70
+
71
+ journey.response.answers.holidayDestination; // string
72
+
73
+ // Plain objects are also accepted in the constructor and by setResponse.
74
+ const plainResponse: JourneyResponseLike<HolidayAppViewModel> = {
75
+ journeyId: 'holiday-journey',
76
+ referenceId: 'ref',
77
+ answers
78
+ };
79
+ journey.setResponse(plainResponse);
80
+ ```
81
+
82
+ Answer types can also be inferred from the supplied response. `makeBaseUrl` and
83
+ `setResponse` retain the journey's answer type. Component, validator, controller,
84
+ and middleware APIs continue to use their existing default answer types.
85
+
86
+ Supply populated answers when using an interface with required properties:
87
+ `JourneyResponse` retains its existing empty-object fallback for null or missing
88
+ answers, which does not populate those properties.
89
+
42
90
  ### Components
43
91
 
44
92
  Components available are exported via the `COMPONENT_TYPES` constant.
@@ -175,6 +223,41 @@ const update: SaveDataFn = async ({
175
223
  }
176
224
  ```
177
225
 
226
+ `SaveDataFn` and `SaveParams` accept two optional generics: `TData` to type the `data` parameter, and `TLocals` to type `res.locals`. Both default to their current behaviour, so you only need to provide the ones you want to narrow. For example, to type `data`:
227
+
228
+ ```typescript
229
+ type MyData = {
230
+ answers: {
231
+ myField: string;
232
+ };
233
+ };
234
+
235
+ const update: SaveDataFn<MyData> = async ({data}) => {
236
+ const answers = data.answers; // typed as { myField: string }
237
+
238
+ console.log('saving', answers);
239
+
240
+ // TODO: map to database
241
+ // TODO: write to database
242
+ }
243
+ ```
244
+
245
+ To type `res.locals` as well, pass `TLocals` as the second generic:
246
+
247
+ ```typescript
248
+ type MyLocals = JourneyLocals & {
249
+ userId: string;
250
+ };
251
+
252
+ const update: SaveDataFn<MyData, MyLocals> = async ({data, res}) => {
253
+ const userId = res.locals.userId; // typed as string
254
+
255
+ console.log('saving', data.answers, 'for', userId);
256
+
257
+ // TODO: map to database
258
+ // TODO: write to database
259
+ }
260
+ ```
178
261
 
179
262
  ### Conditions
180
263
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planning-inspectorate/dynamic-forms",
3
- "version": "3.23.0",
3
+ "version": "3.24.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",
@@ -1,20 +1,26 @@
1
+ /**
2
+ * @typedef {{ journey: import('./journey/journey.js').Journey, journeyResponse: import('./journey/journey-response.js').JourneyResponse } & Record<string, any>} JourneyLocals
3
+ */
1
4
  /**
2
5
  * @param {object} [viewData]
3
6
  * @returns {import('express').Handler}
4
7
  */
5
8
  export function buildList(viewData?: object): import("express").Handler;
6
9
  /**
10
+ * @template {Record<string, any>} [TLocals=JourneyLocals]
7
11
  * @param {import('express').Request} req
8
- * @param {import('express').Response} res
12
+ * @param {import('express').Response<any, TLocals>} res
9
13
  * @param {string} pageCaption
10
14
  * @param {object} viewData
11
15
  */
12
- export function list(req: import("express").Request, res: import("express").Response, pageCaption: string, viewData: object): Promise<void>;
16
+ export function list<TLocals extends Record<string, any> = JourneyLocals>(req: import("express").Request, res: import("express").Response<any, TLocals>, pageCaption: string, viewData: object): Promise<void>;
13
17
  export function question(req: import("express-serve-static-core").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>, res: import("express-serve-static-core").Response<any, Record<string, any>, number>, next: import("express-serve-static-core").NextFunction): unknown;
14
18
  /**
19
+ * @template [TData=Object<string, any>]
20
+ * @template {Record<string, any>} [TLocals=JourneyLocals]
15
21
  * @typedef {Object} SaveParams
16
22
  * @property {import('express').Request} req
17
- * @property {import('express').Response} res
23
+ * @property {import('express').Response<any, TLocals>} res
18
24
  * @property {string} journeyId
19
25
  * @property {string} referenceId
20
26
  * @property {boolean} isManageListItem
@@ -23,17 +29,23 @@ export function question(req: import("express-serve-static-core").Request<import
23
29
  * @property {boolean} isDynamicSection is this save action for a question within a DynamicSection?
24
30
  * @property {string} [dynamicSectionId] if this is within a DynamicSection, what is the section ID (segment)
25
31
  * @property {string} [dynamicSectionFieldName] if this is within a DynamicSection, what is the fieldName for the array?
26
- * @property {Object<string, any>} data
32
+ * @property {TData} data
27
33
  */
28
34
  /**
29
- * @typedef {(params: SaveParams) => Promise<void>} SaveDataFn
35
+ * @template [TData=Object<string, any>]
36
+ * @template {Record<string, any>} [TLocals=JourneyLocals]
37
+ * @typedef {(params: SaveParams<TData, TLocals>) => Promise<void>} SaveDataFn
30
38
  */
31
39
  /**
32
- * @param {SaveDataFn} saveData
40
+ * @template [TData=Object<string, any>]
41
+ * @template {Record<string, any>} [TLocals=JourneyLocals]
42
+ * @param {SaveDataFn<TData, TLocals>} saveData
33
43
  * @param {boolean} [redirectToTaskListOnSuccess] - optionally redirect to the task list after save instead of next question
34
44
  * @returns {import('express').Handler}
35
45
  */
36
- export function buildSave(saveData: SaveDataFn, redirectToTaskListOnSuccess?: boolean): import("express").Handler;
46
+ export function buildSave<TData = {
47
+ [x: string]: any;
48
+ }, TLocals extends Record<string, any> = JourneyLocals>(saveData: SaveDataFn<TData, TLocals>, redirectToTaskListOnSuccess?: boolean): import("express").Handler;
37
49
  export type SectionView = {
38
50
  heading: string;
39
51
  status: string;
@@ -54,9 +66,15 @@ export type RowView = {
54
66
  items: import("#typedefs/question-types.d.ts").ActionView[];
55
67
  } | undefined;
56
68
  };
57
- export type SaveParams = {
69
+ export type JourneyLocals = {
70
+ journey: import("./journey/journey.js").Journey;
71
+ journeyResponse: import("./journey/journey-response.js").JourneyResponse;
72
+ } & Record<string, any>;
73
+ export type SaveParams<TData = {
74
+ [x: string]: any;
75
+ }, TLocals extends Record<string, any> = JourneyLocals> = {
58
76
  req: import("express").Request;
59
- res: import("express").Response;
77
+ res: import("express").Response<any, TLocals>;
60
78
  journeyId: string;
61
79
  referenceId: string;
62
80
  isManageListItem: boolean;
@@ -74,9 +92,9 @@ export type SaveParams = {
74
92
  * if this is within a DynamicSection, what is the fieldName for the array?
75
93
  */
76
94
  dynamicSectionFieldName?: string | undefined;
77
- data: {
78
- [x: string]: any;
79
- };
95
+ data: TData;
80
96
  };
81
- export type SaveDataFn = (params: SaveParams) => Promise<void>;
97
+ export type SaveDataFn<TData = {
98
+ [x: string]: any;
99
+ }, TLocals extends Record<string, any> = JourneyLocals> = (params: SaveParams<TData, TLocals>) => Promise<void>;
82
100
  //# sourceMappingURL=controller.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["controller.js"],"names":[],"mappings":"AAyDA;;;GAGG;AACH,qCAHW,MAAM,GACJ,OAAO,SAAS,EAAE,OAAO,CAIrC;AAED;;;;;GAKG;AACH,0BALW,OAAO,SAAS,EAAE,OAAO,OACzB,OAAO,SAAS,EAAE,QAAQ,eAC1B,MAAM,YACN,MAAM,iBAmEhB;;AAmDD;;;;;;;;;;;;;GAaG;AAEH;;GAEG;AAEH;;;;GAIG;AACH,oCAJW,UAAU,gCACV,OAAO,GACL,OAAO,SAAS,EAAE,OAAO,CA4FrC;;aApSa,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,OAAO,+BAA+B,EAAE,UAAU,EAAE;;;;SA0K7D,OAAO,SAAS,EAAE,OAAO;SACzB,OAAO,SAAS,EAAE,QAAQ;eAC1B,MAAM;iBACN,MAAM;sBACN,OAAO;;;;;;sBAGP,OAAO;;;;;;;;;;;;;yBAOR,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC"}
1
+ {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["controller.js"],"names":[],"mappings":"AAyDA;;GAEG;AAEH;;;GAGG;AACH,qCAHW,MAAM,GACJ,OAAO,SAAS,EAAE,OAAO,CAIrC;AAED;;;;;;GAMG;AACH,qBANoC,OAAO,SAA7B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAE,uBACvB,OAAO,SAAS,EAAE,OAAO,OACzB,OAAO,SAAS,EAAE,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC,eACxC,MAAM,YACN,MAAM,iBAmEhB;;AAmDD;;;;;;;;;;;;;;;GAeG;AAEH;;;;GAIG;AAEH;;;;;;GAMG;AACH,0BANc,KAAK;;GACiB,OAAO,SAA7B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAE,4BACvB,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,gCAC1B,OAAO,GACL,OAAO,SAAS,EAAE,OAAO,CA4FrC;;aA/Sa,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,OAAO,+BAA+B,EAAE,UAAU,EAAE;;;4BAuC9D;IAAE,OAAO,EAAE,OAAO,sBAAsB,EAAE,OAAO,CAAC;IAAC,eAAe,EAAE,OAAO,+BAA+B,EAAE,eAAe,CAAA;CAAE,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;uBAuIlJ,KAAK;;GACiB,OAAO,SAA7B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAE;SAEpB,OAAO,SAAS,EAAE,OAAO;SACzB,OAAO,SAAS,EAAE,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;eACxC,MAAM;iBACN,MAAM;sBACN,OAAO;;;;;;sBAGP,OAAO;;;;;;;;;UAGP,KAAK;;uBAIL,KAAK;;GACiB,OAAO,SAA7B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAE,oBACrB,CAAC,MAAM,EAAE,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC"}
package/src/controller.js CHANGED
@@ -55,6 +55,10 @@ function buildSectionRowViewModel(key, value, action) {
55
55
  };
56
56
  }
57
57
 
58
+ /**
59
+ * @typedef {{ journey: import('./journey/journey.js').Journey, journeyResponse: import('./journey/journey-response.js').JourneyResponse } & Record<string, any>} JourneyLocals
60
+ */
61
+
58
62
  /**
59
63
  * @param {object} [viewData]
60
64
  * @returns {import('express').Handler}
@@ -64,8 +68,9 @@ export function buildList(viewData = {}) {
64
68
  }
65
69
 
66
70
  /**
71
+ * @template {Record<string, any>} [TLocals=JourneyLocals]
67
72
  * @param {import('express').Request} req
68
- * @param {import('express').Response} res
73
+ * @param {import('express').Response<any, TLocals>} res
69
74
  * @param {string} pageCaption
70
75
  * @param {object} viewData
71
76
  */
@@ -186,9 +191,11 @@ export async function question(req, res) {
186
191
  }
187
192
 
188
193
  /**
194
+ * @template [TData=Object<string, any>]
195
+ * @template {Record<string, any>} [TLocals=JourneyLocals]
189
196
  * @typedef {Object} SaveParams
190
197
  * @property {import('express').Request} req
191
- * @property {import('express').Response} res
198
+ * @property {import('express').Response<any, TLocals>} res
192
199
  * @property {string} journeyId
193
200
  * @property {string} referenceId
194
201
  * @property {boolean} isManageListItem
@@ -197,15 +204,19 @@ export async function question(req, res) {
197
204
  * @property {boolean} isDynamicSection is this save action for a question within a DynamicSection?
198
205
  * @property {string} [dynamicSectionId] if this is within a DynamicSection, what is the section ID (segment)
199
206
  * @property {string} [dynamicSectionFieldName] if this is within a DynamicSection, what is the fieldName for the array?
200
- * @property {Object<string, any>} data
207
+ * @property {TData} data
201
208
  */
202
209
 
203
210
  /**
204
- * @typedef {(params: SaveParams) => Promise<void>} SaveDataFn
211
+ * @template [TData=Object<string, any>]
212
+ * @template {Record<string, any>} [TLocals=JourneyLocals]
213
+ * @typedef {(params: SaveParams<TData, TLocals>) => Promise<void>} SaveDataFn
205
214
  */
206
215
 
207
216
  /**
208
- * @param {SaveDataFn} saveData
217
+ * @template [TData=Object<string, any>]
218
+ * @template {Record<string, any>} [TLocals=JourneyLocals]
219
+ * @param {SaveDataFn<TData, TLocals>} saveData
209
220
  * @param {boolean} [redirectToTaskListOnSuccess] - optionally redirect to the task list after save instead of next question
210
221
  * @returns {import('express').Handler}
211
222
  */
@@ -1,17 +1,17 @@
1
1
  /**
2
2
  * Defines a response to a journey, a set of Answers to the questions
3
3
  * @class
4
- * @template {object} [Answers=import("#typedefs/journey-types.d.ts").JourneyAnswers]
4
+ * @template {object} [TAnswers=import("#typedefs/journey-types.d.ts").JourneyAnswers]
5
5
  */
6
- export class JourneyResponse<Answers extends object = import("#typedefs/journey-types.d.ts").JourneyAnswers> {
6
+ export class JourneyResponse<TAnswers extends object = import("#typedefs/journey-types.d.ts").JourneyAnswers> {
7
7
  /**
8
8
  * creates an instance of a JourneyResponse
9
9
  * @param {string} journeyId
10
10
  * @param {string} referenceId
11
- * @param {Answers | null} answers
11
+ * @param {TAnswers | null} answers
12
12
  * @param {string} [lpaCode]
13
13
  */
14
- constructor(journeyId: string, referenceId: string, answers: Answers | null, lpaCode?: string);
14
+ constructor(journeyId: string, referenceId: string, answers: TAnswers | null, lpaCode?: string);
15
15
  /**
16
16
  * @type {string} - reference id used in the url for the journey e.g. appeal id - provides a unique lookup for responses in combination with formId
17
17
  */
@@ -21,9 +21,9 @@ export class JourneyResponse<Answers extends object = import("#typedefs/journey-
21
21
  */
22
22
  journeyId: string;
23
23
  /**
24
- * @type {Answers} - answers to the journey
24
+ * @type {TAnswers} - answers to the journey
25
25
  */
26
- answers: Answers;
26
+ answers: TAnswers;
27
27
  LPACode: string | undefined;
28
28
  }
29
29
  //# sourceMappingURL=journey-response.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"journey-response.d.ts","sourceRoot":"","sources":["journey-response.js"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,6BAFuB,OAAO,SAAjB,MAAQ;IAkBpB;;;;;;OAMG;IACH,uBALW,MAAM,eACN,MAAM,WACN,OAAO,GAAG,IAAI,YACd,MAAM,EAWhB;IA/BD;;OAEG;IACH,aAFU,MAAM,CAEJ;IAEZ;;OAEG;IACH,WAFU,MAAM,CAEN;IAEV;;OAEG;IACH,SAFU,OAAO,CAET;IAiBP,4BAAsB;CAEvB"}
1
+ {"version":3,"file":"journey-response.d.ts","sourceRoot":"","sources":["journey-response.js"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,6BAFuB,QAAQ,SAAlB,MAAQ;IAkBpB;;;;;;OAMG;IACH,uBALW,MAAM,eACN,MAAM,WACN,QAAQ,GAAG,IAAI,YACf,MAAM,EAWhB;IA/BD;;OAEG;IACH,aAFU,MAAM,CAEJ;IAEZ;;OAEG;IACH,WAFU,MAAM,CAEN;IAEV;;OAEG;IACH,SAFU,QAAQ,CAEV;IAiBP,4BAAsB;CAEvB"}
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Defines a response to a journey, a set of Answers to the questions
3
3
  * @class
4
- * @template {object} [Answers=import("#typedefs/journey-types.d.ts").JourneyAnswers]
4
+ * @template {object} [TAnswers=import("#typedefs/journey-types.d.ts").JourneyAnswers]
5
5
  */
6
6
  export class JourneyResponse {
7
7
  /**
@@ -15,7 +15,7 @@ export class JourneyResponse {
15
15
  journeyId;
16
16
 
17
17
  /**
18
- * @type {Answers} - answers to the journey
18
+ * @type {TAnswers} - answers to the journey
19
19
  */
20
20
  answers;
21
21
 
@@ -23,7 +23,7 @@ export class JourneyResponse {
23
23
  * creates an instance of a JourneyResponse
24
24
  * @param {string} journeyId
25
25
  * @param {string} referenceId
26
- * @param {Answers | null} answers
26
+ * @param {TAnswers | null} answers
27
27
  * @param {string} [lpaCode]
28
28
  */
29
29
  constructor(journeyId, referenceId, answers, lpaCode) {
@@ -1,15 +1,16 @@
1
1
  /**
2
2
  * A journey (An entire set of questions required for a completion of a submission)
3
3
  * @class
4
+ * @template {object} [TAnswers=import("#typedefs/journey-types.d.ts").JourneyAnswers]
4
5
  */
5
- export class Journey {
6
+ export class Journey<TAnswers extends object = import("#typedefs/journey-types.d.ts").JourneyAnswers> {
6
7
  /**
7
8
  * creates an instance of a journey
8
9
  * @param {object} options
9
10
  * @param {string} options.journeyId - a unique, human-readable id for this journey
10
- * @param {(response: import('./journey-response.js').JourneyResponse) => string} options.makeBaseUrl - base url of journey
11
+ * @param {(response: import('#typedefs/journey-types.d.ts').JourneyResponseLike<TAnswers>) => string} options.makeBaseUrl - base url of journey
11
12
  * @param {string} [options.taskListUrl] - task list url - added to base url, can be left undefined
12
- * @param {import('./journey-response.js').JourneyResponse} options.response - user's response
13
+ * @param {import('#typedefs/journey-types.d.ts').JourneyResponseLike<TAnswers>} options.response - user's response
13
14
  * @param {string} options.journeyTemplate - template used for all views
14
15
  * @param {string} options.taskListTemplate - path to njk view for listing page
15
16
  * @param {string} [options.informationPageViewPath] - path to njk view for pdf summary page
@@ -20,9 +21,9 @@ export class Journey {
20
21
  */
21
22
  constructor({ journeyId, makeBaseUrl, taskListUrl, response, journeyTemplate, taskListTemplate, informationPageViewPath, journeyTitle, returnToListing, sections, initialBackLink }: {
22
23
  journeyId: string;
23
- makeBaseUrl: (response: import("./journey-response.js").JourneyResponse) => string;
24
+ makeBaseUrl: (response: import("#typedefs/journey-types.d.ts").JourneyResponseLike<TAnswers>) => string;
24
25
  taskListUrl?: string | undefined;
25
- response: import("./journey-response.js").JourneyResponse;
26
+ response: import("#typedefs/journey-types.d.ts").JourneyResponseLike<TAnswers>;
26
27
  journeyTemplate: string;
27
28
  taskListTemplate: string;
28
29
  informationPageViewPath?: string | undefined;
@@ -35,12 +36,12 @@ export class Journey {
35
36
  journeyId: string;
36
37
  /** @type {Array.<import('../section.js').Section>} sections - sections within the journey */
37
38
  sections: Array<import("../section.js").Section>;
38
- /** @type {import('./journey-response.js').JourneyResponse} response - the user's response to the journey so far */
39
- response: import("./journey-response.js").JourneyResponse;
39
+ /** @type {import('#typedefs/journey-types.d.ts').JourneyResponseLike<TAnswers>} response - the user's response to the journey so far */
40
+ response: import("#typedefs/journey-types.d.ts").JourneyResponseLike<TAnswers>;
40
41
  /** @type {string} baseUrl - base url of the journey, gets prepended to question urls */
41
42
  baseUrl: string;
42
- /** @type {(journeyResponse: import('./journey-response.js').JourneyResponse) => string} makeBaseUrl - function to generate base url of the journey */
43
- makeBaseUrl: (journeyResponse: import("./journey-response.js").JourneyResponse) => string;
43
+ /** @type {(journeyResponse: import('#typedefs/journey-types.d.ts').JourneyResponseLike<TAnswers>) => string} makeBaseUrl - function to generate base url of the journey */
44
+ makeBaseUrl: (journeyResponse: import("#typedefs/journey-types.d.ts").JourneyResponseLike<TAnswers>) => string;
44
45
  /** @type {string} taskListUrl - url that renders the task list */
45
46
  taskListUrl: string;
46
47
  /** @type {string} journeyTemplate - nunjucks template file used for */
@@ -138,9 +139,9 @@ export class Journey {
138
139
  */
139
140
  isComplete(): boolean;
140
141
  /**
141
- * @param {import('./journey-response.js').JourneyResponse} journeyResponse
142
+ * @param {import('#typedefs/journey-types.d.ts').JourneyResponseLike<TAnswers>} journeyResponse
142
143
  */
143
- setResponse(journeyResponse: import("./journey-response.js").JourneyResponse): void;
144
+ setResponse(journeyResponse: import("#typedefs/journey-types.d.ts").JourneyResponseLike<TAnswers>): void;
144
145
  #private;
145
146
  }
146
147
  //# sourceMappingURL=journey.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"journey.d.ts","sourceRoot":"","sources":["journey.js"],"names":[],"mappings":"AAQA;;;GAGG;AACH;IAwBC;;;;;;;;;;;;;;OAcG;IACH,qLAZG;QAAwB,SAAS,EAAzB,MAAM;QACyE,WAAW,EAA1F,CAAC,QAAQ,EAAE,OAAO,uBAAuB,EAAE,eAAe,KAAK,MAAM;QACpD,WAAW;QAC6B,QAAQ,EAAjE,OAAO,uBAAuB,EAAE,eAAe;QAC/B,eAAe,EAA/B,MAAM;QACU,gBAAgB,EAAhC,MAAM;QACW,uBAAuB;QACxB,YAAY,EAA5B,MAAM;QACY,eAAe;QACU,QAAQ,EAAnD,OAAO,eAAe,EAAE,OAAO,EAAE;QAChB,eAAe;KAC1C,EAoDA;IAzFD,8EAA8E;IAC9E,WADW,MAAM,CACP;IACV,6FAA6F;IAC7F,UADW,KAAK,CAAE,OAAO,eAAe,EAAE,OAAO,CAAC,CACpC;IACd,mHAAmH;IACnH,UADW,OAAO,uBAAuB,EAAE,eAAe,CACjD;IACT,wFAAwF;IACxF,SADW,MAAM,CACJ;IACb,sJAAsJ;IACtJ,aADW,CAAC,eAAe,EAAE,OAAO,uBAAuB,EAAE,eAAe,KAAK,MAAM,CAChE;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,eAAe,EAAE,OAAO,GAAG,SAAS,CAMvD;IAED;;;;;;OAMG;IACH,mCAFW,MAAM;;MAQhB;IA8BD;;;;OAIG;IACH,4BAHW,OAAO,8BAA8B,EAAE,WAAW,GAChD,OAAO,0BAA0B,EAAE,QAAQ,GAAG,SAAS,CAiBnE;IAED;;;;;;;OAOG;IACH,4CAJG;QAAoE,MAAM,EAAlE,OAAO,8BAA8B,EAAE,WAAW;QACU,kBAAkB;KACtF,GAAU,MAAM,GAAC,IAAI,CAWvB;IAED;;;;;;;;OAQG;IACH,4BALW,OAAO,SAAS,EAAE,QAAQ,UAC1B,OAAO,8BAA8B,EAAE,WAAW,kFAEhD,IAAI,CAKhB;IAED;;;;;;;;OAQG;IACH,2BANW,OAAO,8BAA8B,EAAE,WAAW,oCAE1D;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,OAAO,uBAAuB,EAAE,eAAe,QAKzD;;CACD"}
1
+ {"version":3,"file":"journey.d.ts","sourceRoot":"","sources":["journey.js"],"names":[],"mappings":"AAQA;;;;GAIG;AACH,qBAFuB,QAAQ,SAAlB,MAAQ;IA0BpB;;;;;;;;;;;;;;OAcG;IACH,qLAZG;QAAwB,SAAS,EAAzB,MAAM;QAC8F,WAAW,EAA/G,CAAC,QAAQ,EAAE,OAAO,8BAA8B,EAAE,mBAAmB,CAAC,QAAQ,CAAC,KAAK,MAAM;QACzE,WAAW;QACkD,QAAQ,EAAtF,OAAO,8BAA8B,EAAE,mBAAmB,CAAC,QAAQ,CAAC;QACpD,eAAe,EAA/B,MAAM;QACU,gBAAgB,EAAhC,MAAM;QACW,uBAAuB;QACxB,YAAY,EAA5B,MAAM;QACY,eAAe;QACU,QAAQ,EAAnD,OAAO,eAAe,EAAE,OAAO,EAAE;QAChB,eAAe;KAC1C,EAoDA;IAzFD,8EAA8E;IAC9E,WADW,MAAM,CACP;IACV,6FAA6F;IAC7F,UADW,KAAK,CAAE,OAAO,eAAe,EAAE,OAAO,CAAC,CACpC;IACd,wIAAwI;IACxI,UADW,OAAO,8BAA8B,EAAE,mBAAmB,CAAC,QAAQ,CAAC,CACtE;IACT,wFAAwF;IACxF,SADW,MAAM,CACJ;IACb,2KAA2K;IAC3K,aADW,CAAC,eAAe,EAAE,OAAO,8BAA8B,EAAE,mBAAmB,CAAC,QAAQ,CAAC,KAAK,MAAM,CACrF;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,eAAe,EAAE,OAAO,GAAG,SAAS,CAMvD;IAED;;;;;;OAMG;IACH,mCAFW,MAAM;;MAQhB;IA8BD;;;;OAIG;IACH,4BAHW,OAAO,8BAA8B,EAAE,WAAW,GAChD,OAAO,0BAA0B,EAAE,QAAQ,GAAG,SAAS,CAiBnE;IAED;;;;;;;OAOG;IACH,4CAJG;QAAoE,MAAM,EAAlE,OAAO,8BAA8B,EAAE,WAAW;QACU,kBAAkB;KACtF,GAAU,MAAM,GAAC,IAAI,CAWvB;IAED;;;;;;;;OAQG;IACH,4BALW,OAAO,SAAS,EAAE,QAAQ,UAC1B,OAAO,8BAA8B,EAAE,WAAW,kFAEhD,IAAI,CAKhB;IAED;;;;;;;;OAQG;IACH,2BANW,OAAO,8BAA8B,EAAE,WAAW,oCAE1D;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,OAAO,8BAA8B,EAAE,mBAAmB,CAAC,QAAQ,CAAC,QAK9E;;CACD"}
@@ -9,17 +9,18 @@ import { MANAGE_LIST_ACTIONS } from '#src/components/manage-list/manage-list-act
9
9
  /**
10
10
  * A journey (An entire set of questions required for a completion of a submission)
11
11
  * @class
12
+ * @template {object} [TAnswers=import("#typedefs/journey-types.d.ts").JourneyAnswers]
12
13
  */
13
14
  export class Journey {
14
15
  /** @type {string} journeyId - a unique, human-readable id for this journey */
15
16
  journeyId;
16
17
  /** @type {Array.<import('../section.js').Section>} sections - sections within the journey */
17
18
  sections = [];
18
- /** @type {import('./journey-response.js').JourneyResponse} response - the user's response to the journey so far */
19
+ /** @type {import('#typedefs/journey-types.d.ts').JourneyResponseLike<TAnswers>} response - the user's response to the journey so far */
19
20
  response;
20
21
  /** @type {string} baseUrl - base url of the journey, gets prepended to question urls */
21
22
  baseUrl = '';
22
- /** @type {(journeyResponse: import('./journey-response.js').JourneyResponse) => string} makeBaseUrl - function to generate base url of the journey */
23
+ /** @type {(journeyResponse: import('#typedefs/journey-types.d.ts').JourneyResponseLike<TAnswers>) => string} makeBaseUrl - function to generate base url of the journey */
23
24
  makeBaseUrl = () => '';
24
25
  /** @type {string} taskListUrl - url that renders the task list */
25
26
  taskListUrl = '';
@@ -38,9 +39,9 @@ export class Journey {
38
39
  * creates an instance of a journey
39
40
  * @param {object} options
40
41
  * @param {string} options.journeyId - a unique, human-readable id for this journey
41
- * @param {(response: import('./journey-response.js').JourneyResponse) => string} options.makeBaseUrl - base url of journey
42
+ * @param {(response: import('#typedefs/journey-types.d.ts').JourneyResponseLike<TAnswers>) => string} options.makeBaseUrl - base url of journey
42
43
  * @param {string} [options.taskListUrl] - task list url - added to base url, can be left undefined
43
- * @param {import('./journey-response.js').JourneyResponse} options.response - user's response
44
+ * @param {import('#typedefs/journey-types.d.ts').JourneyResponseLike<TAnswers>} options.response - user's response
44
45
  * @param {string} options.journeyTemplate - template used for all views
45
46
  * @param {string} options.taskListTemplate - path to njk view for listing page
46
47
  * @param {string} [options.informationPageViewPath] - path to njk view for pdf summary page
@@ -408,7 +409,7 @@ export class Journey {
408
409
  }
409
410
 
410
411
  /**
411
- * @param {import('./journey-response.js').JourneyResponse} journeyResponse
412
+ * @param {import('#typedefs/journey-types.d.ts').JourneyResponseLike<TAnswers>} journeyResponse
412
413
  */
413
414
  setResponse(journeyResponse) {
414
415
  this.response = journeyResponse;
@@ -66,6 +66,24 @@ describe('Journey class', () => {
66
66
  });
67
67
 
68
68
  describe('constructor', () => {
69
+ it('should accept structural responses and retain them when updating the response', () => {
70
+ const response = {
71
+ journeyId: 'TEST',
72
+ referenceId: 'first',
73
+ answers: { holidayDestination: 'beach', holidayActivities: ['swimming', 'sightseeing'] }
74
+ };
75
+ constructorArgs.response = response;
76
+ constructorArgs.makeBaseUrl = (currentResponse) => `/journey/${currentResponse.referenceId}`;
77
+ const journey = new Journey(constructorArgs);
78
+ assert.strictEqual(journey.response, response);
79
+ assert.strictEqual(journey.baseUrl, '/journey/first');
80
+
81
+ const updatedResponse = { ...response, referenceId: 'second', LPACode: 'LPA' };
82
+ journey.setResponse(updatedResponse);
83
+ assert.strictEqual(journey.response, updatedResponse);
84
+ assert.strictEqual(journey.baseUrl, '/journey/second');
85
+ });
86
+
69
87
  it('should throw if no arguments passed into constructor', () => {
70
88
  assert.throws(() => new Journey({}), Error);
71
89
  });
@@ -22,6 +22,13 @@ export interface JourneyAnswers {
22
22
  [k: string]: unknown | ManageListAnswers[];
23
23
  }
24
24
 
25
+ export interface JourneyResponseLike<TAnswers = JourneyAnswers> {
26
+ journeyId: string;
27
+ referenceId: string;
28
+ answers: TAnswers;
29
+ LPACode: string | undefined;
30
+ }
31
+
25
32
  export interface ManageListAnswers {
26
33
  id: string;
27
34
  [k: string]: unknown;