@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 +1 @@
1
- {"version":3,"file":"add-another.mjs","sources":["../../../../src/moj/components/add-another/add-another.mjs"],"sourcesContent":["export class AddAnother {\n constructor(container) {\n this.container = container\n\n if (this.container.hasAttribute('data-moj-add-another-init')) {\n return this\n }\n\n this.container.setAttribute('data-moj-add-another-init', '')\n\n this.container.addEventListener(\n 'click',\n this.onRemoveButtonClick.bind(this)\n )\n this.container.addEventListener('click', this.onAddButtonClick.bind(this))\n\n const buttons = this.container.querySelectorAll(\n '.moj-add-another__add-button, moj-add-another__remove-button'\n )\n\n buttons.forEach((button) => {\n if (!(button instanceof HTMLButtonElement)) {\n return\n }\n\n button.type = 'button'\n })\n }\n\n onAddButtonClick(event) {\n const button = event.target\n\n if (\n !button ||\n !(button instanceof HTMLButtonElement) ||\n !button.classList.contains('moj-add-another__add-button')\n ) {\n return\n }\n\n const items = this.getItems()\n const item = this.getNewItem()\n\n if (!item || !(item instanceof HTMLElement)) {\n return\n }\n\n this.updateAttributes(item, items.length)\n this.resetItem(item)\n\n const firstItem = items[0]\n if (!this.hasRemoveButton(firstItem)) {\n this.createRemoveButton(firstItem)\n }\n\n items[items.length - 1].after(item)\n\n const input = item.querySelector('input, textarea, select')\n if (input && input instanceof HTMLInputElement) {\n input.focus()\n }\n }\n\n hasRemoveButton(item) {\n return item.querySelectorAll('.moj-add-another__remove-button').length\n }\n\n getItems() {\n if (!this.container) {\n return []\n }\n\n const items = Array.from(\n this.container.querySelectorAll('.moj-add-another__item')\n )\n\n return items.filter((item) => item instanceof HTMLElement)\n }\n\n getNewItem() {\n const items = this.getItems()\n const item = items[0].cloneNode(true)\n\n if (!item || !(item instanceof HTMLElement)) {\n return\n }\n\n if (!this.hasRemoveButton(item)) {\n this.createRemoveButton(item)\n }\n\n return item\n }\n\n updateAttributes(item, index) {\n item.querySelectorAll('[data-name]').forEach((el) => {\n if (!(el instanceof HTMLInputElement)) {\n return\n }\n\n const name = el.getAttribute('data-name') || ''\n const id = el.getAttribute('data-id') || ''\n const originalId = el.id\n\n el.name = name.replace(/%index%/, `${index}`)\n el.id = id.replace(/%index%/, `${index}`)\n\n const label =\n el.parentElement.querySelector('label') ||\n el.closest('label') ||\n item.querySelector(`[for=\"${originalId}\"]`)\n\n if (label && label instanceof HTMLLabelElement) {\n label.htmlFor = el.id\n }\n })\n }\n\n createRemoveButton(item) {\n const button = document.createElement('button')\n button.type = 'button'\n\n button.classList.add(\n 'govuk-button',\n 'govuk-button--secondary',\n 'moj-add-another__remove-button'\n )\n\n button.textContent = 'Remove'\n\n item.append(button)\n }\n\n resetItem(item) {\n item.querySelectorAll('[data-name], [data-id]').forEach((el) => {\n if (!(el instanceof HTMLInputElement)) {\n return\n }\n\n if (el.type === 'checkbox' || el.type === 'radio') {\n el.checked = false\n } else {\n el.value = ''\n }\n })\n }\n\n onRemoveButtonClick(event) {\n const button = event.target\n\n if (\n !button ||\n !(button instanceof HTMLButtonElement) ||\n !button.classList.contains('moj-add-another__remove-button')\n ) {\n return\n }\n\n button.closest('.moj-add-another__item').remove()\n\n const items = this.getItems()\n\n if (items.length === 1) {\n items[0].querySelector('.moj-add-another__remove-button').remove()\n }\n\n items.forEach((el, index) => {\n this.updateAttributes(el, index)\n })\n\n this.focusHeading()\n }\n\n focusHeading() {\n const heading = this.container.querySelector('.moj-add-another__heading')\n\n if (heading && heading instanceof HTMLElement) {\n heading.focus()\n }\n }\n}\n"],"names":["AddAnother","constructor","container","hasAttribute","setAttribute","addEventListener","onRemoveButtonClick","bind","onAddButtonClick","buttons","querySelectorAll","forEach","button","HTMLButtonElement","type","event","target","classList","contains","items","getItems","item","getNewItem","HTMLElement","updateAttributes","length","resetItem","firstItem","hasRemoveButton","createRemoveButton","after","input","querySelector","HTMLInputElement","focus","Array","from","filter","cloneNode","index","el","name","getAttribute","id","originalId","replace","label","parentElement","closest","HTMLLabelElement","htmlFor","document","createElement","add","textContent","append","checked","value","remove","focusHeading","heading"],"mappings":"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"}
1
+ {"version":3,"file":"add-another.mjs","sources":["../../../../src/moj/components/add-another/add-another.mjs"],"sourcesContent":["import { Component } from 'govuk-frontend'\n\nexport class AddAnother extends Component {\n /**\n * @param {Element | null} $root - HTML element to use for add another\n */\n constructor($root) {\n super($root)\n\n this.$root.addEventListener('click', this.onRemoveButtonClick.bind(this))\n this.$root.addEventListener('click', this.onAddButtonClick.bind(this))\n\n const $buttons = this.$root.querySelectorAll(\n '.moj-add-another__add-button, moj-add-another__remove-button'\n )\n\n $buttons.forEach(($button) => {\n if (!($button instanceof HTMLButtonElement)) {\n return\n }\n\n $button.type = 'button'\n })\n }\n\n /**\n * @param {MouseEvent} event - Click event\n */\n onAddButtonClick(event) {\n const $button = event.target\n\n if (\n !$button ||\n !($button instanceof HTMLButtonElement) ||\n !$button.classList.contains('moj-add-another__add-button')\n ) {\n return\n }\n\n const $items = this.getItems()\n const $item = this.getNewItem()\n\n if (!$item || !($item instanceof HTMLElement)) {\n return\n }\n\n this.updateAttributes($item, $items.length)\n this.resetItem($item)\n\n const $firstItem = $items[0]\n if (!this.hasRemoveButton($firstItem)) {\n this.createRemoveButton($firstItem)\n }\n\n $items[$items.length - 1].after($item)\n\n const $input = $item.querySelector('input, textarea, select')\n if ($input && $input instanceof HTMLInputElement) {\n $input.focus()\n }\n }\n\n /**\n * @param {HTMLElement} $item - Add another item\n */\n hasRemoveButton($item) {\n return $item.querySelectorAll('.moj-add-another__remove-button').length\n }\n\n getItems() {\n if (!this.$root) {\n return []\n }\n\n const $items = Array.from(\n this.$root.querySelectorAll('.moj-add-another__item')\n )\n\n return $items.filter((item) => item instanceof HTMLElement)\n }\n\n getNewItem() {\n const $items = this.getItems()\n const $item = $items[0].cloneNode(true)\n\n if (!$item || !($item instanceof HTMLElement)) {\n return\n }\n\n if (!this.hasRemoveButton($item)) {\n this.createRemoveButton($item)\n }\n\n return $item\n }\n\n /**\n * @param {HTMLElement} $item - Add another item\n * @param {number} index - Add another item index\n */\n updateAttributes($item, index) {\n $item.querySelectorAll('[data-name]').forEach(($input) => {\n if (!($input instanceof HTMLInputElement)) {\n return\n }\n\n const name = $input.getAttribute('data-name') || ''\n const id = $input.getAttribute('data-id') || ''\n const originalId = $input.id\n\n $input.name = name.replace(/%index%/, `${index}`)\n $input.id = id.replace(/%index%/, `${index}`)\n\n const $label =\n $input.parentElement.querySelector('label') ||\n $input.closest('label') ||\n $item.querySelector(`[for=\"${originalId}\"]`)\n\n if ($label && $label instanceof HTMLLabelElement) {\n $label.htmlFor = $input.id\n }\n })\n }\n\n /**\n * @param {HTMLElement} $item - Add another item\n */\n createRemoveButton($item) {\n const $button = document.createElement('button')\n $button.type = 'button'\n\n $button.classList.add(\n 'govuk-button',\n 'govuk-button--secondary',\n 'moj-add-another__remove-button'\n )\n\n $button.textContent = 'Remove'\n\n $item.append($button)\n }\n\n /**\n * @param {HTMLElement} $item - Add another item\n */\n resetItem($item) {\n $item.querySelectorAll('[data-name], [data-id]').forEach(($input) => {\n if (!($input instanceof HTMLInputElement)) {\n return\n }\n\n if ($input.type === 'checkbox' || $input.type === 'radio') {\n $input.checked = false\n } else {\n $input.value = ''\n }\n })\n }\n\n /**\n * @param {MouseEvent} event - Click event\n */\n onRemoveButtonClick(event) {\n const $button = event.target\n\n if (\n !$button ||\n !($button instanceof HTMLButtonElement) ||\n !$button.classList.contains('moj-add-another__remove-button')\n ) {\n return\n }\n\n $button.closest('.moj-add-another__item').remove()\n\n const $items = this.getItems()\n\n if ($items.length === 1) {\n $items[0].querySelector('.moj-add-another__remove-button').remove()\n }\n\n $items.forEach(($item, index) => {\n this.updateAttributes($item, index)\n })\n\n this.focusHeading()\n }\n\n focusHeading() {\n const $heading = this.$root.querySelector('.moj-add-another__heading')\n\n if ($heading && $heading instanceof HTMLElement) {\n $heading.focus()\n }\n }\n\n /**\n * Name for the component used when initialising using data-module attributes.\n */\n static moduleName = 'moj-add-another'\n}\n"],"names":["AddAnother","Component","constructor","$root","addEventListener","onRemoveButtonClick","bind","onAddButtonClick","$buttons","querySelectorAll","forEach","$button","HTMLButtonElement","type","event","target","classList","contains","$items","getItems","$item","getNewItem","HTMLElement","updateAttributes","length","resetItem","$firstItem","hasRemoveButton","createRemoveButton","after","$input","querySelector","HTMLInputElement","focus","Array","from","filter","item","cloneNode","index","name","getAttribute","id","originalId","replace","$label","parentElement","closest","HTMLLabelElement","htmlFor","document","createElement","add","textContent","append","checked","value","remove","focusHeading","$heading","moduleName"],"mappings":"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"}
@@ -1,8 +1,69 @@
1
1
  (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
3
- typeof define === 'function' && define.amd ? define(['exports'], factory) :
4
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.MOJFrontend = global.MOJFrontend || {}));
5
- })(this, (function (exports) { 'use strict';
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('govuk-frontend')) :
3
+ typeof define === 'function' && define.amd ? define(['exports', 'govuk-frontend'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.MOJFrontend = global.MOJFrontend || {}, global.GOVUKFrontend));
5
+ })(this, (function (exports, govukFrontend) { 'use strict';
6
+
7
+ /**
8
+ * GOV.UK Frontend helpers
9
+ *
10
+ * @todo Import from GOV.UK Frontend
11
+ */
12
+
13
+ /**
14
+ * Move focus to element
15
+ *
16
+ * Sets tabindex to -1 to make the element programmatically focusable,
17
+ * but removes it on blur as the element doesn't need to be focused again.
18
+ *
19
+ * @template {HTMLElement} FocusElement
20
+ * @param {FocusElement} $element - HTML element
21
+ * @param {object} [options] - Handler options
22
+ * @param {function(this: FocusElement): void} [options.onBeforeFocus] - Callback before focus
23
+ * @param {function(this: FocusElement): void} [options.onBlur] - Callback on blur
24
+ */
25
+ function setFocus($element, options = {}) {
26
+ var _options$onBeforeFocu;
27
+ const isFocusable = $element.getAttribute('tabindex');
28
+ if (!isFocusable) {
29
+ $element.setAttribute('tabindex', '-1');
30
+ }
31
+
32
+ /**
33
+ * Handle element focus
34
+ */
35
+ function onFocus() {
36
+ $element.addEventListener('blur', onBlur, {
37
+ once: true
38
+ });
39
+ }
40
+
41
+ /**
42
+ * Handle element blur
43
+ */
44
+ function onBlur() {
45
+ var _options$onBlur;
46
+ (_options$onBlur = options.onBlur) == null || _options$onBlur.call($element);
47
+ if (!isFocusable) {
48
+ $element.removeAttribute('tabindex');
49
+ }
50
+ }
51
+
52
+ // Add listener to reset element on blur, after focus
53
+ $element.addEventListener('focus', onFocus, {
54
+ once: true
55
+ });
56
+
57
+ // Focus element
58
+ (_options$onBeforeFocu = options.onBeforeFocus) == null || _options$onBeforeFocu.call($element);
59
+ $element.focus();
60
+ }
61
+
62
+ /**
63
+ * @param {Element} $element - Element to remove attribute value from
64
+ * @param {string} attr - Attribute name
65
+ * @param {string} value - Attribute value
66
+ */
6
67
 
7
68
  /**
8
69
  * Find an elements preceding sibling
@@ -63,89 +124,15 @@
63
124
  }
64
125
 
65
126
  /**
66
- * Move focus to element
67
- *
68
- * Sets tabindex to -1 to make the element programmatically focusable,
69
- * but removes it on blur as the element doesn't need to be focused again.
70
- *
71
- * @param {HTMLElement} $element - HTML element
72
- * @param {object} [options] - Handler options
73
- * @param {function(this: HTMLElement): void} [options.onBeforeFocus] - Callback before focus
74
- * @param {function(this: HTMLElement): void} [options.onBlur] - Callback on blur
127
+ * @augments {ConfigurableComponent<AlertConfig>}
75
128
  */
76
- function setFocus($element, options = {}) {
77
- const isFocusable = $element.getAttribute('tabindex');
78
- if (!isFocusable) {
79
- $element.setAttribute('tabindex', '-1');
80
- }
81
-
129
+ class Alert extends govukFrontend.ConfigurableComponent {
82
130
  /**
83
- * Handle element focus
84
- */
85
- function onFocus() {
86
- $element.addEventListener('blur', onBlur, {
87
- once: true
88
- });
89
- }
90
-
91
- /**
92
- * Handle element blur
93
- */
94
- function onBlur() {
95
- if (options.onBlur) {
96
- options.onBlur.call($element);
97
- }
98
- if (!isFocusable) {
99
- $element.removeAttribute('tabindex');
100
- }
101
- }
102
-
103
- // Add listener to reset element on blur, after focus
104
- $element.addEventListener('focus', onFocus, {
105
- once: true
106
- });
107
-
108
- // Focus element
109
- if (options.onBeforeFocus) {
110
- options.onBeforeFocus.call($element);
111
- }
112
- $element.focus();
113
- }
114
-
115
- class Alert {
116
- /**
117
- * @param {Element | null} $module - HTML element to use for alert
131
+ * @param {Element | null} $root - HTML element to use for alert
118
132
  * @param {AlertConfig} [config] - Alert config
119
133
  */
120
- constructor($module, config = {}) {
121
- if (!$module || !($module instanceof HTMLElement)) {
122
- return this;
123
- }
124
- const schema = Object.freeze({
125
- properties: {
126
- dismissible: {
127
- type: 'boolean'
128
- },
129
- dismissText: {
130
- type: 'string'
131
- },
132
- disableAutoFocus: {
133
- type: 'boolean'
134
- },
135
- focusOnDismissSelector: {
136
- type: 'string'
137
- }
138
- }
139
- });
140
- const defaults = {
141
- dismissible: false,
142
- dismissText: 'Dismiss',
143
- disableAutoFocus: false
144
- };
145
-
146
- // data attributes override JS config, which overrides defaults
147
- this.config = this.mergeConfigs(defaults, config, this.parseDataset(schema, $module.dataset));
148
- this.$module = $module;
134
+ constructor($root, config = {}) {
135
+ super($root, config);
149
136
 
150
137
  /**
151
138
  * Focus the alert
@@ -158,14 +145,14 @@
158
145
  * do this based on user research findings, or to avoid a clash with another
159
146
  * element which should be focused when the page loads.
160
147
  */
161
- if (this.$module.getAttribute('role') === 'alert' && !this.config.disableAutoFocus) {
162
- setFocus(this.$module);
148
+ if (this.$root.getAttribute('role') === 'alert' && !this.config.disableAutoFocus) {
149
+ setFocus(this.$root);
163
150
  }
164
- this.$dismissButton = this.$module.querySelector('.moj-alert__dismiss');
151
+ this.$dismissButton = this.$root.querySelector('.moj-alert__dismiss');
165
152
  if (this.config.dismissible && this.$dismissButton) {
166
153
  this.$dismissButton.innerHTML = this.config.dismissText;
167
154
  this.$dismissButton.removeAttribute('hidden');
168
- this.$module.addEventListener('click', event => {
155
+ this.$root.addEventListener('click', event => {
169
156
  if (event.target instanceof Node && this.$dismissButton.contains(event.target)) {
170
157
  this.dimiss();
171
158
  }
@@ -186,7 +173,7 @@
186
173
 
187
174
  // Is the next sibling another alert
188
175
  if (!$elementToRecieveFocus) {
189
- const $nextSibling = this.$module.nextElementSibling;
176
+ const $nextSibling = this.$root.nextElementSibling;
190
177
  if ($nextSibling && $nextSibling.matches('.moj-alert')) {
191
178
  $elementToRecieveFocus = $nextSibling;
192
179
  }
@@ -194,13 +181,13 @@
194
181
 
195
182
  // Else try to find any preceding sibling alert or heading
196
183
  if (!$elementToRecieveFocus) {
197
- $elementToRecieveFocus = getPreviousSibling(this.$module, '.moj-alert, h1, h2, h3, h4, h5, h6');
184
+ $elementToRecieveFocus = getPreviousSibling(this.$root, '.moj-alert, h1, h2, h3, h4, h5, h6');
198
185
  }
199
186
 
200
187
  // Else find the closest ancestor heading, or fallback to main, or last resort
201
188
  // use the body element
202
189
  if (!$elementToRecieveFocus) {
203
- $elementToRecieveFocus = findNearestMatchingElement(this.$module, 'h1, h2, h3, h4, h5, h6, main, body');
190
+ $elementToRecieveFocus = findNearestMatchingElement(this.$root, 'h1, h2, h3, h4, h5, h6, main, body');
204
191
  }
205
192
 
206
193
  // If we have an element, place focus on it
@@ -209,111 +196,12 @@
209
196
  }
210
197
 
211
198
  // Remove the alert
212
- this.$module.remove();
213
- }
214
-
215
- /**
216
- * Normalise string
217
- *
218
- * 'If it looks like a duck, and it quacks like a duck…' 🦆
219
- *
220
- * If the passed value looks like a boolean or a number, convert it to a boolean
221
- * or number.
222
- *
223
- * Designed to be used to convert config passed via data attributes (which are
224
- * always strings) into something sensible.
225
- *
226
- * @internal
227
- * @param {DOMStringMap[string]} value - The value to normalise
228
- * @param {SchemaProperty} [property] - Component schema property
229
- * @returns {string | boolean | number | undefined} Normalised data
230
- */
231
- normaliseString(value, property) {
232
- const trimmedValue = value ? value.trim() : '';
233
- let output;
234
- let outputType;
235
- if (property && property.type) {
236
- outputType = property.type;
237
- }
238
-
239
- // No schema type set? Determine automatically
240
- if (!outputType) {
241
- if (['true', 'false'].includes(trimmedValue)) {
242
- outputType = 'boolean';
243
- }
244
-
245
- // Empty / whitespace-only strings are considered finite so we need to check
246
- // the length of the trimmed string as well
247
- if (trimmedValue.length > 0 && Number.isFinite(Number(trimmedValue))) {
248
- outputType = 'number';
249
- }
250
- }
251
- switch (outputType) {
252
- case 'boolean':
253
- output = trimmedValue === 'true';
254
- break;
255
- case 'number':
256
- output = Number(trimmedValue);
257
- break;
258
- default:
259
- output = value;
260
- }
261
- return output;
199
+ this.$root.remove();
262
200
  }
263
201
 
264
202
  /**
265
- * Parse dataset
266
- *
267
- * Loop over an object and normalise each value using {@link normaliseString},
268
- * optionally expanding nested `i18n.field`
269
- *
270
- * @param {Schema} schema - component schema
271
- * @param {DOMStringMap} dataset - HTML element dataset
272
- * @returns {object} Normalised dataset
203
+ * Name for the component used when initialising using data-module attributes.
273
204
  */
274
- parseDataset(schema, dataset) {
275
- const parsed = {};
276
- for (const [field, property] of Object.entries(schema.properties)) {
277
- if (field in dataset) {
278
- if (dataset[field]) {
279
- parsed[field] = this.normaliseString(dataset[field], property);
280
- }
281
- }
282
- }
283
- return parsed;
284
- }
285
-
286
- /**
287
- * Config merging function
288
- *
289
- * Takes any number of objects and combines them together, with
290
- * greatest priority on the LAST item passed in.
291
- *
292
- * @param {...{ [key: string]: unknown }} configObjects - Config objects to merge
293
- * @returns {{ [key: string]: unknown }} A merged config object
294
- */
295
- mergeConfigs(...configObjects) {
296
- const formattedConfigObject = {};
297
-
298
- // Loop through each of the passed objects
299
- for (const configObject of configObjects) {
300
- for (const key of Object.keys(configObject)) {
301
- const option = formattedConfigObject[key];
302
- const override = configObject[key];
303
-
304
- // Push their keys one-by-one into formattedConfigObject. Any duplicate
305
- // keys with object values will be merged, otherwise the new value will
306
- // override the existing value.
307
- if (typeof option === 'object' && typeof override === 'object') {
308
- // @ts-expect-error Index signature for type 'string' is missing
309
- formattedConfigObject[key] = this.mergeConfigs(option, override);
310
- } else {
311
- formattedConfigObject[key] = override;
312
- }
313
- }
314
- }
315
- return formattedConfigObject;
316
- }
317
205
  }
318
206
 
319
207
  /**
@@ -324,6 +212,42 @@
324
212
  * @property {string} [focusOnDismissSelector] - CSS Selector for element to be focused on dismiss
325
213
  */
326
214
 
215
+ /**
216
+ * @import { Schema } from 'govuk-frontend/dist/govuk/common/configuration.mjs'
217
+ */
218
+ Alert.moduleName = 'moj-alert';
219
+ /**
220
+ * Alert default config
221
+ *
222
+ * @type {AlertConfig}
223
+ */
224
+ Alert.defaults = Object.freeze({
225
+ dismissible: false,
226
+ dismissText: 'Dismiss',
227
+ disableAutoFocus: false
228
+ });
229
+ /**
230
+ * Alert config schema
231
+ *
232
+ * @satisfies {Schema<AlertConfig>}
233
+ */
234
+ Alert.schema = Object.freeze(/** @type {const} */{
235
+ properties: {
236
+ dismissible: {
237
+ type: 'boolean'
238
+ },
239
+ dismissText: {
240
+ type: 'string'
241
+ },
242
+ disableAutoFocus: {
243
+ type: 'boolean'
244
+ },
245
+ focusOnDismissSelector: {
246
+ type: 'string'
247
+ }
248
+ }
249
+ });
250
+
327
251
  exports.Alert = Alert;
328
252
 
329
253
  }));
@@ -1 +1 @@
1
- {"version":3,"file":"alert.bundle.js","sources":["../../../../src/moj/helpers.mjs","../../../../src/moj/components/alert/alert.mjs"],"sourcesContent":["export function removeAttributeValue(el, attr, value) {\n let re, m\n if (el.getAttribute(attr)) {\n if (el.getAttribute(attr) === value) {\n el.removeAttribute(attr)\n } else {\n re = new RegExp(`(^|\\\\s)${value}(\\\\s|$)`)\n m = el.getAttribute(attr).match(re)\n if (m && m.length === 3) {\n el.setAttribute(\n attr,\n el.getAttribute(attr).replace(re, m[1] && m[2] ? ' ' : '')\n )\n }\n }\n }\n}\n\nexport function addAttributeValue(el, attr, value) {\n let re\n if (!el.getAttribute(attr)) {\n el.setAttribute(attr, value)\n } else {\n re = new RegExp(`(^|\\\\s)${value}(\\\\s|$)`)\n if (!re.test(el.getAttribute(attr))) {\n el.setAttribute(attr, `${el.getAttribute(attr)} ${value}`)\n }\n }\n}\n\nexport function dragAndDropSupported() {\n const div = document.createElement('div')\n return typeof div.ondrop !== 'undefined'\n}\n\nexport function formDataSupported() {\n return typeof FormData === 'function'\n}\n\nexport function fileApiSupported() {\n const input = document.createElement('input')\n input.type = 'file'\n return typeof input.files !== 'undefined'\n}\n\n/**\n * Find an elements next sibling\n *\n * Utility function to find an elements next sibling matching the provided\n * selector.\n *\n * @param {Element | null} $element - Element to find siblings for\n * @param {string} [selector] - selector for required sibling\n */\nexport function getNextSibling($element, selector) {\n if (!$element || !($element instanceof HTMLElement)) {\n return\n }\n\n // Get the next sibling element\n let $sibling = $element.nextElementSibling\n\n // If there's no selector, return the first sibling\n if (!selector) return $sibling\n\n // If the sibling matches our selector, use it\n // If not, jump to the next sibling and continue the loop\n while ($sibling) {\n if ($sibling.matches(selector)) return $sibling\n $sibling = $sibling.nextElementSibling\n }\n}\n\n/**\n * Find an elements preceding sibling\n *\n * Utility function to find an elements previous sibling matching the provided\n * selector.\n *\n * @param {Element | null} $element - Element to find siblings for\n * @param {string} [selector] - selector for required sibling\n */\nexport function getPreviousSibling($element, selector) {\n if (!$element || !($element instanceof HTMLElement)) {\n return\n }\n\n // Get the previous sibling element\n let $sibling = $element.previousElementSibling\n\n // If there's no selector, return the first sibling\n if (!selector) return $sibling\n\n // If the sibling matches our selector, use it\n // If not, jump to the next sibling and continue the loop\n while ($sibling) {\n if ($sibling.matches(selector)) return $sibling\n $sibling = $sibling.previousElementSibling\n }\n}\n\n/**\n * @param {Element | null} $element\n * @param {string} [selector]\n */\nexport function findNearestMatchingElement($element, selector) {\n // If no element or selector is provided, return\n if (!$element || !($element instanceof HTMLElement) || !selector) {\n return\n }\n\n // Start with the current element\n let $currentElement = $element\n\n while ($currentElement) {\n // First check the current element\n if ($currentElement.matches(selector)) {\n return $currentElement\n }\n\n // Check all previous siblings\n let $sibling = $currentElement.previousElementSibling\n while ($sibling) {\n // Check if the sibling itself is a heading\n if ($sibling.matches(selector)) {\n return $sibling\n }\n $sibling = $sibling.previousElementSibling\n }\n\n // If no match found in siblings, move up to parent\n $currentElement = $currentElement.parentElement\n }\n}\n\n/**\n * Move focus to element\n *\n * Sets tabindex to -1 to make the element programmatically focusable,\n * but removes it on blur as the element doesn't need to be focused again.\n *\n * @param {HTMLElement} $element - HTML element\n * @param {object} [options] - Handler options\n * @param {function(this: HTMLElement): void} [options.onBeforeFocus] - Callback before focus\n * @param {function(this: HTMLElement): void} [options.onBlur] - Callback on blur\n */\nexport function setFocus($element, options = {}) {\n const isFocusable = $element.getAttribute('tabindex')\n\n if (!isFocusable) {\n $element.setAttribute('tabindex', '-1')\n }\n\n /**\n * Handle element focus\n */\n function onFocus() {\n $element.addEventListener('blur', onBlur, { once: true })\n }\n\n /**\n * Handle element blur\n */\n function onBlur() {\n if (options.onBlur) {\n options.onBlur.call($element)\n }\n\n if (!isFocusable) {\n $element.removeAttribute('tabindex')\n }\n }\n\n // Add listener to reset element on blur, after focus\n $element.addEventListener('focus', onFocus, { once: true })\n\n // Focus element\n if (options.onBeforeFocus) {\n options.onBeforeFocus.call($element)\n }\n $element.focus()\n}\n","import {\n findNearestMatchingElement,\n getPreviousSibling,\n setFocus\n} from '../../helpers.mjs'\n\nexport class Alert {\n /**\n * @param {Element | null} $module - HTML element to use for alert\n * @param {AlertConfig} [config] - Alert config\n */\n constructor($module, config = {}) {\n if (!$module || !($module instanceof HTMLElement)) {\n return this\n }\n\n const schema = Object.freeze({\n properties: {\n dismissible: { type: 'boolean' },\n dismissText: { type: 'string' },\n disableAutoFocus: { type: 'boolean' },\n focusOnDismissSelector: { type: 'string' }\n }\n })\n\n const defaults = {\n dismissible: false,\n dismissText: 'Dismiss',\n disableAutoFocus: false\n }\n\n // data attributes override JS config, which overrides defaults\n this.config = this.mergeConfigs(\n defaults,\n config,\n this.parseDataset(schema, $module.dataset)\n )\n\n this.$module = $module\n\n /**\n * Focus the alert\n *\n * If `role=\"alert\"` is set, focus the element to help some assistive\n * technologies prioritise announcing it.\n *\n * You can turn off the auto-focus functionality by setting\n * `data-disable-auto-focus=\"true\"` in the component HTML. You might wish to\n * do this based on user research findings, or to avoid a clash with another\n * element which should be focused when the page loads.\n */\n if (\n this.$module.getAttribute('role') === 'alert' &&\n !this.config.disableAutoFocus\n ) {\n setFocus(this.$module)\n }\n\n this.$dismissButton = this.$module.querySelector('.moj-alert__dismiss')\n\n if (this.config.dismissible && this.$dismissButton) {\n this.$dismissButton.innerHTML = this.config.dismissText\n this.$dismissButton.removeAttribute('hidden')\n\n this.$module.addEventListener('click', (event) => {\n if (\n event.target instanceof Node &&\n this.$dismissButton.contains(event.target)\n ) {\n this.dimiss()\n }\n })\n }\n }\n\n /**\n * Handle dismissing the alert\n */\n dimiss() {\n let $elementToRecieveFocus\n\n // If a selector has been provided, attempt to find that element\n if (this.config.focusOnDismissSelector) {\n $elementToRecieveFocus = document.querySelector(\n this.config.focusOnDismissSelector\n )\n }\n\n // Is the next sibling another alert\n if (!$elementToRecieveFocus) {\n const $nextSibling = this.$module.nextElementSibling\n if ($nextSibling && $nextSibling.matches('.moj-alert')) {\n $elementToRecieveFocus = $nextSibling\n }\n }\n\n // Else try to find any preceding sibling alert or heading\n if (!$elementToRecieveFocus) {\n $elementToRecieveFocus = getPreviousSibling(\n this.$module,\n '.moj-alert, h1, h2, h3, h4, h5, h6'\n )\n }\n\n // Else find the closest ancestor heading, or fallback to main, or last resort\n // use the body element\n if (!$elementToRecieveFocus) {\n $elementToRecieveFocus = findNearestMatchingElement(\n this.$module,\n 'h1, h2, h3, h4, h5, h6, main, body'\n )\n }\n\n // If we have an element, place focus on it\n if ($elementToRecieveFocus instanceof HTMLElement) {\n setFocus($elementToRecieveFocus)\n }\n\n // Remove the alert\n this.$module.remove()\n }\n\n /**\n * Normalise string\n *\n * 'If it looks like a duck, and it quacks like a duck…' 🦆\n *\n * If the passed value looks like a boolean or a number, convert it to a boolean\n * or number.\n *\n * Designed to be used to convert config passed via data attributes (which are\n * always strings) into something sensible.\n *\n * @internal\n * @param {DOMStringMap[string]} value - The value to normalise\n * @param {SchemaProperty} [property] - Component schema property\n * @returns {string | boolean | number | undefined} Normalised data\n */\n normaliseString(value, property) {\n const trimmedValue = value ? value.trim() : ''\n\n let output\n let outputType\n if (property && property.type) {\n outputType = property.type\n }\n\n // No schema type set? Determine automatically\n if (!outputType) {\n if (['true', 'false'].includes(trimmedValue)) {\n outputType = 'boolean'\n }\n\n // Empty / whitespace-only strings are considered finite so we need to check\n // the length of the trimmed string as well\n if (trimmedValue.length > 0 && Number.isFinite(Number(trimmedValue))) {\n outputType = 'number'\n }\n }\n\n switch (outputType) {\n case 'boolean':\n output = trimmedValue === 'true'\n break\n\n case 'number':\n output = Number(trimmedValue)\n break\n\n default:\n output = value\n }\n\n return output\n }\n\n /**\n * Parse dataset\n *\n * Loop over an object and normalise each value using {@link normaliseString},\n * optionally expanding nested `i18n.field`\n *\n * @param {Schema} schema - component schema\n * @param {DOMStringMap} dataset - HTML element dataset\n * @returns {object} Normalised dataset\n */\n parseDataset(schema, dataset) {\n const parsed = {}\n\n for (const [field, property] of Object.entries(schema.properties)) {\n if (field in dataset) {\n if (dataset[field]) {\n parsed[field] = this.normaliseString(dataset[field], property)\n }\n }\n }\n\n return parsed\n }\n\n /**\n * Config merging function\n *\n * Takes any number of objects and combines them together, with\n * greatest priority on the LAST item passed in.\n *\n * @param {...{ [key: string]: unknown }} configObjects - Config objects to merge\n * @returns {{ [key: string]: unknown }} A merged config object\n */\n mergeConfigs(...configObjects) {\n const formattedConfigObject = {}\n\n // Loop through each of the passed objects\n for (const configObject of configObjects) {\n for (const key of Object.keys(configObject)) {\n const option = formattedConfigObject[key]\n const override = configObject[key]\n\n // Push their keys one-by-one into formattedConfigObject. Any duplicate\n // keys with object values will be merged, otherwise the new value will\n // override the existing value.\n if (typeof option === 'object' && typeof override === 'object') {\n // @ts-expect-error Index signature for type 'string' is missing\n formattedConfigObject[key] = this.mergeConfigs(option, override)\n } else {\n formattedConfigObject[key] = override\n }\n }\n }\n\n return formattedConfigObject\n }\n}\n\n/**\n * @typedef {object} AlertConfig\n * @property {boolean} [dismissible=false] - Can the alert be dismissed by the user\n * @property {string} [dismissText=Dismiss] - the label text for the dismiss button\n * @property {boolean} [disableAutoFocus=false] - whether the alert will be autofocused\n * @property {string} [focusOnDismissSelector] - CSS Selector for element to be focused on dismiss\n */\n"],"names":["getPreviousSibling","$element","selector","HTMLElement","$sibling","previousElementSibling","matches","findNearestMatchingElement","$currentElement","parentElement","setFocus","options","isFocusable","getAttribute","setAttribute","onFocus","addEventListener","onBlur","once","call","removeAttribute","onBeforeFocus","focus","Alert","constructor","$module","config","schema","Object","freeze","properties","dismissible","type","dismissText","disableAutoFocus","focusOnDismissSelector","defaults","mergeConfigs","parseDataset","dataset","$dismissButton","querySelector","innerHTML","event","target","Node","contains","dimiss","$elementToRecieveFocus","document","$nextSibling","nextElementSibling","remove","normaliseString","value","property","trimmedValue","trim","output","outputType","includes","length","Number","isFinite","parsed","field","entries","configObjects","formattedConfigObject","configObject","key","keys","option","override"],"mappings":"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"}
1
+ {"version":3,"file":"alert.bundle.js","sources":["../../../../src/moj/common/index.mjs","../../../../src/moj/helpers.mjs","../../../../src/moj/components/alert/alert.mjs"],"sourcesContent":["/**\n * GOV.UK Frontend helpers\n *\n * @todo Import from GOV.UK Frontend\n */\n\n/**\n * Move focus to element\n *\n * Sets tabindex to -1 to make the element programmatically focusable,\n * but removes it on blur as the element doesn't need to be focused again.\n *\n * @template {HTMLElement} FocusElement\n * @param {FocusElement} $element - HTML element\n * @param {object} [options] - Handler options\n * @param {function(this: FocusElement): void} [options.onBeforeFocus] - Callback before focus\n * @param {function(this: FocusElement): void} [options.onBlur] - Callback on blur\n */\nexport function setFocus($element, options = {}) {\n const isFocusable = $element.getAttribute('tabindex')\n\n if (!isFocusable) {\n $element.setAttribute('tabindex', '-1')\n }\n\n /**\n * Handle element focus\n */\n function onFocus() {\n $element.addEventListener('blur', onBlur, { once: true })\n }\n\n /**\n * Handle element blur\n */\n function onBlur() {\n options.onBlur?.call($element)\n\n if (!isFocusable) {\n $element.removeAttribute('tabindex')\n }\n }\n\n // Add listener to reset element on blur, after focus\n $element.addEventListener('focus', onFocus, { once: true })\n\n // Focus element\n options.onBeforeFocus?.call($element)\n $element.focus()\n}\n","/**\n * @param {Element} $element - Element to remove attribute value from\n * @param {string} attr - Attribute name\n * @param {string} value - Attribute value\n */\nexport function removeAttributeValue($element, attr, value) {\n let re, m\n if ($element.getAttribute(attr)) {\n if ($element.getAttribute(attr) === value) {\n $element.removeAttribute(attr)\n } else {\n re = new RegExp(`(^|\\\\s)${value}(\\\\s|$)`)\n m = $element.getAttribute(attr).match(re)\n if (m && m.length === 3) {\n $element.setAttribute(\n attr,\n $element.getAttribute(attr).replace(re, m[1] && m[2] ? ' ' : '')\n )\n }\n }\n }\n}\n\n/**\n * @param {Element} $element - Element to add attribute value to\n * @param {string} attr - Attribute name\n * @param {string} value - Attribute value\n */\nexport function addAttributeValue($element, attr, value) {\n let re\n if (!$element.getAttribute(attr)) {\n $element.setAttribute(attr, value)\n } else {\n re = new RegExp(`(^|\\\\s)${value}(\\\\s|$)`)\n if (!re.test($element.getAttribute(attr))) {\n $element.setAttribute(attr, `${$element.getAttribute(attr)} ${value}`)\n }\n }\n}\n\n/**\n * Find an elements next sibling\n *\n * Utility function to find an elements next sibling matching the provided\n * selector.\n *\n * @param {Element | null} $element - Element to find siblings for\n * @param {string} [selector] - selector for required sibling\n */\nexport function getNextSibling($element, selector) {\n if (!$element || !($element instanceof HTMLElement)) {\n return\n }\n\n // Get the next sibling element\n let $sibling = $element.nextElementSibling\n\n // If there's no selector, return the first sibling\n if (!selector) return $sibling\n\n // If the sibling matches our selector, use it\n // If not, jump to the next sibling and continue the loop\n while ($sibling) {\n if ($sibling.matches(selector)) return $sibling\n $sibling = $sibling.nextElementSibling\n }\n}\n\n/**\n * Find an elements preceding sibling\n *\n * Utility function to find an elements previous sibling matching the provided\n * selector.\n *\n * @param {Element | null} $element - Element to find siblings for\n * @param {string} [selector] - selector for required sibling\n */\nexport function getPreviousSibling($element, selector) {\n if (!$element || !($element instanceof HTMLElement)) {\n return\n }\n\n // Get the previous sibling element\n let $sibling = $element.previousElementSibling\n\n // If there's no selector, return the first sibling\n if (!selector) return $sibling\n\n // If the sibling matches our selector, use it\n // If not, jump to the next sibling and continue the loop\n while ($sibling) {\n if ($sibling.matches(selector)) return $sibling\n $sibling = $sibling.previousElementSibling\n }\n}\n\n/**\n * @param {Element | null} $element\n * @param {string} [selector]\n */\nexport function findNearestMatchingElement($element, selector) {\n // If no element or selector is provided, return\n if (!$element || !($element instanceof HTMLElement) || !selector) {\n return\n }\n\n // Start with the current element\n let $currentElement = $element\n\n while ($currentElement) {\n // First check the current element\n if ($currentElement.matches(selector)) {\n return $currentElement\n }\n\n // Check all previous siblings\n let $sibling = $currentElement.previousElementSibling\n while ($sibling) {\n // Check if the sibling itself is a heading\n if ($sibling.matches(selector)) {\n return $sibling\n }\n $sibling = $sibling.previousElementSibling\n }\n\n // If no match found in siblings, move up to parent\n $currentElement = $currentElement.parentElement\n }\n}\n","import { ConfigurableComponent } from 'govuk-frontend'\n\nimport { setFocus } from '../../common/index.mjs'\nimport {\n findNearestMatchingElement,\n getPreviousSibling\n} from '../../helpers.mjs'\n\n/**\n * @augments {ConfigurableComponent<AlertConfig>}\n */\nexport class Alert extends ConfigurableComponent {\n /**\n * @param {Element | null} $root - HTML element to use for alert\n * @param {AlertConfig} [config] - Alert config\n */\n constructor($root, config = {}) {\n super($root, config)\n\n /**\n * Focus the alert\n *\n * If `role=\"alert\"` is set, focus the element to help some assistive\n * technologies prioritise announcing it.\n *\n * You can turn off the auto-focus functionality by setting\n * `data-disable-auto-focus=\"true\"` in the component HTML. You might wish to\n * do this based on user research findings, or to avoid a clash with another\n * element which should be focused when the page loads.\n */\n if (\n this.$root.getAttribute('role') === 'alert' &&\n !this.config.disableAutoFocus\n ) {\n setFocus(this.$root)\n }\n\n this.$dismissButton = this.$root.querySelector('.moj-alert__dismiss')\n\n if (this.config.dismissible && this.$dismissButton) {\n this.$dismissButton.innerHTML = this.config.dismissText\n this.$dismissButton.removeAttribute('hidden')\n\n this.$root.addEventListener('click', (event) => {\n if (\n event.target instanceof Node &&\n this.$dismissButton.contains(event.target)\n ) {\n this.dimiss()\n }\n })\n }\n }\n\n /**\n * Handle dismissing the alert\n */\n dimiss() {\n let $elementToRecieveFocus\n\n // If a selector has been provided, attempt to find that element\n if (this.config.focusOnDismissSelector) {\n $elementToRecieveFocus = document.querySelector(\n this.config.focusOnDismissSelector\n )\n }\n\n // Is the next sibling another alert\n if (!$elementToRecieveFocus) {\n const $nextSibling = this.$root.nextElementSibling\n if ($nextSibling && $nextSibling.matches('.moj-alert')) {\n $elementToRecieveFocus = $nextSibling\n }\n }\n\n // Else try to find any preceding sibling alert or heading\n if (!$elementToRecieveFocus) {\n $elementToRecieveFocus = getPreviousSibling(\n this.$root,\n '.moj-alert, h1, h2, h3, h4, h5, h6'\n )\n }\n\n // Else find the closest ancestor heading, or fallback to main, or last resort\n // use the body element\n if (!$elementToRecieveFocus) {\n $elementToRecieveFocus = findNearestMatchingElement(\n this.$root,\n 'h1, h2, h3, h4, h5, h6, main, body'\n )\n }\n\n // If we have an element, place focus on it\n if ($elementToRecieveFocus instanceof HTMLElement) {\n setFocus($elementToRecieveFocus)\n }\n\n // Remove the alert\n this.$root.remove()\n }\n\n /**\n * Name for the component used when initialising using data-module attributes.\n */\n static moduleName = 'moj-alert'\n\n /**\n * Alert default config\n *\n * @type {AlertConfig}\n */\n static defaults = Object.freeze({\n dismissible: false,\n dismissText: 'Dismiss',\n disableAutoFocus: false\n })\n\n /**\n * Alert config schema\n *\n * @satisfies {Schema<AlertConfig>}\n */\n static schema = Object.freeze(\n /** @type {const} */ ({\n properties: {\n dismissible: { type: 'boolean' },\n dismissText: { type: 'string' },\n disableAutoFocus: { type: 'boolean' },\n focusOnDismissSelector: { type: 'string' }\n }\n })\n )\n}\n\n/**\n * @typedef {object} AlertConfig\n * @property {boolean} [dismissible=false] - Can the alert be dismissed by the user\n * @property {string} [dismissText=Dismiss] - the label text for the dismiss button\n * @property {boolean} [disableAutoFocus=false] - whether the alert will be autofocused\n * @property {string} [focusOnDismissSelector] - CSS Selector for element to be focused on dismiss\n */\n\n/**\n * @import { Schema } from 'govuk-frontend/dist/govuk/common/configuration.mjs'\n */\n"],"names":["setFocus","$element","options","_options$onBeforeFocu","isFocusable","getAttribute","setAttribute","onFocus","addEventListener","onBlur","once","_options$onBlur","call","removeAttribute","onBeforeFocus","focus","getPreviousSibling","selector","HTMLElement","$sibling","previousElementSibling","matches","findNearestMatchingElement","$currentElement","parentElement","Alert","ConfigurableComponent","constructor","$root","config","disableAutoFocus","$dismissButton","querySelector","dismissible","innerHTML","dismissText","event","target","Node","contains","dimiss","$elementToRecieveFocus","focusOnDismissSelector","document","$nextSibling","nextElementSibling","remove","moduleName","defaults","Object","freeze","schema","properties","type"],"mappings":"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"}