govuk_publishing_components 55.0.1 → 55.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/govuk_publishing_components/components/file-upload.js +5 -0
  3. data/app/assets/stylesheets/govuk_publishing_components/components/_layout-super-navigation-header.scss +1 -0
  4. data/app/assets/stylesheets/govuk_publishing_components/components/_organisation-logo.scss +5 -2
  5. data/app/views/govuk_publishing_components/components/_file_upload.html.erb +13 -1
  6. data/app/views/govuk_publishing_components/components/_layout_super_navigation_header.html.erb +0 -1
  7. data/app/views/govuk_publishing_components/components/docs/file_upload.yml +6 -0
  8. data/app/views/govuk_publishing_components/components/feedback/_survey_signup_form.html.erb +1 -1
  9. data/lib/govuk_publishing_components/version.rb +1 -1
  10. data/node_modules/govuk-frontend/dist/govuk/all.bundle.js +313 -47
  11. data/node_modules/govuk-frontend/dist/govuk/all.bundle.js.map +1 -1
  12. data/node_modules/govuk-frontend/dist/govuk/all.bundle.mjs +312 -47
  13. data/node_modules/govuk-frontend/dist/govuk/all.bundle.mjs.map +1 -1
  14. data/node_modules/govuk-frontend/dist/govuk/all.mjs +2 -1
  15. data/node_modules/govuk-frontend/dist/govuk/all.mjs.map +1 -1
  16. data/node_modules/govuk-frontend/dist/govuk/common/configuration.mjs +21 -16
  17. data/node_modules/govuk-frontend/dist/govuk/common/configuration.mjs.map +1 -1
  18. data/node_modules/govuk-frontend/dist/govuk/common/govuk-frontend-version.mjs +1 -1
  19. data/node_modules/govuk-frontend/dist/govuk/common/index.mjs +3 -0
  20. data/node_modules/govuk-frontend/dist/govuk/common/index.mjs.map +1 -1
  21. data/node_modules/govuk-frontend/dist/govuk/{govuk-frontend-component.mjs → component.mjs} +5 -5
  22. data/node_modules/govuk-frontend/dist/govuk/component.mjs.map +1 -0
  23. data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.js +27 -19
  24. data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.js.map +1 -1
  25. data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.mjs +27 -19
  26. data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.mjs.map +1 -1
  27. data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.mjs +1 -1
  28. data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.mjs.map +1 -1
  29. data/node_modules/govuk-frontend/dist/govuk/components/button/button.bundle.js +27 -19
  30. data/node_modules/govuk-frontend/dist/govuk/components/button/button.bundle.js.map +1 -1
  31. data/node_modules/govuk-frontend/dist/govuk/components/button/button.bundle.mjs +27 -19
  32. data/node_modules/govuk-frontend/dist/govuk/components/button/button.bundle.mjs.map +1 -1
  33. data/node_modules/govuk-frontend/dist/govuk/components/button/button.mjs +1 -1
  34. data/node_modules/govuk-frontend/dist/govuk/components/button/button.mjs.map +1 -1
  35. data/node_modules/govuk-frontend/dist/govuk/components/button/macro-options.json +2 -1
  36. data/node_modules/govuk-frontend/dist/govuk/components/character-count/_index.scss +8 -0
  37. data/node_modules/govuk-frontend/dist/govuk/components/character-count/_index.scss.map +1 -1
  38. data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.js +28 -20
  39. data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.js.map +1 -1
  40. data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.mjs +28 -20
  41. data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.mjs.map +1 -1
  42. data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.mjs +2 -2
  43. data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.mjs.map +1 -1
  44. data/node_modules/govuk-frontend/dist/govuk/components/character-count/fixtures.json +16 -1
  45. data/node_modules/govuk-frontend/dist/govuk/components/character-count/macro-options.json +2 -2
  46. data/node_modules/govuk-frontend/dist/govuk/components/character-count/template.njk +5 -4
  47. data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.bundle.js +8 -5
  48. data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.bundle.js.map +1 -1
  49. data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.bundle.mjs +8 -5
  50. data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.bundle.mjs.map +1 -1
  51. data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.mjs +2 -2
  52. data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.mjs.map +1 -1
  53. data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.bundle.js +27 -19
  54. data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.bundle.js.map +1 -1
  55. data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.bundle.mjs +27 -19
  56. data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.bundle.mjs.map +1 -1
  57. data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.mjs +1 -1
  58. data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.mjs.map +1 -1
  59. data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.js +27 -19
  60. data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.js.map +1 -1
  61. data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.mjs +27 -19
  62. data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.mjs.map +1 -1
  63. data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.mjs +1 -1
  64. data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.mjs.map +1 -1
  65. data/node_modules/govuk-frontend/dist/govuk/components/file-upload/_index.scss +167 -0
  66. data/node_modules/govuk-frontend/dist/govuk/components/file-upload/_index.scss.map +1 -1
  67. data/node_modules/govuk-frontend/dist/govuk/components/file-upload/file-upload.bundle.js +754 -0
  68. data/node_modules/govuk-frontend/dist/govuk/components/file-upload/file-upload.bundle.js.map +1 -0
  69. data/node_modules/govuk-frontend/dist/govuk/components/file-upload/file-upload.bundle.mjs +746 -0
  70. data/node_modules/govuk-frontend/dist/govuk/components/file-upload/file-upload.bundle.mjs.map +1 -0
  71. data/node_modules/govuk-frontend/dist/govuk/components/file-upload/file-upload.mjs +267 -0
  72. data/node_modules/govuk-frontend/dist/govuk/components/file-upload/file-upload.mjs.map +1 -0
  73. data/node_modules/govuk-frontend/dist/govuk/components/file-upload/fixtures.json +207 -16
  74. data/node_modules/govuk-frontend/dist/govuk/components/file-upload/macro-options.json +52 -3
  75. data/node_modules/govuk-frontend/dist/govuk/components/file-upload/template-allows-direct-media-capture.html +6 -0
  76. data/node_modules/govuk-frontend/dist/govuk/components/file-upload/template-allows-image-files-only.html +6 -0
  77. data/node_modules/govuk-frontend/dist/govuk/components/file-upload/template-allows-multiple-files.html +6 -0
  78. data/node_modules/govuk-frontend/dist/govuk/components/file-upload/template-disabled.html +6 -0
  79. data/node_modules/govuk-frontend/dist/govuk/components/file-upload/template-enhanced-disabled.html +13 -0
  80. data/node_modules/govuk-frontend/dist/govuk/components/file-upload/template-enhanced-with-error-message-and-hint.html +16 -0
  81. data/node_modules/govuk-frontend/dist/govuk/components/file-upload/template-enhanced.html +10 -0
  82. data/node_modules/govuk-frontend/dist/govuk/components/file-upload/template-translated.html +10 -0
  83. data/node_modules/govuk-frontend/dist/govuk/components/file-upload/template.njk +42 -5
  84. data/node_modules/govuk-frontend/dist/govuk/components/header/_index.scss +14 -10
  85. data/node_modules/govuk-frontend/dist/govuk/components/header/_index.scss.map +1 -1
  86. data/node_modules/govuk-frontend/dist/govuk/components/header/header.bundle.js +8 -5
  87. data/node_modules/govuk-frontend/dist/govuk/components/header/header.bundle.js.map +1 -1
  88. data/node_modules/govuk-frontend/dist/govuk/components/header/header.bundle.mjs +8 -5
  89. data/node_modules/govuk-frontend/dist/govuk/components/header/header.bundle.mjs.map +1 -1
  90. data/node_modules/govuk-frontend/dist/govuk/components/header/header.mjs +2 -2
  91. data/node_modules/govuk-frontend/dist/govuk/components/header/header.mjs.map +1 -1
  92. data/node_modules/govuk-frontend/dist/govuk/components/header/macro-options.json +25 -12
  93. data/node_modules/govuk-frontend/dist/govuk/components/input/fixtures.json +16 -2
  94. data/node_modules/govuk-frontend/dist/govuk/components/input/macro-options.json +2 -2
  95. data/node_modules/govuk-frontend/dist/govuk/components/input/template-default.html +2 -2
  96. data/node_modules/govuk-frontend/dist/govuk/components/input/template.njk +5 -4
  97. data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.bundle.js +27 -19
  98. data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.bundle.js.map +1 -1
  99. data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.bundle.mjs +27 -19
  100. data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.bundle.mjs.map +1 -1
  101. data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.mjs +1 -1
  102. data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.mjs.map +1 -1
  103. data/node_modules/govuk-frontend/dist/govuk/components/password-input/fixtures.json +16 -2
  104. data/node_modules/govuk-frontend/dist/govuk/components/password-input/macro-options.json +2 -2
  105. data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.bundle.js +27 -20
  106. data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.bundle.js.map +1 -1
  107. data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.bundle.mjs +27 -20
  108. data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.bundle.mjs.map +1 -1
  109. data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.mjs +1 -2
  110. data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.mjs.map +1 -1
  111. data/node_modules/govuk-frontend/dist/govuk/components/password-input/template-default.html +3 -3
  112. data/node_modules/govuk-frontend/dist/govuk/components/password-input/template.njk +4 -2
  113. data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.bundle.js +8 -5
  114. data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.bundle.js.map +1 -1
  115. data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.bundle.mjs +8 -5
  116. data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.bundle.mjs.map +1 -1
  117. data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.mjs +2 -2
  118. data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.mjs.map +1 -1
  119. data/node_modules/govuk-frontend/dist/govuk/components/select/fixtures.json +16 -1
  120. data/node_modules/govuk-frontend/dist/govuk/components/select/macro-options.json +2 -2
  121. data/node_modules/govuk-frontend/dist/govuk/components/select/template-id.html +7 -0
  122. data/node_modules/govuk-frontend/dist/govuk/components/select/template.njk +6 -4
  123. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.bundle.js +8 -5
  124. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.bundle.js.map +1 -1
  125. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.bundle.mjs +8 -5
  126. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.bundle.mjs.map +1 -1
  127. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.mjs +2 -2
  128. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.mjs.map +1 -1
  129. data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.bundle.js +9 -6
  130. data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.bundle.js.map +1 -1
  131. data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.bundle.mjs +9 -6
  132. data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.bundle.mjs.map +1 -1
  133. data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.mjs +3 -3
  134. data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.mjs.map +1 -1
  135. data/node_modules/govuk-frontend/dist/govuk/components/summary-list/_index.scss +12 -21
  136. data/node_modules/govuk-frontend/dist/govuk/components/summary-list/_index.scss.map +1 -1
  137. data/node_modules/govuk-frontend/dist/govuk/components/summary-list/fixtures.json +142 -0
  138. data/node_modules/govuk-frontend/dist/govuk/components/summary-list/template-as-a-summary-card-extreme.html +106 -0
  139. data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.bundle.js +8 -5
  140. data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.bundle.js.map +1 -1
  141. data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.bundle.mjs +8 -5
  142. data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.bundle.mjs.map +1 -1
  143. data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.mjs +2 -2
  144. data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.mjs.map +1 -1
  145. data/node_modules/govuk-frontend/dist/govuk/components/textarea/fixtures.json +15 -1
  146. data/node_modules/govuk-frontend/dist/govuk/components/textarea/macro-options.json +2 -2
  147. data/node_modules/govuk-frontend/dist/govuk/components/textarea/template.njk +6 -4
  148. data/node_modules/govuk-frontend/dist/govuk/core/_govuk-frontend-properties.scss +1 -1
  149. data/node_modules/govuk-frontend/dist/govuk/errors/index.mjs +1 -1
  150. data/node_modules/govuk-frontend/dist/govuk/errors/index.mjs.map +1 -1
  151. data/node_modules/govuk-frontend/dist/govuk/govuk-frontend.min.css +2 -2
  152. data/node_modules/govuk-frontend/dist/govuk/govuk-frontend.min.css.map +1 -1
  153. data/node_modules/govuk-frontend/dist/govuk/govuk-frontend.min.js +1 -1
  154. data/node_modules/govuk-frontend/dist/govuk/govuk-frontend.min.js.map +1 -1
  155. data/node_modules/govuk-frontend/dist/govuk/helpers/_colour.scss +2 -2
  156. data/node_modules/govuk-frontend/dist/govuk/helpers/_colour.scss.map +1 -1
  157. data/node_modules/govuk-frontend/dist/govuk/init.mjs +11 -11
  158. data/node_modules/govuk-frontend/dist/govuk/init.mjs.map +1 -1
  159. data/node_modules/govuk-frontend/dist/govuk/settings/_colours-organisations.scss +18 -5
  160. data/node_modules/govuk-frontend/dist/govuk/settings/_colours-organisations.scss.map +1 -1
  161. data/node_modules/govuk-frontend/dist/govuk-prototype-kit/init.scss +1 -1
  162. data/node_modules/govuk-frontend/dist/govuk-prototype-kit/init.scss.map +1 -1
  163. data/node_modules/govuk-frontend/govuk-prototype-kit.config.json +1 -1
  164. data/node_modules/govuk-frontend/package.json +7 -7
  165. metadata +21 -5
  166. data/node_modules/govuk-frontend/dist/govuk/components/file-upload/template-with-value.html +0 -6
  167. data/node_modules/govuk-frontend/dist/govuk/govuk-frontend-component.mjs.map +0 -1
@@ -0,0 +1,746 @@
1
+ function closestAttributeValue($element, attributeName) {
2
+ const $closestElementWithAttribute = $element.closest(`[${attributeName}]`);
3
+ return $closestElementWithAttribute ? $closestElementWithAttribute.getAttribute(attributeName) : null;
4
+ }
5
+
6
+ function isInitialised($root, moduleName) {
7
+ return $root instanceof HTMLElement && $root.hasAttribute(`data-${moduleName}-init`);
8
+ }
9
+
10
+ /**
11
+ * Checks if GOV.UK Frontend is supported on this page
12
+ *
13
+ * Some browsers will load and run our JavaScript but GOV.UK Frontend
14
+ * won't be supported.
15
+ *
16
+ * @param {HTMLElement | null} [$scope] - (internal) `<body>` HTML element checked for browser support
17
+ * @returns {boolean} Whether GOV.UK Frontend is supported on this page
18
+ */
19
+ function isSupported($scope = document.body) {
20
+ if (!$scope) {
21
+ return false;
22
+ }
23
+ return $scope.classList.contains('govuk-frontend-supported');
24
+ }
25
+ function isArray(option) {
26
+ return Array.isArray(option);
27
+ }
28
+ function isObject(option) {
29
+ return !!option && typeof option === 'object' && !isArray(option);
30
+ }
31
+ function formatErrorMessage(Component, message) {
32
+ return `${Component.moduleName}: ${message}`;
33
+ }
34
+ /**
35
+ * @typedef ComponentWithModuleName
36
+ * @property {string} moduleName - Name of the component
37
+ */
38
+ /**
39
+ * @import { ObjectNested } from './configuration.mjs'
40
+ */
41
+
42
+ class GOVUKFrontendError extends Error {
43
+ constructor(...args) {
44
+ super(...args);
45
+ this.name = 'GOVUKFrontendError';
46
+ }
47
+ }
48
+ class SupportError extends GOVUKFrontendError {
49
+ /**
50
+ * Checks if GOV.UK Frontend is supported on this page
51
+ *
52
+ * @param {HTMLElement | null} [$scope] - HTML element `<body>` checked for browser support
53
+ */
54
+ constructor($scope = document.body) {
55
+ const supportMessage = 'noModule' in HTMLScriptElement.prototype ? 'GOV.UK Frontend initialised without `<body class="govuk-frontend-supported">` from template `<script>` snippet' : 'GOV.UK Frontend is not supported in this browser';
56
+ super($scope ? supportMessage : 'GOV.UK Frontend initialised without `<script type="module">`');
57
+ this.name = 'SupportError';
58
+ }
59
+ }
60
+ class ConfigError extends GOVUKFrontendError {
61
+ constructor(...args) {
62
+ super(...args);
63
+ this.name = 'ConfigError';
64
+ }
65
+ }
66
+ class ElementError extends GOVUKFrontendError {
67
+ constructor(messageOrOptions) {
68
+ let message = typeof messageOrOptions === 'string' ? messageOrOptions : '';
69
+ if (typeof messageOrOptions === 'object') {
70
+ const {
71
+ component,
72
+ identifier,
73
+ element,
74
+ expectedType
75
+ } = messageOrOptions;
76
+ message = identifier;
77
+ message += element ? ` is not of type ${expectedType != null ? expectedType : 'HTMLElement'}` : ' not found';
78
+ message = formatErrorMessage(component, message);
79
+ }
80
+ super(message);
81
+ this.name = 'ElementError';
82
+ }
83
+ }
84
+ class InitError extends GOVUKFrontendError {
85
+ constructor(componentOrMessage) {
86
+ const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
87
+ super(message);
88
+ this.name = 'InitError';
89
+ }
90
+ }
91
+ /**
92
+ * @import { ComponentWithModuleName } from '../common/index.mjs'
93
+ */
94
+
95
+ class Component {
96
+ /**
97
+ * Returns the root element of the component
98
+ *
99
+ * @protected
100
+ * @returns {RootElementType} - the root element of component
101
+ */
102
+ get $root() {
103
+ return this._$root;
104
+ }
105
+ constructor($root) {
106
+ this._$root = void 0;
107
+ const childConstructor = this.constructor;
108
+ if (typeof childConstructor.moduleName !== 'string') {
109
+ throw new InitError(`\`moduleName\` not defined in component`);
110
+ }
111
+ if (!($root instanceof childConstructor.elementType)) {
112
+ throw new ElementError({
113
+ element: $root,
114
+ component: childConstructor,
115
+ identifier: 'Root element (`$root`)',
116
+ expectedType: childConstructor.elementType.name
117
+ });
118
+ } else {
119
+ this._$root = $root;
120
+ }
121
+ childConstructor.checkSupport();
122
+ this.checkInitialised();
123
+ const moduleName = childConstructor.moduleName;
124
+ this.$root.setAttribute(`data-${moduleName}-init`, '');
125
+ }
126
+ checkInitialised() {
127
+ const constructor = this.constructor;
128
+ const moduleName = constructor.moduleName;
129
+ if (moduleName && isInitialised(this.$root, moduleName)) {
130
+ throw new InitError(constructor);
131
+ }
132
+ }
133
+ static checkSupport() {
134
+ if (!isSupported()) {
135
+ throw new SupportError();
136
+ }
137
+ }
138
+ }
139
+
140
+ /**
141
+ * @typedef ChildClass
142
+ * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
143
+ */
144
+
145
+ /**
146
+ * @typedef {typeof Component & ChildClass} ChildClassConstructor
147
+ */
148
+ Component.elementType = HTMLElement;
149
+
150
+ const configOverride = Symbol.for('configOverride');
151
+ class ConfigurableComponent extends Component {
152
+ [configOverride](param) {
153
+ return {};
154
+ }
155
+
156
+ /**
157
+ * Returns the root element of the component
158
+ *
159
+ * @protected
160
+ * @returns {ConfigurationType} - the root element of component
161
+ */
162
+ get config() {
163
+ return this._config;
164
+ }
165
+ constructor($root, config) {
166
+ super($root);
167
+ this._config = void 0;
168
+ const childConstructor = this.constructor;
169
+ if (!isObject(childConstructor.defaults)) {
170
+ throw new ConfigError(formatErrorMessage(childConstructor, 'Config passed as parameter into constructor but no defaults defined'));
171
+ }
172
+ const datasetConfig = normaliseDataset(childConstructor, this._$root.dataset);
173
+ this._config = mergeConfigs(childConstructor.defaults, config != null ? config : {}, this[configOverride](datasetConfig), datasetConfig);
174
+ }
175
+ }
176
+ function normaliseString(value, property) {
177
+ const trimmedValue = value ? value.trim() : '';
178
+ let output;
179
+ let outputType = property == null ? void 0 : property.type;
180
+ if (!outputType) {
181
+ if (['true', 'false'].includes(trimmedValue)) {
182
+ outputType = 'boolean';
183
+ }
184
+ if (trimmedValue.length > 0 && isFinite(Number(trimmedValue))) {
185
+ outputType = 'number';
186
+ }
187
+ }
188
+ switch (outputType) {
189
+ case 'boolean':
190
+ output = trimmedValue === 'true';
191
+ break;
192
+ case 'number':
193
+ output = Number(trimmedValue);
194
+ break;
195
+ default:
196
+ output = value;
197
+ }
198
+ return output;
199
+ }
200
+ function normaliseDataset(Component, dataset) {
201
+ if (!isObject(Component.schema)) {
202
+ throw new ConfigError(formatErrorMessage(Component, 'Config passed as parameter into constructor but no schema defined'));
203
+ }
204
+ const out = {};
205
+ const entries = Object.entries(Component.schema.properties);
206
+ for (const entry of entries) {
207
+ const [namespace, property] = entry;
208
+ const field = namespace.toString();
209
+ if (field in dataset) {
210
+ out[field] = normaliseString(dataset[field], property);
211
+ }
212
+ if ((property == null ? void 0 : property.type) === 'object') {
213
+ out[field] = extractConfigByNamespace(Component.schema, dataset, namespace);
214
+ }
215
+ }
216
+ return out;
217
+ }
218
+ function mergeConfigs(...configObjects) {
219
+ const formattedConfigObject = {};
220
+ for (const configObject of configObjects) {
221
+ for (const key of Object.keys(configObject)) {
222
+ const option = formattedConfigObject[key];
223
+ const override = configObject[key];
224
+ if (isObject(option) && isObject(override)) {
225
+ formattedConfigObject[key] = mergeConfigs(option, override);
226
+ } else {
227
+ formattedConfigObject[key] = override;
228
+ }
229
+ }
230
+ }
231
+ return formattedConfigObject;
232
+ }
233
+ function extractConfigByNamespace(schema, dataset, namespace) {
234
+ const property = schema.properties[namespace];
235
+ if ((property == null ? void 0 : property.type) !== 'object') {
236
+ return;
237
+ }
238
+ const newObject = {
239
+ [namespace]: {}
240
+ };
241
+ for (const [key, value] of Object.entries(dataset)) {
242
+ let current = newObject;
243
+ const keyParts = key.split('.');
244
+ for (const [index, name] of keyParts.entries()) {
245
+ if (isObject(current)) {
246
+ if (index < keyParts.length - 1) {
247
+ if (!isObject(current[name])) {
248
+ current[name] = {};
249
+ }
250
+ current = current[name];
251
+ } else if (key !== namespace) {
252
+ current[name] = normaliseString(value);
253
+ }
254
+ }
255
+ }
256
+ }
257
+ return newObject[namespace];
258
+ }
259
+ /**
260
+ * Schema for component config
261
+ *
262
+ * @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType
263
+ * @typedef {object} Schema
264
+ * @property {Record<keyof ConfigurationType, SchemaProperty | undefined>} properties - Schema properties
265
+ * @property {SchemaCondition<ConfigurationType>[]} [anyOf] - List of schema conditions
266
+ */
267
+ /**
268
+ * Schema property for component config
269
+ *
270
+ * @typedef {object} SchemaProperty
271
+ * @property {'string' | 'boolean' | 'number' | 'object'} type - Property type
272
+ */
273
+ /**
274
+ * Schema condition for component config
275
+ *
276
+ * @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType
277
+ * @typedef {object} SchemaCondition
278
+ * @property {(keyof ConfigurationType)[]} required - List of required config fields
279
+ * @property {string} errorMessage - Error message when required config fields not provided
280
+ */
281
+ /**
282
+ * @template {Partial<Record<keyof ConfigurationType, unknown>>} [ConfigurationType=ObjectNested]
283
+ * @typedef ChildClass
284
+ * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
285
+ * @property {Schema<ConfigurationType>} [schema] - The schema of the component configuration
286
+ * @property {ConfigurationType} [defaults] - The default values of the configuration of the component
287
+ */
288
+ /**
289
+ * @template {Partial<Record<keyof ConfigurationType, unknown>>} [ConfigurationType=ObjectNested]
290
+ * @typedef {typeof Component & ChildClass<ConfigurationType>} ChildClassConstructor<ConfigurationType>
291
+ */
292
+
293
+ class I18n {
294
+ constructor(translations = {}, config = {}) {
295
+ var _config$locale;
296
+ this.translations = void 0;
297
+ this.locale = void 0;
298
+ this.translations = translations;
299
+ this.locale = (_config$locale = config.locale) != null ? _config$locale : document.documentElement.lang || 'en';
300
+ }
301
+ t(lookupKey, options) {
302
+ if (!lookupKey) {
303
+ throw new Error('i18n: lookup key missing');
304
+ }
305
+ let translation = this.translations[lookupKey];
306
+ if (typeof (options == null ? void 0 : options.count) === 'number' && typeof translation === 'object') {
307
+ const translationPluralForm = translation[this.getPluralSuffix(lookupKey, options.count)];
308
+ if (translationPluralForm) {
309
+ translation = translationPluralForm;
310
+ }
311
+ }
312
+ if (typeof translation === 'string') {
313
+ if (translation.match(/%{(.\S+)}/)) {
314
+ if (!options) {
315
+ throw new Error('i18n: cannot replace placeholders in string if no option data provided');
316
+ }
317
+ return this.replacePlaceholders(translation, options);
318
+ }
319
+ return translation;
320
+ }
321
+ return lookupKey;
322
+ }
323
+ replacePlaceholders(translationString, options) {
324
+ const formatter = Intl.NumberFormat.supportedLocalesOf(this.locale).length ? new Intl.NumberFormat(this.locale) : undefined;
325
+ return translationString.replace(/%{(.\S+)}/g, function (placeholderWithBraces, placeholderKey) {
326
+ if (Object.prototype.hasOwnProperty.call(options, placeholderKey)) {
327
+ const placeholderValue = options[placeholderKey];
328
+ if (placeholderValue === false || typeof placeholderValue !== 'number' && typeof placeholderValue !== 'string') {
329
+ return '';
330
+ }
331
+ if (typeof placeholderValue === 'number') {
332
+ return formatter ? formatter.format(placeholderValue) : `${placeholderValue}`;
333
+ }
334
+ return placeholderValue;
335
+ }
336
+ throw new Error(`i18n: no data found to replace ${placeholderWithBraces} placeholder in string`);
337
+ });
338
+ }
339
+ hasIntlPluralRulesSupport() {
340
+ return Boolean('PluralRules' in window.Intl && Intl.PluralRules.supportedLocalesOf(this.locale).length);
341
+ }
342
+ getPluralSuffix(lookupKey, count) {
343
+ count = Number(count);
344
+ if (!isFinite(count)) {
345
+ return 'other';
346
+ }
347
+ const translation = this.translations[lookupKey];
348
+ const preferredForm = this.hasIntlPluralRulesSupport() ? new Intl.PluralRules(this.locale).select(count) : this.selectPluralFormUsingFallbackRules(count);
349
+ if (typeof translation === 'object') {
350
+ if (preferredForm in translation) {
351
+ return preferredForm;
352
+ } else if ('other' in translation) {
353
+ console.warn(`i18n: Missing plural form ".${preferredForm}" for "${this.locale}" locale. Falling back to ".other".`);
354
+ return 'other';
355
+ }
356
+ }
357
+ throw new Error(`i18n: Plural form ".other" is required for "${this.locale}" locale`);
358
+ }
359
+ selectPluralFormUsingFallbackRules(count) {
360
+ count = Math.abs(Math.floor(count));
361
+ const ruleset = this.getPluralRulesForLocale();
362
+ if (ruleset) {
363
+ return I18n.pluralRules[ruleset](count);
364
+ }
365
+ return 'other';
366
+ }
367
+ getPluralRulesForLocale() {
368
+ const localeShort = this.locale.split('-')[0];
369
+ for (const pluralRule in I18n.pluralRulesMap) {
370
+ const languages = I18n.pluralRulesMap[pluralRule];
371
+ if (languages.includes(this.locale) || languages.includes(localeShort)) {
372
+ return pluralRule;
373
+ }
374
+ }
375
+ }
376
+ }
377
+ I18n.pluralRulesMap = {
378
+ arabic: ['ar'],
379
+ chinese: ['my', 'zh', 'id', 'ja', 'jv', 'ko', 'ms', 'th', 'vi'],
380
+ french: ['hy', 'bn', 'fr', 'gu', 'hi', 'fa', 'pa', 'zu'],
381
+ german: ['af', 'sq', 'az', 'eu', 'bg', 'ca', 'da', 'nl', 'en', 'et', 'fi', 'ka', 'de', 'el', 'hu', 'lb', 'no', 'so', 'sw', 'sv', 'ta', 'te', 'tr', 'ur'],
382
+ irish: ['ga'],
383
+ russian: ['ru', 'uk'],
384
+ scottish: ['gd'],
385
+ spanish: ['pt-PT', 'it', 'es'],
386
+ welsh: ['cy']
387
+ };
388
+ I18n.pluralRules = {
389
+ arabic(n) {
390
+ if (n === 0) {
391
+ return 'zero';
392
+ }
393
+ if (n === 1) {
394
+ return 'one';
395
+ }
396
+ if (n === 2) {
397
+ return 'two';
398
+ }
399
+ if (n % 100 >= 3 && n % 100 <= 10) {
400
+ return 'few';
401
+ }
402
+ if (n % 100 >= 11 && n % 100 <= 99) {
403
+ return 'many';
404
+ }
405
+ return 'other';
406
+ },
407
+ chinese() {
408
+ return 'other';
409
+ },
410
+ french(n) {
411
+ return n === 0 || n === 1 ? 'one' : 'other';
412
+ },
413
+ german(n) {
414
+ return n === 1 ? 'one' : 'other';
415
+ },
416
+ irish(n) {
417
+ if (n === 1) {
418
+ return 'one';
419
+ }
420
+ if (n === 2) {
421
+ return 'two';
422
+ }
423
+ if (n >= 3 && n <= 6) {
424
+ return 'few';
425
+ }
426
+ if (n >= 7 && n <= 10) {
427
+ return 'many';
428
+ }
429
+ return 'other';
430
+ },
431
+ russian(n) {
432
+ const lastTwo = n % 100;
433
+ const last = lastTwo % 10;
434
+ if (last === 1 && lastTwo !== 11) {
435
+ return 'one';
436
+ }
437
+ if (last >= 2 && last <= 4 && !(lastTwo >= 12 && lastTwo <= 14)) {
438
+ return 'few';
439
+ }
440
+ if (last === 0 || last >= 5 && last <= 9 || lastTwo >= 11 && lastTwo <= 14) {
441
+ return 'many';
442
+ }
443
+ return 'other';
444
+ },
445
+ scottish(n) {
446
+ if (n === 1 || n === 11) {
447
+ return 'one';
448
+ }
449
+ if (n === 2 || n === 12) {
450
+ return 'two';
451
+ }
452
+ if (n >= 3 && n <= 10 || n >= 13 && n <= 19) {
453
+ return 'few';
454
+ }
455
+ return 'other';
456
+ },
457
+ spanish(n) {
458
+ if (n === 1) {
459
+ return 'one';
460
+ }
461
+ if (n % 1000000 === 0 && n !== 0) {
462
+ return 'many';
463
+ }
464
+ return 'other';
465
+ },
466
+ welsh(n) {
467
+ if (n === 0) {
468
+ return 'zero';
469
+ }
470
+ if (n === 1) {
471
+ return 'one';
472
+ }
473
+ if (n === 2) {
474
+ return 'two';
475
+ }
476
+ if (n === 3) {
477
+ return 'few';
478
+ }
479
+ if (n === 6) {
480
+ return 'many';
481
+ }
482
+ return 'other';
483
+ }
484
+ };
485
+
486
+ /**
487
+ * File upload component
488
+ *
489
+ * @preserve
490
+ * @augments ConfigurableComponent<FileUploadConfig>
491
+ */
492
+ class FileUpload extends ConfigurableComponent {
493
+ /**
494
+ * @param {Element | null} $root - File input element
495
+ * @param {FileUploadConfig} [config] - File Upload config
496
+ */
497
+ constructor($root, config = {}) {
498
+ super($root, config);
499
+ this.$input = void 0;
500
+ this.$button = void 0;
501
+ this.$status = void 0;
502
+ this.i18n = void 0;
503
+ this.id = void 0;
504
+ const $input = this.$root.querySelector('input');
505
+ if ($input === null) {
506
+ throw new ElementError({
507
+ component: FileUpload,
508
+ identifier: 'File inputs (`<input type="file">`)'
509
+ });
510
+ }
511
+ if ($input.type !== 'file') {
512
+ throw new ElementError(formatErrorMessage(FileUpload, 'File input (`<input type="file">`) attribute (`type`) is not `file`'));
513
+ }
514
+ this.$input = $input;
515
+ this.$input.setAttribute('hidden', 'true');
516
+ if (!this.$input.id) {
517
+ throw new ElementError({
518
+ component: FileUpload,
519
+ identifier: 'File input (`<input type="file">`) attribute (`id`)'
520
+ });
521
+ }
522
+ this.id = this.$input.id;
523
+ this.i18n = new I18n(this.config.i18n, {
524
+ locale: closestAttributeValue(this.$root, 'lang')
525
+ });
526
+ const $label = this.findLabel();
527
+ if (!$label.id) {
528
+ $label.id = `${this.id}-label`;
529
+ }
530
+ this.$input.id = `${this.id}-input`;
531
+ const $button = document.createElement('button');
532
+ $button.classList.add('govuk-file-upload-button');
533
+ $button.type = 'button';
534
+ $button.id = this.id;
535
+ $button.classList.add('govuk-file-upload-button--empty');
536
+ const ariaDescribedBy = this.$input.getAttribute('aria-describedby');
537
+ if (ariaDescribedBy) {
538
+ $button.setAttribute('aria-describedby', ariaDescribedBy);
539
+ }
540
+ const $status = document.createElement('span');
541
+ $status.className = 'govuk-body govuk-file-upload-button__status';
542
+ $status.setAttribute('aria-live', 'polite');
543
+ $status.innerText = this.i18n.t('noFileChosen');
544
+ $button.appendChild($status);
545
+ const commaSpan = document.createElement('span');
546
+ commaSpan.className = 'govuk-visually-hidden';
547
+ commaSpan.innerText = ', ';
548
+ commaSpan.id = `${this.id}-comma`;
549
+ $button.appendChild(commaSpan);
550
+ const containerSpan = document.createElement('span');
551
+ containerSpan.className = 'govuk-file-upload-button__pseudo-button-container';
552
+ const buttonSpan = document.createElement('span');
553
+ buttonSpan.className = 'govuk-button govuk-button--secondary govuk-file-upload-button__pseudo-button';
554
+ buttonSpan.innerText = this.i18n.t('chooseFilesButton');
555
+ containerSpan.appendChild(buttonSpan);
556
+ containerSpan.insertAdjacentText('beforeend', ' ');
557
+ const instructionSpan = document.createElement('span');
558
+ instructionSpan.className = 'govuk-body govuk-file-upload-button__instruction';
559
+ instructionSpan.innerText = this.i18n.t('dropInstruction');
560
+ containerSpan.appendChild(instructionSpan);
561
+ $button.appendChild(containerSpan);
562
+ $button.setAttribute('aria-labelledby', `${$label.id} ${commaSpan.id} ${$button.id}`);
563
+ $button.addEventListener('click', this.onClick.bind(this));
564
+ $button.addEventListener('dragover', event => {
565
+ event.preventDefault();
566
+ });
567
+ this.$root.insertAdjacentElement('afterbegin', $button);
568
+ this.$input.setAttribute('tabindex', '-1');
569
+ this.$input.setAttribute('aria-hidden', 'true');
570
+ this.$button = $button;
571
+ this.$status = $status;
572
+ this.$input.addEventListener('change', this.onChange.bind(this));
573
+ this.updateDisabledState();
574
+ this.observeDisabledState();
575
+ this.$announcements = document.createElement('span');
576
+ this.$announcements.classList.add('govuk-file-upload-announcements');
577
+ this.$announcements.classList.add('govuk-visually-hidden');
578
+ this.$announcements.setAttribute('aria-live', 'assertive');
579
+ this.$root.insertAdjacentElement('afterend', this.$announcements);
580
+ this.$button.addEventListener('drop', this.onDrop.bind(this));
581
+ document.addEventListener('dragenter', this.updateDropzoneVisibility.bind(this));
582
+ document.addEventListener('dragenter', () => {
583
+ this.enteredAnotherElement = true;
584
+ });
585
+ document.addEventListener('dragleave', () => {
586
+ if (!this.enteredAnotherElement && !this.$button.disabled) {
587
+ this.hideDraggingState();
588
+ this.$announcements.innerText = this.i18n.t('leftDropZone');
589
+ }
590
+ this.enteredAnotherElement = false;
591
+ });
592
+ }
593
+
594
+ /**
595
+ * Updates the visibility of the dropzone as users enters the various elements on the page
596
+ *
597
+ * @param {DragEvent} event - The `dragenter` event
598
+ */
599
+ updateDropzoneVisibility(event) {
600
+ if (this.$button.disabled) return;
601
+ if (event.target instanceof Node) {
602
+ if (this.$root.contains(event.target)) {
603
+ if (event.dataTransfer && isContainingFiles(event.dataTransfer)) {
604
+ if (!this.$button.classList.contains('govuk-file-upload-button--dragging')) {
605
+ this.showDraggingState();
606
+ this.$announcements.innerText = this.i18n.t('enteredDropZone');
607
+ }
608
+ }
609
+ } else {
610
+ if (this.$button.classList.contains('govuk-file-upload-button--dragging')) {
611
+ this.hideDraggingState();
612
+ this.$announcements.innerText = this.i18n.t('leftDropZone');
613
+ }
614
+ }
615
+ }
616
+ }
617
+ showDraggingState() {
618
+ this.$button.classList.add('govuk-file-upload-button--dragging');
619
+ }
620
+ hideDraggingState() {
621
+ this.$button.classList.remove('govuk-file-upload-button--dragging');
622
+ }
623
+
624
+ /**
625
+ * Handles user dropping on the component
626
+ *
627
+ * @param {DragEvent} event - The `dragenter` event
628
+ */
629
+ onDrop(event) {
630
+ event.preventDefault();
631
+ if (event.dataTransfer && isContainingFiles(event.dataTransfer)) {
632
+ this.$input.files = event.dataTransfer.files;
633
+ this.$input.dispatchEvent(new CustomEvent('change'));
634
+ this.hideDraggingState();
635
+ }
636
+ }
637
+ onChange() {
638
+ const fileCount = this.$input.files.length;
639
+ if (fileCount === 0) {
640
+ this.$status.innerText = this.i18n.t('noFileChosen');
641
+ this.$button.classList.add('govuk-file-upload-button--empty');
642
+ } else {
643
+ if (fileCount === 1) {
644
+ this.$status.innerText = this.$input.files[0].name;
645
+ } else {
646
+ this.$status.innerText = this.i18n.t('multipleFilesChosen', {
647
+ count: fileCount
648
+ });
649
+ }
650
+ this.$button.classList.remove('govuk-file-upload-button--empty');
651
+ }
652
+ }
653
+ findLabel() {
654
+ const $label = document.querySelector(`label[for="${this.$input.id}"]`);
655
+ if (!$label) {
656
+ throw new ElementError({
657
+ component: FileUpload,
658
+ identifier: `Field label (\`<label for=${this.$input.id}>\`)`
659
+ });
660
+ }
661
+ return $label;
662
+ }
663
+ onClick() {
664
+ this.$input.click();
665
+ }
666
+ observeDisabledState() {
667
+ const observer = new MutationObserver(mutationList => {
668
+ for (const mutation of mutationList) {
669
+ if (mutation.type === 'attributes' && mutation.attributeName === 'disabled') {
670
+ this.updateDisabledState();
671
+ }
672
+ }
673
+ });
674
+ observer.observe(this.$input, {
675
+ attributes: true
676
+ });
677
+ }
678
+ updateDisabledState() {
679
+ this.$button.disabled = this.$input.disabled;
680
+ this.$root.classList.toggle('govuk-drop-zone--disabled', this.$button.disabled);
681
+ }
682
+ }
683
+ FileUpload.moduleName = 'govuk-file-upload';
684
+ FileUpload.defaults = Object.freeze({
685
+ i18n: {
686
+ chooseFilesButton: 'Choose file',
687
+ dropInstruction: 'or drop file',
688
+ noFileChosen: 'No file chosen',
689
+ multipleFilesChosen: {
690
+ one: '%{count} file chosen',
691
+ other: '%{count} files chosen'
692
+ },
693
+ enteredDropZone: 'Entered drop zone',
694
+ leftDropZone: 'Left drop zone'
695
+ }
696
+ });
697
+ FileUpload.schema = Object.freeze({
698
+ properties: {
699
+ i18n: {
700
+ type: 'object'
701
+ }
702
+ }
703
+ });
704
+ function isContainingFiles(dataTransfer) {
705
+ const hasNoTypesInfo = dataTransfer.types.length === 0;
706
+ const isDraggingFiles = dataTransfer.types.some(type => type === 'Files');
707
+ return hasNoTypesInfo || isDraggingFiles;
708
+ }
709
+
710
+ /**
711
+ * @typedef {HTMLInputElement & {files: FileList}} HTMLFileInputElement
712
+ */
713
+
714
+ /**
715
+ * File upload config
716
+ *
717
+ * @see {@link FileUpload.defaults}
718
+ * @typedef {object} FileUploadConfig
719
+ * @property {FileUploadTranslations} [i18n=FileUpload.defaults.i18n] - File upload translations
720
+ */
721
+
722
+ /**
723
+ * File upload translations
724
+ *
725
+ * @see {@link FileUpload.defaults.i18n}
726
+ * @typedef {object} FileUploadTranslations
727
+ *
728
+ * Messages used by the component
729
+ * @property {string} [chooseFile] - The text of the button that opens the file picker
730
+ * @property {string} [dropInstruction] - The text informing users they can drop files
731
+ * @property {TranslationPluralForms} [multipleFilesChosen] - The text displayed when multiple files
732
+ * have been chosen by the user
733
+ * @property {string} [noFileChosen] - The text to displayed when no file has been chosen by the user
734
+ * @property {string} [enteredDropZone] - The text announced by assistive technology
735
+ * when user drags files and enters the drop zone
736
+ * @property {string} [leftDropZone] - The text announced by assistive technology
737
+ * when user drags files and leaves the drop zone without dropping
738
+ */
739
+
740
+ /**
741
+ * @import { Schema } from '../../common/configuration.mjs'
742
+ * @import { TranslationPluralForms } from '../../i18n.mjs'
743
+ */
744
+
745
+ export { FileUpload };
746
+ //# sourceMappingURL=file-upload.bundle.mjs.map