@ministryofjustice/frontend 3.3.1 → 3.5.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 (83) hide show
  1. package/README.md +4 -10
  2. package/govuk-prototype-kit.config.json +5 -16
  3. package/moj/all.jquery.min.js +15 -4
  4. package/moj/all.js +2856 -2280
  5. package/moj/all.scss +2 -0
  6. package/moj/components/_all.scss +1 -0
  7. package/moj/components/action-bar/_action-bar.scss +4 -6
  8. package/moj/components/add-another/_add-another.scss +9 -7
  9. package/moj/components/add-another/add-another.js +128 -76
  10. package/moj/components/alert/README.md +0 -0
  11. package/moj/components/alert/_alert.scss +142 -0
  12. package/moj/components/alert/alert.js +482 -0
  13. package/moj/components/alert/alert.spec.helper.js +92 -0
  14. package/moj/components/alert/macro.njk +3 -0
  15. package/moj/components/alert/template.njk +83 -0
  16. package/moj/components/badge/_badge.scss +3 -4
  17. package/moj/components/banner/_banner.scss +5 -10
  18. package/moj/components/button-menu/_button-menu.scss +10 -9
  19. package/moj/components/button-menu/button-menu.js +348 -318
  20. package/moj/components/cookie-banner/_cookie-banner.scss +6 -5
  21. package/moj/components/currency-input/_currency-input.scss +4 -4
  22. package/moj/components/date-picker/README.md +14 -17
  23. package/moj/components/date-picker/_date-picker.scss +122 -106
  24. package/moj/components/date-picker/date-picker.js +927 -900
  25. package/moj/components/filter/README.md +1 -1
  26. package/moj/components/filter/_filter.scss +53 -75
  27. package/moj/components/filter-toggle-button/filter-toggle-button.js +122 -87
  28. package/moj/components/form-validator/form-validator.js +399 -156
  29. package/moj/components/header/_header.scss +17 -19
  30. package/moj/components/identity-bar/_identity-bar.scss +5 -5
  31. package/moj/components/interruption-card/_interruption-card.scss +2 -2
  32. package/moj/components/messages/_messages.scss +12 -19
  33. package/moj/components/multi-file-upload/README.md +1 -1
  34. package/moj/components/multi-file-upload/_multi-file-upload.scss +34 -30
  35. package/moj/components/multi-file-upload/multi-file-upload.js +454 -183
  36. package/moj/components/multi-select/_multi-select.scss +4 -3
  37. package/moj/components/multi-select/multi-select.js +106 -70
  38. package/moj/components/notification-badge/_notification-badge.scss +12 -12
  39. package/moj/components/organisation-switcher/_organisation-switcher.scss +1 -1
  40. package/moj/components/page-header-actions/_page-header-actions.scss +3 -2
  41. package/moj/components/pagination/_pagination.scss +26 -31
  42. package/moj/components/password-reveal/_password-reveal.scss +1 -2
  43. package/moj/components/password-reveal/password-reveal.js +63 -31
  44. package/moj/components/primary-navigation/_primary-navigation.scss +26 -29
  45. package/moj/components/progress-bar/_progress-bar.scss +21 -26
  46. package/moj/components/rich-text-editor/_rich-text-editor.scss +17 -16
  47. package/moj/components/rich-text-editor/rich-text-editor.js +186 -139
  48. package/moj/components/search/_search.scss +6 -4
  49. package/moj/components/search-toggle/search-toggle.js +83 -53
  50. package/moj/components/search-toggle/search-toggle.scss +21 -15
  51. package/moj/components/side-navigation/_side-navigation.scss +12 -21
  52. package/moj/components/sortable-table/_sortable-table.scss +25 -23
  53. package/moj/components/sortable-table/sortable-table.js +162 -119
  54. package/moj/components/sub-navigation/_sub-navigation.scss +24 -28
  55. package/moj/components/tag/_tag.scss +8 -9
  56. package/moj/components/task-list/_task-list.scss +8 -7
  57. package/moj/components/ticket-panel/_ticket-panel.scss +14 -6
  58. package/moj/components/timeline/_timeline.scss +18 -20
  59. package/moj/filters/all.js +28 -30
  60. package/moj/filters/prototype-kit-13-filters.js +2 -1
  61. package/moj/helpers/_all.scss +1 -0
  62. package/moj/helpers/_hidden.scss +1 -1
  63. package/moj/helpers/_links.scss +20 -0
  64. package/moj/helpers.js +218 -51
  65. package/moj/init.js +2 -2
  66. package/moj/moj-frontend.min.css +2 -2
  67. package/moj/moj-frontend.min.js +15 -4
  68. package/moj/objects/_filter-layout.scss +11 -10
  69. package/moj/objects/_scrollable-pane.scss +11 -14
  70. package/moj/settings/_colours.scss +5 -0
  71. package/moj/settings/_measurements.scss +0 -2
  72. package/moj/utilities/_hidden.scss +3 -3
  73. package/moj/utilities/_width-container.scss +1 -1
  74. package/moj/version.js +28 -1
  75. package/package.json +1 -1
  76. package/moj/all.spec.js +0 -22
  77. package/moj/components/button-menu/button-menu.spec.js +0 -361
  78. package/moj/components/date-picker/date-picker.spec.js +0 -1130
  79. package/moj/components/filter-toggle-button/filter-toggle-button.spec.js +0 -304
  80. package/moj/components/multi-select/multi-select.spec.js +0 -135
  81. package/moj/components/password-reveal/password-reveal.spec.js +0 -55
  82. package/moj/components/search-toggle/search-toggle.spec.js +0 -134
  83. package/moj/namespace.js +0 -1
@@ -0,0 +1,482 @@
1
+ (function (global, factory) {
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3
+ typeof define === 'function' && define.amd ? define(factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.MOJFrontend = factory());
5
+ })(this, (function () { 'use strict';
6
+
7
+ function getDefaultExportFromCjs (x) {
8
+ return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
9
+ }
10
+
11
+ var helpers;
12
+ var hasRequiredHelpers;
13
+
14
+ function requireHelpers () {
15
+ if (hasRequiredHelpers) return helpers;
16
+ hasRequiredHelpers = 1;
17
+ function removeAttributeValue(el, attr, value) {
18
+ let re, m;
19
+ if (el.getAttribute(attr)) {
20
+ if (el.getAttribute(attr) === value) {
21
+ el.removeAttribute(attr);
22
+ } else {
23
+ re = new RegExp(`(^|\\s)${value}(\\s|$)`);
24
+ m = el.getAttribute(attr).match(re);
25
+ if (m && m.length === 3) {
26
+ el.setAttribute(
27
+ attr,
28
+ el.getAttribute(attr).replace(re, m[1] && m[2] ? ' ' : '')
29
+ );
30
+ }
31
+ }
32
+ }
33
+ }
34
+
35
+ function addAttributeValue(el, attr, value) {
36
+ let re;
37
+ if (!el.getAttribute(attr)) {
38
+ el.setAttribute(attr, value);
39
+ } else {
40
+ re = new RegExp(`(^|\\s)${value}(\\s|$)`);
41
+ if (!re.test(el.getAttribute(attr))) {
42
+ el.setAttribute(attr, `${el.getAttribute(attr)} ${value}`);
43
+ }
44
+ }
45
+ }
46
+
47
+ function dragAndDropSupported() {
48
+ const div = document.createElement('div');
49
+ return typeof div.ondrop !== 'undefined'
50
+ }
51
+
52
+ function formDataSupported() {
53
+ return typeof FormData === 'function'
54
+ }
55
+
56
+ function fileApiSupported() {
57
+ const input = document.createElement('input');
58
+ input.type = 'file';
59
+ return typeof input.files !== 'undefined'
60
+ }
61
+
62
+ function nodeListForEach(nodes, callback) {
63
+ if (window.NodeList.prototype.forEach) {
64
+ return nodes.forEach(callback)
65
+ }
66
+ for (let i = 0; i < nodes.length; i++) {
67
+ callback.call(window, nodes[i], i, nodes);
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Find an elements next sibling
73
+ *
74
+ * Utility function to find an elements next sibling matching the provided
75
+ * selector.
76
+ *
77
+ * @param {HTMLElement} $element - Element to find siblings for
78
+ * @param {string} selector - selector for required sibling
79
+ */
80
+ function getNextSibling($element, selector) {
81
+ if (!$element) return
82
+ // Get the next sibling element
83
+ let $sibling = $element.nextElementSibling;
84
+
85
+ // If there's no selector, return the first sibling
86
+ if (!selector) return $sibling
87
+
88
+ // If the sibling matches our selector, use it
89
+ // If not, jump to the next sibling and continue the loop
90
+ while ($sibling) {
91
+ if ($sibling.matches(selector)) return $sibling
92
+ $sibling = $sibling.nextElementSibling;
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Find an elements preceding sibling
98
+ *
99
+ * Utility function to find an elements previous sibling matching the provided
100
+ * selector.
101
+ *
102
+ * @param {HTMLElement} $element - Element to find siblings for
103
+ * @param {string} selector - selector for required sibling
104
+ */
105
+ function getPreviousSibling($element, selector) {
106
+ if (!$element) return
107
+ // Get the previous sibling element
108
+ let $sibling = $element.previousElementSibling;
109
+
110
+ // If there's no selector, return the first sibling
111
+ if (!selector) return $sibling
112
+
113
+ // If the sibling matches our selector, use it
114
+ // If not, jump to the next sibling and continue the loop
115
+ while ($sibling) {
116
+ if ($sibling.matches(selector)) return $sibling
117
+ $sibling = $sibling.previousElementSibling;
118
+ }
119
+ }
120
+
121
+ function findNearestMatchingElement($element, selector) {
122
+ // If no element or selector is provided, return null
123
+ if (!$element) return
124
+ if (!selector) return
125
+
126
+ // Start with the current element
127
+ let $currentElement = $element;
128
+
129
+ while ($currentElement) {
130
+ // First check the current element
131
+ if ($currentElement.matches(selector)) {
132
+ return $currentElement
133
+ }
134
+
135
+ // Check all previous siblings
136
+ let $sibling = $currentElement.previousElementSibling;
137
+ while ($sibling) {
138
+ // Check if the sibling itself is a heading
139
+ if ($sibling.matches(selector)) {
140
+ return $sibling
141
+ }
142
+ $sibling = $sibling.previousElementSibling;
143
+ }
144
+
145
+ // If no match found in siblings, move up to parent
146
+ $currentElement = $currentElement.parentElement;
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Move focus to element
152
+ *
153
+ * Sets tabindex to -1 to make the element programmatically focusable,
154
+ * but removes it on blur as the element doesn't need to be focused again.
155
+ *
156
+ * @param {HTMLElement} $element - HTML element
157
+ * @param {object} [options] - Handler options
158
+ * @param {function(this: HTMLElement): void} [options.onBeforeFocus] - Callback before focus
159
+ * @param {function(this: HTMLElement): void} [options.onBlur] - Callback on blur
160
+ */
161
+ function setFocus($element, options = {}) {
162
+ const isFocusable = $element.getAttribute('tabindex');
163
+
164
+ if (!isFocusable) {
165
+ $element.setAttribute('tabindex', '-1');
166
+ }
167
+
168
+ /**
169
+ * Handle element focus
170
+ */
171
+ function onFocus() {
172
+ $element.addEventListener('blur', onBlur, { once: true });
173
+ }
174
+
175
+ /**
176
+ * Handle element blur
177
+ */
178
+ function onBlur() {
179
+ if (options.onBlur) {
180
+ options.onBlur.call($element);
181
+ }
182
+
183
+ if (!isFocusable) {
184
+ $element.removeAttribute('tabindex');
185
+ }
186
+ }
187
+
188
+ // Add listener to reset element on blur, after focus
189
+ $element.addEventListener('focus', onFocus, { once: true });
190
+
191
+ // Focus element
192
+ if (options.onBeforeFocus) {
193
+ options.onBeforeFocus.call($element);
194
+ }
195
+ $element.focus();
196
+ }
197
+
198
+ helpers = {
199
+ removeAttributeValue,
200
+ addAttributeValue,
201
+ dragAndDropSupported,
202
+ formDataSupported,
203
+ fileApiSupported,
204
+ nodeListForEach,
205
+ getNextSibling,
206
+ getPreviousSibling,
207
+ findNearestMatchingElement,
208
+ setFocus
209
+ };
210
+ return helpers;
211
+ }
212
+
213
+ var alert$1;
214
+ var hasRequiredAlert;
215
+
216
+ function requireAlert () {
217
+ if (hasRequiredAlert) return alert$1;
218
+ hasRequiredAlert = 1;
219
+ const {
220
+ findNearestMatchingElement,
221
+ getPreviousSibling,
222
+ setFocus
223
+ } = requireHelpers();
224
+
225
+ /**
226
+ * @typedef {object} AlertConfig
227
+ * @property {boolean} [dismissible=false] - Can the alert be dismissed by the user
228
+ * @property {string} [dismissText=Dismiss] - the label text for the dismiss button
229
+ * @property {boolean} [disableAutoFocus=false] - whether the alert will be autofocused
230
+ * @property {string} [focusOnDismissSelector] - CSS Selector for element to be focused on dismiss
231
+ */
232
+
233
+ /**
234
+ * @param {HTMLElement} $module - the Alert element
235
+ * @param {AlertConfig} config - configuration options
236
+ * @class
237
+ */
238
+ function Alert($module, config = {}) {
239
+ if (!$module) {
240
+ return this
241
+ }
242
+
243
+ const schema = Object.freeze({
244
+ properties: {
245
+ dismissible: { type: 'boolean' },
246
+ dismissText: { type: 'string' },
247
+ disableAutoFocus: { type: 'boolean' },
248
+ focusOnDismissSelector: { type: 'string' }
249
+ }
250
+ });
251
+
252
+ const defaults = {
253
+ dismissible: false,
254
+ dismissText: 'Dismiss',
255
+ disableAutoFocus: false
256
+ };
257
+
258
+ // data attributes override JS config, which overrides defaults
259
+ this.config = this.mergeConfigs(
260
+ defaults,
261
+ config,
262
+ this.parseDataset(schema, $module.dataset)
263
+ );
264
+
265
+ this.$module = $module;
266
+ }
267
+
268
+ Alert.prototype.init = function () {
269
+ /**
270
+ * Focus the alert
271
+ *
272
+ * If `role="alert"` is set, focus the element to help some assistive
273
+ * technologies prioritise announcing it.
274
+ *
275
+ * You can turn off the auto-focus functionality by setting
276
+ * `data-disable-auto-focus="true"` in the component HTML. You might wish to
277
+ * do this based on user research findings, or to avoid a clash with another
278
+ * element which should be focused when the page loads.
279
+ */
280
+ if (
281
+ this.$module.getAttribute('role') === 'alert' &&
282
+ !this.config.disableAutoFocus
283
+ ) {
284
+ setFocus(this.$module);
285
+ }
286
+
287
+ this.$dismissButton = this.$module.querySelector('.moj-alert__dismiss');
288
+
289
+ if (this.config.dismissible && this.$dismissButton) {
290
+ this.$dismissButton.innerHTML = this.config.dismissText;
291
+ this.$dismissButton.removeAttribute('hidden');
292
+
293
+ this.$module.addEventListener('click', (event) => {
294
+ if (this.$dismissButton.contains(event.target)) {
295
+ this.dimiss();
296
+ }
297
+ });
298
+ }
299
+ };
300
+
301
+ /**
302
+ * Handle dismissing the alert
303
+ */
304
+ Alert.prototype.dimiss = function () {
305
+ let $elementToRecieveFocus;
306
+
307
+ // If a selector has been provided, attempt to find that element
308
+ if (this.config.focusOnDismissSelector) {
309
+ $elementToRecieveFocus = document.querySelector(
310
+ this.config.focusOnDismissSelector
311
+ );
312
+ }
313
+
314
+ // Is the next sibling another alert
315
+ if (!$elementToRecieveFocus) {
316
+ const $nextSibling = this.$module.nextElementSibling;
317
+ if ($nextSibling && $nextSibling.matches('.moj-alert')) {
318
+ $elementToRecieveFocus = $nextSibling;
319
+ }
320
+ }
321
+
322
+ // Else try to find any preceding sibling alert or heading
323
+ if (!$elementToRecieveFocus) {
324
+ $elementToRecieveFocus = getPreviousSibling(
325
+ this.$module,
326
+ '.moj-alert, h1, h2, h3, h4, h5, h6'
327
+ );
328
+ }
329
+
330
+ // Else find the closest ancestor heading, or fallback to main, or last resort
331
+ // use the body element
332
+ if (!$elementToRecieveFocus) {
333
+ $elementToRecieveFocus = findNearestMatchingElement(
334
+ this.$module,
335
+ 'h1, h2, h3, h4, h5, h6, main, body'
336
+ );
337
+ }
338
+
339
+ // If we have an element, place focus on it
340
+ if ($elementToRecieveFocus) {
341
+ setFocus($elementToRecieveFocus);
342
+ }
343
+
344
+ // Remove the alert
345
+ this.$module.remove();
346
+ };
347
+
348
+ /**
349
+ * Normalise string
350
+ *
351
+ * 'If it looks like a duck, and it quacks like a duck…' 🦆
352
+ *
353
+ * If the passed value looks like a boolean or a number, convert it to a boolean
354
+ * or number.
355
+ *
356
+ * Designed to be used to convert config passed via data attributes (which are
357
+ * always strings) into something sensible.
358
+ *
359
+ * @internal
360
+ * @param {DOMStringMap[string]} value - The value to normalise
361
+ * @param {SchemaProperty} [property] - Component schema property
362
+ * @returns {string | boolean | number | undefined} Normalised data
363
+ */
364
+ Alert.prototype.normaliseString = function (value, property) {
365
+ const trimmedValue = value ? value.trim() : '';
366
+
367
+ let output;
368
+ let outputType;
369
+ if (property && property.type) {
370
+ outputType = property.type;
371
+ }
372
+
373
+ // No schema type set? Determine automatically
374
+ if (!outputType) {
375
+ if (['true', 'false'].includes(trimmedValue)) {
376
+ outputType = 'boolean';
377
+ }
378
+
379
+ // Empty / whitespace-only strings are considered finite so we need to check
380
+ // the length of the trimmed string as well
381
+ if (trimmedValue.length > 0 && isFinite(Number(trimmedValue))) {
382
+ outputType = 'number';
383
+ }
384
+ }
385
+
386
+ switch (outputType) {
387
+ case 'boolean':
388
+ output = trimmedValue === 'true';
389
+ break
390
+
391
+ case 'number':
392
+ output = Number(trimmedValue);
393
+ break
394
+
395
+ default:
396
+ output = value;
397
+ }
398
+
399
+ return output
400
+ };
401
+
402
+ /**
403
+ * Parse dataset
404
+ *
405
+ * Loop over an object and normalise each value using {@link normaliseString},
406
+ * optionally expanding nested `i18n.field`
407
+ *
408
+ * @param {Schema} schema - component schema
409
+ * @param {DOMStringMap} dataset - HTML element dataset
410
+ * @returns {object} Normalised dataset
411
+ */
412
+ Alert.prototype.parseDataset = function (schema, dataset) {
413
+ const parsed = {};
414
+
415
+ for (const [field, property] of Object.entries(schema.properties)) {
416
+ if (field in dataset) {
417
+ if (dataset[field]) {
418
+ parsed[field] = this.normaliseString(dataset[field], property);
419
+ }
420
+ }
421
+ }
422
+
423
+ return parsed
424
+ };
425
+
426
+ /**
427
+ * Config merging function
428
+ *
429
+ * Takes any number of objects and combines them together, with
430
+ * greatest priority on the LAST item passed in.
431
+ *
432
+ * @param {...{ [key: string]: unknown }} configObjects - Config objects to merge
433
+ * @returns {{ [key: string]: unknown }} A merged config object
434
+ */
435
+ Alert.prototype.mergeConfigs = function (...configObjects) {
436
+ const formattedConfigObject = {};
437
+
438
+ // Loop through each of the passed objects
439
+ for (const configObject of configObjects) {
440
+ for (const key of Object.keys(configObject)) {
441
+ const option = formattedConfigObject[key];
442
+ const override = configObject[key];
443
+
444
+ // Push their keys one-by-one into formattedConfigObject. Any duplicate
445
+ // keys with object values will be merged, otherwise the new value will
446
+ // override the existing value.
447
+ if (typeof option === 'object' && typeof override === 'object') {
448
+ // @ts-expect-error Index signature for type 'string' is missing
449
+ formattedConfigObject[key] = this.mergeConfigs(option, override);
450
+ } else {
451
+ formattedConfigObject[key] = override;
452
+ }
453
+ }
454
+ }
455
+
456
+ return formattedConfigObject
457
+ };
458
+
459
+ alert$1 = { Alert };
460
+
461
+ /**
462
+ * Schema for component config
463
+ *
464
+ * @typedef {object} Schema
465
+ * @property {{ [field: string]: SchemaProperty | undefined }} properties - Schema properties
466
+ */
467
+
468
+ /**
469
+ * Schema property for component config
470
+ *
471
+ * @typedef {object} SchemaProperty
472
+ * @property {'string' | 'boolean' | 'number' | 'object'} type - Property type
473
+ */
474
+ return alert$1;
475
+ }
476
+
477
+ var alertExports = requireAlert();
478
+ var alert = /*@__PURE__*/getDefaultExportFromCjs(alertExports);
479
+
480
+ return alert;
481
+
482
+ }));
@@ -0,0 +1,92 @@
1
+ (function (global, factory) {
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3
+ typeof define === 'function' && define.amd ? define(factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.MOJFrontend = factory());
5
+ })(this, (function () { 'use strict';
6
+
7
+ function getDefaultExportFromCjs (x) {
8
+ return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
9
+ }
10
+
11
+ var alert_spec_helper$1;
12
+ var hasRequiredAlert_spec_helper;
13
+
14
+ function requireAlert_spec_helper () {
15
+ if (hasRequiredAlert_spec_helper) return alert_spec_helper$1;
16
+ hasRequiredAlert_spec_helper = 1;
17
+ const pageTemplate = `
18
+ <main>
19
+ <div id="alert-1" role="region" class="moj-alert moj-alert--information moj-alert--with-heading" aria-label="information: This contains information" data-module="moj-alert" data-dismissible="true">
20
+ <div>
21
+ <svg class="moj-alert__icon" role="presentation" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30" height="30" width="30"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.2165 3.45151C11.733 2.82332 13.3585 2.5 15 2.5C16.6415 2.5 18.267 2.82332 19.7835 3.45151C21.3001 4.07969 22.6781 5.00043 23.8388 6.16117C24.9996 7.3219 25.9203 8.69989 26.5485 10.2165C27.1767 11.733 27.5 13.3585 27.5 15C27.5 18.3152 26.183 21.4946 23.8388 23.8388C21.4946 26.183 18.3152 27.5 15 27.5C13.3585 27.5 11.733 27.1767 10.2165 26.5485C8.69989 25.9203 7.3219 24.9996 6.16117 23.8388C3.81696 21.4946 2.5 18.3152 2.5 15C2.5 11.6848 3.81696 8.50537 6.16117 6.16117C7.3219 5.00043 8.69989 4.07969 10.2165 3.45151ZM16.3574 22.4121H13.6621V12.95H16.3574V22.4121ZM13.3789 9.20898C13.3789 8.98763 13.4212 8.7793 13.5059 8.58398C13.5905 8.38216 13.7044 8.20964 13.8477 8.06641C13.9974 7.91667 14.1699 7.79948 14.3652 7.71484C14.5605 7.63021 14.7721 7.58789 15 7.58789C15.2214 7.58789 15.4297 7.63021 15.625 7.71484C15.8268 7.79948 15.9993 7.91667 16.1426 8.06641C16.2923 8.20964 16.4095 8.38216 16.4941 8.58398C16.5788 8.7793 16.6211 8.98763 16.6211 9.20898C16.6211 9.43685 16.5788 9.64844 16.4941 9.84375C16.4095 10.0391 16.2923 10.2116 16.1426 10.3613C15.9993 10.5046 15.8268 10.6185 15.625 10.7031C15.4297 10.7878 15.2214 10.8301 15 10.8301C14.7721 10.8301 14.5605 10.7878 14.3652 10.7031C14.1699 10.6185 13.9974 10.5046 13.8477 10.3613C13.7044 10.2116 13.5905 10.0391 13.5059 9.84375C13.4212 9.64844 13.3789 9.43685 13.3789 9.20898Z" fill="currentColor"/></svg>
22
+ </div>
23
+ <div class="moj-alert__content">
24
+ <h2 class="govuk-heading-m">This contains information</h2>
25
+ Content that informs you of a thing. It really is so very informative, that&#39;s why it needs so much content and is really, exceedingly verbose.
26
+ </div>
27
+ <div class="moj-alert__action">
28
+ <button class="moj-alert__dismiss" hidden>Dismiss</button>
29
+ </div>
30
+ </div>
31
+
32
+ <h1 id="h1">Heading 1</h1>
33
+ <section>
34
+ <h2 id="h2">heading 2</h2>
35
+ <div id="alert-2" role="region" class="moj-alert moj-alert--information" aria-label="information: You might like to know" data-module="moj-alert" data-dismissible="true">
36
+ <div>
37
+ <svg class="moj-alert__icon" role="presentation" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30" height="30" width="30"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.2165 3.45151C11.733 2.82332 13.3585 2.5 15 2.5C16.6415 2.5 18.267 2.82332 19.7835 3.45151C21.3001 4.07969 22.6781 5.00043 23.8388 6.16117C24.9996 7.3219 25.9203 8.69989 26.5485 10.2165C27.1767 11.733 27.5 13.3585 27.5 15C27.5 18.3152 26.183 21.4946 23.8388 23.8388C21.4946 26.183 18.3152 27.5 15 27.5C13.3585 27.5 11.733 27.1767 10.2165 26.5485C8.69989 25.9203 7.3219 24.9996 6.16117 23.8388C3.81696 21.4946 2.5 18.3152 2.5 15C2.5 11.6848 3.81696 8.50537 6.16117 6.16117C7.3219 5.00043 8.69989 4.07969 10.2165 3.45151ZM16.3574 22.4121H13.6621V12.95H16.3574V22.4121ZM13.3789 9.20898C13.3789 8.98763 13.4212 8.7793 13.5059 8.58398C13.5905 8.38216 13.7044 8.20964 13.8477 8.06641C13.9974 7.91667 14.1699 7.79948 14.3652 7.71484C14.5605 7.63021 14.7721 7.58789 15 7.58789C15.2214 7.58789 15.4297 7.63021 15.625 7.71484C15.8268 7.79948 15.9993 7.91667 16.1426 8.06641C16.2923 8.20964 16.4095 8.38216 16.4941 8.58398C16.5788 8.7793 16.6211 8.98763 16.6211 9.20898C16.6211 9.43685 16.5788 9.64844 16.4941 9.84375C16.4095 10.0391 16.2923 10.2116 16.1426 10.3613C15.9993 10.5046 15.8268 10.6185 15.625 10.7031C15.4297 10.7878 15.2214 10.8301 15 10.8301C14.7721 10.8301 14.5605 10.7878 14.3652 10.7031C14.1699 10.6185 13.9974 10.5046 13.8477 10.3613C13.7044 10.2116 13.5905 10.0391 13.5059 9.84375C13.4212 9.64844 13.3789 9.43685 13.3789 9.20898Z" fill="currentColor"/></svg>
38
+ </div>
39
+ <div class="moj-alert__content">Content that informs you of a thing. <a href="#">More information</a></div>
40
+ <div class="moj-alert__action">
41
+ <button class="moj-alert__dismiss" hidden>Dismiss</button>
42
+ </div>
43
+ </div>
44
+
45
+
46
+ <div id="alert-3"role="region" class="moj-alert moj-alert--success" aria-label="success: Woohoo!" data-module="moj-alert" data-dismissible="true">
47
+ <div>
48
+ <svg class="moj-alert__icon" role="presentation" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30" height="30" width="30"><path d="M11.2869 24.6726L2.00415 15.3899L4.62189 12.7722L11.2869 19.4186L25.3781 5.32739L27.9958 7.96369L11.2869 24.6726Z" fill="currentColor"/>
49
+ </svg>
50
+ </div>
51
+ <div class="moj-alert__content">That thing just successfully occurred. <a href="#">Celebrate here</a></div>
52
+ <div class="moj-alert__action">
53
+ <button class="moj-alert__dismiss" hidden>Dismiss</button>
54
+ </div>
55
+ </div>
56
+
57
+ <div id="alert-4" role="region" class="moj-alert moj-alert--warning" aria-label="warning: Something&#39;s not quite right" data-module="moj-alert" data-dismissible="true">
58
+ <div>
59
+ <svg class="moj-alert__icon" role="presentation" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30" height="30" width="30"><path fill-rule="evenodd" clip-rule="evenodd" d="M15 2.44922L28.75 26.1992H1.25L15 2.44922ZM13.5107 9.49579H16.4697L16.2431 17.7678H13.7461L13.5107 9.49579ZM13.1299 21.82C13.1299 21.5661 13.1787 21.3285 13.2764 21.1071C13.374 20.8793 13.5075 20.6807 13.6768 20.5114C13.8525 20.3421 14.0544 20.2087 14.2822 20.111C14.5101 20.0134 14.7542 19.9645 15.0146 19.9645C15.2686 19.9645 15.5062 20.0134 15.7275 20.111C15.9554 20.2087 16.154 20.3421 16.3232 20.5114C16.4925 20.6807 16.626 20.8793 16.7236 21.1071C16.8213 21.3285 16.8701 21.5661 16.8701 21.82C16.8701 22.0804 16.8213 22.3246 16.7236 22.5524C16.626 22.7803 16.4925 22.9789 16.3232 23.1481C16.154 23.3174 15.9554 23.4509 15.7275 23.5485C15.5062 23.6462 15.2686 23.695 15.0146 23.695C14.7542 23.695 14.5101 23.6462 14.2822 23.5485C14.0544 23.4509 13.8525 23.3174 13.6768 23.1481C13.5075 22.9789 13.374 22.7803 13.2764 22.5524C13.1787 22.3246 13.1299 22.0804 13.1299 21.82Z" fill="currentColor"/></svg>
60
+ </div>
61
+ <div class="moj-alert__content">You should be aware of this thing. <a href="#">More information</a></div>
62
+ <div class="moj-alert__action">
63
+ <button class="moj-alert__dismiss" hidden>Dismiss</button>
64
+ </div>
65
+ </div>
66
+
67
+ <div id="alert-5" role="region" class="moj-alert moj-alert--error" aria-label="error: Woah, hold up!" data-module="moj-alert" data-dismissible="true" data-dismissible="true" data-focus-on-dismiss-selector="#focusOnMe">
68
+ <div>
69
+ <svg class="moj-alert__icon" role="presentation" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30" height="30" width="30"><path fill-rule="evenodd" clip-rule="evenodd" d="M20.1777 2.5H9.82233L2.5 9.82233V20.1777L9.82233 27.5H20.1777L27.5 20.1777V9.82233L20.1777 2.5ZM10.9155 8.87769L15.0001 12.9623L19.0847 8.87771L21.1224 10.9154L17.0378 15L21.1224 19.0846L19.0847 21.1222L15.0001 17.0376L10.9155 21.1223L8.87782 19.0846L12.9624 15L8.87783 10.9153L10.9155 8.87769Z" fill="currentColor"/></svg>
70
+ </div>
71
+ <div class="moj-alert__content">Bad things happened. <a href="#">Contact us</a></div>
72
+ <div class="moj-alert__action">
73
+ <button class="moj-alert__dismiss" hidden>Dismiss</button>
74
+ </div>
75
+ </div>
76
+
77
+ <div id="focusOnMe">I will receive focus</div>
78
+ </section>
79
+ </main>
80
+ `;
81
+ alert_spec_helper$1 = {
82
+ pageTemplate
83
+ };
84
+ return alert_spec_helper$1;
85
+ }
86
+
87
+ var alert_spec_helperExports = requireAlert_spec_helper();
88
+ var alert_spec_helper = /*@__PURE__*/getDefaultExportFromCjs(alert_spec_helperExports);
89
+
90
+ return alert_spec_helper;
91
+
92
+ }));
@@ -0,0 +1,3 @@
1
+ {% macro mojAlert(params) %}
2
+ {%- include "./template.njk" -%}
3
+ {% endmacro %}
@@ -0,0 +1,83 @@
1
+ {% from "govuk/macros/attributes.njk" import govukAttributes %}
2
+
3
+ {#- Valid options for params #}
4
+ {%- set headingTags = ["h2", "h3", "h4"] %}
5
+ {%- set variants = ["information", "success", "warning", "error"] %}
6
+
7
+ {#- Validate and set options for component #}
8
+ {%- set variant = variants | select("eq", params.variant) | join | default("information", true) %}
9
+ {%- set headingTag = headingTags | select("eq", params.headingTag) | join | default("h2", true) %}
10
+ {%- set content = params.html | safe if params.html else params.text %}
11
+ {%- set dismissible = true if params.dismissible and params.dismissible !== "false" else false %}
12
+ {%- set disableAutoFocus = true if params.disableAutoFocus and params.disableAutoFocus !== "false" else false %}
13
+
14
+ {#- Set classes for this component #}
15
+ {%- set classNames = ["moj-alert"] -%}
16
+ {%- set classNames = classNames.concat("moj-alert--"~variant) %}
17
+
18
+ {%- if params.showTitleAsHeading and content %}
19
+ {%- set classNames = classNames.concat("moj-alert--with-heading") %}
20
+ {%- endif %}
21
+
22
+ {%- if params.classes %}
23
+ {%- set classNames = classNames.concat(params.classes) %}
24
+ {%- endif %}
25
+
26
+ {%- set classNames = classNames | join(' ') -%}
27
+
28
+ {#- Set icon for this component #}
29
+ {%- if params.variant == 'success' %}
30
+ {%- set iconPath = '<path d="M11.2869 24.6726L2.00415 15.3899L4.62189 12.7722L11.2869 19.4186L25.3781 5.32739L27.9958 7.96369L11.2869 24.6726Z" fill="currentColor"/>' %}
31
+ {%- elif params.variant == 'warning' %}
32
+ {%- set iconPath = '<path fill-rule="evenodd" clip-rule="evenodd" d="M15 2.44922L28.75 26.1992H1.25L15 2.44922ZM13.5107 9.49579H16.4697L16.2431 17.7678H13.7461L13.5107 9.49579ZM13.1299 21.82C13.1299 21.5661 13.1787 21.3285 13.2764 21.1071C13.374 20.8793 13.5075 20.6807 13.6768 20.5114C13.8525 20.3421 14.0544 20.2087 14.2822 20.111C14.5101 20.0134 14.7542 19.9645 15.0146 19.9645C15.2686 19.9645 15.5062 20.0134 15.7275 20.111C15.9554 20.2087 16.154 20.3421 16.3232 20.5114C16.4925 20.6807 16.626 20.8793 16.7236 21.1071C16.8213 21.3285 16.8701 21.5661 16.8701 21.82C16.8701 22.0804 16.8213 22.3246 16.7236 22.5524C16.626 22.7803 16.4925 22.9789 16.3232 23.1481C16.154 23.3174 15.9554 23.4509 15.7275 23.5485C15.5062 23.6462 15.2686 23.695 15.0146 23.695C14.7542 23.695 14.5101 23.6462 14.2822 23.5485C14.0544 23.4509 13.8525 23.3174 13.6768 23.1481C13.5075 22.9789 13.374 22.7803 13.2764 22.5524C13.1787 22.3246 13.1299 22.0804 13.1299 21.82Z" fill="currentColor"/>' %}
33
+ {%- elif variant == 'error' %}
34
+ {%- set iconPath = '<path fill-rule="evenodd" clip-rule="evenodd" d="M20.1777 2.5H9.82233L2.5 9.82233V20.1777L9.82233 27.5H20.1777L27.5 20.1777V9.82233L20.1777 2.5ZM10.9155 8.87769L15.0001 12.9623L19.0847 8.87771L21.1224 10.9154L17.0378 15L21.1224 19.0846L19.0847 21.1222L15.0001 17.0376L10.9155 21.1223L8.87782 19.0846L12.9624 15L8.87783 10.9153L10.9155 8.87769Z" fill="currentColor"/>' %}
35
+ {%- else %}
36
+ {%- set iconPath = '<path fill-rule="evenodd" clip-rule="evenodd" d="M10.2165 3.45151C11.733 2.82332 13.3585 2.5 15 2.5C16.6415 2.5 18.267 2.82332 19.7835 3.45151C21.3001 4.07969 22.6781 5.00043 23.8388 6.16117C24.9996 7.3219 25.9203 8.69989 26.5485 10.2165C27.1767 11.733 27.5 13.3585 27.5 15C27.5 18.3152 26.183 21.4946 23.8388 23.8388C21.4946 26.183 18.3152 27.5 15 27.5C13.3585 27.5 11.733 27.1767 10.2165 26.5485C8.69989 25.9203 7.3219 24.9996 6.16117 23.8388C3.81696 21.4946 2.5 18.3152 2.5 15C2.5 11.6848 3.81696 8.50537 6.16117 6.16117C7.3219 5.00043 8.69989 4.07969 10.2165 3.45151ZM16.3574 22.4121H13.6621V12.95H16.3574V22.4121ZM13.3789 9.20898C13.3789 8.98763 13.4212 8.7793 13.5059 8.58398C13.5905 8.38216 13.7044 8.20964 13.8477 8.06641C13.9974 7.91667 14.1699 7.79948 14.3652 7.71484C14.5605 7.63021 14.7721 7.58789 15 7.58789C15.2214 7.58789 15.4297 7.63021 15.625 7.71484C15.8268 7.79948 15.9993 7.91667 16.1426 8.06641C16.2923 8.20964 16.4095 8.38216 16.4941 8.58398C16.5788 8.7793 16.6211 8.98763 16.6211 9.20898C16.6211 9.43685 16.5788 9.64844 16.4941 9.84375C16.4095 10.0391 16.2923 10.2116 16.1426 10.3613C15.9993 10.5046 15.8268 10.6185 15.625 10.7031C15.4297 10.7878 15.2214 10.8301 15 10.8301C14.7721 10.8301 14.5605 10.7878 14.3652 10.7031C14.1699 10.6185 13.9974 10.5046 13.8477 10.3613C13.7044 10.2116 13.5905 10.0391 13.5059 9.84375C13.4212 9.64844 13.3789 9.43685 13.3789 9.20898Z" fill="currentColor"/>' %}
37
+ {%- endif %}
38
+
39
+ <div
40
+ role="{{ params.role | default("region", true) }}"
41
+ class="{{ classNames }}"
42
+ aria-label="{{ variant }}: {{ params.title }}"
43
+ data-module="moj-alert"
44
+ {{- govukAttributes({
45
+ "data-dismissible": {
46
+ value: dismissible
47
+ }
48
+ }) if dismissible }}
49
+ {{- govukAttributes({
50
+ "data-disable-auto-focus": {
51
+ value: disableAutoFocus
52
+ }
53
+ }) if disableAutoFocus }}
54
+ {{- govukAttributes({
55
+ "data-focus-on-dismiss-selector": {
56
+ value: params.focusOnDismissSelector
57
+ }
58
+ }) if params.focusOnDismissSelector }}
59
+ {{- govukAttributes(params.attributes) -}}
60
+ >
61
+ <div>
62
+ <svg class="moj-alert__icon" role="presentation" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30" height="30" width="30">
63
+ {{- iconPath | safe }}
64
+ </svg>
65
+ </div>
66
+ <div class="moj-alert__content">
67
+ {%- if params.showTitleAsHeading and content %}
68
+ <{{-headingTag }} class="govuk-heading-m">
69
+ {{- params.title }}
70
+ </{{-headingTag}}>
71
+ {%- endif %}
72
+
73
+ {%- if params.title and not (content) %}
74
+ {{- params.title }}
75
+ {%- endif %}
76
+
77
+ {{- content -}}
78
+ </div>
79
+
80
+ <div class="moj-alert__action">
81
+ <button class="moj-alert__dismiss" hidden>{{ params.dismissText | default("Dismiss", true) }}</button>
82
+ </div>
83
+ </div>
@@ -4,14 +4,14 @@
4
4
 
5
5
  .moj-badge {
6
6
  @include govuk-font($size: 14, $weight: "bold");
7
- padding: 0 govuk-spacing(1);
8
7
  display: inline-block;
8
+ padding: 0 govuk-spacing(1);
9
9
  border: 2px solid $govuk-brand-colour;
10
+ outline: 2px solid transparent;
11
+ outline-offset: -2px;
10
12
  color: $govuk-brand-colour;
11
13
  text-transform: uppercase;
12
14
  vertical-align: middle;
13
- outline: 2px solid transparent;
14
- outline-offset: -2px;
15
15
 
16
16
  &--purple {
17
17
  border-color: govuk-colour("purple");
@@ -51,5 +51,4 @@
51
51
  &--large {
52
52
  @include govuk-font($size: 16, $weight: "bold");
53
53
  }
54
-
55
54
  }