@nationalarchives/frontend 0.20.0 → 0.20.1

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.
@@ -14,6 +14,8 @@ export class ErrorSummary {
14
14
  if (!disableAutoFocus) {
15
15
  this.setFocus(this.$module);
16
16
  }
17
+
18
+ this.$module.addEventListener("click", (event) => this.handleClick(event));
17
19
  }
18
20
 
19
21
  setFocus($element, options = {}) {
@@ -48,4 +50,110 @@ export class ErrorSummary {
48
50
  options.onBeforeFocus?.call($element);
49
51
  $element.focus();
50
52
  }
53
+
54
+ handleClick(event) {
55
+ const $target = event.target;
56
+ if ($target && this.focusTarget($target)) {
57
+ event.preventDefault();
58
+ }
59
+ }
60
+
61
+ getFragmentFromUrl(url) {
62
+ if (!url.includes("#")) {
63
+ return undefined;
64
+ }
65
+
66
+ return url.split("#").pop();
67
+ }
68
+
69
+ focusTarget($target) {
70
+ // If the element that was clicked was not a link, return early
71
+ if (!($target instanceof HTMLAnchorElement)) {
72
+ return false;
73
+ }
74
+
75
+ const inputId = this.getFragmentFromUrl($target.href);
76
+ if (!inputId) {
77
+ return false;
78
+ }
79
+
80
+ const $input = document.getElementById(inputId);
81
+ if (!$input) {
82
+ return false;
83
+ }
84
+
85
+ const $legendOrLabel = this.getAssociatedLegendOrLabel($input);
86
+ if (!$legendOrLabel) {
87
+ return false;
88
+ }
89
+
90
+ // Scroll the legend or label into view *before* calling focus on the input
91
+ // to avoid extra scrolling in browsers that don't support `preventScroll`
92
+ // (which at time of writing is most of them...)
93
+ $legendOrLabel.scrollIntoView();
94
+ $input.focus({ preventScroll: true });
95
+
96
+ return true;
97
+ }
98
+
99
+ /**
100
+ * Get associated legend or label
101
+ *
102
+ * Returns the first element that exists from this list:
103
+ *
104
+ * - The `<legend>` associated with the closest `<fieldset>` ancestor, as long
105
+ * as the top of it is no more than half a viewport height away from the
106
+ * bottom of the input
107
+ * - The first `<label>` that is associated with the input using for="inputId"
108
+ * - The closest parent `<label>`
109
+ *
110
+ * @private
111
+ * @param {Element} $input - The input
112
+ * @returns {Element | null} Associated legend or label, or null if no
113
+ * associated legend or label can be found
114
+ */
115
+ getAssociatedLegendOrLabel($input) {
116
+ const $fieldset = $input.closest("fieldset");
117
+
118
+ if ($fieldset) {
119
+ const $legends = $fieldset.getElementsByTagName("legend");
120
+
121
+ if ($legends.length) {
122
+ const $candidateLegend = $legends[0];
123
+
124
+ // If the input type is radio or checkbox, always use the legend if
125
+ // there is one.
126
+ if (
127
+ $input instanceof HTMLInputElement &&
128
+ ($input.type === "checkbox" || $input.type === "radio")
129
+ ) {
130
+ return $candidateLegend;
131
+ }
132
+
133
+ // For other input types, only scroll to the fieldset’s legend (instead
134
+ // of the label associated with the input) if the input would end up in
135
+ // the top half of the screen.
136
+ //
137
+ // This should avoid situations where the input either ends up off the
138
+ // screen, or obscured by a software keyboard.
139
+ const legendTop = $candidateLegend.getBoundingClientRect().top;
140
+ const inputRect = $input.getBoundingClientRect();
141
+
142
+ // If the browser doesn't support Element.getBoundingClientRect().height
143
+ // or window.innerHeight (like IE8), bail and just link to the label.
144
+ if (inputRect.height && window.innerHeight) {
145
+ const inputBottom = inputRect.top + inputRect.height;
146
+
147
+ if (inputBottom - legendTop < window.innerHeight / 2) {
148
+ return $candidateLegend;
149
+ }
150
+ }
151
+ }
152
+ }
153
+
154
+ return (
155
+ document.querySelector(`label[for='${$input.getAttribute("id")}']`) ??
156
+ $input.closest("label")
157
+ );
158
+ }
51
159
  }
@@ -109,7 +109,7 @@
109
109
  "id": "text",
110
110
  "name": "text",
111
111
  "size": "m",
112
- "spellcheck": "true"
112
+ "spellcheck": true
113
113
  },
114
114
  "html": "<div class=\"tna-form__group\" data-module=\"tna-text-input\"><div class=\"tna-form__group-contents\"><h4 class=\"tna-form__heading tna-form__heading--m\"><label class=\"tna-form__label\" for=\"text\">Enter some text</label></h4></div><input id=\"text\" class=\"tna-text-input tna-text-input--m \" name=\"text\" value=\"\" type=\"text\" spellcheck=\"true\" autocapitalize=\"off\" autocorrect=\"off\"></div>"
115
115
  },
@@ -122,9 +122,9 @@
122
122
  "id": "text",
123
123
  "name": "text",
124
124
  "size": "m",
125
- "autocapitalize": "true"
125
+ "autocapitalize": "on"
126
126
  },
127
- "html": "<div class=\"tna-form__group\" data-module=\"tna-text-input\"><div class=\"tna-form__group-contents\"><h4 class=\"tna-form__heading tna-form__heading--m\"><label class=\"tna-form__label\" for=\"text\">Enter some text</label></h4></div><input id=\"text\" class=\"tna-text-input tna-text-input--m \" name=\"text\" value=\"\" type=\"text\" spellcheck=\"false\" autocapitalize=\"true\" autocorrect=\"off\"></div>"
127
+ "html": "<div class=\"tna-form__group\" data-module=\"tna-text-input\"><div class=\"tna-form__group-contents\"><h4 class=\"tna-form__heading tna-form__heading--m\"><label class=\"tna-form__label\" for=\"text\">Enter some text</label></h4></div><input id=\"text\" class=\"tna-text-input tna-text-input--m \" name=\"text\" value=\"\" type=\"text\" spellcheck=\"false\" autocapitalize=\"on\" autocorrect=\"off\"></div>"
128
128
  },
129
129
  {
130
130
  "name": "autocorrect",
@@ -135,7 +135,7 @@
135
135
  "id": "text",
136
136
  "name": "text",
137
137
  "size": "m",
138
- "autocorrect": "true"
138
+ "autocorrect": true
139
139
  },
140
140
  "html": "<div class=\"tna-form__group\" data-module=\"tna-text-input\"><div class=\"tna-form__group-contents\"><h4 class=\"tna-form__heading tna-form__heading--m\"><label class=\"tna-form__label\" for=\"text\">Enter some text</label></h4></div><input id=\"text\" class=\"tna-text-input tna-text-input--m \" name=\"text\" value=\"\" type=\"text\" spellcheck=\"false\" autocapitalize=\"off\" autocorrect=\"on\"></div>"
141
141
  },
@@ -106,9 +106,9 @@
106
106
  "headingSize": "m",
107
107
  "id": "feedback",
108
108
  "name": "feedback",
109
- "autocapitalize": true
109
+ "autocapitalize": "on"
110
110
  },
111
- "html": "<div class=\"tna-form__group\"><div class=\"tna-form__group-contents\"><h4 class=\"tna-form__heading tna-form__heading--m\"><label class=\"tna-form__label\" for=\"feedback\">Enter your feedback</label></h4></div><textarea id=\"feedback\" class=\"tna-textarea \" name=\"feedback\" spellcheck=\"false\" autocapitalize=\"true\" autocorrect=\"off\" rows=\"5\"></textarea></div>"
111
+ "html": "<div class=\"tna-form__group\"><div class=\"tna-form__group-contents\"><h4 class=\"tna-form__heading tna-form__heading--m\"><label class=\"tna-form__label\" for=\"feedback\">Enter your feedback</label></h4></div><textarea id=\"feedback\" class=\"tna-textarea \" name=\"feedback\" spellcheck=\"false\" autocapitalize=\"on\" autocorrect=\"off\" rows=\"5\"></textarea></div>"
112
112
  },
113
113
  {
114
114
  "name": "autocorrect",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nationalarchives/frontend",
3
- "version": "0.20.0",
3
+ "version": "0.20.1",
4
4
  "description": "The National Archives frontend styles",
5
5
  "scripts": {
6
6
  "start": "storybook dev -p 6006",