@instructure/ui-a11y-utils 8.33.2-snapshot-0 → 8.33.2

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.
package/CHANGELOG.md CHANGED
@@ -3,14 +3,10 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
- ## [8.33.2-snapshot-0](https://github.com/instructure/instructure-ui/compare/v8.33.1...v8.33.2-snapshot-0) (2023-01-13)
6
+ ## [8.33.2](https://github.com/instructure/instructure-ui/compare/v8.33.1...v8.33.2) (2023-01-25)
7
7
 
8
8
  **Note:** Version bump only for package @instructure/ui-a11y-utils
9
9
 
10
-
11
-
12
-
13
-
14
10
  ## [8.33.1](https://github.com/instructure/instructure-ui/compare/v8.33.0...v8.33.1) (2023-01-06)
15
11
 
16
12
  **Note:** Version bump only for package @instructure/ui-a11y-utils
package/es/FocusRegion.js CHANGED
@@ -21,13 +21,13 @@
21
21
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
22
  * SOFTWARE.
23
23
  */
24
+
24
25
  import keycode from 'keycode';
25
26
  import { contains, addEventListener, ownerDocument, findTabbable } from '@instructure/ui-dom-utils';
26
27
  import { uid } from '@instructure/uid';
27
28
  import { logError as error } from '@instructure/console';
28
29
  import { ScreenReaderFocusRegion } from './ScreenReaderFocusRegion';
29
30
  import { KeyboardFocusRegion } from './KeyboardFocusRegion';
30
-
31
31
  class FocusRegion {
32
32
  constructor(element, options) {
33
33
  this._contextElement = null;
@@ -38,37 +38,30 @@ class FocusRegion {
38
38
  this._id = void 0;
39
39
  this._listeners = [];
40
40
  this._active = false;
41
-
42
41
  this.handleDismiss = (event, documentClick) => {
43
42
  var _this$_options$onDism, _this$_options;
44
-
45
43
  (_this$_options$onDism = (_this$_options = this._options).onDismiss) === null || _this$_options$onDism === void 0 ? void 0 : _this$_options$onDism.call(_this$_options, event, documentClick);
46
44
  };
47
-
48
45
  this.captureDocumentClick = event => {
49
46
  const target = event.target;
50
47
  this._preventCloseOnDocumentClick = event.button !== 0 || contains(this._contextElement, target);
51
48
  };
52
-
53
49
  this.handleDocumentClick = event => {
54
50
  if (this._options.shouldCloseOnDocumentClick && !this._preventCloseOnDocumentClick) {
55
51
  this.handleDismiss(event, true);
56
52
  }
57
53
  };
58
-
59
54
  this.handleFrameClick = (event, frame) => {
60
55
  if (!contains(this._contextElement, frame)) {
61
56
  // dismiss if frame is not within the region
62
57
  this.handleDismiss(event, true);
63
58
  }
64
59
  };
65
-
66
60
  this.handleKeyUp = event => {
67
61
  if (this._options.shouldCloseOnEscape && event.keyCode === keycode.codes.esc && !event.defaultPrevented) {
68
62
  this.handleDismiss(event);
69
63
  }
70
64
  };
71
-
72
65
  this._options = options || {
73
66
  shouldCloseOnDocumentClick: true,
74
67
  shouldCloseOnEscape: true
@@ -78,54 +71,41 @@ class FocusRegion {
78
71
  this._keyboardFocusRegion = new KeyboardFocusRegion(element, options);
79
72
  this._id = uid();
80
73
  }
81
-
82
74
  updateElement(element, options) {
83
75
  this._contextElement = element;
84
-
85
76
  if (options) {
86
77
  this._options = options;
87
78
  }
88
-
89
79
  if (this._keyboardFocusRegion) {
90
80
  this._keyboardFocusRegion.updateElement(element);
91
81
  }
92
-
93
82
  if (this._screenReaderFocusRegion) {
94
83
  this._screenReaderFocusRegion.updateElement(element);
95
84
  }
96
85
  }
97
-
98
86
  get id() {
99
87
  return this._id;
100
- } // Focused returns when the focus region is active. Checking focused with the active element
101
- // is inconsistent across browsers (Safari/Firefox do not focus elements on click)
102
-
88
+ }
103
89
 
90
+ // Focused returns when the focus region is active. Checking focused with the active element
91
+ // is inconsistent across browsers (Safari/Firefox do not focus elements on click)
104
92
  get focused() {
105
93
  return this._active;
106
94
  }
107
-
108
95
  get keyboardFocusable() {
109
96
  return (findTabbable(this._contextElement) || []).length > 0;
110
97
  }
111
-
112
98
  activate() {
113
99
  if (!this._active) {
114
100
  const doc = ownerDocument(this._contextElement);
115
-
116
101
  this._keyboardFocusRegion.activate();
117
-
118
102
  this._screenReaderFocusRegion.activate();
119
-
120
103
  if (this._options.shouldCloseOnDocumentClick) {
121
104
  this._listeners.push(addEventListener(doc, 'click', this.captureDocumentClick, true));
122
-
123
105
  this._listeners.push(addEventListener(doc, 'click', this.handleDocumentClick));
124
-
125
106
  Array.from(doc.getElementsByTagName('iframe')).forEach(el => {
126
107
  // listen for mouseup events on any iframes in the document
127
108
  const frameDoc = el.contentDocument;
128
-
129
109
  if (frameDoc) {
130
110
  this._listeners.push(addEventListener(frameDoc, 'mouseup', event => {
131
111
  this.handleFrameClick(event, el);
@@ -133,51 +113,37 @@ class FocusRegion {
133
113
  }
134
114
  });
135
115
  }
136
-
137
116
  if (this._options.shouldCloseOnEscape) {
138
117
  this._listeners.push(addEventListener(doc, 'keyup', this.handleKeyUp));
139
118
  }
140
-
141
119
  this._active = true;
142
120
  }
143
121
  }
144
-
145
122
  deactivate() {
146
123
  let _ref = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {},
147
- _ref$keyboard = _ref.keyboard,
148
- keyboard = _ref$keyboard === void 0 ? true : _ref$keyboard;
149
-
124
+ _ref$keyboard = _ref.keyboard,
125
+ keyboard = _ref$keyboard === void 0 ? true : _ref$keyboard;
150
126
  if (this._active) {
151
127
  this._listeners.forEach(listener => {
152
128
  listener.remove();
153
129
  });
154
-
155
130
  this._listeners = [];
156
-
157
131
  if (keyboard) {
158
132
  this._keyboardFocusRegion.deactivate();
159
133
  }
160
-
161
134
  this._screenReaderFocusRegion.deactivate();
162
-
163
135
  this._active = false;
164
136
  }
165
137
  }
166
-
167
138
  focus() {
168
139
  error(this._active, `[FocusRegion] Cannot call '.focus()' on a region that is not currently active.`);
169
-
170
140
  this._keyboardFocusRegion.focus();
171
141
  }
172
-
173
142
  blur() {
174
143
  error(!this._active, `[FocusRegion] Cannot call '.blur()' on a region that is currently active.`);
175
-
176
144
  this._keyboardFocusRegion.blur();
177
145
  }
178
-
179
146
  }
180
-
181
147
  export default FocusRegion;
182
148
  export {
183
149
  /**
@@ -21,62 +21,50 @@
21
21
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
22
  * SOFTWARE.
23
23
  */
24
+
24
25
  import { logError as error } from '@instructure/console';
25
26
  import { FocusRegion } from './FocusRegion';
26
27
  let ENTRIES = [];
27
-
28
28
  class FocusRegionManager {}
29
-
30
29
  FocusRegionManager.focusRegion = function (element) {
31
30
  let idOrOptions = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
32
31
  let entry;
33
-
34
32
  if (typeof idOrOptions === 'string') {
35
33
  entry = FocusRegionManager.getEntry(element, idOrOptions);
36
34
  } else {
37
35
  entry = FocusRegionManager.addEntry(element, idOrOptions);
38
36
  }
39
-
40
37
  if (entry && entry.region && typeof entry.region.focus === 'function') {
41
38
  entry.region.focus();
42
39
  return entry.region;
43
40
  } else {
44
41
  error(false, `[FocusRegionManager] Could not focus region with element: ${element}`);
45
42
  }
46
-
47
43
  return;
48
44
  };
49
-
50
45
  FocusRegionManager.activateRegion = (element, options) => {
51
46
  const _FocusRegionManager$a = FocusRegionManager.addEntry(element, options),
52
- region = _FocusRegionManager$a.region;
53
-
47
+ region = _FocusRegionManager$a.region;
54
48
  return region;
55
49
  };
56
-
57
50
  FocusRegionManager.getActiveEntry = () => {
58
51
  return ENTRIES.find(_ref => {
59
52
  let region = _ref.region;
60
53
  return region.focused;
61
54
  });
62
55
  };
63
-
64
56
  FocusRegionManager.findEntry = (element, id) => {
65
57
  let index;
66
-
67
58
  if (id) {
68
59
  index = ENTRIES.findIndex(entry => entry.id === id);
69
60
  } else {
70
61
  index = ENTRIES.findIndex(entry => entry.element === element);
71
62
  }
72
-
73
63
  return index;
74
64
  };
75
-
76
65
  FocusRegionManager.getEntry = (element, id) => {
77
66
  return ENTRIES[FocusRegionManager.findEntry(element, id)];
78
67
  };
79
-
80
68
  FocusRegionManager.addEntry = function (element) {
81
69
  let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
82
70
  const region = new FocusRegion(element, options);
@@ -84,7 +72,6 @@ FocusRegionManager.addEntry = function (element) {
84
72
  const keyboardFocusable = region.keyboardFocusable;
85
73
  ENTRIES.forEach(_ref2 => {
86
74
  let region = _ref2.region;
87
-
88
75
  if (region) {
89
76
  // If the active region is triggering a new focus region that does not have
90
77
  // keyboard focusable content, don't deactivate the active region's keyboard
@@ -96,11 +83,9 @@ FocusRegionManager.addEntry = function (element) {
96
83
  }
97
84
  });
98
85
  region.activate();
99
-
100
86
  if (options.shouldFocusOnOpen) {
101
87
  region.focus();
102
88
  }
103
-
104
89
  const entry = {
105
90
  id: region.id,
106
91
  element,
@@ -109,59 +94,51 @@ FocusRegionManager.addEntry = function (element) {
109
94
  parent: activeEntry
110
95
  };
111
96
  ENTRIES.push(entry);
112
-
113
97
  if (activeEntry) {
114
98
  activeEntry.children.push(entry);
115
99
  }
116
-
117
100
  return entry;
118
101
  };
119
-
120
102
  FocusRegionManager.removeEntry = (element, id) => {
121
103
  const index = FocusRegionManager.findEntry(element, id);
122
104
  const entry = ENTRIES[index];
123
-
124
105
  if (index > -1) {
125
106
  ENTRIES.splice(index, 1);
126
107
  }
127
-
128
108
  return entry;
129
109
  };
130
-
131
110
  FocusRegionManager.isFocused = (element, id) => {
132
111
  const entry = FocusRegionManager.getActiveEntry();
133
-
134
112
  if (id) {
135
113
  return entry && entry.region && entry.id === id;
136
114
  } else {
137
115
  return entry && entry.region && entry.element === element;
138
116
  }
139
117
  };
140
-
141
118
  FocusRegionManager.clearEntries = () => {
142
119
  ENTRIES = [];
143
120
  };
144
-
145
121
  FocusRegionManager.blurRegion = (element, id) => {
146
122
  const entry = FocusRegionManager.removeEntry(element, id);
147
-
148
123
  if (entry) {
149
124
  const children = entry.children,
150
- region = entry.region,
151
- parent = entry.parent; // deactivate the region...
125
+ region = entry.region,
126
+ parent = entry.parent;
152
127
 
153
- region && region.deactivate(); // and any regions created from it
128
+ // deactivate the region...
129
+ region && region.deactivate();
154
130
 
131
+ // and any regions created from it
155
132
  if (children) {
156
133
  children.forEach(_ref3 => {
157
134
  let id = _ref3.id,
158
- element = _ref3.element;
135
+ element = _ref3.element;
159
136
  const entry = FocusRegionManager.removeEntry(element, id);
160
137
  entry && entry.region && entry.region.deactivate();
161
138
  });
162
- } // activate the region's parent if it exists
163
-
139
+ }
164
140
 
141
+ // activate the region's parent if it exists
165
142
  parent && parent.region && parent.region.activate();
166
143
  region && region.blur(); // this should focus the parent region
167
144
  }
@@ -21,11 +21,11 @@
21
21
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
22
  * SOFTWARE.
23
23
  */
24
+
24
25
  import { findDOMNode, findTabbable, findFocusable, ownerWindow, getActiveElement, addEventListener, ownerDocument, containsActiveElement, requestAnimationFrame } from '@instructure/ui-dom-utils';
25
26
  import { logError as error } from '@instructure/console';
26
27
  import keycode from 'keycode';
27
28
  import { scopeTab } from './scopeTab';
28
-
29
29
  class KeyboardFocusRegion {
30
30
  constructor(element, options) {
31
31
  this._options = void 0;
@@ -36,141 +36,110 @@ class KeyboardFocusRegion {
36
36
  this._active = false;
37
37
  this._wasDocumentClick = void 0;
38
38
  this._contextElement = void 0;
39
-
40
39
  this.handleKeyDown = event => {
41
40
  if (event.keyCode === keycode.codes.tab) {
42
41
  scopeTab(this._contextElement, event);
43
42
  }
44
43
  };
45
-
46
44
  this.handleClick = () => {
47
45
  this._wasDocumentClick = true;
48
46
  };
49
-
50
47
  this.handleWindowBlur = () => {
51
48
  if (this._wasDocumentClick) {
52
49
  this._wasDocumentClick = false;
53
50
  return;
54
51
  }
55
-
56
52
  this._needToFocus = true;
57
53
  };
58
-
59
54
  this.handleFocus = () => {
60
55
  if (this._needToFocus) {
61
56
  this._needToFocus = false;
62
-
63
57
  if (!this._contextElement) {
64
58
  return;
65
- } // need to see how jQuery shims document.on('focusin') so we don't need the
59
+ }
60
+ // need to see how jQuery shims document.on('focusin') so we don't need the
66
61
  // setTimeout, firefox doesn't support focusin, if it did, we could focus
67
62
  // the element outside of a setTimeout. Side-effect of this implementation
68
63
  // is that the document.body gets focus, and then we focus our element right
69
64
  // after, seems fine.
70
-
71
-
72
65
  this._raf.push(requestAnimationFrame(() => {
73
66
  if (containsActiveElement(this._contextElement)) {
74
67
  return;
75
68
  }
76
-
77
69
  this.focusDefaultElement();
78
70
  }));
79
71
  }
80
72
  };
81
-
82
73
  this.handleFirstTabbableKeyDown = event => {
83
74
  if (event.keyCode === keycode.codes.tab && event.shiftKey) {
84
75
  var _this$_options$onBlur, _this$_options;
85
-
86
76
  (_this$_options$onBlur = (_this$_options = this._options).onBlur) === null || _this$_options$onBlur === void 0 ? void 0 : _this$_options$onBlur.call(_this$_options, event);
87
77
  }
88
78
  };
89
-
90
79
  this.handleLastTabbableKeyDown = event => {
91
80
  if (event.keyCode === keycode.codes.tab && !event.shiftKey) {
92
81
  var _this$_options$onBlur2, _this$_options2;
93
-
94
82
  (_this$_options$onBlur2 = (_this$_options2 = this._options).onBlur) === null || _this$_options$onBlur2 === void 0 ? void 0 : _this$_options$onBlur2.call(_this$_options2, event);
95
83
  }
96
84
  };
97
-
98
85
  this._contextElement = findDOMNode(element);
99
86
  this._options = options || {
100
87
  shouldContainFocus: true,
101
88
  shouldReturnFocus: true,
102
89
  defaultFocusElement: null
103
90
  };
104
-
105
91
  if (this._options.shouldReturnFocus) {
106
92
  this._focusLaterElement = getActiveElement(this.doc);
107
93
  }
108
94
  }
109
-
110
95
  get focused() {
111
96
  return containsActiveElement(this._contextElement);
112
97
  }
113
-
114
98
  get shouldContainFocus() {
115
99
  const shouldContainFocus = this._options.shouldContainFocus;
116
100
  return shouldContainFocus === true || Array.isArray(shouldContainFocus) && shouldContainFocus.includes('keyboard');
117
101
  }
118
-
119
102
  get focusable() {
120
103
  return findFocusable(this._contextElement, () => true, true) || [];
121
104
  }
122
-
123
105
  get tabbable() {
124
106
  return findTabbable(this._contextElement) || [];
125
107
  }
126
-
127
108
  get firstTabbable() {
128
109
  return this.tabbable[0];
129
110
  }
130
-
131
111
  get lastTabbable() {
132
112
  return this.tabbable.pop();
133
113
  }
134
-
135
114
  get firstFocusable() {
136
115
  return this.focusable[0];
137
116
  }
138
-
139
117
  get lastFocusable() {
140
118
  return this.focusable.pop();
141
119
  }
142
-
143
120
  get doc() {
144
121
  return ownerDocument(this._contextElement);
145
122
  }
146
-
147
123
  get win() {
148
124
  return ownerWindow(this._contextElement);
149
125
  }
150
-
151
126
  get defaultFocusElement() {
152
127
  const defaultFocusElement = this._options.defaultFocusElement;
153
128
  const element = findDOMNode(typeof defaultFocusElement === 'function' ? defaultFocusElement() : defaultFocusElement);
154
-
155
129
  if (element && this._contextElement && this._contextElement.contains(element)) {
156
130
  return element;
157
131
  }
158
-
159
132
  if (this.firstTabbable) {
160
133
  return this.firstTabbable;
161
134
  }
162
-
163
135
  if (this._contextElement && this.focusable.includes(this._contextElement)) {
164
136
  return this._contextElement;
165
137
  }
166
-
167
138
  return null;
168
139
  }
169
-
170
140
  updateElement(element) {
171
141
  this._contextElement = findDOMNode(element);
172
142
  }
173
-
174
143
  focusDefaultElement() {
175
144
  if (this.defaultFocusElement) {
176
145
  ;
@@ -186,22 +155,18 @@ class KeyboardFocusRegion {
186
155
  }
187
156
  }
188
157
  }
189
-
190
158
  focus() {
191
159
  if (this.focused) {
192
160
  return;
193
161
  }
194
-
195
162
  this._raf.push(requestAnimationFrame(() => {
196
163
  this.focusDefaultElement();
197
164
  }));
198
165
  }
199
-
200
166
  blur() {
201
167
  if (this._options.shouldReturnFocus && this._focusLaterElement) {
202
168
  try {
203
169
  ;
204
-
205
170
  this._focusLaterElement.focus();
206
171
  } catch (e) {
207
172
  error(false, `
@@ -209,53 +174,39 @@ class KeyboardFocusRegion {
209
174
  but it is not in the DOM anymore: ${e}
210
175
  `);
211
176
  }
212
-
213
177
  this._focusLaterElement = null;
214
178
  }
215
179
  }
216
-
217
180
  activate() {
218
181
  const defaultFocusElement = this.defaultFocusElement,
219
- shouldContainFocus = this.shouldContainFocus;
220
-
182
+ shouldContainFocus = this.shouldContainFocus;
221
183
  if (!this._active) {
222
184
  if (defaultFocusElement || shouldContainFocus) {
223
185
  if (shouldContainFocus) {
224
186
  this._listeners.push(addEventListener(this.doc, 'keydown', this.handleKeyDown));
225
187
  } else {
226
188
  this._listeners.push(addEventListener(this.firstTabbable || defaultFocusElement, 'keydown', this.handleFirstTabbableKeyDown));
227
-
228
189
  this._listeners.push(addEventListener(this.lastTabbable || defaultFocusElement, 'keydown', this.handleLastTabbableKeyDown));
229
190
  }
230
-
231
191
  this._listeners.push(addEventListener(this.doc, 'click', this.handleClick, true));
232
-
233
192
  this._listeners.push(addEventListener(this.win, 'blur', this.handleWindowBlur, false));
234
-
235
193
  this._listeners.push(addEventListener(this.doc, 'focus', this.handleFocus, true));
236
-
237
194
  this._active = true;
238
195
  }
239
196
  }
240
197
  }
241
-
242
198
  deactivate() {
243
199
  if (this._active) {
244
200
  this._listeners.forEach(listener => {
245
201
  listener.remove();
246
202
  });
247
-
248
203
  this._listeners = [];
249
-
250
204
  this._raf.forEach(request => request.cancel());
251
-
252
205
  this._raf = [];
253
206
  this._active = false;
254
207
  }
255
208
  }
256
-
257
209
  }
258
-
259
210
  export default KeyboardFocusRegion;
260
211
  export {
261
212
  /**