govuk_publishing_components 32.0.0 → 33.0.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 (206) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/component_guide/accessibility-test.js +0 -1
  3. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-core.js +175 -0
  4. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-ecommerce-tracker.js +1 -1
  5. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-event-tracker.js +5 -13
  6. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-link-tracker.js +80 -309
  7. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-page-views.js +2 -2
  8. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-specialist-link-tracker.js +140 -0
  9. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/init-ga4.js +3 -0
  10. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4.js +1 -0
  11. data/app/assets/javascripts/govuk_publishing_components/components/accordion.js +12 -1
  12. data/app/assets/javascripts/govuk_publishing_components/components/layout-super-navigation-header.js +13 -4
  13. data/app/assets/javascripts/govuk_publishing_components/components/single-page-notification-button.js +24 -8
  14. data/app/assets/javascripts/govuk_publishing_components/vendor/lux/lux-reporter.js +83 -86
  15. data/app/assets/stylesheets/govuk_publishing_components/components/_big-number.scss +2 -5
  16. data/app/assets/stylesheets/govuk_publishing_components/components/_image-card.scss +1 -5
  17. data/app/assets/stylesheets/govuk_publishing_components/components/_input.scss +3 -5
  18. data/app/assets/stylesheets/govuk_publishing_components/components/_layout-super-navigation-header.scss +10 -30
  19. data/app/assets/stylesheets/govuk_publishing_components/components/_search.scss +0 -7
  20. data/app/assets/stylesheets/govuk_publishing_components/components/_share-links.scss +0 -6
  21. data/app/views/govuk_publishing_components/components/_accordion.html.erb +14 -1
  22. data/app/views/govuk_publishing_components/components/_error_summary.html.erb +27 -26
  23. data/app/views/govuk_publishing_components/components/_layout_super_navigation_header.html.erb +2 -2
  24. data/app/views/govuk_publishing_components/components/_phase_banner.html.erb +1 -1
  25. data/app/views/govuk_publishing_components/components/_share_links.html.erb +18 -15
  26. data/app/views/govuk_publishing_components/components/_single_page_notification_button.html.erb +1 -1
  27. data/app/views/govuk_publishing_components/components/docs/accordion.yml +15 -3
  28. data/app/views/govuk_publishing_components/components/docs/button.yml +10 -0
  29. data/app/views/govuk_publishing_components/components/docs/share_links.yml +59 -30
  30. data/app/views/govuk_publishing_components/components/docs/single_page_notification_button.yml +10 -1
  31. data/app/views/govuk_publishing_components/components/feedback/_yes_no_banner.html.erb +3 -3
  32. data/config/locales/ar.yml +4 -1
  33. data/config/locales/az.yml +4 -1
  34. data/config/locales/be.yml +4 -1
  35. data/config/locales/bg.yml +4 -1
  36. data/config/locales/bn.yml +4 -1
  37. data/config/locales/cs.yml +4 -1
  38. data/config/locales/cy.yml +4 -1
  39. data/config/locales/da.yml +4 -1
  40. data/config/locales/de.yml +4 -1
  41. data/config/locales/dr.yml +4 -1
  42. data/config/locales/el.yml +4 -1
  43. data/config/locales/en.yml +20 -17
  44. data/config/locales/es-419.yml +4 -1
  45. data/config/locales/es.yml +4 -1
  46. data/config/locales/et.yml +4 -1
  47. data/config/locales/fa.yml +4 -1
  48. data/config/locales/fi.yml +4 -1
  49. data/config/locales/fr.yml +4 -1
  50. data/config/locales/gd.yml +4 -1
  51. data/config/locales/gu.yml +4 -1
  52. data/config/locales/he.yml +4 -1
  53. data/config/locales/hi.yml +4 -1
  54. data/config/locales/hr.yml +4 -1
  55. data/config/locales/hu.yml +4 -1
  56. data/config/locales/hy.yml +4 -1
  57. data/config/locales/id.yml +4 -1
  58. data/config/locales/is.yml +4 -1
  59. data/config/locales/it.yml +4 -1
  60. data/config/locales/ja.yml +4 -1
  61. data/config/locales/ka.yml +4 -1
  62. data/config/locales/kk.yml +4 -1
  63. data/config/locales/ko.yml +4 -1
  64. data/config/locales/lt.yml +4 -1
  65. data/config/locales/lv.yml +4 -1
  66. data/config/locales/ms.yml +4 -1
  67. data/config/locales/mt.yml +4 -1
  68. data/config/locales/nl.yml +4 -1
  69. data/config/locales/no.yml +4 -1
  70. data/config/locales/pa-pk.yml +4 -1
  71. data/config/locales/pa.yml +4 -1
  72. data/config/locales/pl.yml +4 -1
  73. data/config/locales/ps.yml +4 -1
  74. data/config/locales/pt.yml +4 -1
  75. data/config/locales/ro.yml +4 -1
  76. data/config/locales/ru.yml +4 -1
  77. data/config/locales/si.yml +4 -1
  78. data/config/locales/sk.yml +4 -1
  79. data/config/locales/sl.yml +4 -1
  80. data/config/locales/so.yml +4 -1
  81. data/config/locales/sq.yml +4 -1
  82. data/config/locales/sr.yml +4 -1
  83. data/config/locales/sv.yml +4 -1
  84. data/config/locales/sw.yml +4 -1
  85. data/config/locales/ta.yml +4 -1
  86. data/config/locales/th.yml +4 -1
  87. data/config/locales/tk.yml +4 -1
  88. data/config/locales/tr.yml +4 -1
  89. data/config/locales/uk.yml +4 -1
  90. data/config/locales/ur.yml +4 -1
  91. data/config/locales/uz.yml +4 -1
  92. data/config/locales/vi.yml +4 -1
  93. data/config/locales/zh-hk.yml +4 -1
  94. data/config/locales/zh-tw.yml +4 -1
  95. data/config/locales/zh.yml +4 -1
  96. data/lib/govuk_publishing_components/presenters/button_helper.rb +7 -1
  97. data/lib/govuk_publishing_components/presenters/single_page_notification_button_helper.rb +25 -1
  98. data/lib/govuk_publishing_components/version.rb +1 -1
  99. data/node_modules/axe-core/axe.js +4567 -4678
  100. data/node_modules/axe-core/axe.min.js +2 -2
  101. data/node_modules/axe-core/package.json +2 -2
  102. data/node_modules/axe-core/sri-history.json +8 -0
  103. data/node_modules/govuk-frontend/README.md +1 -2
  104. data/node_modules/govuk-frontend/govuk/all.js +1398 -273
  105. data/node_modules/govuk-frontend/govuk/common/closest-attribute-value.js +70 -0
  106. data/node_modules/govuk-frontend/govuk/common/index.js +172 -0
  107. data/node_modules/govuk-frontend/govuk/common/normalise-dataset.js +373 -0
  108. data/node_modules/govuk-frontend/govuk/common.js +138 -3
  109. data/node_modules/govuk-frontend/govuk/components/accordion/accordion.js +753 -25
  110. data/node_modules/govuk-frontend/govuk/components/accordion/fixtures.json +54 -22
  111. data/node_modules/govuk-frontend/govuk/components/accordion/macro-options.json +36 -0
  112. data/node_modules/govuk-frontend/govuk/components/accordion/template.njk +7 -1
  113. data/node_modules/govuk-frontend/govuk/components/back-link/fixtures.json +12 -12
  114. data/node_modules/govuk-frontend/govuk/components/breadcrumbs/fixtures.json +22 -22
  115. data/node_modules/govuk-frontend/govuk/components/button/_index.scss +23 -5
  116. data/node_modules/govuk-frontend/govuk/components/button/button.js +365 -107
  117. data/node_modules/govuk-frontend/govuk/components/button/fixtures.json +85 -66
  118. data/node_modules/govuk-frontend/govuk/components/button/template.njk +1 -1
  119. data/node_modules/govuk-frontend/govuk/components/character-count/_index.scss +9 -0
  120. data/node_modules/govuk-frontend/govuk/components/character-count/character-count.js +1033 -121
  121. data/node_modules/govuk-frontend/govuk/components/character-count/fixtures.json +112 -36
  122. data/node_modules/govuk-frontend/govuk/components/character-count/macro-options.json +42 -0
  123. data/node_modules/govuk-frontend/govuk/components/character-count/template.njk +27 -3
  124. data/node_modules/govuk-frontend/govuk/components/checkboxes/checkboxes.js +30 -2
  125. data/node_modules/govuk-frontend/govuk/components/checkboxes/fixtures.json +96 -93
  126. data/node_modules/govuk-frontend/govuk/components/cookie-banner/fixtures.json +46 -46
  127. data/node_modules/govuk-frontend/govuk/components/date-input/fixtures.json +50 -50
  128. data/node_modules/govuk-frontend/govuk/components/details/details.js +43 -13
  129. data/node_modules/govuk-frontend/govuk/components/details/fixtures.json +20 -20
  130. data/node_modules/govuk-frontend/govuk/components/error-message/fixtures.json +20 -20
  131. data/node_modules/govuk-frontend/govuk/components/error-summary/error-summary.js +268 -6
  132. data/node_modules/govuk-frontend/govuk/components/error-summary/fixtures.json +44 -35
  133. data/node_modules/govuk-frontend/govuk/components/error-summary/template.njk +25 -21
  134. data/node_modules/govuk-frontend/govuk/components/fieldset/fixtures.json +51 -39
  135. data/node_modules/govuk-frontend/govuk/components/file-upload/fixtures.json +26 -26
  136. data/node_modules/govuk-frontend/govuk/components/footer/_index.scss +1 -1
  137. data/node_modules/govuk-frontend/govuk/components/footer/fixtures.json +46 -46
  138. data/node_modules/govuk-frontend/govuk/components/footer/macro-options.json +2 -2
  139. data/node_modules/govuk-frontend/govuk/components/header/fixtures.json +93 -38
  140. data/node_modules/govuk-frontend/govuk/components/header/header.js +6 -0
  141. data/node_modules/govuk-frontend/govuk/components/header/macro-options.json +8 -2
  142. data/node_modules/govuk-frontend/govuk/components/header/template.njk +4 -2
  143. data/node_modules/govuk-frontend/govuk/components/hint/fixtures.json +12 -12
  144. data/node_modules/govuk-frontend/govuk/components/input/fixtures.json +80 -80
  145. data/node_modules/govuk-frontend/govuk/components/inset-text/fixtures.json +12 -12
  146. data/node_modules/govuk-frontend/govuk/components/label/fixtures.json +34 -34
  147. data/node_modules/govuk-frontend/govuk/components/notification-banner/fixtures.json +56 -46
  148. data/node_modules/govuk-frontend/govuk/components/notification-banner/notification-banner.js +252 -2
  149. data/node_modules/govuk-frontend/govuk/components/notification-banner/template.njk +1 -1
  150. data/node_modules/govuk-frontend/govuk/components/pagination/_index.scss +10 -7
  151. data/node_modules/govuk-frontend/govuk/components/pagination/fixtures.json +33 -26
  152. data/node_modules/govuk-frontend/govuk/components/panel/fixtures.json +18 -18
  153. data/node_modules/govuk-frontend/govuk/components/phase-banner/fixtures.json +14 -14
  154. data/node_modules/govuk-frontend/govuk/components/radios/fixtures.json +94 -91
  155. data/node_modules/govuk-frontend/govuk/components/radios/radios.js +30 -2
  156. data/node_modules/govuk-frontend/govuk/components/select/fixtures.json +32 -32
  157. data/node_modules/govuk-frontend/govuk/components/skip-link/fixtures.json +22 -20
  158. data/node_modules/govuk-frontend/govuk/components/skip-link/skip-link.js +10 -4
  159. data/node_modules/govuk-frontend/govuk/components/summary-list/fixtures.json +50 -50
  160. data/node_modules/govuk-frontend/govuk/components/table/_index.scss +1 -1
  161. data/node_modules/govuk-frontend/govuk/components/table/fixtures.json +40 -40
  162. data/node_modules/govuk-frontend/govuk/components/tabs/fixtures.json +29 -29
  163. data/node_modules/govuk-frontend/govuk/components/tabs/tabs.js +28 -0
  164. data/node_modules/govuk-frontend/govuk/components/tag/fixtures.json +28 -28
  165. data/node_modules/govuk-frontend/govuk/components/textarea/fixtures.json +34 -34
  166. data/node_modules/govuk-frontend/govuk/components/warning-text/fixtures.json +14 -14
  167. data/node_modules/govuk-frontend/govuk/core/_section-break.scss +1 -1
  168. data/node_modules/govuk-frontend/govuk/helpers/_colour.scss +2 -2
  169. data/node_modules/govuk-frontend/govuk/helpers/_links.scss +6 -6
  170. data/node_modules/govuk-frontend/govuk/i18n.js +390 -0
  171. data/node_modules/govuk-frontend/govuk/macros/i18n.njk +15 -0
  172. data/node_modules/govuk-frontend/govuk/settings/_all.scss +1 -0
  173. data/node_modules/govuk-frontend/govuk/settings/_colours-palette.scss +12 -0
  174. data/node_modules/govuk-frontend/govuk/settings/_compatibility.scss +26 -0
  175. data/node_modules/govuk-frontend/govuk/settings/_typography-font.scss +23 -0
  176. data/node_modules/govuk-frontend/govuk/settings/_typography-responsive.scss +12 -0
  177. data/node_modules/govuk-frontend/govuk/settings/_warnings.scss +53 -0
  178. data/node_modules/govuk-frontend/govuk/tools/_compatibility.scss +20 -6
  179. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Date/now.js +21 -0
  180. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/dataset.js +300 -0
  181. data/node_modules/govuk-frontend/govuk/vendor/polyfills/String/prototype/trim.js +21 -0
  182. data/node_modules/govuk-frontend/govuk-esm/all.mjs +50 -27
  183. data/node_modules/govuk-frontend/govuk-esm/common/closest-attribute-value.mjs +15 -0
  184. data/node_modules/govuk-frontend/govuk-esm/common/index.mjs +159 -0
  185. data/node_modules/govuk-frontend/govuk-esm/common/normalise-dataset.mjs +58 -0
  186. data/node_modules/govuk-frontend/govuk-esm/common.mjs +6 -28
  187. data/node_modules/govuk-frontend/govuk-esm/components/accordion/accordion.mjs +113 -43
  188. data/node_modules/govuk-frontend/govuk-esm/components/button/button.mjs +67 -30
  189. data/node_modules/govuk-frontend/govuk-esm/components/character-count/character-count.mjs +325 -123
  190. data/node_modules/govuk-frontend/govuk-esm/components/checkboxes/checkboxes.mjs +9 -3
  191. data/node_modules/govuk-frontend/govuk-esm/components/details/details.mjs +22 -8
  192. data/node_modules/govuk-frontend/govuk-esm/components/error-summary/error-summary.mjs +48 -6
  193. data/node_modules/govuk-frontend/govuk-esm/components/header/header.mjs +6 -0
  194. data/node_modules/govuk-frontend/govuk-esm/components/notification-banner/notification-banner.mjs +32 -2
  195. data/node_modules/govuk-frontend/govuk-esm/components/radios/radios.mjs +9 -3
  196. data/node_modules/govuk-frontend/govuk-esm/components/skip-link/skip-link.mjs +10 -4
  197. data/node_modules/govuk-frontend/govuk-esm/components/tabs/tabs.mjs +8 -2
  198. data/node_modules/govuk-frontend/govuk-esm/i18n.mjs +380 -0
  199. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/Date/now.mjs +13 -0
  200. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/Element/prototype/dataset.mjs +68 -0
  201. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/String/prototype/trim.mjs +13 -0
  202. data/node_modules/govuk-frontend/govuk-prototype-kit/init.js +7 -0
  203. data/node_modules/govuk-frontend/govuk-prototype-kit/init.scss +12 -0
  204. data/node_modules/govuk-frontend/govuk-prototype-kit.config.json +138 -7
  205. data/node_modules/govuk-frontend/package.json +1 -1
  206. metadata +22 -3
@@ -4,24 +4,177 @@
4
4
  (global.GOVUKFrontend = global.GOVUKFrontend || {}, global.GOVUKFrontend.Button = factory());
5
5
  }(this, (function () { 'use strict';
6
6
 
7
+ /**
8
+ * Common helpers which do not require polyfill.
9
+ *
10
+ * IMPORTANT: If a helper require a polyfill, please isolate it in its own module
11
+ * so that the polyfill can be properly tree-shaken and does not burden
12
+ * the components that do not need that helper
13
+ *
14
+ * @module common/index
15
+ */
16
+
17
+ /**
18
+ * Config flattening function
19
+ *
20
+ * Takes any number of objects, flattens them into namespaced key-value pairs,
21
+ * (e.g. {'i18n.showSection': 'Show section'}) and combines them together, with
22
+ * greatest priority on the LAST item passed in.
23
+ *
24
+ * @returns {object} A flattened object of key-value pairs.
25
+ */
26
+ function mergeConfigs (/* configObject1, configObject2, ...configObjects */) {
27
+ /**
28
+ * Function to take nested objects and flatten them to a dot-separated keyed
29
+ * object. Doing this means we don't need to do any deep/recursive merging of
30
+ * each of our objects, nor transform our dataset from a flat list into a
31
+ * nested object.
32
+ *
33
+ * @param {object} configObject - Deeply nested object
34
+ * @returns {object} Flattened object with dot-separated keys
35
+ */
36
+ var flattenObject = function (configObject) {
37
+ // Prepare an empty return object
38
+ var flattenedObject = {};
39
+
40
+ // Our flattening function, this is called recursively for each level of
41
+ // depth in the object. At each level we prepend the previous level names to
42
+ // the key using `prefix`.
43
+ var flattenLoop = function (obj, prefix) {
44
+ // Loop through keys...
45
+ for (var key in obj) {
46
+ // Check to see if this is a prototypical key/value,
47
+ // if it is, skip it.
48
+ if (!Object.prototype.hasOwnProperty.call(obj, key)) {
49
+ continue
50
+ }
51
+ var value = obj[key];
52
+ var prefixedKey = prefix ? prefix + '.' + key : key;
53
+ if (typeof value === 'object') {
54
+ // If the value is a nested object, recurse over that too
55
+ flattenLoop(value, prefixedKey);
56
+ } else {
57
+ // Otherwise, add this value to our return object
58
+ flattenedObject[prefixedKey] = value;
59
+ }
60
+ }
61
+ };
62
+
63
+ // Kick off the recursive loop
64
+ flattenLoop(configObject);
65
+ return flattenedObject
66
+ };
67
+
68
+ // Start with an empty object as our base
69
+ var formattedConfigObject = {};
70
+
71
+ // Loop through each of the remaining passed objects and push their keys
72
+ // one-by-one into configObject. Any duplicate keys will override the existing
73
+ // key with the new value.
74
+ for (var i = 0; i < arguments.length; i++) {
75
+ var obj = flattenObject(arguments[i]);
76
+ for (var key in obj) {
77
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
78
+ formattedConfigObject[key] = obj[key];
79
+ }
80
+ }
81
+ }
82
+
83
+ return formattedConfigObject
84
+ }
85
+
86
+ /**
87
+ * @callback nodeListIterator
88
+ * @param {Element} value - The current node being iterated on
89
+ * @param {number} index - The current index in the iteration
90
+ * @param {NodeListOf<Element>} nodes - NodeList from querySelectorAll()
91
+ * @returns {undefined}
92
+ */
93
+
7
94
  (function(undefined) {
8
95
 
9
- // Detection from https://github.com/Financial-Times/polyfill-service/blob/master/packages/polyfill-library/polyfills/Window/detect.js
10
- var detect = ('Window' in this);
96
+ // Detection from https://github.com/Financial-Times/polyfill-service/blob/master/packages/polyfill-library/polyfills/Object/defineProperty/detect.js
97
+ var detect = (
98
+ // In IE8, defineProperty could only act on DOM elements, so full support
99
+ // for the feature requires the ability to set a property on an arbitrary object
100
+ 'defineProperty' in Object && (function() {
101
+ try {
102
+ var a = {};
103
+ Object.defineProperty(a, 'test', {value:42});
104
+ return true;
105
+ } catch(e) {
106
+ return false
107
+ }
108
+ }())
109
+ );
11
110
 
12
111
  if (detect) return
13
112
 
14
- // Polyfill from https://cdn.polyfill.io/v2/polyfill.js?features=Window&flags=always
15
- if ((typeof WorkerGlobalScope === "undefined") && (typeof importScripts !== "function")) {
16
- (function (global) {
17
- if (global.constructor) {
18
- global.Window = global.constructor;
113
+ // Polyfill from https://cdn.polyfill.io/v2/polyfill.js?features=Object.defineProperty&flags=always
114
+ (function (nativeDefineProperty) {
115
+
116
+ var supportsAccessors = Object.prototype.hasOwnProperty('__defineGetter__');
117
+ var ERR_ACCESSORS_NOT_SUPPORTED = 'Getters & setters cannot be defined on this javascript engine';
118
+ var ERR_VALUE_ACCESSORS = 'A property cannot both have accessors and be writable or have a value';
119
+
120
+ Object.defineProperty = function defineProperty(object, property, descriptor) {
121
+
122
+ // Where native support exists, assume it
123
+ if (nativeDefineProperty && (object === window || object === document || object === Element.prototype || object instanceof Element)) {
124
+ return nativeDefineProperty(object, property, descriptor);
125
+ }
126
+
127
+ if (object === null || !(object instanceof Object || typeof object === 'object')) {
128
+ throw new TypeError('Object.defineProperty called on non-object');
129
+ }
130
+
131
+ if (!(descriptor instanceof Object)) {
132
+ throw new TypeError('Property description must be an object');
133
+ }
134
+
135
+ var propertyString = String(property);
136
+ var hasValueOrWritable = 'value' in descriptor || 'writable' in descriptor;
137
+ var getterType = 'get' in descriptor && typeof descriptor.get;
138
+ var setterType = 'set' in descriptor && typeof descriptor.set;
139
+
140
+ // handle descriptor.get
141
+ if (getterType) {
142
+ if (getterType !== 'function') {
143
+ throw new TypeError('Getter must be a function');
144
+ }
145
+ if (!supportsAccessors) {
146
+ throw new TypeError(ERR_ACCESSORS_NOT_SUPPORTED);
147
+ }
148
+ if (hasValueOrWritable) {
149
+ throw new TypeError(ERR_VALUE_ACCESSORS);
150
+ }
151
+ Object.__defineGetter__.call(object, propertyString, descriptor.get);
19
152
  } else {
20
- (global.Window = global.constructor = new Function('return function Window() {}')()).prototype = this;
153
+ object[propertyString] = descriptor.value;
21
154
  }
22
- }(this));
23
- }
24
155
 
156
+ // handle descriptor.set
157
+ if (setterType) {
158
+ if (setterType !== 'function') {
159
+ throw new TypeError('Setter must be a function');
160
+ }
161
+ if (!supportsAccessors) {
162
+ throw new TypeError(ERR_ACCESSORS_NOT_SUPPORTED);
163
+ }
164
+ if (hasValueOrWritable) {
165
+ throw new TypeError(ERR_VALUE_ACCESSORS);
166
+ }
167
+ Object.__defineSetter__.call(object, propertyString, descriptor.set);
168
+ }
169
+
170
+ // OK to define value unconditionally - if a getter has been specified as well, an error would be thrown above
171
+ if ('value' in descriptor) {
172
+ object[propertyString] = descriptor.value;
173
+ }
174
+
175
+ return object;
176
+ };
177
+ }(Object.defineProperty));
25
178
  })
26
179
  .call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
27
180
 
@@ -167,88 +320,158 @@ if (detect) return
167
320
 
168
321
  (function(undefined) {
169
322
 
170
- // Detection from https://github.com/Financial-Times/polyfill-service/blob/master/packages/polyfill-library/polyfills/Object/defineProperty/detect.js
171
- var detect = (
172
- // In IE8, defineProperty could only act on DOM elements, so full support
173
- // for the feature requires the ability to set a property on an arbitrary object
174
- 'defineProperty' in Object && (function() {
175
- try {
176
- var a = {};
177
- Object.defineProperty(a, 'test', {value:42});
178
- return true;
179
- } catch(e) {
180
- return false
181
- }
182
- }())
183
- );
323
+ // Detection from https://raw.githubusercontent.com/Financial-Times/polyfill-library/13cf7c340974d128d557580b5e2dafcd1b1192d1/polyfills/Element/prototype/dataset/detect.js
324
+ var detect = (function(){
325
+ if (!document.documentElement.dataset) {
326
+ return false;
327
+ }
328
+ var el = document.createElement('div');
329
+ el.setAttribute("data-a-b", "c");
330
+ return el.dataset && el.dataset.aB == "c";
331
+ }());
184
332
 
185
- if (detect) return
333
+ if (detect) return
186
334
 
187
- // Polyfill from https://cdn.polyfill.io/v2/polyfill.js?features=Object.defineProperty&flags=always
188
- (function (nativeDefineProperty) {
335
+ // Polyfill derived from https://raw.githubusercontent.com/Financial-Times/polyfill-library/13cf7c340974d128d557580b5e2dafcd1b1192d1/polyfills/Element/prototype/dataset/polyfill.js
336
+ Object.defineProperty(Element.prototype, 'dataset', {
337
+ get: function() {
338
+ var element = this;
339
+ var attributes = this.attributes;
340
+ var map = {};
341
+
342
+ for (var i = 0; i < attributes.length; i++) {
343
+ var attribute = attributes[i];
344
+
345
+ // This regex has been edited from the original polyfill, to add
346
+ // support for period (.) separators in data-* attribute names. These
347
+ // are allowed in the HTML spec, but were not covered by the original
348
+ // polyfill's regex. We use periods in our i18n implementation.
349
+ if (attribute && attribute.name && (/^data-\w[.\w-]*$/).test(attribute.name)) {
350
+ var name = attribute.name;
351
+ var value = attribute.value;
352
+
353
+ var propName = name.substr(5).replace(/-./g, function (prop) {
354
+ return prop.charAt(1).toUpperCase();
355
+ });
356
+
357
+ // If this browser supports __defineGetter__ and __defineSetter__,
358
+ // continue using defineProperty. If not (like IE 8 and below), we use
359
+ // a hacky fallback which at least gives an object in the right format
360
+ if ('__defineGetter__' in Object.prototype && '__defineSetter__' in Object.prototype) {
361
+ Object.defineProperty(map, propName, {
362
+ enumerable: true,
363
+ get: function() {
364
+ return this.value;
365
+ }.bind({value: value || ''}),
366
+ set: function setter(name, value) {
367
+ if (typeof value !== 'undefined') {
368
+ this.setAttribute(name, value);
369
+ } else {
370
+ this.removeAttribute(name);
371
+ }
372
+ }.bind(element, name)
373
+ });
374
+ } else {
375
+ map[propName] = value;
376
+ }
189
377
 
190
- var supportsAccessors = Object.prototype.hasOwnProperty('__defineGetter__');
191
- var ERR_ACCESSORS_NOT_SUPPORTED = 'Getters & setters cannot be defined on this javascript engine';
192
- var ERR_VALUE_ACCESSORS = 'A property cannot both have accessors and be writable or have a value';
378
+ }
379
+ }
380
+
381
+ return map;
382
+ }
383
+ });
193
384
 
194
- Object.defineProperty = function defineProperty(object, property, descriptor) {
385
+ }).call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
195
386
 
196
- // Where native support exists, assume it
197
- if (nativeDefineProperty && (object === window || object === document || object === Element.prototype || object instanceof Element)) {
198
- return nativeDefineProperty(object, property, descriptor);
199
- }
387
+ (function(undefined) {
200
388
 
201
- if (object === null || !(object instanceof Object || typeof object === 'object')) {
202
- throw new TypeError('Object.defineProperty called on non-object');
203
- }
389
+ // Detection from https://github.com/mdn/content/blob/cf607d68522cd35ee7670782d3ee3a361eaef2e4/files/en-us/web/javascript/reference/global_objects/string/trim/index.md#polyfill
390
+ var detect = ('trim' in String.prototype);
391
+
392
+ if (detect) return
204
393
 
205
- if (!(descriptor instanceof Object)) {
206
- throw new TypeError('Property description must be an object');
207
- }
394
+ // Polyfill from https://github.com/mdn/content/blob/cf607d68522cd35ee7670782d3ee3a361eaef2e4/files/en-us/web/javascript/reference/global_objects/string/trim/index.md#polyfill
395
+ String.prototype.trim = function () {
396
+ return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
397
+ };
208
398
 
209
- var propertyString = String(property);
210
- var hasValueOrWritable = 'value' in descriptor || 'writable' in descriptor;
211
- var getterType = 'get' in descriptor && typeof descriptor.get;
212
- var setterType = 'set' in descriptor && typeof descriptor.set;
399
+ }).call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
213
400
 
214
- // handle descriptor.get
215
- if (getterType) {
216
- if (getterType !== 'function') {
217
- throw new TypeError('Getter must be a function');
218
- }
219
- if (!supportsAccessors) {
220
- throw new TypeError(ERR_ACCESSORS_NOT_SUPPORTED);
221
- }
222
- if (hasValueOrWritable) {
223
- throw new TypeError(ERR_VALUE_ACCESSORS);
224
- }
225
- Object.__defineGetter__.call(object, propertyString, descriptor.get);
226
- } else {
227
- object[propertyString] = descriptor.value;
228
- }
401
+ /**
402
+ * Normalise string
403
+ *
404
+ * 'If it looks like a duck, and it quacks like a duck…' 🦆
405
+ *
406
+ * If the passed value looks like a boolean or a number, convert it to a boolean
407
+ * or number.
408
+ *
409
+ * Designed to be used to convert config passed via data attributes (which are
410
+ * always strings) into something sensible.
411
+ *
412
+ * @param {string} value - The value to normalise
413
+ * @returns {string | boolean | number | undefined} Normalised data
414
+ */
415
+ function normaliseString (value) {
416
+ if (typeof value !== 'string') {
417
+ return value
418
+ }
229
419
 
230
- // handle descriptor.set
231
- if (setterType) {
232
- if (setterType !== 'function') {
233
- throw new TypeError('Setter must be a function');
234
- }
235
- if (!supportsAccessors) {
236
- throw new TypeError(ERR_ACCESSORS_NOT_SUPPORTED);
237
- }
238
- if (hasValueOrWritable) {
239
- throw new TypeError(ERR_VALUE_ACCESSORS);
240
- }
241
- Object.__defineSetter__.call(object, propertyString, descriptor.set);
242
- }
420
+ var trimmedValue = value.trim();
243
421
 
244
- // OK to define value unconditionally - if a getter has been specified as well, an error would be thrown above
245
- if ('value' in descriptor) {
246
- object[propertyString] = descriptor.value;
422
+ if (trimmedValue === 'true') {
423
+ return true
424
+ }
425
+
426
+ if (trimmedValue === 'false') {
427
+ return false
428
+ }
429
+
430
+ // Empty / whitespace-only strings are considered finite so we need to check
431
+ // the length of the trimmed string as well
432
+ if (trimmedValue.length > 0 && isFinite(trimmedValue)) {
433
+ return Number(trimmedValue)
434
+ }
435
+
436
+ return value
437
+ }
438
+
439
+ /**
440
+ * Normalise dataset
441
+ *
442
+ * Loop over an object and normalise each value using normaliseData function
443
+ *
444
+ * @param {DOMStringMap} dataset - HTML element dataset
445
+ * @returns {Object<string, string | boolean | number | undefined>} Normalised dataset
446
+ */
447
+ function normaliseDataset (dataset) {
448
+ var out = {};
449
+
450
+ for (var key in dataset) {
451
+ out[key] = normaliseString(dataset[key]);
452
+ }
453
+
454
+ return out
455
+ }
456
+
457
+ (function(undefined) {
458
+
459
+ // Detection from https://github.com/Financial-Times/polyfill-service/blob/master/packages/polyfill-library/polyfills/Window/detect.js
460
+ var detect = ('Window' in this);
461
+
462
+ if (detect) return
463
+
464
+ // Polyfill from https://cdn.polyfill.io/v2/polyfill.js?features=Window&flags=always
465
+ if ((typeof WorkerGlobalScope === "undefined") && (typeof importScripts !== "function")) {
466
+ (function (global) {
467
+ if (global.constructor) {
468
+ global.Window = global.constructor;
469
+ } else {
470
+ (global.Window = global.constructor = new Function('return function Window() {}')()).prototype = this;
247
471
  }
472
+ }(this));
473
+ }
248
474
 
249
- return object;
250
- };
251
- }(Object.defineProperty));
252
475
  })
253
476
  .call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
254
477
 
@@ -662,44 +885,79 @@ if (detect) return
662
885
  var KEY_SPACE = 32;
663
886
  var DEBOUNCE_TIMEOUT_IN_SECONDS = 1;
664
887
 
665
- function Button ($module) {
888
+ /**
889
+ * JavaScript enhancements for the Button component
890
+ *
891
+ * @class
892
+ * @param {HTMLElement} $module - The element this component controls
893
+ * @param {ButtonConfig} config - Button config
894
+ */
895
+ function Button ($module, config) {
896
+ if (!$module) {
897
+ return this
898
+ }
899
+
666
900
  this.$module = $module;
667
901
  this.debounceFormSubmitTimer = null;
902
+
903
+ var defaultConfig = {
904
+ preventDoubleClick: false
905
+ };
906
+ this.config = mergeConfigs(
907
+ defaultConfig,
908
+ config || {},
909
+ normaliseDataset($module.dataset)
910
+ );
668
911
  }
669
912
 
670
913
  /**
671
- * JavaScript 'shim' to trigger the click event of element(s) when the space key is pressed.
672
- *
673
- * Created since some Assistive Technologies (for example some Screenreaders)
674
- * will tell a user to press space on a 'button', so this functionality needs to be shimmed
675
- * See https://github.com/alphagov/govuk_elements/pull/272#issuecomment-233028270
676
- *
677
- * @param {object} event event
678
- */
914
+ * Initialise component
915
+ */
916
+ Button.prototype.init = function () {
917
+ if (!this.$module) {
918
+ return
919
+ }
920
+
921
+ this.$module.addEventListener('keydown', this.handleKeyDown);
922
+ this.$module.addEventListener('click', this.debounce.bind(this));
923
+ };
924
+
925
+ /**
926
+ * Trigger a click event when the space key is pressed
927
+ *
928
+ * Some screen readers tell users they can activate things with the 'button'
929
+ * role, so we need to match the functionality of native HTML buttons
930
+ *
931
+ * See https://github.com/alphagov/govuk_elements/pull/272#issuecomment-233028270
932
+ *
933
+ * @param {KeyboardEvent} event
934
+ */
679
935
  Button.prototype.handleKeyDown = function (event) {
680
- // get the target element
681
936
  var target = event.target;
682
- // if the element has a role='button' and the pressed key is a space, we'll simulate a click
937
+
683
938
  if (target.getAttribute('role') === 'button' && event.keyCode === KEY_SPACE) {
684
- event.preventDefault();
685
- // trigger the target's click event
939
+ event.preventDefault(); // prevent the page from scrolling
686
940
  target.click();
687
941
  }
688
942
  };
689
943
 
690
944
  /**
691
- * If the click quickly succeeds a previous click then nothing will happen.
692
- * This stops people accidentally causing multiple form submissions by
693
- * double clicking buttons.
694
- */
945
+ * Debounce double-clicks
946
+ *
947
+ * If the click quickly succeeds a previous click then nothing will happen. This
948
+ * stops people accidentally causing multiple form submissions by double
949
+ * clicking buttons.
950
+ *
951
+ * @param {MouseEvent} event
952
+ * @returns {undefined | false} - Returns undefined, or false when debounced
953
+ */
695
954
  Button.prototype.debounce = function (event) {
696
- var target = event.target;
697
- // Check the button that is clicked on has the preventDoubleClick feature enabled
698
- if (target.getAttribute('data-prevent-double-click') !== 'true') {
955
+ // Check the button that was clicked has preventDoubleClick enabled
956
+ if (!this.config.preventDoubleClick) {
699
957
  return
700
958
  }
701
959
 
702
- // If the timer is still running then we want to prevent the click from submitting the form
960
+ // If the timer is still running, prevent the click from submitting the form
703
961
  if (this.debounceFormSubmitTimer) {
704
962
  event.preventDefault();
705
963
  return false
@@ -711,13 +969,13 @@ Button.prototype.debounce = function (event) {
711
969
  };
712
970
 
713
971
  /**
714
- * Initialise an event listener for keydown at document level
715
- * this will help listening for later inserted elements with a role="button"
716
- */
717
- Button.prototype.init = function () {
718
- this.$module.addEventListener('keydown', this.handleKeyDown);
719
- this.$module.addEventListener('click', this.debounce);
720
- };
972
+ * Button config
973
+ *
974
+ * @typedef {object} ButtonConfig
975
+ * @property {boolean} [preventDoubleClick = false] -
976
+ * Prevent accidental double clicks on submit buttons from submitting forms
977
+ * multiple times.
978
+ */
721
979
 
722
980
  return Button;
723
981