@ministryofjustice/frontend 4.0.1 → 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 (256) hide show
  1. package/govuk-prototype-kit.config.json +19 -4
  2. package/moj/_base.scss +2 -0
  3. package/moj/_base.scss.map +1 -0
  4. package/moj/all.bundle.js +3010 -0
  5. package/moj/all.bundle.js.map +1 -0
  6. package/moj/all.bundle.mjs +3293 -0
  7. package/moj/all.bundle.mjs.map +1 -0
  8. package/moj/all.mjs +17 -110
  9. package/moj/all.mjs.map +1 -1
  10. package/moj/all.scss +3 -0
  11. package/moj/all.scss.map +1 -0
  12. package/moj/common/index.mjs +57 -0
  13. package/moj/common/index.mjs.map +1 -0
  14. package/moj/common/moj-frontend-version.mjs +14 -0
  15. package/moj/common/moj-frontend-version.mjs.map +1 -0
  16. package/moj/components/_all.scss +2 -0
  17. package/moj/components/_all.scss.map +1 -0
  18. package/moj/components/action-bar/_action-bar.scss +2 -0
  19. package/moj/components/action-bar/_action-bar.scss.map +1 -0
  20. package/moj/components/add-another/_add-another.scss +2 -0
  21. package/moj/components/add-another/_add-another.scss.map +1 -0
  22. package/moj/components/add-another/add-another.bundle.js +157 -0
  23. package/moj/components/add-another/add-another.bundle.js.map +1 -0
  24. package/moj/components/add-another/add-another.bundle.mjs +271 -0
  25. package/moj/components/add-another/add-another.bundle.mjs.map +1 -0
  26. package/moj/components/add-another/add-another.mjs +135 -91
  27. package/moj/components/add-another/add-another.mjs.map +1 -1
  28. package/moj/components/alert/_alert.scss +4 -0
  29. package/moj/components/alert/_alert.scss.map +1 -0
  30. package/moj/components/alert/alert.bundle.js +254 -0
  31. package/moj/components/alert/alert.bundle.js.map +1 -0
  32. package/moj/components/alert/alert.bundle.mjs +490 -0
  33. package/moj/components/alert/alert.bundle.mjs.map +1 -0
  34. package/moj/components/alert/alert.mjs +97 -218
  35. package/moj/components/alert/alert.mjs.map +1 -1
  36. package/moj/components/alert/{alert.spec.helper.js → alert.spec.helper.bundle.js} +1 -1
  37. package/moj/components/alert/alert.spec.helper.bundle.js.map +1 -0
  38. package/moj/components/alert/alert.spec.helper.bundle.mjs +67 -0
  39. package/moj/components/alert/alert.spec.helper.bundle.mjs.map +1 -0
  40. package/moj/components/alert/alert.spec.helper.mjs.map +1 -1
  41. package/moj/components/badge/_badge.scss +2 -0
  42. package/moj/components/badge/_badge.scss.map +1 -0
  43. package/moj/components/banner/_banner.scss +2 -0
  44. package/moj/components/banner/_banner.scss.map +1 -0
  45. package/moj/components/button-menu/README.md +12 -6
  46. package/moj/components/button-menu/_button-menu.scss +4 -1
  47. package/moj/components/button-menu/_button-menu.scss.map +1 -0
  48. package/moj/components/button-menu/button-menu.bundle.js +270 -0
  49. package/moj/components/button-menu/button-menu.bundle.js.map +1 -0
  50. package/moj/components/button-menu/button-menu.bundle.mjs +506 -0
  51. package/moj/components/button-menu/button-menu.bundle.mjs.map +1 -0
  52. package/moj/components/button-menu/button-menu.mjs +214 -280
  53. package/moj/components/button-menu/button-menu.mjs.map +1 -1
  54. package/moj/components/cookie-banner/_cookie-banner.scss +2 -0
  55. package/moj/components/cookie-banner/_cookie-banner.scss.map +1 -0
  56. package/moj/components/currency-input/_currency-input.scss +2 -0
  57. package/moj/components/currency-input/_currency-input.scss.map +1 -0
  58. package/moj/components/date-picker/_date-picker.scss +2 -0
  59. package/moj/components/date-picker/_date-picker.scss.map +1 -0
  60. package/moj/components/date-picker/date-picker.bundle.js +804 -0
  61. package/moj/components/date-picker/date-picker.bundle.js.map +1 -0
  62. package/moj/components/date-picker/date-picker.bundle.mjs +1040 -0
  63. package/moj/components/date-picker/date-picker.bundle.mjs.map +1 -0
  64. package/moj/components/date-picker/date-picker.mjs +663 -827
  65. package/moj/components/date-picker/date-picker.mjs.map +1 -1
  66. package/moj/components/filter/_filter.scss +2 -0
  67. package/moj/components/filter/_filter.scss.map +1 -0
  68. package/moj/components/filter/template.njk +1 -1
  69. package/moj/components/filter-toggle-button/filter-toggle-button.bundle.js +185 -0
  70. package/moj/components/filter-toggle-button/filter-toggle-button.bundle.js.map +1 -0
  71. package/moj/components/filter-toggle-button/filter-toggle-button.bundle.mjs +421 -0
  72. package/moj/components/filter-toggle-button/filter-toggle-button.bundle.mjs.map +1 -0
  73. package/moj/components/filter-toggle-button/filter-toggle-button.mjs +166 -81
  74. package/moj/components/filter-toggle-button/filter-toggle-button.mjs.map +1 -1
  75. package/moj/components/form-validator/form-validator.bundle.js +288 -0
  76. package/moj/components/form-validator/form-validator.bundle.js.map +1 -0
  77. package/moj/components/form-validator/form-validator.bundle.mjs +524 -0
  78. package/moj/components/form-validator/form-validator.bundle.mjs.map +1 -0
  79. package/moj/components/form-validator/form-validator.mjs +226 -149
  80. package/moj/components/form-validator/form-validator.mjs.map +1 -1
  81. package/moj/components/header/_header.scss +2 -0
  82. package/moj/components/header/_header.scss.map +1 -0
  83. package/moj/components/identity-bar/_identity-bar.scss +2 -0
  84. package/moj/components/identity-bar/_identity-bar.scss.map +1 -0
  85. package/moj/components/interruption-card/_interruption-card.scss +2 -0
  86. package/moj/components/interruption-card/_interruption-card.scss.map +1 -0
  87. package/moj/components/messages/_messages.scss +2 -0
  88. package/moj/components/messages/_messages.scss.map +1 -0
  89. package/moj/components/multi-file-upload/_multi-file-upload.scss +2 -0
  90. package/moj/components/multi-file-upload/_multi-file-upload.scss.map +1 -0
  91. package/moj/components/multi-file-upload/multi-file-upload.bundle.js +397 -0
  92. package/moj/components/multi-file-upload/multi-file-upload.bundle.js.map +1 -0
  93. package/moj/components/multi-file-upload/multi-file-upload.bundle.mjs +633 -0
  94. package/moj/components/multi-file-upload/multi-file-upload.bundle.mjs.map +1 -0
  95. package/moj/components/multi-file-upload/multi-file-upload.mjs +384 -213
  96. package/moj/components/multi-file-upload/multi-file-upload.mjs.map +1 -1
  97. package/moj/components/multi-file-upload/template.njk +1 -1
  98. package/moj/components/multi-select/_multi-select.scss +2 -0
  99. package/moj/components/multi-select/_multi-select.scss.map +1 -0
  100. package/moj/components/multi-select/multi-select.bundle.js +143 -0
  101. package/moj/components/multi-select/multi-select.bundle.js.map +1 -0
  102. package/moj/components/multi-select/multi-select.bundle.mjs +379 -0
  103. package/moj/components/multi-select/multi-select.bundle.mjs.map +1 -0
  104. package/moj/components/multi-select/multi-select.mjs +123 -64
  105. package/moj/components/multi-select/multi-select.mjs.map +1 -1
  106. package/moj/components/notification-badge/_notification-badge.scss +2 -0
  107. package/moj/components/notification-badge/_notification-badge.scss.map +1 -0
  108. package/moj/components/organisation-switcher/_organisation-switcher.scss +2 -0
  109. package/moj/components/organisation-switcher/_organisation-switcher.scss.map +1 -0
  110. package/moj/components/page-header-actions/_page-header-actions.scss +2 -0
  111. package/moj/components/page-header-actions/_page-header-actions.scss.map +1 -0
  112. package/moj/components/pagination/_pagination.scss +2 -2
  113. package/moj/components/pagination/_pagination.scss.map +1 -0
  114. package/moj/components/password-reveal/_password-reveal.scss +5 -1
  115. package/moj/components/password-reveal/_password-reveal.scss.map +1 -0
  116. package/moj/components/password-reveal/password-reveal.bundle.js +52 -0
  117. package/moj/components/password-reveal/password-reveal.bundle.js.map +1 -0
  118. package/moj/components/password-reveal/password-reveal.bundle.mjs +166 -0
  119. package/moj/components/password-reveal/password-reveal.bundle.mjs.map +1 -0
  120. package/moj/components/password-reveal/password-reveal.mjs +39 -29
  121. package/moj/components/password-reveal/password-reveal.mjs.map +1 -1
  122. package/moj/components/primary-navigation/_primary-navigation.scss +2 -0
  123. package/moj/components/primary-navigation/_primary-navigation.scss.map +1 -0
  124. package/moj/components/progress-bar/_progress-bar.scss +2 -0
  125. package/moj/components/progress-bar/_progress-bar.scss.map +1 -0
  126. package/moj/components/rich-text-editor/README.md +16 -9
  127. package/moj/components/rich-text-editor/_rich-text-editor.scss +2 -0
  128. package/moj/components/rich-text-editor/_rich-text-editor.scss.map +1 -0
  129. package/moj/components/rich-text-editor/rich-text-editor.bundle.js +210 -0
  130. package/moj/components/rich-text-editor/rich-text-editor.bundle.js.map +1 -0
  131. package/moj/components/rich-text-editor/rich-text-editor.bundle.mjs +446 -0
  132. package/moj/components/rich-text-editor/rich-text-editor.bundle.mjs.map +1 -0
  133. package/moj/components/rich-text-editor/rich-text-editor.mjs +186 -140
  134. package/moj/components/rich-text-editor/rich-text-editor.mjs.map +1 -1
  135. package/moj/components/search/_search.scss +2 -0
  136. package/moj/components/search/_search.scss.map +1 -0
  137. package/moj/components/search-toggle/{search-toggle.scss → _search-toggle.scss} +2 -0
  138. package/moj/components/search-toggle/_search-toggle.scss.map +1 -0
  139. package/moj/components/search-toggle/search-toggle.bundle.js +122 -0
  140. package/moj/components/search-toggle/search-toggle.bundle.js.map +1 -0
  141. package/moj/components/search-toggle/search-toggle.bundle.mjs +358 -0
  142. package/moj/components/search-toggle/search-toggle.bundle.mjs.map +1 -0
  143. package/moj/components/search-toggle/search-toggle.mjs +104 -43
  144. package/moj/components/search-toggle/search-toggle.mjs.map +1 -1
  145. package/moj/components/side-navigation/_side-navigation.scss +2 -0
  146. package/moj/components/side-navigation/_side-navigation.scss.map +1 -0
  147. package/moj/components/sortable-table/_sortable-table.scss +2 -2
  148. package/moj/components/sortable-table/_sortable-table.scss.map +1 -0
  149. package/moj/components/sortable-table/sortable-table.bundle.js +202 -0
  150. package/moj/components/sortable-table/sortable-table.bundle.js.map +1 -0
  151. package/moj/components/sortable-table/sortable-table.bundle.mjs +438 -0
  152. package/moj/components/sortable-table/sortable-table.bundle.mjs.map +1 -0
  153. package/moj/components/sortable-table/sortable-table.mjs +179 -122
  154. package/moj/components/sortable-table/sortable-table.mjs.map +1 -1
  155. package/moj/components/sub-navigation/_sub-navigation.scss +2 -0
  156. package/moj/components/sub-navigation/_sub-navigation.scss.map +1 -0
  157. package/moj/components/tag/_tag.scss +2 -0
  158. package/moj/components/tag/_tag.scss.map +1 -0
  159. package/moj/components/task-list/_task-list.scss +2 -0
  160. package/moj/components/task-list/_task-list.scss.map +1 -0
  161. package/moj/components/ticket-panel/_ticket-panel.scss +2 -0
  162. package/moj/components/ticket-panel/_ticket-panel.scss.map +1 -0
  163. package/moj/components/timeline/_timeline.scss +2 -0
  164. package/moj/components/timeline/_timeline.scss.map +1 -0
  165. package/moj/core/_all.scss +3 -0
  166. package/moj/core/_all.scss.map +1 -0
  167. package/moj/core/_moj-frontend-properties.scss +7 -0
  168. package/moj/core/_moj-frontend-properties.scss.map +1 -0
  169. package/moj/filters/all.js +44 -22
  170. package/moj/filters/prototype-kit-13-filters.js +4 -3
  171. package/moj/helpers/_all.scss +2 -0
  172. package/moj/helpers/_all.scss.map +1 -0
  173. package/moj/helpers/_hidden.scss +2 -0
  174. package/moj/helpers/_hidden.scss.map +1 -0
  175. package/moj/helpers/_links.scss +2 -0
  176. package/moj/helpers/_links.scss.map +1 -0
  177. package/moj/helpers.bundle.js +140 -0
  178. package/moj/helpers.bundle.js.map +1 -0
  179. package/moj/helpers.bundle.mjs +128 -0
  180. package/moj/helpers.bundle.mjs.map +1 -0
  181. package/moj/helpers.mjs +50 -77
  182. package/moj/helpers.mjs.map +1 -1
  183. package/moj/init.js +11 -2
  184. package/moj/moj-frontend.min.css +1 -1
  185. package/moj/moj-frontend.min.css.map +1 -1
  186. package/moj/moj-frontend.min.js +1 -1
  187. package/moj/moj-frontend.min.js.map +1 -1
  188. package/moj/objects/_all.scss +2 -0
  189. package/moj/objects/_all.scss.map +1 -0
  190. package/moj/objects/_button-group.scss +2 -0
  191. package/moj/objects/_button-group.scss.map +1 -0
  192. package/moj/objects/_filter-layout.scss +2 -0
  193. package/moj/objects/_filter-layout.scss.map +1 -0
  194. package/moj/objects/_scrollable-pane.scss +2 -0
  195. package/moj/objects/_scrollable-pane.scss.map +1 -0
  196. package/moj/objects/_width-container.scss +2 -0
  197. package/moj/objects/_width-container.scss.map +1 -0
  198. package/moj/settings/_all.scss +2 -0
  199. package/moj/settings/_all.scss.map +1 -0
  200. package/moj/settings/_assets.scss +2 -0
  201. package/moj/settings/_assets.scss.map +1 -0
  202. package/moj/settings/_colours.scss +2 -0
  203. package/moj/settings/_colours.scss.map +1 -0
  204. package/moj/settings/_measurements.scss +2 -0
  205. package/moj/settings/_measurements.scss.map +1 -0
  206. package/moj/settings/_typography.scss +2 -0
  207. package/moj/settings/_typography.scss.map +1 -0
  208. package/moj/template.njk +13 -0
  209. package/moj/utilities/_all.scss +2 -0
  210. package/moj/utilities/_all.scss.map +1 -0
  211. package/moj/utilities/_hidden.scss +2 -0
  212. package/moj/utilities/_hidden.scss.map +1 -0
  213. package/moj/utilities/_width-container.scss +2 -0
  214. package/moj/utilities/_width-container.scss.map +1 -0
  215. package/moj/vendor/govuk-frontend/_base.scss +2 -0
  216. package/moj/vendor/govuk-frontend/_base.scss.map +1 -0
  217. package/moj/vendor/govuk-frontend/_index.scss +2 -0
  218. package/moj/vendor/govuk-frontend/_index.scss.map +1 -0
  219. package/package.json +5 -6
  220. package/moj/all.jquery.min.js +0 -1
  221. package/moj/all.jquery.min.js.map +0 -1
  222. package/moj/all.js +0 -2662
  223. package/moj/all.js.map +0 -1
  224. package/moj/components/add-another/add-another.js +0 -115
  225. package/moj/components/add-another/add-another.js.map +0 -1
  226. package/moj/components/alert/alert.js +0 -356
  227. package/moj/components/alert/alert.js.map +0 -1
  228. package/moj/components/alert/alert.spec.helper.js.map +0 -1
  229. package/moj/components/button-menu/button-menu.js +0 -338
  230. package/moj/components/button-menu/button-menu.js.map +0 -1
  231. package/moj/components/date-picker/date-picker.js +0 -970
  232. package/moj/components/date-picker/date-picker.js.map +0 -1
  233. package/moj/components/filter-toggle-button/filter-toggle-button.js +0 -102
  234. package/moj/components/filter-toggle-button/filter-toggle-button.js.map +0 -1
  235. package/moj/components/form-validator/form-validator.js +0 -205
  236. package/moj/components/form-validator/form-validator.js.map +0 -1
  237. package/moj/components/multi-file-upload/multi-file-upload.js +0 -241
  238. package/moj/components/multi-file-upload/multi-file-upload.js.map +0 -1
  239. package/moj/components/multi-select/multi-select.js +0 -86
  240. package/moj/components/multi-select/multi-select.js.map +0 -1
  241. package/moj/components/password-reveal/password-reveal.js +0 -44
  242. package/moj/components/password-reveal/password-reveal.js.map +0 -1
  243. package/moj/components/rich-text-editor/rich-text-editor.js +0 -166
  244. package/moj/components/rich-text-editor/rich-text-editor.js.map +0 -1
  245. package/moj/components/search-toggle/search-toggle.js +0 -63
  246. package/moj/components/search-toggle/search-toggle.js.map +0 -1
  247. package/moj/components/sortable-table/sortable-table.js +0 -147
  248. package/moj/components/sortable-table/sortable-table.js.map +0 -1
  249. package/moj/helpers.js +0 -200
  250. package/moj/helpers.js.map +0 -1
  251. package/moj/vendor/html5shiv.js +0 -326
  252. package/moj/vendor/jquery.js +0 -9300
  253. package/moj/version.js +0 -12
  254. package/moj/version.js.map +0 -1
  255. package/moj/version.mjs +0 -4
  256. package/moj/version.mjs.map +0 -1
@@ -1,94 +1,179 @@
1
- function FilterToggleButton(options) {
2
- this.options = options;
3
- this.container = $(this.options.toggleButton.container);
4
- this.filterContainer = $(this.options.filter.container);
1
+ import { ConfigurableComponent } from 'govuk-frontend';
5
2
 
6
- this.createToggleButton();
7
- this.setupResponsiveChecks();
8
- this.filterContainer.attr('tabindex', '-1');
9
- if (this.options.startHidden) {
3
+ /**
4
+ * @augments {ConfigurableComponent<FilterToggleButtonConfig>}
5
+ */
6
+ class FilterToggleButton extends ConfigurableComponent {
7
+ /**
8
+ * @param {Element | null} $root - HTML element to use for filter toggle button
9
+ * @param {FilterToggleButtonConfig} [config] - Filter toggle button config
10
+ */
11
+ constructor($root, config = {}) {
12
+ var _this$config$toggleBu, _this$config$closeBut;
13
+ super($root, config);
14
+ const $toggleButtonContainer = (_this$config$toggleBu = this.config.toggleButtonContainer.element) != null ? _this$config$toggleBu : document.querySelector(this.config.toggleButtonContainer.selector);
15
+ const $closeButtonContainer = (_this$config$closeBut = this.config.closeButtonContainer.element) != null ? _this$config$closeBut : this.$root.querySelector(this.config.closeButtonContainer.selector);
16
+ if (!($toggleButtonContainer instanceof HTMLElement && $closeButtonContainer instanceof HTMLElement)) {
17
+ return this;
18
+ }
19
+ this.$toggleButtonContainer = $toggleButtonContainer;
20
+ this.$closeButtonContainer = $closeButtonContainer;
21
+ this.createToggleButton();
22
+ this.setupResponsiveChecks();
23
+ this.$root.setAttribute('tabindex', '-1');
24
+ if (this.config.startHidden) {
25
+ this.hideMenu();
26
+ }
27
+ }
28
+ setupResponsiveChecks() {
29
+ this.mq = window.matchMedia(this.config.bigModeMediaQuery);
30
+ this.mq.addListener(this.checkMode.bind(this));
31
+ this.checkMode();
32
+ }
33
+ createToggleButton() {
34
+ this.$menuButton = document.createElement('button');
35
+ this.$menuButton.setAttribute('type', 'button');
36
+ this.$menuButton.setAttribute('aria-haspopup', 'true');
37
+ this.$menuButton.setAttribute('aria-expanded', 'false');
38
+ this.$menuButton.className = `govuk-button ${this.config.toggleButton.classes}`;
39
+ this.$menuButton.textContent = this.config.toggleButton.showText;
40
+ this.$menuButton.addEventListener('click', this.onMenuButtonClick.bind(this));
41
+ this.$toggleButtonContainer.append(this.$menuButton);
42
+ }
43
+ checkMode() {
44
+ if (this.mq.matches) {
45
+ this.enableBigMode();
46
+ } else {
47
+ this.enableSmallMode();
48
+ }
49
+ }
50
+ enableBigMode() {
51
+ this.showMenu();
52
+ this.removeCloseButton();
53
+ }
54
+ enableSmallMode() {
10
55
  this.hideMenu();
56
+ this.addCloseButton();
11
57
  }
12
- }
13
-
14
- FilterToggleButton.prototype.setupResponsiveChecks = function () {
15
- this.mq = window.matchMedia(this.options.bigModeMediaQuery);
16
- this.mq.addListener($.proxy(this, 'checkMode'));
17
- this.checkMode(this.mq);
18
- };
19
-
20
- FilterToggleButton.prototype.createToggleButton = function () {
21
- this.menuButton = $(
22
- `<button class="govuk-button ${this.options.toggleButton.classes}" type="button" aria-haspopup="true" aria-expanded="false">${this.options.toggleButton.showText}</button>`
23
- );
24
- this.menuButton.on('click', $.proxy(this, 'onMenuButtonClick'));
25
- this.container.append(this.menuButton);
26
- };
27
-
28
- FilterToggleButton.prototype.checkMode = function (mq) {
29
- if (mq.matches) {
30
- this.enableBigMode();
31
- } else {
32
- this.enableSmallMode();
58
+ addCloseButton() {
59
+ this.$closeButton = document.createElement('button');
60
+ this.$closeButton.setAttribute('type', 'button');
61
+ this.$closeButton.className = this.config.closeButton.classes;
62
+ this.$closeButton.textContent = this.config.closeButton.text;
63
+ this.$closeButton.addEventListener('click', this.onCloseClick.bind(this));
64
+ this.$closeButtonContainer.append(this.$closeButton);
33
65
  }
34
- };
35
-
36
- FilterToggleButton.prototype.enableBigMode = function () {
37
- this.showMenu();
38
- this.removeCloseButton();
39
- };
40
-
41
- FilterToggleButton.prototype.enableSmallMode = function () {
42
- this.hideMenu();
43
- this.addCloseButton();
44
- };
45
-
46
- FilterToggleButton.prototype.addCloseButton = function () {
47
- if (this.options.closeButton) {
48
- this.closeButton = $(
49
- `<button class="moj-filter__close" type="button">${this.options.closeButton.text}</button>`
50
- );
51
- this.closeButton.on('click', $.proxy(this, 'onCloseClick'));
52
- $(this.options.closeButton.container).append(this.closeButton);
66
+ onCloseClick() {
67
+ this.hideMenu();
68
+ this.$menuButton.focus();
53
69
  }
54
- };
55
-
56
- FilterToggleButton.prototype.onCloseClick = function () {
57
- this.hideMenu();
58
- this.menuButton.focus();
59
- };
60
-
61
- FilterToggleButton.prototype.removeCloseButton = function () {
62
- if (this.closeButton) {
63
- this.closeButton.remove();
64
- this.closeButton = null;
70
+ removeCloseButton() {
71
+ if (this.$closeButton) {
72
+ this.$closeButton.remove();
73
+ this.$closeButton = null;
74
+ }
75
+ }
76
+ hideMenu() {
77
+ this.$menuButton.setAttribute('aria-expanded', 'false');
78
+ this.$root.classList.add('moj-js-hidden');
79
+ this.$menuButton.textContent = this.config.toggleButton.showText;
80
+ }
81
+ showMenu() {
82
+ this.$menuButton.setAttribute('aria-expanded', 'true');
83
+ this.$root.classList.remove('moj-js-hidden');
84
+ this.$menuButton.textContent = this.config.toggleButton.hideText;
85
+ }
86
+ onMenuButtonClick() {
87
+ this.toggle();
88
+ }
89
+ toggle() {
90
+ if (this.$menuButton.getAttribute('aria-expanded') === 'false') {
91
+ this.showMenu();
92
+ this.$root.focus();
93
+ } else {
94
+ this.hideMenu();
95
+ }
65
96
  }
66
- };
67
-
68
- FilterToggleButton.prototype.hideMenu = function () {
69
- this.menuButton.attr('aria-expanded', 'false');
70
- this.filterContainer.addClass('moj-js-hidden');
71
- this.menuButton.text(this.options.toggleButton.showText);
72
- };
73
97
 
74
- FilterToggleButton.prototype.showMenu = function () {
75
- this.menuButton.attr('aria-expanded', 'true');
76
- this.filterContainer.removeClass('moj-js-hidden');
77
- this.menuButton.text(this.options.toggleButton.hideText);
78
- };
98
+ /**
99
+ * Name for the component used when initialising using data-module attributes.
100
+ */
101
+ }
79
102
 
80
- FilterToggleButton.prototype.onMenuButtonClick = function () {
81
- this.toggle();
82
- };
103
+ /**
104
+ * @typedef {object} FilterToggleButtonConfig
105
+ * @property {string} [bigModeMediaQuery] - Media query for big mode
106
+ * @property {boolean} [startHidden] - Whether to start hidden
107
+ * @property {object} [toggleButton] - Toggle button config
108
+ * @property {string} [toggleButton.showText] - Text for show button
109
+ * @property {string} [toggleButton.hideText] - Text for hide button
110
+ * @property {string} [toggleButton.classes] - Classes for toggle button
111
+ * @property {object} [toggleButtonContainer] - Toggle button container config
112
+ * @property {string} [toggleButtonContainer.selector] - Selector for toggle button container
113
+ * @property {Element | null} [toggleButtonContainer.element] - HTML element for toggle button container
114
+ * @property {object} [closeButton] - Close button config
115
+ * @property {string} [closeButton.text] - Text for close button
116
+ * @property {string} [closeButton.classes] - Classes for close button
117
+ * @property {object} [closeButtonContainer] - Close button container config
118
+ * @property {string} [closeButtonContainer.selector] - Selector for close button container
119
+ * @property {Element | null} [closeButtonContainer.element] - HTML element for close button container
120
+ */
83
121
 
84
- FilterToggleButton.prototype.toggle = function () {
85
- if (this.menuButton.attr('aria-expanded') === 'false') {
86
- this.showMenu();
87
- this.filterContainer.get(0).focus();
88
- } else {
89
- this.hideMenu();
122
+ /**
123
+ * @import { Schema } from 'govuk-frontend/dist/govuk/common/configuration.mjs'
124
+ */
125
+ FilterToggleButton.moduleName = 'moj-filter';
126
+ /**
127
+ * Filter toggle button config
128
+ *
129
+ * @type {FilterToggleButtonConfig}
130
+ */
131
+ FilterToggleButton.defaults = Object.freeze({
132
+ bigModeMediaQuery: '(min-width: 48.0625em)',
133
+ startHidden: true,
134
+ toggleButton: {
135
+ showText: 'Show filter',
136
+ hideText: 'Hide filter',
137
+ classes: 'govuk-button--secondary'
138
+ },
139
+ toggleButtonContainer: {
140
+ selector: '.moj-action-bar__filter'
141
+ },
142
+ closeButton: {
143
+ text: 'Close',
144
+ classes: 'moj-filter__close'
145
+ },
146
+ closeButtonContainer: {
147
+ selector: '.moj-filter__header-action'
148
+ }
149
+ });
150
+ /**
151
+ * Filter toggle button config schema
152
+ *
153
+ * @satisfies {Schema<FilterToggleButtonConfig>}
154
+ */
155
+ FilterToggleButton.schema = Object.freeze(/** @type {const} */{
156
+ properties: {
157
+ bigModeMediaQuery: {
158
+ type: 'string'
159
+ },
160
+ startHidden: {
161
+ type: 'boolean'
162
+ },
163
+ toggleButton: {
164
+ type: 'object'
165
+ },
166
+ toggleButtonContainer: {
167
+ type: 'object'
168
+ },
169
+ closeButton: {
170
+ type: 'object'
171
+ },
172
+ closeButtonContainer: {
173
+ type: 'object'
174
+ }
90
175
  }
91
- };
176
+ });
92
177
 
93
178
  export { FilterToggleButton };
94
179
  //# sourceMappingURL=filter-toggle-button.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"filter-toggle-button.mjs","sources":["../../../../src/moj/components/filter-toggle-button/filter-toggle-button.mjs"],"sourcesContent":["import $ from 'jquery'\n\nexport function FilterToggleButton(options) {\n this.options = options\n this.container = $(this.options.toggleButton.container)\n this.filterContainer = $(this.options.filter.container)\n\n this.createToggleButton()\n this.setupResponsiveChecks()\n this.filterContainer.attr('tabindex', '-1')\n if (this.options.startHidden) {\n this.hideMenu()\n }\n}\n\nFilterToggleButton.prototype.setupResponsiveChecks = function () {\n this.mq = window.matchMedia(this.options.bigModeMediaQuery)\n this.mq.addListener($.proxy(this, 'checkMode'))\n this.checkMode(this.mq)\n}\n\nFilterToggleButton.prototype.createToggleButton = function () {\n this.menuButton = $(\n `<button class=\"govuk-button ${this.options.toggleButton.classes}\" type=\"button\" aria-haspopup=\"true\" aria-expanded=\"false\">${this.options.toggleButton.showText}</button>`\n )\n this.menuButton.on('click', $.proxy(this, 'onMenuButtonClick'))\n this.container.append(this.menuButton)\n}\n\nFilterToggleButton.prototype.checkMode = function (mq) {\n if (mq.matches) {\n this.enableBigMode()\n } else {\n this.enableSmallMode()\n }\n}\n\nFilterToggleButton.prototype.enableBigMode = function () {\n this.showMenu()\n this.removeCloseButton()\n}\n\nFilterToggleButton.prototype.enableSmallMode = function () {\n this.hideMenu()\n this.addCloseButton()\n}\n\nFilterToggleButton.prototype.addCloseButton = function () {\n if (this.options.closeButton) {\n this.closeButton = $(\n `<button class=\"moj-filter__close\" type=\"button\">${this.options.closeButton.text}</button>`\n )\n this.closeButton.on('click', $.proxy(this, 'onCloseClick'))\n $(this.options.closeButton.container).append(this.closeButton)\n }\n}\n\nFilterToggleButton.prototype.onCloseClick = function () {\n this.hideMenu()\n this.menuButton.focus()\n}\n\nFilterToggleButton.prototype.removeCloseButton = function () {\n if (this.closeButton) {\n this.closeButton.remove()\n this.closeButton = null\n }\n}\n\nFilterToggleButton.prototype.hideMenu = function () {\n this.menuButton.attr('aria-expanded', 'false')\n this.filterContainer.addClass('moj-js-hidden')\n this.menuButton.text(this.options.toggleButton.showText)\n}\n\nFilterToggleButton.prototype.showMenu = function () {\n this.menuButton.attr('aria-expanded', 'true')\n this.filterContainer.removeClass('moj-js-hidden')\n this.menuButton.text(this.options.toggleButton.hideText)\n}\n\nFilterToggleButton.prototype.onMenuButtonClick = function () {\n this.toggle()\n}\n\nFilterToggleButton.prototype.toggle = function () {\n if (this.menuButton.attr('aria-expanded') === 'false') {\n this.showMenu()\n this.filterContainer.get(0).focus()\n } else {\n this.hideMenu()\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"filter-toggle-button.mjs","sources":["../../../../src/moj/components/filter-toggle-button/filter-toggle-button.mjs"],"sourcesContent":["import { ConfigurableComponent } from 'govuk-frontend'\n\n/**\n * @augments {ConfigurableComponent<FilterToggleButtonConfig>}\n */\nexport class FilterToggleButton extends ConfigurableComponent {\n /**\n * @param {Element | null} $root - HTML element to use for filter toggle button\n * @param {FilterToggleButtonConfig} [config] - Filter toggle button config\n */\n constructor($root, config = {}) {\n super($root, config)\n\n const $toggleButtonContainer =\n this.config.toggleButtonContainer.element ??\n document.querySelector(this.config.toggleButtonContainer.selector)\n\n const $closeButtonContainer =\n this.config.closeButtonContainer.element ??\n this.$root.querySelector(this.config.closeButtonContainer.selector)\n\n if (\n !(\n $toggleButtonContainer instanceof HTMLElement &&\n $closeButtonContainer instanceof HTMLElement\n )\n ) {\n return this\n }\n\n this.$toggleButtonContainer = $toggleButtonContainer\n this.$closeButtonContainer = $closeButtonContainer\n\n this.createToggleButton()\n this.setupResponsiveChecks()\n\n this.$root.setAttribute('tabindex', '-1')\n\n if (this.config.startHidden) {\n this.hideMenu()\n }\n }\n\n setupResponsiveChecks() {\n this.mq = window.matchMedia(this.config.bigModeMediaQuery)\n this.mq.addListener(this.checkMode.bind(this))\n this.checkMode()\n }\n\n createToggleButton() {\n this.$menuButton = document.createElement('button')\n this.$menuButton.setAttribute('type', 'button')\n this.$menuButton.setAttribute('aria-haspopup', 'true')\n this.$menuButton.setAttribute('aria-expanded', 'false')\n\n this.$menuButton.className = `govuk-button ${this.config.toggleButton.classes}`\n this.$menuButton.textContent = this.config.toggleButton.showText\n\n this.$menuButton.addEventListener(\n 'click',\n this.onMenuButtonClick.bind(this)\n )\n\n this.$toggleButtonContainer.append(this.$menuButton)\n }\n\n checkMode() {\n if (this.mq.matches) {\n this.enableBigMode()\n } else {\n this.enableSmallMode()\n }\n }\n\n enableBigMode() {\n this.showMenu()\n this.removeCloseButton()\n }\n\n enableSmallMode() {\n this.hideMenu()\n this.addCloseButton()\n }\n\n addCloseButton() {\n this.$closeButton = document.createElement('button')\n this.$closeButton.setAttribute('type', 'button')\n\n this.$closeButton.className = this.config.closeButton.classes\n this.$closeButton.textContent = this.config.closeButton.text\n\n this.$closeButton.addEventListener('click', this.onCloseClick.bind(this))\n this.$closeButtonContainer.append(this.$closeButton)\n }\n\n onCloseClick() {\n this.hideMenu()\n this.$menuButton.focus()\n }\n\n removeCloseButton() {\n if (this.$closeButton) {\n this.$closeButton.remove()\n this.$closeButton = null\n }\n }\n\n hideMenu() {\n this.$menuButton.setAttribute('aria-expanded', 'false')\n this.$root.classList.add('moj-js-hidden')\n this.$menuButton.textContent = this.config.toggleButton.showText\n }\n\n showMenu() {\n this.$menuButton.setAttribute('aria-expanded', 'true')\n this.$root.classList.remove('moj-js-hidden')\n this.$menuButton.textContent = this.config.toggleButton.hideText\n }\n\n onMenuButtonClick() {\n this.toggle()\n }\n\n toggle() {\n if (this.$menuButton.getAttribute('aria-expanded') === 'false') {\n this.showMenu()\n this.$root.focus()\n } else {\n this.hideMenu()\n }\n }\n\n /**\n * Name for the component used when initialising using data-module attributes.\n */\n static moduleName = 'moj-filter'\n\n /**\n * Filter toggle button config\n *\n * @type {FilterToggleButtonConfig}\n */\n static defaults = Object.freeze({\n bigModeMediaQuery: '(min-width: 48.0625em)',\n startHidden: true,\n toggleButton: {\n showText: 'Show filter',\n hideText: 'Hide filter',\n classes: 'govuk-button--secondary'\n },\n toggleButtonContainer: {\n selector: '.moj-action-bar__filter'\n },\n closeButton: {\n text: 'Close',\n classes: 'moj-filter__close'\n },\n closeButtonContainer: {\n selector: '.moj-filter__header-action'\n }\n })\n\n /**\n * Filter toggle button config schema\n *\n * @satisfies {Schema<FilterToggleButtonConfig>}\n */\n static schema = Object.freeze(\n /** @type {const} */ ({\n properties: {\n bigModeMediaQuery: { type: 'string' },\n startHidden: { type: 'boolean' },\n toggleButton: { type: 'object' },\n toggleButtonContainer: { type: 'object' },\n closeButton: { type: 'object' },\n closeButtonContainer: { type: 'object' }\n }\n })\n )\n}\n\n/**\n * @typedef {object} FilterToggleButtonConfig\n * @property {string} [bigModeMediaQuery] - Media query for big mode\n * @property {boolean} [startHidden] - Whether to start hidden\n * @property {object} [toggleButton] - Toggle button config\n * @property {string} [toggleButton.showText] - Text for show button\n * @property {string} [toggleButton.hideText] - Text for hide button\n * @property {string} [toggleButton.classes] - Classes for toggle button\n * @property {object} [toggleButtonContainer] - Toggle button container config\n * @property {string} [toggleButtonContainer.selector] - Selector for toggle button container\n * @property {Element | null} [toggleButtonContainer.element] - HTML element for toggle button container\n * @property {object} [closeButton] - Close button config\n * @property {string} [closeButton.text] - Text for close button\n * @property {string} [closeButton.classes] - Classes for close button\n * @property {object} [closeButtonContainer] - Close button container config\n * @property {string} [closeButtonContainer.selector] - Selector for close button container\n * @property {Element | null} [closeButtonContainer.element] - HTML element for close button container\n */\n\n/**\n * @import { Schema } from 'govuk-frontend/dist/govuk/common/configuration.mjs'\n */\n"],"names":["FilterToggleButton","ConfigurableComponent","constructor","$root","config","_this$config$toggleBu","_this$config$closeBut","$toggleButtonContainer","toggleButtonContainer","element","document","querySelector","selector","$closeButtonContainer","closeButtonContainer","HTMLElement","createToggleButton","setupResponsiveChecks","setAttribute","startHidden","hideMenu","mq","window","matchMedia","bigModeMediaQuery","addListener","checkMode","bind","$menuButton","createElement","className","toggleButton","classes","textContent","showText","addEventListener","onMenuButtonClick","append","matches","enableBigMode","enableSmallMode","showMenu","removeCloseButton","addCloseButton","$closeButton","closeButton","text","onCloseClick","focus","remove","classList","add","hideText","toggle","getAttribute","moduleName","defaults","Object","freeze","schema","properties","type"],"mappings":"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"}
@@ -0,0 +1,288 @@
1
+ (function (global, factory) {
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
+ * @param {Element} $element - Element to remove attribute value from
9
+ * @param {string} attr - Attribute name
10
+ * @param {string} value - Attribute value
11
+ */
12
+ function removeAttributeValue($element, attr, value) {
13
+ let re, m;
14
+ if ($element.getAttribute(attr)) {
15
+ if ($element.getAttribute(attr) === value) {
16
+ $element.removeAttribute(attr);
17
+ } else {
18
+ re = new RegExp(`(^|\\s)${value}(\\s|$)`);
19
+ m = $element.getAttribute(attr).match(re);
20
+ if (m && m.length === 3) {
21
+ $element.setAttribute(attr, $element.getAttribute(attr).replace(re, m[1] && m[2] ? ' ' : ''));
22
+ }
23
+ }
24
+ }
25
+ }
26
+
27
+ /**
28
+ * @param {Element} $element - Element to add attribute value to
29
+ * @param {string} attr - Attribute name
30
+ * @param {string} value - Attribute value
31
+ */
32
+ function addAttributeValue($element, attr, value) {
33
+ let re;
34
+ if (!$element.getAttribute(attr)) {
35
+ $element.setAttribute(attr, value);
36
+ } else {
37
+ re = new RegExp(`(^|\\s)${value}(\\s|$)`);
38
+ if (!re.test($element.getAttribute(attr))) {
39
+ $element.setAttribute(attr, `${$element.getAttribute(attr)} ${value}`);
40
+ }
41
+ }
42
+ }
43
+
44
+ /**
45
+ * @augments {ConfigurableComponent<FormValidatorConfig, HTMLFormElement>}
46
+ */
47
+ class FormValidator extends govukFrontend.ConfigurableComponent {
48
+ /**
49
+ * @param {Element | null} $root - HTML element to use for form validator
50
+ * @param {FormValidatorConfig} [config] - Form validator config
51
+ */
52
+ constructor($root, config = {}) {
53
+ super($root, config);
54
+ const $summary = this.config.summary.element || document.querySelector(this.config.summary.selector);
55
+ if (!$summary || !($summary instanceof HTMLElement)) {
56
+ return this;
57
+ }
58
+ this.$summary = $summary;
59
+ this.errors = /** @type {ValidationError[]} */[];
60
+ this.validators = /** @type {Validator[]} */[];
61
+ this.originalTitle = document.title;
62
+ this.$root.addEventListener('submit', this.onSubmit.bind(this));
63
+ }
64
+ escapeHtml(string = '') {
65
+ return String(string).replace(/[&<>"'`=/]/g, name => FormValidator.entityMap[name]);
66
+ }
67
+ resetTitle() {
68
+ document.title = this.originalTitle;
69
+ }
70
+ updateTitle() {
71
+ document.title = `${this.errors.length} errors - ${document.title}`;
72
+ }
73
+ showSummary() {
74
+ this.$summary.innerHTML = this.getSummaryHtml();
75
+ this.$summary.classList.remove('moj-hidden');
76
+ this.$summary.setAttribute('aria-labelledby', 'errorSummary-heading');
77
+ this.$summary.focus();
78
+ }
79
+ getSummaryHtml() {
80
+ let html = '<h2 id="error-summary-title" class="govuk-error-summary__title">There is a problem</h2>';
81
+ html += '<div class="govuk-error-summary__body">';
82
+ html += '<ul class="govuk-list govuk-error-summary__list">';
83
+ for (const error of this.errors) {
84
+ html += '<li>';
85
+ html += `<a href="#${this.escapeHtml(error.fieldName)}">`;
86
+ html += this.escapeHtml(error.message);
87
+ html += '</a>';
88
+ html += '</li>';
89
+ }
90
+ html += '</ul>';
91
+ html += '</div>';
92
+ return html;
93
+ }
94
+ hideSummary() {
95
+ this.$summary.classList.add('moj-hidden');
96
+ this.$summary.removeAttribute('aria-labelledby');
97
+ }
98
+
99
+ /**
100
+ * @param {SubmitEvent} event - Form submit event
101
+ */
102
+ onSubmit(event) {
103
+ this.removeInlineErrors();
104
+ this.hideSummary();
105
+ this.resetTitle();
106
+ if (!this.validate()) {
107
+ event.preventDefault();
108
+ this.updateTitle();
109
+ this.showSummary();
110
+ this.showInlineErrors();
111
+ }
112
+ }
113
+ showInlineErrors() {
114
+ for (const error of this.errors) {
115
+ this.showInlineError(error);
116
+ }
117
+ }
118
+
119
+ /**
120
+ * @param {ValidationError} error
121
+ */
122
+ showInlineError(error) {
123
+ const $errorSpan = document.createElement('span');
124
+ $errorSpan.id = `${error.fieldName}-error`;
125
+ $errorSpan.classList.add('govuk-error-message');
126
+ $errorSpan.innerHTML = this.escapeHtml(error.message);
127
+ const $control = document.querySelector(`#${error.fieldName}`);
128
+ const $fieldset = $control.closest('.govuk-fieldset');
129
+ const $fieldContainer = ($fieldset || $control).closest('.govuk-form-group');
130
+ const $label = $fieldContainer.querySelector('label');
131
+ const $legend = $fieldContainer.querySelector('legend');
132
+ $fieldContainer.classList.add('govuk-form-group--error');
133
+ if ($fieldset && $legend) {
134
+ $legend.after($errorSpan);
135
+ $fieldContainer.setAttribute('aria-invalid', 'true');
136
+ addAttributeValue($fieldset, 'aria-describedby', $errorSpan.id);
137
+ } else if ($label && $control) {
138
+ $label.after($errorSpan);
139
+ $control.setAttribute('aria-invalid', 'true');
140
+ addAttributeValue($control, 'aria-describedby', $errorSpan.id);
141
+ }
142
+ }
143
+ removeInlineErrors() {
144
+ for (const error of this.errors) {
145
+ this.removeInlineError(error);
146
+ }
147
+ }
148
+
149
+ /**
150
+ * @param {ValidationError} error
151
+ */
152
+ removeInlineError(error) {
153
+ const $errorSpan = document.querySelector(`#${error.fieldName}-error`);
154
+ const $control = document.querySelector(`#${error.fieldName}`);
155
+ const $fieldset = $control.closest('.govuk-fieldset');
156
+ const $fieldContainer = ($fieldset || $control).closest('.govuk-form-group');
157
+ const $label = $fieldContainer.querySelector('label');
158
+ const $legend = $fieldContainer.querySelector('legend');
159
+ $errorSpan.remove();
160
+ $fieldContainer.classList.remove('govuk-form-group--error');
161
+ if ($fieldset && $legend) {
162
+ $fieldContainer.removeAttribute('aria-invalid');
163
+ removeAttributeValue($fieldset, 'aria-describedby', $errorSpan.id);
164
+ } else if ($label && $control) {
165
+ $control.removeAttribute('aria-invalid');
166
+ removeAttributeValue($control, 'aria-describedby', $errorSpan.id);
167
+ }
168
+ }
169
+
170
+ /**
171
+ * @param {string} fieldName - Field name
172
+ * @param {ValidationRule[]} rules - Validation rules
173
+ */
174
+ addValidator(fieldName, rules) {
175
+ this.validators.push({
176
+ fieldName,
177
+ rules,
178
+ field: this.$root.elements.namedItem(fieldName)
179
+ });
180
+ }
181
+ validate() {
182
+ this.errors = [];
183
+
184
+ /** @type {Validator | null} */
185
+ let validator = null;
186
+
187
+ /** @type {boolean | string} */
188
+ let validatorReturnValue = true;
189
+ let i;
190
+ let j;
191
+ for (i = 0; i < this.validators.length; i++) {
192
+ validator = this.validators[i];
193
+ for (j = 0; j < validator.rules.length; j++) {
194
+ validatorReturnValue = validator.rules[j].method(validator.field, validator.rules[j].params);
195
+ if (typeof validatorReturnValue === 'boolean' && !validatorReturnValue) {
196
+ this.errors.push({
197
+ fieldName: validator.fieldName,
198
+ message: validator.rules[j].message
199
+ });
200
+ break;
201
+ } else if (typeof validatorReturnValue === 'string') {
202
+ this.errors.push({
203
+ fieldName: validatorReturnValue,
204
+ message: validator.rules[j].message
205
+ });
206
+ break;
207
+ }
208
+ }
209
+ }
210
+ return this.errors.length === 0;
211
+ }
212
+
213
+ /**
214
+ * @type {Record<string, string>}
215
+ */
216
+ }
217
+
218
+ /**
219
+ * @typedef {object} FormValidatorConfig
220
+ * @property {object} [summary] - Error summary config
221
+ * @property {string} [summary.selector] - Selector for error summary
222
+ * @property {Element | null} [summary.element] - HTML element for error summary
223
+ */
224
+
225
+ /**
226
+ * @typedef {object} ValidationRule
227
+ * @property {(field: Validator['field'], params: Record<string, Validator['field']>) => boolean | string} method - Validation method
228
+ * @property {string} message - Error message
229
+ * @property {Record<string, Validator['field']>} [params] - Parameters for validation
230
+ */
231
+
232
+ /**
233
+ * @typedef {object} ValidationError
234
+ * @property {string} fieldName - Name of the field
235
+ * @property {string} message - Validation error message
236
+ */
237
+
238
+ /**
239
+ * @typedef {object} Validator
240
+ * @property {string} fieldName - Name of the field
241
+ * @property {ValidationRule[]} rules - Validation rules
242
+ * @property {Element | RadioNodeList} field - Form field
243
+ */
244
+
245
+ /**
246
+ * @import { Schema } from 'govuk-frontend/dist/govuk/common/configuration.mjs'
247
+ */
248
+ FormValidator.entityMap = {
249
+ '&': '&amp;',
250
+ '<': '&lt;',
251
+ '>': '&gt;',
252
+ '"': '&quot;',
253
+ "'": '&#39;',
254
+ '/': '&#x2F;',
255
+ '`': '&#x60;',
256
+ '=': '&#x3D;'
257
+ };
258
+ /**
259
+ * Name for the component used when initialising using data-module attributes.
260
+ */
261
+ FormValidator.moduleName = 'moj-form-validator';
262
+ /**
263
+ * Multi file upload default config
264
+ *
265
+ * @type {FormValidatorConfig}
266
+ */
267
+ FormValidator.defaults = Object.freeze({
268
+ summary: {
269
+ selector: '.govuk-error-summary'
270
+ }
271
+ });
272
+ /**
273
+ * Multi file upload config schema
274
+ *
275
+ * @satisfies {Schema<FormValidatorConfig>}
276
+ */
277
+ FormValidator.schema = Object.freeze(/** @type {const} */{
278
+ properties: {
279
+ summary: {
280
+ type: 'object'
281
+ }
282
+ }
283
+ });
284
+
285
+ exports.FormValidator = FormValidator;
286
+
287
+ }));
288
+ //# sourceMappingURL=form-validator.bundle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form-validator.bundle.js","sources":["../../../../src/moj/helpers.mjs","../../../../src/moj/components/form-validator/form-validator.mjs"],"sourcesContent":["/**\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 { addAttributeValue, removeAttributeValue } from '../../helpers.mjs'\n\n/**\n * @augments {ConfigurableComponent<FormValidatorConfig, HTMLFormElement>}\n */\nexport class FormValidator extends ConfigurableComponent {\n /**\n * @param {Element | null} $root - HTML element to use for form validator\n * @param {FormValidatorConfig} [config] - Form validator config\n */\n constructor($root, config = {}) {\n super($root, config)\n\n const $summary =\n this.config.summary.element ||\n document.querySelector(this.config.summary.selector)\n\n if (!$summary || !($summary instanceof HTMLElement)) {\n return this\n }\n\n this.$summary = $summary\n\n this.errors = /** @type {ValidationError[]} */ ([])\n this.validators = /** @type {Validator[]} */ ([])\n this.originalTitle = document.title\n\n this.$root.addEventListener('submit', this.onSubmit.bind(this))\n }\n\n escapeHtml(string = '') {\n return String(string).replace(\n /[&<>\"'`=/]/g,\n (name) => FormValidator.entityMap[name]\n )\n }\n\n resetTitle() {\n document.title = this.originalTitle\n }\n\n updateTitle() {\n document.title = `${this.errors.length} errors - ${document.title}`\n }\n\n showSummary() {\n this.$summary.innerHTML = this.getSummaryHtml()\n this.$summary.classList.remove('moj-hidden')\n this.$summary.setAttribute('aria-labelledby', 'errorSummary-heading')\n this.$summary.focus()\n }\n\n getSummaryHtml() {\n let html =\n '<h2 id=\"error-summary-title\" class=\"govuk-error-summary__title\">There is a problem</h2>'\n html += '<div class=\"govuk-error-summary__body\">'\n html += '<ul class=\"govuk-list govuk-error-summary__list\">'\n for (const error of this.errors) {\n html += '<li>'\n html += `<a href=\"#${this.escapeHtml(error.fieldName)}\">`\n html += this.escapeHtml(error.message)\n html += '</a>'\n html += '</li>'\n }\n html += '</ul>'\n html += '</div>'\n return html\n }\n\n hideSummary() {\n this.$summary.classList.add('moj-hidden')\n this.$summary.removeAttribute('aria-labelledby')\n }\n\n /**\n * @param {SubmitEvent} event - Form submit event\n */\n onSubmit(event) {\n this.removeInlineErrors()\n this.hideSummary()\n this.resetTitle()\n if (!this.validate()) {\n event.preventDefault()\n this.updateTitle()\n this.showSummary()\n this.showInlineErrors()\n }\n }\n\n showInlineErrors() {\n for (const error of this.errors) {\n this.showInlineError(error)\n }\n }\n\n /**\n * @param {ValidationError} error\n */\n showInlineError(error) {\n const $errorSpan = document.createElement('span')\n $errorSpan.id = `${error.fieldName}-error`\n $errorSpan.classList.add('govuk-error-message')\n $errorSpan.innerHTML = this.escapeHtml(error.message)\n\n const $control = document.querySelector(`#${error.fieldName}`)\n const $fieldset = $control.closest('.govuk-fieldset')\n const $fieldContainer = ($fieldset || $control).closest('.govuk-form-group')\n\n const $label = $fieldContainer.querySelector('label')\n const $legend = $fieldContainer.querySelector('legend')\n\n $fieldContainer.classList.add('govuk-form-group--error')\n\n if ($fieldset && $legend) {\n $legend.after($errorSpan)\n $fieldContainer.setAttribute('aria-invalid', 'true')\n addAttributeValue($fieldset, 'aria-describedby', $errorSpan.id)\n } else if ($label && $control) {\n $label.after($errorSpan)\n $control.setAttribute('aria-invalid', 'true')\n addAttributeValue($control, 'aria-describedby', $errorSpan.id)\n }\n }\n\n removeInlineErrors() {\n for (const error of this.errors) {\n this.removeInlineError(error)\n }\n }\n\n /**\n * @param {ValidationError} error\n */\n removeInlineError(error) {\n const $errorSpan = document.querySelector(`#${error.fieldName}-error`)\n\n const $control = document.querySelector(`#${error.fieldName}`)\n const $fieldset = $control.closest('.govuk-fieldset')\n const $fieldContainer = ($fieldset || $control).closest('.govuk-form-group')\n\n const $label = $fieldContainer.querySelector('label')\n const $legend = $fieldContainer.querySelector('legend')\n\n $errorSpan.remove()\n $fieldContainer.classList.remove('govuk-form-group--error')\n\n if ($fieldset && $legend) {\n $fieldContainer.removeAttribute('aria-invalid')\n removeAttributeValue($fieldset, 'aria-describedby', $errorSpan.id)\n } else if ($label && $control) {\n $control.removeAttribute('aria-invalid')\n removeAttributeValue($control, 'aria-describedby', $errorSpan.id)\n }\n }\n\n /**\n * @param {string} fieldName - Field name\n * @param {ValidationRule[]} rules - Validation rules\n */\n addValidator(fieldName, rules) {\n this.validators.push({\n fieldName,\n rules,\n field: this.$root.elements.namedItem(fieldName)\n })\n }\n\n validate() {\n this.errors = []\n\n /** @type {Validator | null} */\n let validator = null\n\n /** @type {boolean | string} */\n let validatorReturnValue = true\n\n let i\n let j\n\n for (i = 0; i < this.validators.length; i++) {\n validator = this.validators[i]\n for (j = 0; j < validator.rules.length; j++) {\n validatorReturnValue = validator.rules[j].method(\n validator.field,\n validator.rules[j].params\n )\n\n if (\n typeof validatorReturnValue === 'boolean' &&\n !validatorReturnValue\n ) {\n this.errors.push({\n fieldName: validator.fieldName,\n message: validator.rules[j].message\n })\n break\n } else if (typeof validatorReturnValue === 'string') {\n this.errors.push({\n fieldName: validatorReturnValue,\n message: validator.rules[j].message\n })\n break\n }\n }\n }\n return this.errors.length === 0\n }\n\n /**\n * @type {Record<string, string>}\n */\n static entityMap = {\n '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#39;',\n '/': '&#x2F;',\n '`': '&#x60;',\n '=': '&#x3D;'\n }\n\n /**\n * Name for the component used when initialising using data-module attributes.\n */\n static moduleName = 'moj-form-validator'\n\n /**\n * Multi file upload default config\n *\n * @type {FormValidatorConfig}\n */\n static defaults = Object.freeze({\n summary: {\n selector: '.govuk-error-summary'\n }\n })\n\n /**\n * Multi file upload config schema\n *\n * @satisfies {Schema<FormValidatorConfig>}\n */\n static schema = Object.freeze(\n /** @type {const} */ ({\n properties: {\n summary: { type: 'object' }\n }\n })\n )\n}\n\n/**\n * @typedef {object} FormValidatorConfig\n * @property {object} [summary] - Error summary config\n * @property {string} [summary.selector] - Selector for error summary\n * @property {Element | null} [summary.element] - HTML element for error summary\n */\n\n/**\n * @typedef {object} ValidationRule\n * @property {(field: Validator['field'], params: Record<string, Validator['field']>) => boolean | string} method - Validation method\n * @property {string} message - Error message\n * @property {Record<string, Validator['field']>} [params] - Parameters for validation\n */\n\n/**\n * @typedef {object} ValidationError\n * @property {string} fieldName - Name of the field\n * @property {string} message - Validation error message\n */\n\n/**\n * @typedef {object} Validator\n * @property {string} fieldName - Name of the field\n * @property {ValidationRule[]} rules - Validation rules\n * @property {Element | RadioNodeList} field - Form field\n */\n\n/**\n * @import { Schema } from 'govuk-frontend/dist/govuk/common/configuration.mjs'\n */\n"],"names":["removeAttributeValue","$element","attr","value","re","m","getAttribute","removeAttribute","RegExp","match","length","setAttribute","replace","addAttributeValue","test","FormValidator","ConfigurableComponent","constructor","$root","config","$summary","summary","element","document","querySelector","selector","HTMLElement","errors","validators","originalTitle","title","addEventListener","onSubmit","bind","escapeHtml","string","String","name","entityMap","resetTitle","updateTitle","showSummary","innerHTML","getSummaryHtml","classList","remove","focus","html","error","fieldName","message","hideSummary","add","event","removeInlineErrors","validate","preventDefault","showInlineErrors","showInlineError","$errorSpan","createElement","id","$control","$fieldset","closest","$fieldContainer","$label","$legend","after","removeInlineError","addValidator","rules","push","field","elements","namedItem","validator","validatorReturnValue","i","j","method","params","moduleName","defaults","Object","freeze","schema","properties","type"],"mappings":"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"}