defra_ruby_template 5.4.0 → 5.11.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.
- checksums.yaml +4 -4
- data/app/views/layouts/defra_ruby_template.html.erb +43 -23
- data/lib/defra_ruby_template/version.rb +1 -1
- data/lib/defra_ruby_template.rb +3 -8
- data/node_modules/govuk-frontend/dist/govuk/all.bundle.js +882 -492
- data/node_modules/govuk-frontend/dist/govuk/all.bundle.js.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/all.bundle.mjs +877 -492
- data/node_modules/govuk-frontend/dist/govuk/all.bundle.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/all.mjs +5 -0
- data/node_modules/govuk-frontend/dist/govuk/all.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/all.scss +6 -0
- data/node_modules/govuk-frontend/dist/govuk/all.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/assets/images/govuk-crest.svg +1 -0
- data/node_modules/govuk-frontend/dist/govuk/assets/rebrand/images/favicon.ico +0 -0
- data/node_modules/govuk-frontend/dist/govuk/assets/rebrand/images/favicon.svg +1 -0
- data/node_modules/govuk-frontend/dist/govuk/assets/rebrand/images/govuk-crest.svg +1 -0
- data/node_modules/govuk-frontend/dist/govuk/assets/rebrand/images/govuk-icon-180.png +0 -0
- data/node_modules/govuk-frontend/dist/govuk/assets/rebrand/images/govuk-icon-192.png +0 -0
- data/node_modules/govuk-frontend/dist/govuk/assets/rebrand/images/govuk-icon-512.png +0 -0
- data/node_modules/govuk-frontend/dist/govuk/assets/rebrand/images/govuk-icon-mask.svg +1 -0
- data/node_modules/govuk-frontend/dist/govuk/assets/rebrand/images/govuk-opengraph-image.png +0 -0
- data/node_modules/govuk-frontend/dist/govuk/assets/rebrand/manifest.json +39 -0
- data/node_modules/govuk-frontend/dist/govuk/common/closest-attribute-value.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/common/configuration.mjs +169 -0
- data/node_modules/govuk-frontend/dist/govuk/common/configuration.mjs.map +1 -0
- data/node_modules/govuk-frontend/dist/govuk/common/govuk-frontend-version.mjs +1 -1
- data/node_modules/govuk-frontend/dist/govuk/common/index.mjs +20 -83
- data/node_modules/govuk-frontend/dist/govuk/common/index.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/component.mjs +60 -0
- data/node_modules/govuk-frontend/dist/govuk/component.mjs.map +1 -0
- data/node_modules/govuk-frontend/dist/govuk/components/_index.scss +1 -0
- data/node_modules/govuk-frontend/dist/govuk/components/_index.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/accordion/_index.scss +35 -31
- data/node_modules/govuk-frontend/dist/govuk/components/accordion/_index.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.js +296 -195
- data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.js.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.mjs +295 -194
- data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.mjs +88 -110
- data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/accordion/fixtures.json +16 -3
- data/node_modules/govuk-frontend/dist/govuk/components/back-link/fixtures.json +25 -15
- data/node_modules/govuk-frontend/dist/govuk/components/back-link/template-with-custom-link.html +1 -0
- data/node_modules/govuk-frontend/dist/govuk/components/back-link/template.njk +2 -2
- data/node_modules/govuk-frontend/dist/govuk/components/breadcrumbs/fixtures.json +53 -15
- data/node_modules/govuk-frontend/dist/govuk/components/breadcrumbs/macro-options.json +6 -0
- data/node_modules/govuk-frontend/dist/govuk/components/breadcrumbs/template-default.html +2 -2
- data/node_modules/govuk-frontend/dist/govuk/components/breadcrumbs/template-inverse.html +2 -2
- data/node_modules/govuk-frontend/dist/govuk/components/breadcrumbs/template-with-collapse-on-mobile.html +2 -2
- data/node_modules/govuk-frontend/dist/govuk/components/breadcrumbs/template-with-last-breadcrumb-as-current-page.html +2 -2
- data/node_modules/govuk-frontend/dist/govuk/components/breadcrumbs/template-with-multiple-levels.html +2 -2
- data/node_modules/govuk-frontend/dist/govuk/components/breadcrumbs/template-with-one-level.html +2 -2
- data/node_modules/govuk-frontend/dist/govuk/components/breadcrumbs/template-without-the-home-section.html +2 -2
- data/node_modules/govuk-frontend/dist/govuk/components/breadcrumbs/template.njk +2 -2
- data/node_modules/govuk-frontend/dist/govuk/components/button/button.bundle.js +217 -106
- data/node_modules/govuk-frontend/dist/govuk/components/button/button.bundle.js.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/button/button.bundle.mjs +216 -105
- data/node_modules/govuk-frontend/dist/govuk/components/button/button.bundle.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/button/button.mjs +9 -22
- data/node_modules/govuk-frontend/dist/govuk/components/button/button.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/button/fixtures.json +38 -0
- data/node_modules/govuk-frontend/dist/govuk/components/button/macro-options.json +2 -1
- data/node_modules/govuk-frontend/dist/govuk/components/character-count/_index.scss +12 -4
- data/node_modules/govuk-frontend/dist/govuk/components/character-count/_index.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.js +256 -145
- data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.js.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.mjs +255 -144
- data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.mjs +29 -34
- data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/character-count/fixtures.json +80 -1
- data/node_modules/govuk-frontend/dist/govuk/components/character-count/macro-options.json +6 -6
- data/node_modules/govuk-frontend/dist/govuk/components/character-count/template-with-error.html +12 -0
- data/node_modules/govuk-frontend/dist/govuk/components/character-count/template-with-hint-and-error.html +15 -0
- data/node_modules/govuk-frontend/dist/govuk/components/character-count/template.njk +5 -4
- data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/_index.scss +6 -1
- data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/_index.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.bundle.js +96 -50
- data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.bundle.js.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.bundle.mjs +95 -49
- data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.bundle.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.mjs +9 -18
- data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/fixtures.json +88 -1
- data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/template-small-with-divider-and-none.html +34 -0
- data/node_modules/govuk-frontend/dist/govuk/components/cookie-banner/_index.scss +23 -14
- data/node_modules/govuk-frontend/dist/govuk/components/cookie-banner/_index.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/cookie-banner/fixtures.json +23 -0
- data/node_modules/govuk-frontend/dist/govuk/components/date-input/_index.scss +2 -2
- data/node_modules/govuk-frontend/dist/govuk/components/date-input/_index.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/date-input/fixtures.json +27 -0
- data/node_modules/govuk-frontend/dist/govuk/components/date-input/macro-options.json +2 -2
- data/node_modules/govuk-frontend/dist/govuk/components/details/_index.scss +7 -2
- data/node_modules/govuk-frontend/dist/govuk/components/details/_index.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/details/fixtures.json +10 -0
- data/node_modules/govuk-frontend/dist/govuk/components/error-message/fixtures.json +9 -0
- data/node_modules/govuk-frontend/dist/govuk/components/error-summary/_index.scss +4 -0
- data/node_modules/govuk-frontend/dist/govuk/components/error-summary/_index.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.bundle.js +238 -127
- data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.bundle.js.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.bundle.mjs +237 -126
- data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.bundle.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.mjs +10 -22
- data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/error-summary/fixtures.json +19 -0
- data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.js +218 -107
- data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.js.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.mjs +217 -106
- data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.mjs +10 -22
- data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/fixtures.json +4 -0
- data/node_modules/govuk-frontend/dist/govuk/components/fieldset/_index.scss +3 -5
- data/node_modules/govuk-frontend/dist/govuk/components/fieldset/_index.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/fieldset/fixtures.json +18 -0
- data/node_modules/govuk-frontend/dist/govuk/components/file-upload/_index.scss +175 -9
- data/node_modules/govuk-frontend/dist/govuk/components/file-upload/_index.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/file-upload/file-upload.bundle.js +744 -0
- data/node_modules/govuk-frontend/dist/govuk/components/file-upload/file-upload.bundle.js.map +1 -0
- data/node_modules/govuk-frontend/dist/govuk/components/file-upload/file-upload.bundle.mjs +736 -0
- data/node_modules/govuk-frontend/dist/govuk/components/file-upload/file-upload.bundle.mjs.map +1 -0
- data/node_modules/govuk-frontend/dist/govuk/components/file-upload/file-upload.mjs +257 -0
- data/node_modules/govuk-frontend/dist/govuk/components/file-upload/file-upload.mjs.map +1 -0
- data/node_modules/govuk-frontend/dist/govuk/components/file-upload/fixtures.json +220 -16
- data/node_modules/govuk-frontend/dist/govuk/components/file-upload/macro-options.json +52 -3
- data/node_modules/govuk-frontend/dist/govuk/components/file-upload/template-allows-direct-media-capture.html +6 -0
- data/node_modules/govuk-frontend/dist/govuk/components/file-upload/template-allows-image-files-only.html +6 -0
- data/node_modules/govuk-frontend/dist/govuk/components/file-upload/template-allows-multiple-files.html +6 -0
- data/node_modules/govuk-frontend/dist/govuk/components/file-upload/template-disabled.html +6 -0
- data/node_modules/govuk-frontend/dist/govuk/components/file-upload/template-enhanced-disabled.html +13 -0
- data/node_modules/govuk-frontend/dist/govuk/components/file-upload/template-enhanced-with-error-message-and-hint.html +16 -0
- data/node_modules/govuk-frontend/dist/govuk/components/file-upload/template-enhanced.html +10 -0
- data/node_modules/govuk-frontend/dist/govuk/components/file-upload/template-translated.html +10 -0
- data/node_modules/govuk-frontend/dist/govuk/components/file-upload/template.njk +42 -5
- data/node_modules/govuk-frontend/dist/govuk/components/footer/_index.scss +61 -19
- data/node_modules/govuk-frontend/dist/govuk/components/footer/_index.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/footer/fixtures.json +34 -0
- data/node_modules/govuk-frontend/dist/govuk/components/footer/macro-options.json +6 -0
- data/node_modules/govuk-frontend/dist/govuk/components/footer/template.njk +10 -0
- data/node_modules/govuk-frontend/dist/govuk/components/header/_index.scss +223 -21
- data/node_modules/govuk-frontend/dist/govuk/components/header/_index.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/header/fixtures.json +78 -32
- data/node_modules/govuk-frontend/dist/govuk/components/header/header.bundle.js +90 -43
- data/node_modules/govuk-frontend/dist/govuk/components/header/header.bundle.js.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/header/header.bundle.mjs +89 -42
- data/node_modules/govuk-frontend/dist/govuk/components/header/header.bundle.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/header/header.mjs +10 -18
- data/node_modules/govuk-frontend/dist/govuk/components/header/header.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/header/macro-options.json +31 -12
- data/node_modules/govuk-frontend/dist/govuk/components/header/template-default.html +20 -12
- data/node_modules/govuk-frontend/dist/govuk/components/header/template-full-width-with-navigation.html +21 -15
- data/node_modules/govuk-frontend/dist/govuk/components/header/template-full-width.html +21 -15
- data/node_modules/govuk-frontend/dist/govuk/components/header/template-navigation-item-with-html.html +20 -12
- data/node_modules/govuk-frontend/dist/govuk/components/header/template-navigation-item-with-text-without-link.html +20 -12
- data/node_modules/govuk-frontend/dist/govuk/components/header/template-with-custom-menu-button-label.html +20 -12
- data/node_modules/govuk-frontend/dist/govuk/components/header/template-with-custom-menu-button-text.html +20 -12
- data/node_modules/govuk-frontend/dist/govuk/components/header/template-with-custom-navigation-label.html +20 -12
- data/node_modules/govuk-frontend/dist/govuk/components/header/template-with-full-width-border.html +30 -0
- data/node_modules/govuk-frontend/dist/govuk/components/header/template-with-large-navigation.html +20 -12
- data/node_modules/govuk-frontend/dist/govuk/components/header/template-with-navigation.html +20 -12
- data/node_modules/govuk-frontend/dist/govuk/components/header/template-with-product-name.html +21 -15
- data/node_modules/govuk-frontend/dist/govuk/components/header/template-with-service-name-and-navigation.html +20 -12
- data/node_modules/govuk-frontend/dist/govuk/components/header/template-with-service-name-but-no-service-url.html +20 -12
- data/node_modules/govuk-frontend/dist/govuk/components/header/template-with-service-name.html +20 -12
- data/node_modules/govuk-frontend/dist/govuk/components/header/template-with-st-edwards-crown.html +10 -12
- data/node_modules/govuk-frontend/dist/govuk/components/header/template.njk +11 -41
- data/node_modules/govuk-frontend/dist/govuk/components/hint/fixtures.json +6 -0
- data/node_modules/govuk-frontend/dist/govuk/components/input/_index.scss +7 -4
- data/node_modules/govuk-frontend/dist/govuk/components/input/_index.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/input/fixtures.json +97 -27
- data/node_modules/govuk-frontend/dist/govuk/components/input/macro-options.json +6 -6
- data/node_modules/govuk-frontend/dist/govuk/components/input/template-default.html +2 -2
- data/node_modules/govuk-frontend/dist/govuk/components/input/template-disabled.html +6 -0
- data/node_modules/govuk-frontend/dist/govuk/components/input/template-with-error-and-hint.html +12 -0
- data/node_modules/govuk-frontend/dist/govuk/components/input/template-with-error-message.html +2 -5
- data/node_modules/govuk-frontend/dist/govuk/components/input/template.njk +5 -4
- data/node_modules/govuk-frontend/dist/govuk/components/inset-text/fixtures.json +6 -0
- data/node_modules/govuk-frontend/dist/govuk/components/label/fixtures.json +17 -0
- data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/_index.scss +3 -2
- data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/_index.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/fixtures.json +24 -0
- data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.bundle.js +238 -127
- data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.bundle.js.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.bundle.mjs +237 -126
- data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.bundle.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.mjs +10 -22
- data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/pagination/_index.scss +24 -37
- data/node_modules/govuk-frontend/dist/govuk/components/pagination/_index.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/pagination/fixtures.json +15 -0
- data/node_modules/govuk-frontend/dist/govuk/components/pagination/macro-options.json +4 -4
- data/node_modules/govuk-frontend/dist/govuk/components/panel/_index.scss +13 -9
- data/node_modules/govuk-frontend/dist/govuk/components/panel/_index.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/panel/fixtures.json +9 -0
- data/node_modules/govuk-frontend/dist/govuk/components/password-input/_index.scss +12 -9
- data/node_modules/govuk-frontend/dist/govuk/components/password-input/_index.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/password-input/fixtures.json +27 -2
- data/node_modules/govuk-frontend/dist/govuk/components/password-input/macro-options.json +3 -3
- data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.bundle.js +220 -110
- data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.bundle.js.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.bundle.mjs +219 -109
- data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.bundle.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.mjs +12 -25
- data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/password-input/template-default.html +3 -3
- data/node_modules/govuk-frontend/dist/govuk/components/password-input/template.njk +4 -2
- data/node_modules/govuk-frontend/dist/govuk/components/phase-banner/_index.scss +0 -8
- data/node_modules/govuk-frontend/dist/govuk/components/phase-banner/_index.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/phase-banner/fixtures.json +7 -0
- data/node_modules/govuk-frontend/dist/govuk/components/radios/_index.scss +7 -5
- data/node_modules/govuk-frontend/dist/govuk/components/radios/_index.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/radios/fixtures.json +51 -5
- data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.bundle.js +96 -50
- data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.bundle.js.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.bundle.mjs +95 -49
- data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.bundle.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.mjs +9 -18
- data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/radios/template-small-with-a-divider.html +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/radios/template-with-a-divider.html +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/select/_index.scss +5 -5
- data/node_modules/govuk-frontend/dist/govuk/components/select/_index.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/select/fixtures.json +43 -9
- data/node_modules/govuk-frontend/dist/govuk/components/select/macro-options.json +2 -2
- data/node_modules/govuk-frontend/dist/govuk/components/select/template-id.html +7 -0
- data/node_modules/govuk-frontend/dist/govuk/components/select/template.njk +6 -4
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/README.md +15 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/_index.scss +187 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/_index.scss.map +1 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/_service-navigation.scss +4 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/_service-navigation.scss.map +1 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/fixtures.json +515 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/macro-options.json +138 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/macro.njk +3 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.bundle.js +229 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.bundle.js.map +1 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.bundle.mjs +221 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.bundle.mjs.map +1 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.mjs +85 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.mjs.map +1 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template-default.html +57 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template-with-html-navigation-items.html +49 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template-with-large-navigation.html +153 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template-with-long-service-name.html +20 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template-with-navigation-with-a-current-item.html +58 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template-with-navigation-with-an-active-item.html +58 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template-with-non-link-navigation-items.html +49 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template-with-service-link.html +20 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template-with-service-name-and-navigation.html +63 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template-with-service-name.html +18 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template.njk +103 -0
- data/node_modules/govuk-frontend/dist/govuk/components/skip-link/_index.scss +6 -4
- data/node_modules/govuk-frontend/dist/govuk/components/skip-link/_index.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/skip-link/fixtures.json +9 -0
- data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.bundle.js +95 -48
- data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.bundle.js.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.bundle.mjs +94 -47
- data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.bundle.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.mjs +15 -23
- data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/summary-list/_index.scss +14 -22
- data/node_modules/govuk-frontend/dist/govuk/components/summary-list/_index.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/summary-list/fixtures.json +174 -1
- data/node_modules/govuk-frontend/dist/govuk/components/summary-list/template-as-a-summary-card-extreme.html +106 -0
- data/node_modules/govuk-frontend/dist/govuk/components/table/fixtures.json +22 -1
- data/node_modules/govuk-frontend/dist/govuk/components/tabs/fixtures.json +15 -1
- data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.bundle.js +95 -53
- data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.bundle.js.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.bundle.mjs +94 -52
- data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.bundle.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.mjs +15 -28
- data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/tag/_index.scss +11 -8
- data/node_modules/govuk-frontend/dist/govuk/components/tag/_index.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/tag/fixtures.json +14 -0
- data/node_modules/govuk-frontend/dist/govuk/components/task-list/_index.scss +12 -10
- data/node_modules/govuk-frontend/dist/govuk/components/task-list/_index.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/task-list/fixtures.json +44 -0
- data/node_modules/govuk-frontend/dist/govuk/components/task-list/template-with-empty-values.html +27 -0
- data/node_modules/govuk-frontend/dist/govuk/components/task-list/template.njk +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/textarea/_index.scss +3 -3
- data/node_modules/govuk-frontend/dist/govuk/components/textarea/_index.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/textarea/fixtures.json +32 -1
- data/node_modules/govuk-frontend/dist/govuk/components/textarea/macro-options.json +3 -3
- data/node_modules/govuk-frontend/dist/govuk/components/textarea/template.njk +6 -4
- data/node_modules/govuk-frontend/dist/govuk/components/warning-text/_index.scss +6 -5
- data/node_modules/govuk-frontend/dist/govuk/components/warning-text/_index.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/warning-text/fixtures.json +8 -0
- data/node_modules/govuk-frontend/dist/govuk/core/_govuk-frontend-properties.scss +1 -1
- data/node_modules/govuk-frontend/dist/govuk/core/_govuk-frontend-properties.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/errors/index.mjs +16 -3
- data/node_modules/govuk-frontend/dist/govuk/errors/index.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/govuk-frontend.min.css +2 -2
- data/node_modules/govuk-frontend/dist/govuk/govuk-frontend.min.css.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/govuk-frontend.min.js +1 -1
- data/node_modules/govuk-frontend/dist/govuk/govuk-frontend.min.js.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/helpers/_colour.scss +55 -8
- data/node_modules/govuk-frontend/dist/govuk/helpers/_colour.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/helpers/_focused.scss +14 -4
- data/node_modules/govuk-frontend/dist/govuk/helpers/_focused.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/helpers/_grid.scss +1 -1
- data/node_modules/govuk-frontend/dist/govuk/helpers/_grid.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/helpers/_links.scss +2 -2
- data/node_modules/govuk-frontend/dist/govuk/helpers/_links.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/helpers/_shape-arrow.scss +1 -1
- data/node_modules/govuk-frontend/dist/govuk/helpers/_shape-arrow.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/helpers/_typography.scss +18 -1
- data/node_modules/govuk-frontend/dist/govuk/helpers/_typography.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/helpers/_visually-hidden.scss +30 -62
- data/node_modules/govuk-frontend/dist/govuk/helpers/_visually-hidden.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/i18n.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/init.mjs +90 -24
- data/node_modules/govuk-frontend/dist/govuk/init.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/macros/logo.njk +78 -0
- data/node_modules/govuk-frontend/dist/govuk/objects/_template.scss +5 -1
- data/node_modules/govuk-frontend/dist/govuk/objects/_template.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/overrides/_typography.scss +5 -1
- data/node_modules/govuk-frontend/dist/govuk/overrides/_typography.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/settings/_colours-applied.scss +39 -1
- data/node_modules/govuk-frontend/dist/govuk/settings/_colours-applied.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/settings/_colours-organisations.scss +244 -10
- data/node_modules/govuk-frontend/dist/govuk/settings/_colours-organisations.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/settings/_typography-responsive.scss +5 -10
- data/node_modules/govuk-frontend/dist/govuk/settings/_typography-responsive.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/template.njk +20 -9
- data/node_modules/govuk-frontend/dist/govuk/tools/_index.scss +1 -0
- data/node_modules/govuk-frontend/dist/govuk/tools/_index.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/tools/_rebrand.scss +65 -0
- data/node_modules/govuk-frontend/dist/govuk/tools/_rebrand.scss.map +1 -0
- data/node_modules/govuk-frontend/dist/govuk-prototype-kit/functions.js +25 -0
- data/node_modules/govuk-frontend/dist/govuk-prototype-kit/init.scss +1 -1
- data/node_modules/govuk-frontend/dist/govuk-prototype-kit/init.scss.map +1 -1
- data/node_modules/govuk-frontend/govuk-prototype-kit.config.json +8 -1
- data/node_modules/govuk-frontend/package.json +16 -16
- data/spec/spec_helper.rb +98 -0
- data/spec/tasks/assets_spec.rb +53 -0
- data/vendor/assets/assets/fonts/bold-affa96571d-v2.woff +0 -0
- data/vendor/assets/assets/fonts/bold-b542beb274-v2.woff2 +0 -0
- data/vendor/assets/assets/fonts/light-94a07e06a1-v2.woff2 +0 -0
- data/vendor/assets/assets/fonts/light-f591b13f7d-v2.woff +0 -0
- data/vendor/assets/assets/images/favicon.ico +0 -0
- data/vendor/assets/assets/images/favicon.svg +1 -0
- data/vendor/assets/assets/images/govuk-crest.svg +1 -0
- data/vendor/assets/assets/images/govuk-icon-180.png +0 -0
- data/vendor/assets/assets/images/govuk-icon-192.png +0 -0
- data/vendor/assets/assets/images/govuk-icon-512.png +0 -0
- data/vendor/assets/assets/images/govuk-icon-mask.svg +1 -0
- data/vendor/assets/assets/images/govuk-opengraph-image.png +0 -0
- data/vendor/assets/assets/manifest.json +39 -0
- data/vendor/assets/assets/rebrand/images/favicon.ico +0 -0
- data/vendor/assets/assets/rebrand/images/favicon.svg +1 -0
- data/vendor/assets/assets/rebrand/images/govuk-crest.svg +1 -0
- data/vendor/assets/assets/rebrand/images/govuk-icon-180.png +0 -0
- data/vendor/assets/assets/rebrand/images/govuk-icon-192.png +0 -0
- data/vendor/assets/assets/rebrand/images/govuk-icon-512.png +0 -0
- data/vendor/assets/assets/rebrand/images/govuk-icon-mask.svg +1 -0
- data/vendor/assets/assets/rebrand/images/govuk-opengraph-image.png +0 -0
- data/vendor/assets/assets/rebrand/manifest.json +39 -0
- data/vendor/assets/images/govuk-crest.svg +1 -0
- data/vendor/assets/javascripts/defra_ruby_template.js +882 -492
- data/vendor/assets/stylesheets/all.scss +6 -0
- data/vendor/assets/stylesheets/components/_index.scss +1 -0
- data/vendor/assets/stylesheets/components/accordion/_index.scss +35 -31
- data/vendor/assets/stylesheets/components/character-count/_index.scss +12 -4
- data/vendor/assets/stylesheets/components/checkboxes/_index.scss +6 -1
- data/vendor/assets/stylesheets/components/cookie-banner/_index.scss +23 -14
- data/vendor/assets/stylesheets/components/date-input/_index.scss +2 -2
- data/vendor/assets/stylesheets/components/details/_index.scss +7 -2
- data/vendor/assets/stylesheets/components/error-summary/_index.scss +4 -0
- data/vendor/assets/stylesheets/components/fieldset/_index.scss +3 -5
- data/vendor/assets/stylesheets/components/file-upload/_index.scss +175 -9
- data/vendor/assets/stylesheets/components/footer/_index.scss +61 -19
- data/vendor/assets/stylesheets/components/header/_index.scss +223 -21
- data/vendor/assets/stylesheets/components/input/_index.scss +7 -4
- data/vendor/assets/stylesheets/components/notification-banner/_index.scss +3 -2
- data/vendor/assets/stylesheets/components/pagination/_index.scss +24 -37
- data/vendor/assets/stylesheets/components/panel/_index.scss +13 -9
- data/vendor/assets/stylesheets/components/password-input/_index.scss +12 -9
- data/vendor/assets/stylesheets/components/phase-banner/_index.scss +0 -8
- data/vendor/assets/stylesheets/components/radios/_index.scss +7 -5
- data/vendor/assets/stylesheets/components/select/_index.scss +5 -5
- data/vendor/assets/stylesheets/components/service-navigation/_index.scss +187 -0
- data/vendor/assets/stylesheets/components/service-navigation/_service-navigation.scss +4 -0
- data/vendor/assets/stylesheets/components/skip-link/_index.scss +6 -4
- data/vendor/assets/stylesheets/components/summary-list/_index.scss +14 -22
- data/vendor/assets/stylesheets/components/tag/_index.scss +11 -8
- data/vendor/assets/stylesheets/components/task-list/_index.scss +12 -10
- data/vendor/assets/stylesheets/components/textarea/_index.scss +3 -3
- data/vendor/assets/stylesheets/components/warning-text/_index.scss +6 -5
- data/vendor/assets/stylesheets/core/_govuk-frontend-properties.scss +1 -1
- data/vendor/assets/stylesheets/defra_ruby_template.scss +6 -0
- data/vendor/assets/stylesheets/govuk-frontend.min.css +2 -2
- data/vendor/assets/stylesheets/helpers/_colour.scss +55 -8
- data/vendor/assets/stylesheets/helpers/_focused.scss +14 -4
- data/vendor/assets/stylesheets/helpers/_grid.scss +1 -1
- data/vendor/assets/stylesheets/helpers/_links.scss +2 -2
- data/vendor/assets/stylesheets/helpers/_shape-arrow.scss +1 -1
- data/vendor/assets/stylesheets/helpers/_typography.scss +18 -1
- data/vendor/assets/stylesheets/helpers/_visually-hidden.scss +30 -62
- data/vendor/assets/stylesheets/objects/_template.scss +5 -1
- data/vendor/assets/stylesheets/overrides/_typography.scss +5 -1
- data/vendor/assets/stylesheets/settings/_colours-applied.scss +39 -1
- data/vendor/assets/stylesheets/settings/_colours-organisations.scss +244 -10
- data/vendor/assets/stylesheets/settings/_typography-responsive.scss +5 -10
- data/vendor/assets/stylesheets/tools/_index.scss +1 -0
- data/vendor/assets/stylesheets/tools/_rebrand.scss +65 -0
- metadata +100 -24
- data/.github/dependabot.yml +0 -14
- data/.github/workflows/ci.yml +0 -31
- data/.gitignore +0 -8
- data/.rspec +0 -1
- data/.rubocop.yml +0 -3
- data/.ruby-version +0 -1
- data/Gemfile +0 -9
- data/Gemfile.lock +0 -58
- data/defra_ruby_template.gemspec +0 -35
- data/node_modules/.package-lock.json +0 -16
- data/node_modules/govuk-frontend/dist/govuk/assets/images/govuk-crest-2x.png +0 -0
- data/node_modules/govuk-frontend/dist/govuk/assets/images/govuk-crest.png +0 -0
- data/node_modules/govuk-frontend/dist/govuk/common/normalise-dataset.mjs +0 -18
- data/node_modules/govuk-frontend/dist/govuk/common/normalise-dataset.mjs.map +0 -1
- data/node_modules/govuk-frontend/dist/govuk/common/normalise-string.mjs +0 -31
- data/node_modules/govuk-frontend/dist/govuk/common/normalise-string.mjs.map +0 -1
- data/node_modules/govuk-frontend/dist/govuk/components/file-upload/template-with-value.html +0 -6
- data/node_modules/govuk-frontend/dist/govuk/govuk-frontend-component.mjs +0 -16
- data/node_modules/govuk-frontend/dist/govuk/govuk-frontend-component.mjs.map +0 -1
- data/package-lock.json +0 -24
- data/package.json +0 -29
@@ -1,81 +1,11 @@
|
|
1
1
|
(function (global, factory) {
|
2
2
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
3
3
|
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
4
|
-
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.GOVUKFrontend = {}));
|
4
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.GOVUKFrontend = global.GOVUKFrontend || {}));
|
5
5
|
})(this, (function (exports) { 'use strict';
|
6
6
|
|
7
|
-
const version = '5.
|
7
|
+
const version = '5.10.2';
|
8
8
|
|
9
|
-
function normaliseString(value, property) {
|
10
|
-
const trimmedValue = value ? value.trim() : '';
|
11
|
-
let output;
|
12
|
-
let outputType = property == null ? void 0 : property.type;
|
13
|
-
if (!outputType) {
|
14
|
-
if (['true', 'false'].includes(trimmedValue)) {
|
15
|
-
outputType = 'boolean';
|
16
|
-
}
|
17
|
-
if (trimmedValue.length > 0 && isFinite(Number(trimmedValue))) {
|
18
|
-
outputType = 'number';
|
19
|
-
}
|
20
|
-
}
|
21
|
-
switch (outputType) {
|
22
|
-
case 'boolean':
|
23
|
-
output = trimmedValue === 'true';
|
24
|
-
break;
|
25
|
-
case 'number':
|
26
|
-
output = Number(trimmedValue);
|
27
|
-
break;
|
28
|
-
default:
|
29
|
-
output = value;
|
30
|
-
}
|
31
|
-
return output;
|
32
|
-
}
|
33
|
-
|
34
|
-
/**
|
35
|
-
* @typedef {import('./index.mjs').SchemaProperty} SchemaProperty
|
36
|
-
*/
|
37
|
-
|
38
|
-
function mergeConfigs(...configObjects) {
|
39
|
-
const formattedConfigObject = {};
|
40
|
-
for (const configObject of configObjects) {
|
41
|
-
for (const key of Object.keys(configObject)) {
|
42
|
-
const option = formattedConfigObject[key];
|
43
|
-
const override = configObject[key];
|
44
|
-
if (isObject(option) && isObject(override)) {
|
45
|
-
formattedConfigObject[key] = mergeConfigs(option, override);
|
46
|
-
} else {
|
47
|
-
formattedConfigObject[key] = override;
|
48
|
-
}
|
49
|
-
}
|
50
|
-
}
|
51
|
-
return formattedConfigObject;
|
52
|
-
}
|
53
|
-
function extractConfigByNamespace(Component, dataset, namespace) {
|
54
|
-
const property = Component.schema.properties[namespace];
|
55
|
-
if ((property == null ? void 0 : property.type) !== 'object') {
|
56
|
-
return;
|
57
|
-
}
|
58
|
-
const newObject = {
|
59
|
-
[namespace]: ({})
|
60
|
-
};
|
61
|
-
for (const [key, value] of Object.entries(dataset)) {
|
62
|
-
let current = newObject;
|
63
|
-
const keyParts = key.split('.');
|
64
|
-
for (const [index, name] of keyParts.entries()) {
|
65
|
-
if (typeof current === 'object') {
|
66
|
-
if (index < keyParts.length - 1) {
|
67
|
-
if (!isObject(current[name])) {
|
68
|
-
current[name] = {};
|
69
|
-
}
|
70
|
-
current = current[name];
|
71
|
-
} else if (key !== namespace) {
|
72
|
-
current[name] = normaliseString(value);
|
73
|
-
}
|
74
|
-
}
|
75
|
-
}
|
76
|
-
}
|
77
|
-
return newObject[namespace];
|
78
|
-
}
|
79
9
|
function getFragmentFromUrl(url) {
|
80
10
|
if (!url.includes('#')) {
|
81
11
|
return undefined;
|
@@ -114,75 +44,42 @@
|
|
114
44
|
(_options$onBeforeFocu = options.onBeforeFocus) == null || _options$onBeforeFocu.call($element);
|
115
45
|
$element.focus();
|
116
46
|
}
|
47
|
+
function isInitialised($root, moduleName) {
|
48
|
+
return $root instanceof HTMLElement && $root.hasAttribute(`data-${moduleName}-init`);
|
49
|
+
}
|
50
|
+
|
51
|
+
/**
|
52
|
+
* Checks if GOV.UK Frontend is supported on this page
|
53
|
+
*
|
54
|
+
* Some browsers will load and run our JavaScript but GOV.UK Frontend
|
55
|
+
* won't be supported.
|
56
|
+
*
|
57
|
+
* @param {HTMLElement | null} [$scope] - (internal) `<body>` HTML element checked for browser support
|
58
|
+
* @returns {boolean} Whether GOV.UK Frontend is supported on this page
|
59
|
+
*/
|
117
60
|
function isSupported($scope = document.body) {
|
118
61
|
if (!$scope) {
|
119
62
|
return false;
|
120
63
|
}
|
121
64
|
return $scope.classList.contains('govuk-frontend-supported');
|
122
65
|
}
|
123
|
-
function validateConfig(schema, config) {
|
124
|
-
const validationErrors = [];
|
125
|
-
for (const [name, conditions] of Object.entries(schema)) {
|
126
|
-
const errors = [];
|
127
|
-
if (Array.isArray(conditions)) {
|
128
|
-
for (const {
|
129
|
-
required,
|
130
|
-
errorMessage
|
131
|
-
} of conditions) {
|
132
|
-
if (!required.every(key => !!config[key])) {
|
133
|
-
errors.push(errorMessage);
|
134
|
-
}
|
135
|
-
}
|
136
|
-
if (name === 'anyOf' && !(conditions.length - errors.length >= 1)) {
|
137
|
-
validationErrors.push(...errors);
|
138
|
-
}
|
139
|
-
}
|
140
|
-
}
|
141
|
-
return validationErrors;
|
142
|
-
}
|
143
66
|
function isArray(option) {
|
144
67
|
return Array.isArray(option);
|
145
68
|
}
|
146
69
|
function isObject(option) {
|
147
70
|
return !!option && typeof option === 'object' && !isArray(option);
|
148
71
|
}
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
*
|
153
|
-
* @typedef {object} Schema
|
154
|
-
* @property {{ [field: string]: SchemaProperty | undefined }} properties - Schema properties
|
155
|
-
* @property {SchemaCondition[]} [anyOf] - List of schema conditions
|
156
|
-
*/
|
157
|
-
|
72
|
+
function formatErrorMessage(Component, message) {
|
73
|
+
return `${Component.moduleName}: ${message}`;
|
74
|
+
}
|
158
75
|
/**
|
159
|
-
*
|
160
|
-
*
|
161
|
-
* @typedef {object} SchemaProperty
|
162
|
-
* @property {'string' | 'boolean' | 'number' | 'object'} type - Property type
|
76
|
+
* @typedef ComponentWithModuleName
|
77
|
+
* @property {string} moduleName - Name of the component
|
163
78
|
*/
|
164
|
-
|
165
79
|
/**
|
166
|
-
*
|
167
|
-
*
|
168
|
-
* @typedef {object} SchemaCondition
|
169
|
-
* @property {string[]} required - List of required config fields
|
170
|
-
* @property {string} errorMessage - Error message when required config fields not provided
|
80
|
+
* @import { ObjectNested } from './configuration.mjs'
|
171
81
|
*/
|
172
82
|
|
173
|
-
function normaliseDataset(Component, dataset) {
|
174
|
-
const out = {};
|
175
|
-
for (const [field, property] of Object.entries(Component.schema.properties)) {
|
176
|
-
if (field in dataset) {
|
177
|
-
out[field] = normaliseString(dataset[field], property);
|
178
|
-
}
|
179
|
-
if ((property == null ? void 0 : property.type) === 'object') {
|
180
|
-
out[field] = extractConfigByNamespace(Component, dataset, field);
|
181
|
-
}
|
182
|
-
}
|
183
|
-
return out;
|
184
|
-
}
|
185
|
-
|
186
83
|
class GOVUKFrontendError extends Error {
|
187
84
|
constructor(...args) {
|
188
85
|
super(...args);
|
@@ -212,30 +109,248 @@
|
|
212
109
|
let message = typeof messageOrOptions === 'string' ? messageOrOptions : '';
|
213
110
|
if (typeof messageOrOptions === 'object') {
|
214
111
|
const {
|
215
|
-
|
112
|
+
component,
|
216
113
|
identifier,
|
217
114
|
element,
|
218
115
|
expectedType
|
219
116
|
} = messageOrOptions;
|
220
|
-
message =
|
117
|
+
message = identifier;
|
221
118
|
message += element ? ` is not of type ${expectedType != null ? expectedType : 'HTMLElement'}` : ' not found';
|
119
|
+
message = formatErrorMessage(component, message);
|
222
120
|
}
|
223
121
|
super(message);
|
224
122
|
this.name = 'ElementError';
|
225
123
|
}
|
226
124
|
}
|
125
|
+
class InitError extends GOVUKFrontendError {
|
126
|
+
constructor(componentOrMessage) {
|
127
|
+
const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
|
128
|
+
super(message);
|
129
|
+
this.name = 'InitError';
|
130
|
+
}
|
131
|
+
}
|
132
|
+
/**
|
133
|
+
* @import { ComponentWithModuleName } from '../common/index.mjs'
|
134
|
+
*/
|
227
135
|
|
228
|
-
class
|
229
|
-
|
230
|
-
|
136
|
+
class Component {
|
137
|
+
/**
|
138
|
+
* Returns the root element of the component
|
139
|
+
*
|
140
|
+
* @protected
|
141
|
+
* @returns {RootElementType} - the root element of component
|
142
|
+
*/
|
143
|
+
get $root() {
|
144
|
+
return this._$root;
|
145
|
+
}
|
146
|
+
constructor($root) {
|
147
|
+
this._$root = void 0;
|
148
|
+
const childConstructor = this.constructor;
|
149
|
+
if (typeof childConstructor.moduleName !== 'string') {
|
150
|
+
throw new InitError(`\`moduleName\` not defined in component`);
|
151
|
+
}
|
152
|
+
if (!($root instanceof childConstructor.elementType)) {
|
153
|
+
throw new ElementError({
|
154
|
+
element: $root,
|
155
|
+
component: childConstructor,
|
156
|
+
identifier: 'Root element (`$root`)',
|
157
|
+
expectedType: childConstructor.elementType.name
|
158
|
+
});
|
159
|
+
} else {
|
160
|
+
this._$root = $root;
|
161
|
+
}
|
162
|
+
childConstructor.checkSupport();
|
163
|
+
this.checkInitialised();
|
164
|
+
const moduleName = childConstructor.moduleName;
|
165
|
+
this.$root.setAttribute(`data-${moduleName}-init`, '');
|
166
|
+
}
|
167
|
+
checkInitialised() {
|
168
|
+
const constructor = this.constructor;
|
169
|
+
const moduleName = constructor.moduleName;
|
170
|
+
if (moduleName && isInitialised(this.$root, moduleName)) {
|
171
|
+
throw new InitError(constructor);
|
172
|
+
}
|
231
173
|
}
|
232
|
-
checkSupport() {
|
174
|
+
static checkSupport() {
|
233
175
|
if (!isSupported()) {
|
234
176
|
throw new SupportError();
|
235
177
|
}
|
236
178
|
}
|
237
179
|
}
|
238
180
|
|
181
|
+
/**
|
182
|
+
* @typedef ChildClass
|
183
|
+
* @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
|
184
|
+
*/
|
185
|
+
|
186
|
+
/**
|
187
|
+
* @typedef {typeof Component & ChildClass} ChildClassConstructor
|
188
|
+
*/
|
189
|
+
Component.elementType = HTMLElement;
|
190
|
+
|
191
|
+
const configOverride = Symbol.for('configOverride');
|
192
|
+
class ConfigurableComponent extends Component {
|
193
|
+
[configOverride](param) {
|
194
|
+
return {};
|
195
|
+
}
|
196
|
+
|
197
|
+
/**
|
198
|
+
* Returns the root element of the component
|
199
|
+
*
|
200
|
+
* @protected
|
201
|
+
* @returns {ConfigurationType} - the root element of component
|
202
|
+
*/
|
203
|
+
get config() {
|
204
|
+
return this._config;
|
205
|
+
}
|
206
|
+
constructor($root, config) {
|
207
|
+
super($root);
|
208
|
+
this._config = void 0;
|
209
|
+
const childConstructor = this.constructor;
|
210
|
+
if (!isObject(childConstructor.defaults)) {
|
211
|
+
throw new ConfigError(formatErrorMessage(childConstructor, 'Config passed as parameter into constructor but no defaults defined'));
|
212
|
+
}
|
213
|
+
const datasetConfig = normaliseDataset(childConstructor, this._$root.dataset);
|
214
|
+
this._config = mergeConfigs(childConstructor.defaults, config != null ? config : {}, this[configOverride](datasetConfig), datasetConfig);
|
215
|
+
}
|
216
|
+
}
|
217
|
+
function normaliseString(value, property) {
|
218
|
+
const trimmedValue = value ? value.trim() : '';
|
219
|
+
let output;
|
220
|
+
let outputType = property == null ? void 0 : property.type;
|
221
|
+
if (!outputType) {
|
222
|
+
if (['true', 'false'].includes(trimmedValue)) {
|
223
|
+
outputType = 'boolean';
|
224
|
+
}
|
225
|
+
if (trimmedValue.length > 0 && isFinite(Number(trimmedValue))) {
|
226
|
+
outputType = 'number';
|
227
|
+
}
|
228
|
+
}
|
229
|
+
switch (outputType) {
|
230
|
+
case 'boolean':
|
231
|
+
output = trimmedValue === 'true';
|
232
|
+
break;
|
233
|
+
case 'number':
|
234
|
+
output = Number(trimmedValue);
|
235
|
+
break;
|
236
|
+
default:
|
237
|
+
output = value;
|
238
|
+
}
|
239
|
+
return output;
|
240
|
+
}
|
241
|
+
function normaliseDataset(Component, dataset) {
|
242
|
+
if (!isObject(Component.schema)) {
|
243
|
+
throw new ConfigError(formatErrorMessage(Component, 'Config passed as parameter into constructor but no schema defined'));
|
244
|
+
}
|
245
|
+
const out = {};
|
246
|
+
const entries = Object.entries(Component.schema.properties);
|
247
|
+
for (const entry of entries) {
|
248
|
+
const [namespace, property] = entry;
|
249
|
+
const field = namespace.toString();
|
250
|
+
if (field in dataset) {
|
251
|
+
out[field] = normaliseString(dataset[field], property);
|
252
|
+
}
|
253
|
+
if ((property == null ? void 0 : property.type) === 'object') {
|
254
|
+
out[field] = extractConfigByNamespace(Component.schema, dataset, namespace);
|
255
|
+
}
|
256
|
+
}
|
257
|
+
return out;
|
258
|
+
}
|
259
|
+
function mergeConfigs(...configObjects) {
|
260
|
+
const formattedConfigObject = {};
|
261
|
+
for (const configObject of configObjects) {
|
262
|
+
for (const key of Object.keys(configObject)) {
|
263
|
+
const option = formattedConfigObject[key];
|
264
|
+
const override = configObject[key];
|
265
|
+
if (isObject(option) && isObject(override)) {
|
266
|
+
formattedConfigObject[key] = mergeConfigs(option, override);
|
267
|
+
} else {
|
268
|
+
formattedConfigObject[key] = override;
|
269
|
+
}
|
270
|
+
}
|
271
|
+
}
|
272
|
+
return formattedConfigObject;
|
273
|
+
}
|
274
|
+
function validateConfig(schema, config) {
|
275
|
+
const validationErrors = [];
|
276
|
+
for (const [name, conditions] of Object.entries(schema)) {
|
277
|
+
const errors = [];
|
278
|
+
if (Array.isArray(conditions)) {
|
279
|
+
for (const {
|
280
|
+
required,
|
281
|
+
errorMessage
|
282
|
+
} of conditions) {
|
283
|
+
if (!required.every(key => !!config[key])) {
|
284
|
+
errors.push(errorMessage);
|
285
|
+
}
|
286
|
+
}
|
287
|
+
if (name === 'anyOf' && !(conditions.length - errors.length >= 1)) {
|
288
|
+
validationErrors.push(...errors);
|
289
|
+
}
|
290
|
+
}
|
291
|
+
}
|
292
|
+
return validationErrors;
|
293
|
+
}
|
294
|
+
function extractConfigByNamespace(schema, dataset, namespace) {
|
295
|
+
const property = schema.properties[namespace];
|
296
|
+
if ((property == null ? void 0 : property.type) !== 'object') {
|
297
|
+
return;
|
298
|
+
}
|
299
|
+
const newObject = {
|
300
|
+
[namespace]: {}
|
301
|
+
};
|
302
|
+
for (const [key, value] of Object.entries(dataset)) {
|
303
|
+
let current = newObject;
|
304
|
+
const keyParts = key.split('.');
|
305
|
+
for (const [index, name] of keyParts.entries()) {
|
306
|
+
if (isObject(current)) {
|
307
|
+
if (index < keyParts.length - 1) {
|
308
|
+
if (!isObject(current[name])) {
|
309
|
+
current[name] = {};
|
310
|
+
}
|
311
|
+
current = current[name];
|
312
|
+
} else if (key !== namespace) {
|
313
|
+
current[name] = normaliseString(value);
|
314
|
+
}
|
315
|
+
}
|
316
|
+
}
|
317
|
+
}
|
318
|
+
return newObject[namespace];
|
319
|
+
}
|
320
|
+
/**
|
321
|
+
* Schema for component config
|
322
|
+
*
|
323
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType
|
324
|
+
* @typedef {object} Schema
|
325
|
+
* @property {Record<keyof ConfigurationType, SchemaProperty | undefined>} properties - Schema properties
|
326
|
+
* @property {SchemaCondition<ConfigurationType>[]} [anyOf] - List of schema conditions
|
327
|
+
*/
|
328
|
+
/**
|
329
|
+
* Schema property for component config
|
330
|
+
*
|
331
|
+
* @typedef {object} SchemaProperty
|
332
|
+
* @property {'string' | 'boolean' | 'number' | 'object'} type - Property type
|
333
|
+
*/
|
334
|
+
/**
|
335
|
+
* Schema condition for component config
|
336
|
+
*
|
337
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType
|
338
|
+
* @typedef {object} SchemaCondition
|
339
|
+
* @property {(keyof ConfigurationType)[]} required - List of required config fields
|
340
|
+
* @property {string} errorMessage - Error message when required config fields not provided
|
341
|
+
*/
|
342
|
+
/**
|
343
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} [ConfigurationType=ObjectNested]
|
344
|
+
* @typedef ChildClass
|
345
|
+
* @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
|
346
|
+
* @property {Schema<ConfigurationType>} [schema] - The schema of the component configuration
|
347
|
+
* @property {ConfigurationType} [defaults] - The default values of the configuration of the component
|
348
|
+
*/
|
349
|
+
/**
|
350
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} [ConfigurationType=ObjectNested]
|
351
|
+
* @typedef {typeof Component & ChildClass<ConfigurationType>} ChildClassConstructor<ConfigurationType>
|
352
|
+
*/
|
353
|
+
|
239
354
|
class I18n {
|
240
355
|
constructor(translations = {}, config = {}) {
|
241
356
|
var _config$locale;
|
@@ -442,16 +557,15 @@
|
|
442
557
|
* attribute, which also provides accessibility.
|
443
558
|
*
|
444
559
|
* @preserve
|
560
|
+
* @augments ConfigurableComponent<AccordionConfig>
|
445
561
|
*/
|
446
|
-
class Accordion extends
|
562
|
+
class Accordion extends ConfigurableComponent {
|
447
563
|
/**
|
448
|
-
* @param {Element | null} $
|
564
|
+
* @param {Element | null} $root - HTML element to use for accordion
|
449
565
|
* @param {AccordionConfig} [config] - Accordion config
|
450
566
|
*/
|
451
|
-
constructor($
|
452
|
-
super();
|
453
|
-
this.$module = void 0;
|
454
|
-
this.config = void 0;
|
567
|
+
constructor($root, config = {}) {
|
568
|
+
super($root, config);
|
455
569
|
this.i18n = void 0;
|
456
570
|
this.controlsClass = 'govuk-accordion__controls';
|
457
571
|
this.showAllClass = 'govuk-accordion__show-all';
|
@@ -473,33 +587,21 @@
|
|
473
587
|
this.sectionSummaryFocusClass = 'govuk-accordion__section-summary-focus';
|
474
588
|
this.sectionContentClass = 'govuk-accordion__section-content';
|
475
589
|
this.$sections = void 0;
|
476
|
-
this.browserSupportsSessionStorage = false;
|
477
590
|
this.$showAllButton = null;
|
478
591
|
this.$showAllIcon = null;
|
479
592
|
this.$showAllText = null;
|
480
|
-
if (!($module instanceof HTMLElement)) {
|
481
|
-
throw new ElementError({
|
482
|
-
componentName: 'Accordion',
|
483
|
-
element: $module,
|
484
|
-
identifier: 'Root element (`$module`)'
|
485
|
-
});
|
486
|
-
}
|
487
|
-
this.$module = $module;
|
488
|
-
this.config = mergeConfigs(Accordion.defaults, config, normaliseDataset(Accordion, $module.dataset));
|
489
593
|
this.i18n = new I18n(this.config.i18n);
|
490
|
-
const $sections = this.$
|
594
|
+
const $sections = this.$root.querySelectorAll(`.${this.sectionClass}`);
|
491
595
|
if (!$sections.length) {
|
492
596
|
throw new ElementError({
|
493
|
-
|
597
|
+
component: Accordion,
|
494
598
|
identifier: `Sections (\`<div class="${this.sectionClass}">\`)`
|
495
599
|
});
|
496
600
|
}
|
497
601
|
this.$sections = $sections;
|
498
|
-
this.browserSupportsSessionStorage = helper.checkForSessionStorage();
|
499
602
|
this.initControls();
|
500
603
|
this.initSectionHeaders();
|
501
|
-
|
502
|
-
this.updateShowAllButton(areAllSectionsOpen);
|
604
|
+
this.updateShowAllButton(this.areAllSectionsOpen());
|
503
605
|
}
|
504
606
|
initControls() {
|
505
607
|
this.$showAllButton = document.createElement('button');
|
@@ -512,7 +614,7 @@
|
|
512
614
|
const $accordionControls = document.createElement('div');
|
513
615
|
$accordionControls.setAttribute('class', this.controlsClass);
|
514
616
|
$accordionControls.appendChild(this.$showAllButton);
|
515
|
-
this.$
|
617
|
+
this.$root.insertBefore($accordionControls, this.$root.firstChild);
|
516
618
|
this.$showAllText = document.createElement('span');
|
517
619
|
this.$showAllText.classList.add(this.showAllTextClass);
|
518
620
|
this.$showAllButton.appendChild(this.$showAllText);
|
@@ -526,7 +628,7 @@
|
|
526
628
|
const $header = $section.querySelector(`.${this.sectionHeaderClass}`);
|
527
629
|
if (!$header) {
|
528
630
|
throw new ElementError({
|
529
|
-
|
631
|
+
component: Accordion,
|
530
632
|
identifier: `Section headers (\`<div class="${this.sectionHeaderClass}">\`)`
|
531
633
|
});
|
532
634
|
}
|
@@ -542,22 +644,22 @@
|
|
542
644
|
const $summary = $header.querySelector(`.${this.sectionSummaryClass}`);
|
543
645
|
if (!$heading) {
|
544
646
|
throw new ElementError({
|
545
|
-
|
647
|
+
component: Accordion,
|
546
648
|
identifier: `Section heading (\`.${this.sectionHeadingClass}\`)`
|
547
649
|
});
|
548
650
|
}
|
549
651
|
if (!$span) {
|
550
652
|
throw new ElementError({
|
551
|
-
|
653
|
+
component: Accordion,
|
552
654
|
identifier: `Section button placeholder (\`<span class="${this.sectionButtonClass}">\`)`
|
553
655
|
});
|
554
656
|
}
|
555
657
|
const $button = document.createElement('button');
|
556
658
|
$button.setAttribute('type', 'button');
|
557
|
-
$button.setAttribute('aria-controls', `${this.$
|
659
|
+
$button.setAttribute('aria-controls', `${this.$root.id}-content-${index + 1}`);
|
558
660
|
for (const attr of Array.from($span.attributes)) {
|
559
|
-
if (attr.
|
560
|
-
$button.setAttribute(attr.
|
661
|
+
if (attr.name !== 'id') {
|
662
|
+
$button.setAttribute(attr.name, attr.value);
|
561
663
|
}
|
562
664
|
}
|
563
665
|
const $headingText = document.createElement('span');
|
@@ -566,7 +668,7 @@
|
|
566
668
|
const $headingTextFocus = document.createElement('span');
|
567
669
|
$headingTextFocus.classList.add(this.sectionHeadingTextFocusClass);
|
568
670
|
$headingText.appendChild($headingTextFocus);
|
569
|
-
$
|
671
|
+
Array.from($span.childNodes).forEach($child => $headingTextFocus.appendChild($child));
|
570
672
|
const $showHideToggle = document.createElement('span');
|
571
673
|
$showHideToggle.classList.add(this.sectionShowHideToggleClass);
|
572
674
|
$showHideToggle.setAttribute('data-nosnippet', '');
|
@@ -581,16 +683,16 @@
|
|
581
683
|
$showHideToggleFocus.appendChild($showHideText);
|
582
684
|
$button.appendChild($headingText);
|
583
685
|
$button.appendChild(this.getButtonPunctuationEl());
|
584
|
-
if ($summary
|
686
|
+
if ($summary) {
|
585
687
|
const $summarySpan = document.createElement('span');
|
586
688
|
const $summarySpanFocus = document.createElement('span');
|
587
689
|
$summarySpanFocus.classList.add(this.sectionSummaryFocusClass);
|
588
690
|
$summarySpan.appendChild($summarySpanFocus);
|
589
691
|
for (const attr of Array.from($summary.attributes)) {
|
590
|
-
$summarySpan.setAttribute(attr.
|
692
|
+
$summarySpan.setAttribute(attr.name, attr.value);
|
591
693
|
}
|
592
|
-
$
|
593
|
-
$summary.
|
694
|
+
Array.from($summary.childNodes).forEach($child => $summarySpanFocus.appendChild($child));
|
695
|
+
$summary.remove();
|
594
696
|
$button.appendChild($summarySpan);
|
595
697
|
$button.appendChild(this.getButtonPunctuationEl());
|
596
698
|
}
|
@@ -609,15 +711,15 @@
|
|
609
711
|
}
|
610
712
|
}
|
611
713
|
onSectionToggle($section) {
|
612
|
-
const
|
613
|
-
this.setExpanded(
|
614
|
-
this.storeState($section);
|
714
|
+
const nowExpanded = !this.isExpanded($section);
|
715
|
+
this.setExpanded(nowExpanded, $section);
|
716
|
+
this.storeState($section, nowExpanded);
|
615
717
|
}
|
616
718
|
onShowOrHideAllToggle() {
|
617
|
-
const nowExpanded = !this.
|
719
|
+
const nowExpanded = !this.areAllSectionsOpen();
|
618
720
|
this.$sections.forEach($section => {
|
619
721
|
this.setExpanded(nowExpanded, $section);
|
620
|
-
this.storeState($section);
|
722
|
+
this.storeState($section, nowExpanded);
|
621
723
|
});
|
622
724
|
this.updateShowAllButton(nowExpanded);
|
623
725
|
}
|
@@ -628,7 +730,7 @@
|
|
628
730
|
const $content = $section.querySelector(`.${this.sectionContentClass}`);
|
629
731
|
if (!$content) {
|
630
732
|
throw new ElementError({
|
631
|
-
|
733
|
+
component: Accordion,
|
632
734
|
identifier: `Section content (\`<div class="${this.sectionContentClass}">\`)`
|
633
735
|
});
|
634
736
|
}
|
@@ -659,17 +761,13 @@
|
|
659
761
|
$section.classList.remove(this.sectionExpandedClass);
|
660
762
|
$showHideIcon.classList.add(this.downChevronIconClass);
|
661
763
|
}
|
662
|
-
|
663
|
-
this.updateShowAllButton(areAllSectionsOpen);
|
764
|
+
this.updateShowAllButton(this.areAllSectionsOpen());
|
664
765
|
}
|
665
766
|
isExpanded($section) {
|
666
767
|
return $section.classList.contains(this.sectionExpandedClass);
|
667
768
|
}
|
668
|
-
|
669
|
-
|
670
|
-
const expandedSectionCount = this.$module.querySelectorAll(`.${this.sectionExpandedClass}`).length;
|
671
|
-
const areAllSectionsOpen = sectionsCount === expandedSectionCount;
|
672
|
-
return areAllSectionsOpen;
|
769
|
+
areAllSectionsOpen() {
|
770
|
+
return Array.from(this.$sections).every($section => this.isExpanded($section));
|
673
771
|
}
|
674
772
|
updateShowAllButton(expanded) {
|
675
773
|
if (!this.$showAllButton || !this.$showAllText || !this.$showAllIcon) {
|
@@ -679,78 +777,53 @@
|
|
679
777
|
this.$showAllText.textContent = expanded ? this.i18n.t('hideAllSections') : this.i18n.t('showAllSections');
|
680
778
|
this.$showAllIcon.classList.toggle(this.downChevronIconClass, !expanded);
|
681
779
|
}
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
780
|
+
|
781
|
+
/**
|
782
|
+
* Get the identifier for a section
|
783
|
+
*
|
784
|
+
* We need a unique way of identifying each content in the Accordion.
|
785
|
+
* Since an `#id` should be unique and an `id` is required for `aria-`
|
786
|
+
* attributes `id` can be safely used.
|
787
|
+
*
|
788
|
+
* @param {Element} $section - Section element
|
789
|
+
* @returns {string | undefined | null} Identifier for section
|
790
|
+
*/
|
791
|
+
getIdentifier($section) {
|
792
|
+
const $button = $section.querySelector(`.${this.sectionButtonClass}`);
|
793
|
+
return $button == null ? void 0 : $button.getAttribute('aria-controls');
|
693
794
|
}
|
694
|
-
|
695
|
-
if (this.
|
696
|
-
|
697
|
-
if ($button) {
|
698
|
-
const contentId = $button.getAttribute('aria-controls');
|
699
|
-
const contentState = contentId ? window.sessionStorage.getItem(contentId) : null;
|
700
|
-
if (contentState !== null) {
|
701
|
-
this.setExpanded(contentState === 'true', $section);
|
702
|
-
}
|
703
|
-
}
|
795
|
+
storeState($section, isExpanded) {
|
796
|
+
if (!this.config.rememberExpanded) {
|
797
|
+
return;
|
704
798
|
}
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
return $punctuationEl;
|
711
|
-
}
|
712
|
-
}
|
713
|
-
Accordion.moduleName = 'govuk-accordion';
|
714
|
-
Accordion.defaults = Object.freeze({
|
715
|
-
i18n: {
|
716
|
-
hideAllSections: 'Hide all sections',
|
717
|
-
hideSection: 'Hide',
|
718
|
-
hideSectionAriaLabel: 'Hide this section',
|
719
|
-
showAllSections: 'Show all sections',
|
720
|
-
showSection: 'Show',
|
721
|
-
showSectionAriaLabel: 'Show this section'
|
722
|
-
},
|
723
|
-
rememberExpanded: true
|
724
|
-
});
|
725
|
-
Accordion.schema = Object.freeze({
|
726
|
-
properties: {
|
727
|
-
i18n: {
|
728
|
-
type: 'object'
|
729
|
-
},
|
730
|
-
rememberExpanded: {
|
731
|
-
type: 'boolean'
|
799
|
+
const id = this.getIdentifier($section);
|
800
|
+
if (id) {
|
801
|
+
try {
|
802
|
+
window.sessionStorage.setItem(id, isExpanded.toString());
|
803
|
+
} catch (exception) {}
|
732
804
|
}
|
733
805
|
}
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
result = window.sessionStorage.getItem(testString) === testString.toString();
|
747
|
-
window.sessionStorage.removeItem(testString);
|
748
|
-
return result;
|
749
|
-
} catch (exception) {
|
750
|
-
return false;
|
806
|
+
setInitialState($section) {
|
807
|
+
if (!this.config.rememberExpanded) {
|
808
|
+
return;
|
809
|
+
}
|
810
|
+
const id = this.getIdentifier($section);
|
811
|
+
if (id) {
|
812
|
+
try {
|
813
|
+
const state = window.sessionStorage.getItem(id);
|
814
|
+
if (state !== null) {
|
815
|
+
this.setExpanded(state === 'true', $section);
|
816
|
+
}
|
817
|
+
} catch (exception) {}
|
751
818
|
}
|
752
819
|
}
|
753
|
-
|
820
|
+
getButtonPunctuationEl() {
|
821
|
+
const $punctuationEl = document.createElement('span');
|
822
|
+
$punctuationEl.classList.add('govuk-visually-hidden', this.sectionHeadingDividerClass);
|
823
|
+
$punctuationEl.textContent = ', ';
|
824
|
+
return $punctuationEl;
|
825
|
+
}
|
826
|
+
}
|
754
827
|
|
755
828
|
/**
|
756
829
|
* Accordion config
|
@@ -786,8 +859,30 @@
|
|
786
859
|
*/
|
787
860
|
|
788
861
|
/**
|
789
|
-
* @
|
862
|
+
* @import { Schema } from '../../common/configuration.mjs'
|
790
863
|
*/
|
864
|
+
Accordion.moduleName = 'govuk-accordion';
|
865
|
+
Accordion.defaults = Object.freeze({
|
866
|
+
i18n: {
|
867
|
+
hideAllSections: 'Hide all sections',
|
868
|
+
hideSection: 'Hide',
|
869
|
+
hideSectionAriaLabel: 'Hide this section',
|
870
|
+
showAllSections: 'Show all sections',
|
871
|
+
showSection: 'Show',
|
872
|
+
showSectionAriaLabel: 'Show this section'
|
873
|
+
},
|
874
|
+
rememberExpanded: true
|
875
|
+
});
|
876
|
+
Accordion.schema = Object.freeze({
|
877
|
+
properties: {
|
878
|
+
i18n: {
|
879
|
+
type: 'object'
|
880
|
+
},
|
881
|
+
rememberExpanded: {
|
882
|
+
type: 'boolean'
|
883
|
+
}
|
884
|
+
}
|
885
|
+
});
|
791
886
|
|
792
887
|
const DEBOUNCE_TIMEOUT_IN_SECONDS = 1;
|
793
888
|
|
@@ -795,28 +890,18 @@
|
|
795
890
|
* JavaScript enhancements for the Button component
|
796
891
|
*
|
797
892
|
* @preserve
|
893
|
+
* @augments ConfigurableComponent<ButtonConfig>
|
798
894
|
*/
|
799
|
-
class Button extends
|
895
|
+
class Button extends ConfigurableComponent {
|
800
896
|
/**
|
801
|
-
* @param {Element | null} $
|
897
|
+
* @param {Element | null} $root - HTML element to use for button
|
802
898
|
* @param {ButtonConfig} [config] - Button config
|
803
899
|
*/
|
804
|
-
constructor($
|
805
|
-
super();
|
806
|
-
this.$module = void 0;
|
807
|
-
this.config = void 0;
|
900
|
+
constructor($root, config = {}) {
|
901
|
+
super($root, config);
|
808
902
|
this.debounceFormSubmitTimer = null;
|
809
|
-
|
810
|
-
|
811
|
-
componentName: 'Button',
|
812
|
-
element: $module,
|
813
|
-
identifier: 'Root element (`$module`)'
|
814
|
-
});
|
815
|
-
}
|
816
|
-
this.$module = $module;
|
817
|
-
this.config = mergeConfigs(Button.defaults, config, normaliseDataset(Button, $module.dataset));
|
818
|
-
this.$module.addEventListener('keydown', event => this.handleKeyDown(event));
|
819
|
-
this.$module.addEventListener('click', event => this.debounce(event));
|
903
|
+
this.$root.addEventListener('keydown', event => this.handleKeyDown(event));
|
904
|
+
this.$root.addEventListener('click', event => this.debounce(event));
|
820
905
|
}
|
821
906
|
handleKeyDown(event) {
|
822
907
|
const $target = event.target;
|
@@ -851,7 +936,7 @@
|
|
851
936
|
*/
|
852
937
|
|
853
938
|
/**
|
854
|
-
* @
|
939
|
+
* @import { Schema } from '../../common/configuration.mjs'
|
855
940
|
*/
|
856
941
|
Button.moduleName = 'govuk-button';
|
857
942
|
Button.defaults = Object.freeze({
|
@@ -881,69 +966,63 @@
|
|
881
966
|
* of the available characters/words has been entered.
|
882
967
|
*
|
883
968
|
* @preserve
|
969
|
+
* @augments ConfigurableComponent<CharacterCountConfig>
|
884
970
|
*/
|
885
|
-
class CharacterCount extends
|
971
|
+
class CharacterCount extends ConfigurableComponent {
|
972
|
+
[configOverride](datasetConfig) {
|
973
|
+
let configOverrides = {};
|
974
|
+
if ('maxwords' in datasetConfig || 'maxlength' in datasetConfig) {
|
975
|
+
configOverrides = {
|
976
|
+
maxlength: undefined,
|
977
|
+
maxwords: undefined
|
978
|
+
};
|
979
|
+
}
|
980
|
+
return configOverrides;
|
981
|
+
}
|
982
|
+
|
886
983
|
/**
|
887
|
-
* @param {Element | null} $
|
984
|
+
* @param {Element | null} $root - HTML element to use for character count
|
888
985
|
* @param {CharacterCountConfig} [config] - Character count config
|
889
986
|
*/
|
890
|
-
constructor($
|
987
|
+
constructor($root, config = {}) {
|
891
988
|
var _ref, _this$config$maxwords;
|
892
|
-
super();
|
893
|
-
this.$module = void 0;
|
989
|
+
super($root, config);
|
894
990
|
this.$textarea = void 0;
|
895
991
|
this.$visibleCountMessage = void 0;
|
896
992
|
this.$screenReaderCountMessage = void 0;
|
897
993
|
this.lastInputTimestamp = null;
|
898
994
|
this.lastInputValue = '';
|
899
995
|
this.valueChecker = null;
|
900
|
-
this.config = void 0;
|
901
996
|
this.i18n = void 0;
|
902
997
|
this.maxLength = void 0;
|
903
|
-
|
904
|
-
throw new ElementError({
|
905
|
-
componentName: 'Character count',
|
906
|
-
element: $module,
|
907
|
-
identifier: 'Root element (`$module`)'
|
908
|
-
});
|
909
|
-
}
|
910
|
-
const $textarea = $module.querySelector('.govuk-js-character-count');
|
998
|
+
const $textarea = this.$root.querySelector('.govuk-js-character-count');
|
911
999
|
if (!($textarea instanceof HTMLTextAreaElement || $textarea instanceof HTMLInputElement)) {
|
912
1000
|
throw new ElementError({
|
913
|
-
|
1001
|
+
component: CharacterCount,
|
914
1002
|
element: $textarea,
|
915
1003
|
expectedType: 'HTMLTextareaElement or HTMLInputElement',
|
916
1004
|
identifier: 'Form field (`.govuk-js-character-count`)'
|
917
1005
|
});
|
918
1006
|
}
|
919
|
-
const datasetConfig = normaliseDataset(CharacterCount, $module.dataset);
|
920
|
-
let configOverrides = {};
|
921
|
-
if ('maxwords' in datasetConfig || 'maxlength' in datasetConfig) {
|
922
|
-
configOverrides = {
|
923
|
-
maxlength: undefined,
|
924
|
-
maxwords: undefined
|
925
|
-
};
|
926
|
-
}
|
927
|
-
this.config = mergeConfigs(CharacterCount.defaults, config, configOverrides, datasetConfig);
|
928
1007
|
const errors = validateConfig(CharacterCount.schema, this.config);
|
929
1008
|
if (errors[0]) {
|
930
|
-
throw new ConfigError(
|
1009
|
+
throw new ConfigError(formatErrorMessage(CharacterCount, errors[0]));
|
931
1010
|
}
|
932
1011
|
this.i18n = new I18n(this.config.i18n, {
|
933
|
-
locale: closestAttributeValue(
|
1012
|
+
locale: closestAttributeValue(this.$root, 'lang')
|
934
1013
|
});
|
935
1014
|
this.maxLength = (_ref = (_this$config$maxwords = this.config.maxwords) != null ? _this$config$maxwords : this.config.maxlength) != null ? _ref : Infinity;
|
936
|
-
this.$module = $module;
|
937
1015
|
this.$textarea = $textarea;
|
938
1016
|
const textareaDescriptionId = `${this.$textarea.id}-info`;
|
939
1017
|
const $textareaDescription = document.getElementById(textareaDescriptionId);
|
940
1018
|
if (!$textareaDescription) {
|
941
1019
|
throw new ElementError({
|
942
|
-
|
1020
|
+
component: CharacterCount,
|
943
1021
|
element: $textareaDescription,
|
944
1022
|
identifier: `Count message (\`id="${textareaDescriptionId}"\`)`
|
945
1023
|
});
|
946
1024
|
}
|
1025
|
+
this.$errorMessage = this.$root.querySelector('.govuk-error-message');
|
947
1026
|
if (`${$textareaDescription.textContent}`.match(/^\s*$/)) {
|
948
1027
|
$textareaDescription.textContent = this.i18n.t('textareaDescription', {
|
949
1028
|
count: this.maxLength
|
@@ -1002,7 +1081,9 @@
|
|
1002
1081
|
const remainingNumber = this.maxLength - this.count(this.$textarea.value);
|
1003
1082
|
const isError = remainingNumber < 0;
|
1004
1083
|
this.$visibleCountMessage.classList.toggle('govuk-character-count__message--disabled', !this.isOverThreshold());
|
1005
|
-
this.$
|
1084
|
+
if (!this.$errorMessage) {
|
1085
|
+
this.$textarea.classList.toggle('govuk-textarea--error', isError);
|
1086
|
+
}
|
1006
1087
|
this.$visibleCountMessage.classList.toggle('govuk-error-message', isError);
|
1007
1088
|
this.$visibleCountMessage.classList.toggle('govuk-hint', !isError);
|
1008
1089
|
this.$visibleCountMessage.textContent = this.getCountMessage();
|
@@ -1111,8 +1192,8 @@
|
|
1111
1192
|
*/
|
1112
1193
|
|
1113
1194
|
/**
|
1114
|
-
* @
|
1115
|
-
* @
|
1195
|
+
* @import { Schema } from '../../common/configuration.mjs'
|
1196
|
+
* @import { TranslationPluralForms } from '../../i18n.mjs'
|
1116
1197
|
*/
|
1117
1198
|
CharacterCount.moduleName = 'govuk-character-count';
|
1118
1199
|
CharacterCount.defaults = Object.freeze({
|
@@ -1170,7 +1251,7 @@
|
|
1170
1251
|
*
|
1171
1252
|
* @preserve
|
1172
1253
|
*/
|
1173
|
-
class Checkboxes extends
|
1254
|
+
class Checkboxes extends Component {
|
1174
1255
|
/**
|
1175
1256
|
* Checkboxes can be associated with a 'conditionally revealed' content block
|
1176
1257
|
* – for example, a checkbox for 'Phone' could reveal an additional form field
|
@@ -1183,27 +1264,18 @@
|
|
1183
1264
|
* (for example if the user has navigated back), and set up event handlers to
|
1184
1265
|
* keep the reveal in sync with the checkbox state.
|
1185
1266
|
*
|
1186
|
-
* @param {Element | null} $
|
1267
|
+
* @param {Element | null} $root - HTML element to use for checkboxes
|
1187
1268
|
*/
|
1188
|
-
constructor($
|
1189
|
-
super();
|
1190
|
-
this.$module = void 0;
|
1269
|
+
constructor($root) {
|
1270
|
+
super($root);
|
1191
1271
|
this.$inputs = void 0;
|
1192
|
-
|
1193
|
-
throw new ElementError({
|
1194
|
-
componentName: 'Checkboxes',
|
1195
|
-
element: $module,
|
1196
|
-
identifier: 'Root element (`$module`)'
|
1197
|
-
});
|
1198
|
-
}
|
1199
|
-
const $inputs = $module.querySelectorAll('input[type="checkbox"]');
|
1272
|
+
const $inputs = this.$root.querySelectorAll('input[type="checkbox"]');
|
1200
1273
|
if (!$inputs.length) {
|
1201
1274
|
throw new ElementError({
|
1202
|
-
|
1275
|
+
component: Checkboxes,
|
1203
1276
|
identifier: 'Form inputs (`<input type="checkbox">`)'
|
1204
1277
|
});
|
1205
1278
|
}
|
1206
|
-
this.$module = $module;
|
1207
1279
|
this.$inputs = $inputs;
|
1208
1280
|
this.$inputs.forEach($input => {
|
1209
1281
|
const targetId = $input.getAttribute('data-aria-controls');
|
@@ -1212,7 +1284,7 @@
|
|
1212
1284
|
}
|
1213
1285
|
if (!document.getElementById(targetId)) {
|
1214
1286
|
throw new ElementError({
|
1215
|
-
|
1287
|
+
component: Checkboxes,
|
1216
1288
|
identifier: `Conditional reveal (\`id="${targetId}"\`)`
|
1217
1289
|
});
|
1218
1290
|
}
|
@@ -1221,7 +1293,7 @@
|
|
1221
1293
|
});
|
1222
1294
|
window.addEventListener('pageshow', () => this.syncAllConditionalReveals());
|
1223
1295
|
this.syncAllConditionalReveals();
|
1224
|
-
this.$
|
1296
|
+
this.$root.addEventListener('click', event => this.handleClick(event));
|
1225
1297
|
}
|
1226
1298
|
syncAllConditionalReveals() {
|
1227
1299
|
this.$inputs.forEach($input => this.syncConditionalRevealWithInputState($input));
|
@@ -1287,29 +1359,19 @@
|
|
1287
1359
|
* configuration.
|
1288
1360
|
*
|
1289
1361
|
* @preserve
|
1362
|
+
* @augments ConfigurableComponent<ErrorSummaryConfig>
|
1290
1363
|
*/
|
1291
|
-
class ErrorSummary extends
|
1364
|
+
class ErrorSummary extends ConfigurableComponent {
|
1292
1365
|
/**
|
1293
|
-
* @param {Element | null} $
|
1366
|
+
* @param {Element | null} $root - HTML element to use for error summary
|
1294
1367
|
* @param {ErrorSummaryConfig} [config] - Error summary config
|
1295
1368
|
*/
|
1296
|
-
constructor($
|
1297
|
-
super();
|
1298
|
-
this.$module = void 0;
|
1299
|
-
this.config = void 0;
|
1300
|
-
if (!($module instanceof HTMLElement)) {
|
1301
|
-
throw new ElementError({
|
1302
|
-
componentName: 'Error summary',
|
1303
|
-
element: $module,
|
1304
|
-
identifier: 'Root element (`$module`)'
|
1305
|
-
});
|
1306
|
-
}
|
1307
|
-
this.$module = $module;
|
1308
|
-
this.config = mergeConfigs(ErrorSummary.defaults, config, normaliseDataset(ErrorSummary, $module.dataset));
|
1369
|
+
constructor($root, config = {}) {
|
1370
|
+
super($root, config);
|
1309
1371
|
if (!this.config.disableAutoFocus) {
|
1310
|
-
setFocus(this.$
|
1372
|
+
setFocus(this.$root);
|
1311
1373
|
}
|
1312
|
-
this.$
|
1374
|
+
this.$root.addEventListener('click', event => this.handleClick(event));
|
1313
1375
|
}
|
1314
1376
|
handleClick(event) {
|
1315
1377
|
const $target = event.target;
|
@@ -1372,7 +1434,7 @@
|
|
1372
1434
|
*/
|
1373
1435
|
|
1374
1436
|
/**
|
1375
|
-
* @
|
1437
|
+
* @import { Schema } from '../../common/configuration.mjs'
|
1376
1438
|
*/
|
1377
1439
|
ErrorSummary.moduleName = 'govuk-error-summary';
|
1378
1440
|
ErrorSummary.defaults = Object.freeze({
|
@@ -1390,16 +1452,15 @@
|
|
1390
1452
|
* Exit this page component
|
1391
1453
|
*
|
1392
1454
|
* @preserve
|
1455
|
+
* @augments ConfigurableComponent<ExitThisPageConfig>
|
1393
1456
|
*/
|
1394
|
-
class ExitThisPage extends
|
1457
|
+
class ExitThisPage extends ConfigurableComponent {
|
1395
1458
|
/**
|
1396
|
-
* @param {Element | null} $
|
1459
|
+
* @param {Element | null} $root - HTML element that wraps the Exit This Page button
|
1397
1460
|
* @param {ExitThisPageConfig} [config] - Exit This Page config
|
1398
1461
|
*/
|
1399
|
-
constructor($
|
1400
|
-
super();
|
1401
|
-
this.$module = void 0;
|
1402
|
-
this.config = void 0;
|
1462
|
+
constructor($root, config = {}) {
|
1463
|
+
super($root, config);
|
1403
1464
|
this.i18n = void 0;
|
1404
1465
|
this.$button = void 0;
|
1405
1466
|
this.$skiplinkButton = null;
|
@@ -1411,25 +1472,16 @@
|
|
1411
1472
|
this.timeoutTime = 5000;
|
1412
1473
|
this.keypressTimeoutId = null;
|
1413
1474
|
this.timeoutMessageId = null;
|
1414
|
-
|
1415
|
-
throw new ElementError({
|
1416
|
-
componentName: 'Exit this page',
|
1417
|
-
element: $module,
|
1418
|
-
identifier: 'Root element (`$module`)'
|
1419
|
-
});
|
1420
|
-
}
|
1421
|
-
const $button = $module.querySelector('.govuk-exit-this-page__button');
|
1475
|
+
const $button = this.$root.querySelector('.govuk-exit-this-page__button');
|
1422
1476
|
if (!($button instanceof HTMLAnchorElement)) {
|
1423
1477
|
throw new ElementError({
|
1424
|
-
|
1478
|
+
component: ExitThisPage,
|
1425
1479
|
element: $button,
|
1426
1480
|
expectedType: 'HTMLAnchorElement',
|
1427
1481
|
identifier: 'Button (`.govuk-exit-this-page__button`)'
|
1428
1482
|
});
|
1429
1483
|
}
|
1430
|
-
this.config = mergeConfigs(ExitThisPage.defaults, config, normaliseDataset(ExitThisPage, $module.dataset));
|
1431
1484
|
this.i18n = new I18n(this.config.i18n);
|
1432
|
-
this.$module = $module;
|
1433
1485
|
this.$button = $button;
|
1434
1486
|
const $skiplinkButton = document.querySelector('.govuk-js-exit-this-page-skiplink');
|
1435
1487
|
if ($skiplinkButton instanceof HTMLAnchorElement) {
|
@@ -1448,7 +1500,7 @@
|
|
1448
1500
|
this.$updateSpan = document.createElement('span');
|
1449
1501
|
this.$updateSpan.setAttribute('role', 'status');
|
1450
1502
|
this.$updateSpan.className = 'govuk-visually-hidden';
|
1451
|
-
this.$
|
1503
|
+
this.$root.appendChild(this.$updateSpan);
|
1452
1504
|
}
|
1453
1505
|
initButtonClickHandler() {
|
1454
1506
|
this.$button.addEventListener('click', this.handleClick.bind(this));
|
@@ -1594,7 +1646,7 @@
|
|
1594
1646
|
*/
|
1595
1647
|
|
1596
1648
|
/**
|
1597
|
-
* @
|
1649
|
+
* @import { Schema } from '../../common/configuration.mjs'
|
1598
1650
|
*/
|
1599
1651
|
ExitThisPage.moduleName = 'govuk-exit-this-page';
|
1600
1652
|
ExitThisPage.defaults = Object.freeze({
|
@@ -1613,48 +1665,289 @@
|
|
1613
1665
|
}
|
1614
1666
|
});
|
1615
1667
|
|
1668
|
+
/**
|
1669
|
+
* File upload component
|
1670
|
+
*
|
1671
|
+
* @preserve
|
1672
|
+
* @augments ConfigurableComponent<FileUploadConfig>
|
1673
|
+
*/
|
1674
|
+
class FileUpload extends ConfigurableComponent {
|
1675
|
+
/**
|
1676
|
+
* @param {Element | null} $root - File input element
|
1677
|
+
* @param {FileUploadConfig} [config] - File Upload config
|
1678
|
+
*/
|
1679
|
+
constructor($root, config = {}) {
|
1680
|
+
super($root, config);
|
1681
|
+
this.$input = void 0;
|
1682
|
+
this.$button = void 0;
|
1683
|
+
this.$status = void 0;
|
1684
|
+
this.i18n = void 0;
|
1685
|
+
this.id = void 0;
|
1686
|
+
this.$announcements = void 0;
|
1687
|
+
this.enteredAnotherElement = void 0;
|
1688
|
+
const $input = this.$root.querySelector('input');
|
1689
|
+
if ($input === null) {
|
1690
|
+
throw new ElementError({
|
1691
|
+
component: FileUpload,
|
1692
|
+
identifier: 'File inputs (`<input type="file">`)'
|
1693
|
+
});
|
1694
|
+
}
|
1695
|
+
if ($input.type !== 'file') {
|
1696
|
+
throw new ElementError(formatErrorMessage(FileUpload, 'File input (`<input type="file">`) attribute (`type`) is not `file`'));
|
1697
|
+
}
|
1698
|
+
this.$input = $input;
|
1699
|
+
this.$input.setAttribute('hidden', 'true');
|
1700
|
+
if (!this.$input.id) {
|
1701
|
+
throw new ElementError({
|
1702
|
+
component: FileUpload,
|
1703
|
+
identifier: 'File input (`<input type="file">`) attribute (`id`)'
|
1704
|
+
});
|
1705
|
+
}
|
1706
|
+
this.id = this.$input.id;
|
1707
|
+
this.i18n = new I18n(this.config.i18n, {
|
1708
|
+
locale: closestAttributeValue(this.$root, 'lang')
|
1709
|
+
});
|
1710
|
+
const $label = this.findLabel();
|
1711
|
+
if (!$label.id) {
|
1712
|
+
$label.id = `${this.id}-label`;
|
1713
|
+
}
|
1714
|
+
this.$input.id = `${this.id}-input`;
|
1715
|
+
const $button = document.createElement('button');
|
1716
|
+
$button.classList.add('govuk-file-upload-button');
|
1717
|
+
$button.type = 'button';
|
1718
|
+
$button.id = this.id;
|
1719
|
+
$button.classList.add('govuk-file-upload-button--empty');
|
1720
|
+
const ariaDescribedBy = this.$input.getAttribute('aria-describedby');
|
1721
|
+
if (ariaDescribedBy) {
|
1722
|
+
$button.setAttribute('aria-describedby', ariaDescribedBy);
|
1723
|
+
}
|
1724
|
+
const $status = document.createElement('span');
|
1725
|
+
$status.className = 'govuk-body govuk-file-upload-button__status';
|
1726
|
+
$status.setAttribute('aria-live', 'polite');
|
1727
|
+
$status.innerText = this.i18n.t('noFileChosen');
|
1728
|
+
$button.appendChild($status);
|
1729
|
+
const commaSpan = document.createElement('span');
|
1730
|
+
commaSpan.className = 'govuk-visually-hidden';
|
1731
|
+
commaSpan.innerText = ', ';
|
1732
|
+
commaSpan.id = `${this.id}-comma`;
|
1733
|
+
$button.appendChild(commaSpan);
|
1734
|
+
const containerSpan = document.createElement('span');
|
1735
|
+
containerSpan.className = 'govuk-file-upload-button__pseudo-button-container';
|
1736
|
+
const buttonSpan = document.createElement('span');
|
1737
|
+
buttonSpan.className = 'govuk-button govuk-button--secondary govuk-file-upload-button__pseudo-button';
|
1738
|
+
buttonSpan.innerText = this.i18n.t('chooseFilesButton');
|
1739
|
+
containerSpan.appendChild(buttonSpan);
|
1740
|
+
containerSpan.insertAdjacentText('beforeend', ' ');
|
1741
|
+
const instructionSpan = document.createElement('span');
|
1742
|
+
instructionSpan.className = 'govuk-body govuk-file-upload-button__instruction';
|
1743
|
+
instructionSpan.innerText = this.i18n.t('dropInstruction');
|
1744
|
+
containerSpan.appendChild(instructionSpan);
|
1745
|
+
$button.appendChild(containerSpan);
|
1746
|
+
$button.setAttribute('aria-labelledby', `${$label.id} ${commaSpan.id} ${$button.id}`);
|
1747
|
+
$button.addEventListener('click', this.onClick.bind(this));
|
1748
|
+
$button.addEventListener('dragover', event => {
|
1749
|
+
event.preventDefault();
|
1750
|
+
});
|
1751
|
+
this.$root.insertAdjacentElement('afterbegin', $button);
|
1752
|
+
this.$input.setAttribute('tabindex', '-1');
|
1753
|
+
this.$input.setAttribute('aria-hidden', 'true');
|
1754
|
+
this.$button = $button;
|
1755
|
+
this.$status = $status;
|
1756
|
+
this.$input.addEventListener('change', this.onChange.bind(this));
|
1757
|
+
this.updateDisabledState();
|
1758
|
+
this.observeDisabledState();
|
1759
|
+
this.$announcements = document.createElement('span');
|
1760
|
+
this.$announcements.classList.add('govuk-file-upload-announcements');
|
1761
|
+
this.$announcements.classList.add('govuk-visually-hidden');
|
1762
|
+
this.$announcements.setAttribute('aria-live', 'assertive');
|
1763
|
+
this.$root.insertAdjacentElement('afterend', this.$announcements);
|
1764
|
+
this.$button.addEventListener('drop', this.onDrop.bind(this));
|
1765
|
+
document.addEventListener('dragenter', this.updateDropzoneVisibility.bind(this));
|
1766
|
+
document.addEventListener('dragenter', () => {
|
1767
|
+
this.enteredAnotherElement = true;
|
1768
|
+
});
|
1769
|
+
document.addEventListener('dragleave', () => {
|
1770
|
+
if (!this.enteredAnotherElement && !this.$button.disabled) {
|
1771
|
+
this.hideDraggingState();
|
1772
|
+
this.$announcements.innerText = this.i18n.t('leftDropZone');
|
1773
|
+
}
|
1774
|
+
this.enteredAnotherElement = false;
|
1775
|
+
});
|
1776
|
+
}
|
1777
|
+
updateDropzoneVisibility(event) {
|
1778
|
+
if (this.$button.disabled) return;
|
1779
|
+
if (event.target instanceof Node) {
|
1780
|
+
if (this.$root.contains(event.target)) {
|
1781
|
+
if (event.dataTransfer && isContainingFiles(event.dataTransfer)) {
|
1782
|
+
if (!this.$button.classList.contains('govuk-file-upload-button--dragging')) {
|
1783
|
+
this.showDraggingState();
|
1784
|
+
this.$announcements.innerText = this.i18n.t('enteredDropZone');
|
1785
|
+
}
|
1786
|
+
}
|
1787
|
+
} else {
|
1788
|
+
if (this.$button.classList.contains('govuk-file-upload-button--dragging')) {
|
1789
|
+
this.hideDraggingState();
|
1790
|
+
this.$announcements.innerText = this.i18n.t('leftDropZone');
|
1791
|
+
}
|
1792
|
+
}
|
1793
|
+
}
|
1794
|
+
}
|
1795
|
+
showDraggingState() {
|
1796
|
+
this.$button.classList.add('govuk-file-upload-button--dragging');
|
1797
|
+
}
|
1798
|
+
hideDraggingState() {
|
1799
|
+
this.$button.classList.remove('govuk-file-upload-button--dragging');
|
1800
|
+
}
|
1801
|
+
onDrop(event) {
|
1802
|
+
event.preventDefault();
|
1803
|
+
if (event.dataTransfer && isContainingFiles(event.dataTransfer)) {
|
1804
|
+
this.$input.files = event.dataTransfer.files;
|
1805
|
+
this.$input.dispatchEvent(new CustomEvent('change'));
|
1806
|
+
this.hideDraggingState();
|
1807
|
+
}
|
1808
|
+
}
|
1809
|
+
onChange() {
|
1810
|
+
const fileCount = this.$input.files.length;
|
1811
|
+
if (fileCount === 0) {
|
1812
|
+
this.$status.innerText = this.i18n.t('noFileChosen');
|
1813
|
+
this.$button.classList.add('govuk-file-upload-button--empty');
|
1814
|
+
} else {
|
1815
|
+
if (fileCount === 1) {
|
1816
|
+
this.$status.innerText = this.$input.files[0].name;
|
1817
|
+
} else {
|
1818
|
+
this.$status.innerText = this.i18n.t('multipleFilesChosen', {
|
1819
|
+
count: fileCount
|
1820
|
+
});
|
1821
|
+
}
|
1822
|
+
this.$button.classList.remove('govuk-file-upload-button--empty');
|
1823
|
+
}
|
1824
|
+
}
|
1825
|
+
findLabel() {
|
1826
|
+
const $label = document.querySelector(`label[for="${this.$input.id}"]`);
|
1827
|
+
if (!$label) {
|
1828
|
+
throw new ElementError({
|
1829
|
+
component: FileUpload,
|
1830
|
+
identifier: `Field label (\`<label for=${this.$input.id}>\`)`
|
1831
|
+
});
|
1832
|
+
}
|
1833
|
+
return $label;
|
1834
|
+
}
|
1835
|
+
onClick() {
|
1836
|
+
this.$input.click();
|
1837
|
+
}
|
1838
|
+
observeDisabledState() {
|
1839
|
+
const observer = new MutationObserver(mutationList => {
|
1840
|
+
for (const mutation of mutationList) {
|
1841
|
+
if (mutation.type === 'attributes' && mutation.attributeName === 'disabled') {
|
1842
|
+
this.updateDisabledState();
|
1843
|
+
}
|
1844
|
+
}
|
1845
|
+
});
|
1846
|
+
observer.observe(this.$input, {
|
1847
|
+
attributes: true
|
1848
|
+
});
|
1849
|
+
}
|
1850
|
+
updateDisabledState() {
|
1851
|
+
this.$button.disabled = this.$input.disabled;
|
1852
|
+
this.$root.classList.toggle('govuk-drop-zone--disabled', this.$button.disabled);
|
1853
|
+
}
|
1854
|
+
}
|
1855
|
+
FileUpload.moduleName = 'govuk-file-upload';
|
1856
|
+
FileUpload.defaults = Object.freeze({
|
1857
|
+
i18n: {
|
1858
|
+
chooseFilesButton: 'Choose file',
|
1859
|
+
dropInstruction: 'or drop file',
|
1860
|
+
noFileChosen: 'No file chosen',
|
1861
|
+
multipleFilesChosen: {
|
1862
|
+
one: '%{count} file chosen',
|
1863
|
+
other: '%{count} files chosen'
|
1864
|
+
},
|
1865
|
+
enteredDropZone: 'Entered drop zone',
|
1866
|
+
leftDropZone: 'Left drop zone'
|
1867
|
+
}
|
1868
|
+
});
|
1869
|
+
FileUpload.schema = Object.freeze({
|
1870
|
+
properties: {
|
1871
|
+
i18n: {
|
1872
|
+
type: 'object'
|
1873
|
+
}
|
1874
|
+
}
|
1875
|
+
});
|
1876
|
+
function isContainingFiles(dataTransfer) {
|
1877
|
+
const hasNoTypesInfo = dataTransfer.types.length === 0;
|
1878
|
+
const isDraggingFiles = dataTransfer.types.some(type => type === 'Files');
|
1879
|
+
return hasNoTypesInfo || isDraggingFiles;
|
1880
|
+
}
|
1881
|
+
|
1882
|
+
/**
|
1883
|
+
* @typedef {HTMLInputElement & {files: FileList}} HTMLFileInputElement
|
1884
|
+
*/
|
1885
|
+
|
1886
|
+
/**
|
1887
|
+
* File upload config
|
1888
|
+
*
|
1889
|
+
* @see {@link FileUpload.defaults}
|
1890
|
+
* @typedef {object} FileUploadConfig
|
1891
|
+
* @property {FileUploadTranslations} [i18n=FileUpload.defaults.i18n] - File upload translations
|
1892
|
+
*/
|
1893
|
+
|
1894
|
+
/**
|
1895
|
+
* File upload translations
|
1896
|
+
*
|
1897
|
+
* @see {@link FileUpload.defaults.i18n}
|
1898
|
+
* @typedef {object} FileUploadTranslations
|
1899
|
+
*
|
1900
|
+
* Messages used by the component
|
1901
|
+
* @property {string} [chooseFile] - The text of the button that opens the file picker
|
1902
|
+
* @property {string} [dropInstruction] - The text informing users they can drop files
|
1903
|
+
* @property {TranslationPluralForms} [multipleFilesChosen] - The text displayed when multiple files
|
1904
|
+
* have been chosen by the user
|
1905
|
+
* @property {string} [noFileChosen] - The text to displayed when no file has been chosen by the user
|
1906
|
+
* @property {string} [enteredDropZone] - The text announced by assistive technology
|
1907
|
+
* when user drags files and enters the drop zone
|
1908
|
+
* @property {string} [leftDropZone] - The text announced by assistive technology
|
1909
|
+
* when user drags files and leaves the drop zone without dropping
|
1910
|
+
*/
|
1911
|
+
|
1912
|
+
/**
|
1913
|
+
* @import { Schema } from '../../common/configuration.mjs'
|
1914
|
+
* @import { TranslationPluralForms } from '../../i18n.mjs'
|
1915
|
+
*/
|
1916
|
+
|
1616
1917
|
/**
|
1617
1918
|
* Header component
|
1618
1919
|
*
|
1619
1920
|
* @preserve
|
1620
1921
|
*/
|
1621
|
-
class Header extends
|
1922
|
+
class Header extends Component {
|
1622
1923
|
/**
|
1623
1924
|
* Apply a matchMedia for desktop which will trigger a state sync if the
|
1624
1925
|
* browser viewport moves between states.
|
1625
1926
|
*
|
1626
|
-
* @param {Element | null} $
|
1927
|
+
* @param {Element | null} $root - HTML element to use for header
|
1627
1928
|
*/
|
1628
|
-
constructor($
|
1629
|
-
super();
|
1630
|
-
this.$module = void 0;
|
1929
|
+
constructor($root) {
|
1930
|
+
super($root);
|
1631
1931
|
this.$menuButton = void 0;
|
1632
1932
|
this.$menu = void 0;
|
1633
1933
|
this.menuIsOpen = false;
|
1634
1934
|
this.mql = null;
|
1635
|
-
|
1636
|
-
throw new ElementError({
|
1637
|
-
componentName: 'Header',
|
1638
|
-
element: $module,
|
1639
|
-
identifier: 'Root element (`$module`)'
|
1640
|
-
});
|
1641
|
-
}
|
1642
|
-
this.$module = $module;
|
1643
|
-
const $menuButton = $module.querySelector('.govuk-js-header-toggle');
|
1935
|
+
const $menuButton = this.$root.querySelector('.govuk-js-header-toggle');
|
1644
1936
|
if (!$menuButton) {
|
1645
1937
|
return this;
|
1646
1938
|
}
|
1939
|
+
this.$root.classList.add('govuk-header--with-js-navigation');
|
1647
1940
|
const menuId = $menuButton.getAttribute('aria-controls');
|
1648
1941
|
if (!menuId) {
|
1649
1942
|
throw new ElementError({
|
1650
|
-
|
1943
|
+
component: Header,
|
1651
1944
|
identifier: 'Navigation button (`<button class="govuk-js-header-toggle">`) attribute (`aria-controls`)'
|
1652
1945
|
});
|
1653
1946
|
}
|
1654
1947
|
const $menu = document.getElementById(menuId);
|
1655
1948
|
if (!$menu) {
|
1656
1949
|
throw new ElementError({
|
1657
|
-
|
1950
|
+
component: Header,
|
1658
1951
|
element: $menu,
|
1659
1952
|
identifier: `Navigation (\`<ul id="${menuId}">\`)`
|
1660
1953
|
});
|
@@ -1668,7 +1961,7 @@
|
|
1668
1961
|
const breakpoint = getBreakpoint('desktop');
|
1669
1962
|
if (!breakpoint.value) {
|
1670
1963
|
throw new ElementError({
|
1671
|
-
|
1964
|
+
component: Header,
|
1672
1965
|
identifier: `CSS custom property (\`${breakpoint.property}\`) on pseudo-class \`:root\``
|
1673
1966
|
});
|
1674
1967
|
}
|
@@ -1708,27 +2001,17 @@
|
|
1708
2001
|
* Notification Banner component
|
1709
2002
|
*
|
1710
2003
|
* @preserve
|
2004
|
+
* @augments ConfigurableComponent<NotificationBannerConfig>
|
1711
2005
|
*/
|
1712
|
-
class NotificationBanner extends
|
2006
|
+
class NotificationBanner extends ConfigurableComponent {
|
1713
2007
|
/**
|
1714
|
-
* @param {Element | null} $
|
2008
|
+
* @param {Element | null} $root - HTML element to use for notification banner
|
1715
2009
|
* @param {NotificationBannerConfig} [config] - Notification banner config
|
1716
2010
|
*/
|
1717
|
-
constructor($
|
1718
|
-
super();
|
1719
|
-
this.$
|
1720
|
-
|
1721
|
-
if (!($module instanceof HTMLElement)) {
|
1722
|
-
throw new ElementError({
|
1723
|
-
componentName: 'Notification banner',
|
1724
|
-
element: $module,
|
1725
|
-
identifier: 'Root element (`$module`)'
|
1726
|
-
});
|
1727
|
-
}
|
1728
|
-
this.$module = $module;
|
1729
|
-
this.config = mergeConfigs(NotificationBanner.defaults, config, normaliseDataset(NotificationBanner, $module.dataset));
|
1730
|
-
if (this.$module.getAttribute('role') === 'alert' && !this.config.disableAutoFocus) {
|
1731
|
-
setFocus(this.$module);
|
2011
|
+
constructor($root, config = {}) {
|
2012
|
+
super($root, config);
|
2013
|
+
if (this.$root.getAttribute('role') === 'alert' && !this.config.disableAutoFocus) {
|
2014
|
+
setFocus(this.$root);
|
1732
2015
|
}
|
1733
2016
|
}
|
1734
2017
|
}
|
@@ -1744,7 +2027,7 @@
|
|
1744
2027
|
*/
|
1745
2028
|
|
1746
2029
|
/**
|
1747
|
-
* @
|
2030
|
+
* @import { Schema } from '../../common/configuration.mjs'
|
1748
2031
|
*/
|
1749
2032
|
NotificationBanner.moduleName = 'govuk-notification-banner';
|
1750
2033
|
NotificationBanner.defaults = Object.freeze({
|
@@ -1762,31 +2045,23 @@
|
|
1762
2045
|
* Password input component
|
1763
2046
|
*
|
1764
2047
|
* @preserve
|
2048
|
+
* @augments ConfigurableComponent<PasswordInputConfig>
|
1765
2049
|
*/
|
1766
|
-
class PasswordInput extends
|
2050
|
+
class PasswordInput extends ConfigurableComponent {
|
1767
2051
|
/**
|
1768
|
-
* @param {Element | null} $
|
2052
|
+
* @param {Element | null} $root - HTML element to use for password input
|
1769
2053
|
* @param {PasswordInputConfig} [config] - Password input config
|
1770
2054
|
*/
|
1771
|
-
constructor($
|
1772
|
-
super();
|
1773
|
-
this.$module = void 0;
|
1774
|
-
this.config = void 0;
|
2055
|
+
constructor($root, config = {}) {
|
2056
|
+
super($root, config);
|
1775
2057
|
this.i18n = void 0;
|
1776
2058
|
this.$input = void 0;
|
1777
2059
|
this.$showHideButton = void 0;
|
1778
2060
|
this.$screenReaderStatusMessage = void 0;
|
1779
|
-
|
1780
|
-
throw new ElementError({
|
1781
|
-
componentName: 'Password input',
|
1782
|
-
element: $module,
|
1783
|
-
identifier: 'Root element (`$module`)'
|
1784
|
-
});
|
1785
|
-
}
|
1786
|
-
const $input = $module.querySelector('.govuk-js-password-input-input');
|
2061
|
+
const $input = this.$root.querySelector('.govuk-js-password-input-input');
|
1787
2062
|
if (!($input instanceof HTMLInputElement)) {
|
1788
2063
|
throw new ElementError({
|
1789
|
-
|
2064
|
+
component: PasswordInput,
|
1790
2065
|
element: $input,
|
1791
2066
|
expectedType: 'HTMLInputElement',
|
1792
2067
|
identifier: 'Form field (`.govuk-js-password-input-input`)'
|
@@ -1795,10 +2070,10 @@
|
|
1795
2070
|
if ($input.type !== 'password') {
|
1796
2071
|
throw new ElementError('Password input: Form field (`.govuk-js-password-input-input`) must be of type `password`.');
|
1797
2072
|
}
|
1798
|
-
const $showHideButton =
|
2073
|
+
const $showHideButton = this.$root.querySelector('.govuk-js-password-input-toggle');
|
1799
2074
|
if (!($showHideButton instanceof HTMLButtonElement)) {
|
1800
2075
|
throw new ElementError({
|
1801
|
-
|
2076
|
+
component: PasswordInput,
|
1802
2077
|
element: $showHideButton,
|
1803
2078
|
expectedType: 'HTMLButtonElement',
|
1804
2079
|
identifier: 'Button (`.govuk-js-password-input-toggle`)'
|
@@ -1807,12 +2082,10 @@
|
|
1807
2082
|
if ($showHideButton.type !== 'button') {
|
1808
2083
|
throw new ElementError('Password input: Button (`.govuk-js-password-input-toggle`) must be of type `button`.');
|
1809
2084
|
}
|
1810
|
-
this.$module = $module;
|
1811
2085
|
this.$input = $input;
|
1812
2086
|
this.$showHideButton = $showHideButton;
|
1813
|
-
this.config = mergeConfigs(PasswordInput.defaults, config, normaliseDataset(PasswordInput, $module.dataset));
|
1814
2087
|
this.i18n = new I18n(this.config.i18n, {
|
1815
|
-
locale: closestAttributeValue(
|
2088
|
+
locale: closestAttributeValue(this.$root, 'lang')
|
1816
2089
|
});
|
1817
2090
|
this.$showHideButton.removeAttribute('hidden');
|
1818
2091
|
const $screenReaderStatusMessage = document.createElement('div');
|
@@ -1890,8 +2163,7 @@
|
|
1890
2163
|
*/
|
1891
2164
|
|
1892
2165
|
/**
|
1893
|
-
* @
|
1894
|
-
* @typedef {import('../../i18n.mjs').TranslationPluralForms} TranslationPluralForms
|
2166
|
+
* @import { Schema } from '../../common/configuration.mjs'
|
1895
2167
|
*/
|
1896
2168
|
PasswordInput.moduleName = 'govuk-password-input';
|
1897
2169
|
PasswordInput.defaults = Object.freeze({
|
@@ -1917,7 +2189,7 @@
|
|
1917
2189
|
*
|
1918
2190
|
* @preserve
|
1919
2191
|
*/
|
1920
|
-
class Radios extends
|
2192
|
+
class Radios extends Component {
|
1921
2193
|
/**
|
1922
2194
|
* Radios can be associated with a 'conditionally revealed' content block –
|
1923
2195
|
* for example, a radio for 'Phone' could reveal an additional form field for
|
@@ -1930,27 +2202,18 @@
|
|
1930
2202
|
* (for example if the user has navigated back), and set up event handlers to
|
1931
2203
|
* keep the reveal in sync with the radio state.
|
1932
2204
|
*
|
1933
|
-
* @param {Element | null} $
|
2205
|
+
* @param {Element | null} $root - HTML element to use for radios
|
1934
2206
|
*/
|
1935
|
-
constructor($
|
1936
|
-
super();
|
1937
|
-
this.$module = void 0;
|
2207
|
+
constructor($root) {
|
2208
|
+
super($root);
|
1938
2209
|
this.$inputs = void 0;
|
1939
|
-
|
1940
|
-
throw new ElementError({
|
1941
|
-
componentName: 'Radios',
|
1942
|
-
element: $module,
|
1943
|
-
identifier: 'Root element (`$module`)'
|
1944
|
-
});
|
1945
|
-
}
|
1946
|
-
const $inputs = $module.querySelectorAll('input[type="radio"]');
|
2210
|
+
const $inputs = this.$root.querySelectorAll('input[type="radio"]');
|
1947
2211
|
if (!$inputs.length) {
|
1948
2212
|
throw new ElementError({
|
1949
|
-
|
2213
|
+
component: Radios,
|
1950
2214
|
identifier: 'Form inputs (`<input type="radio">`)'
|
1951
2215
|
});
|
1952
2216
|
}
|
1953
|
-
this.$module = $module;
|
1954
2217
|
this.$inputs = $inputs;
|
1955
2218
|
this.$inputs.forEach($input => {
|
1956
2219
|
const targetId = $input.getAttribute('data-aria-controls');
|
@@ -1959,7 +2222,7 @@
|
|
1959
2222
|
}
|
1960
2223
|
if (!document.getElementById(targetId)) {
|
1961
2224
|
throw new ElementError({
|
1962
|
-
|
2225
|
+
component: Radios,
|
1963
2226
|
identifier: `Conditional reveal (\`id="${targetId}"\`)`
|
1964
2227
|
});
|
1965
2228
|
}
|
@@ -1968,7 +2231,7 @@
|
|
1968
2231
|
});
|
1969
2232
|
window.addEventListener('pageshow', () => this.syncAllConditionalReveals());
|
1970
2233
|
this.syncAllConditionalReveals();
|
1971
|
-
this.$
|
2234
|
+
this.$root.addEventListener('click', event => this.handleClick(event));
|
1972
2235
|
}
|
1973
2236
|
syncAllConditionalReveals() {
|
1974
2237
|
this.$inputs.forEach($input => this.syncConditionalRevealWithInputState($input));
|
@@ -2005,35 +2268,105 @@
|
|
2005
2268
|
Radios.moduleName = 'govuk-radios';
|
2006
2269
|
|
2007
2270
|
/**
|
2008
|
-
*
|
2271
|
+
* Service Navigation component
|
2009
2272
|
*
|
2010
2273
|
* @preserve
|
2011
2274
|
*/
|
2012
|
-
class
|
2275
|
+
class ServiceNavigation extends Component {
|
2013
2276
|
/**
|
2014
|
-
* @param {Element | null} $
|
2015
|
-
* @throws {ElementError} when $module is not set or the wrong type
|
2016
|
-
* @throws {ElementError} when $module.hash does not contain a hash
|
2017
|
-
* @throws {ElementError} when the linked element is missing or the wrong type
|
2277
|
+
* @param {Element | null} $root - HTML element to use for header
|
2018
2278
|
*/
|
2019
|
-
constructor($
|
2020
|
-
|
2021
|
-
|
2022
|
-
this.$
|
2023
|
-
|
2279
|
+
constructor($root) {
|
2280
|
+
super($root);
|
2281
|
+
this.$menuButton = void 0;
|
2282
|
+
this.$menu = void 0;
|
2283
|
+
this.menuIsOpen = false;
|
2284
|
+
this.mql = null;
|
2285
|
+
const $menuButton = this.$root.querySelector('.govuk-js-service-navigation-toggle');
|
2286
|
+
if (!$menuButton) {
|
2287
|
+
return this;
|
2288
|
+
}
|
2289
|
+
const menuId = $menuButton.getAttribute('aria-controls');
|
2290
|
+
if (!menuId) {
|
2024
2291
|
throw new ElementError({
|
2025
|
-
|
2026
|
-
|
2027
|
-
|
2028
|
-
|
2292
|
+
component: ServiceNavigation,
|
2293
|
+
identifier: 'Navigation button (`<button class="govuk-js-service-navigation-toggle">`) attribute (`aria-controls`)'
|
2294
|
+
});
|
2295
|
+
}
|
2296
|
+
const $menu = document.getElementById(menuId);
|
2297
|
+
if (!$menu) {
|
2298
|
+
throw new ElementError({
|
2299
|
+
component: ServiceNavigation,
|
2300
|
+
element: $menu,
|
2301
|
+
identifier: `Navigation (\`<ul id="${menuId}">\`)`
|
2302
|
+
});
|
2303
|
+
}
|
2304
|
+
this.$menu = $menu;
|
2305
|
+
this.$menuButton = $menuButton;
|
2306
|
+
this.setupResponsiveChecks();
|
2307
|
+
this.$menuButton.addEventListener('click', () => this.handleMenuButtonClick());
|
2308
|
+
}
|
2309
|
+
setupResponsiveChecks() {
|
2310
|
+
const breakpoint = getBreakpoint('tablet');
|
2311
|
+
if (!breakpoint.value) {
|
2312
|
+
throw new ElementError({
|
2313
|
+
component: ServiceNavigation,
|
2314
|
+
identifier: `CSS custom property (\`${breakpoint.property}\`) on pseudo-class \`:root\``
|
2029
2315
|
});
|
2030
2316
|
}
|
2031
|
-
this
|
2032
|
-
|
2033
|
-
|
2317
|
+
this.mql = window.matchMedia(`(min-width: ${breakpoint.value})`);
|
2318
|
+
if ('addEventListener' in this.mql) {
|
2319
|
+
this.mql.addEventListener('change', () => this.checkMode());
|
2320
|
+
} else {
|
2321
|
+
this.mql.addListener(() => this.checkMode());
|
2322
|
+
}
|
2323
|
+
this.checkMode();
|
2324
|
+
}
|
2325
|
+
checkMode() {
|
2326
|
+
if (!this.mql || !this.$menu || !this.$menuButton) {
|
2327
|
+
return;
|
2328
|
+
}
|
2329
|
+
if (this.mql.matches) {
|
2330
|
+
this.$menu.removeAttribute('hidden');
|
2331
|
+
this.$menuButton.setAttribute('hidden', '');
|
2332
|
+
} else {
|
2333
|
+
this.$menuButton.removeAttribute('hidden');
|
2334
|
+
this.$menuButton.setAttribute('aria-expanded', this.menuIsOpen.toString());
|
2335
|
+
if (this.menuIsOpen) {
|
2336
|
+
this.$menu.removeAttribute('hidden');
|
2337
|
+
} else {
|
2338
|
+
this.$menu.setAttribute('hidden', '');
|
2339
|
+
}
|
2340
|
+
}
|
2341
|
+
}
|
2342
|
+
handleMenuButtonClick() {
|
2343
|
+
this.menuIsOpen = !this.menuIsOpen;
|
2344
|
+
this.checkMode();
|
2345
|
+
}
|
2346
|
+
}
|
2347
|
+
ServiceNavigation.moduleName = 'govuk-service-navigation';
|
2348
|
+
|
2349
|
+
/**
|
2350
|
+
* Skip link component
|
2351
|
+
*
|
2352
|
+
* @preserve
|
2353
|
+
* @augments Component<HTMLAnchorElement>
|
2354
|
+
*/
|
2355
|
+
class SkipLink extends Component {
|
2356
|
+
/**
|
2357
|
+
* @param {Element | null} $root - HTML element to use for skip link
|
2358
|
+
* @throws {ElementError} when $root is not set or the wrong type
|
2359
|
+
* @throws {ElementError} when $root.hash does not contain a hash
|
2360
|
+
* @throws {ElementError} when the linked element is missing or the wrong type
|
2361
|
+
*/
|
2362
|
+
constructor($root) {
|
2363
|
+
var _this$$root$getAttrib;
|
2364
|
+
super($root);
|
2365
|
+
const hash = this.$root.hash;
|
2366
|
+
const href = (_this$$root$getAttrib = this.$root.getAttribute('href')) != null ? _this$$root$getAttrib : '';
|
2034
2367
|
let url;
|
2035
2368
|
try {
|
2036
|
-
url = new window.URL(this.$
|
2369
|
+
url = new window.URL(this.$root.href);
|
2037
2370
|
} catch (error) {
|
2038
2371
|
throw new ElementError(`Skip link: Target link (\`href="${href}"\`) is invalid`);
|
2039
2372
|
}
|
@@ -2047,12 +2380,12 @@
|
|
2047
2380
|
const $linkedElement = document.getElementById(linkedElementId);
|
2048
2381
|
if (!$linkedElement) {
|
2049
2382
|
throw new ElementError({
|
2050
|
-
|
2383
|
+
component: SkipLink,
|
2051
2384
|
element: $linkedElement,
|
2052
2385
|
identifier: `Target content (\`id="${linkedElementId}"\`)`
|
2053
2386
|
});
|
2054
2387
|
}
|
2055
|
-
this.$
|
2388
|
+
this.$root.addEventListener('click', () => setFocus($linkedElement, {
|
2056
2389
|
onBeforeFocus() {
|
2057
2390
|
$linkedElement.classList.add('govuk-skip-link-focused-element');
|
2058
2391
|
},
|
@@ -2062,6 +2395,7 @@
|
|
2062
2395
|
}));
|
2063
2396
|
}
|
2064
2397
|
}
|
2398
|
+
SkipLink.elementType = HTMLAnchorElement;
|
2065
2399
|
SkipLink.moduleName = 'govuk-skip-link';
|
2066
2400
|
|
2067
2401
|
/**
|
@@ -2069,13 +2403,12 @@
|
|
2069
2403
|
*
|
2070
2404
|
* @preserve
|
2071
2405
|
*/
|
2072
|
-
class Tabs extends
|
2406
|
+
class Tabs extends Component {
|
2073
2407
|
/**
|
2074
|
-
* @param {Element | null} $
|
2408
|
+
* @param {Element | null} $root - HTML element to use for tabs
|
2075
2409
|
*/
|
2076
|
-
constructor($
|
2077
|
-
super();
|
2078
|
-
this.$module = void 0;
|
2410
|
+
constructor($root) {
|
2411
|
+
super($root);
|
2079
2412
|
this.$tabs = void 0;
|
2080
2413
|
this.$tabList = void 0;
|
2081
2414
|
this.$tabListItems = void 0;
|
@@ -2085,36 +2418,28 @@
|
|
2085
2418
|
this.boundTabKeydown = void 0;
|
2086
2419
|
this.boundOnHashChange = void 0;
|
2087
2420
|
this.mql = null;
|
2088
|
-
|
2089
|
-
throw new ElementError({
|
2090
|
-
componentName: 'Tabs',
|
2091
|
-
element: $module,
|
2092
|
-
identifier: 'Root element (`$module`)'
|
2093
|
-
});
|
2094
|
-
}
|
2095
|
-
const $tabs = $module.querySelectorAll('a.govuk-tabs__tab');
|
2421
|
+
const $tabs = this.$root.querySelectorAll('a.govuk-tabs__tab');
|
2096
2422
|
if (!$tabs.length) {
|
2097
2423
|
throw new ElementError({
|
2098
|
-
|
2424
|
+
component: Tabs,
|
2099
2425
|
identifier: 'Links (`<a class="govuk-tabs__tab">`)'
|
2100
2426
|
});
|
2101
2427
|
}
|
2102
|
-
this.$module = $module;
|
2103
2428
|
this.$tabs = $tabs;
|
2104
2429
|
this.boundTabClick = this.onTabClick.bind(this);
|
2105
2430
|
this.boundTabKeydown = this.onTabKeydown.bind(this);
|
2106
2431
|
this.boundOnHashChange = this.onHashChange.bind(this);
|
2107
|
-
const $tabList = this.$
|
2108
|
-
const $tabListItems = this.$
|
2432
|
+
const $tabList = this.$root.querySelector('.govuk-tabs__list');
|
2433
|
+
const $tabListItems = this.$root.querySelectorAll('li.govuk-tabs__list-item');
|
2109
2434
|
if (!$tabList) {
|
2110
2435
|
throw new ElementError({
|
2111
|
-
|
2436
|
+
component: Tabs,
|
2112
2437
|
identifier: 'List (`<ul class="govuk-tabs__list">`)'
|
2113
2438
|
});
|
2114
2439
|
}
|
2115
2440
|
if (!$tabListItems.length) {
|
2116
2441
|
throw new ElementError({
|
2117
|
-
|
2442
|
+
component: Tabs,
|
2118
2443
|
identifier: 'List items (`<li class="govuk-tabs__list-item">`)'
|
2119
2444
|
});
|
2120
2445
|
}
|
@@ -2126,7 +2451,7 @@
|
|
2126
2451
|
const breakpoint = getBreakpoint('tablet');
|
2127
2452
|
if (!breakpoint.value) {
|
2128
2453
|
throw new ElementError({
|
2129
|
-
|
2454
|
+
component: Tabs,
|
2130
2455
|
identifier: `CSS custom property (\`${breakpoint.property}\`) on pseudo-class \`:root\``
|
2131
2456
|
});
|
2132
2457
|
}
|
@@ -2201,7 +2526,7 @@
|
|
2201
2526
|
this.showPanel($tab);
|
2202
2527
|
}
|
2203
2528
|
getTab(hash) {
|
2204
|
-
return this.$
|
2529
|
+
return this.$root.querySelector(`a.govuk-tabs__tab[href="${hash}"]`);
|
2205
2530
|
}
|
2206
2531
|
setAttributes($tab) {
|
2207
2532
|
const panelId = getFragmentFromUrl($tab.href);
|
@@ -2260,16 +2585,12 @@
|
|
2260
2585
|
onTabKeydown(event) {
|
2261
2586
|
switch (event.key) {
|
2262
2587
|
case 'ArrowLeft':
|
2263
|
-
case 'ArrowUp':
|
2264
2588
|
case 'Left':
|
2265
|
-
case 'Up':
|
2266
2589
|
this.activatePreviousTab();
|
2267
2590
|
event.preventDefault();
|
2268
2591
|
break;
|
2269
2592
|
case 'ArrowRight':
|
2270
|
-
case 'ArrowDown':
|
2271
2593
|
case 'Right':
|
2272
|
-
case 'Down':
|
2273
2594
|
this.activateNextTab();
|
2274
2595
|
event.preventDefault();
|
2275
2596
|
break;
|
@@ -2316,7 +2637,7 @@
|
|
2316
2637
|
if (!panelId) {
|
2317
2638
|
return null;
|
2318
2639
|
}
|
2319
|
-
return this.$
|
2640
|
+
return this.$root.querySelector(`#${panelId}`);
|
2320
2641
|
}
|
2321
2642
|
showPanel($tab) {
|
2322
2643
|
const $panel = this.getPanel($tab);
|
@@ -2349,7 +2670,7 @@
|
|
2349
2670
|
$tab.setAttribute('tabindex', '0');
|
2350
2671
|
}
|
2351
2672
|
getCurrentTab() {
|
2352
|
-
return this.$
|
2673
|
+
return this.$root.querySelector('.govuk-tabs__list-item--selected a.govuk-tabs__tab');
|
2353
2674
|
}
|
2354
2675
|
}
|
2355
2676
|
Tabs.moduleName = 'govuk-tabs';
|
@@ -2360,19 +2681,28 @@
|
|
2360
2681
|
* Use the `data-module` attributes to find, instantiate and init all of the
|
2361
2682
|
* components provided as part of GOV.UK Frontend.
|
2362
2683
|
*
|
2363
|
-
* @param {Config & { scope?: Element }} [config] - Config for all components (with optional scope)
|
2684
|
+
* @param {Config & { scope?: Element, onError?: OnErrorCallback<CompatibleClass> }} [config] - Config for all components (with optional scope)
|
2364
2685
|
*/
|
2365
2686
|
function initAll(config) {
|
2366
2687
|
var _config$scope;
|
2367
2688
|
config = typeof config !== 'undefined' ? config : {};
|
2368
2689
|
if (!isSupported()) {
|
2369
|
-
|
2690
|
+
if (config.onError) {
|
2691
|
+
config.onError(new SupportError(), {
|
2692
|
+
config
|
2693
|
+
});
|
2694
|
+
} else {
|
2695
|
+
console.log(new SupportError());
|
2696
|
+
}
|
2370
2697
|
return;
|
2371
2698
|
}
|
2372
|
-
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], [SkipLink], [Tabs]];
|
2373
|
-
const
|
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]];
|
2700
|
+
const options = {
|
2701
|
+
scope: (_config$scope = config.scope) != null ? _config$scope : document,
|
2702
|
+
onError: config.onError
|
2703
|
+
};
|
2374
2704
|
components.forEach(([Component, config]) => {
|
2375
|
-
createAll(Component, config,
|
2705
|
+
createAll(Component, config, options);
|
2376
2706
|
});
|
2377
2707
|
}
|
2378
2708
|
|
@@ -2385,25 +2715,58 @@
|
|
2385
2715
|
*
|
2386
2716
|
* Any component errors will be caught and logged to the console.
|
2387
2717
|
*
|
2388
|
-
* @template {CompatibleClass}
|
2389
|
-
* @param {
|
2390
|
-
* @param {
|
2391
|
-
* @param {Element|Document} [
|
2392
|
-
* @returns {Array<InstanceType<
|
2718
|
+
* @template {CompatibleClass} ComponentClass
|
2719
|
+
* @param {ComponentClass} Component - class of the component to create
|
2720
|
+
* @param {ComponentConfig<ComponentClass>} [config] - Config supplied to component
|
2721
|
+
* @param {OnErrorCallback<ComponentClass> | Element | Document | CreateAllOptions<ComponentClass> } [createAllOptions] - options for createAll including scope of the document to search within and callback function if error throw by component on init
|
2722
|
+
* @returns {Array<InstanceType<ComponentClass>>} - array of instantiated components
|
2393
2723
|
*/
|
2394
|
-
function createAll(Component, config,
|
2724
|
+
function createAll(Component, config, createAllOptions) {
|
2725
|
+
let $scope = document;
|
2726
|
+
let onError;
|
2727
|
+
if (typeof createAllOptions === 'object') {
|
2728
|
+
var _createAllOptions$sco;
|
2729
|
+
createAllOptions = createAllOptions;
|
2730
|
+
$scope = (_createAllOptions$sco = createAllOptions.scope) != null ? _createAllOptions$sco : $scope;
|
2731
|
+
onError = createAllOptions.onError;
|
2732
|
+
}
|
2733
|
+
if (typeof createAllOptions === 'function') {
|
2734
|
+
onError = createAllOptions;
|
2735
|
+
}
|
2736
|
+
if (createAllOptions instanceof HTMLElement) {
|
2737
|
+
$scope = createAllOptions;
|
2738
|
+
}
|
2395
2739
|
const $elements = $scope.querySelectorAll(`[data-module="${Component.moduleName}"]`);
|
2740
|
+
if (!isSupported()) {
|
2741
|
+
if (onError) {
|
2742
|
+
onError(new SupportError(), {
|
2743
|
+
component: Component,
|
2744
|
+
config
|
2745
|
+
});
|
2746
|
+
} else {
|
2747
|
+
console.log(new SupportError());
|
2748
|
+
}
|
2749
|
+
return [];
|
2750
|
+
}
|
2396
2751
|
return Array.from($elements).map($element => {
|
2397
2752
|
try {
|
2398
|
-
return
|
2753
|
+
return typeof config !== 'undefined' ? new Component($element, config) : new Component($element);
|
2399
2754
|
} catch (error) {
|
2400
|
-
|
2755
|
+
if (onError) {
|
2756
|
+
onError(error, {
|
2757
|
+
element: $element,
|
2758
|
+
component: Component,
|
2759
|
+
config
|
2760
|
+
});
|
2761
|
+
} else {
|
2762
|
+
console.log(error);
|
2763
|
+
}
|
2401
2764
|
return null;
|
2402
2765
|
}
|
2403
2766
|
}).filter(Boolean);
|
2404
2767
|
}
|
2405
2768
|
/**
|
2406
|
-
* @typedef {{new (...args: any[]): any,
|
2769
|
+
* @typedef {{new (...args: any[]): any, moduleName: string}} CompatibleClass
|
2407
2770
|
*/
|
2408
2771
|
/**
|
2409
2772
|
* Config for all components via `initAll()`
|
@@ -2414,43 +2777,70 @@
|
|
2414
2777
|
* @property {CharacterCountConfig} [characterCount] - Character Count config
|
2415
2778
|
* @property {ErrorSummaryConfig} [errorSummary] - Error Summary config
|
2416
2779
|
* @property {ExitThisPageConfig} [exitThisPage] - Exit This Page config
|
2780
|
+
* @property {FileUploadConfig} [fileUpload] - File Upload config
|
2417
2781
|
* @property {NotificationBannerConfig} [notificationBanner] - Notification Banner config
|
2418
2782
|
* @property {PasswordInputConfig} [passwordInput] - Password input config
|
2419
2783
|
*/
|
2420
2784
|
/**
|
2421
2785
|
* Config for individual components
|
2422
2786
|
*
|
2423
|
-
* @
|
2424
|
-
* @
|
2425
|
-
* @
|
2426
|
-
* @
|
2427
|
-
* @
|
2428
|
-
* @
|
2429
|
-
* @
|
2430
|
-
* @
|
2431
|
-
* @typedef {import('./components/notification-banner/notification-banner.mjs').NotificationBannerConfig} NotificationBannerConfig
|
2432
|
-
* @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'
|
2433
2795
|
*/
|
2434
2796
|
/**
|
2435
2797
|
* Component config keys, e.g. `accordion` and `characterCount`
|
2436
2798
|
*
|
2437
2799
|
* @typedef {keyof Config} ConfigKey
|
2438
2800
|
*/
|
2801
|
+
/**
|
2802
|
+
* @template {CompatibleClass} ComponentClass
|
2803
|
+
* @typedef {ConstructorParameters<ComponentClass>[1]} ComponentConfig
|
2804
|
+
*/
|
2805
|
+
/**
|
2806
|
+
* @template {CompatibleClass} ComponentClass
|
2807
|
+
* @typedef {object} ErrorContext
|
2808
|
+
* @property {Element} [element] - Element used for component module initialisation
|
2809
|
+
* @property {ComponentClass} [component] - Class of component
|
2810
|
+
* @property {ComponentConfig<ComponentClass>} config - Config supplied to component
|
2811
|
+
*/
|
2812
|
+
/**
|
2813
|
+
* @template {CompatibleClass} ComponentClass
|
2814
|
+
* @callback OnErrorCallback
|
2815
|
+
* @param {unknown} error - Thrown error
|
2816
|
+
* @param {ErrorContext<ComponentClass>} context - Object containing the element, component class and configuration
|
2817
|
+
*/
|
2818
|
+
/**
|
2819
|
+
* @template {CompatibleClass} ComponentClass
|
2820
|
+
* @typedef {object} CreateAllOptions
|
2821
|
+
* @property {Element | Document} [scope] - scope of the document to search within
|
2822
|
+
* @property {OnErrorCallback<ComponentClass>} [onError] - callback function if error throw by component on init
|
2823
|
+
*/
|
2439
2824
|
|
2440
2825
|
exports.Accordion = Accordion;
|
2441
2826
|
exports.Button = Button;
|
2442
2827
|
exports.CharacterCount = CharacterCount;
|
2443
2828
|
exports.Checkboxes = Checkboxes;
|
2829
|
+
exports.Component = Component;
|
2830
|
+
exports.ConfigurableComponent = ConfigurableComponent;
|
2444
2831
|
exports.ErrorSummary = ErrorSummary;
|
2445
2832
|
exports.ExitThisPage = ExitThisPage;
|
2833
|
+
exports.FileUpload = FileUpload;
|
2446
2834
|
exports.Header = Header;
|
2447
2835
|
exports.NotificationBanner = NotificationBanner;
|
2448
2836
|
exports.PasswordInput = PasswordInput;
|
2449
2837
|
exports.Radios = Radios;
|
2838
|
+
exports.ServiceNavigation = ServiceNavigation;
|
2450
2839
|
exports.SkipLink = SkipLink;
|
2451
2840
|
exports.Tabs = Tabs;
|
2452
2841
|
exports.createAll = createAll;
|
2453
2842
|
exports.initAll = initAll;
|
2843
|
+
exports.isSupported = isSupported;
|
2454
2844
|
exports.version = version;
|
2455
2845
|
|
2456
2846
|
}));
|