govuk_publishing_components 35.11.0 → 35.12.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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-core.js +2 -48
  3. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-form-tracker.js +5 -0
  4. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-link-tracker.js +2 -2
  5. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-schemas.js +51 -5
  6. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-scroll-tracker.js +225 -0
  7. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/init-ga4.js +0 -5
  8. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4.js +1 -0
  9. data/app/views/govuk_publishing_components/components/_attachment.html.erb +3 -1
  10. data/app/views/govuk_publishing_components/components/_tabs.html.erb +30 -14
  11. data/app/views/govuk_publishing_components/components/docs/tabs.yml +25 -2
  12. data/lib/govuk_publishing_components/version.rb +1 -1
  13. data/node_modules/govuk-frontend/govuk/all.js +406 -1
  14. data/node_modules/govuk-frontend/govuk/all.js.map +1 -1
  15. data/node_modules/govuk-frontend/govuk/common/govuk-frontend-version.js +1 -1
  16. data/node_modules/govuk-frontend/govuk/components/_all.scss +2 -1
  17. data/node_modules/govuk-frontend/govuk/components/back-link/_index.scss +8 -0
  18. data/node_modules/govuk-frontend/govuk/components/back-link/fixtures.json +9 -0
  19. data/node_modules/govuk-frontend/govuk/components/breadcrumbs/_index.scss +12 -0
  20. data/node_modules/govuk-frontend/govuk/components/breadcrumbs/fixtures.json +21 -0
  21. data/node_modules/govuk-frontend/govuk/components/button/_index.scss +41 -3
  22. data/node_modules/govuk-frontend/govuk/components/button/fixtures.json +44 -0
  23. data/node_modules/govuk-frontend/govuk/components/checkboxes/macro-options.json +9 -8
  24. data/node_modules/govuk-frontend/govuk/components/exit-this-page/README.md +15 -0
  25. data/node_modules/govuk-frontend/govuk/components/exit-this-page/_exit-this-page.scss +2 -0
  26. data/node_modules/govuk-frontend/govuk/components/exit-this-page/_index.scss +97 -0
  27. data/node_modules/govuk-frontend/govuk/components/exit-this-page/exit-this-page.js +2120 -0
  28. data/node_modules/govuk-frontend/govuk/components/exit-this-page/exit-this-page.js.map +1 -0
  29. data/node_modules/govuk-frontend/govuk/components/exit-this-page/fixtures.json +50 -0
  30. data/node_modules/govuk-frontend/govuk/components/exit-this-page/macro-options.json +62 -0
  31. data/node_modules/govuk-frontend/govuk/components/exit-this-page/macro.njk +3 -0
  32. data/node_modules/govuk-frontend/govuk/components/exit-this-page/template.njk +16 -0
  33. data/node_modules/govuk-frontend/govuk/components/radios/macro-options.json +9 -8
  34. data/node_modules/govuk-frontend/govuk/core/_govuk-frontend-version.scss +1 -1
  35. data/node_modules/govuk-frontend/govuk/helpers/_visually-hidden.scss +12 -0
  36. data/node_modules/govuk-frontend/govuk/objects/_template.scss +20 -0
  37. data/node_modules/govuk-frontend/govuk-esm/all.mjs +8 -0
  38. data/node_modules/govuk-frontend/govuk-esm/all.mjs.map +1 -1
  39. data/node_modules/govuk-frontend/govuk-esm/common/govuk-frontend-version.mjs +1 -1
  40. data/node_modules/govuk-frontend/govuk-esm/components/exit-this-page/exit-this-page.mjs +406 -0
  41. data/node_modules/govuk-frontend/govuk-esm/components/exit-this-page/exit-this-page.mjs.map +1 -0
  42. data/node_modules/govuk-frontend/govuk-prototype-kit.config.json +4 -0
  43. data/node_modules/govuk-frontend/package.json +4 -2
  44. metadata +14 -2
@@ -1 +1 @@
1
- {"version":3,"file":"all.mjs","sources":["../../src/govuk/all.mjs"],"sourcesContent":["import { version } from './common/govuk-frontend-version.mjs'\nimport { nodeListForEach } from './common/index.mjs'\nimport Accordion from './components/accordion/accordion.mjs'\nimport Button from './components/button/button.mjs'\nimport CharacterCount from './components/character-count/character-count.mjs'\nimport Checkboxes from './components/checkboxes/checkboxes.mjs'\nimport Details from './components/details/details.mjs'\nimport ErrorSummary from './components/error-summary/error-summary.mjs'\nimport Header from './components/header/header.mjs'\nimport NotificationBanner from './components/notification-banner/notification-banner.mjs'\nimport Radios from './components/radios/radios.mjs'\nimport SkipLink from './components/skip-link/skip-link.mjs'\nimport Tabs from './components/tabs/tabs.mjs'\n\n/**\n * Initialise all components\n *\n * Use the `data-module` attributes to find, instantiate and init all of the\n * components provided as part of GOV.UK Frontend.\n *\n * @param {Config} [config] - Config for all components\n */\nfunction initAll (config) {\n config = typeof config !== 'undefined' ? config : {}\n\n // Allow the user to initialise GOV.UK Frontend in only certain sections of the page\n // Defaults to the entire document if nothing is set.\n var $scope = config.scope instanceof HTMLElement ? config.scope : document\n\n var $accordions = $scope.querySelectorAll('[data-module=\"govuk-accordion\"]')\n nodeListForEach($accordions, function ($accordion) {\n new Accordion($accordion, config.accordion).init()\n })\n\n var $buttons = $scope.querySelectorAll('[data-module=\"govuk-button\"]')\n nodeListForEach($buttons, function ($button) {\n new Button($button, config.button).init()\n })\n\n var $characterCounts = $scope.querySelectorAll('[data-module=\"govuk-character-count\"]')\n nodeListForEach($characterCounts, function ($characterCount) {\n new CharacterCount($characterCount, config.characterCount).init()\n })\n\n var $checkboxes = $scope.querySelectorAll('[data-module=\"govuk-checkboxes\"]')\n nodeListForEach($checkboxes, function ($checkbox) {\n new Checkboxes($checkbox).init()\n })\n\n var $details = $scope.querySelectorAll('[data-module=\"govuk-details\"]')\n nodeListForEach($details, function ($detail) {\n new Details($detail).init()\n })\n\n // Find first error summary module to enhance.\n var $errorSummary = $scope.querySelector('[data-module=\"govuk-error-summary\"]')\n if ($errorSummary) {\n new ErrorSummary($errorSummary, config.errorSummary).init()\n }\n\n // Find first header module to enhance.\n var $header = $scope.querySelector('[data-module=\"govuk-header\"]')\n if ($header) {\n new Header($header).init()\n }\n\n var $notificationBanners = $scope.querySelectorAll('[data-module=\"govuk-notification-banner\"]')\n nodeListForEach($notificationBanners, function ($notificationBanner) {\n new NotificationBanner($notificationBanner, config.notificationBanner).init()\n })\n\n var $radios = $scope.querySelectorAll('[data-module=\"govuk-radios\"]')\n nodeListForEach($radios, function ($radio) {\n new Radios($radio).init()\n })\n\n // Find first skip link module to enhance.\n var $skipLink = $scope.querySelector('[data-module=\"govuk-skip-link\"]')\n if ($skipLink) {\n new SkipLink($skipLink).init()\n }\n\n var $tabs = $scope.querySelectorAll('[data-module=\"govuk-tabs\"]')\n nodeListForEach($tabs, function ($tabs) {\n new Tabs($tabs).init()\n })\n}\n\nexport {\n initAll,\n version,\n\n // Components\n Accordion,\n Button,\n Details,\n CharacterCount,\n Checkboxes,\n ErrorSummary,\n Header,\n NotificationBanner,\n Radios,\n SkipLink,\n Tabs\n}\n\n/**\n * Config for all components\n *\n * @typedef {object} Config\n * @property {Element} [scope=document] - Scope to query for components\n * @property {import('./components/accordion/accordion.mjs').AccordionConfig} [accordion] - Accordion config\n * @property {import('./components/button/button.mjs').ButtonConfig} [button] - Button config\n * @property {import('./components/character-count/character-count.mjs').CharacterCountConfig} [characterCount] - Character Count config\n * @property {import('./components/error-summary/error-summary.mjs').ErrorSummaryConfig} [errorSummary] - Error Summary config\n * @property {import('./components/notification-banner/notification-banner.mjs').NotificationBannerConfig} [notificationBanner] - Notification Banner config\n */\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAcA;;;;;;;;AAQA,SAAS,OAAO,EAAE,MAAM,EAAE;EACxB,MAAM,GAAG,OAAO,MAAM,KAAK,WAAW,GAAG,MAAM,GAAG,GAAE;;;;EAIpD,IAAI,MAAM,GAAG,MAAM,CAAC,KAAK,YAAY,WAAW,GAAG,MAAM,CAAC,KAAK,GAAG,SAAQ;;EAE1E,IAAI,WAAW,GAAG,MAAM,CAAC,gBAAgB,CAAC,iCAAiC,EAAC;EAC5E,eAAe,CAAC,WAAW,EAAE,UAAU,UAAU,EAAE;IACjD,IAAI,SAAS,CAAC,UAAU,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,GAAE;GACnD,EAAC;;EAEF,IAAI,QAAQ,GAAG,MAAM,CAAC,gBAAgB,CAAC,8BAA8B,EAAC;EACtE,eAAe,CAAC,QAAQ,EAAE,UAAU,OAAO,EAAE;IAC3C,IAAI,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,GAAE;GAC1C,EAAC;;EAEF,IAAI,gBAAgB,GAAG,MAAM,CAAC,gBAAgB,CAAC,uCAAuC,EAAC;EACvF,eAAe,CAAC,gBAAgB,EAAE,UAAU,eAAe,EAAE;IAC3D,IAAI,cAAc,CAAC,eAAe,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC,IAAI,GAAE;GAClE,EAAC;;EAEF,IAAI,WAAW,GAAG,MAAM,CAAC,gBAAgB,CAAC,kCAAkC,EAAC;EAC7E,eAAe,CAAC,WAAW,EAAE,UAAU,SAAS,EAAE;IAChD,IAAI,UAAU,CAAC,SAAS,CAAC,CAAC,IAAI,GAAE;GACjC,EAAC;;EAEF,IAAI,QAAQ,GAAG,MAAM,CAAC,gBAAgB,CAAC,+BAA+B,EAAC;EACvE,eAAe,CAAC,QAAQ,EAAE,UAAU,OAAO,EAAE;IAC3C,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,GAAE;GAC5B,EAAC;;;EAGF,IAAI,aAAa,GAAG,MAAM,CAAC,aAAa,CAAC,qCAAqC,EAAC;EAC/E,IAAI,aAAa,EAAE;IACjB,IAAI,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,GAAE;GAC5D;;;EAGD,IAAI,OAAO,GAAG,MAAM,CAAC,aAAa,CAAC,8BAA8B,EAAC;EAClE,IAAI,OAAO,EAAE;IACX,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,GAAE;GAC3B;;EAED,IAAI,oBAAoB,GAAG,MAAM,CAAC,gBAAgB,CAAC,2CAA2C,EAAC;EAC/F,eAAe,CAAC,oBAAoB,EAAE,UAAU,mBAAmB,EAAE;IACnE,IAAI,kBAAkB,CAAC,mBAAmB,EAAE,MAAM,CAAC,kBAAkB,CAAC,CAAC,IAAI,GAAE;GAC9E,EAAC;;EAEF,IAAI,OAAO,GAAG,MAAM,CAAC,gBAAgB,CAAC,8BAA8B,EAAC;EACrE,eAAe,CAAC,OAAO,EAAE,UAAU,MAAM,EAAE;IACzC,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,GAAE;GAC1B,EAAC;;;EAGF,IAAI,SAAS,GAAG,MAAM,CAAC,aAAa,CAAC,iCAAiC,EAAC;EACvE,IAAI,SAAS,EAAE;IACb,IAAI,QAAQ,CAAC,SAAS,CAAC,CAAC,IAAI,GAAE;GAC/B;;EAED,IAAI,KAAK,GAAG,MAAM,CAAC,gBAAgB,CAAC,4BAA4B,EAAC;EACjE,eAAe,CAAC,KAAK,EAAE,UAAU,KAAK,EAAE;IACtC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,GAAE;GACvB,EAAC;CACH;AACD,AAkBA;;;;;;;;;;;GAWG;;;;"}
1
+ {"version":3,"file":"all.mjs","sources":["../../src/govuk/all.mjs"],"sourcesContent":["import { version } from './common/govuk-frontend-version.mjs'\nimport { nodeListForEach } from './common/index.mjs'\nimport Accordion from './components/accordion/accordion.mjs'\nimport Button from './components/button/button.mjs'\nimport CharacterCount from './components/character-count/character-count.mjs'\nimport Checkboxes from './components/checkboxes/checkboxes.mjs'\nimport Details from './components/details/details.mjs'\nimport ErrorSummary from './components/error-summary/error-summary.mjs'\nimport ExitThisPage from './components/exit-this-page/exit-this-page.mjs'\nimport Header from './components/header/header.mjs'\nimport NotificationBanner from './components/notification-banner/notification-banner.mjs'\nimport Radios from './components/radios/radios.mjs'\nimport SkipLink from './components/skip-link/skip-link.mjs'\nimport Tabs from './components/tabs/tabs.mjs'\n\n/**\n * Initialise all components\n *\n * Use the `data-module` attributes to find, instantiate and init all of the\n * components provided as part of GOV.UK Frontend.\n *\n * @param {Config} [config] - Config for all components\n */\nfunction initAll (config) {\n config = typeof config !== 'undefined' ? config : {}\n\n // Allow the user to initialise GOV.UK Frontend in only certain sections of the page\n // Defaults to the entire document if nothing is set.\n var $scope = config.scope instanceof HTMLElement ? config.scope : document\n\n var $accordions = $scope.querySelectorAll('[data-module=\"govuk-accordion\"]')\n nodeListForEach($accordions, function ($accordion) {\n new Accordion($accordion, config.accordion).init()\n })\n\n var $buttons = $scope.querySelectorAll('[data-module=\"govuk-button\"]')\n nodeListForEach($buttons, function ($button) {\n new Button($button, config.button).init()\n })\n\n var $characterCounts = $scope.querySelectorAll('[data-module=\"govuk-character-count\"]')\n nodeListForEach($characterCounts, function ($characterCount) {\n new CharacterCount($characterCount, config.characterCount).init()\n })\n\n var $checkboxes = $scope.querySelectorAll('[data-module=\"govuk-checkboxes\"]')\n nodeListForEach($checkboxes, function ($checkbox) {\n new Checkboxes($checkbox).init()\n })\n\n var $details = $scope.querySelectorAll('[data-module=\"govuk-details\"]')\n nodeListForEach($details, function ($detail) {\n new Details($detail).init()\n })\n\n // Find first error summary module to enhance.\n var $errorSummary = $scope.querySelector('[data-module=\"govuk-error-summary\"]')\n if ($errorSummary) {\n new ErrorSummary($errorSummary, config.errorSummary).init()\n }\n\n var $exitThisPageButtons = $scope.querySelectorAll('[data-module=\"govuk-exit-this-page\"]')\n nodeListForEach($exitThisPageButtons, function ($button) {\n new ExitThisPage($button, config.exitThisPage).init()\n })\n\n // Find first header module to enhance.\n var $header = $scope.querySelector('[data-module=\"govuk-header\"]')\n if ($header) {\n new Header($header).init()\n }\n\n var $notificationBanners = $scope.querySelectorAll('[data-module=\"govuk-notification-banner\"]')\n nodeListForEach($notificationBanners, function ($notificationBanner) {\n new NotificationBanner($notificationBanner, config.notificationBanner).init()\n })\n\n var $radios = $scope.querySelectorAll('[data-module=\"govuk-radios\"]')\n nodeListForEach($radios, function ($radio) {\n new Radios($radio).init()\n })\n\n // Find first skip link module to enhance.\n var $skipLink = $scope.querySelector('[data-module=\"govuk-skip-link\"]')\n if ($skipLink) {\n new SkipLink($skipLink).init()\n }\n\n var $tabs = $scope.querySelectorAll('[data-module=\"govuk-tabs\"]')\n nodeListForEach($tabs, function ($tabs) {\n new Tabs($tabs).init()\n })\n}\n\nexport {\n initAll,\n version,\n\n // Components\n Accordion,\n Button,\n Details,\n CharacterCount,\n Checkboxes,\n ErrorSummary,\n ExitThisPage,\n Header,\n NotificationBanner,\n Radios,\n SkipLink,\n Tabs\n}\n\n/**\n * Config for all components\n *\n * @typedef {object} Config\n * @property {Element} [scope=document] - Scope to query for components\n * @property {import('./components/accordion/accordion.mjs').AccordionConfig} [accordion] - Accordion config\n * @property {import('./components/button/button.mjs').ButtonConfig} [button] - Button config\n * @property {import('./components/character-count/character-count.mjs').CharacterCountConfig} [characterCount] - Character Count config\n * @property {import('./components/error-summary/error-summary.mjs').ErrorSummaryConfig} [errorSummary] - Error Summary config\n * @property {import('./components/exit-this-page/exit-this-page.mjs').ExitThisPageConfig} [exitThisPage] - Exit This Page config\n * @property {import('./components/notification-banner/notification-banner.mjs').NotificationBannerConfig} [notificationBanner] - Notification Banner config\n */\n"],"names":[],"mappings":"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"}
@@ -3,7 +3,7 @@
3
3
  * It doesn't need to be updated manually.
4
4
  */
5
5
 
6
- var version = '4.6.0';
6
+ var version = '4.7.0';
7
7
 
8
8
  export { version };
9
9
  //# sourceMappingURL=common/govuk-frontend-version.mjs.map
@@ -0,0 +1,406 @@
1
+ import { normaliseDataset } from '../../common/normalise-dataset.mjs';
2
+ import { I18n } from '../../i18n.mjs';
3
+ import '../../vendor/polyfills/Element/prototype/classList.mjs';
4
+ import '../../vendor/polyfills/Event.mjs';
5
+ import '../../vendor/polyfills/Element/prototype/dataset.mjs';
6
+ import '../../vendor/polyfills/Function/prototype/bind.mjs';
7
+ import { nodeListForEach, mergeConfigs, extractConfigByNamespace } from '../../common/index.mjs';
8
+
9
+ /* eslint-disable es-x/no-function-prototype-bind -- Polyfill imported */
10
+
11
+ /**
12
+ * @constant
13
+ * @type {ExitThisPageTranslations}
14
+ * @see Default value for {@link ExitThisPageConfig.i18n}
15
+ * @default
16
+ */
17
+ var EXIT_THIS_PAGE_TRANSLATIONS = {
18
+ activated: 'Loading.',
19
+ timedOut: 'Exit this page expired.',
20
+ pressTwoMoreTimes: 'Shift, press 2 more times to exit.',
21
+ pressOneMoreTime: 'Shift, press 1 more time to exit.'
22
+ };
23
+
24
+ /**
25
+ * Exit This Page component
26
+ *
27
+ * @class
28
+ * @param {HTMLElement} $module - HTML element that wraps the Exit This Page button
29
+ * @param {ExitThisPageConfig} [config] - Exit This Page config
30
+ */
31
+ function ExitThisPage ($module, config) {
32
+ /** @type {ExitThisPageConfig} */
33
+ var defaultConfig = {
34
+ i18n: EXIT_THIS_PAGE_TRANSLATIONS
35
+ };
36
+
37
+ if (!($module instanceof HTMLElement)) {
38
+ return this
39
+ }
40
+
41
+ var $button = $module.querySelector('.govuk-exit-this-page__button');
42
+ if (!($button instanceof HTMLElement)) {
43
+ return this
44
+ }
45
+
46
+ /**
47
+ * @deprecated Will be made private in v5.0
48
+ * @type {ExitThisPageConfig}
49
+ */
50
+ this.config = mergeConfigs(
51
+ defaultConfig,
52
+ config || {},
53
+ normaliseDataset($module.dataset)
54
+ );
55
+
56
+ this.i18n = new I18n(extractConfigByNamespace(this.config, 'i18n'));
57
+
58
+ /** @deprecated Will be made private in v5.0 */
59
+ this.$module = $module;
60
+
61
+ /** @deprecated Will be made private in v5.0 */
62
+ this.$button = $button;
63
+
64
+ /** @deprecated Will be made private in v5.0 */
65
+ this.$skiplinkButton = document.querySelector('.govuk-js-exit-this-page-skiplink');
66
+
67
+ /** @deprecated Will be made private in v5.0 */
68
+ this.$updateSpan = null;
69
+
70
+ /** @deprecated Will be made private in v5.0 */
71
+ this.$indicatorContainer = null;
72
+
73
+ /** @deprecated Will be made private in v5.0 */
74
+ this.$overlay = null;
75
+
76
+ /** @deprecated Will be made private in v5.0 */
77
+ this.keypressCounter = 0;
78
+
79
+ /** @deprecated Will be made private in v5.0 */
80
+ this.lastKeyWasModified = false;
81
+
82
+ /** @deprecated Will be made private in v5.0 */
83
+ this.timeoutTime = 5000; // milliseconds
84
+
85
+ // Store the timeout events so that we can clear them to avoid user keypresses overlapping
86
+ // setTimeout returns an id that we can use to clear it with clearTimeout,
87
+ // hence the 'Id' suffix
88
+
89
+ /** @deprecated Will be made private in v5.0 */
90
+ this.keypressTimeoutId = null;
91
+
92
+ /** @deprecated Will be made private in v5.0 */
93
+ this.timeoutMessageId = null;
94
+ }
95
+
96
+ /**
97
+ * Create the <span> we use for screen reader announcements.
98
+ *
99
+ * @deprecated Will be made private in v5.0
100
+ */
101
+ ExitThisPage.prototype.initUpdateSpan = function () {
102
+ this.$updateSpan = document.createElement('span');
103
+ this.$updateSpan.setAttribute('role', 'status');
104
+ this.$updateSpan.className = 'govuk-visually-hidden';
105
+
106
+ this.$module.appendChild(this.$updateSpan);
107
+ };
108
+
109
+ /**
110
+ * Create button click handlers.
111
+ *
112
+ * @deprecated Will be made private in v5.0
113
+ */
114
+ ExitThisPage.prototype.initButtonClickHandler = function () {
115
+ // Main EtP button
116
+ this.$button.addEventListener('click', this.handleClick.bind(this));
117
+
118
+ // EtP skiplink
119
+ if (this.$skiplinkButton) {
120
+ this.$skiplinkButton.addEventListener('click', this.handleClick.bind(this));
121
+ }
122
+ };
123
+
124
+ /**
125
+ * Create the HTML for the 'three lights' indicator on the button.
126
+ *
127
+ * @deprecated Will be made private in v5.0
128
+ */
129
+ ExitThisPage.prototype.buildIndicator = function () {
130
+ // Build container
131
+ // Putting `aria-hidden` on it as it won't contain any readable information
132
+ this.$indicatorContainer = document.createElement('div');
133
+ this.$indicatorContainer.className = 'govuk-exit-this-page__indicator';
134
+ this.$indicatorContainer.setAttribute('aria-hidden', 'true');
135
+
136
+ // Create three 'lights' and place them within the container
137
+ for (var i = 0; i < 3; i++) {
138
+ var $indicator = document.createElement('div');
139
+ $indicator.className = 'govuk-exit-this-page__indicator-light';
140
+ this.$indicatorContainer.appendChild($indicator);
141
+ }
142
+
143
+ // Append it all to the module
144
+ this.$button.appendChild(this.$indicatorContainer);
145
+ };
146
+
147
+ /**
148
+ * Update whether the lights are visible and which ones are lit up depending on
149
+ * the value of `keypressCounter`.
150
+ *
151
+ * @deprecated Will be made private in v5.0
152
+ */
153
+ ExitThisPage.prototype.updateIndicator = function () {
154
+ // Show or hide the indicator container depending on keypressCounter value
155
+ if (this.keypressCounter > 0) {
156
+ this.$indicatorContainer.classList.add('govuk-exit-this-page__indicator--visible');
157
+ } else {
158
+ this.$indicatorContainer.classList.remove('govuk-exit-this-page__indicator--visible');
159
+ }
160
+
161
+ // Turn on only the indicators we want on
162
+ var $indicators = this.$indicatorContainer.querySelectorAll(
163
+ '.govuk-exit-this-page__indicator-light'
164
+ );
165
+ nodeListForEach($indicators, function ($indicator, index) {
166
+ $indicator.classList.toggle(
167
+ 'govuk-exit-this-page__indicator-light--on',
168
+ index < this.keypressCounter
169
+ );
170
+ }.bind(this));
171
+ };
172
+
173
+ /**
174
+ * Initiates the redirection away from the current page.
175
+ * Includes the loading overlay functionality, which covers the current page with a
176
+ * white overlay so that the contents are not visible during the loading
177
+ * process. This is particularly important on slow network connections.
178
+ *
179
+ * @deprecated Will be made private in v5.0
180
+ */
181
+ ExitThisPage.prototype.exitPage = function () {
182
+ this.$updateSpan.innerText = '';
183
+
184
+ // Blank the page
185
+ // As well as creating an overlay with text, we also set the body to hidden
186
+ // to prevent screen reader and sequential navigation users potentially
187
+ // navigating through the page behind the overlay during loading
188
+ document.body.classList.add('govuk-exit-this-page-hide-content');
189
+ this.$overlay = document.createElement('div');
190
+ this.$overlay.className = 'govuk-exit-this-page-overlay';
191
+ this.$overlay.setAttribute('role', 'alert');
192
+
193
+ // we do these this way round, thus incurring a second paint, because changing
194
+ // the element text after adding it means that screen readers pick up the
195
+ // announcement more reliably.
196
+ document.body.appendChild(this.$overlay);
197
+ this.$overlay.innerText = this.i18n.t('activated');
198
+
199
+ window.location.href = this.$button.getAttribute('href');
200
+ };
201
+
202
+ /**
203
+ * Pre-activation logic for when the button is clicked/activated via mouse or
204
+ * pointer.
205
+ *
206
+ * We do this to differentiate it from the keyboard activation event because we
207
+ * need to run `e.preventDefault` as the button or skiplink are both links and we
208
+ * want to apply some additional logic in `exitPage` before navigating.
209
+ *
210
+ * @deprecated Will be made private in v5.0
211
+ * @param {MouseEvent} event - mouse click event
212
+ */
213
+ ExitThisPage.prototype.handleClick = function (event) {
214
+ event.preventDefault();
215
+ this.exitPage();
216
+ };
217
+
218
+ /**
219
+ * Logic for the 'quick escape' keyboard sequence functionality (pressing the
220
+ * Shift key three times without interruption, within a time limit).
221
+ *
222
+ * @deprecated Will be made private in v5.0
223
+ * @param {KeyboardEvent} event - keyup event
224
+ */
225
+ ExitThisPage.prototype.handleKeypress = function (event) {
226
+ // Detect if the 'Shift' key has been pressed. We want to only do things if it
227
+ // was pressed by itself and not in a combination with another key—so we keep
228
+ // track of whether the preceding keyup had shiftKey: true on it, and if it
229
+ // did, we ignore the next Shift keyup event.
230
+ //
231
+ // This works because using Shift as a modifier key (e.g. pressing Shift + A)
232
+ // will fire TWO keyup events, one for A (with e.shiftKey: true) and the other
233
+ // for Shift (with e.shiftKey: false).
234
+ if (
235
+ (event.key === 'Shift' || event.keyCode === 16 || event.which === 16) &&
236
+ !this.lastKeyWasModified
237
+ ) {
238
+ this.keypressCounter += 1;
239
+
240
+ // Update the indicator before the below if statement can reset it back to 0
241
+ this.updateIndicator();
242
+
243
+ // Clear the timeout for the keypress timeout message clearing itself
244
+ if (this.timeoutMessageId !== null) {
245
+ clearTimeout(this.timeoutMessageId);
246
+ this.timeoutMessageId = null;
247
+ }
248
+
249
+ if (this.keypressCounter >= 3) {
250
+ this.keypressCounter = 0;
251
+
252
+ if (this.keypressTimeoutId !== null) {
253
+ clearTimeout(this.keypressTimeoutId);
254
+ this.keypressTimeoutId = null;
255
+ }
256
+
257
+ this.exitPage();
258
+ } else {
259
+ if (this.keypressCounter === 1) {
260
+ this.$updateSpan.innerText = this.i18n.t('pressTwoMoreTimes');
261
+ } else {
262
+ this.$updateSpan.innerText = this.i18n.t('pressOneMoreTime');
263
+ }
264
+ }
265
+
266
+ this.setKeypressTimer();
267
+ } else if (this.keypressTimeoutId !== null) {
268
+ // If the user pressed any key other than 'Shift', after having pressed
269
+ // 'Shift' and activating the timer, stop and reset the timer.
270
+ this.resetKeypressTimer();
271
+ }
272
+
273
+ // Keep track of whether the Shift modifier key was held during this keypress
274
+ this.lastKeyWasModified = event.shiftKey;
275
+ };
276
+
277
+ /**
278
+ * Starts the 'quick escape' keyboard sequence timer.
279
+ *
280
+ * This can be invoked several times. We want this to be possible so that the
281
+ * timer is restarted each time the shortcut key is pressed (e.g. the user has
282
+ * up to n seconds between each keypress, rather than n seconds to invoke the
283
+ * entire sequence.)
284
+ *
285
+ * @deprecated Will be made private in v5.0
286
+ */
287
+ ExitThisPage.prototype.setKeypressTimer = function () {
288
+ // Clear any existing timeout. This is so only one timer is running even if
289
+ // there are multiple keypresses in quick succession.
290
+ clearTimeout(this.keypressTimeoutId);
291
+
292
+ // Set a fresh timeout
293
+ this.keypressTimeoutId = setTimeout(
294
+ this.resetKeypressTimer.bind(this),
295
+ this.timeoutTime
296
+ );
297
+ };
298
+
299
+ /**
300
+ * Stops and resets the 'quick escape' keyboard sequence timer.
301
+ *
302
+ * @deprecated Will be made private in v5.0
303
+ */
304
+ ExitThisPage.prototype.resetKeypressTimer = function () {
305
+ clearTimeout(this.keypressTimeoutId);
306
+ this.keypressTimeoutId = null;
307
+
308
+ this.keypressCounter = 0;
309
+ this.$updateSpan.innerText = this.i18n.t('timedOut');
310
+
311
+ this.timeoutMessageId = setTimeout(function () {
312
+ this.$updateSpan.innerText = '';
313
+ }.bind(this), this.timeoutTime);
314
+
315
+ this.updateIndicator();
316
+ };
317
+
318
+ /**
319
+ * Reset the page using the EtP button
320
+ *
321
+ * We use this in situations where a user may re-enter a page using the browser
322
+ * back button. In these cases, the browser can choose to restore the state of
323
+ * the page as it was previously, including restoring the 'ghost page' overlay,
324
+ * the announcement span having it's role set to "alert" and the keypress
325
+ * indicator still active, leaving the page in an unusable state.
326
+ *
327
+ * By running this check when the page is shown, we can programatically restore
328
+ * the page and the component to a "default" state
329
+ *
330
+ * @deprecated Will be made private in v5.0
331
+ */
332
+ ExitThisPage.prototype.resetPage = function () {
333
+ // If an overlay is set, remove it and reset the value
334
+ document.body.classList.remove('govuk-exit-this-page-hide-content');
335
+
336
+ if (this.$overlay) {
337
+ this.$overlay.remove();
338
+ this.$overlay = null;
339
+ }
340
+
341
+ // Ensure the announcement span's role is status, not alert and clear any text
342
+ this.$updateSpan.setAttribute('role', 'status');
343
+ this.$updateSpan.innerText = '';
344
+
345
+ // Sync the keypress indicator lights
346
+ this.updateIndicator();
347
+
348
+ // If the timeouts are active, clear them
349
+ if (this.keypressTimeoutId) {
350
+ clearTimeout(this.keypressTimeoutId);
351
+ }
352
+
353
+ if (this.timeoutMessageId) {
354
+ clearTimeout(this.timeoutMessageId);
355
+ }
356
+ };
357
+
358
+ /**
359
+ * Initialise component
360
+ */
361
+ ExitThisPage.prototype.init = function () {
362
+ this.buildIndicator();
363
+ this.initUpdateSpan();
364
+ this.initButtonClickHandler();
365
+
366
+ // Check to see if this has already been done by a previous initialisation of ExitThisPage
367
+ if (!('govukFrontendExitThisPageKeypress' in document.body.dataset)) {
368
+ document.addEventListener('keyup', this.handleKeypress.bind(this), true);
369
+ document.body.dataset.govukFrontendExitThisPageKeypress = 'true';
370
+ }
371
+
372
+ // When the page is restored after navigating 'back' in some browsers the
373
+ // blank overlay remains present, rendering the page unusable. Here, we check
374
+ // to see if it's present on page (re)load, and remove it if so.
375
+ window.addEventListener(
376
+ 'onpageshow' in window ? 'pageshow' : 'DOMContentLoaded',
377
+ this.resetPage.bind(this)
378
+ );
379
+ };
380
+
381
+ /**
382
+ * Exit this Page config
383
+ *
384
+ * @typedef {object} ExitThisPageConfig
385
+ * @property {ExitThisPageTranslations} [i18n = EXIT_THIS_PAGE_TRANSLATIONS] - See constant {@link EXIT_THIS_PAGE_TRANSLATIONS}
386
+ */
387
+
388
+ /**
389
+ * Exit this Page translations
390
+ *
391
+ * @typedef {object} ExitThisPageTranslations
392
+ *
393
+ * Messages used by the component programatically inserted text, including
394
+ * overlay text and screen reader announcements.
395
+ * @property {string} [activated] - Screen reader announcement for when EtP
396
+ * keypress functionality has been successfully activated.
397
+ * @property {string} [timedOut] - Screen reader announcement for when the EtP
398
+ * keypress functionality has timed out.
399
+ * @property {string} [pressTwoMoreTimes] - Screen reader announcement informing
400
+ * the user they must press the activation key two more times.
401
+ * @property {string} [pressOneMoreTime] - Screen reader announcement informing
402
+ * the user they must press the activation key one more time.
403
+ */
404
+
405
+ export default ExitThisPage;
406
+ //# sourceMappingURL=components/exit-this-page/exit-this-page.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exit-this-page.mjs","sources":["../../../../src/govuk/components/exit-this-page/exit-this-page.mjs"],"sourcesContent":["/* eslint-disable es-x/no-function-prototype-bind -- Polyfill imported */\n\nimport { normaliseDataset } from '../../common/normalise-dataset.mjs'\nimport { nodeListForEach, mergeConfigs, extractConfigByNamespace } from '../../common.mjs'\nimport { I18n } from '../../i18n.mjs'\nimport '../../vendor/polyfills/Element/prototype/classList.mjs'\nimport '../../vendor/polyfills/Event.mjs' // addEventListener, event.target normalization and DOMContentLoaded\nimport '../../vendor/polyfills/Element/prototype/dataset.mjs'\nimport '../../vendor/polyfills/Function/prototype/bind.mjs'\n\n/**\n * @constant\n * @type {ExitThisPageTranslations}\n * @see Default value for {@link ExitThisPageConfig.i18n}\n * @default\n */\nvar EXIT_THIS_PAGE_TRANSLATIONS = {\n activated: 'Loading.',\n timedOut: 'Exit this page expired.',\n pressTwoMoreTimes: 'Shift, press 2 more times to exit.',\n pressOneMoreTime: 'Shift, press 1 more time to exit.'\n}\n\n/**\n * Exit This Page component\n *\n * @class\n * @param {HTMLElement} $module - HTML element that wraps the Exit This Page button\n * @param {ExitThisPageConfig} [config] - Exit This Page config\n */\nfunction ExitThisPage ($module, config) {\n /** @type {ExitThisPageConfig} */\n var defaultConfig = {\n i18n: EXIT_THIS_PAGE_TRANSLATIONS\n }\n\n if (!($module instanceof HTMLElement)) {\n return this\n }\n\n var $button = $module.querySelector('.govuk-exit-this-page__button')\n if (!($button instanceof HTMLElement)) {\n return this\n }\n\n /**\n * @deprecated Will be made private in v5.0\n * @type {ExitThisPageConfig}\n */\n this.config = mergeConfigs(\n defaultConfig,\n config || {},\n normaliseDataset($module.dataset)\n )\n\n this.i18n = new I18n(extractConfigByNamespace(this.config, 'i18n'))\n\n /** @deprecated Will be made private in v5.0 */\n this.$module = $module\n\n /** @deprecated Will be made private in v5.0 */\n this.$button = $button\n\n /** @deprecated Will be made private in v5.0 */\n this.$skiplinkButton = document.querySelector('.govuk-js-exit-this-page-skiplink')\n\n /** @deprecated Will be made private in v5.0 */\n this.$updateSpan = null\n\n /** @deprecated Will be made private in v5.0 */\n this.$indicatorContainer = null\n\n /** @deprecated Will be made private in v5.0 */\n this.$overlay = null\n\n /** @deprecated Will be made private in v5.0 */\n this.keypressCounter = 0\n\n /** @deprecated Will be made private in v5.0 */\n this.lastKeyWasModified = false\n\n /** @deprecated Will be made private in v5.0 */\n this.timeoutTime = 5000 // milliseconds\n\n // Store the timeout events so that we can clear them to avoid user keypresses overlapping\n // setTimeout returns an id that we can use to clear it with clearTimeout,\n // hence the 'Id' suffix\n\n /** @deprecated Will be made private in v5.0 */\n this.keypressTimeoutId = null\n\n /** @deprecated Will be made private in v5.0 */\n this.timeoutMessageId = null\n}\n\n/**\n * Create the <span> we use for screen reader announcements.\n *\n * @deprecated Will be made private in v5.0\n */\nExitThisPage.prototype.initUpdateSpan = function () {\n this.$updateSpan = document.createElement('span')\n this.$updateSpan.setAttribute('role', 'status')\n this.$updateSpan.className = 'govuk-visually-hidden'\n\n this.$module.appendChild(this.$updateSpan)\n}\n\n/**\n * Create button click handlers.\n *\n * @deprecated Will be made private in v5.0\n */\nExitThisPage.prototype.initButtonClickHandler = function () {\n // Main EtP button\n this.$button.addEventListener('click', this.handleClick.bind(this))\n\n // EtP skiplink\n if (this.$skiplinkButton) {\n this.$skiplinkButton.addEventListener('click', this.handleClick.bind(this))\n }\n}\n\n/**\n * Create the HTML for the 'three lights' indicator on the button.\n *\n * @deprecated Will be made private in v5.0\n */\nExitThisPage.prototype.buildIndicator = function () {\n // Build container\n // Putting `aria-hidden` on it as it won't contain any readable information\n this.$indicatorContainer = document.createElement('div')\n this.$indicatorContainer.className = 'govuk-exit-this-page__indicator'\n this.$indicatorContainer.setAttribute('aria-hidden', 'true')\n\n // Create three 'lights' and place them within the container\n for (var i = 0; i < 3; i++) {\n var $indicator = document.createElement('div')\n $indicator.className = 'govuk-exit-this-page__indicator-light'\n this.$indicatorContainer.appendChild($indicator)\n }\n\n // Append it all to the module\n this.$button.appendChild(this.$indicatorContainer)\n}\n\n/**\n * Update whether the lights are visible and which ones are lit up depending on\n * the value of `keypressCounter`.\n *\n * @deprecated Will be made private in v5.0\n */\nExitThisPage.prototype.updateIndicator = function () {\n // Show or hide the indicator container depending on keypressCounter value\n if (this.keypressCounter > 0) {\n this.$indicatorContainer.classList.add('govuk-exit-this-page__indicator--visible')\n } else {\n this.$indicatorContainer.classList.remove('govuk-exit-this-page__indicator--visible')\n }\n\n // Turn on only the indicators we want on\n var $indicators = this.$indicatorContainer.querySelectorAll(\n '.govuk-exit-this-page__indicator-light'\n )\n nodeListForEach($indicators, function ($indicator, index) {\n $indicator.classList.toggle(\n 'govuk-exit-this-page__indicator-light--on',\n index < this.keypressCounter\n )\n }.bind(this))\n}\n\n/**\n * Initiates the redirection away from the current page.\n * Includes the loading overlay functionality, which covers the current page with a\n * white overlay so that the contents are not visible during the loading\n * process. This is particularly important on slow network connections.\n *\n * @deprecated Will be made private in v5.0\n */\nExitThisPage.prototype.exitPage = function () {\n this.$updateSpan.innerText = ''\n\n // Blank the page\n // As well as creating an overlay with text, we also set the body to hidden\n // to prevent screen reader and sequential navigation users potentially\n // navigating through the page behind the overlay during loading\n document.body.classList.add('govuk-exit-this-page-hide-content')\n this.$overlay = document.createElement('div')\n this.$overlay.className = 'govuk-exit-this-page-overlay'\n this.$overlay.setAttribute('role', 'alert')\n\n // we do these this way round, thus incurring a second paint, because changing\n // the element text after adding it means that screen readers pick up the\n // announcement more reliably.\n document.body.appendChild(this.$overlay)\n this.$overlay.innerText = this.i18n.t('activated')\n\n window.location.href = this.$button.getAttribute('href')\n}\n\n/**\n * Pre-activation logic for when the button is clicked/activated via mouse or\n * pointer.\n *\n * We do this to differentiate it from the keyboard activation event because we\n * need to run `e.preventDefault` as the button or skiplink are both links and we\n * want to apply some additional logic in `exitPage` before navigating.\n *\n * @deprecated Will be made private in v5.0\n * @param {MouseEvent} event - mouse click event\n */\nExitThisPage.prototype.handleClick = function (event) {\n event.preventDefault()\n this.exitPage()\n}\n\n/**\n * Logic for the 'quick escape' keyboard sequence functionality (pressing the\n * Shift key three times without interruption, within a time limit).\n *\n * @deprecated Will be made private in v5.0\n * @param {KeyboardEvent} event - keyup event\n */\nExitThisPage.prototype.handleKeypress = function (event) {\n // Detect if the 'Shift' key has been pressed. We want to only do things if it\n // was pressed by itself and not in a combination with another key—so we keep\n // track of whether the preceding keyup had shiftKey: true on it, and if it\n // did, we ignore the next Shift keyup event.\n //\n // This works because using Shift as a modifier key (e.g. pressing Shift + A)\n // will fire TWO keyup events, one for A (with e.shiftKey: true) and the other\n // for Shift (with e.shiftKey: false).\n if (\n (event.key === 'Shift' || event.keyCode === 16 || event.which === 16) &&\n !this.lastKeyWasModified\n ) {\n this.keypressCounter += 1\n\n // Update the indicator before the below if statement can reset it back to 0\n this.updateIndicator()\n\n // Clear the timeout for the keypress timeout message clearing itself\n if (this.timeoutMessageId !== null) {\n clearTimeout(this.timeoutMessageId)\n this.timeoutMessageId = null\n }\n\n if (this.keypressCounter >= 3) {\n this.keypressCounter = 0\n\n if (this.keypressTimeoutId !== null) {\n clearTimeout(this.keypressTimeoutId)\n this.keypressTimeoutId = null\n }\n\n this.exitPage()\n } else {\n if (this.keypressCounter === 1) {\n this.$updateSpan.innerText = this.i18n.t('pressTwoMoreTimes')\n } else {\n this.$updateSpan.innerText = this.i18n.t('pressOneMoreTime')\n }\n }\n\n this.setKeypressTimer()\n } else if (this.keypressTimeoutId !== null) {\n // If the user pressed any key other than 'Shift', after having pressed\n // 'Shift' and activating the timer, stop and reset the timer.\n this.resetKeypressTimer()\n }\n\n // Keep track of whether the Shift modifier key was held during this keypress\n this.lastKeyWasModified = event.shiftKey\n}\n\n/**\n * Starts the 'quick escape' keyboard sequence timer.\n *\n * This can be invoked several times. We want this to be possible so that the\n * timer is restarted each time the shortcut key is pressed (e.g. the user has\n * up to n seconds between each keypress, rather than n seconds to invoke the\n * entire sequence.)\n *\n * @deprecated Will be made private in v5.0\n */\nExitThisPage.prototype.setKeypressTimer = function () {\n // Clear any existing timeout. This is so only one timer is running even if\n // there are multiple keypresses in quick succession.\n clearTimeout(this.keypressTimeoutId)\n\n // Set a fresh timeout\n this.keypressTimeoutId = setTimeout(\n this.resetKeypressTimer.bind(this),\n this.timeoutTime\n )\n}\n\n/**\n * Stops and resets the 'quick escape' keyboard sequence timer.\n *\n * @deprecated Will be made private in v5.0\n */\nExitThisPage.prototype.resetKeypressTimer = function () {\n clearTimeout(this.keypressTimeoutId)\n this.keypressTimeoutId = null\n\n this.keypressCounter = 0\n this.$updateSpan.innerText = this.i18n.t('timedOut')\n\n this.timeoutMessageId = setTimeout(function () {\n this.$updateSpan.innerText = ''\n }.bind(this), this.timeoutTime)\n\n this.updateIndicator()\n}\n\n/**\n * Reset the page using the EtP button\n *\n * We use this in situations where a user may re-enter a page using the browser\n * back button. In these cases, the browser can choose to restore the state of\n * the page as it was previously, including restoring the 'ghost page' overlay,\n * the announcement span having it's role set to \"alert\" and the keypress\n * indicator still active, leaving the page in an unusable state.\n *\n * By running this check when the page is shown, we can programatically restore\n * the page and the component to a \"default\" state\n *\n * @deprecated Will be made private in v5.0\n */\nExitThisPage.prototype.resetPage = function () {\n // If an overlay is set, remove it and reset the value\n document.body.classList.remove('govuk-exit-this-page-hide-content')\n\n if (this.$overlay) {\n this.$overlay.remove()\n this.$overlay = null\n }\n\n // Ensure the announcement span's role is status, not alert and clear any text\n this.$updateSpan.setAttribute('role', 'status')\n this.$updateSpan.innerText = ''\n\n // Sync the keypress indicator lights\n this.updateIndicator()\n\n // If the timeouts are active, clear them\n if (this.keypressTimeoutId) {\n clearTimeout(this.keypressTimeoutId)\n }\n\n if (this.timeoutMessageId) {\n clearTimeout(this.timeoutMessageId)\n }\n}\n\n/**\n * Initialise component\n */\nExitThisPage.prototype.init = function () {\n this.buildIndicator()\n this.initUpdateSpan()\n this.initButtonClickHandler()\n\n // Check to see if this has already been done by a previous initialisation of ExitThisPage\n if (!('govukFrontendExitThisPageKeypress' in document.body.dataset)) {\n document.addEventListener('keyup', this.handleKeypress.bind(this), true)\n document.body.dataset.govukFrontendExitThisPageKeypress = 'true'\n }\n\n // When the page is restored after navigating 'back' in some browsers the\n // blank overlay remains present, rendering the page unusable. Here, we check\n // to see if it's present on page (re)load, and remove it if so.\n window.addEventListener(\n 'onpageshow' in window ? 'pageshow' : 'DOMContentLoaded',\n this.resetPage.bind(this)\n )\n}\n\nexport default ExitThisPage\n\n/**\n * Exit this Page config\n *\n * @typedef {object} ExitThisPageConfig\n * @property {ExitThisPageTranslations} [i18n = EXIT_THIS_PAGE_TRANSLATIONS] - See constant {@link EXIT_THIS_PAGE_TRANSLATIONS}\n */\n\n/**\n * Exit this Page translations\n *\n * @typedef {object} ExitThisPageTranslations\n *\n * Messages used by the component programatically inserted text, including\n * overlay text and screen reader announcements.\n * @property {string} [activated] - Screen reader announcement for when EtP\n * keypress functionality has been successfully activated.\n * @property {string} [timedOut] - Screen reader announcement for when the EtP\n * keypress functionality has timed out.\n * @property {string} [pressTwoMoreTimes] - Screen reader announcement informing\n * the user they must press the activation key two more times.\n * @property {string} [pressOneMoreTime] - Screen reader announcement informing\n * the user they must press the activation key one more time.\n */\n"],"names":[],"mappings":"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"}
@@ -55,6 +55,10 @@
55
55
  "importFrom": "govuk/components/error-summary/macro.njk",
56
56
  "macroName": "govukErrorSummary"
57
57
  },
58
+ {
59
+ "importFrom": "govuk/components/exit-this-page/macro.njk",
60
+ "macroName": "govukExitThisPage"
61
+ },
58
62
  {
59
63
  "importFrom": "govuk/components/fieldset/macro.njk",
60
64
  "macroName": "govukFieldset"
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "govuk-frontend",
3
3
  "description": "GOV.UK Frontend contains the code you need to start building a user interface for government platforms and services.",
4
- "version": "4.6.0",
4
+ "version": "4.7.0",
5
5
  "main": "govuk/all.js",
6
6
  "module": "govuk-esm/all.mjs",
7
+ "sass": "govuk/all.scss",
7
8
  "exports": {
8
9
  ".": {
9
10
  "sass": "./govuk/all.scss",
@@ -11,13 +12,14 @@
11
12
  "require": "./govuk/all.js"
12
13
  },
13
14
  "./govuk/": "./govuk/",
15
+ "./govuk/*": "./govuk/*",
14
16
  "./govuk-esm/": "./govuk-esm/",
17
+ "./govuk-esm/*": "./govuk-esm/*",
15
18
  "./package.json": "./package.json"
16
19
  },
17
20
  "sideEffects": [
18
21
  "govuk-esm/vendor/**"
19
22
  ],
20
- "sass": "govuk/all.scss",
21
23
  "engines": {
22
24
  "node": ">= 4.2.0"
23
25
  },
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: govuk_publishing_components
3
3
  version: !ruby/object:Gem::Version
4
- version: 35.11.0
4
+ version: 35.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GOV.UK Dev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-07-18 00:00:00.000000000 Z
11
+ date: 2023-07-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: govuk_app_config
@@ -448,6 +448,7 @@ files:
448
448
  - app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-link-tracker.js
449
449
  - app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-page-views.js
450
450
  - app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-schemas.js
451
+ - app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-scroll-tracker.js
451
452
  - app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-smart-answer-results-tracker.js
452
453
  - app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-specialist-link-tracker.js
453
454
  - app/assets/javascripts/govuk_publishing_components/analytics-ga4/init-ga4.js
@@ -1010,6 +1011,8 @@ files:
1010
1011
  - node_modules/govuk-frontend/govuk-esm/components/details/details.mjs.map
1011
1012
  - node_modules/govuk-frontend/govuk-esm/components/error-summary/error-summary.mjs
1012
1013
  - node_modules/govuk-frontend/govuk-esm/components/error-summary/error-summary.mjs.map
1014
+ - node_modules/govuk-frontend/govuk-esm/components/exit-this-page/exit-this-page.mjs
1015
+ - node_modules/govuk-frontend/govuk-esm/components/exit-this-page/exit-this-page.mjs.map
1013
1016
  - node_modules/govuk-frontend/govuk-esm/components/header/header.mjs
1014
1017
  - node_modules/govuk-frontend/govuk-esm/components/header/header.mjs.map
1015
1018
  - node_modules/govuk-frontend/govuk-esm/components/notification-banner/notification-banner.mjs
@@ -1175,6 +1178,15 @@ files:
1175
1178
  - node_modules/govuk-frontend/govuk/components/error-summary/macro-options.json
1176
1179
  - node_modules/govuk-frontend/govuk/components/error-summary/macro.njk
1177
1180
  - node_modules/govuk-frontend/govuk/components/error-summary/template.njk
1181
+ - node_modules/govuk-frontend/govuk/components/exit-this-page/README.md
1182
+ - node_modules/govuk-frontend/govuk/components/exit-this-page/_exit-this-page.scss
1183
+ - node_modules/govuk-frontend/govuk/components/exit-this-page/_index.scss
1184
+ - node_modules/govuk-frontend/govuk/components/exit-this-page/exit-this-page.js
1185
+ - node_modules/govuk-frontend/govuk/components/exit-this-page/exit-this-page.js.map
1186
+ - node_modules/govuk-frontend/govuk/components/exit-this-page/fixtures.json
1187
+ - node_modules/govuk-frontend/govuk/components/exit-this-page/macro-options.json
1188
+ - node_modules/govuk-frontend/govuk/components/exit-this-page/macro.njk
1189
+ - node_modules/govuk-frontend/govuk/components/exit-this-page/template.njk
1178
1190
  - node_modules/govuk-frontend/govuk/components/fieldset/README.md
1179
1191
  - node_modules/govuk-frontend/govuk/components/fieldset/_fieldset.scss
1180
1192
  - node_modules/govuk-frontend/govuk/components/fieldset/_index.scss