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
@@ -1,4 +1,4 @@
1
- const version = '5.8.0';
1
+ const version = '5.9.0';
2
2
 
3
3
  function getFragmentFromUrl(url) {
4
4
  if (!url.includes('#')) {
@@ -70,6 +70,9 @@ function formatErrorMessage(Component, message) {
70
70
  * @typedef ComponentWithModuleName
71
71
  * @property {string} moduleName - Name of the component
72
72
  */
73
+ /**
74
+ * @import { ObjectNested } from './configuration.mjs'
75
+ */
73
76
 
74
77
  class GOVUKFrontendError extends Error {
75
78
  constructor(...args) {
@@ -121,10 +124,10 @@ class InitError extends GOVUKFrontendError {
121
124
  }
122
125
  }
123
126
  /**
124
- * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
127
+ * @import { ComponentWithModuleName } from '../common/index.mjs'
125
128
  */
126
129
 
127
- class GOVUKFrontendComponent {
130
+ class Component {
128
131
  /**
129
132
  * Returns the root element of the component
130
133
  *
@@ -175,12 +178,12 @@ class GOVUKFrontendComponent {
175
178
  */
176
179
 
177
180
  /**
178
- * @typedef {typeof GOVUKFrontendComponent & ChildClass} ChildClassConstructor
181
+ * @typedef {typeof Component & ChildClass} ChildClassConstructor
179
182
  */
180
- GOVUKFrontendComponent.elementType = HTMLElement;
183
+ Component.elementType = HTMLElement;
181
184
 
182
185
  const configOverride = Symbol.for('configOverride');
183
- class ConfigurableComponent extends GOVUKFrontendComponent {
186
+ class ConfigurableComponent extends Component {
184
187
  [configOverride](param) {
185
188
  return {};
186
189
  }
@@ -198,7 +201,7 @@ class ConfigurableComponent extends GOVUKFrontendComponent {
198
201
  super($root);
199
202
  this._config = void 0;
200
203
  const childConstructor = this.constructor;
201
- if (typeof childConstructor.defaults === 'undefined') {
204
+ if (!isObject(childConstructor.defaults)) {
202
205
  throw new ConfigError(formatErrorMessage(childConstructor, 'Config passed as parameter into constructor but no defaults defined'));
203
206
  }
204
207
  const datasetConfig = normaliseDataset(childConstructor, this._$root.dataset);
@@ -230,16 +233,19 @@ function normaliseString(value, property) {
230
233
  return output;
231
234
  }
232
235
  function normaliseDataset(Component, dataset) {
233
- if (typeof Component.schema === 'undefined') {
236
+ if (!isObject(Component.schema)) {
234
237
  throw new ConfigError(formatErrorMessage(Component, 'Config passed as parameter into constructor but no schema defined'));
235
238
  }
236
239
  const out = {};
237
- for (const [field, property] of Object.entries(Component.schema.properties)) {
240
+ const entries = Object.entries(Component.schema.properties);
241
+ for (const entry of entries) {
242
+ const [namespace, property] = entry;
243
+ const field = namespace.toString();
238
244
  if (field in dataset) {
239
245
  out[field] = normaliseString(dataset[field], property);
240
246
  }
241
247
  if ((property == null ? void 0 : property.type) === 'object') {
242
- out[field] = extractConfigByNamespace(Component.schema, dataset, field);
248
+ out[field] = extractConfigByNamespace(Component.schema, dataset, namespace);
243
249
  }
244
250
  }
245
251
  return out;
@@ -285,13 +291,13 @@ function extractConfigByNamespace(schema, dataset, namespace) {
285
291
  return;
286
292
  }
287
293
  const newObject = {
288
- [namespace]: ({})
294
+ [namespace]: {}
289
295
  };
290
296
  for (const [key, value] of Object.entries(dataset)) {
291
297
  let current = newObject;
292
298
  const keyParts = key.split('.');
293
299
  for (const [index, name] of keyParts.entries()) {
294
- if (typeof current === 'object') {
300
+ if (isObject(current)) {
295
301
  if (index < keyParts.length - 1) {
296
302
  if (!isObject(current[name])) {
297
303
  current[name] = {};
@@ -308,9 +314,10 @@ function extractConfigByNamespace(schema, dataset, namespace) {
308
314
  /**
309
315
  * Schema for component config
310
316
  *
317
+ * @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType
311
318
  * @typedef {object} Schema
312
- * @property {{ [field: string]: SchemaProperty | undefined }} properties - Schema properties
313
- * @property {SchemaCondition[]} [anyOf] - List of schema conditions
319
+ * @property {Record<keyof ConfigurationType, SchemaProperty | undefined>} properties - Schema properties
320
+ * @property {SchemaCondition<ConfigurationType>[]} [anyOf] - List of schema conditions
314
321
  */
315
322
  /**
316
323
  * Schema property for component config
@@ -321,20 +328,21 @@ function extractConfigByNamespace(schema, dataset, namespace) {
321
328
  /**
322
329
  * Schema condition for component config
323
330
  *
331
+ * @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType
324
332
  * @typedef {object} SchemaCondition
325
- * @property {string[]} required - List of required config fields
333
+ * @property {(keyof ConfigurationType)[]} required - List of required config fields
326
334
  * @property {string} errorMessage - Error message when required config fields not provided
327
335
  */
328
336
  /**
329
- * @template {ObjectNested} [ConfigurationType={}]
337
+ * @template {Partial<Record<keyof ConfigurationType, unknown>>} [ConfigurationType=ObjectNested]
330
338
  * @typedef ChildClass
331
339
  * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
332
- * @property {Schema} [schema] - The schema of the component configuration
340
+ * @property {Schema<ConfigurationType>} [schema] - The schema of the component configuration
333
341
  * @property {ConfigurationType} [defaults] - The default values of the configuration of the component
334
342
  */
335
343
  /**
336
- * @template {ObjectNested} [ConfigurationType={}]
337
- * @typedef {typeof GOVUKFrontendComponent & ChildClass<ConfigurationType>} ChildClassConstructor<ConfigurationType>
344
+ * @template {Partial<Record<keyof ConfigurationType, unknown>>} [ConfigurationType=ObjectNested]
345
+ * @typedef {typeof Component & ChildClass<ConfigurationType>} ChildClassConstructor<ConfigurationType>
338
346
  */
339
347
 
340
348
  class I18n {
@@ -845,7 +853,7 @@ class Accordion extends ConfigurableComponent {
845
853
  */
846
854
 
847
855
  /**
848
- * @typedef {import('../../common/configuration.mjs').Schema} Schema
856
+ * @import { Schema } from '../../common/configuration.mjs'
849
857
  */
850
858
  Accordion.moduleName = 'govuk-accordion';
851
859
  Accordion.defaults = Object.freeze({
@@ -922,7 +930,7 @@ class Button extends ConfigurableComponent {
922
930
  */
923
931
 
924
932
  /**
925
- * @typedef {import('../../common/configuration.mjs').Schema} Schema
933
+ * @import { Schema } from '../../common/configuration.mjs'
926
934
  */
927
935
  Button.moduleName = 'govuk-button';
928
936
  Button.defaults = Object.freeze({
@@ -1175,8 +1183,8 @@ class CharacterCount extends ConfigurableComponent {
1175
1183
  */
1176
1184
 
1177
1185
  /**
1178
- * @typedef {import('../../common/configuration.mjs').Schema} Schema
1179
- * @typedef {import('../../i18n.mjs').TranslationPluralForms} TranslationPluralForms
1186
+ * @import { Schema } from '../../common/configuration.mjs'
1187
+ * @import { TranslationPluralForms } from '../../i18n.mjs'
1180
1188
  */
1181
1189
  CharacterCount.moduleName = 'govuk-character-count';
1182
1190
  CharacterCount.defaults = Object.freeze({
@@ -1234,7 +1242,7 @@ CharacterCount.schema = Object.freeze({
1234
1242
  *
1235
1243
  * @preserve
1236
1244
  */
1237
- class Checkboxes extends GOVUKFrontendComponent {
1245
+ class Checkboxes extends Component {
1238
1246
  /**
1239
1247
  * Checkboxes can be associated with a 'conditionally revealed' content block
1240
1248
  * – for example, a checkbox for 'Phone' could reveal an additional form field
@@ -1417,7 +1425,7 @@ class ErrorSummary extends ConfigurableComponent {
1417
1425
  */
1418
1426
 
1419
1427
  /**
1420
- * @typedef {import('../../common/configuration.mjs').Schema} Schema
1428
+ * @import { Schema } from '../../common/configuration.mjs'
1421
1429
  */
1422
1430
  ErrorSummary.moduleName = 'govuk-error-summary';
1423
1431
  ErrorSummary.defaults = Object.freeze({
@@ -1629,7 +1637,7 @@ class ExitThisPage extends ConfigurableComponent {
1629
1637
  */
1630
1638
 
1631
1639
  /**
1632
- * @typedef {import('../../common/configuration.mjs').Schema} Schema
1640
+ * @import { Schema } from '../../common/configuration.mjs'
1633
1641
  */
1634
1642
  ExitThisPage.moduleName = 'govuk-exit-this-page';
1635
1643
  ExitThisPage.defaults = Object.freeze({
@@ -1648,12 +1656,271 @@ ExitThisPage.schema = Object.freeze({
1648
1656
  }
1649
1657
  });
1650
1658
 
1659
+ /**
1660
+ * File upload component
1661
+ *
1662
+ * @preserve
1663
+ * @augments ConfigurableComponent<FileUploadConfig>
1664
+ */
1665
+ class FileUpload extends ConfigurableComponent {
1666
+ /**
1667
+ * @param {Element | null} $root - File input element
1668
+ * @param {FileUploadConfig} [config] - File Upload config
1669
+ */
1670
+ constructor($root, config = {}) {
1671
+ super($root, config);
1672
+ this.$input = void 0;
1673
+ this.$button = void 0;
1674
+ this.$status = void 0;
1675
+ this.i18n = void 0;
1676
+ this.id = void 0;
1677
+ const $input = this.$root.querySelector('input');
1678
+ if ($input === null) {
1679
+ throw new ElementError({
1680
+ component: FileUpload,
1681
+ identifier: 'File inputs (`<input type="file">`)'
1682
+ });
1683
+ }
1684
+ if ($input.type !== 'file') {
1685
+ throw new ElementError(formatErrorMessage(FileUpload, 'File input (`<input type="file">`) attribute (`type`) is not `file`'));
1686
+ }
1687
+ this.$input = $input;
1688
+ this.$input.setAttribute('hidden', 'true');
1689
+ if (!this.$input.id) {
1690
+ throw new ElementError({
1691
+ component: FileUpload,
1692
+ identifier: 'File input (`<input type="file">`) attribute (`id`)'
1693
+ });
1694
+ }
1695
+ this.id = this.$input.id;
1696
+ this.i18n = new I18n(this.config.i18n, {
1697
+ locale: closestAttributeValue(this.$root, 'lang')
1698
+ });
1699
+ const $label = this.findLabel();
1700
+ if (!$label.id) {
1701
+ $label.id = `${this.id}-label`;
1702
+ }
1703
+ this.$input.id = `${this.id}-input`;
1704
+ const $button = document.createElement('button');
1705
+ $button.classList.add('govuk-file-upload-button');
1706
+ $button.type = 'button';
1707
+ $button.id = this.id;
1708
+ $button.classList.add('govuk-file-upload-button--empty');
1709
+ const ariaDescribedBy = this.$input.getAttribute('aria-describedby');
1710
+ if (ariaDescribedBy) {
1711
+ $button.setAttribute('aria-describedby', ariaDescribedBy);
1712
+ }
1713
+ const $status = document.createElement('span');
1714
+ $status.className = 'govuk-body govuk-file-upload-button__status';
1715
+ $status.setAttribute('aria-live', 'polite');
1716
+ $status.innerText = this.i18n.t('noFileChosen');
1717
+ $button.appendChild($status);
1718
+ const commaSpan = document.createElement('span');
1719
+ commaSpan.className = 'govuk-visually-hidden';
1720
+ commaSpan.innerText = ', ';
1721
+ commaSpan.id = `${this.id}-comma`;
1722
+ $button.appendChild(commaSpan);
1723
+ const containerSpan = document.createElement('span');
1724
+ containerSpan.className = 'govuk-file-upload-button__pseudo-button-container';
1725
+ const buttonSpan = document.createElement('span');
1726
+ buttonSpan.className = 'govuk-button govuk-button--secondary govuk-file-upload-button__pseudo-button';
1727
+ buttonSpan.innerText = this.i18n.t('chooseFilesButton');
1728
+ containerSpan.appendChild(buttonSpan);
1729
+ containerSpan.insertAdjacentText('beforeend', ' ');
1730
+ const instructionSpan = document.createElement('span');
1731
+ instructionSpan.className = 'govuk-body govuk-file-upload-button__instruction';
1732
+ instructionSpan.innerText = this.i18n.t('dropInstruction');
1733
+ containerSpan.appendChild(instructionSpan);
1734
+ $button.appendChild(containerSpan);
1735
+ $button.setAttribute('aria-labelledby', `${$label.id} ${commaSpan.id} ${$button.id}`);
1736
+ $button.addEventListener('click', this.onClick.bind(this));
1737
+ $button.addEventListener('dragover', event => {
1738
+ event.preventDefault();
1739
+ });
1740
+ this.$root.insertAdjacentElement('afterbegin', $button);
1741
+ this.$input.setAttribute('tabindex', '-1');
1742
+ this.$input.setAttribute('aria-hidden', 'true');
1743
+ this.$button = $button;
1744
+ this.$status = $status;
1745
+ this.$input.addEventListener('change', this.onChange.bind(this));
1746
+ this.updateDisabledState();
1747
+ this.observeDisabledState();
1748
+ this.$announcements = document.createElement('span');
1749
+ this.$announcements.classList.add('govuk-file-upload-announcements');
1750
+ this.$announcements.classList.add('govuk-visually-hidden');
1751
+ this.$announcements.setAttribute('aria-live', 'assertive');
1752
+ this.$root.insertAdjacentElement('afterend', this.$announcements);
1753
+ this.$button.addEventListener('drop', this.onDrop.bind(this));
1754
+ document.addEventListener('dragenter', this.updateDropzoneVisibility.bind(this));
1755
+ document.addEventListener('dragenter', () => {
1756
+ this.enteredAnotherElement = true;
1757
+ });
1758
+ document.addEventListener('dragleave', () => {
1759
+ if (!this.enteredAnotherElement && !this.$button.disabled) {
1760
+ this.hideDraggingState();
1761
+ this.$announcements.innerText = this.i18n.t('leftDropZone');
1762
+ }
1763
+ this.enteredAnotherElement = false;
1764
+ });
1765
+ }
1766
+
1767
+ /**
1768
+ * Updates the visibility of the dropzone as users enters the various elements on the page
1769
+ *
1770
+ * @param {DragEvent} event - The `dragenter` event
1771
+ */
1772
+ updateDropzoneVisibility(event) {
1773
+ if (this.$button.disabled) return;
1774
+ if (event.target instanceof Node) {
1775
+ if (this.$root.contains(event.target)) {
1776
+ if (event.dataTransfer && isContainingFiles(event.dataTransfer)) {
1777
+ if (!this.$button.classList.contains('govuk-file-upload-button--dragging')) {
1778
+ this.showDraggingState();
1779
+ this.$announcements.innerText = this.i18n.t('enteredDropZone');
1780
+ }
1781
+ }
1782
+ } else {
1783
+ if (this.$button.classList.contains('govuk-file-upload-button--dragging')) {
1784
+ this.hideDraggingState();
1785
+ this.$announcements.innerText = this.i18n.t('leftDropZone');
1786
+ }
1787
+ }
1788
+ }
1789
+ }
1790
+ showDraggingState() {
1791
+ this.$button.classList.add('govuk-file-upload-button--dragging');
1792
+ }
1793
+ hideDraggingState() {
1794
+ this.$button.classList.remove('govuk-file-upload-button--dragging');
1795
+ }
1796
+
1797
+ /**
1798
+ * Handles user dropping on the component
1799
+ *
1800
+ * @param {DragEvent} event - The `dragenter` event
1801
+ */
1802
+ onDrop(event) {
1803
+ event.preventDefault();
1804
+ if (event.dataTransfer && isContainingFiles(event.dataTransfer)) {
1805
+ this.$input.files = event.dataTransfer.files;
1806
+ this.$input.dispatchEvent(new CustomEvent('change'));
1807
+ this.hideDraggingState();
1808
+ }
1809
+ }
1810
+ onChange() {
1811
+ const fileCount = this.$input.files.length;
1812
+ if (fileCount === 0) {
1813
+ this.$status.innerText = this.i18n.t('noFileChosen');
1814
+ this.$button.classList.add('govuk-file-upload-button--empty');
1815
+ } else {
1816
+ if (fileCount === 1) {
1817
+ this.$status.innerText = this.$input.files[0].name;
1818
+ } else {
1819
+ this.$status.innerText = this.i18n.t('multipleFilesChosen', {
1820
+ count: fileCount
1821
+ });
1822
+ }
1823
+ this.$button.classList.remove('govuk-file-upload-button--empty');
1824
+ }
1825
+ }
1826
+ findLabel() {
1827
+ const $label = document.querySelector(`label[for="${this.$input.id}"]`);
1828
+ if (!$label) {
1829
+ throw new ElementError({
1830
+ component: FileUpload,
1831
+ identifier: `Field label (\`<label for=${this.$input.id}>\`)`
1832
+ });
1833
+ }
1834
+ return $label;
1835
+ }
1836
+ onClick() {
1837
+ this.$input.click();
1838
+ }
1839
+ observeDisabledState() {
1840
+ const observer = new MutationObserver(mutationList => {
1841
+ for (const mutation of mutationList) {
1842
+ if (mutation.type === 'attributes' && mutation.attributeName === 'disabled') {
1843
+ this.updateDisabledState();
1844
+ }
1845
+ }
1846
+ });
1847
+ observer.observe(this.$input, {
1848
+ attributes: true
1849
+ });
1850
+ }
1851
+ updateDisabledState() {
1852
+ this.$button.disabled = this.$input.disabled;
1853
+ this.$root.classList.toggle('govuk-drop-zone--disabled', this.$button.disabled);
1854
+ }
1855
+ }
1856
+ FileUpload.moduleName = 'govuk-file-upload';
1857
+ FileUpload.defaults = Object.freeze({
1858
+ i18n: {
1859
+ chooseFilesButton: 'Choose file',
1860
+ dropInstruction: 'or drop file',
1861
+ noFileChosen: 'No file chosen',
1862
+ multipleFilesChosen: {
1863
+ one: '%{count} file chosen',
1864
+ other: '%{count} files chosen'
1865
+ },
1866
+ enteredDropZone: 'Entered drop zone',
1867
+ leftDropZone: 'Left drop zone'
1868
+ }
1869
+ });
1870
+ FileUpload.schema = Object.freeze({
1871
+ properties: {
1872
+ i18n: {
1873
+ type: 'object'
1874
+ }
1875
+ }
1876
+ });
1877
+ function isContainingFiles(dataTransfer) {
1878
+ const hasNoTypesInfo = dataTransfer.types.length === 0;
1879
+ const isDraggingFiles = dataTransfer.types.some(type => type === 'Files');
1880
+ return hasNoTypesInfo || isDraggingFiles;
1881
+ }
1882
+
1883
+ /**
1884
+ * @typedef {HTMLInputElement & {files: FileList}} HTMLFileInputElement
1885
+ */
1886
+
1887
+ /**
1888
+ * File upload config
1889
+ *
1890
+ * @see {@link FileUpload.defaults}
1891
+ * @typedef {object} FileUploadConfig
1892
+ * @property {FileUploadTranslations} [i18n=FileUpload.defaults.i18n] - File upload translations
1893
+ */
1894
+
1895
+ /**
1896
+ * File upload translations
1897
+ *
1898
+ * @see {@link FileUpload.defaults.i18n}
1899
+ * @typedef {object} FileUploadTranslations
1900
+ *
1901
+ * Messages used by the component
1902
+ * @property {string} [chooseFile] - The text of the button that opens the file picker
1903
+ * @property {string} [dropInstruction] - The text informing users they can drop files
1904
+ * @property {TranslationPluralForms} [multipleFilesChosen] - The text displayed when multiple files
1905
+ * have been chosen by the user
1906
+ * @property {string} [noFileChosen] - The text to displayed when no file has been chosen by the user
1907
+ * @property {string} [enteredDropZone] - The text announced by assistive technology
1908
+ * when user drags files and enters the drop zone
1909
+ * @property {string} [leftDropZone] - The text announced by assistive technology
1910
+ * when user drags files and leaves the drop zone without dropping
1911
+ */
1912
+
1913
+ /**
1914
+ * @import { Schema } from '../../common/configuration.mjs'
1915
+ * @import { TranslationPluralForms } from '../../i18n.mjs'
1916
+ */
1917
+
1651
1918
  /**
1652
1919
  * Header component
1653
1920
  *
1654
1921
  * @preserve
1655
1922
  */
1656
- class Header extends GOVUKFrontendComponent {
1923
+ class Header extends Component {
1657
1924
  /**
1658
1925
  * Apply a matchMedia for desktop which will trigger a state sync if the
1659
1926
  * browser viewport moves between states.
@@ -1760,7 +2027,7 @@ class NotificationBanner extends ConfigurableComponent {
1760
2027
  */
1761
2028
 
1762
2029
  /**
1763
- * @typedef {import('../../common/configuration.mjs').Schema} Schema
2030
+ * @import { Schema } from '../../common/configuration.mjs'
1764
2031
  */
1765
2032
  NotificationBanner.moduleName = 'govuk-notification-banner';
1766
2033
  NotificationBanner.defaults = Object.freeze({
@@ -1896,8 +2163,7 @@ class PasswordInput extends ConfigurableComponent {
1896
2163
  */
1897
2164
 
1898
2165
  /**
1899
- * @typedef {import('../../common/configuration.mjs').Schema} Schema
1900
- * @typedef {import('../../i18n.mjs').TranslationPluralForms} TranslationPluralForms
2166
+ * @import { Schema } from '../../common/configuration.mjs'
1901
2167
  */
1902
2168
  PasswordInput.moduleName = 'govuk-password-input';
1903
2169
  PasswordInput.defaults = Object.freeze({
@@ -1923,7 +2189,7 @@ PasswordInput.schema = Object.freeze({
1923
2189
  *
1924
2190
  * @preserve
1925
2191
  */
1926
- class Radios extends GOVUKFrontendComponent {
2192
+ class Radios extends Component {
1927
2193
  /**
1928
2194
  * Radios can be associated with a 'conditionally revealed' content block –
1929
2195
  * for example, a radio for 'Phone' could reveal an additional form field for
@@ -2006,7 +2272,7 @@ Radios.moduleName = 'govuk-radios';
2006
2272
  *
2007
2273
  * @preserve
2008
2274
  */
2009
- class ServiceNavigation extends GOVUKFrontendComponent {
2275
+ class ServiceNavigation extends Component {
2010
2276
  /**
2011
2277
  * @param {Element | null} $root - HTML element to use for header
2012
2278
  */
@@ -2084,9 +2350,9 @@ ServiceNavigation.moduleName = 'govuk-service-navigation';
2084
2350
  * Skip link component
2085
2351
  *
2086
2352
  * @preserve
2087
- * @augments GOVUKFrontendComponent<HTMLAnchorElement>
2353
+ * @augments Component<HTMLAnchorElement>
2088
2354
  */
2089
- class SkipLink extends GOVUKFrontendComponent {
2355
+ class SkipLink extends Component {
2090
2356
  /**
2091
2357
  * @param {Element | null} $root - HTML element to use for skip link
2092
2358
  * @throws {ElementError} when $root is not set or the wrong type
@@ -2137,7 +2403,7 @@ SkipLink.moduleName = 'govuk-skip-link';
2137
2403
  *
2138
2404
  * @preserve
2139
2405
  */
2140
- class Tabs extends GOVUKFrontendComponent {
2406
+ class Tabs extends Component {
2141
2407
  /**
2142
2408
  * @param {Element | null} $root - HTML element to use for tabs
2143
2409
  */
@@ -2430,7 +2696,7 @@ function initAll(config) {
2430
2696
  }
2431
2697
  return;
2432
2698
  }
2433
- 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]];
2699
+ 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]];
2434
2700
  const options = {
2435
2701
  scope: (_config$scope = config.scope) != null ? _config$scope : document,
2436
2702
  onError: config.onError
@@ -2511,22 +2777,21 @@ function createAll(Component, config, createAllOptions) {
2511
2777
  * @property {CharacterCountConfig} [characterCount] - Character Count config
2512
2778
  * @property {ErrorSummaryConfig} [errorSummary] - Error Summary config
2513
2779
  * @property {ExitThisPageConfig} [exitThisPage] - Exit This Page config
2780
+ * @property {FileUploadConfig} [fileUpload] - File Upload config
2514
2781
  * @property {NotificationBannerConfig} [notificationBanner] - Notification Banner config
2515
2782
  * @property {PasswordInputConfig} [passwordInput] - Password input config
2516
2783
  */
2517
2784
  /**
2518
2785
  * Config for individual components
2519
2786
  *
2520
- * @typedef {import('./components/accordion/accordion.mjs').AccordionConfig} AccordionConfig
2521
- * @typedef {import('./components/accordion/accordion.mjs').AccordionTranslations} AccordionTranslations
2522
- * @typedef {import('./components/button/button.mjs').ButtonConfig} ButtonConfig
2523
- * @typedef {import('./components/character-count/character-count.mjs').CharacterCountConfig} CharacterCountConfig
2524
- * @typedef {import('./components/character-count/character-count.mjs').CharacterCountTranslations} CharacterCountTranslations
2525
- * @typedef {import('./components/error-summary/error-summary.mjs').ErrorSummaryConfig} ErrorSummaryConfig
2526
- * @typedef {import('./components/exit-this-page/exit-this-page.mjs').ExitThisPageConfig} ExitThisPageConfig
2527
- * @typedef {import('./components/exit-this-page/exit-this-page.mjs').ExitThisPageTranslations} ExitThisPageTranslations
2528
- * @typedef {import('./components/notification-banner/notification-banner.mjs').NotificationBannerConfig} NotificationBannerConfig
2529
- * @typedef {import('./components/password-input/password-input.mjs').PasswordInputConfig} PasswordInputConfig
2787
+ * @import { AccordionConfig } from './components/accordion/accordion.mjs'
2788
+ * @import { ButtonConfig } from './components/button/button.mjs'
2789
+ * @import { CharacterCountConfig } from './components/character-count/character-count.mjs'
2790
+ * @import { ErrorSummaryConfig } from './components/error-summary/error-summary.mjs'
2791
+ * @import { ExitThisPageConfig } from './components/exit-this-page/exit-this-page.mjs'
2792
+ * @import { NotificationBannerConfig } from './components/notification-banner/notification-banner.mjs'
2793
+ * @import { PasswordInputConfig } from './components/password-input/password-input.mjs'
2794
+ * @import { FileUploadConfig } from './components/file-upload/file-upload.mjs'
2530
2795
  */
2531
2796
  /**
2532
2797
  * Component config keys, e.g. `accordion` and `characterCount`
@@ -2557,5 +2822,5 @@ function createAll(Component, config, createAllOptions) {
2557
2822
  * @property {OnErrorCallback<ComponentClass>} [onError] - callback function if error throw by component on init
2558
2823
  */
2559
2824
 
2560
- export { Accordion, Button, CharacterCount, Checkboxes, GOVUKFrontendComponent as Component, ConfigurableComponent, ErrorSummary, ExitThisPage, Header, NotificationBanner, PasswordInput, Radios, ServiceNavigation, SkipLink, Tabs, createAll, initAll, isSupported, version };
2825
+ export { Accordion, Button, CharacterCount, Checkboxes, Component, ConfigurableComponent, ErrorSummary, ExitThisPage, FileUpload, Header, NotificationBanner, PasswordInput, Radios, ServiceNavigation, SkipLink, Tabs, createAll, initAll, isSupported, version };
2561
2826
  //# sourceMappingURL=all.bundle.mjs.map