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
@@ -4,7 +4,7 @@
4
4
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.GOVUKFrontend = global.GOVUKFrontend || {}));
5
5
  })(this, (function (exports) { 'use strict';
6
6
 
7
- const version = '5.8.0';
7
+ const version = '5.9.0';
8
8
 
9
9
  function getFragmentFromUrl(url) {
10
10
  if (!url.includes('#')) {
@@ -76,6 +76,9 @@
76
76
  * @typedef ComponentWithModuleName
77
77
  * @property {string} moduleName - Name of the component
78
78
  */
79
+ /**
80
+ * @import { ObjectNested } from './configuration.mjs'
81
+ */
79
82
 
80
83
  class GOVUKFrontendError extends Error {
81
84
  constructor(...args) {
@@ -127,10 +130,10 @@
127
130
  }
128
131
  }
129
132
  /**
130
- * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
133
+ * @import { ComponentWithModuleName } from '../common/index.mjs'
131
134
  */
132
135
 
133
- class GOVUKFrontendComponent {
136
+ class Component {
134
137
  /**
135
138
  * Returns the root element of the component
136
139
  *
@@ -181,12 +184,12 @@
181
184
  */
182
185
 
183
186
  /**
184
- * @typedef {typeof GOVUKFrontendComponent & ChildClass} ChildClassConstructor
187
+ * @typedef {typeof Component & ChildClass} ChildClassConstructor
185
188
  */
186
- GOVUKFrontendComponent.elementType = HTMLElement;
189
+ Component.elementType = HTMLElement;
187
190
 
188
191
  const configOverride = Symbol.for('configOverride');
189
- class ConfigurableComponent extends GOVUKFrontendComponent {
192
+ class ConfigurableComponent extends Component {
190
193
  [configOverride](param) {
191
194
  return {};
192
195
  }
@@ -204,7 +207,7 @@
204
207
  super($root);
205
208
  this._config = void 0;
206
209
  const childConstructor = this.constructor;
207
- if (typeof childConstructor.defaults === 'undefined') {
210
+ if (!isObject(childConstructor.defaults)) {
208
211
  throw new ConfigError(formatErrorMessage(childConstructor, 'Config passed as parameter into constructor but no defaults defined'));
209
212
  }
210
213
  const datasetConfig = normaliseDataset(childConstructor, this._$root.dataset);
@@ -236,16 +239,19 @@
236
239
  return output;
237
240
  }
238
241
  function normaliseDataset(Component, dataset) {
239
- if (typeof Component.schema === 'undefined') {
242
+ if (!isObject(Component.schema)) {
240
243
  throw new ConfigError(formatErrorMessage(Component, 'Config passed as parameter into constructor but no schema defined'));
241
244
  }
242
245
  const out = {};
243
- for (const [field, property] of Object.entries(Component.schema.properties)) {
246
+ const entries = Object.entries(Component.schema.properties);
247
+ for (const entry of entries) {
248
+ const [namespace, property] = entry;
249
+ const field = namespace.toString();
244
250
  if (field in dataset) {
245
251
  out[field] = normaliseString(dataset[field], property);
246
252
  }
247
253
  if ((property == null ? void 0 : property.type) === 'object') {
248
- out[field] = extractConfigByNamespace(Component.schema, dataset, field);
254
+ out[field] = extractConfigByNamespace(Component.schema, dataset, namespace);
249
255
  }
250
256
  }
251
257
  return out;
@@ -291,13 +297,13 @@
291
297
  return;
292
298
  }
293
299
  const newObject = {
294
- [namespace]: ({})
300
+ [namespace]: {}
295
301
  };
296
302
  for (const [key, value] of Object.entries(dataset)) {
297
303
  let current = newObject;
298
304
  const keyParts = key.split('.');
299
305
  for (const [index, name] of keyParts.entries()) {
300
- if (typeof current === 'object') {
306
+ if (isObject(current)) {
301
307
  if (index < keyParts.length - 1) {
302
308
  if (!isObject(current[name])) {
303
309
  current[name] = {};
@@ -314,9 +320,10 @@
314
320
  /**
315
321
  * Schema for component config
316
322
  *
323
+ * @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType
317
324
  * @typedef {object} Schema
318
- * @property {{ [field: string]: SchemaProperty | undefined }} properties - Schema properties
319
- * @property {SchemaCondition[]} [anyOf] - List of schema conditions
325
+ * @property {Record<keyof ConfigurationType, SchemaProperty | undefined>} properties - Schema properties
326
+ * @property {SchemaCondition<ConfigurationType>[]} [anyOf] - List of schema conditions
320
327
  */
321
328
  /**
322
329
  * Schema property for component config
@@ -327,20 +334,21 @@
327
334
  /**
328
335
  * Schema condition for component config
329
336
  *
337
+ * @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType
330
338
  * @typedef {object} SchemaCondition
331
- * @property {string[]} required - List of required config fields
339
+ * @property {(keyof ConfigurationType)[]} required - List of required config fields
332
340
  * @property {string} errorMessage - Error message when required config fields not provided
333
341
  */
334
342
  /**
335
- * @template {ObjectNested} [ConfigurationType={}]
343
+ * @template {Partial<Record<keyof ConfigurationType, unknown>>} [ConfigurationType=ObjectNested]
336
344
  * @typedef ChildClass
337
345
  * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
338
- * @property {Schema} [schema] - The schema of the component configuration
346
+ * @property {Schema<ConfigurationType>} [schema] - The schema of the component configuration
339
347
  * @property {ConfigurationType} [defaults] - The default values of the configuration of the component
340
348
  */
341
349
  /**
342
- * @template {ObjectNested} [ConfigurationType={}]
343
- * @typedef {typeof GOVUKFrontendComponent & ChildClass<ConfigurationType>} ChildClassConstructor<ConfigurationType>
350
+ * @template {Partial<Record<keyof ConfigurationType, unknown>>} [ConfigurationType=ObjectNested]
351
+ * @typedef {typeof Component & ChildClass<ConfigurationType>} ChildClassConstructor<ConfigurationType>
344
352
  */
345
353
 
346
354
  class I18n {
@@ -851,7 +859,7 @@
851
859
  */
852
860
 
853
861
  /**
854
- * @typedef {import('../../common/configuration.mjs').Schema} Schema
862
+ * @import { Schema } from '../../common/configuration.mjs'
855
863
  */
856
864
  Accordion.moduleName = 'govuk-accordion';
857
865
  Accordion.defaults = Object.freeze({
@@ -928,7 +936,7 @@
928
936
  */
929
937
 
930
938
  /**
931
- * @typedef {import('../../common/configuration.mjs').Schema} Schema
939
+ * @import { Schema } from '../../common/configuration.mjs'
932
940
  */
933
941
  Button.moduleName = 'govuk-button';
934
942
  Button.defaults = Object.freeze({
@@ -1181,8 +1189,8 @@
1181
1189
  */
1182
1190
 
1183
1191
  /**
1184
- * @typedef {import('../../common/configuration.mjs').Schema} Schema
1185
- * @typedef {import('../../i18n.mjs').TranslationPluralForms} TranslationPluralForms
1192
+ * @import { Schema } from '../../common/configuration.mjs'
1193
+ * @import { TranslationPluralForms } from '../../i18n.mjs'
1186
1194
  */
1187
1195
  CharacterCount.moduleName = 'govuk-character-count';
1188
1196
  CharacterCount.defaults = Object.freeze({
@@ -1240,7 +1248,7 @@
1240
1248
  *
1241
1249
  * @preserve
1242
1250
  */
1243
- class Checkboxes extends GOVUKFrontendComponent {
1251
+ class Checkboxes extends Component {
1244
1252
  /**
1245
1253
  * Checkboxes can be associated with a 'conditionally revealed' content block
1246
1254
  * – for example, a checkbox for 'Phone' could reveal an additional form field
@@ -1423,7 +1431,7 @@
1423
1431
  */
1424
1432
 
1425
1433
  /**
1426
- * @typedef {import('../../common/configuration.mjs').Schema} Schema
1434
+ * @import { Schema } from '../../common/configuration.mjs'
1427
1435
  */
1428
1436
  ErrorSummary.moduleName = 'govuk-error-summary';
1429
1437
  ErrorSummary.defaults = Object.freeze({
@@ -1635,7 +1643,7 @@
1635
1643
  */
1636
1644
 
1637
1645
  /**
1638
- * @typedef {import('../../common/configuration.mjs').Schema} Schema
1646
+ * @import { Schema } from '../../common/configuration.mjs'
1639
1647
  */
1640
1648
  ExitThisPage.moduleName = 'govuk-exit-this-page';
1641
1649
  ExitThisPage.defaults = Object.freeze({
@@ -1654,12 +1662,271 @@
1654
1662
  }
1655
1663
  });
1656
1664
 
1665
+ /**
1666
+ * File upload component
1667
+ *
1668
+ * @preserve
1669
+ * @augments ConfigurableComponent<FileUploadConfig>
1670
+ */
1671
+ class FileUpload extends ConfigurableComponent {
1672
+ /**
1673
+ * @param {Element | null} $root - File input element
1674
+ * @param {FileUploadConfig} [config] - File Upload config
1675
+ */
1676
+ constructor($root, config = {}) {
1677
+ super($root, config);
1678
+ this.$input = void 0;
1679
+ this.$button = void 0;
1680
+ this.$status = void 0;
1681
+ this.i18n = void 0;
1682
+ this.id = void 0;
1683
+ const $input = this.$root.querySelector('input');
1684
+ if ($input === null) {
1685
+ throw new ElementError({
1686
+ component: FileUpload,
1687
+ identifier: 'File inputs (`<input type="file">`)'
1688
+ });
1689
+ }
1690
+ if ($input.type !== 'file') {
1691
+ throw new ElementError(formatErrorMessage(FileUpload, 'File input (`<input type="file">`) attribute (`type`) is not `file`'));
1692
+ }
1693
+ this.$input = $input;
1694
+ this.$input.setAttribute('hidden', 'true');
1695
+ if (!this.$input.id) {
1696
+ throw new ElementError({
1697
+ component: FileUpload,
1698
+ identifier: 'File input (`<input type="file">`) attribute (`id`)'
1699
+ });
1700
+ }
1701
+ this.id = this.$input.id;
1702
+ this.i18n = new I18n(this.config.i18n, {
1703
+ locale: closestAttributeValue(this.$root, 'lang')
1704
+ });
1705
+ const $label = this.findLabel();
1706
+ if (!$label.id) {
1707
+ $label.id = `${this.id}-label`;
1708
+ }
1709
+ this.$input.id = `${this.id}-input`;
1710
+ const $button = document.createElement('button');
1711
+ $button.classList.add('govuk-file-upload-button');
1712
+ $button.type = 'button';
1713
+ $button.id = this.id;
1714
+ $button.classList.add('govuk-file-upload-button--empty');
1715
+ const ariaDescribedBy = this.$input.getAttribute('aria-describedby');
1716
+ if (ariaDescribedBy) {
1717
+ $button.setAttribute('aria-describedby', ariaDescribedBy);
1718
+ }
1719
+ const $status = document.createElement('span');
1720
+ $status.className = 'govuk-body govuk-file-upload-button__status';
1721
+ $status.setAttribute('aria-live', 'polite');
1722
+ $status.innerText = this.i18n.t('noFileChosen');
1723
+ $button.appendChild($status);
1724
+ const commaSpan = document.createElement('span');
1725
+ commaSpan.className = 'govuk-visually-hidden';
1726
+ commaSpan.innerText = ', ';
1727
+ commaSpan.id = `${this.id}-comma`;
1728
+ $button.appendChild(commaSpan);
1729
+ const containerSpan = document.createElement('span');
1730
+ containerSpan.className = 'govuk-file-upload-button__pseudo-button-container';
1731
+ const buttonSpan = document.createElement('span');
1732
+ buttonSpan.className = 'govuk-button govuk-button--secondary govuk-file-upload-button__pseudo-button';
1733
+ buttonSpan.innerText = this.i18n.t('chooseFilesButton');
1734
+ containerSpan.appendChild(buttonSpan);
1735
+ containerSpan.insertAdjacentText('beforeend', ' ');
1736
+ const instructionSpan = document.createElement('span');
1737
+ instructionSpan.className = 'govuk-body govuk-file-upload-button__instruction';
1738
+ instructionSpan.innerText = this.i18n.t('dropInstruction');
1739
+ containerSpan.appendChild(instructionSpan);
1740
+ $button.appendChild(containerSpan);
1741
+ $button.setAttribute('aria-labelledby', `${$label.id} ${commaSpan.id} ${$button.id}`);
1742
+ $button.addEventListener('click', this.onClick.bind(this));
1743
+ $button.addEventListener('dragover', event => {
1744
+ event.preventDefault();
1745
+ });
1746
+ this.$root.insertAdjacentElement('afterbegin', $button);
1747
+ this.$input.setAttribute('tabindex', '-1');
1748
+ this.$input.setAttribute('aria-hidden', 'true');
1749
+ this.$button = $button;
1750
+ this.$status = $status;
1751
+ this.$input.addEventListener('change', this.onChange.bind(this));
1752
+ this.updateDisabledState();
1753
+ this.observeDisabledState();
1754
+ this.$announcements = document.createElement('span');
1755
+ this.$announcements.classList.add('govuk-file-upload-announcements');
1756
+ this.$announcements.classList.add('govuk-visually-hidden');
1757
+ this.$announcements.setAttribute('aria-live', 'assertive');
1758
+ this.$root.insertAdjacentElement('afterend', this.$announcements);
1759
+ this.$button.addEventListener('drop', this.onDrop.bind(this));
1760
+ document.addEventListener('dragenter', this.updateDropzoneVisibility.bind(this));
1761
+ document.addEventListener('dragenter', () => {
1762
+ this.enteredAnotherElement = true;
1763
+ });
1764
+ document.addEventListener('dragleave', () => {
1765
+ if (!this.enteredAnotherElement && !this.$button.disabled) {
1766
+ this.hideDraggingState();
1767
+ this.$announcements.innerText = this.i18n.t('leftDropZone');
1768
+ }
1769
+ this.enteredAnotherElement = false;
1770
+ });
1771
+ }
1772
+
1773
+ /**
1774
+ * Updates the visibility of the dropzone as users enters the various elements on the page
1775
+ *
1776
+ * @param {DragEvent} event - The `dragenter` event
1777
+ */
1778
+ updateDropzoneVisibility(event) {
1779
+ if (this.$button.disabled) return;
1780
+ if (event.target instanceof Node) {
1781
+ if (this.$root.contains(event.target)) {
1782
+ if (event.dataTransfer && isContainingFiles(event.dataTransfer)) {
1783
+ if (!this.$button.classList.contains('govuk-file-upload-button--dragging')) {
1784
+ this.showDraggingState();
1785
+ this.$announcements.innerText = this.i18n.t('enteredDropZone');
1786
+ }
1787
+ }
1788
+ } else {
1789
+ if (this.$button.classList.contains('govuk-file-upload-button--dragging')) {
1790
+ this.hideDraggingState();
1791
+ this.$announcements.innerText = this.i18n.t('leftDropZone');
1792
+ }
1793
+ }
1794
+ }
1795
+ }
1796
+ showDraggingState() {
1797
+ this.$button.classList.add('govuk-file-upload-button--dragging');
1798
+ }
1799
+ hideDraggingState() {
1800
+ this.$button.classList.remove('govuk-file-upload-button--dragging');
1801
+ }
1802
+
1803
+ /**
1804
+ * Handles user dropping on the component
1805
+ *
1806
+ * @param {DragEvent} event - The `dragenter` event
1807
+ */
1808
+ onDrop(event) {
1809
+ event.preventDefault();
1810
+ if (event.dataTransfer && isContainingFiles(event.dataTransfer)) {
1811
+ this.$input.files = event.dataTransfer.files;
1812
+ this.$input.dispatchEvent(new CustomEvent('change'));
1813
+ this.hideDraggingState();
1814
+ }
1815
+ }
1816
+ onChange() {
1817
+ const fileCount = this.$input.files.length;
1818
+ if (fileCount === 0) {
1819
+ this.$status.innerText = this.i18n.t('noFileChosen');
1820
+ this.$button.classList.add('govuk-file-upload-button--empty');
1821
+ } else {
1822
+ if (fileCount === 1) {
1823
+ this.$status.innerText = this.$input.files[0].name;
1824
+ } else {
1825
+ this.$status.innerText = this.i18n.t('multipleFilesChosen', {
1826
+ count: fileCount
1827
+ });
1828
+ }
1829
+ this.$button.classList.remove('govuk-file-upload-button--empty');
1830
+ }
1831
+ }
1832
+ findLabel() {
1833
+ const $label = document.querySelector(`label[for="${this.$input.id}"]`);
1834
+ if (!$label) {
1835
+ throw new ElementError({
1836
+ component: FileUpload,
1837
+ identifier: `Field label (\`<label for=${this.$input.id}>\`)`
1838
+ });
1839
+ }
1840
+ return $label;
1841
+ }
1842
+ onClick() {
1843
+ this.$input.click();
1844
+ }
1845
+ observeDisabledState() {
1846
+ const observer = new MutationObserver(mutationList => {
1847
+ for (const mutation of mutationList) {
1848
+ if (mutation.type === 'attributes' && mutation.attributeName === 'disabled') {
1849
+ this.updateDisabledState();
1850
+ }
1851
+ }
1852
+ });
1853
+ observer.observe(this.$input, {
1854
+ attributes: true
1855
+ });
1856
+ }
1857
+ updateDisabledState() {
1858
+ this.$button.disabled = this.$input.disabled;
1859
+ this.$root.classList.toggle('govuk-drop-zone--disabled', this.$button.disabled);
1860
+ }
1861
+ }
1862
+ FileUpload.moduleName = 'govuk-file-upload';
1863
+ FileUpload.defaults = Object.freeze({
1864
+ i18n: {
1865
+ chooseFilesButton: 'Choose file',
1866
+ dropInstruction: 'or drop file',
1867
+ noFileChosen: 'No file chosen',
1868
+ multipleFilesChosen: {
1869
+ one: '%{count} file chosen',
1870
+ other: '%{count} files chosen'
1871
+ },
1872
+ enteredDropZone: 'Entered drop zone',
1873
+ leftDropZone: 'Left drop zone'
1874
+ }
1875
+ });
1876
+ FileUpload.schema = Object.freeze({
1877
+ properties: {
1878
+ i18n: {
1879
+ type: 'object'
1880
+ }
1881
+ }
1882
+ });
1883
+ function isContainingFiles(dataTransfer) {
1884
+ const hasNoTypesInfo = dataTransfer.types.length === 0;
1885
+ const isDraggingFiles = dataTransfer.types.some(type => type === 'Files');
1886
+ return hasNoTypesInfo || isDraggingFiles;
1887
+ }
1888
+
1889
+ /**
1890
+ * @typedef {HTMLInputElement & {files: FileList}} HTMLFileInputElement
1891
+ */
1892
+
1893
+ /**
1894
+ * File upload config
1895
+ *
1896
+ * @see {@link FileUpload.defaults}
1897
+ * @typedef {object} FileUploadConfig
1898
+ * @property {FileUploadTranslations} [i18n=FileUpload.defaults.i18n] - File upload translations
1899
+ */
1900
+
1901
+ /**
1902
+ * File upload translations
1903
+ *
1904
+ * @see {@link FileUpload.defaults.i18n}
1905
+ * @typedef {object} FileUploadTranslations
1906
+ *
1907
+ * Messages used by the component
1908
+ * @property {string} [chooseFile] - The text of the button that opens the file picker
1909
+ * @property {string} [dropInstruction] - The text informing users they can drop files
1910
+ * @property {TranslationPluralForms} [multipleFilesChosen] - The text displayed when multiple files
1911
+ * have been chosen by the user
1912
+ * @property {string} [noFileChosen] - The text to displayed when no file has been chosen by the user
1913
+ * @property {string} [enteredDropZone] - The text announced by assistive technology
1914
+ * when user drags files and enters the drop zone
1915
+ * @property {string} [leftDropZone] - The text announced by assistive technology
1916
+ * when user drags files and leaves the drop zone without dropping
1917
+ */
1918
+
1919
+ /**
1920
+ * @import { Schema } from '../../common/configuration.mjs'
1921
+ * @import { TranslationPluralForms } from '../../i18n.mjs'
1922
+ */
1923
+
1657
1924
  /**
1658
1925
  * Header component
1659
1926
  *
1660
1927
  * @preserve
1661
1928
  */
1662
- class Header extends GOVUKFrontendComponent {
1929
+ class Header extends Component {
1663
1930
  /**
1664
1931
  * Apply a matchMedia for desktop which will trigger a state sync if the
1665
1932
  * browser viewport moves between states.
@@ -1766,7 +2033,7 @@
1766
2033
  */
1767
2034
 
1768
2035
  /**
1769
- * @typedef {import('../../common/configuration.mjs').Schema} Schema
2036
+ * @import { Schema } from '../../common/configuration.mjs'
1770
2037
  */
1771
2038
  NotificationBanner.moduleName = 'govuk-notification-banner';
1772
2039
  NotificationBanner.defaults = Object.freeze({
@@ -1902,8 +2169,7 @@
1902
2169
  */
1903
2170
 
1904
2171
  /**
1905
- * @typedef {import('../../common/configuration.mjs').Schema} Schema
1906
- * @typedef {import('../../i18n.mjs').TranslationPluralForms} TranslationPluralForms
2172
+ * @import { Schema } from '../../common/configuration.mjs'
1907
2173
  */
1908
2174
  PasswordInput.moduleName = 'govuk-password-input';
1909
2175
  PasswordInput.defaults = Object.freeze({
@@ -1929,7 +2195,7 @@
1929
2195
  *
1930
2196
  * @preserve
1931
2197
  */
1932
- class Radios extends GOVUKFrontendComponent {
2198
+ class Radios extends Component {
1933
2199
  /**
1934
2200
  * Radios can be associated with a 'conditionally revealed' content block –
1935
2201
  * for example, a radio for 'Phone' could reveal an additional form field for
@@ -2012,7 +2278,7 @@
2012
2278
  *
2013
2279
  * @preserve
2014
2280
  */
2015
- class ServiceNavigation extends GOVUKFrontendComponent {
2281
+ class ServiceNavigation extends Component {
2016
2282
  /**
2017
2283
  * @param {Element | null} $root - HTML element to use for header
2018
2284
  */
@@ -2090,9 +2356,9 @@
2090
2356
  * Skip link component
2091
2357
  *
2092
2358
  * @preserve
2093
- * @augments GOVUKFrontendComponent<HTMLAnchorElement>
2359
+ * @augments Component<HTMLAnchorElement>
2094
2360
  */
2095
- class SkipLink extends GOVUKFrontendComponent {
2361
+ class SkipLink extends Component {
2096
2362
  /**
2097
2363
  * @param {Element | null} $root - HTML element to use for skip link
2098
2364
  * @throws {ElementError} when $root is not set or the wrong type
@@ -2143,7 +2409,7 @@
2143
2409
  *
2144
2410
  * @preserve
2145
2411
  */
2146
- class Tabs extends GOVUKFrontendComponent {
2412
+ class Tabs extends Component {
2147
2413
  /**
2148
2414
  * @param {Element | null} $root - HTML element to use for tabs
2149
2415
  */
@@ -2436,7 +2702,7 @@
2436
2702
  }
2437
2703
  return;
2438
2704
  }
2439
- const components = [[Accordion, config.accordion], [Button, config.button], [CharacterCount, config.characterCount], [Checkboxes], [ErrorSummary, config.errorSummary], [ExitThisPage, config.exitThisPage], [Header], [NotificationBanner, config.notificationBanner], [PasswordInput, config.passwordInput], [Radios], [ServiceNavigation], [SkipLink], [Tabs]];
2705
+ const components = [[Accordion, config.accordion], [Button, config.button], [CharacterCount, config.characterCount], [Checkboxes], [ErrorSummary, config.errorSummary], [ExitThisPage, config.exitThisPage], [FileUpload, config.fileUpload], [Header], [NotificationBanner, config.notificationBanner], [PasswordInput, config.passwordInput], [Radios], [ServiceNavigation], [SkipLink], [Tabs]];
2440
2706
  const options = {
2441
2707
  scope: (_config$scope = config.scope) != null ? _config$scope : document,
2442
2708
  onError: config.onError
@@ -2517,22 +2783,21 @@
2517
2783
  * @property {CharacterCountConfig} [characterCount] - Character Count config
2518
2784
  * @property {ErrorSummaryConfig} [errorSummary] - Error Summary config
2519
2785
  * @property {ExitThisPageConfig} [exitThisPage] - Exit This Page config
2786
+ * @property {FileUploadConfig} [fileUpload] - File Upload config
2520
2787
  * @property {NotificationBannerConfig} [notificationBanner] - Notification Banner config
2521
2788
  * @property {PasswordInputConfig} [passwordInput] - Password input config
2522
2789
  */
2523
2790
  /**
2524
2791
  * Config for individual components
2525
2792
  *
2526
- * @typedef {import('./components/accordion/accordion.mjs').AccordionConfig} AccordionConfig
2527
- * @typedef {import('./components/accordion/accordion.mjs').AccordionTranslations} AccordionTranslations
2528
- * @typedef {import('./components/button/button.mjs').ButtonConfig} ButtonConfig
2529
- * @typedef {import('./components/character-count/character-count.mjs').CharacterCountConfig} CharacterCountConfig
2530
- * @typedef {import('./components/character-count/character-count.mjs').CharacterCountTranslations} CharacterCountTranslations
2531
- * @typedef {import('./components/error-summary/error-summary.mjs').ErrorSummaryConfig} ErrorSummaryConfig
2532
- * @typedef {import('./components/exit-this-page/exit-this-page.mjs').ExitThisPageConfig} ExitThisPageConfig
2533
- * @typedef {import('./components/exit-this-page/exit-this-page.mjs').ExitThisPageTranslations} ExitThisPageTranslations
2534
- * @typedef {import('./components/notification-banner/notification-banner.mjs').NotificationBannerConfig} NotificationBannerConfig
2535
- * @typedef {import('./components/password-input/password-input.mjs').PasswordInputConfig} PasswordInputConfig
2793
+ * @import { AccordionConfig } from './components/accordion/accordion.mjs'
2794
+ * @import { ButtonConfig } from './components/button/button.mjs'
2795
+ * @import { CharacterCountConfig } from './components/character-count/character-count.mjs'
2796
+ * @import { ErrorSummaryConfig } from './components/error-summary/error-summary.mjs'
2797
+ * @import { ExitThisPageConfig } from './components/exit-this-page/exit-this-page.mjs'
2798
+ * @import { NotificationBannerConfig } from './components/notification-banner/notification-banner.mjs'
2799
+ * @import { PasswordInputConfig } from './components/password-input/password-input.mjs'
2800
+ * @import { FileUploadConfig } from './components/file-upload/file-upload.mjs'
2536
2801
  */
2537
2802
  /**
2538
2803
  * Component config keys, e.g. `accordion` and `characterCount`
@@ -2567,10 +2832,11 @@
2567
2832
  exports.Button = Button;
2568
2833
  exports.CharacterCount = CharacterCount;
2569
2834
  exports.Checkboxes = Checkboxes;
2570
- exports.Component = GOVUKFrontendComponent;
2835
+ exports.Component = Component;
2571
2836
  exports.ConfigurableComponent = ConfigurableComponent;
2572
2837
  exports.ErrorSummary = ErrorSummary;
2573
2838
  exports.ExitThisPage = ExitThisPage;
2839
+ exports.FileUpload = FileUpload;
2574
2840
  exports.Header = Header;
2575
2841
  exports.NotificationBanner = NotificationBanner;
2576
2842
  exports.PasswordInput = PasswordInput;