govuk_tech_docs 3.2.1 → 3.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/publish.yaml +1 -1
  3. data/CHANGELOG.md +26 -7
  4. data/README.md +2 -2
  5. data/lib/assets/javascripts/_modules/search.js +2 -2
  6. data/lib/govuk_tech_docs/contribution_banner.rb +1 -1
  7. data/lib/govuk_tech_docs/version.rb +1 -1
  8. data/lib/source/layouts/core.erb +1 -1
  9. data/node_modules/govuk-frontend/govuk/all.js +1548 -311
  10. data/node_modules/govuk-frontend/govuk/common/closest-attribute-value.js +70 -0
  11. data/node_modules/govuk-frontend/govuk/common/index.js +172 -0
  12. data/node_modules/govuk-frontend/govuk/common/normalise-dataset.js +373 -0
  13. data/node_modules/govuk-frontend/govuk/common.js +138 -3
  14. data/node_modules/govuk-frontend/govuk/components/_all.scss +1 -0
  15. data/node_modules/govuk-frontend/govuk/components/accordion/_index.scss +5 -6
  16. data/node_modules/govuk-frontend/govuk/components/accordion/accordion.js +754 -36
  17. data/node_modules/govuk-frontend/govuk/components/breadcrumbs/_index.scss +0 -2
  18. data/node_modules/govuk-frontend/govuk/components/button/_index.scss +29 -21
  19. data/node_modules/govuk-frontend/govuk/components/button/button.js +365 -107
  20. data/node_modules/govuk-frontend/govuk/components/character-count/_index.scss +9 -0
  21. data/node_modules/govuk-frontend/govuk/components/character-count/character-count.js +1092 -109
  22. data/node_modules/govuk-frontend/govuk/components/checkboxes/_index.scss +3 -2
  23. data/node_modules/govuk-frontend/govuk/components/checkboxes/checkboxes.js +30 -2
  24. data/node_modules/govuk-frontend/govuk/components/details/details.js +51 -33
  25. data/node_modules/govuk-frontend/govuk/components/error-summary/error-summary.js +289 -6
  26. data/node_modules/govuk-frontend/govuk/components/footer/_index.scss +13 -23
  27. data/node_modules/govuk-frontend/govuk/components/header/_index.scss +30 -24
  28. data/node_modules/govuk-frontend/govuk/components/header/header.js +59 -11
  29. data/node_modules/govuk-frontend/govuk/components/input/_index.scss +13 -23
  30. data/node_modules/govuk-frontend/govuk/components/notification-banner/notification-banner.js +252 -2
  31. data/node_modules/govuk-frontend/govuk/components/pagination/_index.scss +247 -0
  32. data/node_modules/govuk-frontend/govuk/components/pagination/_pagination.scss +2 -0
  33. data/node_modules/govuk-frontend/govuk/components/panel/_index.scss +1 -1
  34. data/node_modules/govuk-frontend/govuk/components/radios/_index.scss +5 -12
  35. data/node_modules/govuk-frontend/govuk/components/radios/radios.js +30 -2
  36. data/node_modules/govuk-frontend/govuk/components/select/_index.scss +11 -0
  37. data/node_modules/govuk-frontend/govuk/components/skip-link/_index.scss +1 -3
  38. data/node_modules/govuk-frontend/govuk/components/skip-link/skip-link.js +10 -4
  39. data/node_modules/govuk-frontend/govuk/components/summary-list/_index.scss +45 -13
  40. data/node_modules/govuk-frontend/govuk/components/table/_index.scss +1 -1
  41. data/node_modules/govuk-frontend/govuk/components/tabs/tabs.js +28 -0
  42. data/node_modules/govuk-frontend/govuk/core/_section-break.scss +1 -1
  43. data/node_modules/govuk-frontend/govuk/helpers/_colour.scss +5 -5
  44. data/node_modules/govuk-frontend/govuk/helpers/_focused.scss +5 -0
  45. data/node_modules/govuk-frontend/govuk/helpers/_links.scss +13 -11
  46. data/node_modules/govuk-frontend/govuk/helpers/_media-queries.scss +2 -2
  47. data/node_modules/govuk-frontend/govuk/helpers/_shape-arrow.scss +1 -1
  48. data/node_modules/govuk-frontend/govuk/helpers/_spacing.scss +3 -3
  49. data/node_modules/govuk-frontend/govuk/helpers/_typography.scss +16 -9
  50. data/node_modules/govuk-frontend/govuk/i18n.js +390 -0
  51. data/node_modules/govuk-frontend/govuk/objects/_button-group.scss +10 -26
  52. data/node_modules/govuk-frontend/govuk/objects/_template.scss +1 -1
  53. data/node_modules/govuk-frontend/govuk/objects/_width-container.scss +0 -4
  54. data/node_modules/govuk-frontend/govuk/overrides/_spacing.scss +56 -12
  55. data/node_modules/govuk-frontend/govuk/settings/_all.scss +1 -0
  56. data/node_modules/govuk-frontend/govuk/settings/_colours-palette.scss +12 -0
  57. data/node_modules/govuk-frontend/govuk/settings/_compatibility.scss +26 -0
  58. data/node_modules/govuk-frontend/govuk/settings/_spacing.scss +4 -8
  59. data/node_modules/govuk-frontend/govuk/settings/_typography-font.scss +23 -0
  60. data/node_modules/govuk-frontend/govuk/settings/_typography-responsive.scss +12 -0
  61. data/node_modules/govuk-frontend/govuk/settings/_warnings.scss +53 -0
  62. data/node_modules/govuk-frontend/govuk/tools/_compatibility.scss +20 -6
  63. data/node_modules/govuk-frontend/govuk/tools/_exports.scss +1 -1
  64. data/node_modules/govuk-frontend/govuk/tools/_font-url.scss +1 -1
  65. data/node_modules/govuk-frontend/govuk/tools/_image-url.scss +1 -1
  66. data/node_modules/govuk-frontend/govuk/tools/_px-to-em.scss +2 -2
  67. data/node_modules/govuk-frontend/govuk/tools/_px-to-rem.scss +1 -1
  68. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Date/now.js +21 -0
  69. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/dataset.js +300 -0
  70. data/node_modules/govuk-frontend/govuk/vendor/polyfills/String/prototype/trim.js +21 -0
  71. data/node_modules/govuk-frontend/govuk-prototype-kit/init.js +7 -0
  72. data/node_modules/govuk-frontend/govuk-prototype-kit/init.scss +12 -0
  73. data/package-lock.json +12 -12
  74. data/package.json +1 -1
  75. metadata +14 -2
@@ -58,7 +58,7 @@
58
58
  position: relative;
59
59
  top: -1px;
60
60
  margin-right: 1px;
61
- fill: currentColor;
61
+ fill: currentcolor;
62
62
  vertical-align: top;
63
63
  }
64
64
 
@@ -91,7 +91,7 @@
91
91
  text-decoration: underline;
92
92
  text-decoration-thickness: $govuk-header-link-underline-thickness;
93
93
 
94
- @if ($govuk-link-underline-offset) {
94
+ @if $govuk-link-underline-offset {
95
95
  text-underline-offset: $govuk-link-underline-offset;
96
96
  }
97
97
  }
@@ -111,6 +111,16 @@
111
111
  font-size: 30px; // We don't have a mixin that produces 30px font size
112
112
  line-height: 1;
113
113
 
114
+ @include govuk-media-query($from: tablet) {
115
+ display: inline;
116
+
117
+ &:focus {
118
+ // Replicate the focus box shadow but without the -2px y-offset of the first yellow shadow
119
+ // This is to stop the logo getting cut off by the box shadow when focused on above a product name
120
+ box-shadow: 0 0 $govuk-focus-colour;
121
+ }
122
+ }
123
+
114
124
  &:link,
115
125
  &:visited {
116
126
  text-decoration: none;
@@ -132,6 +142,9 @@
132
142
  }
133
143
  }
134
144
 
145
+ // The govuk-header__link--service-name class is deprecated - use
146
+ // govuk-header__service-name instead.
147
+ .govuk-header__service-name,
135
148
  .govuk-header__link--service-name {
136
149
  display: inline-block;
137
150
  margin-bottom: govuk-spacing(2);
@@ -180,7 +193,7 @@
180
193
  -webkit-text-decoration: solid underline $govuk-header-link-underline-thickness;
181
194
  text-decoration: solid underline $govuk-header-link-underline-thickness;
182
195
 
183
- @if ($govuk-link-underline-offset) {
196
+ @if $govuk-link-underline-offset {
184
197
  text-underline-offset: $govuk-link-underline-offset;
185
198
  }
186
199
  }
@@ -195,14 +208,21 @@
195
208
  margin-left: govuk-spacing(1);
196
209
  }
197
210
 
211
+ &[aria-expanded="true"]:after {
212
+ @include govuk-shape-arrow($direction: up, $base: 10px, $display: inline-block);
213
+ }
214
+
198
215
  @include govuk-media-query ($from: tablet) {
199
216
  top: govuk-spacing(3);
200
217
  }
201
- }
202
218
 
203
- .govuk-header__menu-button--open {
204
- &:after {
205
- @include govuk-shape-arrow($direction: up, $base: 10px, $display: inline-block);
219
+ .js-enabled & {
220
+ display: block;
221
+ }
222
+
223
+ &[hidden],
224
+ .js-enabled &[hidden] {
225
+ display: none;
206
226
  }
207
227
  }
208
228
 
@@ -217,25 +237,9 @@
217
237
  margin: 0;
218
238
  padding: 0;
219
239
  list-style: none;
220
- }
221
240
 
222
- .js-enabled {
223
- .govuk-header__menu-button {
224
- display: block;
225
- @include govuk-media-query ($from: desktop) {
226
- display: none;
227
- }
228
- }
229
-
230
- .govuk-header__navigation-list {
241
+ &[hidden] {
231
242
  display: none;
232
- @include govuk-media-query ($from: desktop) {
233
- display: block;
234
- }
235
- }
236
-
237
- .govuk-header__navigation-list--open {
238
- display: block;
239
243
  }
240
244
  }
241
245
 
@@ -247,6 +251,8 @@
247
251
  }
248
252
  }
249
253
 
254
+ // The govuk-header__navigation--no-service-name class is deprecated and will
255
+ // be removed in the next major release.
250
256
  .govuk-header__navigation--no-service-name {
251
257
  padding-top: govuk-spacing(7);
252
258
  }
@@ -1014,12 +1014,29 @@ if (detect) return
1014
1014
  })
1015
1015
  .call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
1016
1016
 
1017
+ /**
1018
+ * Header component
1019
+ *
1020
+ * @class
1021
+ * @param {HTMLElement} $module - HTML element to use for header
1022
+ */
1017
1023
  function Header ($module) {
1018
1024
  this.$module = $module;
1019
1025
  this.$menuButton = $module && $module.querySelector('.govuk-js-header-toggle');
1020
1026
  this.$menu = this.$menuButton && $module.querySelector(
1021
1027
  '#' + this.$menuButton.getAttribute('aria-controls')
1022
1028
  );
1029
+
1030
+ // Save the opened/closed state for the nav in memory so that we can
1031
+ // accurately maintain state when the screen is changed from small to
1032
+ // big and back to small
1033
+ this.menuIsOpen = false;
1034
+
1035
+ // A global const for storing a matchMedia instance which we'll use to
1036
+ // detect when a screen size change happens. We set this later during the
1037
+ // init function and rely on it being null if the feature isn't available
1038
+ // to initially apply hidden attributes
1039
+ this.mql = null;
1023
1040
  }
1024
1041
 
1025
1042
  /**
@@ -1027,27 +1044,58 @@ function Header ($module) {
1027
1044
  *
1028
1045
  * Check for the presence of the header, menu and menu button – if any are
1029
1046
  * missing then there's nothing to do so return early.
1047
+ * Feature sniff for and apply a matchMedia for desktop which will
1048
+ * trigger a state sync if the browser viewport moves between states. If
1049
+ * matchMedia isn't available, hide the menu button and present the "no js"
1050
+ * version of the menu to the user.
1030
1051
  */
1031
1052
  Header.prototype.init = function () {
1032
1053
  if (!this.$module || !this.$menuButton || !this.$menu) {
1033
1054
  return
1034
1055
  }
1035
1056
 
1036
- this.syncState(this.$menu.classList.contains('govuk-header__navigation-list--open'));
1037
- this.$menuButton.addEventListener('click', this.handleMenuButtonClick.bind(this));
1057
+ if ('matchMedia' in window) {
1058
+ // Set the matchMedia to the govuk-frontend desktop breakpoint
1059
+ this.mql = window.matchMedia('(min-width: 48.0625em)');
1060
+
1061
+ if ('addEventListener' in this.mql) {
1062
+ this.mql.addEventListener('change', this.syncState.bind(this));
1063
+ } else {
1064
+ // addListener is a deprecated function, however addEventListener
1065
+ // isn't supported by IE or Safari. We therefore add this in as
1066
+ // a fallback for those browsers
1067
+ this.mql.addListener(this.syncState.bind(this));
1068
+ }
1069
+
1070
+ this.syncState();
1071
+ this.$menuButton.addEventListener('click', this.handleMenuButtonClick.bind(this));
1072
+ } else {
1073
+ this.$menuButton.setAttribute('hidden', '');
1074
+ }
1038
1075
  };
1039
1076
 
1040
1077
  /**
1041
1078
  * Sync menu state
1042
1079
  *
1043
- * Sync the menu button class and the accessible state of the menu and the menu
1044
- * button with the visible state of the menu
1045
- *
1046
- * @param {boolean} isVisible Whether the menu is currently visible
1080
+ * Uses the global variable menuIsOpen to correctly set the accessible and
1081
+ * visual states of the menu and the menu button.
1082
+ * Additionally will force the menu to be visible and the menu button to be
1083
+ * hidden if the matchMedia is triggered to desktop.
1047
1084
  */
1048
- Header.prototype.syncState = function (isVisible) {
1049
- this.$menuButton.classList.toggle('govuk-header__menu-button--open', isVisible);
1050
- this.$menuButton.setAttribute('aria-expanded', isVisible);
1085
+ Header.prototype.syncState = function () {
1086
+ if (this.mql.matches) {
1087
+ this.$menu.removeAttribute('hidden');
1088
+ this.$menuButton.setAttribute('hidden', '');
1089
+ } else {
1090
+ this.$menuButton.removeAttribute('hidden');
1091
+ this.$menuButton.setAttribute('aria-expanded', this.menuIsOpen);
1092
+
1093
+ if (this.menuIsOpen) {
1094
+ this.$menu.removeAttribute('hidden');
1095
+ } else {
1096
+ this.$menu.setAttribute('hidden', '');
1097
+ }
1098
+ }
1051
1099
  };
1052
1100
 
1053
1101
  /**
@@ -1057,8 +1105,8 @@ Header.prototype.syncState = function (isVisible) {
1057
1105
  * sync the accessibility state and menu button state
1058
1106
  */
1059
1107
  Header.prototype.handleMenuButtonClick = function () {
1060
- var isVisible = this.$menu.classList.toggle('govuk-header__navigation-list--open');
1061
- this.syncState(isVisible);
1108
+ this.menuIsOpen = !this.menuIsOpen;
1109
+ this.syncState();
1062
1110
  };
1063
1111
 
1064
1112
  return Header;
@@ -22,7 +22,6 @@
22
22
 
23
23
  // Disable inner shadow and remove rounded corners
24
24
  -webkit-appearance: none;
25
- -moz-appearance: none;
26
25
  appearance: none;
27
26
 
28
27
  &:focus {
@@ -61,49 +60,44 @@
61
60
  }
62
61
  }
63
62
 
64
- // The ex measurements are based on the number of W's that can fit inside the input
65
- // Extra space is left on the right hand side to allow for the Safari prefill icon
66
- // Linear regression estimation based on visual tests: y = 1.76 + 1.81x
63
+ // em measurements are based on the point size of the typeface
64
+ // Extra space is added on the right hand side to allow for the Safari prefill icon
67
65
 
68
66
  .govuk-input--width-30 {
69
- max-width: 56ex + 3ex;
67
+ max-width: 29.5em;
70
68
  }
71
69
 
72
70
  .govuk-input--width-20 {
73
- max-width: 38ex + 3ex;
71
+ max-width: 20.5em;
74
72
  }
75
73
 
76
74
  .govuk-input--width-10 {
77
- max-width: 20ex + 3ex;
75
+ max-width: 11.5em;
78
76
  }
79
77
 
80
78
  .govuk-input--width-5 {
81
- max-width: 10.8ex;
79
+ max-width: 5.5em;
82
80
  }
83
81
 
84
82
  .govuk-input--width-4 {
85
- max-width: 9ex;
83
+ max-width: 4.5em;
86
84
  }
87
85
 
88
86
  .govuk-input--width-3 {
89
- max-width: 7.2ex;
87
+ max-width: 3.75em;
90
88
  }
91
89
 
92
90
  .govuk-input--width-2 {
93
- max-width: 5.4ex;
91
+ max-width: 2.75em;
94
92
  }
95
93
 
96
94
  .govuk-input__wrapper {
97
- display: -webkit-box;
98
- display: -webkit-flex;
99
95
  display: -ms-flexbox;
100
96
  display: flex;
101
97
 
102
98
  .govuk-input {
103
- -webkit-box-flex: 0;
104
- -webkit-flex: 0 1 auto;
105
- -ms-flex: 0 1 auto;
106
- flex: 0 1 auto;
99
+ -ms-flex: 0 1 auto;
100
+ flex: 0 1 auto;
107
101
  }
108
102
 
109
103
  .govuk-input:focus {
@@ -150,13 +144,9 @@
150
144
  // Emphasise non-editable status of prefixes and suffixes
151
145
  cursor: default;
152
146
 
153
- -webkit-box-flex: 0;
147
+ -ms-flex: 0 0 auto;
154
148
 
155
- -webkit-flex: 0 0 auto;
156
-
157
- -ms-flex: 0 0 auto;
158
-
159
- flex: 0 0 auto;
149
+ flex: 0 0 auto;
160
150
 
161
151
  // Split prefix/suffix onto separate lines on narrow screens
162
152
  @include govuk-media-query($until: mobile) {
@@ -501,8 +501,247 @@ if (detect) return
501
501
  })
502
502
  .call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
503
503
 
504
- function NotificationBanner ($module) {
504
+ /**
505
+ * Common helpers which do not require polyfill.
506
+ *
507
+ * IMPORTANT: If a helper require a polyfill, please isolate it in its own module
508
+ * so that the polyfill can be properly tree-shaken and does not burden
509
+ * the components that do not need that helper
510
+ *
511
+ * @module common/index
512
+ */
513
+
514
+ /**
515
+ * Config flattening function
516
+ *
517
+ * Takes any number of objects, flattens them into namespaced key-value pairs,
518
+ * (e.g. {'i18n.showSection': 'Show section'}) and combines them together, with
519
+ * greatest priority on the LAST item passed in.
520
+ *
521
+ * @returns {object} A flattened object of key-value pairs.
522
+ */
523
+ function mergeConfigs (/* configObject1, configObject2, ...configObjects */) {
524
+ /**
525
+ * Function to take nested objects and flatten them to a dot-separated keyed
526
+ * object. Doing this means we don't need to do any deep/recursive merging of
527
+ * each of our objects, nor transform our dataset from a flat list into a
528
+ * nested object.
529
+ *
530
+ * @param {object} configObject - Deeply nested object
531
+ * @returns {object} Flattened object with dot-separated keys
532
+ */
533
+ var flattenObject = function (configObject) {
534
+ // Prepare an empty return object
535
+ var flattenedObject = {};
536
+
537
+ // Our flattening function, this is called recursively for each level of
538
+ // depth in the object. At each level we prepend the previous level names to
539
+ // the key using `prefix`.
540
+ var flattenLoop = function (obj, prefix) {
541
+ // Loop through keys...
542
+ for (var key in obj) {
543
+ // Check to see if this is a prototypical key/value,
544
+ // if it is, skip it.
545
+ if (!Object.prototype.hasOwnProperty.call(obj, key)) {
546
+ continue
547
+ }
548
+ var value = obj[key];
549
+ var prefixedKey = prefix ? prefix + '.' + key : key;
550
+ if (typeof value === 'object') {
551
+ // If the value is a nested object, recurse over that too
552
+ flattenLoop(value, prefixedKey);
553
+ } else {
554
+ // Otherwise, add this value to our return object
555
+ flattenedObject[prefixedKey] = value;
556
+ }
557
+ }
558
+ };
559
+
560
+ // Kick off the recursive loop
561
+ flattenLoop(configObject);
562
+ return flattenedObject
563
+ };
564
+
565
+ // Start with an empty object as our base
566
+ var formattedConfigObject = {};
567
+
568
+ // Loop through each of the remaining passed objects and push their keys
569
+ // one-by-one into configObject. Any duplicate keys will override the existing
570
+ // key with the new value.
571
+ for (var i = 0; i < arguments.length; i++) {
572
+ var obj = flattenObject(arguments[i]);
573
+ for (var key in obj) {
574
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
575
+ formattedConfigObject[key] = obj[key];
576
+ }
577
+ }
578
+ }
579
+
580
+ return formattedConfigObject
581
+ }
582
+
583
+ /**
584
+ * @callback nodeListIterator
585
+ * @param {Element} value - The current node being iterated on
586
+ * @param {number} index - The current index in the iteration
587
+ * @param {NodeListOf<Element>} nodes - NodeList from querySelectorAll()
588
+ * @returns {undefined}
589
+ */
590
+
591
+ (function(undefined) {
592
+
593
+ // Detection from https://raw.githubusercontent.com/Financial-Times/polyfill-library/13cf7c340974d128d557580b5e2dafcd1b1192d1/polyfills/Element/prototype/dataset/detect.js
594
+ var detect = (function(){
595
+ if (!document.documentElement.dataset) {
596
+ return false;
597
+ }
598
+ var el = document.createElement('div');
599
+ el.setAttribute("data-a-b", "c");
600
+ return el.dataset && el.dataset.aB == "c";
601
+ }());
602
+
603
+ if (detect) return
604
+
605
+ // Polyfill derived from https://raw.githubusercontent.com/Financial-Times/polyfill-library/13cf7c340974d128d557580b5e2dafcd1b1192d1/polyfills/Element/prototype/dataset/polyfill.js
606
+ Object.defineProperty(Element.prototype, 'dataset', {
607
+ get: function() {
608
+ var element = this;
609
+ var attributes = this.attributes;
610
+ var map = {};
611
+
612
+ for (var i = 0; i < attributes.length; i++) {
613
+ var attribute = attributes[i];
614
+
615
+ // This regex has been edited from the original polyfill, to add
616
+ // support for period (.) separators in data-* attribute names. These
617
+ // are allowed in the HTML spec, but were not covered by the original
618
+ // polyfill's regex. We use periods in our i18n implementation.
619
+ if (attribute && attribute.name && (/^data-\w[.\w-]*$/).test(attribute.name)) {
620
+ var name = attribute.name;
621
+ var value = attribute.value;
622
+
623
+ var propName = name.substr(5).replace(/-./g, function (prop) {
624
+ return prop.charAt(1).toUpperCase();
625
+ });
626
+
627
+ // If this browser supports __defineGetter__ and __defineSetter__,
628
+ // continue using defineProperty. If not (like IE 8 and below), we use
629
+ // a hacky fallback which at least gives an object in the right format
630
+ if ('__defineGetter__' in Object.prototype && '__defineSetter__' in Object.prototype) {
631
+ Object.defineProperty(map, propName, {
632
+ enumerable: true,
633
+ get: function() {
634
+ return this.value;
635
+ }.bind({value: value || ''}),
636
+ set: function setter(name, value) {
637
+ if (typeof value !== 'undefined') {
638
+ this.setAttribute(name, value);
639
+ } else {
640
+ this.removeAttribute(name);
641
+ }
642
+ }.bind(element, name)
643
+ });
644
+ } else {
645
+ map[propName] = value;
646
+ }
647
+
648
+ }
649
+ }
650
+
651
+ return map;
652
+ }
653
+ });
654
+
655
+ }).call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
656
+
657
+ (function(undefined) {
658
+
659
+ // Detection from https://github.com/mdn/content/blob/cf607d68522cd35ee7670782d3ee3a361eaef2e4/files/en-us/web/javascript/reference/global_objects/string/trim/index.md#polyfill
660
+ var detect = ('trim' in String.prototype);
661
+
662
+ if (detect) return
663
+
664
+ // Polyfill from https://github.com/mdn/content/blob/cf607d68522cd35ee7670782d3ee3a361eaef2e4/files/en-us/web/javascript/reference/global_objects/string/trim/index.md#polyfill
665
+ String.prototype.trim = function () {
666
+ return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
667
+ };
668
+
669
+ }).call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
670
+
671
+ /**
672
+ * Normalise string
673
+ *
674
+ * 'If it looks like a duck, and it quacks like a duck…' 🦆
675
+ *
676
+ * If the passed value looks like a boolean or a number, convert it to a boolean
677
+ * or number.
678
+ *
679
+ * Designed to be used to convert config passed via data attributes (which are
680
+ * always strings) into something sensible.
681
+ *
682
+ * @param {string} value - The value to normalise
683
+ * @returns {string | boolean | number | undefined} Normalised data
684
+ */
685
+ function normaliseString (value) {
686
+ if (typeof value !== 'string') {
687
+ return value
688
+ }
689
+
690
+ var trimmedValue = value.trim();
691
+
692
+ if (trimmedValue === 'true') {
693
+ return true
694
+ }
695
+
696
+ if (trimmedValue === 'false') {
697
+ return false
698
+ }
699
+
700
+ // Empty / whitespace-only strings are considered finite so we need to check
701
+ // the length of the trimmed string as well
702
+ if (trimmedValue.length > 0 && isFinite(trimmedValue)) {
703
+ return Number(trimmedValue)
704
+ }
705
+
706
+ return value
707
+ }
708
+
709
+ /**
710
+ * Normalise dataset
711
+ *
712
+ * Loop over an object and normalise each value using normaliseData function
713
+ *
714
+ * @param {DOMStringMap} dataset - HTML element dataset
715
+ * @returns {Object<string, string | boolean | number | undefined>} Normalised dataset
716
+ */
717
+ function normaliseDataset (dataset) {
718
+ var out = {};
719
+
720
+ for (var key in dataset) {
721
+ out[key] = normaliseString(dataset[key]);
722
+ }
723
+
724
+ return out
725
+ }
726
+
727
+ /**
728
+ * Notification Banner component
729
+ *
730
+ * @class
731
+ * @param {HTMLElement} $module - HTML element to use for notification banner
732
+ * @param {NotificationBannerConfig} config - Notification banner config
733
+ */
734
+ function NotificationBanner ($module, config) {
505
735
  this.$module = $module;
736
+
737
+ var defaultConfig = {
738
+ disableAutoFocus: false
739
+ };
740
+ this.config = mergeConfigs(
741
+ defaultConfig,
742
+ config || {},
743
+ normaliseDataset($module.dataset)
744
+ );
506
745
  }
507
746
 
508
747
  /**
@@ -531,7 +770,7 @@ NotificationBanner.prototype.init = function () {
531
770
  NotificationBanner.prototype.setFocus = function () {
532
771
  var $module = this.$module;
533
772
 
534
- if ($module.getAttribute('data-disable-auto-focus') === 'true') {
773
+ if (this.config.disableAutoFocus) {
535
774
  return
536
775
  }
537
776
 
@@ -553,6 +792,17 @@ NotificationBanner.prototype.setFocus = function () {
553
792
  $module.focus();
554
793
  };
555
794
 
795
+ /**
796
+ * Notification banner config
797
+ *
798
+ * @typedef {object} NotificationBannerConfig
799
+ * @property {boolean} [disableAutoFocus = false] -
800
+ * If set to `true` the notification banner will not be focussed when the page
801
+ * loads. This only applies if the component has a `role` of `alert` – in
802
+ * other cases the component will not be focused on page load, regardless of
803
+ * this option.
804
+ */
805
+
556
806
  return NotificationBanner;
557
807
 
558
808
  })));