govuk_publishing_components 35.11.0 → 35.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) 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/assets/stylesheets/govuk_publishing_components/components/_document-list.scss +3 -1
  10. data/app/assets/stylesheets/govuk_publishing_components/components/_search.scss +8 -2
  11. data/app/controllers/govuk_publishing_components/audit_controller.rb +3 -2
  12. data/app/models/govuk_publishing_components/audit_applications.rb +3 -1
  13. data/app/models/govuk_publishing_components/audit_comparer.rb +1 -1
  14. data/app/models/govuk_publishing_components/audit_components.rb +3 -2
  15. data/app/models/govuk_publishing_components/component_wrapper_helper_options.rb +1 -0
  16. data/app/views/govuk_publishing_components/audit/_applications.html.erb +1 -1
  17. data/app/views/govuk_publishing_components/components/_attachment.html.erb +3 -1
  18. data/app/views/govuk_publishing_components/components/_document_list.html.erb +29 -27
  19. data/app/views/govuk_publishing_components/components/_heading.html.erb +8 -5
  20. data/app/views/govuk_publishing_components/components/_tabs.html.erb +30 -14
  21. data/app/views/govuk_publishing_components/components/docs/heading.yml +1 -4
  22. data/app/views/govuk_publishing_components/components/docs/single_page_notification_button.yml +1 -6
  23. data/app/views/govuk_publishing_components/components/docs/tabs.yml +26 -3
  24. data/lib/govuk_publishing_components/config.rb +3 -0
  25. data/lib/govuk_publishing_components/presenters/component_wrapper_helper.rb +17 -1
  26. data/lib/govuk_publishing_components/version.rb +1 -1
  27. data/node_modules/govuk-frontend/govuk/all.js +406 -1
  28. data/node_modules/govuk-frontend/govuk/all.js.map +1 -1
  29. data/node_modules/govuk-frontend/govuk/common/govuk-frontend-version.js +1 -1
  30. data/node_modules/govuk-frontend/govuk/components/_all.scss +2 -1
  31. data/node_modules/govuk-frontend/govuk/components/back-link/_index.scss +8 -0
  32. data/node_modules/govuk-frontend/govuk/components/back-link/fixtures.json +9 -0
  33. data/node_modules/govuk-frontend/govuk/components/breadcrumbs/_index.scss +12 -0
  34. data/node_modules/govuk-frontend/govuk/components/breadcrumbs/fixtures.json +21 -0
  35. data/node_modules/govuk-frontend/govuk/components/button/_index.scss +41 -3
  36. data/node_modules/govuk-frontend/govuk/components/button/fixtures.json +44 -0
  37. data/node_modules/govuk-frontend/govuk/components/checkboxes/macro-options.json +9 -8
  38. data/node_modules/govuk-frontend/govuk/components/exit-this-page/README.md +15 -0
  39. data/node_modules/govuk-frontend/govuk/components/exit-this-page/_exit-this-page.scss +2 -0
  40. data/node_modules/govuk-frontend/govuk/components/exit-this-page/_index.scss +97 -0
  41. data/node_modules/govuk-frontend/govuk/components/exit-this-page/exit-this-page.js +2120 -0
  42. data/node_modules/govuk-frontend/govuk/components/exit-this-page/exit-this-page.js.map +1 -0
  43. data/node_modules/govuk-frontend/govuk/components/exit-this-page/fixtures.json +50 -0
  44. data/node_modules/govuk-frontend/govuk/components/exit-this-page/macro-options.json +62 -0
  45. data/node_modules/govuk-frontend/govuk/components/exit-this-page/macro.njk +3 -0
  46. data/node_modules/govuk-frontend/govuk/components/exit-this-page/template.njk +16 -0
  47. data/node_modules/govuk-frontend/govuk/components/radios/macro-options.json +9 -8
  48. data/node_modules/govuk-frontend/govuk/core/_govuk-frontend-version.scss +1 -1
  49. data/node_modules/govuk-frontend/govuk/helpers/_visually-hidden.scss +12 -0
  50. data/node_modules/govuk-frontend/govuk/objects/_template.scss +20 -0
  51. data/node_modules/govuk-frontend/govuk-esm/all.mjs +8 -0
  52. data/node_modules/govuk-frontend/govuk-esm/all.mjs.map +1 -1
  53. data/node_modules/govuk-frontend/govuk-esm/common/govuk-frontend-version.mjs +1 -1
  54. data/node_modules/govuk-frontend/govuk-esm/components/exit-this-page/exit-this-page.mjs +406 -0
  55. data/node_modules/govuk-frontend/govuk-esm/components/exit-this-page/exit-this-page.mjs.map +1 -0
  56. data/node_modules/govuk-frontend/govuk-prototype-kit.config.json +4 -0
  57. data/node_modules/govuk-frontend/package.json +4 -2
  58. metadata +14 -2
@@ -9,7 +9,7 @@
9
9
  * It doesn't need to be updated manually.
10
10
  */
11
11
 
12
- var version = '4.6.0';
12
+ var version = '4.7.0';
13
13
 
14
14
  /**
15
15
  * Common helpers which do not require polyfill.
@@ -3640,6 +3640,404 @@
3640
3640
  * summary will not be focussed when the page loads.
3641
3641
  */
3642
3642
 
3643
+ // Implementation of common function is gathered in the `common` folder
3644
+
3645
+ /* eslint-disable es-x/no-function-prototype-bind -- Polyfill imported */
3646
+
3647
+ /**
3648
+ * @constant
3649
+ * @type {ExitThisPageTranslations}
3650
+ * @see Default value for {@link ExitThisPageConfig.i18n}
3651
+ * @default
3652
+ */
3653
+ var EXIT_THIS_PAGE_TRANSLATIONS = {
3654
+ activated: 'Loading.',
3655
+ timedOut: 'Exit this page expired.',
3656
+ pressTwoMoreTimes: 'Shift, press 2 more times to exit.',
3657
+ pressOneMoreTime: 'Shift, press 1 more time to exit.'
3658
+ };
3659
+
3660
+ /**
3661
+ * Exit This Page component
3662
+ *
3663
+ * @class
3664
+ * @param {HTMLElement} $module - HTML element that wraps the Exit This Page button
3665
+ * @param {ExitThisPageConfig} [config] - Exit This Page config
3666
+ */
3667
+ function ExitThisPage ($module, config) {
3668
+ /** @type {ExitThisPageConfig} */
3669
+ var defaultConfig = {
3670
+ i18n: EXIT_THIS_PAGE_TRANSLATIONS
3671
+ };
3672
+
3673
+ if (!($module instanceof HTMLElement)) {
3674
+ return this
3675
+ }
3676
+
3677
+ var $button = $module.querySelector('.govuk-exit-this-page__button');
3678
+ if (!($button instanceof HTMLElement)) {
3679
+ return this
3680
+ }
3681
+
3682
+ /**
3683
+ * @deprecated Will be made private in v5.0
3684
+ * @type {ExitThisPageConfig}
3685
+ */
3686
+ this.config = mergeConfigs(
3687
+ defaultConfig,
3688
+ config || {},
3689
+ normaliseDataset($module.dataset)
3690
+ );
3691
+
3692
+ this.i18n = new I18n(extractConfigByNamespace(this.config, 'i18n'));
3693
+
3694
+ /** @deprecated Will be made private in v5.0 */
3695
+ this.$module = $module;
3696
+
3697
+ /** @deprecated Will be made private in v5.0 */
3698
+ this.$button = $button;
3699
+
3700
+ /** @deprecated Will be made private in v5.0 */
3701
+ this.$skiplinkButton = document.querySelector('.govuk-js-exit-this-page-skiplink');
3702
+
3703
+ /** @deprecated Will be made private in v5.0 */
3704
+ this.$updateSpan = null;
3705
+
3706
+ /** @deprecated Will be made private in v5.0 */
3707
+ this.$indicatorContainer = null;
3708
+
3709
+ /** @deprecated Will be made private in v5.0 */
3710
+ this.$overlay = null;
3711
+
3712
+ /** @deprecated Will be made private in v5.0 */
3713
+ this.keypressCounter = 0;
3714
+
3715
+ /** @deprecated Will be made private in v5.0 */
3716
+ this.lastKeyWasModified = false;
3717
+
3718
+ /** @deprecated Will be made private in v5.0 */
3719
+ this.timeoutTime = 5000; // milliseconds
3720
+
3721
+ // Store the timeout events so that we can clear them to avoid user keypresses overlapping
3722
+ // setTimeout returns an id that we can use to clear it with clearTimeout,
3723
+ // hence the 'Id' suffix
3724
+
3725
+ /** @deprecated Will be made private in v5.0 */
3726
+ this.keypressTimeoutId = null;
3727
+
3728
+ /** @deprecated Will be made private in v5.0 */
3729
+ this.timeoutMessageId = null;
3730
+ }
3731
+
3732
+ /**
3733
+ * Create the <span> we use for screen reader announcements.
3734
+ *
3735
+ * @deprecated Will be made private in v5.0
3736
+ */
3737
+ ExitThisPage.prototype.initUpdateSpan = function () {
3738
+ this.$updateSpan = document.createElement('span');
3739
+ this.$updateSpan.setAttribute('role', 'status');
3740
+ this.$updateSpan.className = 'govuk-visually-hidden';
3741
+
3742
+ this.$module.appendChild(this.$updateSpan);
3743
+ };
3744
+
3745
+ /**
3746
+ * Create button click handlers.
3747
+ *
3748
+ * @deprecated Will be made private in v5.0
3749
+ */
3750
+ ExitThisPage.prototype.initButtonClickHandler = function () {
3751
+ // Main EtP button
3752
+ this.$button.addEventListener('click', this.handleClick.bind(this));
3753
+
3754
+ // EtP skiplink
3755
+ if (this.$skiplinkButton) {
3756
+ this.$skiplinkButton.addEventListener('click', this.handleClick.bind(this));
3757
+ }
3758
+ };
3759
+
3760
+ /**
3761
+ * Create the HTML for the 'three lights' indicator on the button.
3762
+ *
3763
+ * @deprecated Will be made private in v5.0
3764
+ */
3765
+ ExitThisPage.prototype.buildIndicator = function () {
3766
+ // Build container
3767
+ // Putting `aria-hidden` on it as it won't contain any readable information
3768
+ this.$indicatorContainer = document.createElement('div');
3769
+ this.$indicatorContainer.className = 'govuk-exit-this-page__indicator';
3770
+ this.$indicatorContainer.setAttribute('aria-hidden', 'true');
3771
+
3772
+ // Create three 'lights' and place them within the container
3773
+ for (var i = 0; i < 3; i++) {
3774
+ var $indicator = document.createElement('div');
3775
+ $indicator.className = 'govuk-exit-this-page__indicator-light';
3776
+ this.$indicatorContainer.appendChild($indicator);
3777
+ }
3778
+
3779
+ // Append it all to the module
3780
+ this.$button.appendChild(this.$indicatorContainer);
3781
+ };
3782
+
3783
+ /**
3784
+ * Update whether the lights are visible and which ones are lit up depending on
3785
+ * the value of `keypressCounter`.
3786
+ *
3787
+ * @deprecated Will be made private in v5.0
3788
+ */
3789
+ ExitThisPage.prototype.updateIndicator = function () {
3790
+ // Show or hide the indicator container depending on keypressCounter value
3791
+ if (this.keypressCounter > 0) {
3792
+ this.$indicatorContainer.classList.add('govuk-exit-this-page__indicator--visible');
3793
+ } else {
3794
+ this.$indicatorContainer.classList.remove('govuk-exit-this-page__indicator--visible');
3795
+ }
3796
+
3797
+ // Turn on only the indicators we want on
3798
+ var $indicators = this.$indicatorContainer.querySelectorAll(
3799
+ '.govuk-exit-this-page__indicator-light'
3800
+ );
3801
+ nodeListForEach($indicators, function ($indicator, index) {
3802
+ $indicator.classList.toggle(
3803
+ 'govuk-exit-this-page__indicator-light--on',
3804
+ index < this.keypressCounter
3805
+ );
3806
+ }.bind(this));
3807
+ };
3808
+
3809
+ /**
3810
+ * Initiates the redirection away from the current page.
3811
+ * Includes the loading overlay functionality, which covers the current page with a
3812
+ * white overlay so that the contents are not visible during the loading
3813
+ * process. This is particularly important on slow network connections.
3814
+ *
3815
+ * @deprecated Will be made private in v5.0
3816
+ */
3817
+ ExitThisPage.prototype.exitPage = function () {
3818
+ this.$updateSpan.innerText = '';
3819
+
3820
+ // Blank the page
3821
+ // As well as creating an overlay with text, we also set the body to hidden
3822
+ // to prevent screen reader and sequential navigation users potentially
3823
+ // navigating through the page behind the overlay during loading
3824
+ document.body.classList.add('govuk-exit-this-page-hide-content');
3825
+ this.$overlay = document.createElement('div');
3826
+ this.$overlay.className = 'govuk-exit-this-page-overlay';
3827
+ this.$overlay.setAttribute('role', 'alert');
3828
+
3829
+ // we do these this way round, thus incurring a second paint, because changing
3830
+ // the element text after adding it means that screen readers pick up the
3831
+ // announcement more reliably.
3832
+ document.body.appendChild(this.$overlay);
3833
+ this.$overlay.innerText = this.i18n.t('activated');
3834
+
3835
+ window.location.href = this.$button.getAttribute('href');
3836
+ };
3837
+
3838
+ /**
3839
+ * Pre-activation logic for when the button is clicked/activated via mouse or
3840
+ * pointer.
3841
+ *
3842
+ * We do this to differentiate it from the keyboard activation event because we
3843
+ * need to run `e.preventDefault` as the button or skiplink are both links and we
3844
+ * want to apply some additional logic in `exitPage` before navigating.
3845
+ *
3846
+ * @deprecated Will be made private in v5.0
3847
+ * @param {MouseEvent} event - mouse click event
3848
+ */
3849
+ ExitThisPage.prototype.handleClick = function (event) {
3850
+ event.preventDefault();
3851
+ this.exitPage();
3852
+ };
3853
+
3854
+ /**
3855
+ * Logic for the 'quick escape' keyboard sequence functionality (pressing the
3856
+ * Shift key three times without interruption, within a time limit).
3857
+ *
3858
+ * @deprecated Will be made private in v5.0
3859
+ * @param {KeyboardEvent} event - keyup event
3860
+ */
3861
+ ExitThisPage.prototype.handleKeypress = function (event) {
3862
+ // Detect if the 'Shift' key has been pressed. We want to only do things if it
3863
+ // was pressed by itself and not in a combination with another key—so we keep
3864
+ // track of whether the preceding keyup had shiftKey: true on it, and if it
3865
+ // did, we ignore the next Shift keyup event.
3866
+ //
3867
+ // This works because using Shift as a modifier key (e.g. pressing Shift + A)
3868
+ // will fire TWO keyup events, one for A (with e.shiftKey: true) and the other
3869
+ // for Shift (with e.shiftKey: false).
3870
+ if (
3871
+ (event.key === 'Shift' || event.keyCode === 16 || event.which === 16) &&
3872
+ !this.lastKeyWasModified
3873
+ ) {
3874
+ this.keypressCounter += 1;
3875
+
3876
+ // Update the indicator before the below if statement can reset it back to 0
3877
+ this.updateIndicator();
3878
+
3879
+ // Clear the timeout for the keypress timeout message clearing itself
3880
+ if (this.timeoutMessageId !== null) {
3881
+ clearTimeout(this.timeoutMessageId);
3882
+ this.timeoutMessageId = null;
3883
+ }
3884
+
3885
+ if (this.keypressCounter >= 3) {
3886
+ this.keypressCounter = 0;
3887
+
3888
+ if (this.keypressTimeoutId !== null) {
3889
+ clearTimeout(this.keypressTimeoutId);
3890
+ this.keypressTimeoutId = null;
3891
+ }
3892
+
3893
+ this.exitPage();
3894
+ } else {
3895
+ if (this.keypressCounter === 1) {
3896
+ this.$updateSpan.innerText = this.i18n.t('pressTwoMoreTimes');
3897
+ } else {
3898
+ this.$updateSpan.innerText = this.i18n.t('pressOneMoreTime');
3899
+ }
3900
+ }
3901
+
3902
+ this.setKeypressTimer();
3903
+ } else if (this.keypressTimeoutId !== null) {
3904
+ // If the user pressed any key other than 'Shift', after having pressed
3905
+ // 'Shift' and activating the timer, stop and reset the timer.
3906
+ this.resetKeypressTimer();
3907
+ }
3908
+
3909
+ // Keep track of whether the Shift modifier key was held during this keypress
3910
+ this.lastKeyWasModified = event.shiftKey;
3911
+ };
3912
+
3913
+ /**
3914
+ * Starts the 'quick escape' keyboard sequence timer.
3915
+ *
3916
+ * This can be invoked several times. We want this to be possible so that the
3917
+ * timer is restarted each time the shortcut key is pressed (e.g. the user has
3918
+ * up to n seconds between each keypress, rather than n seconds to invoke the
3919
+ * entire sequence.)
3920
+ *
3921
+ * @deprecated Will be made private in v5.0
3922
+ */
3923
+ ExitThisPage.prototype.setKeypressTimer = function () {
3924
+ // Clear any existing timeout. This is so only one timer is running even if
3925
+ // there are multiple keypresses in quick succession.
3926
+ clearTimeout(this.keypressTimeoutId);
3927
+
3928
+ // Set a fresh timeout
3929
+ this.keypressTimeoutId = setTimeout(
3930
+ this.resetKeypressTimer.bind(this),
3931
+ this.timeoutTime
3932
+ );
3933
+ };
3934
+
3935
+ /**
3936
+ * Stops and resets the 'quick escape' keyboard sequence timer.
3937
+ *
3938
+ * @deprecated Will be made private in v5.0
3939
+ */
3940
+ ExitThisPage.prototype.resetKeypressTimer = function () {
3941
+ clearTimeout(this.keypressTimeoutId);
3942
+ this.keypressTimeoutId = null;
3943
+
3944
+ this.keypressCounter = 0;
3945
+ this.$updateSpan.innerText = this.i18n.t('timedOut');
3946
+
3947
+ this.timeoutMessageId = setTimeout(function () {
3948
+ this.$updateSpan.innerText = '';
3949
+ }.bind(this), this.timeoutTime);
3950
+
3951
+ this.updateIndicator();
3952
+ };
3953
+
3954
+ /**
3955
+ * Reset the page using the EtP button
3956
+ *
3957
+ * We use this in situations where a user may re-enter a page using the browser
3958
+ * back button. In these cases, the browser can choose to restore the state of
3959
+ * the page as it was previously, including restoring the 'ghost page' overlay,
3960
+ * the announcement span having it's role set to "alert" and the keypress
3961
+ * indicator still active, leaving the page in an unusable state.
3962
+ *
3963
+ * By running this check when the page is shown, we can programatically restore
3964
+ * the page and the component to a "default" state
3965
+ *
3966
+ * @deprecated Will be made private in v5.0
3967
+ */
3968
+ ExitThisPage.prototype.resetPage = function () {
3969
+ // If an overlay is set, remove it and reset the value
3970
+ document.body.classList.remove('govuk-exit-this-page-hide-content');
3971
+
3972
+ if (this.$overlay) {
3973
+ this.$overlay.remove();
3974
+ this.$overlay = null;
3975
+ }
3976
+
3977
+ // Ensure the announcement span's role is status, not alert and clear any text
3978
+ this.$updateSpan.setAttribute('role', 'status');
3979
+ this.$updateSpan.innerText = '';
3980
+
3981
+ // Sync the keypress indicator lights
3982
+ this.updateIndicator();
3983
+
3984
+ // If the timeouts are active, clear them
3985
+ if (this.keypressTimeoutId) {
3986
+ clearTimeout(this.keypressTimeoutId);
3987
+ }
3988
+
3989
+ if (this.timeoutMessageId) {
3990
+ clearTimeout(this.timeoutMessageId);
3991
+ }
3992
+ };
3993
+
3994
+ /**
3995
+ * Initialise component
3996
+ */
3997
+ ExitThisPage.prototype.init = function () {
3998
+ this.buildIndicator();
3999
+ this.initUpdateSpan();
4000
+ this.initButtonClickHandler();
4001
+
4002
+ // Check to see if this has already been done by a previous initialisation of ExitThisPage
4003
+ if (!('govukFrontendExitThisPageKeypress' in document.body.dataset)) {
4004
+ document.addEventListener('keyup', this.handleKeypress.bind(this), true);
4005
+ document.body.dataset.govukFrontendExitThisPageKeypress = 'true';
4006
+ }
4007
+
4008
+ // When the page is restored after navigating 'back' in some browsers the
4009
+ // blank overlay remains present, rendering the page unusable. Here, we check
4010
+ // to see if it's present on page (re)load, and remove it if so.
4011
+ window.addEventListener(
4012
+ 'onpageshow' in window ? 'pageshow' : 'DOMContentLoaded',
4013
+ this.resetPage.bind(this)
4014
+ );
4015
+ };
4016
+
4017
+ /**
4018
+ * Exit this Page config
4019
+ *
4020
+ * @typedef {object} ExitThisPageConfig
4021
+ * @property {ExitThisPageTranslations} [i18n = EXIT_THIS_PAGE_TRANSLATIONS] - See constant {@link EXIT_THIS_PAGE_TRANSLATIONS}
4022
+ */
4023
+
4024
+ /**
4025
+ * Exit this Page translations
4026
+ *
4027
+ * @typedef {object} ExitThisPageTranslations
4028
+ *
4029
+ * Messages used by the component programatically inserted text, including
4030
+ * overlay text and screen reader announcements.
4031
+ * @property {string} [activated] - Screen reader announcement for when EtP
4032
+ * keypress functionality has been successfully activated.
4033
+ * @property {string} [timedOut] - Screen reader announcement for when the EtP
4034
+ * keypress functionality has timed out.
4035
+ * @property {string} [pressTwoMoreTimes] - Screen reader announcement informing
4036
+ * the user they must press the activation key two more times.
4037
+ * @property {string} [pressOneMoreTime] - Screen reader announcement informing
4038
+ * the user they must press the activation key one more time.
4039
+ */
4040
+
3643
4041
  /* eslint-disable es-x/no-function-prototype-bind -- Polyfill imported */
3644
4042
 
3645
4043
  /**
@@ -4709,6 +5107,11 @@
4709
5107
  new ErrorSummary($errorSummary, config.errorSummary).init();
4710
5108
  }
4711
5109
 
5110
+ var $exitThisPageButtons = $scope.querySelectorAll('[data-module="govuk-exit-this-page"]');
5111
+ nodeListForEach($exitThisPageButtons, function ($button) {
5112
+ new ExitThisPage($button, config.exitThisPage).init();
5113
+ });
5114
+
4712
5115
  // Find first header module to enhance.
4713
5116
  var $header = $scope.querySelector('[data-module="govuk-header"]');
4714
5117
  if ($header) {
@@ -4746,6 +5149,7 @@
4746
5149
  * @property {import('./components/button/button.mjs').ButtonConfig} [button] - Button config
4747
5150
  * @property {import('./components/character-count/character-count.mjs').CharacterCountConfig} [characterCount] - Character Count config
4748
5151
  * @property {import('./components/error-summary/error-summary.mjs').ErrorSummaryConfig} [errorSummary] - Error Summary config
5152
+ * @property {import('./components/exit-this-page/exit-this-page.mjs').ExitThisPageConfig} [exitThisPage] - Exit This Page config
4749
5153
  * @property {import('./components/notification-banner/notification-banner.mjs').NotificationBannerConfig} [notificationBanner] - Notification Banner config
4750
5154
  */
4751
5155
 
@@ -4757,6 +5161,7 @@
4757
5161
  exports.CharacterCount = CharacterCount;
4758
5162
  exports.Checkboxes = Checkboxes;
4759
5163
  exports.ErrorSummary = ErrorSummary;
5164
+ exports.ExitThisPage = ExitThisPage;
4760
5165
  exports.Header = Header;
4761
5166
  exports.NotificationBanner = NotificationBanner;
4762
5167
  exports.Radios = Radios;