govuk_tech_docs 3.2.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/publish.yaml +1 -1
  3. data/CHANGELOG.md +26 -7
  4. data/README.md +2 -2
  5. data/example/source/code.html.md +3 -26
  6. data/lib/govuk_tech_docs/contribution_banner.rb +1 -1
  7. data/lib/govuk_tech_docs/tech_docs_html_renderer.rb +3 -3
  8. data/lib/govuk_tech_docs/version.rb +1 -1
  9. data/lib/source/layouts/core.erb +1 -1
  10. data/node_modules/govuk-frontend/govuk/all.js +1548 -311
  11. data/node_modules/govuk-frontend/govuk/common/closest-attribute-value.js +70 -0
  12. data/node_modules/govuk-frontend/govuk/common/index.js +172 -0
  13. data/node_modules/govuk-frontend/govuk/common/normalise-dataset.js +373 -0
  14. data/node_modules/govuk-frontend/govuk/common.js +138 -3
  15. data/node_modules/govuk-frontend/govuk/components/_all.scss +1 -0
  16. data/node_modules/govuk-frontend/govuk/components/accordion/_index.scss +5 -6
  17. data/node_modules/govuk-frontend/govuk/components/accordion/accordion.js +754 -36
  18. data/node_modules/govuk-frontend/govuk/components/breadcrumbs/_index.scss +0 -2
  19. data/node_modules/govuk-frontend/govuk/components/button/_index.scss +29 -21
  20. data/node_modules/govuk-frontend/govuk/components/button/button.js +365 -107
  21. data/node_modules/govuk-frontend/govuk/components/character-count/_index.scss +9 -0
  22. data/node_modules/govuk-frontend/govuk/components/character-count/character-count.js +1092 -109
  23. data/node_modules/govuk-frontend/govuk/components/checkboxes/_index.scss +3 -2
  24. data/node_modules/govuk-frontend/govuk/components/checkboxes/checkboxes.js +30 -2
  25. data/node_modules/govuk-frontend/govuk/components/details/details.js +51 -33
  26. data/node_modules/govuk-frontend/govuk/components/error-summary/error-summary.js +289 -6
  27. data/node_modules/govuk-frontend/govuk/components/footer/_index.scss +13 -23
  28. data/node_modules/govuk-frontend/govuk/components/header/_index.scss +30 -24
  29. data/node_modules/govuk-frontend/govuk/components/header/header.js +59 -11
  30. data/node_modules/govuk-frontend/govuk/components/input/_index.scss +13 -23
  31. data/node_modules/govuk-frontend/govuk/components/notification-banner/notification-banner.js +252 -2
  32. data/node_modules/govuk-frontend/govuk/components/pagination/_index.scss +247 -0
  33. data/node_modules/govuk-frontend/govuk/components/pagination/_pagination.scss +2 -0
  34. data/node_modules/govuk-frontend/govuk/components/panel/_index.scss +1 -1
  35. data/node_modules/govuk-frontend/govuk/components/radios/_index.scss +5 -12
  36. data/node_modules/govuk-frontend/govuk/components/radios/radios.js +30 -2
  37. data/node_modules/govuk-frontend/govuk/components/select/_index.scss +11 -0
  38. data/node_modules/govuk-frontend/govuk/components/skip-link/_index.scss +1 -3
  39. data/node_modules/govuk-frontend/govuk/components/skip-link/skip-link.js +10 -4
  40. data/node_modules/govuk-frontend/govuk/components/summary-list/_index.scss +45 -13
  41. data/node_modules/govuk-frontend/govuk/components/table/_index.scss +1 -1
  42. data/node_modules/govuk-frontend/govuk/components/tabs/tabs.js +28 -0
  43. data/node_modules/govuk-frontend/govuk/core/_section-break.scss +1 -1
  44. data/node_modules/govuk-frontend/govuk/helpers/_colour.scss +5 -5
  45. data/node_modules/govuk-frontend/govuk/helpers/_focused.scss +5 -0
  46. data/node_modules/govuk-frontend/govuk/helpers/_links.scss +13 -11
  47. data/node_modules/govuk-frontend/govuk/helpers/_media-queries.scss +2 -2
  48. data/node_modules/govuk-frontend/govuk/helpers/_shape-arrow.scss +1 -1
  49. data/node_modules/govuk-frontend/govuk/helpers/_spacing.scss +3 -3
  50. data/node_modules/govuk-frontend/govuk/helpers/_typography.scss +16 -9
  51. data/node_modules/govuk-frontend/govuk/i18n.js +390 -0
  52. data/node_modules/govuk-frontend/govuk/objects/_button-group.scss +10 -26
  53. data/node_modules/govuk-frontend/govuk/objects/_template.scss +1 -1
  54. data/node_modules/govuk-frontend/govuk/objects/_width-container.scss +0 -4
  55. data/node_modules/govuk-frontend/govuk/overrides/_spacing.scss +56 -12
  56. data/node_modules/govuk-frontend/govuk/settings/_all.scss +1 -0
  57. data/node_modules/govuk-frontend/govuk/settings/_colours-palette.scss +12 -0
  58. data/node_modules/govuk-frontend/govuk/settings/_compatibility.scss +26 -0
  59. data/node_modules/govuk-frontend/govuk/settings/_spacing.scss +4 -8
  60. data/node_modules/govuk-frontend/govuk/settings/_typography-font.scss +23 -0
  61. data/node_modules/govuk-frontend/govuk/settings/_typography-responsive.scss +12 -0
  62. data/node_modules/govuk-frontend/govuk/settings/_warnings.scss +53 -0
  63. data/node_modules/govuk-frontend/govuk/tools/_compatibility.scss +20 -6
  64. data/node_modules/govuk-frontend/govuk/tools/_exports.scss +1 -1
  65. data/node_modules/govuk-frontend/govuk/tools/_font-url.scss +1 -1
  66. data/node_modules/govuk-frontend/govuk/tools/_image-url.scss +1 -1
  67. data/node_modules/govuk-frontend/govuk/tools/_px-to-em.scss +2 -2
  68. data/node_modules/govuk-frontend/govuk/tools/_px-to-rem.scss +1 -1
  69. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Date/now.js +21 -0
  70. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/dataset.js +300 -0
  71. data/node_modules/govuk-frontend/govuk/vendor/polyfills/String/prototype/trim.js +21 -0
  72. data/node_modules/govuk-frontend/govuk-prototype-kit/init.js +7 -0
  73. data/node_modules/govuk-frontend/govuk-prototype-kit/init.scss +12 -0
  74. data/package-lock.json +12 -12
  75. data/package.json +1 -1
  76. metadata +17 -5
@@ -83,7 +83,7 @@
83
83
  left: 0;
84
84
  width: $govuk-checkboxes-size;
85
85
  height: $govuk-checkboxes-size;
86
- border: $govuk-border-width-form-element solid currentColor;
86
+ border: $govuk-border-width-form-element solid currentcolor;
87
87
  background: transparent;
88
88
  }
89
89
 
@@ -155,7 +155,8 @@
155
155
  cursor: default;
156
156
  }
157
157
 
158
- .govuk-checkboxes__input:disabled + .govuk-checkboxes__label {
158
+ .govuk-checkboxes__input:disabled + .govuk-checkboxes__label,
159
+ .govuk-checkboxes__input:disabled ~ .govuk-hint {
159
160
  opacity: .5;
160
161
  }
161
162
 
@@ -1014,10 +1014,24 @@ if (detect) return
1014
1014
 
1015
1015
  }).call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
1016
1016
 
1017
+ /**
1018
+ * Common helpers which do not require polyfill.
1019
+ *
1020
+ * IMPORTANT: If a helper require a polyfill, please isolate it in its own module
1021
+ * so that the polyfill can be properly tree-shaken and does not burden
1022
+ * the components that do not need that helper
1023
+ *
1024
+ * @module common/index
1025
+ */
1026
+
1017
1027
  /**
1018
1028
  * TODO: Ideally this would be a NodeList.prototype.forEach polyfill
1019
1029
  * This seems to fail in IE8, requires more investigation.
1020
1030
  * See: https://github.com/imagitama/nodelist-foreach-polyfill
1031
+ *
1032
+ * @param {NodeListOf<Element>} nodes - NodeList from querySelectorAll()
1033
+ * @param {nodeListIterator} callback - Callback function to run for each node
1034
+ * @returns {undefined}
1021
1035
  */
1022
1036
  function nodeListForEach (nodes, callback) {
1023
1037
  if (window.NodeList.prototype.forEach) {
@@ -1028,6 +1042,20 @@ function nodeListForEach (nodes, callback) {
1028
1042
  }
1029
1043
  }
1030
1044
 
1045
+ /**
1046
+ * @callback nodeListIterator
1047
+ * @param {Element} value - The current node being iterated on
1048
+ * @param {number} index - The current index in the iteration
1049
+ * @param {NodeListOf<Element>} nodes - NodeList from querySelectorAll()
1050
+ * @returns {undefined}
1051
+ */
1052
+
1053
+ /**
1054
+ * Checkboxes component
1055
+ *
1056
+ * @class
1057
+ * @param {HTMLElement} $module - HTML element to use for checkboxes
1058
+ */
1031
1059
  function Checkboxes ($module) {
1032
1060
  this.$module = $module;
1033
1061
  this.$inputs = $module.querySelectorAll('input[type="checkbox"]');
@@ -1097,7 +1125,7 @@ Checkboxes.prototype.syncAllConditionalReveals = function () {
1097
1125
  * Synchronise the visibility of the conditional reveal, and its accessible
1098
1126
  * state, with the input's checked state.
1099
1127
  *
1100
- * @param {HTMLInputElement} $input Checkbox input
1128
+ * @param {HTMLInputElement} $input - Checkbox input
1101
1129
  */
1102
1130
  Checkboxes.prototype.syncConditionalRevealWithInputState = function ($input) {
1103
1131
  var $target = document.getElementById($input.getAttribute('aria-controls'));
@@ -1155,7 +1183,7 @@ Checkboxes.prototype.unCheckExclusiveInputs = function ($input) {
1155
1183
  * Handle a click within the $module – if the click occurred on a checkbox, sync
1156
1184
  * the state of any associated conditional reveal with the checkbox state.
1157
1185
  *
1158
- * @param {MouseEvent} event Click event
1186
+ * @param {MouseEvent} event - Click event
1159
1187
  */
1160
1188
  Checkboxes.prototype.handleClick = function (event) {
1161
1189
  var $target = event.target;
@@ -660,14 +660,22 @@ if (detect) return
660
660
  .call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
661
661
 
662
662
  /**
663
- * TODO: Ideally this would be a NodeList.prototype.forEach polyfill
664
- * This seems to fail in IE8, requires more investigation.
665
- * See: https://github.com/imagitama/nodelist-foreach-polyfill
663
+ * Common helpers which do not require polyfill.
664
+ *
665
+ * IMPORTANT: If a helper require a polyfill, please isolate it in its own module
666
+ * so that the polyfill can be properly tree-shaken and does not burden
667
+ * the components that do not need that helper
668
+ *
669
+ * @module common/index
666
670
  */
667
671
 
668
- // Used to generate a unique string, allows multiple instances of the component without
669
- // Them conflicting with each other.
670
- // https://stackoverflow.com/a/8809472
672
+ /**
673
+ * Used to generate a unique string, allows multiple instances of the component
674
+ * without them conflicting with each other.
675
+ * https://stackoverflow.com/a/8809472
676
+ *
677
+ * @returns {string} Unique ID
678
+ */
671
679
  function generateUniqueID () {
672
680
  var d = new Date().getTime();
673
681
  if (typeof window.performance !== 'undefined' && typeof window.performance.now === 'function') {
@@ -680,6 +688,14 @@ function generateUniqueID () {
680
688
  })
681
689
  }
682
690
 
691
+ /**
692
+ * @callback nodeListIterator
693
+ * @param {Element} value - The current node being iterated on
694
+ * @param {number} index - The current index in the iteration
695
+ * @param {NodeListOf<Element>} nodes - NodeList from querySelectorAll()
696
+ * @returns {undefined}
697
+ */
698
+
683
699
  /**
684
700
  * JavaScript 'polyfill' for HTML5's <details> and <summary> elements
685
701
  * and 'shim' to add accessiblity enhancements for all browsers
@@ -690,6 +706,12 @@ function generateUniqueID () {
690
706
  var KEY_ENTER = 13;
691
707
  var KEY_SPACE = 32;
692
708
 
709
+ /**
710
+ * Details component
711
+ *
712
+ * @class
713
+ * @param {HTMLElement} $module - HTML element to use for details
714
+ */
693
715
  function Details ($module) {
694
716
  this.$module = $module;
695
717
  }
@@ -744,13 +766,10 @@ Details.prototype.polyfillDetails = function () {
744
766
  $summary.tabIndex = 0;
745
767
 
746
768
  // Detect initial open state
747
- var openAttr = $module.getAttribute('open') !== null;
748
- if (openAttr === true) {
769
+ if (this.$module.hasAttribute('open')) {
749
770
  $summary.setAttribute('aria-expanded', 'true');
750
- $content.setAttribute('aria-hidden', 'false');
751
771
  } else {
752
772
  $summary.setAttribute('aria-expanded', 'false');
753
- $content.setAttribute('aria-hidden', 'true');
754
773
  $content.style.display = 'none';
755
774
  }
756
775
 
@@ -759,37 +778,30 @@ Details.prototype.polyfillDetails = function () {
759
778
  };
760
779
 
761
780
  /**
762
- * Define a statechange function that updates aria-expanded and style.display
763
- * @param {object} summary element
764
- */
781
+ * Define a statechange function that updates aria-expanded and style.display
782
+ *
783
+ * @returns {boolean} Returns true
784
+ */
765
785
  Details.prototype.polyfillSetAttributes = function () {
766
- var $module = this.$module;
767
- var $summary = this.$summary;
768
- var $content = this.$content;
769
-
770
- var expanded = $summary.getAttribute('aria-expanded') === 'true';
771
- var hidden = $content.getAttribute('aria-hidden') === 'true';
772
-
773
- $summary.setAttribute('aria-expanded', (expanded ? 'false' : 'true'));
774
- $content.setAttribute('aria-hidden', (hidden ? 'false' : 'true'));
775
-
776
- $content.style.display = (expanded ? 'none' : '');
777
-
778
- var hasOpenAttr = $module.getAttribute('open') !== null;
779
- if (!hasOpenAttr) {
780
- $module.setAttribute('open', 'open');
786
+ if (this.$module.hasAttribute('open')) {
787
+ this.$module.removeAttribute('open');
788
+ this.$summary.setAttribute('aria-expanded', 'false');
789
+ this.$content.style.display = 'none';
781
790
  } else {
782
- $module.removeAttribute('open');
791
+ this.$module.setAttribute('open', 'open');
792
+ this.$summary.setAttribute('aria-expanded', 'true');
793
+ this.$content.style.display = '';
783
794
  }
784
795
 
785
796
  return true
786
797
  };
787
798
 
788
799
  /**
789
- * Handle cross-modal click events
790
- * @param {object} node element
791
- * @param {function} callback function
792
- */
800
+ * Handle cross-modal click events
801
+ *
802
+ * @param {object} node - element
803
+ * @param {polyfillHandleInputsCallback} callback - function
804
+ */
793
805
  Details.prototype.polyfillHandleInputs = function (node, callback) {
794
806
  node.addEventListener('keypress', function (event) {
795
807
  var target = event.target;
@@ -823,6 +835,12 @@ Details.prototype.polyfillHandleInputs = function (node, callback) {
823
835
  node.addEventListener('click', callback);
824
836
  };
825
837
 
838
+ /**
839
+ * @callback polyfillHandleInputsCallback
840
+ * @param {KeyboardEvent} event - Keyboard event
841
+ * @returns {undefined}
842
+ */
843
+
826
844
  return Details;
827
845
 
828
846
  })));
@@ -706,8 +706,262 @@ if (detect) return
706
706
 
707
707
  }).call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
708
708
 
709
- function ErrorSummary ($module) {
709
+ /**
710
+ * Common helpers which do not require polyfill.
711
+ *
712
+ * IMPORTANT: If a helper require a polyfill, please isolate it in its own module
713
+ * so that the polyfill can be properly tree-shaken and does not burden
714
+ * the components that do not need that helper
715
+ *
716
+ * @module common/index
717
+ */
718
+
719
+ /**
720
+ * Config flattening function
721
+ *
722
+ * Takes any number of objects, flattens them into namespaced key-value pairs,
723
+ * (e.g. {'i18n.showSection': 'Show section'}) and combines them together, with
724
+ * greatest priority on the LAST item passed in.
725
+ *
726
+ * @returns {object} A flattened object of key-value pairs.
727
+ */
728
+ function mergeConfigs (/* configObject1, configObject2, ...configObjects */) {
729
+ /**
730
+ * Function to take nested objects and flatten them to a dot-separated keyed
731
+ * object. Doing this means we don't need to do any deep/recursive merging of
732
+ * each of our objects, nor transform our dataset from a flat list into a
733
+ * nested object.
734
+ *
735
+ * @param {object} configObject - Deeply nested object
736
+ * @returns {object} Flattened object with dot-separated keys
737
+ */
738
+ var flattenObject = function (configObject) {
739
+ // Prepare an empty return object
740
+ var flattenedObject = {};
741
+
742
+ // Our flattening function, this is called recursively for each level of
743
+ // depth in the object. At each level we prepend the previous level names to
744
+ // the key using `prefix`.
745
+ var flattenLoop = function (obj, prefix) {
746
+ // Loop through keys...
747
+ for (var key in obj) {
748
+ // Check to see if this is a prototypical key/value,
749
+ // if it is, skip it.
750
+ if (!Object.prototype.hasOwnProperty.call(obj, key)) {
751
+ continue
752
+ }
753
+ var value = obj[key];
754
+ var prefixedKey = prefix ? prefix + '.' + key : key;
755
+ if (typeof value === 'object') {
756
+ // If the value is a nested object, recurse over that too
757
+ flattenLoop(value, prefixedKey);
758
+ } else {
759
+ // Otherwise, add this value to our return object
760
+ flattenedObject[prefixedKey] = value;
761
+ }
762
+ }
763
+ };
764
+
765
+ // Kick off the recursive loop
766
+ flattenLoop(configObject);
767
+ return flattenedObject
768
+ };
769
+
770
+ // Start with an empty object as our base
771
+ var formattedConfigObject = {};
772
+
773
+ // Loop through each of the remaining passed objects and push their keys
774
+ // one-by-one into configObject. Any duplicate keys will override the existing
775
+ // key with the new value.
776
+ for (var i = 0; i < arguments.length; i++) {
777
+ var obj = flattenObject(arguments[i]);
778
+ for (var key in obj) {
779
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
780
+ formattedConfigObject[key] = obj[key];
781
+ }
782
+ }
783
+ }
784
+
785
+ return formattedConfigObject
786
+ }
787
+
788
+ /**
789
+ * @callback nodeListIterator
790
+ * @param {Element} value - The current node being iterated on
791
+ * @param {number} index - The current index in the iteration
792
+ * @param {NodeListOf<Element>} nodes - NodeList from querySelectorAll()
793
+ * @returns {undefined}
794
+ */
795
+
796
+ (function(undefined) {
797
+
798
+ // Detection from https://raw.githubusercontent.com/Financial-Times/polyfill-library/13cf7c340974d128d557580b5e2dafcd1b1192d1/polyfills/Element/prototype/dataset/detect.js
799
+ var detect = (function(){
800
+ if (!document.documentElement.dataset) {
801
+ return false;
802
+ }
803
+ var el = document.createElement('div');
804
+ el.setAttribute("data-a-b", "c");
805
+ return el.dataset && el.dataset.aB == "c";
806
+ }());
807
+
808
+ if (detect) return
809
+
810
+ // Polyfill derived from https://raw.githubusercontent.com/Financial-Times/polyfill-library/13cf7c340974d128d557580b5e2dafcd1b1192d1/polyfills/Element/prototype/dataset/polyfill.js
811
+ Object.defineProperty(Element.prototype, 'dataset', {
812
+ get: function() {
813
+ var element = this;
814
+ var attributes = this.attributes;
815
+ var map = {};
816
+
817
+ for (var i = 0; i < attributes.length; i++) {
818
+ var attribute = attributes[i];
819
+
820
+ // This regex has been edited from the original polyfill, to add
821
+ // support for period (.) separators in data-* attribute names. These
822
+ // are allowed in the HTML spec, but were not covered by the original
823
+ // polyfill's regex. We use periods in our i18n implementation.
824
+ if (attribute && attribute.name && (/^data-\w[.\w-]*$/).test(attribute.name)) {
825
+ var name = attribute.name;
826
+ var value = attribute.value;
827
+
828
+ var propName = name.substr(5).replace(/-./g, function (prop) {
829
+ return prop.charAt(1).toUpperCase();
830
+ });
831
+
832
+ // If this browser supports __defineGetter__ and __defineSetter__,
833
+ // continue using defineProperty. If not (like IE 8 and below), we use
834
+ // a hacky fallback which at least gives an object in the right format
835
+ if ('__defineGetter__' in Object.prototype && '__defineSetter__' in Object.prototype) {
836
+ Object.defineProperty(map, propName, {
837
+ enumerable: true,
838
+ get: function() {
839
+ return this.value;
840
+ }.bind({value: value || ''}),
841
+ set: function setter(name, value) {
842
+ if (typeof value !== 'undefined') {
843
+ this.setAttribute(name, value);
844
+ } else {
845
+ this.removeAttribute(name);
846
+ }
847
+ }.bind(element, name)
848
+ });
849
+ } else {
850
+ map[propName] = value;
851
+ }
852
+
853
+ }
854
+ }
855
+
856
+ return map;
857
+ }
858
+ });
859
+
860
+ }).call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
861
+
862
+ (function(undefined) {
863
+
864
+ // Detection from https://github.com/mdn/content/blob/cf607d68522cd35ee7670782d3ee3a361eaef2e4/files/en-us/web/javascript/reference/global_objects/string/trim/index.md#polyfill
865
+ var detect = ('trim' in String.prototype);
866
+
867
+ if (detect) return
868
+
869
+ // Polyfill from https://github.com/mdn/content/blob/cf607d68522cd35ee7670782d3ee3a361eaef2e4/files/en-us/web/javascript/reference/global_objects/string/trim/index.md#polyfill
870
+ String.prototype.trim = function () {
871
+ return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
872
+ };
873
+
874
+ }).call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
875
+
876
+ /**
877
+ * Normalise string
878
+ *
879
+ * 'If it looks like a duck, and it quacks like a duck…' 🦆
880
+ *
881
+ * If the passed value looks like a boolean or a number, convert it to a boolean
882
+ * or number.
883
+ *
884
+ * Designed to be used to convert config passed via data attributes (which are
885
+ * always strings) into something sensible.
886
+ *
887
+ * @param {string} value - The value to normalise
888
+ * @returns {string | boolean | number | undefined} Normalised data
889
+ */
890
+ function normaliseString (value) {
891
+ if (typeof value !== 'string') {
892
+ return value
893
+ }
894
+
895
+ var trimmedValue = value.trim();
896
+
897
+ if (trimmedValue === 'true') {
898
+ return true
899
+ }
900
+
901
+ if (trimmedValue === 'false') {
902
+ return false
903
+ }
904
+
905
+ // Empty / whitespace-only strings are considered finite so we need to check
906
+ // the length of the trimmed string as well
907
+ if (trimmedValue.length > 0 && isFinite(trimmedValue)) {
908
+ return Number(trimmedValue)
909
+ }
910
+
911
+ return value
912
+ }
913
+
914
+ /**
915
+ * Normalise dataset
916
+ *
917
+ * Loop over an object and normalise each value using normaliseData function
918
+ *
919
+ * @param {DOMStringMap} dataset - HTML element dataset
920
+ * @returns {Object<string, string | boolean | number | undefined>} Normalised dataset
921
+ */
922
+ function normaliseDataset (dataset) {
923
+ var out = {};
924
+
925
+ for (var key in dataset) {
926
+ out[key] = normaliseString(dataset[key]);
927
+ }
928
+
929
+ return out
930
+ }
931
+
932
+ /**
933
+ * JavaScript enhancements for the ErrorSummary
934
+ *
935
+ * Takes focus on initialisation for accessible announcement, unless disabled in configuration.
936
+ *
937
+ * @class
938
+ * @param {HTMLElement} $module - The element this component controls
939
+ * @param {ErrorSummaryConfig} config - Error summary config
940
+ */
941
+ function ErrorSummary ($module, config) {
942
+ // Some consuming code may not be passing a module,
943
+ // for example if they initialise the component
944
+ // on their own by directly passing the result
945
+ // of `document.querySelector`.
946
+ // To avoid breaking further JavaScript initialisation
947
+ // we need to safeguard against this so things keep
948
+ // working the same now we read the elements data attributes
949
+ if (!$module) {
950
+ // Little safety in case code gets ported as-is
951
+ // into and ES6 class constructor, where the return value matters
952
+ return this
953
+ }
954
+
710
955
  this.$module = $module;
956
+
957
+ var defaultConfig = {
958
+ disableAutoFocus: false
959
+ };
960
+ this.config = mergeConfigs(
961
+ defaultConfig,
962
+ config || {},
963
+ normaliseDataset($module.dataset)
964
+ );
711
965
  }
712
966
 
713
967
  ErrorSummary.prototype.init = function () {
@@ -715,16 +969,37 @@ ErrorSummary.prototype.init = function () {
715
969
  if (!$module) {
716
970
  return
717
971
  }
718
- $module.focus();
719
972
 
973
+ this.setFocus();
720
974
  $module.addEventListener('click', this.handleClick.bind(this));
721
975
  };
722
976
 
723
977
  /**
724
- * Click event handler
725
- *
726
- * @param {MouseEvent} event - Click event
727
- */
978
+ * Focus the error summary
979
+ */
980
+ ErrorSummary.prototype.setFocus = function () {
981
+ var $module = this.$module;
982
+
983
+ if (this.config.disableAutoFocus) {
984
+ return
985
+ }
986
+
987
+ // Set tabindex to -1 to make the element programmatically focusable, but
988
+ // remove it on blur as the error summary doesn't need to be focused again.
989
+ $module.setAttribute('tabindex', '-1');
990
+
991
+ $module.addEventListener('blur', function () {
992
+ $module.removeAttribute('tabindex');
993
+ });
994
+
995
+ $module.focus();
996
+ };
997
+
998
+ /**
999
+ * Click event handler
1000
+ *
1001
+ * @param {MouseEvent} event - Click event
1002
+ */
728
1003
  ErrorSummary.prototype.handleClick = function (event) {
729
1004
  var target = event.target;
730
1005
  if (this.focusTarget(target)) {
@@ -848,6 +1123,14 @@ ErrorSummary.prototype.getAssociatedLegendOrLabel = function ($input) {
848
1123
  $input.closest('label')
849
1124
  };
850
1125
 
1126
+ /**
1127
+ * Error summary config
1128
+ *
1129
+ * @typedef {object} ErrorSummaryConfig
1130
+ * @property {boolean} [disableAutoFocus = false] -
1131
+ * If set to `true` the error summary will not be focussed when the page loads.
1132
+ */
1133
+
851
1134
  return ErrorSummary;
852
1135
 
853
1136
  })));
@@ -8,7 +8,7 @@
8
8
  $govuk-footer-text: $govuk-text-colour;
9
9
  $govuk-footer-link-hover-colour: null; // Only used with the legacy palette
10
10
 
11
- @if ($govuk-use-legacy-palette) {
11
+ @if $govuk-use-legacy-palette {
12
12
  $govuk-footer-border-top: #a1acb2;
13
13
  $govuk-footer-border: govuk-colour("grey-2");
14
14
  $govuk-footer-text: #454a4c;
@@ -35,7 +35,7 @@
35
35
  .govuk-footer__link {
36
36
  @include govuk-link-common;
37
37
 
38
- @if ($govuk-use-legacy-palette) {
38
+ @if $govuk-use-legacy-palette {
39
39
  &:link,
40
40
  &:visited {
41
41
  color: $govuk-footer-text;
@@ -52,7 +52,7 @@
52
52
  // alphagov/govuk_template includes a specific a:link:focus selector
53
53
  // designed to make unvisited links a slightly darker blue when focussed, so
54
54
  // we need to override the text colour for that combination of selectors.
55
- @include govuk-compatibility(govuk_template) {
55
+ @include _govuk-compatibility(govuk_template) {
56
56
  &:link:focus {
57
57
  @include govuk-text-colour;
58
58
  }
@@ -67,23 +67,16 @@
67
67
  }
68
68
 
69
69
  .govuk-footer__meta {
70
- display: -webkit-box;
71
- display: -webkit-flex;
72
70
  display: -ms-flexbox;
73
71
  display: flex; // Support: Flexbox
74
72
  margin-right: -$govuk-gutter-half;
75
73
  margin-left: -$govuk-gutter-half;
76
- -webkit-flex-wrap: wrap;
77
- -ms-flex-wrap: wrap;
78
- flex-wrap: wrap; // Support: Flexbox
79
- -webkit-box-align: end;
80
- -webkit-align-items: flex-end;
81
- -ms-flex-align: end;
82
- align-items: flex-end; // Support: Flexbox
83
- -webkit-box-pack: center;
84
- -webkit-justify-content: center;
85
- -ms-flex-pack: center;
86
- justify-content: center; // Support: Flexbox
74
+ -ms-flex-wrap: wrap;
75
+ flex-wrap: wrap; // Support: Flexbox
76
+ -ms-flex-align: end;
77
+ align-items: flex-end; // Support: Flexbox
78
+ -ms-flex-pack: center;
79
+ justify-content: center; // Support: Flexbox
87
80
  }
88
81
 
89
82
  .govuk-footer__meta-item {
@@ -93,14 +86,11 @@
93
86
  }
94
87
 
95
88
  .govuk-footer__meta-item--grow {
96
- -webkit-box-flex: 1;
97
- -webkit-flex: 1;
98
- -ms-flex: 1;
99
- flex: 1; // Support: Flexbox
89
+ -ms-flex: 1;
90
+ flex: 1; // Support: Flexbox
100
91
  @include govuk-media-query ($until: tablet) {
101
- -webkit-flex-basis: 320px;
102
- -ms-flex-preferred-size: 320px;
103
- flex-basis: 320px; // Support: Flexbox
92
+ -ms-flex-preferred-size: 320px;
93
+ flex-basis: 320px; // Support: Flexbox
104
94
  }
105
95
  }
106
96