@ministryofjustice/frontend 3.3.1 → 3.4.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 (87) hide show
  1. package/README.md +4 -10
  2. package/govuk-prototype-kit.config.json +5 -16
  3. package/moj/all.jquery.min.js +77 -3
  4. package/moj/all.js +2021 -1436
  5. package/moj/all.scss +2 -0
  6. package/moj/all.spec.js +15 -13
  7. package/moj/components/_all.scss +1 -0
  8. package/moj/components/action-bar/_action-bar.scss +4 -6
  9. package/moj/components/add-another/_add-another.scss +9 -7
  10. package/moj/components/add-another/add-another.js +90 -69
  11. package/moj/components/add-another/add-another.spec.js +165 -0
  12. package/moj/components/alert/README.md +0 -0
  13. package/moj/components/alert/_alert.scss +142 -0
  14. package/moj/components/alert/alert.js +247 -0
  15. package/moj/components/alert/alert.spec.helper.js +67 -0
  16. package/moj/components/alert/alert.spec.js +229 -0
  17. package/moj/components/alert/macro.njk +3 -0
  18. package/moj/components/alert/template.njk +83 -0
  19. package/moj/components/badge/_badge.scss +3 -4
  20. package/moj/components/banner/_banner.scss +5 -10
  21. package/moj/components/button-menu/_button-menu.scss +10 -9
  22. package/moj/components/button-menu/button-menu.js +139 -136
  23. package/moj/components/button-menu/button-menu.spec.js +295 -296
  24. package/moj/components/cookie-banner/_cookie-banner.scss +6 -5
  25. package/moj/components/currency-input/_currency-input.scss +4 -4
  26. package/moj/components/date-picker/README.md +14 -17
  27. package/moj/components/date-picker/_date-picker.scss +122 -106
  28. package/moj/components/date-picker/date-picker.js +473 -471
  29. package/moj/components/date-picker/date-picker.spec.js +971 -923
  30. package/moj/components/filter/README.md +1 -1
  31. package/moj/components/filter/_filter.scss +53 -75
  32. package/moj/components/filter-toggle-button/filter-toggle-button.js +71 -67
  33. package/moj/components/filter-toggle-button/filter-toggle-button.spec.js +203 -205
  34. package/moj/components/form-validator/form-validator.js +117 -109
  35. package/moj/components/header/_header.scss +17 -19
  36. package/moj/components/identity-bar/_identity-bar.scss +5 -5
  37. package/moj/components/interruption-card/_interruption-card.scss +2 -2
  38. package/moj/components/messages/_messages.scss +12 -19
  39. package/moj/components/multi-file-upload/README.md +1 -1
  40. package/moj/components/multi-file-upload/_multi-file-upload.scss +34 -30
  41. package/moj/components/multi-file-upload/multi-file-upload.js +188 -152
  42. package/moj/components/multi-file-upload/multi-file-upload.spec.js +510 -0
  43. package/moj/components/multi-select/_multi-select.scss +4 -3
  44. package/moj/components/multi-select/multi-select.js +55 -50
  45. package/moj/components/multi-select/multi-select.spec.js +72 -79
  46. package/moj/components/notification-badge/_notification-badge.scss +12 -12
  47. package/moj/components/organisation-switcher/_organisation-switcher.scss +1 -1
  48. package/moj/components/page-header-actions/_page-header-actions.scss +3 -2
  49. package/moj/components/pagination/_pagination.scss +26 -31
  50. package/moj/components/password-reveal/_password-reveal.scss +1 -2
  51. package/moj/components/password-reveal/password-reveal.js +22 -21
  52. package/moj/components/password-reveal/password-reveal.spec.js +39 -37
  53. package/moj/components/primary-navigation/_primary-navigation.scss +26 -29
  54. package/moj/components/progress-bar/_progress-bar.scss +21 -26
  55. package/moj/components/rich-text-editor/_rich-text-editor.scss +17 -16
  56. package/moj/components/rich-text-editor/rich-text-editor.js +117 -103
  57. package/moj/components/search/_search.scss +6 -4
  58. package/moj/components/search-toggle/search-toggle.js +29 -30
  59. package/moj/components/search-toggle/search-toggle.scss +21 -15
  60. package/moj/components/search-toggle/search-toggle.spec.js +65 -70
  61. package/moj/components/side-navigation/_side-navigation.scss +12 -21
  62. package/moj/components/sortable-table/_sortable-table.scss +25 -23
  63. package/moj/components/sortable-table/sortable-table.js +139 -117
  64. package/moj/components/sortable-table/sortable-table.spec.js +362 -0
  65. package/moj/components/sub-navigation/_sub-navigation.scss +24 -28
  66. package/moj/components/tag/_tag.scss +8 -9
  67. package/moj/components/task-list/_task-list.scss +8 -7
  68. package/moj/components/ticket-panel/_ticket-panel.scss +14 -6
  69. package/moj/components/timeline/_timeline.scss +18 -20
  70. package/moj/filters/all.js +28 -30
  71. package/moj/filters/prototype-kit-13-filters.js +2 -1
  72. package/moj/helpers/_all.scss +1 -0
  73. package/moj/helpers/_hidden.scss +1 -1
  74. package/moj/helpers/_links.scss +20 -0
  75. package/moj/helpers.js +160 -31
  76. package/moj/helpers.spec.js +235 -0
  77. package/moj/init.js +2 -2
  78. package/moj/moj-frontend.min.css +2 -2
  79. package/moj/moj-frontend.min.js +77 -3
  80. package/moj/namespace.js +2 -1
  81. package/moj/objects/_filter-layout.scss +11 -10
  82. package/moj/objects/_scrollable-pane.scss +11 -14
  83. package/moj/settings/_colours.scss +5 -0
  84. package/moj/settings/_measurements.scss +0 -2
  85. package/moj/utilities/_hidden.scss +3 -3
  86. package/moj/utilities/_width-container.scss +1 -1
  87. package/package.json +1 -1
@@ -2,234 +2,232 @@
2
2
  * Datepicker config
3
3
  *
4
4
  * @typedef {object} DatepickerConfig
5
- * @property {string} [excludedDates] - Dates that cannot be selected
6
- * @property {string} [excludedDays] - Days that cannot be selected
5
+ * @property {string} [excludedDates] - Dates that cannot be selected
6
+ * @property {string} [excludedDays] - Days that cannot be selected
7
7
  * @property {boolean} [leadingZeroes] - Whether to add leading zeroes when populating the field
8
- * @property {string} [minDate] - The earliest available date
9
- * @property {string} [maxDate] - The latest available date
10
- * @property {string} [weekStartDay] - First day of the week in calendar view
8
+ * @property {string} [minDate] - The earliest available date
9
+ * @property {string} [maxDate] - The latest available date
10
+ * @property {string} [weekStartDay] - First day of the week in calendar view
11
11
  */
12
12
 
13
13
  /**
14
14
  * @param {HTMLElement} $module - HTML element
15
15
  * @param {DatepickerConfig} config - config object
16
- * @constructor
16
+ * @class
17
17
  */
18
18
  function Datepicker($module, config = {}) {
19
19
  if (!$module) {
20
- return this;
20
+ return this
21
21
  }
22
22
 
23
23
  const schema = Object.freeze({
24
24
  properties: {
25
- excludedDates: { type: "string" },
26
- excludedDays: { type: "string" },
27
- leadingZeros: { type: "string" },
28
- maxDate: { type: "string" },
29
- minDate: { type: "string" },
30
- weekStartDay: { type: "string" },
31
- },
32
- });
25
+ excludedDates: { type: 'string' },
26
+ excludedDays: { type: 'string' },
27
+ leadingZeros: { type: 'string' },
28
+ maxDate: { type: 'string' },
29
+ minDate: { type: 'string' },
30
+ weekStartDay: { type: 'string' }
31
+ }
32
+ })
33
33
 
34
34
  const defaults = {
35
35
  leadingZeros: false,
36
- weekStartDay: "monday",
37
- };
36
+ weekStartDay: 'monday'
37
+ }
38
38
 
39
39
  // data attributes override JS config, which overrides defaults
40
40
  this.config = this.mergeConfigs(
41
41
  defaults,
42
42
  config,
43
- this.parseDataset(schema, $module.dataset),
44
- );
43
+ this.parseDataset(schema, $module.dataset)
44
+ )
45
45
 
46
46
  this.dayLabels = [
47
- "Monday",
48
- "Tuesday",
49
- "Wednesday",
50
- "Thursday",
51
- "Friday",
52
- "Saturday",
53
- "Sunday",
54
- ];
47
+ 'Monday',
48
+ 'Tuesday',
49
+ 'Wednesday',
50
+ 'Thursday',
51
+ 'Friday',
52
+ 'Saturday',
53
+ 'Sunday'
54
+ ]
55
55
 
56
56
  this.monthLabels = [
57
- "January",
58
- "February",
59
- "March",
60
- "April",
61
- "May",
62
- "June",
63
- "July",
64
- "August",
65
- "September",
66
- "October",
67
- "November",
68
- "December",
69
- ];
70
-
71
- this.currentDate = new Date();
72
- this.currentDate.setHours(0, 0, 0, 0);
73
- this.calendarDays = [];
74
- this.excludedDates = [];
75
- this.excludedDays = [];
76
-
77
- this.buttonClass = "moj-datepicker__button";
78
- this.selectedDayButtonClass = "moj-datepicker__button--selected";
79
- this.currentDayButtonClass = "moj-datepicker__button--current";
80
- this.todayButtonClass = "moj-datepicker__button--today";
81
-
82
- this.$module = $module;
83
- this.$input = $module.querySelector(".moj-js-datepicker-input");
57
+ 'January',
58
+ 'February',
59
+ 'March',
60
+ 'April',
61
+ 'May',
62
+ 'June',
63
+ 'July',
64
+ 'August',
65
+ 'September',
66
+ 'October',
67
+ 'November',
68
+ 'December'
69
+ ]
70
+
71
+ this.currentDate = new Date()
72
+ this.currentDate.setHours(0, 0, 0, 0)
73
+ this.calendarDays = []
74
+ this.excludedDates = []
75
+ this.excludedDays = []
76
+
77
+ this.buttonClass = 'moj-datepicker__button'
78
+ this.selectedDayButtonClass = 'moj-datepicker__button--selected'
79
+ this.currentDayButtonClass = 'moj-datepicker__button--current'
80
+ this.todayButtonClass = 'moj-datepicker__button--today'
81
+
82
+ this.$module = $module
83
+ this.$input = $module.querySelector('.moj-js-datepicker-input')
84
84
  }
85
85
 
86
86
  Datepicker.prototype.init = function () {
87
87
  // Check that required elements are present
88
88
  if (!this.$input) {
89
- return;
89
+ return
90
90
  }
91
91
  if (this.$module.dataset.initialized) {
92
- return;
92
+ return
93
93
  }
94
94
 
95
- this.setOptions();
96
- this.initControls();
97
- this.$module.setAttribute("data-initialized", "true");
98
- };
95
+ this.setOptions()
96
+ this.initControls()
97
+ this.$module.setAttribute('data-initialized', 'true')
98
+ }
99
99
 
100
100
  Datepicker.prototype.initControls = function () {
101
- this.id = `datepicker-${this.$input.id}`;
101
+ this.id = `datepicker-${this.$input.id}`
102
102
 
103
- this.$dialog = this.createDialog();
104
- this.createCalendarHeaders();
103
+ this.$dialog = this.createDialog()
104
+ this.createCalendarHeaders()
105
105
 
106
- const $componentWrapper = document.createElement("div");
107
- const $inputWrapper = document.createElement("div");
108
- $componentWrapper.classList.add("moj-datepicker__wrapper");
109
- $inputWrapper.classList.add("govuk-input__wrapper");
106
+ const $componentWrapper = document.createElement('div')
107
+ const $inputWrapper = document.createElement('div')
108
+ $componentWrapper.classList.add('moj-datepicker__wrapper')
109
+ $inputWrapper.classList.add('govuk-input__wrapper')
110
110
 
111
- this.$input.parentNode.insertBefore($componentWrapper, this.$input);
112
- $componentWrapper.appendChild($inputWrapper);
113
- $inputWrapper.appendChild(this.$input);
111
+ this.$input.parentNode.insertBefore($componentWrapper, this.$input)
112
+ $componentWrapper.appendChild($inputWrapper)
113
+ $inputWrapper.appendChild(this.$input)
114
114
 
115
- $inputWrapper.insertAdjacentHTML("beforeend", this.toggleTemplate());
116
- $componentWrapper.insertAdjacentElement("beforeend", this.$dialog);
115
+ $inputWrapper.insertAdjacentHTML('beforeend', this.toggleTemplate())
116
+ $componentWrapper.insertAdjacentElement('beforeend', this.$dialog)
117
117
 
118
- this.$calendarButton = this.$module.querySelector(
119
- ".moj-js-datepicker-toggle",
120
- );
118
+ this.$calendarButton = this.$module.querySelector('.moj-js-datepicker-toggle')
121
119
  this.$dialogTitle = this.$dialog.querySelector(
122
- ".moj-js-datepicker-month-year",
123
- );
120
+ '.moj-js-datepicker-month-year'
121
+ )
124
122
 
125
- this.createCalendar();
123
+ this.createCalendar()
126
124
 
127
125
  this.$prevMonthButton = this.$dialog.querySelector(
128
- ".moj-js-datepicker-prev-month",
129
- );
126
+ '.moj-js-datepicker-prev-month'
127
+ )
130
128
  this.$prevYearButton = this.$dialog.querySelector(
131
- ".moj-js-datepicker-prev-year",
132
- );
129
+ '.moj-js-datepicker-prev-year'
130
+ )
133
131
  this.$nextMonthButton = this.$dialog.querySelector(
134
- ".moj-js-datepicker-next-month",
135
- );
132
+ '.moj-js-datepicker-next-month'
133
+ )
136
134
  this.$nextYearButton = this.$dialog.querySelector(
137
- ".moj-js-datepicker-next-year",
138
- );
139
- this.$cancelButton = this.$dialog.querySelector(".moj-js-datepicker-cancel");
140
- this.$okButton = this.$dialog.querySelector(".moj-js-datepicker-ok");
135
+ '.moj-js-datepicker-next-year'
136
+ )
137
+ this.$cancelButton = this.$dialog.querySelector('.moj-js-datepicker-cancel')
138
+ this.$okButton = this.$dialog.querySelector('.moj-js-datepicker-ok')
141
139
 
142
140
  // add event listeners
143
- this.$prevMonthButton.addEventListener("click", (event) =>
144
- this.focusPreviousMonth(event, false),
145
- );
146
- this.$prevYearButton.addEventListener("click", (event) =>
147
- this.focusPreviousYear(event, false),
148
- );
149
- this.$nextMonthButton.addEventListener("click", (event) =>
150
- this.focusNextMonth(event, false),
151
- );
152
- this.$nextYearButton.addEventListener("click", (event) =>
153
- this.focusNextYear(event, false),
154
- );
155
- this.$cancelButton.addEventListener("click", (event) => {
156
- event.preventDefault();
157
- this.closeDialog(event);
158
- });
159
- this.$okButton.addEventListener("click", () => {
160
- this.selectDate(this.currentDate);
161
- });
141
+ this.$prevMonthButton.addEventListener('click', (event) =>
142
+ this.focusPreviousMonth(event, false)
143
+ )
144
+ this.$prevYearButton.addEventListener('click', (event) =>
145
+ this.focusPreviousYear(event, false)
146
+ )
147
+ this.$nextMonthButton.addEventListener('click', (event) =>
148
+ this.focusNextMonth(event, false)
149
+ )
150
+ this.$nextYearButton.addEventListener('click', (event) =>
151
+ this.focusNextYear(event, false)
152
+ )
153
+ this.$cancelButton.addEventListener('click', (event) => {
154
+ event.preventDefault()
155
+ this.closeDialog(event)
156
+ })
157
+ this.$okButton.addEventListener('click', () => {
158
+ this.selectDate(this.currentDate)
159
+ })
162
160
 
163
161
  const dialogButtons = this.$dialog.querySelectorAll(
164
- 'button:not([disabled="true"])',
165
- );
162
+ 'button:not([disabled="true"])'
163
+ )
166
164
  // eslint-disable-next-line prefer-destructuring
167
- this.$firstButtonInDialog = dialogButtons[0];
168
- this.$lastButtonInDialog = dialogButtons[dialogButtons.length - 1];
169
- this.$firstButtonInDialog.addEventListener("keydown", (event) =>
170
- this.firstButtonKeydown(event),
171
- );
172
- this.$lastButtonInDialog.addEventListener("keydown", (event) =>
173
- this.lastButtonKeydown(event),
174
- );
175
-
176
- this.$calendarButton.addEventListener("click", (event) =>
177
- this.toggleDialog(event),
178
- );
179
-
180
- this.$dialog.addEventListener("keydown", (event) => {
181
- if (event.key === "Escape") {
182
- this.closeDialog();
183
- event.preventDefault();
184
- event.stopPropagation();
165
+ this.$firstButtonInDialog = dialogButtons[0]
166
+ this.$lastButtonInDialog = dialogButtons[dialogButtons.length - 1]
167
+ this.$firstButtonInDialog.addEventListener('keydown', (event) =>
168
+ this.firstButtonKeydown(event)
169
+ )
170
+ this.$lastButtonInDialog.addEventListener('keydown', (event) =>
171
+ this.lastButtonKeydown(event)
172
+ )
173
+
174
+ this.$calendarButton.addEventListener('click', (event) =>
175
+ this.toggleDialog(event)
176
+ )
177
+
178
+ this.$dialog.addEventListener('keydown', (event) => {
179
+ if (event.key === 'Escape') {
180
+ this.closeDialog()
181
+ event.preventDefault()
182
+ event.stopPropagation()
185
183
  }
186
- });
184
+ })
187
185
 
188
- document.body.addEventListener("mouseup", (event) =>
189
- this.backgroundClick(event),
190
- );
186
+ document.body.addEventListener('mouseup', (event) =>
187
+ this.backgroundClick(event)
188
+ )
191
189
 
192
190
  // populates calendar with initial dates, avoids Wave errors about null buttons
193
- this.updateCalendar();
194
- };
191
+ this.updateCalendar()
192
+ }
195
193
 
196
194
  Datepicker.prototype.createDialog = function () {
197
- const titleId = `datepicker-title-${this.$input.id}`;
198
- const $dialog = document.createElement("div");
199
-
200
- $dialog.id = this.id;
201
- $dialog.setAttribute("class", "moj-datepicker__dialog");
202
- $dialog.setAttribute("role", "dialog");
203
- $dialog.setAttribute("aria-modal", "true");
204
- $dialog.setAttribute("aria-labelledby", titleId);
205
- $dialog.innerHTML = this.dialogTemplate(titleId);
206
- $dialog.hidden = true;
207
-
208
- return $dialog;
209
- };
195
+ const titleId = `datepicker-title-${this.$input.id}`
196
+ const $dialog = document.createElement('div')
197
+
198
+ $dialog.id = this.id
199
+ $dialog.setAttribute('class', 'moj-datepicker__dialog')
200
+ $dialog.setAttribute('role', 'dialog')
201
+ $dialog.setAttribute('aria-modal', 'true')
202
+ $dialog.setAttribute('aria-labelledby', titleId)
203
+ $dialog.innerHTML = this.dialogTemplate(titleId)
204
+ $dialog.hidden = true
205
+
206
+ return $dialog
207
+ }
210
208
 
211
209
  Datepicker.prototype.createCalendar = function () {
212
- const $tbody = this.$dialog.querySelector("tbody");
213
- let dayCount = 0;
210
+ const $tbody = this.$dialog.querySelector('tbody')
211
+ let dayCount = 0
214
212
  for (let i = 0; i < 6; i++) {
215
213
  // create row
216
- const $row = $tbody.insertRow(i);
214
+ const $row = $tbody.insertRow(i)
217
215
 
218
216
  for (let j = 0; j < 7; j++) {
219
217
  // create cell (day)
220
- const $cell = document.createElement("td");
221
- const $dateButton = document.createElement("button");
218
+ const $cell = document.createElement('td')
219
+ const $dateButton = document.createElement('button')
222
220
 
223
- $cell.appendChild($dateButton);
224
- $row.appendChild($cell);
221
+ $cell.appendChild($dateButton)
222
+ $row.appendChild($cell)
225
223
 
226
- const calendarDay = new DSCalendarDay($dateButton, dayCount, i, j, this);
227
- calendarDay.init();
228
- this.calendarDays.push(calendarDay);
229
- dayCount++;
224
+ const calendarDay = new DSCalendarDay($dateButton, dayCount, i, j, this)
225
+ calendarDay.init()
226
+ this.calendarDays.push(calendarDay)
227
+ dayCount++
230
228
  }
231
229
  }
232
- };
230
+ }
233
231
 
234
232
  Datepicker.prototype.toggleTemplate = function () {
235
233
  return `<button class="moj-datepicker__toggle moj-js-datepicker-toggle" type="button" aria-haspopup="dialog" aria-controls="${this.id}" aria-expanded="false">
@@ -244,14 +242,14 @@ Datepicker.prototype.toggleTemplate = function () {
244
242
  <rect x="3.66669" width="1.46667" height="5.13333" rx="0.733333" fill="currentColor"></rect>
245
243
  <rect x="16.8667" width="1.46667" height="5.13333" rx="0.733333" fill="currentColor"></rect>
246
244
  </svg>
247
- </button>`;
248
- };
245
+ </button>`
246
+ }
249
247
 
250
248
  /**
251
249
  * HTML template for calendar dialog
252
250
  *
253
251
  * @param {string} [titleId] - Id attribute for dialog title
254
- * @return {string}
252
+ * @returns {string}
255
253
  */
256
254
  Datepicker.prototype.dialogTemplate = function (titleId) {
257
255
  return `<div class="moj-datepicker__dialog-header">
@@ -303,213 +301,220 @@ Datepicker.prototype.dialogTemplate = function (titleId) {
303
301
  <div class="govuk-button-group">
304
302
  <button type="button" class="govuk-button moj-js-datepicker-ok">Select</button>
305
303
  <button type="button" class="govuk-button govuk-button--secondary moj-js-datepicker-cancel">Close</button>
306
- </div>`;
307
- };
304
+ </div>`
305
+ }
308
306
 
309
307
  Datepicker.prototype.createCalendarHeaders = function () {
310
308
  this.dayLabels.forEach((day) => {
311
- const html = `<th scope="col"><span aria-hidden="true">${day.substring(0, 3)}</span><span class="govuk-visually-hidden">${day}</span></th>`;
312
- const $headerRow = this.$dialog.querySelector("thead > tr");
313
- $headerRow.insertAdjacentHTML("beforeend", html);
314
- });
315
- };
309
+ const html = `<th scope="col"><span aria-hidden="true">${day.substring(0, 3)}</span><span class="govuk-visually-hidden">${day}</span></th>`
310
+ const $headerRow = this.$dialog.querySelector('thead > tr')
311
+ $headerRow.insertAdjacentHTML('beforeend', html)
312
+ })
313
+ }
316
314
 
317
315
  /**
318
316
  * Pads given number with leading zeros
319
317
  *
320
318
  * @param {number} value - The value to be padded
321
319
  * @param {number} length - The length in characters of the output
322
- * @return {string}
320
+ * @returns {string}
323
321
  */
324
322
  Datepicker.prototype.leadingZeros = function (value, length = 2) {
325
- let ret = value.toString();
323
+ let ret = value.toString()
326
324
 
327
325
  while (ret.length < length) {
328
- ret = `0${ret}`;
326
+ ret = `0${ret}`
329
327
  }
330
328
 
331
- return ret;
332
- };
329
+ return ret
330
+ }
333
331
 
334
332
  Datepicker.prototype.setOptions = function () {
335
- this.setMinAndMaxDatesOnCalendar();
336
- this.setExcludedDates();
337
- this.setExcludedDays();
338
- this.setLeadingZeros();
339
- this.setWeekStartDay();
340
- };
333
+ this.setMinAndMaxDatesOnCalendar()
334
+ this.setExcludedDates()
335
+ this.setExcludedDays()
336
+ this.setLeadingZeros()
337
+ this.setWeekStartDay()
338
+ }
341
339
 
342
340
  Datepicker.prototype.setMinAndMaxDatesOnCalendar = function () {
343
341
  if (this.config.minDate) {
344
- this.minDate = this.formattedDateFromString(this.config.minDate, null);
342
+ this.minDate = this.formattedDateFromString(this.config.minDate, null)
345
343
  if (this.minDate && this.currentDate < this.minDate) {
346
- this.currentDate = this.minDate;
344
+ this.currentDate = this.minDate
347
345
  }
348
346
  }
349
347
 
350
348
  if (this.config.maxDate) {
351
- this.maxDate = this.formattedDateFromString(this.config.maxDate, null);
349
+ this.maxDate = this.formattedDateFromString(this.config.maxDate, null)
352
350
  if (this.maxDate && this.currentDate > this.maxDate) {
353
- this.currentDate = this.maxDate;
351
+ this.currentDate = this.maxDate
354
352
  }
355
353
  }
356
- };
354
+ }
357
355
 
358
356
  Datepicker.prototype.setExcludedDates = function () {
359
357
  if (this.config.excludedDates) {
360
358
  this.excludedDates = this.config.excludedDates
361
- .replace(/\s+/, " ")
362
- .split(" ")
359
+ .replace(/\s+/, ' ')
360
+ .split(' ')
363
361
  .map((item) => {
364
- if (item.includes("-")) {
365
- // parse the date range from the format "dd/mm/yyyy-dd/mm/yyyy"
366
- const [startDate, endDate] = item
367
- .split("-")
368
- .map((d) => this.formattedDateFromString(d, null));
369
- if (startDate && endDate) {
370
- const date = new Date(startDate.getTime());
371
- const dates = [];
372
- while (date <= endDate) {
373
- dates.push(new Date(date));
374
- date.setDate(date.getDate() + 1);
375
- }
376
- return dates;
377
- }
378
- } else {
379
- return this.formattedDateFromString(item, null);
380
- }
362
+ return item.includes('-')
363
+ ? this.parseDateRangeString(item)
364
+ : this.formattedDateFromString(item)
381
365
  })
382
366
  .flat()
383
- .filter((item) => item);
367
+ .filter((item) => item)
384
368
  }
385
- };
369
+ }
370
+
371
+ /*
372
+ * Parses a daterange string into an array of dates
373
+ * @param {String} datestring - A daterange string in the format "dd/mm/yyyy-dd/mm/yyyy"
374
+ * @returns {Date[]}
375
+ */
376
+ Datepicker.prototype.parseDateRangeString = function (datestring) {
377
+ const dates = []
378
+ const [startDate, endDate] = datestring
379
+ .split('-')
380
+ .map((d) => this.formattedDateFromString(d, null))
381
+
382
+ if (startDate && endDate) {
383
+ const date = new Date(startDate.getTime())
384
+ /* eslint-disable no-unmodified-loop-condition */
385
+ while (date <= endDate) {
386
+ dates.push(new Date(date))
387
+ date.setDate(date.getDate() + 1)
388
+ }
389
+ /* eslint-enable no-unmodified-loop-condition */
390
+ }
391
+ return dates
392
+ }
386
393
 
387
394
  Datepicker.prototype.setExcludedDays = function () {
388
395
  if (this.config.excludedDays) {
389
396
  // lowercase and arrange dayLabels to put indexOf sunday == 0 for comparison
390
397
  // with getDay() function
391
- let weekDays = this.dayLabels.map((item) => item.toLowerCase());
392
- if (this.config.weekStartDay === "monday") {
393
- weekDays.unshift(weekDays.pop());
398
+ const weekDays = this.dayLabels.map((item) => item.toLowerCase())
399
+ if (this.config.weekStartDay === 'monday') {
400
+ weekDays.unshift(weekDays.pop())
394
401
  }
395
402
 
396
403
  this.excludedDays = this.config.excludedDays
397
- .replace(/\s+/, " ")
404
+ .replace(/\s+/, ' ')
398
405
  .toLowerCase()
399
- .split(" ")
406
+ .split(' ')
400
407
  .map((item) => weekDays.indexOf(item))
401
- .filter((item) => item !== -1);
408
+ .filter((item) => item !== -1)
402
409
  }
403
- };
410
+ }
404
411
 
405
412
  Datepicker.prototype.setLeadingZeros = function () {
406
- if (typeof this.config.leadingZeros !== "boolean") {
407
- if (this.config.leadingZeros.toLowerCase() === "true") {
408
- this.config.leadingZeros = true;
409
- return;
413
+ if (typeof this.config.leadingZeros !== 'boolean') {
414
+ if (this.config.leadingZeros.toLowerCase() === 'true') {
415
+ this.config.leadingZeros = true
416
+ return
410
417
  }
411
- if (this.config.leadingZeros.toLowerCase() === "false") {
412
- this.config.leadingZeros = false;
413
- return;
418
+ if (this.config.leadingZeros.toLowerCase() === 'false') {
419
+ this.config.leadingZeros = false
414
420
  }
415
421
  }
416
- };
422
+ }
417
423
 
418
424
  Datepicker.prototype.setWeekStartDay = function () {
419
- const weekStartDayParam = this.config.weekStartDay;
420
- if (weekStartDayParam?.toLowerCase() === "sunday") {
421
- this.config.weekStartDay = "sunday";
425
+ const weekStartDayParam = this.config.weekStartDay
426
+ if (weekStartDayParam && weekStartDayParam.toLowerCase() === 'sunday') {
427
+ this.config.weekStartDay = 'sunday'
422
428
  // Rotate dayLabels array to put Sunday as the first item
423
- this.dayLabels.unshift(this.dayLabels.pop());
429
+ this.dayLabels.unshift(this.dayLabels.pop())
424
430
  } else {
425
- this.config.weekStartDay = "monday";
431
+ this.config.weekStartDay = 'monday'
426
432
  }
427
- };
433
+ }
428
434
 
429
435
  /**
430
436
  * Determine if a date is selecteable
431
437
  *
432
438
  * @param {Date} date - the date to check
433
- * @return {boolean}
434
- *
439
+ * @returns {boolean}
435
440
  */
436
441
  Datepicker.prototype.isExcludedDate = function (date) {
437
442
  // This comparison does not work correctly - it will exclude the mindate itself
438
443
  // see: https://github.com/ministryofjustice/moj-frontend/issues/923
439
444
  if (this.minDate && this.minDate > date) {
440
- return true;
445
+ return true
441
446
  }
442
447
 
443
448
  // This comparison works as expected - the maxdate will not be excluded
444
449
  if (this.maxDate && this.maxDate < date) {
445
- return true;
450
+ return true
446
451
  }
447
452
 
448
453
  for (const excludedDate of this.excludedDates) {
449
454
  if (date.toDateString() === excludedDate.toDateString()) {
450
- return true;
455
+ return true
451
456
  }
452
457
  }
453
458
 
454
459
  if (this.excludedDays.includes(date.getDay())) {
455
- return true;
460
+ return true
456
461
  }
457
462
 
458
- return false;
459
- };
463
+ return false
464
+ }
460
465
 
461
466
  /**
462
467
  * Get a Date object from a string
463
468
  *
464
469
  * @param {string} dateString - string in the format d/m/yyyy dd/mm/yyyy
465
470
  * @param {Date} fallback - date object to return if formatting fails
466
- * @return {Date}
471
+ * @returns {Date}
467
472
  */
468
473
  Datepicker.prototype.formattedDateFromString = function (
469
474
  dateString,
470
- fallback = new Date(),
475
+ fallback = new Date()
471
476
  ) {
472
- let formattedDate = null;
477
+ let formattedDate = null
473
478
  // Accepts d/m/yyyy and dd/mm/yyyy
474
- const dateFormatPattern = /(\d{1,2})([-/,. ])(\d{1,2})\2(\d{4})/;
479
+ const dateFormatPattern = /(\d{1,2})([-/,. ])(\d{1,2})\2(\d{4})/
475
480
 
476
- if (!dateFormatPattern.test(dateString)) return fallback;
481
+ if (!dateFormatPattern.test(dateString)) return fallback
477
482
 
478
- const match = dateString.match(dateFormatPattern);
479
- const day = match[1];
480
- const month = match[3];
481
- const year = match[4];
483
+ const match = dateString.match(dateFormatPattern)
484
+ const day = match[1]
485
+ const month = match[3]
486
+ const year = match[4]
482
487
 
483
- formattedDate = new Date(`${year}-${month}-${day}`);
488
+ formattedDate = new Date(`${year}-${month}-${day}`)
484
489
  if (formattedDate instanceof Date && !isNaN(formattedDate)) {
485
- return formattedDate;
490
+ return formattedDate
486
491
  }
487
- return fallback;
488
- };
492
+ return fallback
493
+ }
489
494
 
490
495
  /**
491
496
  * Get a formatted date string from a Date object
492
497
  *
493
498
  * @param {Date} date - date to format to a string
494
- * @return {string}
499
+ * @returns {string}
495
500
  */
496
501
  Datepicker.prototype.formattedDateFromDate = function (date) {
497
502
  if (this.config.leadingZeros) {
498
- return `${this.leadingZeros(date.getDate())}/${this.leadingZeros(date.getMonth() + 1)}/${date.getFullYear()}`;
499
- } else {
500
- return `${date.getDate()}/${date.getMonth() + 1}/${date.getFullYear()}`;
503
+ return `${this.leadingZeros(date.getDate())}/${this.leadingZeros(date.getMonth() + 1)}/${date.getFullYear()}`
501
504
  }
502
- };
505
+
506
+ return `${date.getDate()}/${date.getMonth() + 1}/${date.getFullYear()}`
507
+ }
503
508
 
504
509
  /**
505
510
  * Get a human readable date in the format Monday 2 March 2024
506
511
  *
507
- * @param {Date} - date to format
508
- * @return {string}
512
+ * @param {Date} date - date to format
513
+ * @returns {string}
509
514
  */
510
515
  Datepicker.prototype.formattedDateHuman = function (date) {
511
- return `${this.dayLabels[(date.getDay() + 6) % 7]} ${date.getDate()} ${this.monthLabels[date.getMonth()]} ${date.getFullYear()}`;
512
- };
516
+ return `${this.dayLabels[(date.getDay() + 6) % 7]} ${date.getDate()} ${this.monthLabels[date.getMonth()]} ${date.getFullYear()}`
517
+ }
513
518
 
514
519
  Datepicker.prototype.backgroundClick = function (event) {
515
520
  if (
@@ -518,75 +523,75 @@ Datepicker.prototype.backgroundClick = function (event) {
518
523
  !this.$input.contains(event.target) &&
519
524
  !this.$calendarButton.contains(event.target)
520
525
  ) {
521
- event.preventDefault();
522
- this.closeDialog();
526
+ event.preventDefault()
527
+ this.closeDialog()
523
528
  }
524
- };
529
+ }
525
530
 
526
531
  Datepicker.prototype.firstButtonKeydown = function (event) {
527
- if (event.key === "Tab" && event.shiftKey) {
528
- this.$lastButtonInDialog.focus();
529
- event.preventDefault();
532
+ if (event.key === 'Tab' && event.shiftKey) {
533
+ this.$lastButtonInDialog.focus()
534
+ event.preventDefault()
530
535
  }
531
- };
536
+ }
532
537
 
533
538
  Datepicker.prototype.lastButtonKeydown = function (event) {
534
- if (event.key === "Tab" && !event.shiftKey) {
535
- this.$firstButtonInDialog.focus();
536
- event.preventDefault();
539
+ if (event.key === 'Tab' && !event.shiftKey) {
540
+ this.$firstButtonInDialog.focus()
541
+ event.preventDefault()
537
542
  }
538
- };
543
+ }
539
544
 
540
545
  // render calendar
541
546
  Datepicker.prototype.updateCalendar = function () {
542
- this.$dialogTitle.innerHTML = `${this.monthLabels[this.currentDate.getMonth()]} ${this.currentDate.getFullYear()}`;
547
+ this.$dialogTitle.innerHTML = `${this.monthLabels[this.currentDate.getMonth()]} ${this.currentDate.getFullYear()}`
543
548
 
544
- const day = this.currentDate;
545
- const firstOfMonth = new Date(day.getFullYear(), day.getMonth(), 1);
546
- let dayOfWeek;
549
+ const day = this.currentDate
550
+ const firstOfMonth = new Date(day.getFullYear(), day.getMonth(), 1)
551
+ let dayOfWeek
547
552
 
548
- if (this.config.weekStartDay === "monday") {
549
- dayOfWeek = firstOfMonth.getDay() === 0 ? 6 : firstOfMonth.getDay() - 1; // Change logic to make Monday first day of week, i.e. 0
553
+ if (this.config.weekStartDay === 'monday') {
554
+ dayOfWeek = firstOfMonth.getDay() === 0 ? 6 : firstOfMonth.getDay() - 1 // Change logic to make Monday first day of week, i.e. 0
550
555
  } else {
551
- dayOfWeek = firstOfMonth.getDay();
556
+ dayOfWeek = firstOfMonth.getDay()
552
557
  }
553
558
 
554
- firstOfMonth.setDate(firstOfMonth.getDate() - dayOfWeek);
559
+ firstOfMonth.setDate(firstOfMonth.getDate() - dayOfWeek)
555
560
 
556
- const thisDay = new Date(firstOfMonth);
561
+ const thisDay = new Date(firstOfMonth)
557
562
 
558
563
  // loop through our days
559
564
  for (let i = 0; i < this.calendarDays.length; i++) {
560
- const hidden = thisDay.getMonth() !== day.getMonth();
561
- const disabled = this.isExcludedDate(thisDay);
565
+ const hidden = thisDay.getMonth() !== day.getMonth()
566
+ const disabled = this.isExcludedDate(thisDay)
562
567
 
563
- this.calendarDays[i].update(thisDay, hidden, disabled);
568
+ this.calendarDays[i].update(thisDay, hidden, disabled)
564
569
 
565
- thisDay.setDate(thisDay.getDate() + 1);
570
+ thisDay.setDate(thisDay.getDate() + 1)
566
571
  }
567
- };
572
+ }
568
573
 
569
574
  Datepicker.prototype.setCurrentDate = function (focus = true) {
570
- const { currentDate } = this;
575
+ const { currentDate } = this
571
576
  this.calendarDays.forEach((calendarDay) => {
572
- calendarDay.button.classList.add("moj-datepicker__button");
573
- calendarDay.button.classList.add("moj-datepicker__calendar-day");
574
- calendarDay.button.setAttribute("tabindex", -1);
575
- calendarDay.button.classList.remove(this.selectedDayButtonClass);
576
- const calendarDayDate = calendarDay.date;
577
- calendarDayDate.setHours(0, 0, 0, 0);
577
+ calendarDay.button.classList.add('moj-datepicker__button')
578
+ calendarDay.button.classList.add('moj-datepicker__calendar-day')
579
+ calendarDay.button.setAttribute('tabindex', -1)
580
+ calendarDay.button.classList.remove(this.selectedDayButtonClass)
581
+ const calendarDayDate = calendarDay.date
582
+ calendarDayDate.setHours(0, 0, 0, 0)
578
583
 
579
- const today = new Date();
580
- today.setHours(0, 0, 0, 0);
584
+ const today = new Date()
585
+ today.setHours(0, 0, 0, 0)
581
586
 
582
587
  if (
583
588
  calendarDayDate.getTime() ===
584
589
  currentDate.getTime() /* && !calendarDay.button.disabled */
585
590
  ) {
586
591
  if (focus) {
587
- calendarDay.button.setAttribute("tabindex", 0);
588
- calendarDay.button.focus();
589
- calendarDay.button.classList.add(this.selectedDayButtonClass);
592
+ calendarDay.button.setAttribute('tabindex', 0)
593
+ calendarDay.button.focus()
594
+ calendarDay.button.classList.add(this.selectedDayButtonClass)
590
595
  }
591
596
  }
592
597
 
@@ -594,213 +599,210 @@ Datepicker.prototype.setCurrentDate = function (focus = true) {
594
599
  this.inputDate &&
595
600
  calendarDayDate.getTime() === this.inputDate.getTime()
596
601
  ) {
597
- calendarDay.button.classList.add(this.currentDayButtonClass);
598
- calendarDay.button.setAttribute("aria-current", "date");
602
+ calendarDay.button.classList.add(this.currentDayButtonClass)
603
+ calendarDay.button.setAttribute('aria-current', 'date')
599
604
  } else {
600
- calendarDay.button.classList.remove(this.currentDayButtonClass);
601
- calendarDay.button.removeAttribute("aria-current");
605
+ calendarDay.button.classList.remove(this.currentDayButtonClass)
606
+ calendarDay.button.removeAttribute('aria-current')
602
607
  }
603
608
 
604
609
  if (calendarDayDate.getTime() === today.getTime()) {
605
- calendarDay.button.classList.add(this.todayButtonClass);
610
+ calendarDay.button.classList.add(this.todayButtonClass)
606
611
  } else {
607
- calendarDay.button.classList.remove(this.todayButtonClass);
612
+ calendarDay.button.classList.remove(this.todayButtonClass)
608
613
  }
609
- });
614
+ })
610
615
 
611
616
  // if no date is tab-able, make the first non-disabled date tab-able
612
617
  if (!focus) {
613
618
  const enabledDays = this.calendarDays.filter((calendarDay) => {
614
619
  return (
615
- window.getComputedStyle(calendarDay.button).display === "block" &&
620
+ window.getComputedStyle(calendarDay.button).display === 'block' &&
616
621
  !calendarDay.button.disabled
617
- );
618
- });
622
+ )
623
+ })
619
624
 
620
- enabledDays[0].button.setAttribute("tabindex", 0);
625
+ enabledDays[0].button.setAttribute('tabindex', 0)
621
626
 
622
- this.currentDate = enabledDays[0].date;
627
+ this.currentDate = enabledDays[0].date
623
628
  }
624
- };
629
+ }
625
630
 
626
631
  Datepicker.prototype.selectDate = function (date) {
627
632
  if (this.isExcludedDate(date)) {
628
- return;
633
+ return
629
634
  }
630
635
 
631
- this.$calendarButton.querySelector("span").innerText =
632
- `Choose date. Selected date is ${this.formattedDateHuman(date)}`;
633
- this.$input.value = this.formattedDateFromDate(date);
636
+ this.$calendarButton.querySelector('span').innerText =
637
+ `Choose date. Selected date is ${this.formattedDateHuman(date)}`
638
+ this.$input.value = this.formattedDateFromDate(date)
634
639
 
635
- const changeEvent = new Event("change", { bubbles: true, cancelable: true });
636
- this.$input.dispatchEvent(changeEvent);
640
+ const changeEvent = new Event('change', { bubbles: true, cancelable: true })
641
+ this.$input.dispatchEvent(changeEvent)
637
642
 
638
- this.closeDialog();
639
- };
643
+ this.closeDialog()
644
+ }
640
645
 
641
646
  Datepicker.prototype.isOpen = function () {
642
- return this.$dialog.classList.contains("moj-datepicker__dialog--open");
643
- };
647
+ return this.$dialog.classList.contains('moj-datepicker__dialog--open')
648
+ }
644
649
 
645
650
  Datepicker.prototype.toggleDialog = function (event) {
646
- event.preventDefault();
651
+ event.preventDefault()
647
652
  if (this.isOpen()) {
648
- this.closeDialog();
653
+ this.closeDialog()
649
654
  } else {
650
- this.setMinAndMaxDatesOnCalendar();
651
- this.openDialog();
655
+ this.setMinAndMaxDatesOnCalendar()
656
+ this.openDialog()
652
657
  }
653
- };
658
+ }
654
659
 
655
660
  Datepicker.prototype.openDialog = function () {
656
- this.$dialog.hidden = false;
657
- this.$dialog.classList.add("moj-datepicker__dialog--open");
658
- this.$calendarButton.setAttribute("aria-expanded", "true");
661
+ this.$dialog.hidden = false
662
+ this.$dialog.classList.add('moj-datepicker__dialog--open')
663
+ this.$calendarButton.setAttribute('aria-expanded', 'true')
659
664
 
660
665
  // position the dialog
661
666
  // if input is wider than dialog pin it to the right
662
667
  if (this.$input.offsetWidth > this.$dialog.offsetWidth) {
663
- this.$dialog.style.right = `0px`;
668
+ this.$dialog.style.right = `0px`
664
669
  }
665
- this.$dialog.style.top = `${this.$input.offsetHeight + 3}px`;
670
+ this.$dialog.style.top = `${this.$input.offsetHeight + 3}px`
666
671
 
667
672
  // get the date from the input element
668
- this.inputDate = this.formattedDateFromString(this.$input.value);
669
- this.currentDate = this.inputDate;
670
- this.currentDate.setHours(0, 0, 0, 0);
673
+ this.inputDate = this.formattedDateFromString(this.$input.value)
674
+ this.currentDate = this.inputDate
675
+ this.currentDate.setHours(0, 0, 0, 0)
671
676
 
672
- this.updateCalendar();
673
- this.setCurrentDate();
674
- };
677
+ this.updateCalendar()
678
+ this.setCurrentDate()
679
+ }
675
680
 
676
681
  Datepicker.prototype.closeDialog = function () {
677
- this.$dialog.hidden = true;
678
- this.$dialog.classList.remove("moj-datepicker__dialog--open");
679
- this.$calendarButton.setAttribute("aria-expanded", "false");
680
- this.$calendarButton.focus();
681
- };
682
+ this.$dialog.hidden = true
683
+ this.$dialog.classList.remove('moj-datepicker__dialog--open')
684
+ this.$calendarButton.setAttribute('aria-expanded', 'false')
685
+ this.$calendarButton.focus()
686
+ }
682
687
 
683
688
  Datepicker.prototype.goToDate = function (date, focus) {
684
- const current = this.currentDate;
685
- this.currentDate = date;
689
+ const current = this.currentDate
690
+ this.currentDate = date
686
691
 
687
692
  if (
688
693
  current.getMonth() !== this.currentDate.getMonth() ||
689
694
  current.getFullYear() !== this.currentDate.getFullYear()
690
695
  ) {
691
- this.updateCalendar();
696
+ this.updateCalendar()
692
697
  }
693
698
 
694
- this.setCurrentDate(focus);
695
- };
699
+ this.setCurrentDate(focus)
700
+ }
696
701
 
697
702
  // day navigation
698
703
  Datepicker.prototype.focusNextDay = function () {
699
- const date = new Date(this.currentDate);
700
- date.setDate(date.getDate() + 1);
701
- this.goToDate(date);
702
- };
704
+ const date = new Date(this.currentDate)
705
+ date.setDate(date.getDate() + 1)
706
+ this.goToDate(date)
707
+ }
703
708
 
704
709
  Datepicker.prototype.focusPreviousDay = function () {
705
- const date = new Date(this.currentDate);
706
- date.setDate(date.getDate() - 1);
707
- this.goToDate(date);
708
- };
710
+ const date = new Date(this.currentDate)
711
+ date.setDate(date.getDate() - 1)
712
+ this.goToDate(date)
713
+ }
709
714
 
710
715
  // week navigation
711
716
  Datepicker.prototype.focusNextWeek = function () {
712
- const date = new Date(this.currentDate);
713
- date.setDate(date.getDate() + 7);
714
- this.goToDate(date);
715
- };
717
+ const date = new Date(this.currentDate)
718
+ date.setDate(date.getDate() + 7)
719
+ this.goToDate(date)
720
+ }
716
721
 
717
722
  Datepicker.prototype.focusPreviousWeek = function () {
718
- const date = new Date(this.currentDate);
719
- date.setDate(date.getDate() - 7);
720
- this.goToDate(date);
721
- };
723
+ const date = new Date(this.currentDate)
724
+ date.setDate(date.getDate() - 7)
725
+ this.goToDate(date)
726
+ }
722
727
 
723
728
  Datepicker.prototype.focusFirstDayOfWeek = function () {
724
- const date = new Date(this.currentDate);
725
- const firstDayOfWeekIndex = this.config.weekStartDay == "sunday" ? 0 : 1;
726
- const dayOfWeek = date.getDay();
729
+ const date = new Date(this.currentDate)
730
+ const firstDayOfWeekIndex = this.config.weekStartDay === 'sunday' ? 0 : 1
731
+ const dayOfWeek = date.getDay()
727
732
  const diff =
728
733
  dayOfWeek >= firstDayOfWeekIndex
729
734
  ? dayOfWeek - firstDayOfWeekIndex
730
- : 6 - dayOfWeek;
735
+ : 6 - dayOfWeek
731
736
 
732
- date.setDate(date.getDate() - diff);
733
- date.setHours(0, 0, 0, 0);
737
+ date.setDate(date.getDate() - diff)
738
+ date.setHours(0, 0, 0, 0)
734
739
 
735
- this.goToDate(date);
736
- };
740
+ this.goToDate(date)
741
+ }
737
742
 
738
743
  Datepicker.prototype.focusLastDayOfWeek = function () {
739
- const date = new Date(this.currentDate);
740
- const lastDayOfWeekIndex = this.config.weekStartDay == "sunday" ? 6 : 0;
741
- const dayOfWeek = date.getDay();
744
+ const date = new Date(this.currentDate)
745
+ const lastDayOfWeekIndex = this.config.weekStartDay === 'sunday' ? 6 : 0
746
+ const dayOfWeek = date.getDay()
742
747
  const diff =
743
748
  dayOfWeek <= lastDayOfWeekIndex
744
749
  ? lastDayOfWeekIndex - dayOfWeek
745
- : 7 - dayOfWeek;
750
+ : 7 - dayOfWeek
746
751
 
747
- date.setDate(date.getDate() + diff);
748
- date.setHours(0, 0, 0, 0);
752
+ date.setDate(date.getDate() + diff)
753
+ date.setHours(0, 0, 0, 0)
749
754
 
750
- this.goToDate(date);
751
- };
755
+ this.goToDate(date)
756
+ }
752
757
 
753
758
  // month navigation
754
759
  Datepicker.prototype.focusNextMonth = function (event, focus = true) {
755
- event.preventDefault();
756
- const date = new Date(this.currentDate);
757
- date.setMonth(date.getMonth() + 1, 1);
758
- this.goToDate(date, focus);
759
- };
760
+ event.preventDefault()
761
+ const date = new Date(this.currentDate)
762
+ date.setMonth(date.getMonth() + 1, 1)
763
+ this.goToDate(date, focus)
764
+ }
760
765
 
761
766
  Datepicker.prototype.focusPreviousMonth = function (event, focus = true) {
762
- event.preventDefault();
763
- const date = new Date(this.currentDate);
764
- date.setMonth(date.getMonth() - 1, 1);
765
- this.goToDate(date, focus);
766
- };
767
+ event.preventDefault()
768
+ const date = new Date(this.currentDate)
769
+ date.setMonth(date.getMonth() - 1, 1)
770
+ this.goToDate(date, focus)
771
+ }
767
772
 
768
773
  // year navigation
769
774
  Datepicker.prototype.focusNextYear = function (event, focus = true) {
770
- event.preventDefault();
771
- const date = new Date(this.currentDate);
772
- date.setFullYear(date.getFullYear() + 1, date.getMonth(), 1);
773
- this.goToDate(date, focus);
774
- };
775
+ event.preventDefault()
776
+ const date = new Date(this.currentDate)
777
+ date.setFullYear(date.getFullYear() + 1, date.getMonth(), 1)
778
+ this.goToDate(date, focus)
779
+ }
775
780
 
776
781
  Datepicker.prototype.focusPreviousYear = function (event, focus = true) {
777
- event.preventDefault();
778
- const date = new Date(this.currentDate);
779
- date.setFullYear(date.getFullYear() - 1, date.getMonth(), 1);
780
- this.goToDate(date, focus);
781
- };
782
+ event.preventDefault()
783
+ const date = new Date(this.currentDate)
784
+ date.setFullYear(date.getFullYear() - 1, date.getMonth(), 1)
785
+ this.goToDate(date, focus)
786
+ }
782
787
 
783
788
  /**
784
789
  * Parse dataset
785
790
  *
786
- * Loop over an object and normalise each value using {@link normaliseString},
787
- * optionally expanding nested `i18n.field`
788
- *
789
- * @param {{ schema: Schema }} Component - Component class
791
+ * @param {Schema} schema - Component class
790
792
  * @param {DOMStringMap} dataset - HTML element dataset
791
- * @returns {Object} Normalised dataset
793
+ * @returns {object} Normalised dataset
792
794
  */
793
795
  Datepicker.prototype.parseDataset = function (schema, dataset) {
794
- const parsed = {};
796
+ const parsed = {}
795
797
 
796
- for (const [field, attributes] of Object.entries(schema.properties)) {
798
+ for (const [field, ,] of Object.entries(schema.properties)) {
797
799
  if (field in dataset) {
798
- parsed[field] = dataset[field];
800
+ parsed[field] = dataset[field]
799
801
  }
800
802
  }
801
803
 
802
- return parsed;
803
- };
804
+ return parsed
805
+ }
804
806
 
805
807
  /**
806
808
  * Config merging function
@@ -812,28 +814,28 @@ Datepicker.prototype.parseDataset = function (schema, dataset) {
812
814
  * @returns {{ [key: string]: unknown }} A merged config object
813
815
  */
814
816
  Datepicker.prototype.mergeConfigs = function (...configObjects) {
815
- const formattedConfigObject = {};
817
+ const formattedConfigObject = {}
816
818
 
817
819
  // Loop through each of the passed objects
818
820
  for (const configObject of configObjects) {
819
821
  for (const key of Object.keys(configObject)) {
820
- const option = formattedConfigObject[key];
821
- const override = configObject[key];
822
+ const option = formattedConfigObject[key]
823
+ const override = configObject[key]
822
824
 
823
825
  // Push their keys one-by-one into formattedConfigObject. Any duplicate
824
826
  // keys with object values will be merged, otherwise the new value will
825
827
  // override the existing value.
826
- if (typeof option === "object" && typeof override === "object") {
828
+ if (typeof option === 'object' && typeof override === 'object') {
827
829
  // @ts-expect-error Index signature for type 'string' is missing
828
- formattedConfigObject[key] = this.mergeConfigs(option, override);
830
+ formattedConfigObject[key] = this.mergeConfigs(option, override)
829
831
  } else {
830
- formattedConfigObject[key] = override;
832
+ formattedConfigObject[key] = override
831
833
  }
832
834
  }
833
835
  }
834
836
 
835
- return formattedConfigObject;
836
- };
837
+ return formattedConfigObject
838
+ }
837
839
 
838
840
  /**
839
841
  *
@@ -842,22 +844,22 @@ Datepicker.prototype.mergeConfigs = function (...configObjects) {
842
844
  * @param {number} row
843
845
  * @param {number} column
844
846
  * @param {Datepicker} picker
845
- * @constructor
847
+ * @class
846
848
  */
847
849
  function DSCalendarDay(button, index, row, column, picker) {
848
- this.index = index;
849
- this.row = row;
850
- this.column = column;
851
- this.button = button;
852
- this.picker = picker;
850
+ this.index = index
851
+ this.row = row
852
+ this.column = column
853
+ this.button = button
854
+ this.picker = picker
853
855
 
854
- this.date = new Date();
856
+ this.date = new Date()
855
857
  }
856
858
 
857
859
  DSCalendarDay.prototype.init = function () {
858
- this.button.addEventListener("keydown", this.keyPress.bind(this));
859
- this.button.addEventListener("click", this.click.bind(this));
860
- };
860
+ this.button.addEventListener('keydown', this.keyPress.bind(this))
861
+ this.button.addEventListener('click', this.click.bind(this))
862
+ }
861
863
 
862
864
  /**
863
865
  * @param {Date} day - the Date for the calendar day
@@ -865,84 +867,84 @@ DSCalendarDay.prototype.init = function () {
865
867
  * @param {boolean} disabled - is the day selectable or excluded
866
868
  */
867
869
  DSCalendarDay.prototype.update = function (day, hidden, disabled) {
868
- let label = day.getDate();
869
- let accessibleLabel = this.picker.formattedDateHuman(day);
870
+ const label = day.getDate()
871
+ let accessibleLabel = this.picker.formattedDateHuman(day)
870
872
 
871
873
  if (disabled) {
872
- this.button.setAttribute("aria-disabled", true);
873
- accessibleLabel = "Excluded date, " + accessibleLabel;
874
+ this.button.setAttribute('aria-disabled', true)
875
+ accessibleLabel = `Excluded date, ${accessibleLabel}`
874
876
  } else {
875
- this.button.removeAttribute("aria-disabled");
877
+ this.button.removeAttribute('aria-disabled')
876
878
  }
877
879
 
878
880
  if (hidden) {
879
- this.button.style.display = "none";
881
+ this.button.style.display = 'none'
880
882
  } else {
881
- this.button.style.display = "block";
883
+ this.button.style.display = 'block'
882
884
  }
883
885
  this.button.setAttribute(
884
- "data-testid",
885
- this.picker.formattedDateFromDate(day),
886
- );
886
+ 'data-testid',
887
+ this.picker.formattedDateFromDate(day)
888
+ )
887
889
 
888
- this.button.innerHTML = `<span class="govuk-visually-hidden">${accessibleLabel}</span><span aria-hidden="true">${label}</span>`;
889
- this.date = new Date(day);
890
- };
890
+ this.button.innerHTML = `<span class="govuk-visually-hidden">${accessibleLabel}</span><span aria-hidden="true">${label}</span>`
891
+ this.date = new Date(day)
892
+ }
891
893
 
892
894
  DSCalendarDay.prototype.click = function (event) {
893
- this.picker.goToDate(this.date);
894
- this.picker.selectDate(this.date);
895
+ this.picker.goToDate(this.date)
896
+ this.picker.selectDate(this.date)
895
897
 
896
- event.stopPropagation();
897
- event.preventDefault();
898
- };
898
+ event.stopPropagation()
899
+ event.preventDefault()
900
+ }
899
901
 
900
902
  DSCalendarDay.prototype.keyPress = function (event) {
901
- let calendarNavKey = true;
903
+ let calendarNavKey = true
902
904
 
903
905
  switch (event.key) {
904
- case "ArrowLeft":
905
- this.picker.focusPreviousDay();
906
- break;
907
- case "ArrowRight":
908
- this.picker.focusNextDay();
909
- break;
910
- case "ArrowUp":
911
- this.picker.focusPreviousWeek();
912
- break;
913
- case "ArrowDown":
914
- this.picker.focusNextWeek();
915
- break;
916
- case "Home":
917
- this.picker.focusFirstDayOfWeek();
918
- break;
919
- case "End":
920
- this.picker.focusLastDayOfWeek();
921
- break;
922
- case "PageUp":
906
+ case 'ArrowLeft':
907
+ this.picker.focusPreviousDay()
908
+ break
909
+ case 'ArrowRight':
910
+ this.picker.focusNextDay()
911
+ break
912
+ case 'ArrowUp':
913
+ this.picker.focusPreviousWeek()
914
+ break
915
+ case 'ArrowDown':
916
+ this.picker.focusNextWeek()
917
+ break
918
+ case 'Home':
919
+ this.picker.focusFirstDayOfWeek()
920
+ break
921
+ case 'End':
922
+ this.picker.focusLastDayOfWeek()
923
+ break
924
+ case 'PageUp':
923
925
  // eslint-disable-next-line no-unused-expressions
924
926
  event.shiftKey
925
927
  ? this.picker.focusPreviousYear(event)
926
- : this.picker.focusPreviousMonth(event);
927
- break;
928
- case "PageDown":
928
+ : this.picker.focusPreviousMonth(event)
929
+ break
930
+ case 'PageDown':
929
931
  // eslint-disable-next-line no-unused-expressions
930
932
  event.shiftKey
931
933
  ? this.picker.focusNextYear(event)
932
- : this.picker.focusNextMonth(event);
933
- break;
934
+ : this.picker.focusNextMonth(event)
935
+ break
934
936
  default:
935
- calendarNavKey = false;
936
- break;
937
+ calendarNavKey = false
938
+ break
937
939
  }
938
940
 
939
941
  if (calendarNavKey) {
940
- event.preventDefault();
941
- event.stopPropagation();
942
+ event.preventDefault()
943
+ event.stopPropagation()
942
944
  }
943
- };
945
+ }
944
946
 
945
- MOJFrontend.DatePicker = Datepicker;
947
+ MOJFrontend.DatePicker = Datepicker
946
948
 
947
949
  /**
948
950
  * Schema for component config