hof 19.13.6 → 19.13.9

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 (32) hide show
  1. package/.eslintignore +1 -1
  2. package/README.md +4 -6
  3. package/build/lib/spawn.js +1 -1
  4. package/build/tasks/watch/index.js +1 -1
  5. package/components/date/index.js +32 -23
  6. package/frontend/template-partials/views/cookies.html +1 -9
  7. package/index.js +1 -1
  8. package/package.json +3 -3
  9. package/sandbox/README.md +66 -0
  10. package/sandbox/apps/sandbox/behaviours/clear-session.js +8 -0
  11. package/sandbox/apps/sandbox/behaviours/country-select.js +10 -0
  12. package/sandbox/apps/sandbox/behaviours/international-number.js +22 -0
  13. package/sandbox/apps/sandbox/fields.js +106 -0
  14. package/sandbox/apps/sandbox/index.js +91 -0
  15. package/sandbox/apps/sandbox/lib/staticAppealStages.js +189 -0
  16. package/sandbox/apps/sandbox/sections/summary-data-sections.js +40 -0
  17. package/sandbox/apps/sandbox/translations/src/en/fields.json +94 -0
  18. package/sandbox/apps/sandbox/translations/src/en/journey.json +3 -0
  19. package/sandbox/apps/sandbox/translations/src/en/pages.json +75 -0
  20. package/sandbox/apps/sandbox/translations/src/en/validation.json +50 -0
  21. package/sandbox/apps/sandbox/views/confirmation.html +15 -0
  22. package/sandbox/apps/sandbox/views/form-guidance-link.html +8 -0
  23. package/sandbox/assets/images/icons/icon-caret-left.png +0 -0
  24. package/sandbox/assets/images/icons/icon-complete.png +0 -0
  25. package/sandbox/assets/images/icons/icon-cross-remove-sign.png +0 -0
  26. package/sandbox/assets/js/index.js +70 -0
  27. package/sandbox/assets/scss/app.scss +27 -0
  28. package/sandbox/codecept.conf.js +15 -0
  29. package/sandbox/config.js +17 -0
  30. package/sandbox/package.json +21 -0
  31. package/sandbox/server.js +12 -0
  32. package/sandbox/yarn.lock +15 -0
package/.eslintignore CHANGED
@@ -1 +1 @@
1
- example/public/*
1
+ sandbox/public/*
package/README.md CHANGED
@@ -1629,13 +1629,11 @@ if a nonce value is provided by the version of HOF.
1629
1629
 
1630
1630
  ## Example
1631
1631
 
1632
- There is an example implmentation in '/example'. To run:
1632
+ There is an example implementation in [demo application](https://github.com/UKHomeOffice/home-office-forms-demo).
1633
1633
 
1634
- ```
1635
- cd example
1636
- npm install
1637
- npm start
1638
- ```
1634
+ ## Sandbox
1635
+
1636
+ There is a sandbox application for developers to test components directly in hof called [sandbox](/sandbox)
1639
1637
 
1640
1638
  # HOF FRONTEND TOOLKIT
1641
1639
  Set of common UI patterns/styles for HOF projects
@@ -4,7 +4,7 @@
4
4
  const cp = require('child_process');
5
5
 
6
6
  module.exports = (cmd, args) => new Promise((resolve, reject) => {
7
- if (process.env.HOF_EXAMPLE_APP === 'true') {
7
+ if (process.env.HOF_SANDBOX === 'true') {
8
8
  cmd = cmd.replace('node_modules/hof', '..');
9
9
  }
10
10
 
@@ -114,7 +114,7 @@ module.exports = config => {
114
114
  ignored.push(/(^|[\/\\])\../);
115
115
  }
116
116
 
117
- if (process.env.HOF_EXAMPLE_APP === 'true') {
117
+ if (process.env.HOF_SANDBOX === 'true') {
118
118
  const rootDir = require('path').resolve(__dirname, '../../../');
119
119
  ignored.push(`${rootDir}/frontend/govuk-template/govuk_template.html`);
120
120
  watchLocation = [rootDir, '.'];
@@ -60,6 +60,35 @@ module.exports = (key, opts) => {
60
60
  dayOptional = true;
61
61
  }
62
62
 
63
+ // take the 3 date parts, padding or defaulting
64
+ // to '01' if applic, then create a date value in the
65
+ // format YYYY-MM-DD. Save to req.body for processing
66
+ const preProcess = (req, res, next) => {
67
+ const parts = getParts(req.body, fields, key);
68
+ if (_.some(parts, part => part !== '')) {
69
+ if (dayOptional && parts.day === '') {
70
+ parts.day = '01';
71
+ } else {
72
+ parts.day = pad(parts.day);
73
+ }
74
+ if (monthOptional && parts.month === '') {
75
+ parts.month = '01';
76
+ } else {
77
+ parts.month = pad(parts.month);
78
+ }
79
+ req.body[key] = `${parts.year}-${parts.month}-${parts.day}`;
80
+ }
81
+ next();
82
+ };
83
+
84
+ // defaultFormatters on the base controller replace '--' with '-' on the process step.
85
+ // This ensures having the correct number of hyphens,
86
+ // so values do not jump from year to month when the page reloads.
87
+ const postProcess = (req, res, next) => {
88
+ req.form.values[key] = req.body[key];
89
+ next();
90
+ };
91
+
63
92
  // if date field is included in errorValues, extend
64
93
  // errorValues with the individual components
65
94
  const preGetErrors = (req, res, next) => {
@@ -125,35 +154,15 @@ module.exports = (key, opts) => {
125
154
  });
126
155
  };
127
156
 
128
- // take the 3 date parts, padding or defaulting
129
- // to '01' if applic, then create a date value in the
130
- // format YYYY-MM-DD. Save to req.body for processing
131
- const preProcess = (req, res, next) => {
132
- const parts = getParts(req.body, fields, key);
133
- if (_.some(parts, part => part !== '')) {
134
- if (dayOptional && parts.day === '') {
135
- parts.day = '01';
136
- } else {
137
- parts.day = pad(parts.day);
138
- }
139
- if (monthOptional && parts.month === '') {
140
- parts.month = '01';
141
- } else {
142
- parts.month = pad(parts.month);
143
- }
144
- req.body[key] = `${parts.year}-${parts.month}-${parts.day}`;
145
- }
146
- next();
147
- };
148
-
149
157
  // return config extended with hooks
150
158
  return Object.assign({}, options, {
151
159
  hooks: {
160
+ 'pre-process': preProcess,
161
+ 'post-process': postProcess,
152
162
  'pre-getErrors': preGetErrors,
153
163
  'post-getErrors': postGetErrors,
154
164
  'post-getValues': postGetValues,
155
- 'pre-render': preRender,
156
- 'pre-process': preProcess
165
+ 'pre-render': preRender
157
166
  }
158
167
  });
159
168
  };
@@ -46,12 +46,12 @@
46
46
  {{#intro-cookie-table}}
47
47
  {{> partials-table}}
48
48
  {{/intro-cookie-table}}
49
- <div class="js-enabled">
50
49
  <h2>{{session-cookies}}</h2>
51
50
  <p>{{session-cookies-content}}</p>
52
51
  {{#session-cookies-table}}
53
52
  {{> partials-session-cookies-table}}
54
53
  {{/session-cookies-table}}
54
+ <div class="js-enabled">
55
55
  <form>
56
56
  <div class="form-group">
57
57
  <fieldset>
@@ -77,14 +77,6 @@
77
77
  {{> partials-cookie-settings-button}}
78
78
  </form>
79
79
  </div>
80
- <div class="js-disabled">
81
- <h2>Cookie settings</h2>
82
- <p>We use JavaScript to set most of our cookies. Unfortunately JavaScript is not running on your browser, so you cannot change your settings. You can try:</p>
83
- <ul class="list-bullet">
84
- <li>reloading the page</li>
85
- <li>turning on JavaScript in your browser</li>
86
- </ul>
87
- </div>
88
80
  </div>
89
81
  {{/gaTagId}}
90
82
 
package/index.js CHANGED
@@ -112,7 +112,7 @@ const getContentSecurityPolicy = (config, res) => {
112
112
  * @param options {object} Configuration options for the HOF application
113
113
  * @param options.behaviours {object | Array<object>} The HOF behaviour(s) to invoke for all sub-applications
114
114
  * @param options.translations {string} The translations path for the application
115
- * @param options.routes {Array<object>} The sub-applications for this app: for example; require('./apps/example-app')
115
+ * @param options.routes {Array<object>} The sub-applications for this app: for sandbox; require('./apps/sandbox')
116
116
  * @param options.views {Array<string>} The view template paths for the application
117
117
  * @param options.middleware {Array<function>} An array of Express middleware functions to use
118
118
  * @param options.theme {string} Optional HOF theme - defaults to govuk
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "hof",
3
3
  "description": "A bootstrap for HOF projects",
4
- "version": "19.13.6",
4
+ "version": "19.13.9",
5
5
  "license": "MIT",
6
6
  "main": "index.js",
7
7
  "author": "HomeOffice",
@@ -22,7 +22,7 @@
22
22
  },
23
23
  "scripts": {
24
24
  "test": "yarn run unit && yarn run test:cookie-banner && yarn run test:functional && yarn run test:client && yarn run test:lint",
25
- "unit": "LOG_LEVEL=error nyc _mocha \"test/**/*.spec.js\" \"example/test/**/*.spec.js\"",
25
+ "unit": "LOG_LEVEL=error nyc _mocha \"test/**/*.spec.js\" \"sandbox/test/**/*.spec.js\"",
26
26
  "unit:nocov": "LOG_LEVEL=error mocha",
27
27
  "test:lint": "eslint . --config ./node_modules/eslint-config-hof/default.js",
28
28
  "test:functional": "funkie mocha ./test/functional-tests --exit",
@@ -30,7 +30,7 @@
30
30
  "test:cookie-banner": "jest test/frontend/jest",
31
31
  "test:acceptance": "TAGS=\"${TAGS:=@feature}\" yarn run test:cucumber",
32
32
  "test:acceptance_browser": "ACCEPTANCE_WITH_BROWSER=true TAGS=\"${TAGS:=@feature}\" yarn run test:cucumber",
33
- "test:cucumber": "cucumber-js -f @cucumber/pretty-formatter \"example/test/_features/**/*.feature\" --require example/test/_features/test.setup.js --require \"example/test/_features/step_definitions/**/*.js\" --tags $TAGS",
33
+ "test:cucumber": "cucumber-js -f @cucumber/pretty-formatter \"sandbox/test/_features/**/*.feature\" --require sandbox/test/_features/test.setup.js --require \"sandbox/test/_features/step_definitions/**/*.js\" --tags $TAGS",
34
34
  "ci": "travis-conditions",
35
35
  "postversion": "git push && git push --tags"
36
36
  },
@@ -0,0 +1,66 @@
1
+ # HOF Sandbox App
2
+
3
+ The Home Office Forms (HOF) Sandbox app is used by developers to test out different components in the framework.
4
+
5
+ #### How to run the app locally
6
+
7
+ Install [Homebrew](https://brew.sh/), if it is not installed
8
+
9
+ Once Homebrew is installed run
10
+
11
+ ```bash
12
+ brew install nvm
13
+ ```
14
+ ```bash
15
+ source ~/.bash_profile
16
+ ```
17
+
18
+ Install the correct version of node
19
+
20
+ ```bash
21
+ nvm install 14.15.0
22
+ ```
23
+
24
+ Set the node version
25
+
26
+ ```bash
27
+ nvm use 14.15.0
28
+ ```
29
+
30
+ Clone the service locally
31
+
32
+ ```bash
33
+ git clone ...
34
+ ```
35
+
36
+ Install yarn
37
+
38
+ ```bash
39
+ npm i yarn -g
40
+ ```
41
+
42
+ Install the dependencies
43
+
44
+ ```bash
45
+ yarn
46
+ ```
47
+
48
+ Move into the example folder
49
+
50
+ ```bash
51
+ cd example
52
+ ```
53
+
54
+ Install any example app specific dependencies
55
+
56
+ ```bash
57
+ yarn
58
+ ```
59
+
60
+ Run in development mode
61
+
62
+ ```bash
63
+ yarn start:dev
64
+ ```
65
+
66
+ go to http://localhost:8080/
@@ -0,0 +1,8 @@
1
+ 'use strict';
2
+
3
+ module.exports = SuperClass => class extends SuperClass {
4
+ getValues(req, res, next) {
5
+ req.sessionModel.reset();
6
+ this.successHandler(req, res, next);
7
+ }
8
+ };
@@ -0,0 +1,10 @@
1
+ module.exports = superclass => class extends superclass {
2
+ configure(req, res, next) {
3
+ const homeOfficeCountries = [''].concat(require('homeoffice-countries').allCountries);
4
+ req.form.options.fields.countrySelect.options = homeOfficeCountries.map(country => {
5
+ const labelString = country !== '' ? country : 'Please select a country';
6
+ return { label: labelString, value: country };
7
+ });
8
+ next();
9
+ }
10
+ };
@@ -0,0 +1,22 @@
1
+ 'use strict';
2
+
3
+ const validators = require('../../../../controller').validators;
4
+
5
+ module.exports = SuperClass => class extends SuperClass {
6
+ validateField(key, req) {
7
+ const code = req.sessionModel.get('countryCode');
8
+ const number = req.form.values['int-phone-number'];
9
+
10
+ const isValid = validators.internationalPhoneNumber(number, code);
11
+
12
+ if (!isValid) {
13
+ return new this.ValidationError('int-phone-number', {
14
+ key: 'int-phone-number',
15
+ type: 'internationalPhoneNumber',
16
+ redirect: undefined
17
+ });
18
+ }
19
+
20
+ return super.validateField(key, req);
21
+ }
22
+ };
@@ -0,0 +1,106 @@
1
+ /* eslint-disable */
2
+ 'use strict';
3
+
4
+ const dateComponent = require('../../../').components.date;
5
+ const staticAppealStages = require('./lib/staticAppealStages');
6
+
7
+ module.exports = {
8
+ 'landing-page-radio': {
9
+ mixin: 'radio-group',
10
+ validate: ['required'],
11
+ legend: {
12
+ className: 'visuallyhidden'
13
+ },
14
+ options: ['basic-form', 'complex-form', 'build-your-own-form']
15
+ },
16
+ name: {
17
+ validate: ['required', 'notUrl', { type: 'maxlength', arguments: 200 }],
18
+ },
19
+ dateOfBirth: dateComponent('dateOfBirth', {
20
+ validate: ['required', 'before', { type: 'after', arguments: ['1900'] }]
21
+ }),
22
+ building: {
23
+ validate: ['required', 'notUrl', { type: 'maxlength', arguments: 100 }]
24
+ },
25
+ street: {
26
+ validate: ['notUrl', { type: 'maxlength', arguments: 50 }],
27
+ labelClassName: 'visuallyhidden'
28
+ },
29
+ townOrCity: {
30
+ validate: ['required', 'notUrl',
31
+ { type: 'regex', arguments: /^([^0-9]*)$/ },
32
+ { type: 'maxlength', arguments: 100 }
33
+ ]
34
+ },
35
+ postcode: {
36
+ validate: ['required', 'postcode'],
37
+ formatter: ['removespaces', 'uppercase']
38
+ },
39
+ incomeTypes: {
40
+ mixin: 'checkbox-group',
41
+ labelClassName: 'visuallyhidden',
42
+ validate: ['required'],
43
+ options: [
44
+ 'salary',
45
+ 'universal_credit',
46
+ 'child_benefit',
47
+ 'housing_benefit',
48
+ 'other'
49
+ ]
50
+ },
51
+ countryOfHearing: {
52
+ mixin: 'radio-group',
53
+ validate: ['required'],
54
+ legend: {
55
+ className: 'visuallyhidden'
56
+ },
57
+ options: [
58
+ 'englandAndWales',
59
+ 'scotland',
60
+ 'northernIreland'
61
+ ]
62
+ },
63
+ email: {
64
+ validate: ['required', 'email']
65
+ },
66
+ phone: {
67
+ validate: ['required', 'internationalPhoneNumber']
68
+ },
69
+ 'int-phone-number': {
70
+ validate: ['required'],
71
+ labelClassName: 'visuallyhidden'
72
+ },
73
+ countrySelect: {
74
+ mixin: 'select',
75
+ className: ['typeahead'],
76
+ options:[''].concat(require('homeoffice-countries').allCountries),
77
+ legend: {
78
+ className: 'visuallyhidden'
79
+ },
80
+ validate: ['required']
81
+ },
82
+ complaintDetails: {
83
+ mixin: 'textarea',
84
+ labelClassName: 'visuallyhidden',
85
+ // we want to ignore default formatters as we want
86
+ // to preserve white space
87
+ 'ignore-defaults': true,
88
+ // apply the other default formatters
89
+ formatter: ['trim', 'hyphens'],
90
+ // attributes here are passed to the field element
91
+ validate: ['required', { type: 'maxlength', arguments: 5000 }],
92
+ attributes: [{
93
+ attribute: 'rows',
94
+ value: 8
95
+ }]
96
+ },
97
+ appealStages: {
98
+ mixin: 'select',
99
+ labelClassName: 'visuallyhidden',
100
+ validate: ['required'],
101
+ options: [{
102
+ value: '',
103
+ label: 'fields.appealStages.options.null'
104
+ }].concat(staticAppealStages.getstaticAppealStages())
105
+ }
106
+ }
@@ -0,0 +1,91 @@
1
+ /* eslint-disable */
2
+ 'use strict';
3
+
4
+ const CountrySelect = require('./behaviours/country-select')
5
+ const SummaryPageBehaviour = require('../../../').components.summary;
6
+ const InternationalPhoneNumber = require('./behaviours/international-number');
7
+
8
+ module.exports = {
9
+ name: 'sandbox',
10
+ steps: {
11
+ '/landing-page': {
12
+ fields: [
13
+ 'landing-page-radio'
14
+ ],
15
+ next: '/name',
16
+ forks: [{
17
+ target: '/build-your-own-form',
18
+ condition: {
19
+ field: 'landing-page-radio',
20
+ value: 'build-your-own-form'
21
+ }
22
+ }],
23
+ },
24
+ '/build-your-own-form': {
25
+ template: 'form-guidance-link'
26
+ },
27
+ '/name': {
28
+ fields: ['name'],
29
+ next: '/dob'
30
+ },
31
+ '/dob': {
32
+ fields: ['dateOfBirth'],
33
+ next: '/address'
34
+ },
35
+ '/address': {
36
+ fields: ['building', 'street', 'townOrCity', 'postcode'],
37
+ next: '/checkboxes'
38
+ },
39
+ '/checkboxes': {
40
+ fields: ['incomeTypes'],
41
+ next: '/radio'
42
+ },
43
+ '/radio':{
44
+ fields: ['countryOfHearing'],
45
+ forks: [{
46
+ target: '/country-select',
47
+ condition: {
48
+ field: 'landing-page-radio',
49
+ value: 'complex-form'
50
+ }
51
+ }],
52
+ next: '/email'
53
+ },
54
+ '/email': {
55
+ fields: ['email'],
56
+ next: '/phone-number'
57
+ },
58
+ '/phone-number': {
59
+ fields: ['phone'],
60
+ next: '/confirm'
61
+ },
62
+ '/country-select': {
63
+ behaviours: CountrySelect,
64
+ fields: ['countrySelect'],
65
+ next: '/text-input-area'
66
+ },
67
+ '/text-input-area': {
68
+ fields: ['complaintDetails'],
69
+ next: '/select'
70
+ },
71
+ '/select':{
72
+ fields: ['appealStages'],
73
+ next: '/confirm'
74
+ },
75
+ '/confirm': {
76
+ behaviours: [SummaryPageBehaviour, 'complete'],
77
+ sections: require('./sections/summary-data-sections'),
78
+ next: '/confirmation'
79
+ },
80
+ '/confirmation': {
81
+ backLink: false
82
+ },
83
+ '/international-phone-number': {
84
+ behaviours: InternationalPhoneNumber,
85
+ fields: [
86
+ 'int-phone-number'
87
+ ],
88
+ next: '/confirm'
89
+ },
90
+ }
91
+ };
@@ -0,0 +1,189 @@
1
+ /* eslint max-len: 0 */
2
+
3
+ 'use strict';
4
+
5
+ module.exports = {
6
+ getstaticAppealStages: function () {
7
+ return [
8
+ { value: 'FT_IC',
9
+ label: '01. First Tier IAC Appeal - In Country Appeals',
10
+ timeLimit: {value: 14, type: 'calendar days'},
11
+ startDateLabel: 'Date refusal letter sent by HO',
12
+ rules: 'The Tribunal Procedure (First-tier tribunal) (Immigration and Asylum Chamber) Rules 2014',
13
+ ruleNumber: '19(2)',
14
+ adminAllowance: {value: 1, type: 'working day'},
15
+ country: 'All',
16
+ trigger: 'Refusal Notice',
17
+ sortCode: 10
18
+ },
19
+
20
+ { value: 'FT_OOC_1',
21
+ label: '02. First Tier IAC Appeal - Out of Country Appeals where the appellant must leave the UK before appealing',
22
+ timeLimit: {value: 28, type: 'calendar days'},
23
+ startDateLabel: 'Appellant\'s date of departure',
24
+ rules: 'The Tribunal Procedure (First-tier tribunal) (Immigration and Asylum Chamber) Rules 2014',
25
+ ruleNumber: '19(3)(a)',
26
+ adminAllowance: {value: 0, type: 'working days'},
27
+ country: 'All',
28
+ trigger: 'Refusal Notice',
29
+ sortCode: 20
30
+ },
31
+
32
+ { value: 'FT_OOC_2',
33
+ label: '03. First Tier IAC Appeal - Other out of Country Appeals (e.g Voluntary Departure)',
34
+ timeLimit: {value: 28, type: 'calendar days'},
35
+ startDateLabel: 'Date of receipt of the refusal letter',
36
+ rules: 'The Tribunal Procedure (First-tier tribunal) (Immigration and Asylum Chamber) Rules 2014',
37
+ ruleNumber: '19(3)(b)',
38
+ adminAllowance: {value: 1, type: 'working days'},
39
+ country: 'All',
40
+ trigger: 'Refusal Notice',
41
+ sortCode: 30
42
+ },
43
+
44
+ { value: 'FT_IC_FAST',
45
+ label: '04. First Tier IAC Appeal - In Country Detained Fast Track',
46
+ timeLimit: {value: 2, type: 'working days'},
47
+ startDateLabel: 'Date when appellant was provided notice of decision',
48
+ rules: 'The Tribunal Procedure (First-tier tribunal) (Immigration and Asylum Chamber) Rules 2014 Schedule The Fast Track Rules',
49
+ ruleNumber: '5(1)',
50
+ adminAllowance: {value: 1, type: 'working day'},
51
+ country: 'All',
52
+ trigger: 'Refusal Notice',
53
+ sortCode: 40
54
+ },
55
+
56
+ { value: 'FT_UT_IC',
57
+ label: '05. First Tier IAC PTA to the UT - In Country',
58
+ timeLimit: {value: 14, type: 'calendar days'},
59
+ startDateLabel: 'Date written reasons for the decision sent',
60
+ rules: 'The Tribunal Procedure (First-tier tribunal) (Immigration and Asylum Chamber) Rules 2014',
61
+ ruleNumber: '33(2)',
62
+ adminAllowance: {value: 1, type: 'working day'},
63
+ country: 'All',
64
+ trigger: 'IA60 first tier dismissed',
65
+ sortCode: 50
66
+ },
67
+
68
+ { value: 'FT_UT_OOC',
69
+ label: '06. First Tier IAC PTA to the UT - Out of Country',
70
+ timeLimit: {value: 28, type: 'calendar days'},
71
+ startDateLabel: 'Date written reasons for the decision sent',
72
+ rules: 'The Tribunal Procedure (First-tier tribunal) (Immigration and Asylum Chamber) Rules 2014',
73
+ ruleNumber: '33(3)',
74
+ adminAllowance: {value: 1, type: 'working day'},
75
+ country: 'All',
76
+ trigger: 'IA60 first tier dismissed',
77
+ sortCode: 60
78
+ },
79
+
80
+ { value: 'FT_UT_FAST',
81
+ label: '07. First Tier IAC PTA to the UT - Detained Fast Track',
82
+ timeLimit: {value: 3, type: 'working days'},
83
+ startDateLabel: 'Date when appellant was provided notice of decision',
84
+ rules: 'The Tribunal Procedure (First-tier tribunal) (Immigration and Asylum Chamber) Rules 2014 Schedule The Fast Track Rules',
85
+ ruleNumber: '11',
86
+ adminAllowance: {value: 1, type: 'working day'},
87
+ country: 'All',
88
+ trigger: 'IA60 first tier dismissed',
89
+ sortCode: 70
90
+ },
91
+
92
+ { value: 'UT_IC',
93
+ label: '08. Upper Tribunal IAC PTA - In Country',
94
+ timeLimit: {value: 14, type: 'calendar days'},
95
+ startDateLabel: 'Date when the FtT refused FTPA',
96
+ rules: 'The Tribunal Procedure (Upper Tribunal) Rules 2008',
97
+ ruleNumber: '21(3)(a)(aa)(i)',
98
+ adminAllowance: {value: 1, type: 'working day'},
99
+ country: 'All',
100
+ trigger: 'IA67 first tier permission to appeal refused',
101
+ sortCode: 80
102
+ },
103
+
104
+ { value: 'UT_OOC',
105
+ label: '09. Upper Tribunal IAC PTA - Out of Country',
106
+ timeLimit: {value: 1, type: 'calendar month'},
107
+ startDateLabel: 'Date when the FtT refused FTPA',
108
+ rules: 'The Tribunal Procedure (Upper Tribunal) Rules 2008',
109
+ ruleNumber: '21(3)(b)',
110
+ adminAllowance: {value: 1, type: 'working day'},
111
+ country: 'All',
112
+ trigger: 'IA67 first tier permission to appeal refused',
113
+ sortCode: 90
114
+ },
115
+
116
+ { value: 'UT_IAC_JR',
117
+ label: '10. Upper Tribunal IAC - Judicial Review',
118
+ timeLimit: {value: 3, type: 'calendar months'},
119
+ startDateLabel: 'Date of decision of the First Tier Tribunal or Home Office',
120
+ rules: 'The Tribunal Procedure (Upper Tribunal) Rules 2008',
121
+ ruleNumber: '28(2)',
122
+ adminAllowance: {value: 0, type: 'working days'},
123
+ country: 'All',
124
+ trigger: 'n/a',
125
+ sortCode: 100
126
+ },
127
+
128
+ { value: 'UT_IAC_IC',
129
+ label: '11. Upper Tribunal IAC - In Country PTA to review UT determination',
130
+ timeLimit: {value: 12, type: 'calendar days'},
131
+ startDateLabel: 'Date when appellant was sent notice of decision',
132
+ rules: 'The Tribunal Procedure (Upper Tribunal) Rules 2008',
133
+ ruleNumber: '44(3B)(a)(i)',
134
+ adminAllowance: {value: 1, type: 'working day'},
135
+ country: 'All',
136
+ trigger: 'IA150 - Upper Tier determination (dismissed)',
137
+ sortCode: 110
138
+ },
139
+
140
+ { value: 'UT_IAC_OOC',
141
+ label: '12. Upper Tribunal IAC - Out of Country PTA to review UT determination',
142
+ timeLimit: {value: 38, type: 'calendar days'},
143
+ startDateLabel: 'Date when appellant was sent notice of decision',
144
+ rules: 'The Tribunal Procedure (Upper Tribunal) Rules 2008',
145
+ ruleNumber: '44(3B)(b)',
146
+ adminAllowance: {value: 1, type: 'working day'},
147
+ country: 'All',
148
+ trigger: 'IA150 - Upper Tier determination (dismissed)',
149
+ sortCode: 120
150
+ },
151
+
152
+ { value: 'COA_IAC',
153
+ label: '13. Court of Appeal via IAC',
154
+ timeLimit: {value: 28 + 2, type: 'calendar days'},
155
+ startDateLabel: 'Date when appellant was sent notice of decision',
156
+ rules: 'Civil Procedure Rules',
157
+ ruleNumber: '52.4',
158
+ adminAllowance: {value: 1, type: 'working days'},
159
+ country: ['England & Wales'],
160
+ trigger: 'IA157 PTA to the CoA refused',
161
+ sortCode: 130
162
+ },
163
+
164
+ { value: 'COS_IAC',
165
+ label: '14. Court of Sessions via IAC',
166
+ timeLimit: {value: 42 + 1, type: 'calendar days'},
167
+ startDateLabel: 'Date when appellant was sent notice of decision',
168
+ rules: 'Civil Procedure Rules',
169
+ ruleNumber: 'Rule 41.20',
170
+ adminAllowance: {value: 1, type: 'working days'},
171
+ country: ['Scotland', 'Northern Ireland'],
172
+ trigger: 'IA157 PTA to the CoA refused',
173
+ sortCode: 140
174
+ },
175
+
176
+ { value: 'COA_DIRECT',
177
+ label: '15. Court of Appeal Direct',
178
+ timeLimit: {value: 7 + 2, type: 'calendar days'},
179
+ startDateLabel: 'Date when appellant was sent notice of decision',
180
+ rules: 'Civil Procedure Rules',
181
+ ruleNumber: '52.3',
182
+ adminAllowance: {value: 1, type: 'working days'},
183
+ country: 'All',
184
+ trigger: 'IA157 PTA to the CoA refused',
185
+ sortCode: 150
186
+ }
187
+ ];
188
+ }
189
+ };
@@ -0,0 +1,40 @@
1
+ const moment = require('moment');
2
+ const PRETTY_DATE_FORMAT = 'Do MMMM YYYY';
3
+ const APPEAL_STAGES = require('../lib/staticAppealStages').getstaticAppealStages();
4
+ const _ = require('lodash');
5
+
6
+ module.exports = {
7
+ applicantsDetails: [
8
+ 'name',
9
+ {
10
+ field: 'dateOfBirth',
11
+ parse: d => d && moment(d).format(PRETTY_DATE_FORMAT)
12
+ }
13
+ ],
14
+ address: [
15
+ 'building',
16
+ 'street',
17
+ 'townOrCity',
18
+ 'postcode'
19
+ ],
20
+ income: [
21
+ 'incomeTypes'
22
+ ],
23
+ appealDetails: [
24
+ 'countryOfHearing',
25
+ {
26
+ field: 'appealStages',
27
+ parse: v => _.get(_.find(APPEAL_STAGES, stage => stage.value === v), 'label', '')
28
+ }
29
+ ],
30
+ contactDetails: [
31
+ 'email',
32
+ 'phone'
33
+ ],
34
+ countrySelect: [
35
+ 'countrySelect'
36
+ ],
37
+ complaintDetails: [
38
+ 'complaintDetails'
39
+ ]
40
+ };
@@ -0,0 +1,94 @@
1
+ {
2
+ "landing-page-radio": {
3
+ "legend": "Which form would you like to explore?",
4
+ "options": {
5
+ "basic-form": {
6
+ "label": "Basic form"
7
+ },
8
+ "complex-form": {
9
+ "label": "Complex form"
10
+ },
11
+ "build-your-own-form": {
12
+ "label": "Build your own form"
13
+ }
14
+ }
15
+ },
16
+ "name": {
17
+ "label": "Full name"
18
+ },
19
+ "dateOfBirth": {
20
+ "legend": "Date of birth",
21
+ "hint": "For example, 31 10 1990"
22
+ },
23
+ "building": {
24
+ "label": "Building and street"
25
+ },
26
+ "street": {
27
+ "label": "Address line 2"
28
+ },
29
+ "townOrCity": {
30
+ "label": "Town or city"
31
+ },
32
+ "postcode": {
33
+ "label": "Postcode"
34
+ },
35
+ "incomeTypes" : {
36
+ "label": "Sources of income",
37
+ "legend": "Select the options where you receive income from",
38
+ "hint": "Select all options that apply to you.",
39
+ "options": {
40
+ "salary": {
41
+ "label": "Salary"
42
+ },
43
+ "universal_credit": {
44
+ "label": "Universal Credit"
45
+ },
46
+ "child_benefit": {
47
+ "label": "Child Benefit"
48
+ },
49
+ "housing_benefit": {
50
+ "label": "Housing Benefit"
51
+ },
52
+ "other": {
53
+ "label": "Other"
54
+ }
55
+ }
56
+ },
57
+ "countryOfHearing": {
58
+ "label": "Country of hearing",
59
+ "options": {
60
+ "englandAndWales": {
61
+ "label": "England and Wales"
62
+ },
63
+ "scotland": {
64
+ "label": "Scotland"
65
+ },
66
+ "northernIreland": {
67
+ "label": "Northern Ireland"
68
+ }
69
+ }
70
+ },
71
+ "email" : {
72
+ "label": "Email address"
73
+ },
74
+ "phone": {
75
+ "label": "Phone number",
76
+ "hint": "International phone numbers require the international dialling code, for example +33235066182"
77
+ },
78
+ "int-phone-number": {
79
+ "legend": "International phone number"
80
+ },
81
+ "complaintDetails": {
82
+ "label": "Complaint details"
83
+ },
84
+ "countrySelect": {
85
+ "label": "Select a country",
86
+ "hint": "Start to type the country name and options will appear"
87
+ },
88
+ "appealStages": {
89
+ "label": "Appeal stage",
90
+ "options": {
91
+ "null": "Select..."
92
+ }
93
+ }
94
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "header": "HOF Bootstrap Sandbox Form"
3
+ }
@@ -0,0 +1,75 @@
1
+ {
2
+ "landing-page": {
3
+ "header": "Landing page",
4
+ "intro": "Choose one of the options below and press continue."
5
+ },
6
+ "build-your-own-form": {
7
+ "title": "Build your own form",
8
+ "subheader": "Access the build your own form guidance link"
9
+ },
10
+ "name": {
11
+ "header": "What is your full name?"
12
+ },
13
+ "dob": {
14
+ "header": "What is your date of birth?"
15
+ },
16
+ "address": {
17
+ "header": "What is your address in the UK?",
18
+ "intro": "If you have no fixed address, enter an address where we can contact you."
19
+ },
20
+ "checkboxes": {
21
+ "header": "Where does your money come from each month?"
22
+ },
23
+ "radio": {
24
+ "header": "What country was the appeal lodged?"
25
+ },
26
+ "country-select": {
27
+ "header": "What country is your address located?"
28
+ },
29
+ "email": {
30
+ "header": "Enter your email address"
31
+ },
32
+ "phone-number": {
33
+ "header": "Enter your phone number"
34
+ },
35
+ "text-input-area": {
36
+ "header": "What are the details of your complaint?",
37
+ "intro": "Briefly summarise your complaint. Include anything that can help our investigation."
38
+ },
39
+ "select":{
40
+ "header": "What is the appeal stage?",
41
+ "intro": "Choose an appeal stage from the drop down menu"
42
+ },
43
+ "confirm": {
44
+ "header": "Check your answers before submitting your application.",
45
+ "sections": {
46
+ "applicantsDetails": {
47
+ "header": "Applicant's details"
48
+ },
49
+ "address": {
50
+ "header": "Address"
51
+ },
52
+ "income": {
53
+ "header": "Income"
54
+ },
55
+ "appealDetails": {
56
+ "header": "Appeal details"
57
+ },
58
+ "countrySelect": {
59
+ "header": "Country of residence"
60
+ },
61
+ "contactDetails": {
62
+ "header": "Contact details"
63
+ },
64
+ "complaintDetails": {
65
+ "header": "Complaint details"
66
+ }
67
+ }
68
+ },
69
+ "confirmation": {
70
+ "title": "Application sent",
71
+ "alert": "Application sent",
72
+ "subheader": "What happens next",
73
+ "content": "We’ll contact you with the decision of your application or if we need more information from you."
74
+ }
75
+ }
@@ -0,0 +1,50 @@
1
+ {
2
+ "landing-page-radio": {
3
+ "required": "Select an option below and press continue"
4
+ },
5
+ "name":{
6
+ "default": "Enter your full name"
7
+ },
8
+ "dateOfBirth": {
9
+ "default": "Enter your date of birth in the correct format; for example, 31 10 1990",
10
+ "after": "Enter a date after 1 1 1900",
11
+ "before": "Enter a date that is in the past"
12
+ },
13
+ "building": {
14
+ "default": "Enter details of your building and street"
15
+ },
16
+ "townOrCity": {
17
+ "default": "Enter a town or city",
18
+ "regex": "Enter a town or city without including digits"
19
+ },
20
+ "postcode": {
21
+ "default": "Enter your postcode"
22
+ },
23
+ "incomeTypes": {
24
+ "default": "Select all options that apply to you."
25
+ },
26
+ "countryOfHearing": {
27
+ "default": "Select where the appeal hearing is to be held"
28
+ },
29
+ "countrySelect": {
30
+ "default": "Enter a valid country of residence",
31
+ "required": "Enter your country of residence"
32
+ },
33
+ "email": {
34
+ "default": "Enter your email address in the correct format"
35
+ },
36
+ "phone": {
37
+ "default": "Enter your phone number"
38
+ },
39
+ "int-phone-number": {
40
+ "required": "Enter an international phone number",
41
+ "internationalPhoneNumber": "Enter a valid international phone number"
42
+ },
43
+ "complaintDetails": {
44
+ "default": "Enter details about why you are making a complaint",
45
+ "maxlength": "Keep to the 5000 character limit"
46
+ },
47
+ "appealStages": {
48
+ "required": "Select an appeal stage from the list"
49
+ }
50
+ }
@@ -0,0 +1,15 @@
1
+ <title>{{#t}}pages.confirmation.title{{/t}} – GOV.UK</title>
2
+
3
+ {{<partials-page}}
4
+ {{$page-content}}
5
+ <div class="alert-complete" role="alert">
6
+ <span class="icon icon-complete" role="presentation"></span>
7
+ <div class="alert-message">
8
+ <h1>{{#t}}pages.confirmation.alert{{/t}}</h1>
9
+ </div>
10
+ </div>
11
+ <h2>{{#t}}pages.confirmation.subheader{{/t}}</h2>
12
+ <p>{{#t}}pages.confirmation.content{{/t}}</p>
13
+ <a href="/landing-page" class="button" role="button">{{#t}}buttons.start-again{{/t}}</a>
14
+ {{/page-content}}
15
+ {{/partials-page}}
@@ -0,0 +1,8 @@
1
+ <title>{{#t}}pages.build-your-own-form.title{{/t}} – GOV.UK</title>
2
+
3
+ {{<partials-page}}
4
+ {{$page-content}}
5
+ <h2>{{#t}}pages.build-your-own-form.subheader{{/t}}</h2>
6
+ <a href="https://ukhomeofficeforms.github.io/hof-guide/documentation/#building-your-first-hof-form">Link to HOF form guidance</a>
7
+ {{/page-content}}
8
+ {{/partials-page}}
@@ -0,0 +1,70 @@
1
+ /* eslint-disable */
2
+ 'use strict'
3
+
4
+ require('../../../frontend/themes/gov-uk/client-js');
5
+
6
+ var $ = require('jquery');
7
+ var typeahead = require('typeahead-aria');
8
+ var Bloodhound = require('typeahead-aria').Bloodhound;
9
+
10
+ typeahead.loadjQueryPlugin();
11
+
12
+ $('.typeahead').each(function applyTypeahead() {
13
+ var $el = $(this);
14
+ var $parent = $el.parent();
15
+ var attributes = $el.prop('attributes');
16
+ var $input = $('<input/>');
17
+ var selectedValue = $el.val();
18
+ var typeaheadList = $el.find('option').map(function mapOptions() {
19
+ if (this.value === '') {
20
+ return undefined;
21
+ }
22
+ return this.value;
23
+ }).get();
24
+
25
+ // remove the selectbox
26
+ $el.remove();
27
+
28
+ $.each(attributes, function applyAttributes() {
29
+ $input.attr(this.name, this.value);
30
+ });
31
+
32
+ $input.removeClass('js-hidden');
33
+ $input.addClass('form-control');
34
+ $input.val(selectedValue);
35
+
36
+ $parent.append($input);
37
+
38
+ $input.typeahead({
39
+ hint: false
40
+ }, {
41
+ source: new Bloodhound({
42
+ datumTokenizer: Bloodhound.tokenizers.whitespace,
43
+ queryTokenizer: Bloodhound.tokenizers.whitespace,
44
+ local: typeaheadList,
45
+ sorter: function sorter(a, b) {
46
+ var input = $input.val();
47
+ var startsWithInput = function startsWithInput(x) {
48
+ return x.toLowerCase().substr(0, input.length) === input.toLowerCase() ? -1 : 1;
49
+ };
50
+
51
+ var compareAlpha = function compareAlpha(x, y) {
52
+ var less = x < y ? -1 : 1;
53
+ return x === y ? 0 : less;
54
+ };
55
+
56
+ var compareStartsWithInput = function compareStartsWithInput(x, y) {
57
+ var startsWithFirst = startsWithInput(x);
58
+ var startsWithSecond = startsWithInput(y);
59
+
60
+ return startsWithFirst === startsWithSecond ? 0 : startsWithFirst;
61
+ };
62
+
63
+ var first = compareStartsWithInput(a, b);
64
+
65
+ return first === 0 ? compareAlpha(a, b) : first;
66
+ }
67
+ }),
68
+ limit: 100
69
+ });
70
+ });
@@ -0,0 +1,27 @@
1
+
2
+ @import "../../../frontend/themes/gov-uk/styles/govuk";
3
+
4
+ //autocomplete styling
5
+ .tt-menu {
6
+ background-color: #fff;
7
+ border: 1px solid #ccc;
8
+ width: 65%;
9
+ }
10
+
11
+ .tt-suggestion {
12
+ padding: 0.5em;
13
+
14
+ &:hover,
15
+ &.tt-cursor {
16
+ color: #fff;
17
+ background-color: #0097cf;
18
+ }
19
+
20
+ &:hover {
21
+ cursor: pointer;
22
+ }
23
+ }
24
+
25
+ .twitter-typeahead {
26
+ width: 100%;
27
+ }
@@ -0,0 +1,15 @@
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+
5
+ const pagesPath = page => path.resolve(__dirname,
6
+ `./apps/sandbox/acceptance/pages/${page}`);
7
+
8
+ module.exports = {
9
+ name: 'sandbox',
10
+ include: {
11
+ firstPage: pagesPath('first-step.js'),
12
+ secondPage: pagesPath('second-step.js'),
13
+ thirdPage: pagesPath('third-step.js')
14
+ }
15
+ };
@@ -0,0 +1,17 @@
1
+ 'use strict';
2
+
3
+ /* eslint no-process-env: 0 */
4
+ module.exports = {
5
+ email: {
6
+ caseworker: process.env.CASEWORKER_EMAIL || '',
7
+ from: process.env.FROM_ADDRESS || '',
8
+ replyTo: process.env.REPLY_TO || '',
9
+ accessKeyId: process.env.AWS_USER || '',
10
+ secretAccessKey: process.env.AWS_PASSWORD || '',
11
+ transportType: 'ses',
12
+ region: process.env.EMAIL_REGION || ''
13
+ },
14
+ hosts: {
15
+ acceptanceTests: process.env.ACCEPTANCE_HOST_NAME || `http://localhost:${process.env.PORT || 8080}`
16
+ }
17
+ };
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "hof-bootstrap-example-app",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "engines": {
7
+ "node": ">=10.22.1"
8
+ },
9
+ "scripts": {
10
+ "start": "node server.js",
11
+ "start:dev": "HOF_SANDBOX=true ../bin/hof-build watch",
12
+ "build": "HOF_SANDBOX=true ../bin/hof-build",
13
+ "postinstall": "yarn run build"
14
+ },
15
+ "author": "",
16
+ "dependencies": {
17
+ "jquery": "^3.6.0",
18
+ "typeahead-aria": "^1.0.4"
19
+ },
20
+ "devDependencies": {}
21
+ }
@@ -0,0 +1,12 @@
1
+ /* eslint-disable */
2
+ 'use strict';
3
+
4
+ const bootstrap = require('../');
5
+
6
+ bootstrap({
7
+ translations: './apps/sandbox/translations',
8
+ routes: [
9
+ require('./apps/sandbox')
10
+ ],
11
+ getAccessibility: true
12
+ });
@@ -0,0 +1,15 @@
1
+ # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2
+ # yarn lockfile v1
3
+
4
+
5
+ jquery@>=1.11, jquery@^3.6.0:
6
+ version "3.6.0"
7
+ resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.0.tgz#c72a09f15c1bdce142f49dbf1170bdf8adac2470"
8
+ integrity sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==
9
+
10
+ typeahead-aria@^1.0.4:
11
+ version "1.0.4"
12
+ resolved "https://registry.yarnpkg.com/typeahead-aria/-/typeahead-aria-1.0.4.tgz#d2829cc01c0226a367627f3098a468c4e0343f00"
13
+ integrity sha1-0oKcwBwCJqNnYn8wmKRoxOA0PwA=
14
+ dependencies:
15
+ jquery ">=1.11"