@ministryofjustice/frontend 5.0.0 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/moj/all.bundle.js +1549 -1062
  2. package/moj/all.bundle.js.map +1 -1
  3. package/moj/all.bundle.mjs +1845 -1054
  4. package/moj/all.bundle.mjs.map +1 -1
  5. package/moj/all.mjs +7 -90
  6. package/moj/all.mjs.map +1 -1
  7. package/moj/all.scss +1 -0
  8. package/moj/all.scss.map +1 -1
  9. package/moj/common/index.mjs +57 -0
  10. package/moj/common/index.mjs.map +1 -0
  11. package/moj/common/moj-frontend-version.mjs +14 -0
  12. package/moj/common/moj-frontend-version.mjs.map +1 -0
  13. package/moj/components/add-another/add-another.bundle.js +105 -76
  14. package/moj/components/add-another/add-another.bundle.js.map +1 -1
  15. package/moj/components/add-another/add-another.bundle.mjs +222 -71
  16. package/moj/components/add-another/add-another.bundle.mjs.map +1 -1
  17. package/moj/components/add-another/add-another.mjs +103 -72
  18. package/moj/components/add-another/add-another.mjs.map +1 -1
  19. package/moj/components/alert/alert.bundle.js +115 -191
  20. package/moj/components/alert/alert.bundle.js.map +1 -1
  21. package/moj/components/alert/alert.bundle.mjs +354 -186
  22. package/moj/components/alert/alert.bundle.mjs.map +1 -1
  23. package/moj/components/alert/alert.mjs +55 -140
  24. package/moj/components/alert/alert.mjs.map +1 -1
  25. package/moj/components/button-menu/README.md +3 -1
  26. package/moj/components/button-menu/button-menu.bundle.js +91 -120
  27. package/moj/components/button-menu/button-menu.bundle.js.map +1 -1
  28. package/moj/components/button-menu/button-menu.bundle.mjs +329 -114
  29. package/moj/components/button-menu/button-menu.bundle.mjs.map +1 -1
  30. package/moj/components/button-menu/button-menu.mjs +89 -116
  31. package/moj/components/button-menu/button-menu.mjs.map +1 -1
  32. package/moj/components/date-picker/date-picker.bundle.js +174 -154
  33. package/moj/components/date-picker/date-picker.bundle.js.map +1 -1
  34. package/moj/components/date-picker/date-picker.bundle.mjs +411 -147
  35. package/moj/components/date-picker/date-picker.bundle.mjs.map +1 -1
  36. package/moj/components/date-picker/date-picker.mjs +172 -150
  37. package/moj/components/date-picker/date-picker.mjs.map +1 -1
  38. package/moj/components/filter/template.njk +1 -1
  39. package/moj/components/filter-toggle-button/filter-toggle-button.bundle.js +133 -44
  40. package/moj/components/filter-toggle-button/filter-toggle-button.bundle.js.map +1 -1
  41. package/moj/components/filter-toggle-button/filter-toggle-button.bundle.mjs +374 -41
  42. package/moj/components/filter-toggle-button/filter-toggle-button.bundle.mjs.map +1 -1
  43. package/moj/components/filter-toggle-button/filter-toggle-button.mjs +131 -40
  44. package/moj/components/filter-toggle-button/filter-toggle-button.mjs.map +1 -1
  45. package/moj/components/form-validator/form-validator.bundle.js +159 -69
  46. package/moj/components/form-validator/form-validator.bundle.js.map +1 -1
  47. package/moj/components/form-validator/form-validator.bundle.mjs +399 -65
  48. package/moj/components/form-validator/form-validator.bundle.mjs.map +1 -1
  49. package/moj/components/form-validator/form-validator.mjs +134 -54
  50. package/moj/components/form-validator/form-validator.mjs.map +1 -1
  51. package/moj/components/multi-file-upload/multi-file-upload.bundle.js +291 -117
  52. package/moj/components/multi-file-upload/multi-file-upload.bundle.js.map +1 -1
  53. package/moj/components/multi-file-upload/multi-file-upload.bundle.mjs +527 -109
  54. package/moj/components/multi-file-upload/multi-file-upload.bundle.mjs.map +1 -1
  55. package/moj/components/multi-file-upload/multi-file-upload.mjs +288 -101
  56. package/moj/components/multi-file-upload/multi-file-upload.mjs.map +1 -1
  57. package/moj/components/multi-file-upload/template.njk +1 -1
  58. package/moj/components/multi-select/multi-select.bundle.js +106 -41
  59. package/moj/components/multi-select/multi-select.bundle.js.map +1 -1
  60. package/moj/components/multi-select/multi-select.bundle.mjs +346 -37
  61. package/moj/components/multi-select/multi-select.bundle.mjs.map +1 -1
  62. package/moj/components/multi-select/multi-select.mjs +104 -37
  63. package/moj/components/multi-select/multi-select.mjs.map +1 -1
  64. package/moj/components/password-reveal/_password-reveal.scss +3 -1
  65. package/moj/components/password-reveal/_password-reveal.scss.map +1 -1
  66. package/moj/components/password-reveal/password-reveal.bundle.js +32 -29
  67. package/moj/components/password-reveal/password-reveal.bundle.js.map +1 -1
  68. package/moj/components/password-reveal/password-reveal.bundle.mjs +149 -24
  69. package/moj/components/password-reveal/password-reveal.bundle.mjs.map +1 -1
  70. package/moj/components/password-reveal/password-reveal.mjs +30 -25
  71. package/moj/components/password-reveal/password-reveal.mjs.map +1 -1
  72. package/moj/components/rich-text-editor/README.md +4 -3
  73. package/moj/components/rich-text-editor/rich-text-editor.bundle.js +127 -62
  74. package/moj/components/rich-text-editor/rich-text-editor.bundle.js.map +1 -1
  75. package/moj/components/rich-text-editor/rich-text-editor.bundle.mjs +367 -58
  76. package/moj/components/rich-text-editor/rich-text-editor.bundle.mjs.map +1 -1
  77. package/moj/components/rich-text-editor/rich-text-editor.mjs +125 -58
  78. package/moj/components/rich-text-editor/rich-text-editor.mjs.map +1 -1
  79. package/moj/components/search-toggle/search-toggle.bundle.js +94 -26
  80. package/moj/components/search-toggle/search-toggle.bundle.js.map +1 -1
  81. package/moj/components/search-toggle/search-toggle.bundle.mjs +334 -22
  82. package/moj/components/search-toggle/search-toggle.bundle.mjs.map +1 -1
  83. package/moj/components/search-toggle/search-toggle.mjs +92 -22
  84. package/moj/components/search-toggle/search-toggle.mjs.map +1 -1
  85. package/moj/components/sortable-table/sortable-table.bundle.js +151 -83
  86. package/moj/components/sortable-table/sortable-table.bundle.js.map +1 -1
  87. package/moj/components/sortable-table/sortable-table.bundle.mjs +390 -78
  88. package/moj/components/sortable-table/sortable-table.bundle.mjs.map +1 -1
  89. package/moj/components/sortable-table/sortable-table.mjs +149 -79
  90. package/moj/components/sortable-table/sortable-table.mjs.map +1 -1
  91. package/moj/core/_all.scss +3 -0
  92. package/moj/core/_all.scss.map +1 -0
  93. package/moj/core/_moj-frontend-properties.scss +7 -0
  94. package/moj/core/_moj-frontend-properties.scss.map +1 -0
  95. package/moj/filters/prototype-kit-13-filters.js +4 -3
  96. package/moj/helpers.bundle.js +22 -77
  97. package/moj/helpers.bundle.js.map +1 -1
  98. package/moj/helpers.bundle.mjs +23 -74
  99. package/moj/helpers.bundle.mjs.map +1 -1
  100. package/moj/helpers.mjs +23 -74
  101. package/moj/helpers.mjs.map +1 -1
  102. package/moj/moj-frontend.min.css +1 -1
  103. package/moj/moj-frontend.min.css.map +1 -1
  104. package/moj/moj-frontend.min.js +1 -1
  105. package/moj/moj-frontend.min.js.map +1 -1
  106. package/package.json +1 -1
  107. package/moj/version.bundle.js +0 -12
  108. package/moj/version.bundle.js.map +0 -1
  109. package/moj/version.bundle.mjs +0 -4
  110. package/moj/version.bundle.mjs.map +0 -1
  111. package/moj/version.mjs +0 -4
  112. package/moj/version.mjs.map +0 -1
@@ -1,66 +1,34 @@
1
- class DatePicker {
1
+ import { ConfigurableComponent } from 'govuk-frontend';
2
+
3
+ /**
4
+ * @augments {ConfigurableComponent<DatePickerConfig>}
5
+ */
6
+ class DatePicker extends ConfigurableComponent {
2
7
  /**
3
- * @param {Element | null} $module - HTML element to use for date picker
8
+ * @param {Element | null} $root - HTML element to use for date picker
4
9
  * @param {DatePickerConfig} [config] - Date picker config
5
10
  */
6
- constructor($module, config = {}) {
7
- if (!$module || !($module instanceof HTMLElement)) {
8
- return this;
9
- }
10
- const $input = $module.querySelector('.moj-js-datepicker-input');
11
-
12
- // Check that required elements are present
11
+ constructor($root, config = {}) {
12
+ var _this$config$input$el;
13
+ super($root, config);
14
+ const $input = (_this$config$input$el = this.config.input.element) != null ? _this$config$input$el : this.$root.querySelector(this.config.input.selector);
13
15
  if (!$input || !($input instanceof HTMLInputElement)) {
14
16
  return this;
15
17
  }
16
- this.$module = $module;
17
18
  this.$input = $input;
18
- const schema = Object.freeze({
19
- properties: {
20
- excludedDates: {
21
- type: 'string'
22
- },
23
- excludedDays: {
24
- type: 'string'
25
- },
26
- leadingZeros: {
27
- type: 'string'
28
- },
29
- maxDate: {
30
- type: 'string'
31
- },
32
- minDate: {
33
- type: 'string'
34
- },
35
- weekStartDay: {
36
- type: 'string'
37
- }
38
- }
39
- });
40
- const defaults = {
41
- leadingZeros: false,
42
- weekStartDay: 'monday'
43
- };
44
-
45
- // data attributes override JS config, which overrides defaults
46
- this.config = this.mergeConfigs(defaults, config, this.parseDataset(schema, $module.dataset));
47
19
  this.dayLabels = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
48
20
  this.monthLabels = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
49
21
  this.currentDate = new Date();
50
22
  this.currentDate.setHours(0, 0, 0, 0);
51
- this.calendarDays = [];
52
- this.excludedDates = [];
53
- this.excludedDays = [];
23
+ this.calendarDays = /** @type {DSCalendarDay[]} */[];
24
+ this.excludedDates = /** @type {Date[]} */[];
25
+ this.excludedDays = /** @type {number[]} */[];
54
26
  this.buttonClass = 'moj-datepicker__button';
55
27
  this.selectedDayButtonClass = 'moj-datepicker__button--selected';
56
28
  this.currentDayButtonClass = 'moj-datepicker__button--current';
57
29
  this.todayButtonClass = 'moj-datepicker__button--today';
58
- if (this.$module.dataset.initialized) {
59
- return this;
60
- }
61
30
  this.setOptions();
62
31
  this.initControls();
63
- this.$module.setAttribute('data-initialized', 'true');
64
32
  }
65
33
  initControls() {
66
34
  this.id = `datepicker-${this.$input.id}`;
@@ -75,15 +43,23 @@ class DatePicker {
75
43
  $inputWrapper.appendChild(this.$input);
76
44
  $inputWrapper.insertAdjacentHTML('beforeend', this.toggleTemplate());
77
45
  $componentWrapper.insertAdjacentElement('beforeend', this.$dialog);
78
- this.$calendarButton = this.$module.querySelector('.moj-js-datepicker-toggle');
79
- this.$dialogTitle = this.$dialog.querySelector('.moj-js-datepicker-month-year');
46
+ this.$calendarButton = /** @type {HTMLButtonElement} */
47
+ this.$root.querySelector('.moj-js-datepicker-toggle');
48
+ this.$dialogTitle = /** @type {HTMLHeadingElement} */
49
+ this.$dialog.querySelector('.moj-js-datepicker-month-year');
80
50
  this.createCalendar();
81
- this.$prevMonthButton = this.$dialog.querySelector('.moj-js-datepicker-prev-month');
82
- this.$prevYearButton = this.$dialog.querySelector('.moj-js-datepicker-prev-year');
83
- this.$nextMonthButton = this.$dialog.querySelector('.moj-js-datepicker-next-month');
84
- this.$nextYearButton = this.$dialog.querySelector('.moj-js-datepicker-next-year');
85
- this.$cancelButton = this.$dialog.querySelector('.moj-js-datepicker-cancel');
86
- this.$okButton = this.$dialog.querySelector('.moj-js-datepicker-ok');
51
+ this.$prevMonthButton = /** @type {HTMLButtonElement} */
52
+ this.$dialog.querySelector('.moj-js-datepicker-prev-month');
53
+ this.$prevYearButton = /** @type {HTMLButtonElement} */
54
+ this.$dialog.querySelector('.moj-js-datepicker-prev-year');
55
+ this.$nextMonthButton = /** @type {HTMLButtonElement} */
56
+ this.$dialog.querySelector('.moj-js-datepicker-next-month');
57
+ this.$nextYearButton = /** @type {HTMLButtonElement} */
58
+ this.$dialog.querySelector('.moj-js-datepicker-next-year');
59
+ this.$cancelButton = /** @type {HTMLButtonElement} */
60
+ this.$dialog.querySelector('.moj-js-datepicker-cancel');
61
+ this.$okButton = /** @type {HTMLButtonElement} */
62
+ this.$dialog.querySelector('.moj-js-datepicker-ok');
87
63
 
88
64
  // add event listeners
89
65
  this.$prevMonthButton.addEventListener('click', event => this.focusPreviousMonth(event, false));
@@ -97,9 +73,9 @@ class DatePicker {
97
73
  this.$okButton.addEventListener('click', () => {
98
74
  this.selectDate(this.currentDate);
99
75
  });
100
- const dialogButtons = this.$dialog.querySelectorAll('button:not([disabled="true"])');
101
- this.$firstButtonInDialog = dialogButtons[0];
102
- this.$lastButtonInDialog = dialogButtons[dialogButtons.length - 1];
76
+ const $dialogButtons = this.$dialog.querySelectorAll('button:not([disabled="true"])');
77
+ this.$firstButtonInDialog = $dialogButtons[0];
78
+ this.$lastButtonInDialog = $dialogButtons[$dialogButtons.length - 1];
103
79
  this.$firstButtonInDialog.addEventListener('keydown', event => this.firstButtonKeydown(event));
104
80
  this.$lastButtonInDialog.addEventListener('keydown', event => this.lastButtonKeydown(event));
105
81
  this.$calendarButton.addEventListener('click', event => this.toggleDialog(event));
@@ -245,7 +221,6 @@ class DatePicker {
245
221
  this.setMinAndMaxDatesOnCalendar();
246
222
  this.setExcludedDates();
247
223
  this.setExcludedDays();
248
- this.setLeadingZeros();
249
224
  this.setWeekStartDay();
250
225
  }
251
226
  setMinAndMaxDatesOnCalendar() {
@@ -270,10 +245,10 @@ class DatePicker {
270
245
  }
271
246
  }
272
247
 
273
- /*
248
+ /**
274
249
  * Parses a daterange string into an array of dates
275
- * @param {String} datestring - A daterange string in the format "dd/mm/yyyy-dd/mm/yyyy"
276
- * @returns {Date[]}
250
+ *
251
+ * @param {string} datestring - A daterange string in the format "dd/mm/yyyy-dd/mm/yyyy"
277
252
  */
278
253
  parseDateRangeString(datestring) {
279
254
  const dates = [];
@@ -300,17 +275,6 @@ class DatePicker {
300
275
  this.excludedDays = this.config.excludedDays.replace(/\s+/, ' ').toLowerCase().split(' ').map(item => weekDays.indexOf(item)).filter(item => item !== -1);
301
276
  }
302
277
  }
303
- setLeadingZeros() {
304
- if (typeof this.config.leadingZeros !== 'boolean') {
305
- if (this.config.leadingZeros.toLowerCase() === 'true') {
306
- this.config.leadingZeros = true;
307
- return;
308
- }
309
- if (this.config.leadingZeros.toLowerCase() === 'false') {
310
- this.config.leadingZeros = false;
311
- }
312
- }
313
- }
314
278
  setWeekStartDay() {
315
279
  const weekStartDayParam = this.config.weekStartDay;
316
280
  if (weekStartDayParam && weekStartDayParam.toLowerCase() === 'sunday') {
@@ -323,7 +287,7 @@ class DatePicker {
323
287
  }
324
288
 
325
289
  /**
326
- * Determine if a date is selecteable
290
+ * Determine if a date is selectable
327
291
  *
328
292
  * @param {Date} date - the date to check
329
293
  * @returns {boolean}
@@ -389,24 +353,36 @@ class DatePicker {
389
353
  /**
390
354
  * Get a human readable date in the format Monday 2 March 2024
391
355
  *
392
- * @param {Date} date - date to format
356
+ * @param {Date} date - Date to format
393
357
  * @returns {string}
394
358
  */
395
359
  formattedDateHuman(date) {
396
360
  return `${this.dayLabels[(date.getDay() + 6) % 7]} ${date.getDate()} ${this.monthLabels[date.getMonth()]} ${date.getFullYear()}`;
397
361
  }
362
+
363
+ /**
364
+ * @param {MouseEvent} event - Click event
365
+ */
398
366
  backgroundClick(event) {
399
367
  if (this.isOpen() && event.target instanceof Node && !this.$dialog.contains(event.target) && !this.$input.contains(event.target) && !this.$calendarButton.contains(event.target)) {
400
368
  event.preventDefault();
401
369
  this.closeDialog();
402
370
  }
403
371
  }
372
+
373
+ /**
374
+ * @param {KeyboardEvent} event - Keydown event
375
+ */
404
376
  firstButtonKeydown(event) {
405
377
  if (event.key === 'Tab' && event.shiftKey) {
406
378
  this.$lastButtonInDialog.focus();
407
379
  event.preventDefault();
408
380
  }
409
381
  }
382
+
383
+ /**
384
+ * @param {KeyboardEvent} event - Keydown event
385
+ */
410
386
  lastButtonKeydown(event) {
411
387
  if (event.key === 'Tab' && !event.shiftKey) {
412
388
  this.$firstButtonInDialog.focus();
@@ -436,49 +412,57 @@ class DatePicker {
436
412
  thisDay.setDate(thisDay.getDate() + 1);
437
413
  }
438
414
  }
415
+
416
+ /**
417
+ * @param {boolean} [focus] - Focus the day button
418
+ */
439
419
  setCurrentDate(focus = true) {
440
420
  const {
441
421
  currentDate
442
422
  } = this;
443
423
  this.calendarDays.forEach(calendarDay => {
444
- calendarDay.button.classList.add('moj-datepicker__button');
445
- calendarDay.button.classList.add('moj-datepicker__calendar-day');
446
- calendarDay.button.setAttribute('tabindex', '-1');
447
- calendarDay.button.classList.remove(this.selectedDayButtonClass);
424
+ calendarDay.$button.classList.add('moj-datepicker__button');
425
+ calendarDay.$button.classList.add('moj-datepicker__calendar-day');
426
+ calendarDay.$button.setAttribute('tabindex', '-1');
427
+ calendarDay.$button.classList.remove(this.selectedDayButtonClass);
448
428
  const calendarDayDate = calendarDay.date;
449
429
  calendarDayDate.setHours(0, 0, 0, 0);
450
430
  const today = new Date();
451
431
  today.setHours(0, 0, 0, 0);
452
432
  if (calendarDayDate.getTime() === currentDate.getTime() /* && !calendarDay.button.disabled */) {
453
433
  if (focus) {
454
- calendarDay.button.setAttribute('tabindex', '0');
455
- calendarDay.button.focus();
456
- calendarDay.button.classList.add(this.selectedDayButtonClass);
434
+ calendarDay.$button.setAttribute('tabindex', '0');
435
+ calendarDay.$button.focus();
436
+ calendarDay.$button.classList.add(this.selectedDayButtonClass);
457
437
  }
458
438
  }
459
439
  if (this.inputDate && calendarDayDate.getTime() === this.inputDate.getTime()) {
460
- calendarDay.button.classList.add(this.currentDayButtonClass);
461
- calendarDay.button.setAttribute('aria-current', 'date');
440
+ calendarDay.$button.classList.add(this.currentDayButtonClass);
441
+ calendarDay.$button.setAttribute('aria-current', 'date');
462
442
  } else {
463
- calendarDay.button.classList.remove(this.currentDayButtonClass);
464
- calendarDay.button.removeAttribute('aria-current');
443
+ calendarDay.$button.classList.remove(this.currentDayButtonClass);
444
+ calendarDay.$button.removeAttribute('aria-current');
465
445
  }
466
446
  if (calendarDayDate.getTime() === today.getTime()) {
467
- calendarDay.button.classList.add(this.todayButtonClass);
447
+ calendarDay.$button.classList.add(this.todayButtonClass);
468
448
  } else {
469
- calendarDay.button.classList.remove(this.todayButtonClass);
449
+ calendarDay.$button.classList.remove(this.todayButtonClass);
470
450
  }
471
451
  });
472
452
 
473
453
  // if no date is tab-able, make the first non-disabled date tab-able
474
454
  if (!focus) {
475
455
  const enabledDays = this.calendarDays.filter(calendarDay => {
476
- return window.getComputedStyle(calendarDay.button).display === 'block' && !calendarDay.button.disabled;
456
+ return window.getComputedStyle(calendarDay.$button).display === 'block' && !calendarDay.$button.disabled;
477
457
  });
478
- enabledDays[0].button.setAttribute('tabindex', '0');
458
+ enabledDays[0].$button.setAttribute('tabindex', '0');
479
459
  this.currentDate = enabledDays[0].date;
480
460
  }
481
461
  }
462
+
463
+ /**
464
+ * @param {Date} date - Date to select
465
+ */
482
466
  selectDate(date) {
483
467
  if (this.isExcludedDate(date)) {
484
468
  return;
@@ -495,6 +479,10 @@ class DatePicker {
495
479
  isOpen() {
496
480
  return this.$dialog.classList.contains('moj-datepicker__dialog--open');
497
481
  }
482
+
483
+ /**
484
+ * @param {MouseEvent} event - Click event
485
+ */
498
486
  toggleDialog(event) {
499
487
  event.preventDefault();
500
488
  if (this.isOpen()) {
@@ -529,6 +517,11 @@ class DatePicker {
529
517
  this.$calendarButton.setAttribute('aria-expanded', 'false');
530
518
  this.$calendarButton.focus();
531
519
  }
520
+
521
+ /**
522
+ * @param {Date} date - Date to go to
523
+ * @param {boolean} [focus] - Focus the day button
524
+ */
532
525
  goToDate(date, focus) {
533
526
  const current = this.currentDate;
534
527
  this.currentDate = date;
@@ -580,13 +573,23 @@ class DatePicker {
580
573
  this.goToDate(date);
581
574
  }
582
575
 
583
- // month navigation
576
+ /**
577
+ * Month navigation
578
+ *
579
+ * @param {KeyboardEvent | MouseEvent} event - Key press or click event
580
+ * @param {boolean} [focus] - Focus the day button
581
+ */
584
582
  focusNextMonth(event, focus = true) {
585
583
  event.preventDefault();
586
584
  const date = new Date(this.currentDate);
587
585
  date.setMonth(date.getMonth() + 1, 1);
588
586
  this.goToDate(date, focus);
589
587
  }
588
+
589
+ /**
590
+ * @param {KeyboardEvent | MouseEvent} event - Key press or click event
591
+ * @param {boolean} [focus] - Focus the day button
592
+ */
590
593
  focusPreviousMonth(event, focus = true) {
591
594
  event.preventDefault();
592
595
  const date = new Date(this.currentDate);
@@ -594,13 +597,23 @@ class DatePicker {
594
597
  this.goToDate(date, focus);
595
598
  }
596
599
 
597
- // year navigation
600
+ /**
601
+ * Year navigation
602
+ *
603
+ * @param {KeyboardEvent | MouseEvent} event - Key press or click event
604
+ * @param {boolean} [focus] - Focus the day button
605
+ */
598
606
  focusNextYear(event, focus = true) {
599
607
  event.preventDefault();
600
608
  const date = new Date(this.currentDate);
601
609
  date.setFullYear(date.getFullYear() + 1, date.getMonth(), 1);
602
610
  this.goToDate(date, focus);
603
611
  }
612
+
613
+ /**
614
+ * @param {KeyboardEvent | MouseEvent} event - Key press or click event
615
+ * @param {boolean} [focus] - Focus the day button
616
+ */
604
617
  focusPreviousYear(event, focus = true) {
605
618
  event.preventDefault();
606
619
  const date = new Date(this.currentDate);
@@ -609,72 +622,70 @@ class DatePicker {
609
622
  }
610
623
 
611
624
  /**
612
- * Parse dataset
613
- *
614
- * @param {Schema} schema - Component class
615
- * @param {DOMStringMap} dataset - HTML element dataset
616
- * @returns {object} Normalised dataset
617
- */
618
- parseDataset(schema, dataset) {
619
- const parsed = {};
620
- for (const [field,,] of Object.entries(schema.properties)) {
621
- if (field in dataset) {
622
- parsed[field] = dataset[field];
623
- }
624
- }
625
- return parsed;
626
- }
627
-
628
- /**
629
- * Config merging function
630
- *
631
- * Takes any number of objects and combines them together, with
632
- * greatest priority on the LAST item passed in.
633
- *
634
- * @param {...{ [key: string]: unknown }} configObjects - Config objects to merge
635
- * @returns {{ [key: string]: unknown }} A merged config object
625
+ * Name for the component used when initialising using data-module attributes.
636
626
  */
637
- mergeConfigs(...configObjects) {
638
- const formattedConfigObject = {};
639
-
640
- // Loop through each of the passed objects
641
- for (const configObject of configObjects) {
642
- for (const key of Object.keys(configObject)) {
643
- const option = formattedConfigObject[key];
644
- const override = configObject[key];
645
-
646
- // Push their keys one-by-one into formattedConfigObject. Any duplicate
647
- // keys with object values will be merged, otherwise the new value will
648
- // override the existing value.
649
- if (typeof option === 'object' && typeof override === 'object') {
650
- // @ts-expect-error Index signature for type 'string' is missing
651
- formattedConfigObject[key] = this.mergeConfigs(option, override);
652
- } else {
653
- formattedConfigObject[key] = override;
654
- }
655
- }
656
- }
657
- return formattedConfigObject;
658
- }
659
627
  }
628
+ DatePicker.moduleName = 'moj-date-picker';
629
+ /**
630
+ * Date picker default config
631
+ *
632
+ * @type {DatePickerConfig}
633
+ */
634
+ DatePicker.defaults = Object.freeze({
635
+ leadingZeros: false,
636
+ weekStartDay: 'monday',
637
+ input: {
638
+ selector: '.moj-js-datepicker-input'
639
+ }
640
+ });
641
+ /**
642
+ * Date picker config schema
643
+ *
644
+ * @satisfies {Schema<DatePickerConfig>}
645
+ */
646
+ DatePicker.schema = Object.freeze(/** @type {const} */{
647
+ properties: {
648
+ excludedDates: {
649
+ type: 'string'
650
+ },
651
+ excludedDays: {
652
+ type: 'string'
653
+ },
654
+ leadingZeros: {
655
+ type: 'boolean'
656
+ },
657
+ maxDate: {
658
+ type: 'string'
659
+ },
660
+ minDate: {
661
+ type: 'string'
662
+ },
663
+ weekStartDay: {
664
+ type: 'string'
665
+ },
666
+ input: {
667
+ type: 'object'
668
+ }
669
+ }
670
+ });
660
671
  class DSCalendarDay {
661
672
  /**
662
673
  *
663
- * @param {HTMLElement} button
674
+ * @param {HTMLButtonElement} $button
664
675
  * @param {number} index
665
676
  * @param {number} row
666
677
  * @param {number} column
667
678
  * @param {DatePicker} picker
668
679
  */
669
- constructor(button, index, row, column, picker) {
680
+ constructor($button, index, row, column, picker) {
670
681
  this.index = index;
671
682
  this.row = row;
672
683
  this.column = column;
673
- this.button = button;
684
+ this.$button = $button;
674
685
  this.picker = picker;
675
686
  this.date = new Date();
676
- this.button.addEventListener('keydown', this.keyPress.bind(this));
677
- this.button.addEventListener('click', this.click.bind(this));
687
+ this.$button.addEventListener('keydown', this.keyPress.bind(this));
688
+ this.$button.addEventListener('click', this.click.bind(this));
678
689
  }
679
690
 
680
691
  /**
@@ -686,26 +697,34 @@ class DSCalendarDay {
686
697
  const label = day.getDate();
687
698
  let accessibleLabel = this.picker.formattedDateHuman(day);
688
699
  if (disabled) {
689
- this.button.setAttribute('aria-disabled', 'true');
700
+ this.$button.setAttribute('aria-disabled', 'true');
690
701
  accessibleLabel = `Excluded date, ${accessibleLabel}`;
691
702
  } else {
692
- this.button.removeAttribute('aria-disabled');
703
+ this.$button.removeAttribute('aria-disabled');
693
704
  }
694
705
  if (hidden) {
695
- this.button.style.display = 'none';
706
+ this.$button.style.display = 'none';
696
707
  } else {
697
- this.button.style.display = 'block';
708
+ this.$button.style.display = 'block';
698
709
  }
699
- this.button.setAttribute('data-testid', this.picker.formattedDateFromDate(day));
700
- this.button.innerHTML = `<span class="govuk-visually-hidden">${accessibleLabel}</span><span aria-hidden="true">${label}</span>`;
710
+ this.$button.setAttribute('data-testid', this.picker.formattedDateFromDate(day));
711
+ this.$button.innerHTML = `<span class="govuk-visually-hidden">${accessibleLabel}</span><span aria-hidden="true">${label}</span>`;
701
712
  this.date = new Date(day);
702
713
  }
714
+
715
+ /**
716
+ * @param {MouseEvent} event - Click event
717
+ */
703
718
  click(event) {
704
719
  this.picker.goToDate(this.date);
705
720
  this.picker.selectDate(this.date);
706
721
  event.stopPropagation();
707
722
  event.preventDefault();
708
723
  }
724
+
725
+ /**
726
+ * @param {KeyboardEvent} event - Keydown event
727
+ */
709
728
  keyPress(event) {
710
729
  let calendarNavKey = true;
711
730
  switch (event.key) {
@@ -762,14 +781,17 @@ class DSCalendarDay {
762
781
  * @typedef {object} DatePickerConfig
763
782
  * @property {string} [excludedDates] - Dates that cannot be selected
764
783
  * @property {string} [excludedDays] - Days that cannot be selected
765
- * @property {boolean} [leadingZeroes] - Whether to add leading zeroes when populating the field
784
+ * @property {boolean} [leadingZeros] - Whether to add leading zeroes when populating the field
766
785
  * @property {string} [minDate] - The earliest available date
767
786
  * @property {string} [maxDate] - The latest available date
768
787
  * @property {string} [weekStartDay] - First day of the week in calendar view
788
+ * @property {object} [input] - Input config
789
+ * @property {string} [input.selector] - Selector for the input element
790
+ * @property {Element | null} [input.element] - HTML element for the input
769
791
  */
770
792
 
771
793
  /**
772
- * @import { Schema } from '../../all.mjs'
794
+ * @import { Schema } from 'govuk-frontend/dist/govuk/common/configuration.mjs'
773
795
  */
774
796
 
775
797
  export { DatePicker };