govuk_tech_docs 3.0.1 → 3.2.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of govuk_tech_docs might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -0
- data/example/source/code.html.md +3 -26
- data/lib/assets/javascripts/_modules/collapsible-navigation.js +13 -7
- data/lib/assets/javascripts/_modules/in-page-navigation.js +9 -4
- data/lib/assets/javascripts/_modules/search.js +11 -7
- data/lib/assets/stylesheets/_govuk_tech_docs.scss +1 -1
- data/lib/assets/stylesheets/modules/_anchored-heading.scss +2 -2
- data/lib/assets/stylesheets/modules/_search.scss +2 -2
- data/lib/assets/stylesheets/modules/_toc.scss +4 -4
- data/lib/govuk_tech_docs/pages.rb +4 -2
- data/lib/govuk_tech_docs/path_helpers.rb +30 -0
- data/lib/govuk_tech_docs/table_of_contents/helpers.rb +7 -7
- data/lib/govuk_tech_docs/tech_docs_html_renderer.rb +3 -3
- data/lib/govuk_tech_docs/version.rb +1 -1
- data/lib/govuk_tech_docs.rb +1 -0
- data/lib/source/api/pages.json.erb +1 -1
- data/lib/source/layouts/_header.erb +3 -3
- data/lib/source/layouts/_search.erb +1 -1
- data/lib/source/layouts/core.erb +1 -1
- data/node_modules/govuk-frontend/govuk/all.js +272 -75
- data/node_modules/govuk-frontend/govuk/components/accordion/_index.scss +275 -98
- data/node_modules/govuk-frontend/govuk/components/accordion/accordion.js +169 -65
- data/node_modules/govuk-frontend/govuk/components/button/_index.scss +3 -4
- data/node_modules/govuk-frontend/govuk/components/button/button.js +2 -2
- data/node_modules/govuk-frontend/govuk/components/character-count/character-count.js +3 -3
- data/node_modules/govuk-frontend/govuk/components/checkboxes/_index.scss +14 -0
- data/node_modules/govuk-frontend/govuk/components/checkboxes/checkboxes.js +8 -10
- data/node_modules/govuk-frontend/govuk/components/cookie-banner/_index.scss +0 -2
- data/node_modules/govuk-frontend/govuk/components/details/_index.scss +2 -1
- data/node_modules/govuk-frontend/govuk/components/details/details.js +2 -2
- data/node_modules/govuk-frontend/govuk/components/error-message/_index.scss +1 -0
- data/node_modules/govuk-frontend/govuk/components/error-summary/error-summary.js +2 -2
- data/node_modules/govuk-frontend/govuk/components/file-upload/_index.scss +1 -0
- data/node_modules/govuk-frontend/govuk/components/footer/_index.scss +6 -37
- data/node_modules/govuk-frontend/govuk/components/header/_index.scss +10 -4
- data/node_modules/govuk-frontend/govuk/components/header/header.js +4 -4
- data/node_modules/govuk-frontend/govuk/components/hint/_index.scss +1 -3
- data/node_modules/govuk-frontend/govuk/components/input/_index.scss +1 -1
- data/node_modules/govuk-frontend/govuk/components/notification-banner/notification-banner.js +499 -2
- data/node_modules/govuk-frontend/govuk/components/panel/_index.scss +13 -1
- data/node_modules/govuk-frontend/govuk/components/radios/_index.scss +14 -0
- data/node_modules/govuk-frontend/govuk/components/radios/radios.js +4 -4
- data/node_modules/govuk-frontend/govuk/components/select/_index.scss +1 -1
- data/node_modules/govuk-frontend/govuk/components/skip-link/_index.scss +13 -0
- data/node_modules/govuk-frontend/govuk/components/skip-link/skip-link.js +1108 -0
- data/node_modules/govuk-frontend/govuk/components/summary-list/_index.scss +15 -23
- data/node_modules/govuk-frontend/govuk/components/tabs/_index.scss +2 -2
- data/node_modules/govuk-frontend/govuk/components/tabs/tabs.js +2 -2
- data/node_modules/govuk-frontend/govuk/components/tag/_index.scss +0 -5
- data/node_modules/govuk-frontend/govuk/components/textarea/_index.scss +1 -1
- data/node_modules/govuk-frontend/govuk/core/_all.scss +0 -1
- data/node_modules/govuk-frontend/govuk/core/_global-styles.scss +0 -6
- data/node_modules/govuk-frontend/govuk/core/_links.scss +0 -6
- data/node_modules/govuk-frontend/govuk/core/_lists.scss +0 -6
- data/node_modules/govuk-frontend/govuk/core/_section-break.scss +0 -6
- data/node_modules/govuk-frontend/govuk/core/_typography.scss +0 -6
- data/node_modules/govuk-frontend/govuk/helpers/_colour.scss +2 -2
- data/node_modules/govuk-frontend/govuk/helpers/_spacing.scss +22 -4
- data/node_modules/govuk-frontend/govuk/objects/_all.scss +1 -0
- data/node_modules/govuk-frontend/govuk/objects/_main-wrapper.scss +15 -30
- data/node_modules/govuk-frontend/govuk/{core → objects}/_template.scss +1 -5
- data/node_modules/govuk-frontend/govuk/overrides/_all.scss +1 -0
- data/node_modules/govuk-frontend/govuk/overrides/_display.scss +0 -6
- data/node_modules/govuk-frontend/govuk/overrides/_spacing.scss +0 -6
- data/node_modules/govuk-frontend/govuk/overrides/_text-align.scss +14 -0
- data/node_modules/govuk-frontend/govuk/overrides/_typography.scss +0 -6
- data/node_modules/govuk-frontend/govuk/overrides/_width.scss +0 -6
- data/node_modules/govuk-frontend/govuk/settings/_colours-organisations.scss +3 -0
- data/node_modules/govuk-frontend/govuk/settings/_measurements.scss +0 -10
- data/node_modules/govuk-frontend/govuk/tools/_all.scss +0 -1
- data/package-lock.json +3 -3
- data/package.json +1 -1
- metadata +9 -7
- data/node_modules/govuk-frontend/govuk/tools/_iff.scss +0 -17
@@ -777,19 +777,28 @@ function Accordion ($module) {
|
|
777
777
|
this.$module = $module;
|
778
778
|
this.moduleId = $module.getAttribute('id');
|
779
779
|
this.$sections = $module.querySelectorAll('.govuk-accordion__section');
|
780
|
-
this.$
|
780
|
+
this.$showAllButton = '';
|
781
781
|
this.browserSupportsSessionStorage = helper.checkForSessionStorage();
|
782
782
|
|
783
783
|
this.controlsClass = 'govuk-accordion__controls';
|
784
|
-
this.
|
785
|
-
this.
|
784
|
+
this.showAllClass = 'govuk-accordion__show-all';
|
785
|
+
this.showAllTextClass = 'govuk-accordion__show-all-text';
|
786
786
|
|
787
|
+
this.sectionExpandedClass = 'govuk-accordion__section--expanded';
|
788
|
+
this.sectionButtonClass = 'govuk-accordion__section-button';
|
787
789
|
this.sectionHeaderClass = 'govuk-accordion__section-header';
|
788
|
-
this.sectionHeaderFocusedClass = 'govuk-accordion__section-header--focused';
|
789
790
|
this.sectionHeadingClass = 'govuk-accordion__section-heading';
|
791
|
+
this.sectionHeadingTextClass = 'govuk-accordion__section-heading-text';
|
792
|
+
this.sectionHeadingTextFocusClass = 'govuk-accordion__section-heading-text-focus';
|
793
|
+
|
794
|
+
this.sectionShowHideToggleClass = 'govuk-accordion__section-toggle';
|
795
|
+
this.sectionShowHideToggleFocusClass = 'govuk-accordion__section-toggle-focus';
|
796
|
+
this.sectionShowHideTextClass = 'govuk-accordion__section-toggle-text';
|
797
|
+
this.upChevronIconClass = 'govuk-accordion-nav__chevron';
|
798
|
+
this.downChevronIconClass = 'govuk-accordion-nav__chevron--down';
|
799
|
+
|
790
800
|
this.sectionSummaryClass = 'govuk-accordion__section-summary';
|
791
|
-
this.
|
792
|
-
this.sectionExpandedClass = 'govuk-accordion__section--expanded';
|
801
|
+
this.sectionSummaryFocusClass = 'govuk-accordion__section-summary-focus';
|
793
802
|
}
|
794
803
|
|
795
804
|
// Initialize component
|
@@ -800,32 +809,39 @@ Accordion.prototype.init = function () {
|
|
800
809
|
}
|
801
810
|
|
802
811
|
this.initControls();
|
803
|
-
|
804
812
|
this.initSectionHeaders();
|
805
813
|
|
806
|
-
// See if "
|
814
|
+
// See if "Show all sections" button text should be updated
|
807
815
|
var areAllSectionsOpen = this.checkIfAllSectionsOpen();
|
808
|
-
this.
|
816
|
+
this.updateShowAllButton(areAllSectionsOpen);
|
809
817
|
};
|
810
818
|
|
811
819
|
// Initialise controls and set attributes
|
812
820
|
Accordion.prototype.initControls = function () {
|
813
|
-
// Create "
|
814
|
-
this.$
|
815
|
-
this.$
|
816
|
-
this.$
|
817
|
-
this.$
|
818
|
-
|
819
|
-
|
821
|
+
// Create "Show all" button and set attributes
|
822
|
+
this.$showAllButton = document.createElement('button');
|
823
|
+
this.$showAllButton.setAttribute('type', 'button');
|
824
|
+
this.$showAllButton.setAttribute('class', this.showAllClass);
|
825
|
+
this.$showAllButton.setAttribute('aria-expanded', 'false');
|
826
|
+
|
827
|
+
// Create icon, add to element
|
828
|
+
var $icon = document.createElement('span');
|
829
|
+
$icon.classList.add(this.upChevronIconClass);
|
830
|
+
this.$showAllButton.appendChild($icon);
|
820
831
|
|
821
832
|
// Create control wrapper and add controls to it
|
822
|
-
var accordionControls = document.createElement('div');
|
823
|
-
accordionControls.setAttribute('class', this.controlsClass);
|
824
|
-
accordionControls.appendChild(this.$
|
825
|
-
this.$module.insertBefore(accordionControls, this.$module.firstChild);
|
833
|
+
var $accordionControls = document.createElement('div');
|
834
|
+
$accordionControls.setAttribute('class', this.controlsClass);
|
835
|
+
$accordionControls.appendChild(this.$showAllButton);
|
836
|
+
this.$module.insertBefore($accordionControls, this.$module.firstChild);
|
837
|
+
|
838
|
+
// Build additional wrapper for Show all toggle text and place after icon
|
839
|
+
var $wrappershowAllText = document.createElement('span');
|
840
|
+
$wrappershowAllText.classList.add(this.showAllTextClass);
|
841
|
+
this.$showAllButton.appendChild($wrappershowAllText);
|
826
842
|
|
827
|
-
// Handle events
|
828
|
-
this.$
|
843
|
+
// Handle click events on the show/hide all button
|
844
|
+
this.$showAllButton.addEventListener('click', this.onShowOrHideAllToggle.bind(this));
|
829
845
|
};
|
830
846
|
|
831
847
|
// Initialise section headers
|
@@ -833,13 +849,12 @@ Accordion.prototype.initSectionHeaders = function () {
|
|
833
849
|
// Loop through section headers
|
834
850
|
nodeListForEach(this.$sections, function ($section, i) {
|
835
851
|
// Set header attributes
|
836
|
-
var header = $section.querySelector('.' + this.sectionHeaderClass);
|
837
|
-
this.
|
838
|
-
|
852
|
+
var $header = $section.querySelector('.' + this.sectionHeaderClass);
|
853
|
+
this.constructHeaderMarkup($header, i);
|
839
854
|
this.setExpanded(this.isExpanded($section), $section);
|
840
855
|
|
841
856
|
// Handle events
|
842
|
-
header.addEventListener('click', this.onSectionToggle.bind(this, $section));
|
857
|
+
$header.addEventListener('click', this.onSectionToggle.bind(this, $section));
|
843
858
|
|
844
859
|
// See if there is any state stored in sessionStorage and set the sections to
|
845
860
|
// open or closed.
|
@@ -847,51 +862,100 @@ Accordion.prototype.initSectionHeaders = function () {
|
|
847
862
|
}.bind(this));
|
848
863
|
};
|
849
864
|
|
850
|
-
|
851
|
-
Accordion.prototype.initHeaderAttributes = function ($headerWrapper, index) {
|
852
|
-
var $module = this;
|
865
|
+
Accordion.prototype.constructHeaderMarkup = function ($headerWrapper, index) {
|
853
866
|
var $span = $headerWrapper.querySelector('.' + this.sectionButtonClass);
|
854
867
|
var $heading = $headerWrapper.querySelector('.' + this.sectionHeadingClass);
|
855
868
|
var $summary = $headerWrapper.querySelector('.' + this.sectionSummaryClass);
|
856
869
|
|
857
|
-
//
|
870
|
+
// Create a button element that will replace the '.govuk-accordion__section-button' span
|
858
871
|
var $button = document.createElement('button');
|
859
872
|
$button.setAttribute('type', 'button');
|
860
|
-
$button.setAttribute('id', this.moduleId + '-heading-' + (index + 1));
|
861
873
|
$button.setAttribute('aria-controls', this.moduleId + '-content-' + (index + 1));
|
862
874
|
|
863
875
|
// Copy all attributes (https://developer.mozilla.org/en-US/docs/Web/API/Element/attributes) from $span to $button
|
864
876
|
for (var i = 0; i < $span.attributes.length; i++) {
|
865
877
|
var attr = $span.attributes.item(i);
|
866
|
-
|
878
|
+
// Add all attributes but not ID as this is being added to
|
879
|
+
// the section heading ($headingText)
|
880
|
+
if (attr.nodeName !== 'id') {
|
881
|
+
$button.setAttribute(attr.nodeName, attr.nodeValue);
|
882
|
+
}
|
867
883
|
}
|
868
884
|
|
869
|
-
|
870
|
-
|
871
|
-
|
885
|
+
// Create container for heading text so it can be styled
|
886
|
+
var $headingText = document.createElement('span');
|
887
|
+
$headingText.classList.add(this.sectionHeadingTextClass);
|
888
|
+
// Copy the span ID to the heading text to allow it to be referenced by `aria-labelledby` on the
|
889
|
+
// hidden content area without "Show this section"
|
890
|
+
$headingText.id = $span.id;
|
891
|
+
|
892
|
+
// Create an inner heading text container to limit the width of the focus state
|
893
|
+
var $headingTextFocus = document.createElement('span');
|
894
|
+
$headingTextFocus.classList.add(this.sectionHeadingTextFocusClass);
|
895
|
+
$headingText.appendChild($headingTextFocus);
|
896
|
+
// span could contain HTML elements (see https://www.w3.org/TR/2011/WD-html5-20110525/content-models.html#phrasing-content)
|
897
|
+
$headingTextFocus.innerHTML = $span.innerHTML;
|
898
|
+
|
899
|
+
// Create container for show / hide icons and text.
|
900
|
+
var $showToggle = document.createElement('span');
|
901
|
+
$showToggle.classList.add(this.sectionShowHideToggleClass);
|
902
|
+
// Tell Google not to index the 'show' text as part of the heading
|
903
|
+
// For the snippet to work with JavaScript, it must be added before adding the page element to the
|
904
|
+
// page's DOM. See https://developers.google.com/search/docs/advanced/robots/robots_meta_tag#data-nosnippet-attr
|
905
|
+
$showToggle.setAttribute('data-nosnippet', '');
|
906
|
+
// Create an inner container to limit the width of the focus state
|
907
|
+
var $showToggleFocus = document.createElement('span');
|
908
|
+
$showToggleFocus.classList.add(this.sectionShowHideToggleFocusClass);
|
909
|
+
$showToggle.appendChild($showToggleFocus);
|
910
|
+
// Create wrapper for the show / hide text. Append text after the show/hide icon
|
911
|
+
var $showToggleText = document.createElement('span');
|
912
|
+
var $icon = document.createElement('span');
|
913
|
+
$icon.classList.add(this.upChevronIconClass);
|
914
|
+
$showToggleFocus.appendChild($icon);
|
915
|
+
$showToggleText.classList.add(this.sectionShowHideTextClass);
|
916
|
+
$showToggleFocus.appendChild($showToggleText);
|
917
|
+
|
918
|
+
// Append elements to the button:
|
919
|
+
// 1. Heading text
|
920
|
+
// 2. Punctuation
|
921
|
+
// 3. (Optional: Summary line followed by punctuation)
|
922
|
+
// 4. Show / hide toggle
|
923
|
+
$button.appendChild($headingText);
|
924
|
+
$button.appendChild(this.getButtonPunctuationEl());
|
925
|
+
|
926
|
+
// If summary content exists add to DOM in correct order
|
927
|
+
if (typeof ($summary) !== 'undefined' && $summary !== null) {
|
928
|
+
// Create a new `span` element and copy the summary line content from the original `div` to the
|
929
|
+
// new `span`
|
930
|
+
// This is because the summary line text is now inside a button element, which can only contain
|
931
|
+
// phrasing content
|
932
|
+
var $summarySpan = document.createElement('span');
|
933
|
+
// Create an inner summary container to limit the width of the summary focus state
|
934
|
+
var $summarySpanFocus = document.createElement('span');
|
935
|
+
$summarySpanFocus.classList.add(this.sectionSummaryFocusClass);
|
936
|
+
$summarySpan.appendChild($summarySpanFocus);
|
937
|
+
|
938
|
+
// Get original attributes, and pass them to the replacement
|
939
|
+
for (var j = 0, l = $summary.attributes.length; j < l; ++j) {
|
940
|
+
var nodeName = $summary.attributes.item(j).nodeName;
|
941
|
+
var nodeValue = $summary.attributes.item(j).nodeValue;
|
942
|
+
$summarySpan.setAttribute(nodeName, nodeValue);
|
872
943
|
}
|
873
|
-
});
|
874
944
|
|
875
|
-
|
876
|
-
$
|
877
|
-
});
|
945
|
+
// Copy original contents of summary to the new summary span
|
946
|
+
$summarySpanFocus.innerHTML = $summary.innerHTML;
|
878
947
|
|
879
|
-
|
880
|
-
$
|
948
|
+
// Replace the original summary `div` with the new summary `span`
|
949
|
+
$summary.parentNode.replaceChild($summarySpan, $summary);
|
950
|
+
|
951
|
+
$button.appendChild($summarySpan);
|
952
|
+
$button.appendChild(this.getButtonPunctuationEl());
|
881
953
|
}
|
882
954
|
|
883
|
-
|
884
|
-
$button.innerHTML = $span.innerHTML;
|
955
|
+
$button.appendChild($showToggle);
|
885
956
|
|
886
957
|
$heading.removeChild($span);
|
887
958
|
$heading.appendChild($button);
|
888
|
-
|
889
|
-
// Add "+/-" icon
|
890
|
-
var icon = document.createElement('span');
|
891
|
-
icon.className = this.iconClass;
|
892
|
-
icon.setAttribute('aria-hidden', 'true');
|
893
|
-
|
894
|
-
$button.appendChild(icon);
|
895
959
|
};
|
896
960
|
|
897
961
|
// When section toggled, set and store state
|
@@ -904,10 +968,9 @@ Accordion.prototype.onSectionToggle = function ($section) {
|
|
904
968
|
};
|
905
969
|
|
906
970
|
// When Open/Close All toggled, set and store state
|
907
|
-
Accordion.prototype.
|
971
|
+
Accordion.prototype.onShowOrHideAllToggle = function () {
|
908
972
|
var $module = this;
|
909
973
|
var $sections = this.$sections;
|
910
|
-
|
911
974
|
var nowExpanded = !this.checkIfAllSectionsOpen();
|
912
975
|
|
913
976
|
nodeListForEach($sections, function ($section) {
|
@@ -916,23 +979,37 @@ Accordion.prototype.onOpenOrCloseAllToggle = function () {
|
|
916
979
|
$module.storeState($section);
|
917
980
|
});
|
918
981
|
|
919
|
-
$module.
|
982
|
+
$module.updateShowAllButton(nowExpanded);
|
920
983
|
};
|
921
984
|
|
922
985
|
// Set section attributes when opened/closed
|
923
986
|
Accordion.prototype.setExpanded = function (expanded, $section) {
|
987
|
+
var $icon = $section.querySelector('.' + this.upChevronIconClass);
|
988
|
+
var $showHideText = $section.querySelector('.' + this.sectionShowHideTextClass);
|
924
989
|
var $button = $section.querySelector('.' + this.sectionButtonClass);
|
990
|
+
var $newButtonText = expanded ? 'Hide' : 'Show';
|
991
|
+
|
992
|
+
// Build additional copy of "this section" for assistive technology and place inside toggle link
|
993
|
+
var $visuallyHiddenText = document.createElement('span');
|
994
|
+
$visuallyHiddenText.classList.add('govuk-visually-hidden');
|
995
|
+
$visuallyHiddenText.innerHTML = ' this section';
|
996
|
+
|
997
|
+
$showHideText.innerHTML = $newButtonText;
|
998
|
+
$showHideText.appendChild($visuallyHiddenText);
|
925
999
|
$button.setAttribute('aria-expanded', expanded);
|
926
1000
|
|
1001
|
+
// Swap icon, change class
|
927
1002
|
if (expanded) {
|
928
1003
|
$section.classList.add(this.sectionExpandedClass);
|
1004
|
+
$icon.classList.remove(this.downChevronIconClass);
|
929
1005
|
} else {
|
930
1006
|
$section.classList.remove(this.sectionExpandedClass);
|
1007
|
+
$icon.classList.add(this.downChevronIconClass);
|
931
1008
|
}
|
932
1009
|
|
933
|
-
// See if "
|
1010
|
+
// See if "Show all sections" button text should be updated
|
934
1011
|
var areAllSectionsOpen = this.checkIfAllSectionsOpen();
|
935
|
-
this.
|
1012
|
+
this.updateShowAllButton(areAllSectionsOpen);
|
936
1013
|
};
|
937
1014
|
|
938
1015
|
// Get state of section
|
@@ -951,12 +1028,20 @@ Accordion.prototype.checkIfAllSectionsOpen = function () {
|
|
951
1028
|
return areAllSectionsOpen
|
952
1029
|
};
|
953
1030
|
|
954
|
-
// Update "
|
955
|
-
Accordion.prototype.
|
956
|
-
var
|
957
|
-
|
958
|
-
|
959
|
-
this.$
|
1031
|
+
// Update "Show all sections" button
|
1032
|
+
Accordion.prototype.updateShowAllButton = function (expanded) {
|
1033
|
+
var $showAllIcon = this.$showAllButton.querySelector('.' + this.upChevronIconClass);
|
1034
|
+
var $showAllText = this.$showAllButton.querySelector('.' + this.showAllTextClass);
|
1035
|
+
var newButtonText = expanded ? 'Hide all sections' : 'Show all sections';
|
1036
|
+
this.$showAllButton.setAttribute('aria-expanded', expanded);
|
1037
|
+
$showAllText.innerHTML = newButtonText;
|
1038
|
+
|
1039
|
+
// Swap icon, toggle class
|
1040
|
+
if (expanded) {
|
1041
|
+
$showAllIcon.classList.remove(this.downChevronIconClass);
|
1042
|
+
} else {
|
1043
|
+
$showAllIcon.classList.add(this.downChevronIconClass);
|
1044
|
+
}
|
960
1045
|
};
|
961
1046
|
|
962
1047
|
// Check for `window.sessionStorage`, and that it actually works.
|
@@ -980,7 +1065,7 @@ var helper = {
|
|
980
1065
|
// Set the state of the accordions in sessionStorage
|
981
1066
|
Accordion.prototype.storeState = function ($section) {
|
982
1067
|
if (this.browserSupportsSessionStorage) {
|
983
|
-
// We need a unique way of identifying each content in the
|
1068
|
+
// We need a unique way of identifying each content in the Accordion. Since
|
984
1069
|
// an `#id` should be unique and an `id` is required for `aria-` attributes
|
985
1070
|
// `id` can be safely used.
|
986
1071
|
var $button = $section.querySelector('.' + this.sectionButtonClass);
|
@@ -1021,6 +1106,25 @@ Accordion.prototype.setInitialState = function ($section) {
|
|
1021
1106
|
}
|
1022
1107
|
};
|
1023
1108
|
|
1109
|
+
/**
|
1110
|
+
* Create an element to improve semantics of the section button with punctuation
|
1111
|
+
* @return {object} DOM element
|
1112
|
+
*
|
1113
|
+
* Used to add pause (with a comma) for assistive technology.
|
1114
|
+
* Example: [heading]Section A ,[pause] Show this section.
|
1115
|
+
* https://accessibility.blog.gov.uk/2017/12/18/what-working-on-gov-uk-navigation-taught-us-about-accessibility/
|
1116
|
+
*
|
1117
|
+
* Adding punctuation to the button can also improve its general semantics by dividing its contents
|
1118
|
+
* into thematic chunks.
|
1119
|
+
* See https://github.com/alphagov/govuk-frontend/issues/2327#issuecomment-922957442
|
1120
|
+
*/
|
1121
|
+
Accordion.prototype.getButtonPunctuationEl = function () {
|
1122
|
+
var $punctuationEl = document.createElement('span');
|
1123
|
+
$punctuationEl.classList.add('govuk-visually-hidden', 'govuk-accordion__section-heading-divider');
|
1124
|
+
$punctuationEl.innerHTML = ', ';
|
1125
|
+
return $punctuationEl
|
1126
|
+
};
|
1127
|
+
|
1024
1128
|
(function(undefined) {
|
1025
1129
|
|
1026
1130
|
// Detection from https://github.com/Financial-Times/polyfill-service/blob/master/packages/polyfill-library/polyfills/Window/detect.js
|
@@ -1498,7 +1602,7 @@ function CharacterCount ($module) {
|
|
1498
1602
|
this.$module = $module;
|
1499
1603
|
this.$textarea = $module.querySelector('.govuk-js-character-count');
|
1500
1604
|
if (this.$textarea) {
|
1501
|
-
this.$countMessage =
|
1605
|
+
this.$countMessage = document.getElementById(this.$textarea.id + '-info');
|
1502
1606
|
}
|
1503
1607
|
}
|
1504
1608
|
|
@@ -1697,7 +1801,7 @@ Checkboxes.prototype.init = function () {
|
|
1697
1801
|
|
1698
1802
|
// Skip checkboxes without data-aria-controls attributes, or where the
|
1699
1803
|
// target element does not exist.
|
1700
|
-
if (!target ||
|
1804
|
+
if (!target || !document.getElementById(target)) {
|
1701
1805
|
return
|
1702
1806
|
}
|
1703
1807
|
|
@@ -1741,7 +1845,7 @@ Checkboxes.prototype.syncAllConditionalReveals = function () {
|
|
1741
1845
|
* @param {HTMLInputElement} $input Checkbox input
|
1742
1846
|
*/
|
1743
1847
|
Checkboxes.prototype.syncConditionalRevealWithInputState = function ($input) {
|
1744
|
-
var $target =
|
1848
|
+
var $target = document.getElementById($input.getAttribute('aria-controls'));
|
1745
1849
|
|
1746
1850
|
if ($target && $target.classList.contains('govuk-checkboxes__conditional')) {
|
1747
1851
|
var inputIsChecked = $input.checked;
|
@@ -1764,10 +1868,9 @@ Checkboxes.prototype.unCheckAllInputsExcept = function ($input) {
|
|
1764
1868
|
var hasSameFormOwner = ($input.form === $inputWithSameName.form);
|
1765
1869
|
if (hasSameFormOwner && $inputWithSameName !== $input) {
|
1766
1870
|
$inputWithSameName.checked = false;
|
1871
|
+
this.syncConditionalRevealWithInputState($inputWithSameName);
|
1767
1872
|
}
|
1768
|
-
});
|
1769
|
-
|
1770
|
-
this.syncAllConditionalReveals();
|
1873
|
+
}.bind(this));
|
1771
1874
|
};
|
1772
1875
|
|
1773
1876
|
/**
|
@@ -1786,10 +1889,9 @@ Checkboxes.prototype.unCheckExclusiveInputs = function ($input) {
|
|
1786
1889
|
var hasSameFormOwner = ($input.form === $exclusiveInput.form);
|
1787
1890
|
if (hasSameFormOwner) {
|
1788
1891
|
$exclusiveInput.checked = false;
|
1892
|
+
this.syncConditionalRevealWithInputState($exclusiveInput);
|
1789
1893
|
}
|
1790
|
-
});
|
1791
|
-
|
1792
|
-
this.syncAllConditionalReveals();
|
1894
|
+
}.bind(this));
|
1793
1895
|
};
|
1794
1896
|
|
1795
1897
|
/**
|
@@ -2088,7 +2190,7 @@ Header.prototype.init = function () {
|
|
2088
2190
|
return
|
2089
2191
|
}
|
2090
2192
|
|
2091
|
-
this.syncState(this.$menu.classList.contains('govuk-header__navigation--open'));
|
2193
|
+
this.syncState(this.$menu.classList.contains('govuk-header__navigation-list--open'));
|
2092
2194
|
this.$menuButton.addEventListener('click', this.handleMenuButtonClick.bind(this));
|
2093
2195
|
};
|
2094
2196
|
|
@@ -2112,7 +2214,7 @@ Header.prototype.syncState = function (isVisible) {
|
|
2112
2214
|
* sync the accessibility state and menu button state
|
2113
2215
|
*/
|
2114
2216
|
Header.prototype.handleMenuButtonClick = function () {
|
2115
|
-
var isVisible = this.$menu.classList.toggle('govuk-header__navigation--open');
|
2217
|
+
var isVisible = this.$menu.classList.toggle('govuk-header__navigation-list--open');
|
2116
2218
|
this.syncState(isVisible);
|
2117
2219
|
};
|
2118
2220
|
|
@@ -2144,7 +2246,7 @@ Radios.prototype.init = function () {
|
|
2144
2246
|
|
2145
2247
|
// Skip radios without data-aria-controls attributes, or where the
|
2146
2248
|
// target element does not exist.
|
2147
|
-
if (!target ||
|
2249
|
+
if (!target || !document.getElementById(target)) {
|
2148
2250
|
return
|
2149
2251
|
}
|
2150
2252
|
|
@@ -2189,7 +2291,7 @@ Radios.prototype.syncAllConditionalReveals = function () {
|
|
2189
2291
|
* @param {HTMLInputElement} $input Radio input
|
2190
2292
|
*/
|
2191
2293
|
Radios.prototype.syncConditionalRevealWithInputState = function ($input) {
|
2192
|
-
var $target = document.
|
2294
|
+
var $target = document.getElementById($input.getAttribute('aria-controls'));
|
2193
2295
|
|
2194
2296
|
if ($target && $target.classList.contains('govuk-radios__conditional')) {
|
2195
2297
|
var inputIsChecked = $input.checked;
|
@@ -2231,6 +2333,95 @@ Radios.prototype.handleClick = function (event) {
|
|
2231
2333
|
}.bind(this));
|
2232
2334
|
};
|
2233
2335
|
|
2336
|
+
function SkipLink ($module) {
|
2337
|
+
this.$module = $module;
|
2338
|
+
this.$linkedElement = null;
|
2339
|
+
this.linkedElementListener = false;
|
2340
|
+
}
|
2341
|
+
|
2342
|
+
/**
|
2343
|
+
* Initialise the component
|
2344
|
+
*/
|
2345
|
+
SkipLink.prototype.init = function () {
|
2346
|
+
// Check for module
|
2347
|
+
if (!this.$module) {
|
2348
|
+
return
|
2349
|
+
}
|
2350
|
+
|
2351
|
+
// Check for linked element
|
2352
|
+
this.$linkedElement = this.getLinkedElement();
|
2353
|
+
if (!this.$linkedElement) {
|
2354
|
+
return
|
2355
|
+
}
|
2356
|
+
|
2357
|
+
this.$module.addEventListener('click', this.focusLinkedElement.bind(this));
|
2358
|
+
};
|
2359
|
+
|
2360
|
+
/**
|
2361
|
+
* Get linked element
|
2362
|
+
*
|
2363
|
+
* @returns {HTMLElement} $linkedElement - DOM element linked to from the skip link
|
2364
|
+
*/
|
2365
|
+
SkipLink.prototype.getLinkedElement = function () {
|
2366
|
+
var linkedElementId = this.getFragmentFromUrl();
|
2367
|
+
|
2368
|
+
if (!linkedElementId) {
|
2369
|
+
return false
|
2370
|
+
}
|
2371
|
+
|
2372
|
+
return document.getElementById(linkedElementId)
|
2373
|
+
};
|
2374
|
+
|
2375
|
+
/**
|
2376
|
+
* Focus the linked element
|
2377
|
+
*
|
2378
|
+
* Set tabindex and helper CSS class. Set listener to remove them on blur.
|
2379
|
+
*/
|
2380
|
+
SkipLink.prototype.focusLinkedElement = function () {
|
2381
|
+
var $linkedElement = this.$linkedElement;
|
2382
|
+
|
2383
|
+
if (!$linkedElement.getAttribute('tabindex')) {
|
2384
|
+
// Set the element tabindex to -1 so it can be focused with JavaScript.
|
2385
|
+
$linkedElement.setAttribute('tabindex', '-1');
|
2386
|
+
$linkedElement.classList.add('govuk-skip-link-focused-element');
|
2387
|
+
|
2388
|
+
// Add listener for blur on the focused element (unless the listener has previously been added)
|
2389
|
+
if (!this.linkedElementListener) {
|
2390
|
+
this.$linkedElement.addEventListener('blur', this.removeFocusProperties.bind(this));
|
2391
|
+
this.linkedElementListener = true;
|
2392
|
+
}
|
2393
|
+
}
|
2394
|
+
$linkedElement.focus();
|
2395
|
+
};
|
2396
|
+
|
2397
|
+
/**
|
2398
|
+
* Remove the tabindex that makes the linked element focusable because the element only needs to be
|
2399
|
+
* focusable until it has received programmatic focus and a screen reader has announced it.
|
2400
|
+
*
|
2401
|
+
* Remove the CSS class that removes the native focus styles.
|
2402
|
+
*/
|
2403
|
+
SkipLink.prototype.removeFocusProperties = function () {
|
2404
|
+
this.$linkedElement.removeAttribute('tabindex');
|
2405
|
+
this.$linkedElement.classList.remove('govuk-skip-link-focused-element');
|
2406
|
+
};
|
2407
|
+
|
2408
|
+
/**
|
2409
|
+
* Get fragment from URL
|
2410
|
+
*
|
2411
|
+
* Extract the fragment (everything after the hash symbol) from a URL, but not including
|
2412
|
+
* the symbol.
|
2413
|
+
*
|
2414
|
+
* @returns {string} Fragment from URL, without the hash symbol
|
2415
|
+
*/
|
2416
|
+
SkipLink.prototype.getFragmentFromUrl = function () {
|
2417
|
+
// Bail if the anchor link doesn't have a hash
|
2418
|
+
if (!this.$module.hash) {
|
2419
|
+
return false
|
2420
|
+
}
|
2421
|
+
|
2422
|
+
return this.$module.hash.split('#').pop()
|
2423
|
+
};
|
2424
|
+
|
2234
2425
|
(function(undefined) {
|
2235
2426
|
|
2236
2427
|
// Detection from https://raw.githubusercontent.com/Financial-Times/polyfill-library/master/polyfills/Element/prototype/nextElementSibling/detect.js
|
@@ -2596,6 +2787,10 @@ function initAll (options) {
|
|
2596
2787
|
new Radios($radio).init();
|
2597
2788
|
});
|
2598
2789
|
|
2790
|
+
// Find first skip link module to enhance.
|
2791
|
+
var $skipLink = scope.querySelector('[data-module="govuk-skip-link"]');
|
2792
|
+
new SkipLink($skipLink).init();
|
2793
|
+
|
2599
2794
|
var $tabs = scope.querySelectorAll('[data-module="govuk-tabs"]');
|
2600
2795
|
nodeListForEach($tabs, function ($tabs) {
|
2601
2796
|
new Tabs($tabs).init();
|
@@ -2610,7 +2805,9 @@ exports.CharacterCount = CharacterCount;
|
|
2610
2805
|
exports.Checkboxes = Checkboxes;
|
2611
2806
|
exports.ErrorSummary = ErrorSummary;
|
2612
2807
|
exports.Header = Header;
|
2808
|
+
exports.NotificationBanner = NotificationBanner;
|
2613
2809
|
exports.Radios = Radios;
|
2810
|
+
exports.SkipLink = SkipLink;
|
2614
2811
|
exports.Tabs = Tabs;
|
2615
2812
|
|
2616
2813
|
})));
|