hof 24.5.0 → 24.6.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/CHANGELOG.md +23 -0
- package/components/notify/notify.js +17 -16
- package/controller/validation/validators.js +9 -6
- package/frontend/template-mixins/mixins/helpers.js +2 -2
- package/frontend/template-partials/translations/src/cy/cookies.json +14 -2
- package/frontend/themes/gov-uk/client-js/session-timeout-dialog.js +61 -37
- package/middleware/rate-limiter.js +6 -6
- package/package.json +28 -33
- package/sandbox/apps/sandbox/sections/summary-data-sections.js +2 -2
- package/sandbox/assets/js/index.js +11 -64
- package/sandbox/assets/scss/app.scss +1 -26
- package/sandbox/assets/scss/autocomplete.scss +143 -0
- package/sandbox/package.json +2 -6
- package/sandbox/yarn.lock +15 -22
- package/utilities/date.js +14 -0
- package/utilities/index.js +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
## 2026-10-07 Version 24.5.2 (Stable), @vivekkumar-ho
|
|
2
|
+
|
|
3
|
+
### Fixes
|
|
4
|
+
- Preserved original Notify errors and their response payloads, and removed duplicate failure logging.
|
|
5
|
+
|
|
6
|
+
### Chore & Maintenance
|
|
7
|
+
- Replaced Notify email reference generation with Node.js `crypto.randomUUID`.
|
|
8
|
+
- Added Notify coverage for email payloads, UUID references, attachments, and error propagation and logging.
|
|
9
|
+
|
|
10
|
+
## 2026-09-14 Version 24.5.1 (Stable), @nzorba @dk4g @dalglishdhandaHO
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- Removed jQuery from the session timeout dialog and replaced its DOM operations with native browser APIs.
|
|
14
|
+
- Removed the jQuery dependency and updated frontend tests to use native DOM APIs and `fetch`.
|
|
15
|
+
- Migrated from `momentjs` to `dayjs`
|
|
16
|
+
|
|
17
|
+
### Fixes
|
|
18
|
+
- Added missing Welsh cookie page translation entries to match the English cookie configuration ((#710)[https://github.com/UKHomeOfficeForms/hof/pull/710])
|
|
19
|
+
|
|
20
|
+
### Chore & Maintenance
|
|
21
|
+
- Removed the vulnerable PhantomJS-based functional test driver stack and migrated the functional test browser runner to Playwright ((#699)[https://github.com/UKHomeOfficeForms/hof/pull/699])
|
|
22
|
+
- Updated node and Redis versions in testing matrix ((#700)[https://github.com/UKHomeOfficeForms/hof/pull/700])
|
|
23
|
+
|
|
1
24
|
## 2026-07-01, Version 24.4.0 (Stable), @vivekkumar-ho
|
|
2
25
|
|
|
3
26
|
### Security
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const NotifyClient = require('notifications-node-client').NotifyClient;
|
|
4
|
-
const {
|
|
4
|
+
const { randomUUID } = require('node:crypto');
|
|
5
5
|
const config = require('../../config/hof-defaults');
|
|
6
6
|
const logger = require('../../lib/logger');
|
|
7
7
|
|
|
@@ -10,41 +10,42 @@ module.exports = class Notify {
|
|
|
10
10
|
const options = opts || {};
|
|
11
11
|
this.options = options;
|
|
12
12
|
this.logger = logger(config);
|
|
13
|
-
this.notifyClient =
|
|
13
|
+
this.notifyClient = new NotifyClient(options.notifyApiKey);
|
|
14
14
|
this.notifyTemplate = options.notifyTemplate;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
async sendEmail(templateId, recipient, personalisation) {
|
|
18
|
-
const reference =
|
|
18
|
+
const reference = randomUUID();
|
|
19
19
|
|
|
20
20
|
try {
|
|
21
21
|
await this.notifyClient.sendEmail(templateId, recipient, {
|
|
22
22
|
personalisation: personalisation,
|
|
23
|
-
reference
|
|
23
|
+
reference
|
|
24
|
+
});
|
|
24
25
|
this.logger.log('info', 'Email sent');
|
|
25
26
|
} catch (error) {
|
|
26
|
-
this.logger.log(
|
|
27
|
-
|
|
27
|
+
this.logger.log(
|
|
28
|
+
'error',
|
|
29
|
+
error.response ? error.response.data : error.message
|
|
30
|
+
);
|
|
31
|
+
// Preserve the original Notify error and its response payload.
|
|
32
|
+
throw error;
|
|
28
33
|
}
|
|
29
34
|
}
|
|
30
35
|
|
|
31
36
|
async send(email) {
|
|
32
|
-
|
|
37
|
+
const personalisation = {
|
|
33
38
|
'email-subject': email.subject,
|
|
34
39
|
'email-body': email.body
|
|
35
40
|
};
|
|
36
41
|
|
|
37
|
-
if (email.attachment
|
|
38
|
-
personalisation =
|
|
39
|
-
|
|
42
|
+
if (email.attachment) {
|
|
43
|
+
personalisation['email-attachment'] = this.notifyClient.prepareUpload(
|
|
44
|
+
email.attachment
|
|
45
|
+
);
|
|
40
46
|
}
|
|
41
47
|
|
|
42
|
-
|
|
43
|
-
await this.sendEmail(this.notifyTemplate, email.recipient, personalisation);
|
|
44
|
-
} catch (error) {
|
|
45
|
-
this.logger.log('error', error.response ? error.response.data : error.message);
|
|
46
|
-
throw new Error(error.response ? error.response.data : error.message);
|
|
47
|
-
}
|
|
48
|
+
await this.sendEmail(this.notifyTemplate, email.recipient, personalisation);
|
|
48
49
|
}
|
|
49
50
|
};
|
|
50
51
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const dayjs = require('dayjs');
|
|
4
|
+
const customParseFormat = require('dayjs/plugin/customParseFormat');
|
|
4
5
|
const _ = require('lodash');
|
|
5
6
|
const libPhoneNumber = require('libphonenumber-js/max');
|
|
6
7
|
const deprecate = require('deprecate');
|
|
@@ -11,6 +12,8 @@ const emailValidator = require('./email');
|
|
|
11
12
|
const dateFormat = 'YYYY-MM-DD';
|
|
12
13
|
let Validators;
|
|
13
14
|
|
|
15
|
+
dayjs.extend(customParseFormat);
|
|
16
|
+
|
|
14
17
|
module.exports = Validators = {
|
|
15
18
|
|
|
16
19
|
string(value) {
|
|
@@ -113,7 +116,7 @@ module.exports = Validators = {
|
|
|
113
116
|
},
|
|
114
117
|
|
|
115
118
|
date(value) {
|
|
116
|
-
return value === '' || Validators.regex(value, /\d{4}\-\d{2}\-\d{2}/) &&
|
|
119
|
+
return value === '' || Validators.regex(value, /\d{4}\-\d{2}\-\d{2}/) && dayjs(value, dateFormat, true).isValid();
|
|
117
120
|
},
|
|
118
121
|
|
|
119
122
|
'date-year'(value) {
|
|
@@ -131,12 +134,12 @@ module.exports = Validators = {
|
|
|
131
134
|
// eslint-disable-next-line no-inline-comments, spaced-comment
|
|
132
135
|
before(value, date) {
|
|
133
136
|
// validator can also do before(value, [diff, unit][, diff, unit])
|
|
134
|
-
let valueDate =
|
|
137
|
+
let valueDate = dayjs(value, dateFormat, true);
|
|
135
138
|
let comparator;
|
|
136
139
|
if (arguments.length === 2) {
|
|
137
140
|
comparator = date;
|
|
138
141
|
} else {
|
|
139
|
-
comparator =
|
|
142
|
+
comparator = dayjs();
|
|
140
143
|
const args = [].slice.call(arguments, 1);
|
|
141
144
|
let diff;
|
|
142
145
|
let unit;
|
|
@@ -151,12 +154,12 @@ module.exports = Validators = {
|
|
|
151
154
|
|
|
152
155
|
after(value, date) {
|
|
153
156
|
// validator can also do after(value, [diff, unit][, diff, unit])
|
|
154
|
-
let valueDate =
|
|
157
|
+
let valueDate = dayjs(value, dateFormat, true);
|
|
155
158
|
let comparator;
|
|
156
159
|
if (arguments.length === 2) {
|
|
157
160
|
comparator = date;
|
|
158
161
|
} else {
|
|
159
|
-
comparator =
|
|
162
|
+
comparator = dayjs();
|
|
160
163
|
const args = [].slice.call(arguments, 1);
|
|
161
164
|
let diff;
|
|
162
165
|
let unit;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
const querystring = require('querystring');
|
|
5
5
|
const path = require('path');
|
|
6
|
-
const
|
|
6
|
+
const { formatDate } = require('../../../utilities/date');
|
|
7
7
|
|
|
8
8
|
const renderer = require('./render');
|
|
9
9
|
|
|
@@ -33,7 +33,7 @@ module.exports = options => (req, res, next) => {
|
|
|
33
33
|
return function (txt) {
|
|
34
34
|
txt = (txt || '').split('|');
|
|
35
35
|
const value = hoganRender(txt[0], this);
|
|
36
|
-
return
|
|
36
|
+
return formatDate(value, txt[1]);
|
|
37
37
|
};
|
|
38
38
|
};
|
|
39
39
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"banner": {
|
|
3
|
-
"message": "
|
|
4
|
-
"link": "
|
|
3
|
+
"message": "Rydym yn defnyddio rhai cwcis hanfodol i wneud i'r wefan hon weithio.",
|
|
4
|
+
"link": "Gweld cwcis"
|
|
5
5
|
},
|
|
6
6
|
"header": "Cwcis",
|
|
7
7
|
"intro": "Mae’r gwasanaeth hwn yn gosod ffeiliau bychan (a elwir yn ‘cwcis’) ar eich cyfrifiadur er mwyn:",
|
|
@@ -39,6 +39,11 @@
|
|
|
39
39
|
"seen_cookie_message",
|
|
40
40
|
"Yn gadael i ni wybod eich bod wedi gweld ein neges cwcis yn barod",
|
|
41
41
|
"1 mis"
|
|
42
|
+
],
|
|
43
|
+
[
|
|
44
|
+
"cookie_preferences",
|
|
45
|
+
"Rhowch wybod i ni eich bod wedi cadw eich gosodiadau caniatâd cwci",
|
|
46
|
+
"1 mis"
|
|
42
47
|
]
|
|
43
48
|
]
|
|
44
49
|
},
|
|
@@ -56,6 +61,8 @@
|
|
|
56
61
|
},
|
|
57
62
|
"no-identify": "Nid oes unrhyw fanylion personol yn cael eu storio gyda’r wybodaeth hon, felly ni ellir eich adnabod.",
|
|
58
63
|
"analytics-table": {
|
|
64
|
+
"containerIdExpires": "2 flynedd",
|
|
65
|
+
"containerIdPurpose": "Defnyddir er mwyn parhau'r sesiwn",
|
|
59
66
|
"headers": [
|
|
60
67
|
"Enw",
|
|
61
68
|
"Pwrpas",
|
|
@@ -71,6 +78,11 @@
|
|
|
71
78
|
"_gat",
|
|
72
79
|
"Defnyddir i wasgu’r gyfradd ceisiadau",
|
|
73
80
|
"10 munud"
|
|
81
|
+
],
|
|
82
|
+
[
|
|
83
|
+
"_gid",
|
|
84
|
+
"Defnyddir i gyfrif ac olrhain y tudalennau rydych yn eu gweld",
|
|
85
|
+
"24 awr"
|
|
74
86
|
]
|
|
75
87
|
]
|
|
76
88
|
},
|
|
@@ -1,40 +1,55 @@
|
|
|
1
1
|
/* eslint max-len: 0 */
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
const $ = require('jquery');
|
|
5
4
|
window.dialogPolyfill = require('dialog-polyfill');
|
|
6
5
|
|
|
6
|
+
function getTextContent(selector) {
|
|
7
|
+
const element = document.querySelector(selector);
|
|
8
|
+
return element ? element.textContent : '';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function dialogDatasetValue(key) {
|
|
12
|
+
const element = document.getElementById('js-modal-dialog');
|
|
13
|
+
return element ? element.dataset[key] : undefined;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function parseTimeout(value, fallback) {
|
|
17
|
+
const parsedValue = parseInt(value, 10);
|
|
18
|
+
return Number.isNaN(parsedValue) ? fallback : parsedValue;
|
|
19
|
+
}
|
|
20
|
+
|
|
7
21
|
// Modal dialog prototype
|
|
8
22
|
window.GOVUK.sessionDialog = {
|
|
9
23
|
el: document.getElementById('js-modal-dialog'),
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
$fallBackElement: $('.govuk-timeout-warning-fallback'),
|
|
24
|
+
lastFocusedEl: null,
|
|
25
|
+
closeButton: document.querySelector('.modal-dialog .js-dialog-close'),
|
|
26
|
+
fallBackElement: document.querySelector('.govuk-timeout-warning-fallback'),
|
|
14
27
|
dialogIsOpenClass: 'dialog-is-open',
|
|
15
28
|
timers: [],
|
|
16
|
-
warningTextPrefix:
|
|
29
|
+
warningTextPrefix: getTextContent('.dialog-text-prefix'),
|
|
17
30
|
warningTextSuffix: '.',
|
|
18
|
-
warningText:
|
|
31
|
+
warningText: getTextContent('.dialog-text'),
|
|
19
32
|
warningTextExtra: '',
|
|
20
33
|
|
|
21
34
|
// Timer specific markup. If these are not present, timeout and redirection are disabled
|
|
22
|
-
|
|
23
|
-
|
|
35
|
+
timer: document.querySelector('#js-modal-dialog') ? document.querySelector('#js-modal-dialog .timer') : null,
|
|
36
|
+
accessibleTimer: document.querySelector('#js-modal-dialog') ? document.querySelector('#js-modal-dialog .at-timer') : null,
|
|
24
37
|
|
|
25
|
-
secondsSessionTimeout:
|
|
26
|
-
secondsTimeoutWarning:
|
|
27
|
-
timeoutRedirectUrl:
|
|
38
|
+
secondsSessionTimeout: parseTimeout(dialogDatasetValue('sessionTimeout'), 1800),
|
|
39
|
+
secondsTimeoutWarning: parseTimeout(dialogDatasetValue('sessionTimeoutWarning'), 300),
|
|
40
|
+
timeoutRedirectUrl: dialogDatasetValue('urlRedirect'),
|
|
28
41
|
timeSessionRefreshed: new Date(),
|
|
29
42
|
|
|
30
43
|
bindUIElements: function () {
|
|
31
|
-
window.GOVUK.sessionDialog
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
44
|
+
if (window.GOVUK.sessionDialog.closeButton) {
|
|
45
|
+
window.GOVUK.sessionDialog.closeButton.addEventListener('click', function (e) {
|
|
46
|
+
e.preventDefault();
|
|
47
|
+
window.GOVUK.sessionDialog.closeDialog();
|
|
48
|
+
});
|
|
49
|
+
}
|
|
35
50
|
|
|
36
51
|
// Close modal when ESC pressed
|
|
37
|
-
|
|
52
|
+
document.addEventListener('keydown', function (e) {
|
|
38
53
|
if (window.GOVUK.sessionDialog.isDialogOpen() && e.keyCode === 27) {
|
|
39
54
|
window.GOVUK.sessionDialog.closeDialog();
|
|
40
55
|
}
|
|
@@ -46,8 +61,8 @@ window.GOVUK.sessionDialog = {
|
|
|
46
61
|
},
|
|
47
62
|
|
|
48
63
|
isConfigured: function () {
|
|
49
|
-
return window.GOVUK.sessionDialog
|
|
50
|
-
window.GOVUK.sessionDialog
|
|
64
|
+
return window.GOVUK.sessionDialog.timer &&
|
|
65
|
+
window.GOVUK.sessionDialog.accessibleTimer &&
|
|
51
66
|
window.GOVUK.sessionDialog.secondsSessionTimeout &&
|
|
52
67
|
window.GOVUK.sessionDialog.secondsTimeoutWarning &&
|
|
53
68
|
window.GOVUK.sessionDialog.timeoutRedirectUrl;
|
|
@@ -55,7 +70,8 @@ window.GOVUK.sessionDialog = {
|
|
|
55
70
|
|
|
56
71
|
openDialog: function () {
|
|
57
72
|
if (!window.GOVUK.sessionDialog.isDialogOpen()) {
|
|
58
|
-
|
|
73
|
+
document.documentElement.classList.add(window.GOVUK.sessionDialog.dialogIsOpenClass);
|
|
74
|
+
document.body.classList.add(window.GOVUK.sessionDialog.dialogIsOpenClass);
|
|
59
75
|
window.GOVUK.sessionDialog.saveLastFocusedEl();
|
|
60
76
|
window.GOVUK.sessionDialog.makePageContentInert();
|
|
61
77
|
window.GOVUK.sessionDialog.el.showModal();
|
|
@@ -65,7 +81,8 @@ window.GOVUK.sessionDialog = {
|
|
|
65
81
|
|
|
66
82
|
closeDialog: function () {
|
|
67
83
|
if (window.GOVUK.sessionDialog.isDialogOpen()) {
|
|
68
|
-
|
|
84
|
+
document.documentElement.classList.remove(window.GOVUK.sessionDialog.dialogIsOpenClass);
|
|
85
|
+
document.body.classList.remove(window.GOVUK.sessionDialog.dialogIsOpenClass);
|
|
69
86
|
window.GOVUK.sessionDialog.el.close();
|
|
70
87
|
window.GOVUK.sessionDialog.el.open = false;
|
|
71
88
|
window.GOVUK.sessionDialog.setFocusOnLastFocusedEl();
|
|
@@ -75,19 +92,19 @@ window.GOVUK.sessionDialog = {
|
|
|
75
92
|
},
|
|
76
93
|
|
|
77
94
|
saveLastFocusedEl: function () {
|
|
78
|
-
window.GOVUK.sessionDialog
|
|
79
|
-
if (!window.GOVUK.sessionDialog
|
|
80
|
-
window.GOVUK.sessionDialog
|
|
95
|
+
window.GOVUK.sessionDialog.lastFocusedEl = document.activeElement;
|
|
96
|
+
if (!window.GOVUK.sessionDialog.lastFocusedEl || window.GOVUK.sessionDialog.lastFocusedEl === document.body) {
|
|
97
|
+
window.GOVUK.sessionDialog.lastFocusedEl = null;
|
|
81
98
|
} else if (document.querySelector) {
|
|
82
|
-
window.GOVUK.sessionDialog
|
|
99
|
+
window.GOVUK.sessionDialog.lastFocusedEl = document.querySelector(':focus');
|
|
83
100
|
}
|
|
84
101
|
},
|
|
85
102
|
|
|
86
103
|
// Set focus back on last focused el when modal closed
|
|
87
104
|
setFocusOnLastFocusedEl: function () {
|
|
88
|
-
if (window.GOVUK.sessionDialog
|
|
105
|
+
if (window.GOVUK.sessionDialog.lastFocusedEl) {
|
|
89
106
|
window.setTimeout(function () {
|
|
90
|
-
window.GOVUK.sessionDialog
|
|
107
|
+
window.GOVUK.sessionDialog.lastFocusedEl.focus();
|
|
91
108
|
}, 0);
|
|
92
109
|
}
|
|
93
110
|
},
|
|
@@ -222,15 +239,15 @@ window.GOVUK.sessionDialog = {
|
|
|
222
239
|
},
|
|
223
240
|
|
|
224
241
|
startCountdown: function () {
|
|
225
|
-
const
|
|
226
|
-
const
|
|
242
|
+
const timer = window.GOVUK.sessionDialog.timer;
|
|
243
|
+
const accessibleTimer = window.GOVUK.sessionDialog.accessibleTimer;
|
|
227
244
|
let timerRunOnce = false;
|
|
228
245
|
const iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
|
|
229
246
|
|
|
230
247
|
const seconds = window.GOVUK.sessionDialog.secondsUntilSessionTimeout();
|
|
231
248
|
const minutes = seconds / 60;
|
|
232
249
|
|
|
233
|
-
|
|
250
|
+
timer.textContent = minutes + ' minute' + (minutes > 1 ? 's' : '');
|
|
234
251
|
|
|
235
252
|
(function countdown() {
|
|
236
253
|
const secondsUntilSessionTimeout = window.GOVUK.sessionDialog.secondsUntilSessionTimeout();
|
|
@@ -250,17 +267,17 @@ window.GOVUK.sessionDialog = {
|
|
|
250
267
|
const atText = window.GOVUK.sessionDialog.warningTextPrefix + countdownAtText + window.GOVUK.sessionDialog.warningTextSuffix + ' ' + window.GOVUK.sessionDialog.warningText;
|
|
251
268
|
const extraText = '\n' + window.GOVUK.sessionDialog.warningTextExtra;
|
|
252
269
|
|
|
253
|
-
|
|
270
|
+
timer.innerHTML = text + ' ' + extraText;
|
|
254
271
|
|
|
255
272
|
// Update screen reader friendly content every 20 secs
|
|
256
273
|
if (secondsLeft % 20 === 0) {
|
|
257
274
|
// Read out the extra content only once.
|
|
258
275
|
// Don't read out on iOS VoiceOver which stalls on the longer text
|
|
259
276
|
if (!timerRunOnce && !iOS) {
|
|
260
|
-
|
|
277
|
+
accessibleTimer.textContent = atText + extraText;
|
|
261
278
|
timerRunOnce = true;
|
|
262
279
|
} else {
|
|
263
|
-
|
|
280
|
+
accessibleTimer.textContent = atText;
|
|
264
281
|
}
|
|
265
282
|
}
|
|
266
283
|
|
|
@@ -277,10 +294,17 @@ window.GOVUK.sessionDialog = {
|
|
|
277
294
|
},
|
|
278
295
|
|
|
279
296
|
refreshSession: function () {
|
|
280
|
-
|
|
281
|
-
.
|
|
297
|
+
return fetch('')
|
|
298
|
+
.then(function (response) {
|
|
299
|
+
if (!response.ok) {
|
|
300
|
+
throw new Error('Session refresh failed');
|
|
301
|
+
}
|
|
282
302
|
window.GOVUK.sessionDialog.timeSessionRefreshed = new Date();
|
|
283
303
|
window.GOVUK.sessionDialog.controller();
|
|
304
|
+
})
|
|
305
|
+
.catch(function (error) {
|
|
306
|
+
// eslint-disable-next-line no-console
|
|
307
|
+
console.error(error);
|
|
284
308
|
});
|
|
285
309
|
},
|
|
286
310
|
|
|
@@ -327,7 +351,7 @@ window.GOVUK.sessionDialog = {
|
|
|
327
351
|
},
|
|
328
352
|
|
|
329
353
|
init: function (options) {
|
|
330
|
-
|
|
354
|
+
Object.assign(window.GOVUK.sessionDialog, options);
|
|
331
355
|
if (window.GOVUK.sessionDialog.el && window.GOVUK.sessionDialog.isConfigured()) {
|
|
332
356
|
// Native dialog is not supported by some browsers so use polyfill
|
|
333
357
|
if (typeof HTMLDialogElement !== 'function') {
|
|
@@ -336,7 +360,7 @@ window.GOVUK.sessionDialog = {
|
|
|
336
360
|
return true;
|
|
337
361
|
} catch (error) {
|
|
338
362
|
// Doesn't support polyfill (IE8) - display fallback element
|
|
339
|
-
window.GOVUK.sessionDialog
|
|
363
|
+
window.GOVUK.sessionDialog.fallBackElement.classList.add('govuk-!-display-block');
|
|
340
364
|
return false;
|
|
341
365
|
}
|
|
342
366
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
const
|
|
2
|
+
const dayjs = require('dayjs');
|
|
3
3
|
const redis = require('redis');
|
|
4
4
|
const config = require('./../config/hof-defaults');
|
|
5
5
|
|
|
@@ -36,8 +36,8 @@ module.exports = (options, rateLimitType) => {
|
|
|
36
36
|
logger.log('error', `Error with requesting redis session for rate limiting: ${err}`);
|
|
37
37
|
return await closeConnection();
|
|
38
38
|
}
|
|
39
|
-
|
|
40
|
-
const windowStartTimestamp =
|
|
39
|
+
let currentRequestTime = dayjs();
|
|
40
|
+
const windowStartTimestamp = dayjs().subtract(WINDOW_SIZE_IN_MINUTES, 'minutes').unix();
|
|
41
41
|
let oldRecord = false;
|
|
42
42
|
let data;
|
|
43
43
|
// if no record is found , create a new record for user and store to redis
|
|
@@ -74,9 +74,9 @@ module.exports = (options, rateLimitType) => {
|
|
|
74
74
|
}
|
|
75
75
|
// if number of requests made is less than allowed maximum, log new entry
|
|
76
76
|
const lastRequestLog = data[data.length - 1];
|
|
77
|
-
|
|
78
|
-
.subtract(WINDOW_LOG_INTERVAL_IN_MINUTES, 'minutes')
|
|
79
|
-
|
|
77
|
+
currentRequestTime = currentRequestTime
|
|
78
|
+
.subtract(WINDOW_LOG_INTERVAL_IN_MINUTES, 'minutes');
|
|
79
|
+
const potentialCurrentWindowIntervalStartTimeStamp = currentRequestTime.unix();
|
|
80
80
|
// if interval has not passed since last request log, increment counter
|
|
81
81
|
if (lastRequestLog[timestampName] > potentialCurrentWindowIntervalStartTimeStamp) {
|
|
82
82
|
lastRequestLog[countName]++;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hof",
|
|
3
3
|
"description": "A bootstrap for HOF projects",
|
|
4
|
-
"version": "24.
|
|
4
|
+
"version": "24.6.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"author": "HomeOffice",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"unit": "LOG_LEVEL=error nyc _mocha \"test/**/*.spec.js\" \"sandbox/test/**/*.spec.js\"",
|
|
25
25
|
"unit:nocov": "LOG_LEVEL=error mocha \"test/**/*.spec.js\" \"sandbox/test/**/*.spec.js\"",
|
|
26
26
|
"test:lint": "eslint . --config ./.eslintrc.hof-wrapper.js",
|
|
27
|
-
"test:functional": "
|
|
27
|
+
"test:functional": "mocha ./test/functional-tests --timeout 20000 --exit",
|
|
28
28
|
"test:vite": "vitest run --config test/frontend/toolkit/vitest.config.js",
|
|
29
29
|
"test:jest": "jest test/frontend/jest test/integration/index test/utilities/helpers/jest",
|
|
30
30
|
"test:acceptance": "TAGS=\"${TAGS:=@feature}\" yarn run test:cucumber",
|
|
@@ -34,13 +34,13 @@
|
|
|
34
34
|
"postversion": "git push && git push --tags"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@aws-sdk/client-sesv2": "^3.
|
|
38
|
-
"@rollup/plugin-commonjs": "^29.0.
|
|
37
|
+
"@aws-sdk/client-sesv2": "^3.1109.0",
|
|
38
|
+
"@rollup/plugin-commonjs": "^29.0.3",
|
|
39
39
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
40
40
|
"aliasify": "^2.1.0",
|
|
41
|
-
"axios": "^1.
|
|
41
|
+
"axios": "^1.19.0",
|
|
42
42
|
"bluebird": "^3.7.2",
|
|
43
|
-
"body-parser": "^1.20.
|
|
43
|
+
"body-parser": "^1.20.6",
|
|
44
44
|
"callsite": "^1.0.0",
|
|
45
45
|
"chalk": "^2.4.2",
|
|
46
46
|
"chokidar": "^3.6.0",
|
|
@@ -48,16 +48,17 @@
|
|
|
48
48
|
"cookie-parser": "^1.4.7",
|
|
49
49
|
"cp": "^0.2.0",
|
|
50
50
|
"csrf": "^3.1.0",
|
|
51
|
+
"dayjs": "^1.11.23",
|
|
51
52
|
"debug": "^4.4.3",
|
|
52
53
|
"deprecate": "^1.1.1",
|
|
53
54
|
"dialog-polyfill": "^0.5.6",
|
|
54
|
-
"dotenv": "^16.
|
|
55
|
+
"dotenv": "^16.6.1",
|
|
55
56
|
"duplexify": "^3.7.1",
|
|
56
57
|
"exorcist": "^2.0.0",
|
|
57
|
-
"express": "^4.
|
|
58
|
+
"express": "^4.22.2",
|
|
58
59
|
"express-healthcheck": "^0.1.0",
|
|
59
60
|
"express-partial-templates": "^0.2.1",
|
|
60
|
-
"express-session": "^1.
|
|
61
|
+
"express-session": "^1.19.0",
|
|
61
62
|
"findup": "^0.1.5",
|
|
62
63
|
"glob": "^7.2.3",
|
|
63
64
|
"govuk-elements-sass": "^3.1.3",
|
|
@@ -70,24 +71,22 @@
|
|
|
70
71
|
"i18n-future": "^2.0.1",
|
|
71
72
|
"i18n-lookup": "^0.1.0",
|
|
72
73
|
"is-pdf": "^1.0.0",
|
|
73
|
-
"
|
|
74
|
-
"libphonenumber-js": "^1.12.23",
|
|
74
|
+
"libphonenumber-js": "^1.13.10",
|
|
75
75
|
"lodash": "^4.18.1",
|
|
76
|
-
"markdown-it": "^14.
|
|
77
|
-
"minimatch": "^4.2.
|
|
76
|
+
"markdown-it": "^14.3.0",
|
|
77
|
+
"minimatch": "^4.2.6",
|
|
78
78
|
"minimist": "^1.2.8",
|
|
79
79
|
"mixwith": "^0.1.1",
|
|
80
|
-
"
|
|
81
|
-
"morgan": "^1.10.1",
|
|
80
|
+
"morgan": "^1.11.0",
|
|
82
81
|
"mustache": "^4.2.0",
|
|
83
|
-
"notifications-node-client": "^8.
|
|
82
|
+
"notifications-node-client": "^8.5.0",
|
|
84
83
|
"redis": "^3.1.2",
|
|
85
84
|
"reqres": "^3.0.1",
|
|
86
85
|
"rimraf": "^3.0.2",
|
|
87
|
-
"sass": "^1.
|
|
88
|
-
"serve-static": "^1.16.
|
|
86
|
+
"sass": "^1.102.0",
|
|
87
|
+
"serve-static": "^1.16.3",
|
|
89
88
|
"uglify-js": "^3.19.3",
|
|
90
|
-
"underscore": "^1.13.
|
|
89
|
+
"underscore": "^1.13.8",
|
|
91
90
|
"urijs": "^1.19.11",
|
|
92
91
|
"uuid": "^11.1.1",
|
|
93
92
|
"winston": "^3.18.3",
|
|
@@ -95,10 +94,10 @@
|
|
|
95
94
|
},
|
|
96
95
|
"devDependencies": {
|
|
97
96
|
"@cucumber/cucumber": "^7.3.2",
|
|
98
|
-
"@cucumber/pretty-formatter": "^4.0.
|
|
97
|
+
"@cucumber/pretty-formatter": "^4.0.1",
|
|
99
98
|
"@types/jest": "^30.0.0",
|
|
100
|
-
"@vitest/coverage-v8": "^4.
|
|
101
|
-
"@xmldom/xmldom": "~0.
|
|
99
|
+
"@vitest/coverage-v8": "^4.1.10",
|
|
100
|
+
"@xmldom/xmldom": "~0.9.11",
|
|
102
101
|
"chai": "^3.5.0",
|
|
103
102
|
"chai-as-promised": "^7.1.2",
|
|
104
103
|
"chai-subset": "^1.6.0",
|
|
@@ -106,28 +105,24 @@
|
|
|
106
105
|
"csv": "^5.5.3",
|
|
107
106
|
"eslint": "^8.57.1",
|
|
108
107
|
"eslint-config-hof": "^1.3.4",
|
|
109
|
-
"funkie": "0.0.5",
|
|
110
|
-
"funkie-phantom": "0.0.1",
|
|
111
108
|
"istanbul": "^0.4.5",
|
|
112
109
|
"jest": "^30.4.2",
|
|
113
110
|
"jest-environment-jsdom": "^30.4.1",
|
|
114
|
-
"
|
|
115
|
-
"mocha": "^11.7.6",
|
|
111
|
+
"mocha": "^11.8.0",
|
|
116
112
|
"mocha-sandbox": "^1.0.0",
|
|
117
|
-
"node-mocks-http": "^1.
|
|
113
|
+
"node-mocks-http": "^1.18.1",
|
|
118
114
|
"npm-run-all": "^4.1.5",
|
|
119
115
|
"nyc": "^15.1.0",
|
|
120
|
-
"playwright": "^1.
|
|
121
|
-
"postcode": "0.2.
|
|
116
|
+
"playwright": "^1.62.1",
|
|
117
|
+
"postcode": "0.2.5",
|
|
122
118
|
"proxyquire": "^1.8.0",
|
|
123
119
|
"release-it": "^20.2.1",
|
|
124
120
|
"sinon": "^11.1.2",
|
|
125
121
|
"sinon-chai": "^3.7.0",
|
|
126
122
|
"supertest": "^3.4.2",
|
|
127
123
|
"travis-conditions": "0.0.0",
|
|
128
|
-
"vitest": "^4.
|
|
129
|
-
"watchify": "^4.0.0"
|
|
130
|
-
"webdriverio": "^5.0.0"
|
|
124
|
+
"vitest": "^4.1.10",
|
|
125
|
+
"watchify": "^4.0.0"
|
|
131
126
|
},
|
|
132
127
|
"jest": {
|
|
133
128
|
"testEnvironment": "jsdom"
|
|
@@ -140,12 +135,12 @@
|
|
|
140
135
|
"exit": "true"
|
|
141
136
|
},
|
|
142
137
|
"resolutions": {
|
|
138
|
+
"vite": "^7.3.6",
|
|
143
139
|
"form-data": "^4.0.6",
|
|
144
140
|
"js-yaml": "^4.2.0",
|
|
145
141
|
"qs": "^6.14.1",
|
|
146
142
|
"serialize-javascript": "^7.0.3",
|
|
147
143
|
"tough-cookie": "^4.1.3",
|
|
148
|
-
"vite": "^7.3.6",
|
|
149
144
|
"esbuild": "^0.28.1"
|
|
150
145
|
}
|
|
151
146
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const { formatDate } = require('../../../../utilities/date');
|
|
2
2
|
const PRETTY_DATE_FORMAT = 'Do MMMM YYYY';
|
|
3
3
|
const APPEAL_STAGES = require('../lib/staticAppealStages').getstaticAppealStages();
|
|
4
4
|
const _ = require('lodash');
|
|
@@ -8,7 +8,7 @@ module.exports = {
|
|
|
8
8
|
'name',
|
|
9
9
|
{
|
|
10
10
|
field: 'dateOfBirth',
|
|
11
|
-
parse: d => d &&
|
|
11
|
+
parse: d => d && formatDate(d, PRETTY_DATE_FORMAT)
|
|
12
12
|
}
|
|
13
13
|
],
|
|
14
14
|
address: [
|
|
@@ -3,68 +3,15 @@
|
|
|
3
3
|
|
|
4
4
|
require('../../../frontend/themes/gov-uk/client-js');
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
});
|
|
6
|
+
const accessibleAutocomplete = require('accessible-autocomplete');
|
|
7
|
+
|
|
8
|
+
document.addEventListener('DOMContentLoaded', function onDOMContentLoaded() {
|
|
9
|
+
document
|
|
10
|
+
.querySelectorAll('.typeahead')
|
|
11
|
+
.forEach(function applyTypeahead(element) {
|
|
12
|
+
accessibleAutocomplete.enhanceSelectElement({
|
|
13
|
+
defaultValue: '',
|
|
14
|
+
selectElement: element
|
|
15
|
+
});
|
|
16
|
+
});
|
|
70
17
|
});
|
|
@@ -1,27 +1,2 @@
|
|
|
1
|
-
|
|
2
1
|
@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
|
-
}
|
|
2
|
+
@import "./autocomplete.scss";
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
// Accessible autocomplete
|
|
2
|
+
.autocomplete__wrapper {
|
|
3
|
+
position: relative;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.autocomplete__hint,
|
|
7
|
+
.autocomplete__input {
|
|
8
|
+
-webkit-appearance: none;
|
|
9
|
+
border: 2px solid #0b0c0c;
|
|
10
|
+
border-radius: 0; /* Safari 10 on iOS adds implicit border rounding. */
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
-moz-box-sizing: border-box;
|
|
13
|
+
-webkit-box-sizing: border-box;
|
|
14
|
+
margin-bottom: 0; /* BUG: Safari 10 on macOS seems to add an implicit margin. */
|
|
15
|
+
width: 100%;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.autocomplete__input {
|
|
19
|
+
background-color: transparent;
|
|
20
|
+
position: relative;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.autocomplete__hint {
|
|
24
|
+
color: #b1b4b6;
|
|
25
|
+
position: absolute;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.autocomplete__input--default {
|
|
29
|
+
padding: 5px;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.autocomplete__input--focused {
|
|
33
|
+
outline: 3px solid #fd0;
|
|
34
|
+
outline-offset: 0;
|
|
35
|
+
box-shadow: inset 0 0 0 2px;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.autocomplete__input--show-all-values {
|
|
39
|
+
padding: 5px 34px 5px 5px; /* Space for arrow. Other padding should match .autocomplete__input--default. */
|
|
40
|
+
cursor: pointer;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.autocomplete__dropdown-arrow-down{
|
|
44
|
+
z-index: -1;
|
|
45
|
+
display: inline-block;
|
|
46
|
+
position: absolute;
|
|
47
|
+
right: 8px;
|
|
48
|
+
width: 24px;
|
|
49
|
+
height: 24px;
|
|
50
|
+
top: 10px;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.autocomplete__menu {
|
|
54
|
+
background-color: #fff;
|
|
55
|
+
border: 2px solid #0B0C0C;
|
|
56
|
+
border-top: 0;
|
|
57
|
+
color: #0B0C0C;
|
|
58
|
+
margin: 0;
|
|
59
|
+
max-height: 342px;
|
|
60
|
+
overflow-x: hidden;
|
|
61
|
+
padding: 0;
|
|
62
|
+
width: 100%;
|
|
63
|
+
width: calc(100% - 4px);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.autocomplete__menu--visible {
|
|
67
|
+
display: block;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.autocomplete__menu--hidden {
|
|
71
|
+
display: none;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.autocomplete__menu--overlay {
|
|
75
|
+
box-shadow: rgba(0, 0, 0, 0.256863) 0px 2px 6px;
|
|
76
|
+
left: 0;
|
|
77
|
+
position: absolute;
|
|
78
|
+
top: 100%;
|
|
79
|
+
z-index: 100;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.autocomplete__menu--inline {
|
|
83
|
+
position: relative;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.autocomplete__option {
|
|
87
|
+
border-bottom: solid #b1b4b6;
|
|
88
|
+
border-width: 1px 0;
|
|
89
|
+
cursor: pointer;
|
|
90
|
+
display: block;
|
|
91
|
+
position: relative;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.autocomplete__option > * {
|
|
95
|
+
pointer-events: none;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.autocomplete__option:first-of-type {
|
|
99
|
+
border-top-width: 0;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.autocomplete__option:last-of-type {
|
|
103
|
+
border-bottom-width: 0;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.autocomplete__option--odd {
|
|
107
|
+
background-color: #FAFAFA;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.autocomplete__option--focused,
|
|
111
|
+
.autocomplete__option:hover {
|
|
112
|
+
background-color: #1d70b8;
|
|
113
|
+
border-color: #1d70b8;
|
|
114
|
+
color: white;
|
|
115
|
+
outline: none;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.autocomplete__option--no-results {
|
|
119
|
+
background-color: #FAFAFA;
|
|
120
|
+
color: #646b6f;
|
|
121
|
+
cursor: not-allowed;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.autocomplete__hint,
|
|
125
|
+
.autocomplete__input,
|
|
126
|
+
.autocomplete__option {
|
|
127
|
+
font-size: 16px;
|
|
128
|
+
line-height: 1.25;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.autocomplete__hint,
|
|
132
|
+
.autocomplete__option {
|
|
133
|
+
padding: 5px;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
@media (min-width: 641px) {
|
|
137
|
+
.autocomplete__hint,
|
|
138
|
+
.autocomplete__input,
|
|
139
|
+
.autocomplete__option {
|
|
140
|
+
font-size: 19px;
|
|
141
|
+
line-height: 1.31579;
|
|
142
|
+
}
|
|
143
|
+
}
|
package/sandbox/package.json
CHANGED
|
@@ -15,15 +15,11 @@
|
|
|
15
15
|
},
|
|
16
16
|
"author": "",
|
|
17
17
|
"dependencies": {
|
|
18
|
+
"accessible-autocomplete": "^3.0.1",
|
|
18
19
|
"govuk-frontend": "4.10",
|
|
19
|
-
"
|
|
20
|
-
"sass": "^1.101.0",
|
|
21
|
-
"typeahead-aria": "^1.0.4"
|
|
20
|
+
"sass": "^1.104.1"
|
|
22
21
|
},
|
|
23
22
|
"devDependencies": {
|
|
24
23
|
"nodemon": "^3.1.14"
|
|
25
|
-
},
|
|
26
|
-
"resolutions": {
|
|
27
|
-
"picomatch": "^2.3.2"
|
|
28
24
|
}
|
|
29
25
|
}
|
package/sandbox/yarn.lock
CHANGED
|
@@ -91,6 +91,11 @@
|
|
|
91
91
|
"@parcel/watcher-win32-ia32" "2.5.6"
|
|
92
92
|
"@parcel/watcher-win32-x64" "2.5.6"
|
|
93
93
|
|
|
94
|
+
accessible-autocomplete@^3.0.1:
|
|
95
|
+
version "3.0.1"
|
|
96
|
+
resolved "https://registry.yarnpkg.com/accessible-autocomplete/-/accessible-autocomplete-3.0.1.tgz#8ddf4934d0b4ba6acb28de486dd505e062cdc86e"
|
|
97
|
+
integrity sha512-xMshgc2LT5addvvfCTGzIkRrvhbOFeylFSnSMfS/PdjvvvElZkakCwxO3/yJYBWyi1hi3tZloqOJQ5kqqJtH4g==
|
|
98
|
+
|
|
94
99
|
anymatch@~3.1.2:
|
|
95
100
|
version "3.1.3"
|
|
96
101
|
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
|
|
@@ -220,16 +225,6 @@ is-number@^7.0.0:
|
|
|
220
225
|
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
|
|
221
226
|
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
|
|
222
227
|
|
|
223
|
-
jquery@>=1.11:
|
|
224
|
-
version "4.0.0"
|
|
225
|
-
resolved "https://registry.yarnpkg.com/jquery/-/jquery-4.0.0.tgz#95c33ac29005ff72ec444c5ba1cf457e61404fbb"
|
|
226
|
-
integrity sha512-TXCHVR3Lb6TZdtw1l3RTLf8RBWVGexdxL6AC8/e0xZKEpBflBsjh9/8LXw+dkNFuOyW9B7iB3O1sP7hS0Kiacg==
|
|
227
|
-
|
|
228
|
-
jquery@^3.7.1:
|
|
229
|
-
version "3.7.1"
|
|
230
|
-
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.7.1.tgz#083ef98927c9a6a74d05a6af02806566d16274de"
|
|
231
|
-
integrity sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==
|
|
232
|
-
|
|
233
228
|
minimatch@^10.2.1:
|
|
234
229
|
version "10.2.5"
|
|
235
230
|
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.5.tgz#bd48687a0be38ed2961399105600f832095861d1"
|
|
@@ -268,11 +263,16 @@ normalize-path@^3.0.0, normalize-path@~3.0.0:
|
|
|
268
263
|
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
|
|
269
264
|
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
|
|
270
265
|
|
|
271
|
-
picomatch@^2.0.4, picomatch@^2.2.1
|
|
266
|
+
picomatch@^2.0.4, picomatch@^2.2.1:
|
|
272
267
|
version "2.3.2"
|
|
273
268
|
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.2.tgz#5a942915e26b372dc0f0e6753149a16e6b1c5601"
|
|
274
269
|
integrity sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==
|
|
275
270
|
|
|
271
|
+
picomatch@^4.0.3:
|
|
272
|
+
version "4.0.7"
|
|
273
|
+
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.7.tgz#6313360034ccb36b3dc61ecbdff78121f90fe21f"
|
|
274
|
+
integrity sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==
|
|
275
|
+
|
|
276
276
|
pstree.remy@^1.1.8:
|
|
277
277
|
version "1.1.8"
|
|
278
278
|
resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a"
|
|
@@ -290,10 +290,10 @@ readdirp@~3.6.0:
|
|
|
290
290
|
dependencies:
|
|
291
291
|
picomatch "^2.2.1"
|
|
292
292
|
|
|
293
|
-
sass@^1.
|
|
294
|
-
version "1.
|
|
295
|
-
resolved "https://registry.yarnpkg.com/sass/-/sass-1.
|
|
296
|
-
integrity sha512-
|
|
293
|
+
sass@^1.104.1:
|
|
294
|
+
version "1.104.1"
|
|
295
|
+
resolved "https://registry.yarnpkg.com/sass/-/sass-1.104.1.tgz#29a4bdb33c48a8bb656e300a33afcafbfcbc8491"
|
|
296
|
+
integrity sha512-yDA+1aIG3EHgN4V/BvuhCvu61FF6hEd4e+9DxikUm9U0CAGuvdIZ/UYy7qbOxjhbbWQraoyLlMuzdRGOSV5Bmw==
|
|
297
297
|
dependencies:
|
|
298
298
|
chokidar "^5.0.0"
|
|
299
299
|
immutable "^5.1.5"
|
|
@@ -337,13 +337,6 @@ touch@^3.1.0:
|
|
|
337
337
|
resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.1.tgz#097a23d7b161476435e5c1344a95c0f75b4a5694"
|
|
338
338
|
integrity sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==
|
|
339
339
|
|
|
340
|
-
typeahead-aria@^1.0.4:
|
|
341
|
-
version "1.0.4"
|
|
342
|
-
resolved "https://registry.yarnpkg.com/typeahead-aria/-/typeahead-aria-1.0.4.tgz#d2829cc01c0226a367627f3098a468c4e0343f00"
|
|
343
|
-
integrity sha512-6g0XtcM1AiPMlyXOuAb90Lg0WGi9PdbH68La7kUJmZwxUdpE80IC6tewBg0a/ug1Betoz1xCfEqTgkhuN6wo4w==
|
|
344
|
-
dependencies:
|
|
345
|
-
jquery ">=1.11"
|
|
346
|
-
|
|
347
340
|
undefsafe@^2.0.5:
|
|
348
341
|
version "2.0.5"
|
|
349
342
|
resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const dayjs = require('dayjs');
|
|
4
|
+
const advancedFormat = require('dayjs/plugin/advancedFormat');
|
|
5
|
+
|
|
6
|
+
dayjs.extend(advancedFormat);
|
|
7
|
+
|
|
8
|
+
const DEFAULT_DATE_FORMAT = 'D MMMM YYYY';
|
|
9
|
+
|
|
10
|
+
const formatDate = (value, format = DEFAULT_DATE_FORMAT) => dayjs(value).format(format);
|
|
11
|
+
|
|
12
|
+
module.exports = {
|
|
13
|
+
formatDate
|
|
14
|
+
};
|