govuk_tech_docs 3.2.1 → 3.3.0

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.

Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/publish.yaml +1 -1
  3. data/CHANGELOG.md +20 -7
  4. data/README.md +2 -2
  5. data/lib/govuk_tech_docs/contribution_banner.rb +1 -1
  6. data/lib/govuk_tech_docs/version.rb +1 -1
  7. data/lib/source/layouts/core.erb +1 -1
  8. data/node_modules/govuk-frontend/govuk/all.js +1548 -311
  9. data/node_modules/govuk-frontend/govuk/common/closest-attribute-value.js +70 -0
  10. data/node_modules/govuk-frontend/govuk/common/index.js +172 -0
  11. data/node_modules/govuk-frontend/govuk/common/normalise-dataset.js +373 -0
  12. data/node_modules/govuk-frontend/govuk/common.js +138 -3
  13. data/node_modules/govuk-frontend/govuk/components/_all.scss +1 -0
  14. data/node_modules/govuk-frontend/govuk/components/accordion/_index.scss +5 -6
  15. data/node_modules/govuk-frontend/govuk/components/accordion/accordion.js +754 -36
  16. data/node_modules/govuk-frontend/govuk/components/breadcrumbs/_index.scss +0 -2
  17. data/node_modules/govuk-frontend/govuk/components/button/_index.scss +29 -21
  18. data/node_modules/govuk-frontend/govuk/components/button/button.js +365 -107
  19. data/node_modules/govuk-frontend/govuk/components/character-count/_index.scss +9 -0
  20. data/node_modules/govuk-frontend/govuk/components/character-count/character-count.js +1092 -109
  21. data/node_modules/govuk-frontend/govuk/components/checkboxes/_index.scss +3 -2
  22. data/node_modules/govuk-frontend/govuk/components/checkboxes/checkboxes.js +30 -2
  23. data/node_modules/govuk-frontend/govuk/components/details/details.js +51 -33
  24. data/node_modules/govuk-frontend/govuk/components/error-summary/error-summary.js +289 -6
  25. data/node_modules/govuk-frontend/govuk/components/footer/_index.scss +13 -23
  26. data/node_modules/govuk-frontend/govuk/components/header/_index.scss +30 -24
  27. data/node_modules/govuk-frontend/govuk/components/header/header.js +59 -11
  28. data/node_modules/govuk-frontend/govuk/components/input/_index.scss +13 -23
  29. data/node_modules/govuk-frontend/govuk/components/notification-banner/notification-banner.js +252 -2
  30. data/node_modules/govuk-frontend/govuk/components/pagination/_index.scss +247 -0
  31. data/node_modules/govuk-frontend/govuk/components/pagination/_pagination.scss +2 -0
  32. data/node_modules/govuk-frontend/govuk/components/panel/_index.scss +1 -1
  33. data/node_modules/govuk-frontend/govuk/components/radios/_index.scss +5 -12
  34. data/node_modules/govuk-frontend/govuk/components/radios/radios.js +30 -2
  35. data/node_modules/govuk-frontend/govuk/components/select/_index.scss +11 -0
  36. data/node_modules/govuk-frontend/govuk/components/skip-link/_index.scss +1 -3
  37. data/node_modules/govuk-frontend/govuk/components/skip-link/skip-link.js +10 -4
  38. data/node_modules/govuk-frontend/govuk/components/summary-list/_index.scss +45 -13
  39. data/node_modules/govuk-frontend/govuk/components/table/_index.scss +1 -1
  40. data/node_modules/govuk-frontend/govuk/components/tabs/tabs.js +28 -0
  41. data/node_modules/govuk-frontend/govuk/core/_section-break.scss +1 -1
  42. data/node_modules/govuk-frontend/govuk/helpers/_colour.scss +5 -5
  43. data/node_modules/govuk-frontend/govuk/helpers/_focused.scss +5 -0
  44. data/node_modules/govuk-frontend/govuk/helpers/_links.scss +13 -11
  45. data/node_modules/govuk-frontend/govuk/helpers/_media-queries.scss +2 -2
  46. data/node_modules/govuk-frontend/govuk/helpers/_shape-arrow.scss +1 -1
  47. data/node_modules/govuk-frontend/govuk/helpers/_spacing.scss +3 -3
  48. data/node_modules/govuk-frontend/govuk/helpers/_typography.scss +16 -9
  49. data/node_modules/govuk-frontend/govuk/i18n.js +390 -0
  50. data/node_modules/govuk-frontend/govuk/objects/_button-group.scss +10 -26
  51. data/node_modules/govuk-frontend/govuk/objects/_template.scss +1 -1
  52. data/node_modules/govuk-frontend/govuk/objects/_width-container.scss +0 -4
  53. data/node_modules/govuk-frontend/govuk/overrides/_spacing.scss +56 -12
  54. data/node_modules/govuk-frontend/govuk/settings/_all.scss +1 -0
  55. data/node_modules/govuk-frontend/govuk/settings/_colours-palette.scss +12 -0
  56. data/node_modules/govuk-frontend/govuk/settings/_compatibility.scss +26 -0
  57. data/node_modules/govuk-frontend/govuk/settings/_spacing.scss +4 -8
  58. data/node_modules/govuk-frontend/govuk/settings/_typography-font.scss +23 -0
  59. data/node_modules/govuk-frontend/govuk/settings/_typography-responsive.scss +12 -0
  60. data/node_modules/govuk-frontend/govuk/settings/_warnings.scss +53 -0
  61. data/node_modules/govuk-frontend/govuk/tools/_compatibility.scss +20 -6
  62. data/node_modules/govuk-frontend/govuk/tools/_exports.scss +1 -1
  63. data/node_modules/govuk-frontend/govuk/tools/_font-url.scss +1 -1
  64. data/node_modules/govuk-frontend/govuk/tools/_image-url.scss +1 -1
  65. data/node_modules/govuk-frontend/govuk/tools/_px-to-em.scss +2 -2
  66. data/node_modules/govuk-frontend/govuk/tools/_px-to-rem.scss +1 -1
  67. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Date/now.js +21 -0
  68. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/dataset.js +300 -0
  69. data/node_modules/govuk-frontend/govuk/vendor/polyfills/String/prototype/trim.js +21 -0
  70. data/node_modules/govuk-frontend/govuk-prototype-kit/init.js +7 -0
  71. data/node_modules/govuk-frontend/govuk-prototype-kit/init.scss +12 -0
  72. data/package-lock.json +12 -12
  73. data/package.json +1 -1
  74. metadata +14 -2
@@ -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