@planning-inspectorate/dynamic-forms 2.4.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +12 -1
  2. package/package.json +3 -2
  3. package/src/components/address/question.js +25 -19
  4. package/src/components/address/question.test.js +1 -1
  5. package/src/components/date/question.js +7 -21
  6. package/src/components/date/question.test.js +1 -1
  7. package/src/components/date-period/question.js +17 -25
  8. package/src/components/date-period/question.test.js +1 -1
  9. package/src/components/date-time/question.js +10 -23
  10. package/src/components/identifier/question.js +3 -6
  11. package/src/components/manage-list/README.md +97 -0
  12. package/src/components/manage-list/answer-summary-list.njk +10 -0
  13. package/src/components/manage-list/index.njk +61 -0
  14. package/src/components/manage-list/manage-list-section.js +39 -0
  15. package/src/components/manage-list/manage-list-section.test.js +52 -0
  16. package/src/components/manage-list/question.js +147 -0
  17. package/src/components/manage-list/question.test.js +139 -0
  18. package/src/components/multi-field-input/index.njk +1 -1
  19. package/src/components/multi-field-input/question.js +12 -13
  20. package/src/components/multi-field-input/question.test.js +8 -8
  21. package/src/components/number-entry/question.js +7 -13
  22. package/src/components/radio/question.js +2 -4
  23. package/src/components/select/question.js +2 -4
  24. package/src/components/single-line-input/question.js +5 -6
  25. package/src/components/text-entry/question.js +5 -5
  26. package/src/components/text-entry-redact/question.js +18 -9
  27. package/src/components/text-entry-redact/question.test.js +12 -19
  28. package/src/components/unit-option-entry/question.js +9 -8
  29. package/src/components/utils/nunjucks.js +20 -0
  30. package/src/controller.js +39 -10
  31. package/src/controller.test.js +90 -24
  32. package/src/index.js +3 -2
  33. package/src/journey/journey-response.js +2 -2
  34. package/src/journey/journey-types.d.ts +21 -1
  35. package/src/journey/journey.js +64 -18
  36. package/src/journey/journey.test.js +80 -15
  37. package/src/questions/options-question.js +6 -7
  38. package/src/questions/question-props.d.ts +2 -1
  39. package/src/questions/question-types.d.ts +26 -0
  40. package/src/questions/question.js +119 -15
  41. package/src/questions/question.test.js +98 -5
  42. package/src/questions/questions.js +3 -1
  43. package/src/section-types.d.ts +18 -0
  44. package/src/section.js +40 -11
  45. package/src/section.test.js +89 -0
  46. package/test/journey.js +5 -0
  47. package/test/mock/journey.js +17 -0
  48. package/test/questions.js +30 -1
  49. package/test/questions.test.js +104 -40
  50. package/test/snapshots/check-your-answers-not-started.html +11 -0
  51. package/test/snapshots/check-your-answers-with-answers.html +11 -0
  52. package/test/snapshots/manage-list-answer-summary.html +14 -0
  53. package/test/snapshots/manage-list-render-questions.html +97 -0
  54. package/test/snapshots/manage-list-render.html +91 -0
  55. package/test/snapshots/travel-companions.html +19 -0
  56. package/test/snapshots/travel-companions_travel-companion-email.html +21 -0
  57. package/test/snapshots/travel-companions_travel-companion-name.html +21 -0
  58. package/tsconfig.json +2 -1
package/test/questions.js CHANGED
@@ -174,6 +174,27 @@ export const questionProps = {
174
174
  url: 'contact-email',
175
175
  label: 'Email address',
176
176
  validators: [new EmailValidator()]
177
+ },
178
+ travelCompanions: {
179
+ type: COMPONENT_TYPES.MANAGE_LIST,
180
+ title: 'Travel Companions',
181
+ question: 'Travel Companions',
182
+ fieldName: 'travelCompanions',
183
+ url: 'travel-companions'
184
+ },
185
+ travelCompanionName: {
186
+ type: COMPONENT_TYPES.SINGLE_LINE_INPUT,
187
+ title: 'Travel Companion Name',
188
+ question: 'Travel Companion Name',
189
+ fieldName: 'travelCompanionName',
190
+ url: 'travel-companion-name'
191
+ },
192
+ travelCompanionEmail: {
193
+ type: COMPONENT_TYPES.EMAIL,
194
+ title: 'Travel Companion Email',
195
+ question: 'Travel Companion Email',
196
+ fieldName: 'travelCompanionEmail',
197
+ url: 'travel-companion-email'
177
198
  }
178
199
  };
179
200
 
@@ -194,7 +215,15 @@ export const questionsInOrder = [
194
215
  questionProps.nights,
195
216
  questionProps.hotelAddress,
196
217
  questionProps.luggageWeight,
197
- questionProps.contactEmail
218
+ questionProps.contactEmail,
219
+ questionProps.travelCompanions
220
+ ];
221
+
222
+ export const manageListQuestions = [
223
+ {
224
+ manageListQuestion: questionProps.travelCompanions,
225
+ questions: [questionProps.travelCompanionName, questionProps.travelCompanionEmail]
226
+ }
198
227
  ];
199
228
 
200
229
  export const getQuestions = () => createQuestions(questionProps, questionClasses, {});
@@ -2,7 +2,7 @@ import { describe, it } from 'node:test';
2
2
  import assert from 'assert';
3
3
  import { createApp } from './utils/app.js';
4
4
  import { TestServer } from './utils/test-server.js';
5
- import { getQuestions, questionsInOrder } from './questions.js';
5
+ import { getQuestions, manageListQuestions, questionsInOrder } from './questions.js';
6
6
  import { buildGetJourney } from '../src/middleware/build-get-journey.js';
7
7
  import { createJourney, JOURNEY_ID } from './journey.js';
8
8
  import { buildGetJourneyResponseFromSession, saveDataToSession } from '../src/lib/session-answer-store.js';
@@ -13,6 +13,7 @@ import path from 'path';
13
13
  import { COMPONENT_TYPES } from '../src/index.js';
14
14
  import { escapeForRegExp, snapshotsDir } from './utils/utils.js';
15
15
  import { BOOLEAN_OPTIONS } from '../src/components/boolean/question.js';
16
+ import { mockRandomUUID } from '#src/lib/uuid.test.js';
16
17
 
17
18
  /**
18
19
  * @param {import('node:test').TestContext} ctx
@@ -29,10 +30,15 @@ export async function createAppWithQuestions(ctx) {
29
30
  // next();
30
31
  // });
31
32
 
32
- app.get('/:section/:question', getJourneyResponse, getJourney, question);
33
+ app.get(
34
+ '/:section/:question{/:manageListAction/:manageListItemId/:manageListQuestion}',
35
+ getJourneyResponse,
36
+ getJourney,
37
+ question
38
+ );
33
39
 
34
40
  app.post(
35
- '/:section/:question',
41
+ '/:section/:question{/:manageListAction/:manageListItemId/:manageListQuestion}',
36
42
  getJourneyResponse,
37
43
  getJourney,
38
44
  validate,
@@ -83,50 +89,72 @@ describe('question pages', () => {
83
89
  }
84
90
  });
85
91
 
92
+ /**
93
+ * @param {import('node:test').TestContext} ctx
94
+ * @param {string} url
95
+ * @param {string} snapshotName
96
+ * @param {import('#src/questions/question.js')} q
97
+ * @returns {Promise<void>}
98
+ */
99
+ async function renderQuestionCheck(ctx, url, snapshotName, q) {
100
+ mockRandomUUID(ctx);
101
+ const testServer = await createAppWithQuestions(ctx);
102
+
103
+ const response = await testServer.get(url, {
104
+ redirect: 'manual'
105
+ });
106
+ assert.strictEqual(response.status, 200);
107
+ const text = await response.text();
108
+ // be sure to escape parentheses in the question text for the regex
109
+ assert.match(text, new RegExp(escapeForRegExp(q.question), 'i'));
110
+ assert.match(text, /<form action="" method="post"/i);
111
+ if (q.options) {
112
+ for (const option of q.options) {
113
+ if (q.type === COMPONENT_TYPES.SELECT) {
114
+ assert.match(
115
+ text,
116
+ new RegExp(`<option[^>]*value="${option.value}"[^>]*>\\s*${option.text}\\s*</option>`, 'i')
117
+ );
118
+ } else {
119
+ assert.match(text, new RegExp(`<input[^>]*value="${option.value}"[^>]*>`));
120
+ assert.match(text, new RegExp(`<label[^>]*>\\s*${option.text}\\s*</label>`, 'i'));
121
+ }
122
+ }
123
+ }
124
+ ctx.assert.fileSnapshot(text, path.join(snapshotsDir(), snapshotName + '.html'), {
125
+ serializers: [(v) => v]
126
+ });
127
+ }
128
+
129
+ /**
130
+ * @param {import('node:test').TestContext} ctx
131
+ * @param {string} url
132
+ * @param {import('#src/questions/question.js')} q
133
+ * @returns {Promise<string>}
134
+ */
135
+ async function postQuestionCheck(ctx, url, q) {
136
+ const testServer = await createAppWithQuestions(ctx);
137
+ const payload = mockAnswerBody(q);
138
+ const response = await testServer.post(url, payload, { redirect: 'manual' });
139
+ if (![302, 303].includes(response.status)) {
140
+ const text = await response.text();
141
+ console.log(`Response for ${q.url}:\n`, text);
142
+ }
143
+ // Should redirect (302 or 303)
144
+ assert.ok([302, 303].includes(response.status), `Expected redirect, got ${response.status}`);
145
+ // Should redirect to the next question (if there is one)
146
+ return response.headers.get('location');
147
+ }
148
+
86
149
  for (let i = 0; i < questionsInOrder.length; i++) {
87
150
  const q = questionsInOrder[i];
88
151
 
89
152
  it(`should render question: ${q.url}`, async (ctx) => {
90
- const testServer = await createAppWithQuestions(ctx);
91
-
92
- const response = await testServer.get('/questions/' + q.url, {
93
- redirect: 'manual'
94
- });
95
- assert.strictEqual(response.status, 200);
96
- const text = await response.text();
97
- // be sure to escape parentheses in the question text for the regex
98
- assert.match(text, new RegExp(escapeForRegExp(q.question), 'i'));
99
- assert.match(text, /<form action="" method="post"/i);
100
- if (q.options) {
101
- for (const option of q.options) {
102
- if (q.type === COMPONENT_TYPES.SELECT) {
103
- assert.match(
104
- text,
105
- new RegExp(`<option[^>]*value="${option.value}"[^>]*>\\s*${option.text}\\s*</option>`, 'i')
106
- );
107
- } else {
108
- assert.match(text, new RegExp(`<input[^>]*value="${option.value}"[^>]*>`));
109
- assert.match(text, new RegExp(`<label[^>]*>\\s*${option.text}\\s*</label>`, 'i'));
110
- }
111
- }
112
- }
113
- ctx.assert.fileSnapshot(text, path.join(snapshotsDir(), q.url + '.html'), {
114
- serializers: [(v) => v]
115
- });
153
+ await renderQuestionCheck(ctx, '/questions/' + q.url, q.url, q);
116
154
  });
117
155
 
118
156
  it(`should POST valid data for question: ${q.url} and redirect to next`, async (ctx) => {
119
- const testServer = await createAppWithQuestions(ctx);
120
- const payload = mockAnswerBody(q);
121
- const response = await testServer.post(`/questions/${q.url}`, payload, { redirect: 'manual' });
122
- if (![302, 303].includes(response.status)) {
123
- const text = await response.text();
124
- console.log(`Response for ${q.url}:\n`, text);
125
- }
126
- // Should redirect (302 or 303)
127
- assert.ok([302, 303].includes(response.status), `Expected redirect, got ${response.status}`);
128
- // Should redirect to the next question (if there is one)
129
- const location = response.headers.get('location');
157
+ const location = await postQuestionCheck(ctx, `/questions/${q.url}`, q);
130
158
  const nextQ = questionsInOrder[i + 1];
131
159
  if (nextQ) {
132
160
  assert.strictEqual(location, `questions/${nextQ.url}`);
@@ -135,6 +163,29 @@ describe('question pages', () => {
135
163
  }
136
164
  });
137
165
  }
166
+
167
+ describe('manage list questions', () => {
168
+ for (const { manageListQuestion, questions } of manageListQuestions) {
169
+ const qUrl = (q) => ['questions', manageListQuestion.url, 'add', 'id', q.url].join('/');
170
+ for (let i = 0; i < questions.length; i++) {
171
+ const q = questions[i];
172
+ it(`should render question: ${q.url}`, async (ctx) => {
173
+ await renderQuestionCheck(ctx, '/' + qUrl(q), manageListQuestion.url + '_' + q.url, q);
174
+ });
175
+
176
+ it(`should POST valid data for question: ${q.url} and redirect to next`, async (ctx) => {
177
+ const location = await postQuestionCheck(ctx, '/' + qUrl(q), q);
178
+ const nextQ = questions[i + 1];
179
+ if (nextQ) {
180
+ assert.strictEqual(location, qUrl(nextQ));
181
+ } else {
182
+ // at the end of the manage list section, return to the manage list question
183
+ assert.strictEqual(location, 'questions/' + manageListQuestion.url);
184
+ }
185
+ });
186
+ }
187
+ }
188
+ });
138
189
  });
139
190
 
140
191
  /**
@@ -202,6 +253,16 @@ export function mockAnswerBody(q) {
202
253
  };
203
254
  case COMPONENT_TYPES.EMAIL:
204
255
  return { [q.fieldName]: 'test@example.com' };
256
+ case COMPONENT_TYPES.MANAGE_LIST: {
257
+ // manage list questions not available here so just hard coded
258
+ return {
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' }
263
+ ]
264
+ };
265
+ }
205
266
  default:
206
267
  return { [q.fieldName]: 'test' };
207
268
  }
@@ -251,6 +312,9 @@ export function mockAnswer(q) {
251
312
  return '10 kg';
252
313
  case COMPONENT_TYPES.EMAIL:
253
314
  return 'test@example.com';
315
+ case COMPONENT_TYPES.MANAGE_LIST: {
316
+ return `3 ${q.title}`;
317
+ }
254
318
  default:
255
319
  return 'test';
256
320
  }
@@ -196,6 +196,17 @@
196
196
  <a class="govuk-link" href="questions/contact-email">Answer<span class="govuk-visually-hidden"> What is your email address for booking confirmations?</span></a>
197
197
  </dd>
198
198
  </div>
199
+ <div class="govuk-summary-list__row">
200
+ <dt class="govuk-summary-list__key">
201
+ Travel Companions
202
+ </dt>
203
+ <dd class="govuk-summary-list__value">
204
+ Not started
205
+ </dd>
206
+ <dd class="govuk-summary-list__actions">
207
+ <a class="govuk-link" href="questions/travel-companions">Answer<span class="govuk-visually-hidden"> Travel Companions</span></a>
208
+ </dd>
209
+ </div>
199
210
  </dl>
200
211
 
201
212
  <form action method="post">
@@ -196,6 +196,17 @@
196
196
  <a class="govuk-link" href="questions/contact-email">Change<span class="govuk-visually-hidden"> What is your email address for booking confirmations?</span></a>
197
197
  </dd>
198
198
  </div>
199
+ <div class="govuk-summary-list__row">
200
+ <dt class="govuk-summary-list__key">
201
+ Travel Companions
202
+ </dt>
203
+ <dd class="govuk-summary-list__value">
204
+ 3 Travel Companions
205
+ </dd>
206
+ <dd class="govuk-summary-list__actions">
207
+ <a class="govuk-link" href="questions/travel-companions">Change<span class="govuk-visually-hidden"> Travel Companions</span></a>
208
+ </dd>
209
+ </div>
199
210
  </dl>
200
211
 
201
212
  <form action method="post">
@@ -0,0 +1,14 @@
1
+ <ul>
2
+ <li>
3
+ mock answer<br>
4
+ mock answer 2
5
+ </li>
6
+ <li>
7
+ mock answer<br>
8
+ mock answer 2
9
+ </li>
10
+ <li>
11
+ mock answer<br>
12
+ mock answer 2
13
+ </li>
14
+ </ul>
@@ -0,0 +1,97 @@
1
+ <div class="govuk-grid-row">
2
+ <div class="govuk-grid-column-two-thirds">
3
+ <form action="" method="post" novalidate>
4
+
5
+ <h1 class="govuk-heading-l">You have added 3 Things</h1>
6
+
7
+ <dl class="govuk-summary-list">
8
+ <div class="govuk-summary-list__row ">
9
+ <dt class="govuk-summary-list__key">
10
+ Thing 1
11
+ </dt>
12
+ <dt class="govuk-summary-list__value">
13
+ Q 1:<br>
14
+ mock answer
15
+ <br><br>
16
+ Q 2:<br>
17
+ mock answer 2
18
+ </dt>
19
+ <dd class="govuk-summary-list__actions">
20
+ <ul class="govuk-summary-list__actions-list">
21
+ <li class="govuk-summary-list__actions-list-item">
22
+ <a class="govuk-link" href="/edit/id-1/first-question">
23
+ Change <span class="govuk-visually-hidden">Thing 1</span>
24
+ </a>
25
+ </li>
26
+ <li class="govuk-summary-list__actions-list-item">
27
+ <a class="govuk-link" href="/remove/id-1/confirm">
28
+ Remove <span class="govuk-visually-hidden">Thing 1</span>
29
+ </a>
30
+ </li>
31
+ </ul>
32
+ </dd>
33
+ </div>
34
+ <div class="govuk-summary-list__row ">
35
+ <dt class="govuk-summary-list__key">
36
+ Thing 2
37
+ </dt>
38
+ <dt class="govuk-summary-list__value">
39
+ Q 1:<br>
40
+ mock answer
41
+ <br><br>
42
+ Q 2:<br>
43
+ mock answer 2
44
+ </dt>
45
+ <dd class="govuk-summary-list__actions">
46
+ <ul class="govuk-summary-list__actions-list">
47
+ <li class="govuk-summary-list__actions-list-item">
48
+ <a class="govuk-link" href="/edit/id-2/first-question">
49
+ Change <span class="govuk-visually-hidden">Thing 2</span>
50
+ </a>
51
+ </li>
52
+ <li class="govuk-summary-list__actions-list-item">
53
+ <a class="govuk-link" href="/remove/id-2/confirm">
54
+ Remove <span class="govuk-visually-hidden">Thing 2</span>
55
+ </a>
56
+ </li>
57
+ </ul>
58
+ </dd>
59
+ </div>
60
+ <div class="govuk-summary-list__row ">
61
+ <dt class="govuk-summary-list__key">
62
+ Thing 3
63
+ </dt>
64
+ <dt class="govuk-summary-list__value">
65
+ Q 1:<br>
66
+ mock answer
67
+ <br><br>
68
+ Q 2:<br>
69
+ mock answer 2
70
+ </dt>
71
+ <dd class="govuk-summary-list__actions">
72
+ <ul class="govuk-summary-list__actions-list">
73
+ <li class="govuk-summary-list__actions-list-item">
74
+ <a class="govuk-link" href="/edit/id-3/first-question">
75
+ Change <span class="govuk-visually-hidden">Thing 3</span>
76
+ </a>
77
+ </li>
78
+ <li class="govuk-summary-list__actions-list-item">
79
+ <a class="govuk-link" href="/remove/id-3/confirm">
80
+ Remove <span class="govuk-visually-hidden">Thing 3</span>
81
+ </a>
82
+ </li>
83
+ </ul>
84
+ </dd>
85
+ </div>
86
+ </dl>
87
+
88
+ <p class="govuk-body">
89
+ <a href="/add/00000000-0000-0000-0000-000000000000/first-question" class="govuk-link">Add another</a>
90
+ </p>
91
+
92
+ <button type="submit" class="govuk-button" data-module="govuk-button" data-cy="button-save-and-continue">
93
+ Continue
94
+ </button>
95
+ </form>
96
+ </div>
97
+ </div>
@@ -0,0 +1,91 @@
1
+ <div class="govuk-grid-row">
2
+ <div class="govuk-grid-column-two-thirds">
3
+ <form action="" method="post" novalidate>
4
+
5
+ <h1 class="govuk-heading-l">You have added 3 Things</h1>
6
+
7
+ <dl class="govuk-summary-list">
8
+ <div class="govuk-summary-list__row ">
9
+ <dt class="govuk-summary-list__key">
10
+ Thing 1
11
+ </dt>
12
+ <dt class="govuk-summary-list__value">
13
+ mock answer
14
+ <br>
15
+ mock answer 2
16
+ </dt>
17
+ <dd class="govuk-summary-list__actions">
18
+ <ul class="govuk-summary-list__actions-list">
19
+ <li class="govuk-summary-list__actions-list-item">
20
+ <a class="govuk-link" href="/edit/id-1/first-question">
21
+ Change <span class="govuk-visually-hidden">Thing 1</span>
22
+ </a>
23
+ </li>
24
+ <li class="govuk-summary-list__actions-list-item">
25
+ <a class="govuk-link" href="/remove/id-1/confirm">
26
+ Remove <span class="govuk-visually-hidden">Thing 1</span>
27
+ </a>
28
+ </li>
29
+ </ul>
30
+ </dd>
31
+ </div>
32
+ <div class="govuk-summary-list__row ">
33
+ <dt class="govuk-summary-list__key">
34
+ Thing 2
35
+ </dt>
36
+ <dt class="govuk-summary-list__value">
37
+ mock answer
38
+ <br>
39
+ mock answer 2
40
+ </dt>
41
+ <dd class="govuk-summary-list__actions">
42
+ <ul class="govuk-summary-list__actions-list">
43
+ <li class="govuk-summary-list__actions-list-item">
44
+ <a class="govuk-link" href="/edit/id-2/first-question">
45
+ Change <span class="govuk-visually-hidden">Thing 2</span>
46
+ </a>
47
+ </li>
48
+ <li class="govuk-summary-list__actions-list-item">
49
+ <a class="govuk-link" href="/remove/id-2/confirm">
50
+ Remove <span class="govuk-visually-hidden">Thing 2</span>
51
+ </a>
52
+ </li>
53
+ </ul>
54
+ </dd>
55
+ </div>
56
+ <div class="govuk-summary-list__row ">
57
+ <dt class="govuk-summary-list__key">
58
+ Thing 3
59
+ </dt>
60
+ <dt class="govuk-summary-list__value">
61
+ mock answer
62
+ <br>
63
+ mock answer 2
64
+ </dt>
65
+ <dd class="govuk-summary-list__actions">
66
+ <ul class="govuk-summary-list__actions-list">
67
+ <li class="govuk-summary-list__actions-list-item">
68
+ <a class="govuk-link" href="/edit/id-3/first-question">
69
+ Change <span class="govuk-visually-hidden">Thing 3</span>
70
+ </a>
71
+ </li>
72
+ <li class="govuk-summary-list__actions-list-item">
73
+ <a class="govuk-link" href="/remove/id-3/confirm">
74
+ Remove <span class="govuk-visually-hidden">Thing 3</span>
75
+ </a>
76
+ </li>
77
+ </ul>
78
+ </dd>
79
+ </div>
80
+ </dl>
81
+
82
+ <p class="govuk-body">
83
+ <a href="/add/00000000-0000-0000-0000-000000000000/first-question" class="govuk-link">Add another</a>
84
+ </p>
85
+
86
+ <button type="submit" class="govuk-button" data-module="govuk-button" data-cy="button-save-and-continue">
87
+ Continue
88
+ </button>
89
+ </form>
90
+ </div>
91
+ </div>
@@ -0,0 +1,19 @@
1
+ <div class="govuk-grid-row">
2
+ <div class="govuk-grid-column-two-thirds">
3
+ <form action="" method="post" novalidate>
4
+
5
+ <h1 class="govuk-heading-l">You have added 0 Travel Companions</h1>
6
+
7
+ <dl class="govuk-summary-list">
8
+ </dl>
9
+
10
+ <p class="govuk-body">
11
+ <a href="/questions/travel-companions/add/00000000-0000-0000-0000-000000000000/travel-companion-name" class="govuk-link">Add another</a>
12
+ </p>
13
+
14
+ <button type="submit" class="govuk-button" data-module="govuk-button" data-cy="button-save-and-continue">
15
+ Continue
16
+ </button>
17
+ </form>
18
+ </div>
19
+ </div>
@@ -0,0 +1,21 @@
1
+ <div class="govuk-grid-row">
2
+ <div class="govuk-grid-column-two-thirds">
3
+ <form action="" method="post" novalidate>
4
+
5
+ <div class="govuk-form-group">
6
+ <h1 class="govuk-label-wrapper">
7
+ <label class="govuk-label govuk-label--l" for="travelCompanionEmail">
8
+ Travel Companion Email
9
+ </label>
10
+ </h1>
11
+ <input class="govuk-input" id="travelCompanionEmail" name="travelCompanionEmail" type="email" value="" autocomplete="email" spellcheck="false">
12
+ </div>
13
+
14
+
15
+
16
+ <button type="submit" class="govuk-button" data-module="govuk-button" data-cy="button-save-and-continue">
17
+ Continue
18
+ </button>
19
+ </form>
20
+ </div>
21
+ </div>
@@ -0,0 +1,21 @@
1
+ <div class="govuk-grid-row">
2
+ <div class="govuk-grid-column-two-thirds">
3
+ <form action="" method="post" novalidate>
4
+
5
+ <div class="govuk-form-group">
6
+ <h1 class="govuk-label-wrapper">
7
+ <label class="govuk-label govuk-label--l" for="travelCompanionName">
8
+ Travel Companion Name
9
+ </label>
10
+ </h1>
11
+ <input class="govuk-input" id="travelCompanionName" name="travelCompanionName" type="text" value="">
12
+ </div>
13
+
14
+
15
+
16
+ <button type="submit" class="govuk-button" data-module="govuk-button" data-cy="button-save-and-continue">
17
+ Continue
18
+ </button>
19
+ </form>
20
+ </div>
21
+ </div>
package/tsconfig.json CHANGED
@@ -17,7 +17,8 @@
17
17
  "paths": {
18
18
  "#question-types": ["./src/questions/question-types.d.ts"],
19
19
  "#src/*": ["./src/*"],
20
- "#test/*": ["./test/*"]
20
+ "#test/*": ["./test/*"],
21
+ "#question": ["./src/questions/question.js"]
21
22
  }
22
23
  }
23
24
  }