@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.
@@ -21,8 +21,8 @@
21
21
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
22
  * SOFTWARE.
23
23
  */
24
- import { logWarn as warn } from '@instructure/console';
25
24
 
25
+ import { logWarn as warn } from '@instructure/console';
26
26
  class ScreenReaderFocusRegion {
27
27
  constructor(element) {
28
28
  let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {
@@ -36,7 +36,6 @@ class ScreenReaderFocusRegion {
36
36
  this._options = void 0;
37
37
  this._observer = null;
38
38
  this._attributes = [];
39
-
40
39
  this.handleDOMMutation = records => {
41
40
  records.forEach(record => {
42
41
  const addedNodes = Array.from(record.addedNodes);
@@ -48,7 +47,6 @@ class ScreenReaderFocusRegion {
48
47
  if (removedNode.tagName.toLowerCase() !== 'iframe') {
49
48
  this.restoreNode(removedNode);
50
49
  }
51
-
52
50
  const iframeBodies = this.parseIframeBodies(removedNode);
53
51
  iframeBodies.forEach(iframeBody => {
54
52
  this.restoreNode(iframeBody);
@@ -56,17 +54,14 @@ class ScreenReaderFocusRegion {
56
54
  });
57
55
  });
58
56
  };
59
-
60
57
  const liveRegion = typeof options.liveRegion === 'function' ? options.liveRegion() : options.liveRegion;
61
58
  this._liveRegion = Array.isArray(liveRegion) ? liveRegion : [liveRegion];
62
59
  this._contextElement = element;
63
60
  this._options = options;
64
61
  }
65
-
66
62
  updateElement(element) {
67
63
  this._contextElement = element;
68
64
  }
69
-
70
65
  muteNode(node) {
71
66
  if (node && node.tagName.toLowerCase() !== 'script') {
72
67
  // When we are trapping screen reader focus on an element that
@@ -79,31 +74,24 @@ class ScreenReaderFocusRegion {
79
74
  ['role', 'aria-label', 'aria-hidden' // this should never happen right?
80
75
  ].forEach(attribute => {
81
76
  const value = node.getAttribute(attribute);
82
-
83
77
  if (value !== null) {
84
78
  this._attributes.push([node, attribute, value]);
85
-
86
79
  node.removeAttribute(attribute);
87
80
  }
88
81
  });
89
-
90
82
  this._observer.observe(node, {
91
83
  childList: true
92
84
  });
93
85
  }
94
86
  }
95
-
96
87
  hideNodes(nodes) {
97
88
  nodes.forEach(node => {
98
89
  var _node$getAttribute;
99
-
100
90
  const ariaLive = typeof node.getAttribute === 'function' && ((_node$getAttribute = node.getAttribute('aria-live')) === null || _node$getAttribute === void 0 ? void 0 : _node$getAttribute.toLowerCase());
101
-
102
91
  if (node && node.nodeType === 1 && node.tagName.toLowerCase() !== 'script' && ariaLive !== 'assertive' && ariaLive !== 'polite' && this._parents.indexOf(node) === -1 && this._nodes.indexOf(node) === -1 && this._liveRegion.indexOf(node) === -1 && !this._contextElement.contains(node)) {
103
92
  if (node.tagName.toLowerCase() !== 'iframe') {
104
93
  this.hideNode(node);
105
94
  }
106
-
107
95
  const iframeBodies = this.parseIframeBodies(node);
108
96
  iframeBodies.forEach(iframeBody => {
109
97
  this.hideNode(iframeBody);
@@ -111,29 +99,22 @@ class ScreenReaderFocusRegion {
111
99
  }
112
100
  });
113
101
  }
114
-
115
102
  hideNode(node) {
116
103
  if (node.getAttribute('aria-hidden') !== 'true') {
117
104
  node.setAttribute('aria-hidden', 'true');
118
-
119
105
  this._nodes.push(node);
120
106
  }
121
107
  }
122
-
123
108
  restoreNode(removedNode) {
124
109
  const index = this._nodes.indexOf(removedNode);
125
-
126
110
  if (index >= 0) {
127
111
  removedNode.removeAttribute('aria-hidden');
128
-
129
112
  this._nodes.splice(index, 1);
130
113
  }
131
114
  }
132
-
133
115
  parseIframeBodies(node) {
134
116
  if (!node) return [];
135
117
  let iframes = [];
136
-
137
118
  if (node.tagName.toLowerCase() === 'iframe') {
138
119
  iframes.push(node);
139
120
  } else {
@@ -141,38 +122,29 @@ class ScreenReaderFocusRegion {
141
122
  iframes = Array.from(node.getElementsByTagName('iframe'));
142
123
  }
143
124
  }
144
-
145
125
  return iframes.map(iframe => {
146
126
  let body = null;
147
-
148
127
  try {
149
128
  body = iframe.contentDocument.body;
150
129
  } catch (e) {
151
130
  warn(false, `[ui-a11y] could not find a document for iframe: ${e} ${iframe}`);
152
131
  }
153
-
154
132
  return body;
155
133
  }).filter(body => body !== null);
156
134
  }
157
-
158
135
  activate() {
159
136
  if (!this._options.shouldContainFocus) {
160
137
  return;
161
138
  }
162
-
163
139
  this._observer = new MutationObserver(this.handleDOMMutation);
164
140
  let node = this._contextElement;
165
-
166
141
  while (node && node.nodeType === Node.ELEMENT_NODE && node.tagName.toLowerCase() !== 'body') {
167
142
  const parent = node.parentElement;
168
-
169
143
  if (parent) {
170
144
  this._parents.push(parent);
171
-
172
145
  this.muteNode(parent);
173
146
  this.hideNodes(Array.prototype.slice.call(parent.childNodes));
174
147
  }
175
-
176
148
  node = node.parentNode; // should never be null, will default to doc element
177
149
  }
178
150
  }
@@ -180,26 +152,19 @@ class ScreenReaderFocusRegion {
180
152
  deactivate() {
181
153
  if (this._observer) {
182
154
  this._observer.disconnect();
183
-
184
155
  this._observer = null;
185
156
  }
186
-
187
157
  this._nodes.forEach(node => {
188
158
  node.removeAttribute('aria-hidden');
189
159
  });
190
-
191
160
  this._nodes = [];
192
-
193
161
  this._attributes.forEach(attribute => {
194
162
  attribute[0].setAttribute(attribute[1], attribute[2] || '');
195
163
  });
196
-
197
164
  this._attributes = [];
198
165
  this._parents = [];
199
166
  }
200
-
201
167
  }
202
-
203
168
  export default ScreenReaderFocusRegion;
204
169
  export {
205
170
  /**
@@ -21,10 +21,10 @@
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 React from 'react';
25
26
  import { matchComponentTypes } from '@instructure/ui-react-utils';
26
27
  import { ScreenReaderContent } from '@instructure/ui-a11y-content';
27
-
28
28
  function hasVisibleChildren(children) {
29
29
  let visible = false;
30
30
  React.Children.forEach(children, child => {
@@ -34,7 +34,6 @@ function hasVisibleChildren(children) {
34
34
  });
35
35
  return visible;
36
36
  }
37
-
38
37
  export default hasVisibleChildren;
39
38
  export {
40
39
  /**
package/es/index.js CHANGED
@@ -21,6 +21,7 @@
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
  export { FocusRegion } from './FocusRegion';
25
26
  export { FocusRegionManager } from './FocusRegionManager';
26
27
  export { hasVisibleChildren } from './hasVisibleChildren';
package/es/scopeTab.js CHANGED
@@ -21,43 +21,39 @@
21
21
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
22
  * SOFTWARE.
23
23
  */
24
- import { findDOMNode, findTabbable, isActiveElement, containsActiveElement, getActiveElement } from '@instructure/ui-dom-utils';
25
24
 
25
+ import { findDOMNode, findTabbable, isActiveElement, containsActiveElement, getActiveElement } from '@instructure/ui-dom-utils';
26
26
  function scopeTab(element, event, onLeavingFinalTabbable) {
27
27
  const node = findDOMNode(element);
28
28
  const tabbable = findTabbable(node);
29
-
30
29
  if (!tabbable.length) {
31
30
  event.preventDefault();
32
31
  return;
33
- } // Account for a changing tabindex of the active element
34
- // (a case that happens with Menu for KO a11y)
35
-
32
+ }
36
33
 
34
+ // Account for a changing tabindex of the active element
35
+ // (a case that happens with Menu for KO a11y)
37
36
  if (containsActiveElement(element)) {
38
37
  const activeElement = getActiveElement();
39
-
40
38
  if (activeElement && tabbable.indexOf(activeElement) === -1) {
41
39
  tabbable.push(activeElement);
42
40
  }
43
41
  }
44
-
45
42
  const finalTabbable = tabbable[event.shiftKey ? 0 : tabbable.length - 1];
46
- const leavingFinalTabbable = isActiveElement(finalTabbable) || // handle immediate shift+tab after opening with mouse
47
- isActiveElement(node) || // already left final tabbable
43
+ const leavingFinalTabbable = isActiveElement(finalTabbable) ||
44
+ // handle immediate shift+tab after opening with mouse
45
+ isActiveElement(node) ||
46
+ // already left final tabbable
48
47
  !containsActiveElement(element);
49
48
  if (!leavingFinalTabbable) return;
50
-
51
49
  if (typeof onLeavingFinalTabbable === 'function') {
52
50
  onLeavingFinalTabbable();
53
51
  return;
54
52
  }
55
-
56
53
  event.preventDefault();
57
54
  const target = tabbable[event.shiftKey ? tabbable.length - 1 : 0];
58
55
  target.focus();
59
56
  }
60
-
61
57
  export default scopeTab;
62
58
  export {
63
59
  /**
@@ -1,30 +1,19 @@
1
1
  "use strict";
2
2
 
3
3
  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
4
-
5
4
  Object.defineProperty(exports, "__esModule", {
6
5
  value: true
7
6
  });
8
7
  exports.default = exports.FocusRegion = void 0;
9
-
10
8
  var _keycode = _interopRequireDefault(require("keycode"));
11
-
12
9
  var _contains = require("@instructure/ui-dom-utils/lib/contains.js");
13
-
14
10
  var _addEventListener = require("@instructure/ui-dom-utils/lib/addEventListener.js");
15
-
16
11
  var _ownerDocument = require("@instructure/ui-dom-utils/lib/ownerDocument.js");
17
-
18
12
  var _findTabbable = require("@instructure/ui-dom-utils/lib/findTabbable.js");
19
-
20
13
  var _uid = require("@instructure/uid");
21
-
22
14
  var _console = require("@instructure/console");
23
-
24
15
  var _ScreenReaderFocusRegion = require("./ScreenReaderFocusRegion");
25
-
26
16
  var _KeyboardFocusRegion = require("./KeyboardFocusRegion");
27
-
28
17
  /*
29
18
  * The MIT License (MIT)
30
19
  *
@@ -48,6 +37,7 @@ var _KeyboardFocusRegion = require("./KeyboardFocusRegion");
48
37
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
49
38
  * SOFTWARE.
50
39
  */
40
+
51
41
  class FocusRegion {
52
42
  constructor(element, options) {
53
43
  this._contextElement = null;
@@ -58,37 +48,30 @@ class FocusRegion {
58
48
  this._id = void 0;
59
49
  this._listeners = [];
60
50
  this._active = false;
61
-
62
51
  this.handleDismiss = (event, documentClick) => {
63
52
  var _this$_options$onDism, _this$_options;
64
-
65
53
  (_this$_options$onDism = (_this$_options = this._options).onDismiss) === null || _this$_options$onDism === void 0 ? void 0 : _this$_options$onDism.call(_this$_options, event, documentClick);
66
54
  };
67
-
68
55
  this.captureDocumentClick = event => {
69
56
  const target = event.target;
70
57
  this._preventCloseOnDocumentClick = event.button !== 0 || (0, _contains.contains)(this._contextElement, target);
71
58
  };
72
-
73
59
  this.handleDocumentClick = event => {
74
60
  if (this._options.shouldCloseOnDocumentClick && !this._preventCloseOnDocumentClick) {
75
61
  this.handleDismiss(event, true);
76
62
  }
77
63
  };
78
-
79
64
  this.handleFrameClick = (event, frame) => {
80
65
  if (!(0, _contains.contains)(this._contextElement, frame)) {
81
66
  // dismiss if frame is not within the region
82
67
  this.handleDismiss(event, true);
83
68
  }
84
69
  };
85
-
86
70
  this.handleKeyUp = event => {
87
71
  if (this._options.shouldCloseOnEscape && event.keyCode === _keycode.default.codes.esc && !event.defaultPrevented) {
88
72
  this.handleDismiss(event);
89
73
  }
90
74
  };
91
-
92
75
  this._options = options || {
93
76
  shouldCloseOnDocumentClick: true,
94
77
  shouldCloseOnEscape: true
@@ -98,54 +81,41 @@ class FocusRegion {
98
81
  this._keyboardFocusRegion = new _KeyboardFocusRegion.KeyboardFocusRegion(element, options);
99
82
  this._id = (0, _uid.uid)();
100
83
  }
101
-
102
84
  updateElement(element, options) {
103
85
  this._contextElement = element;
104
-
105
86
  if (options) {
106
87
  this._options = options;
107
88
  }
108
-
109
89
  if (this._keyboardFocusRegion) {
110
90
  this._keyboardFocusRegion.updateElement(element);
111
91
  }
112
-
113
92
  if (this._screenReaderFocusRegion) {
114
93
  this._screenReaderFocusRegion.updateElement(element);
115
94
  }
116
95
  }
117
-
118
96
  get id() {
119
97
  return this._id;
120
- } // Focused returns when the focus region is active. Checking focused with the active element
121
- // is inconsistent across browsers (Safari/Firefox do not focus elements on click)
122
-
98
+ }
123
99
 
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)
124
102
  get focused() {
125
103
  return this._active;
126
104
  }
127
-
128
105
  get keyboardFocusable() {
129
106
  return ((0, _findTabbable.findTabbable)(this._contextElement) || []).length > 0;
130
107
  }
131
-
132
108
  activate() {
133
109
  if (!this._active) {
134
110
  const doc = (0, _ownerDocument.ownerDocument)(this._contextElement);
135
-
136
111
  this._keyboardFocusRegion.activate();
137
-
138
112
  this._screenReaderFocusRegion.activate();
139
-
140
113
  if (this._options.shouldCloseOnDocumentClick) {
141
114
  this._listeners.push((0, _addEventListener.addEventListener)(doc, 'click', this.captureDocumentClick, true));
142
-
143
115
  this._listeners.push((0, _addEventListener.addEventListener)(doc, 'click', this.handleDocumentClick));
144
-
145
116
  Array.from(doc.getElementsByTagName('iframe')).forEach(el => {
146
117
  // listen for mouseup events on any iframes in the document
147
118
  const frameDoc = el.contentDocument;
148
-
149
119
  if (frameDoc) {
150
120
  this._listeners.push((0, _addEventListener.addEventListener)(frameDoc, 'mouseup', event => {
151
121
  this.handleFrameClick(event, el);
@@ -153,51 +123,37 @@ class FocusRegion {
153
123
  }
154
124
  });
155
125
  }
156
-
157
126
  if (this._options.shouldCloseOnEscape) {
158
127
  this._listeners.push((0, _addEventListener.addEventListener)(doc, 'keyup', this.handleKeyUp));
159
128
  }
160
-
161
129
  this._active = true;
162
130
  }
163
131
  }
164
-
165
132
  deactivate() {
166
133
  let _ref = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {},
167
- _ref$keyboard = _ref.keyboard,
168
- keyboard = _ref$keyboard === void 0 ? true : _ref$keyboard;
169
-
134
+ _ref$keyboard = _ref.keyboard,
135
+ keyboard = _ref$keyboard === void 0 ? true : _ref$keyboard;
170
136
  if (this._active) {
171
137
  this._listeners.forEach(listener => {
172
138
  listener.remove();
173
139
  });
174
-
175
140
  this._listeners = [];
176
-
177
141
  if (keyboard) {
178
142
  this._keyboardFocusRegion.deactivate();
179
143
  }
180
-
181
144
  this._screenReaderFocusRegion.deactivate();
182
-
183
145
  this._active = false;
184
146
  }
185
147
  }
186
-
187
148
  focus() {
188
149
  (0, _console.logError)(this._active, `[FocusRegion] Cannot call '.focus()' on a region that is not currently active.`);
189
-
190
150
  this._keyboardFocusRegion.focus();
191
151
  }
192
-
193
152
  blur() {
194
153
  (0, _console.logError)(!this._active, `[FocusRegion] Cannot call '.blur()' on a region that is currently active.`);
195
-
196
154
  this._keyboardFocusRegion.blur();
197
155
  }
198
-
199
156
  }
200
-
201
157
  exports.FocusRegion = FocusRegion;
202
158
  var _default = FocusRegion;
203
159
  exports.default = _default;
@@ -4,11 +4,8 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = exports.FocusRegionManager = void 0;
7
-
8
7
  var _console = require("@instructure/console");
9
-
10
8
  var _FocusRegion = require("./FocusRegion");
11
-
12
9
  /*
13
10
  * The MIT License (MIT)
14
11
  *
@@ -32,62 +29,49 @@ var _FocusRegion = require("./FocusRegion");
32
29
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33
30
  * SOFTWARE.
34
31
  */
35
- let ENTRIES = [];
36
32
 
33
+ let ENTRIES = [];
37
34
  class FocusRegionManager {}
38
-
39
35
  exports.FocusRegionManager = FocusRegionManager;
40
-
41
36
  FocusRegionManager.focusRegion = function (element) {
42
37
  let idOrOptions = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
43
38
  let entry;
44
-
45
39
  if (typeof idOrOptions === 'string') {
46
40
  entry = FocusRegionManager.getEntry(element, idOrOptions);
47
41
  } else {
48
42
  entry = FocusRegionManager.addEntry(element, idOrOptions);
49
43
  }
50
-
51
44
  if (entry && entry.region && typeof entry.region.focus === 'function') {
52
45
  entry.region.focus();
53
46
  return entry.region;
54
47
  } else {
55
48
  (0, _console.logError)(false, `[FocusRegionManager] Could not focus region with element: ${element}`);
56
49
  }
57
-
58
50
  return;
59
51
  };
60
-
61
52
  FocusRegionManager.activateRegion = (element, options) => {
62
53
  const _FocusRegionManager$a = FocusRegionManager.addEntry(element, options),
63
- region = _FocusRegionManager$a.region;
64
-
54
+ region = _FocusRegionManager$a.region;
65
55
  return region;
66
56
  };
67
-
68
57
  FocusRegionManager.getActiveEntry = () => {
69
58
  return ENTRIES.find(_ref => {
70
59
  let region = _ref.region;
71
60
  return region.focused;
72
61
  });
73
62
  };
74
-
75
63
  FocusRegionManager.findEntry = (element, id) => {
76
64
  let index;
77
-
78
65
  if (id) {
79
66
  index = ENTRIES.findIndex(entry => entry.id === id);
80
67
  } else {
81
68
  index = ENTRIES.findIndex(entry => entry.element === element);
82
69
  }
83
-
84
70
  return index;
85
71
  };
86
-
87
72
  FocusRegionManager.getEntry = (element, id) => {
88
73
  return ENTRIES[FocusRegionManager.findEntry(element, id)];
89
74
  };
90
-
91
75
  FocusRegionManager.addEntry = function (element) {
92
76
  let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
93
77
  const region = new _FocusRegion.FocusRegion(element, options);
@@ -95,7 +79,6 @@ FocusRegionManager.addEntry = function (element) {
95
79
  const keyboardFocusable = region.keyboardFocusable;
96
80
  ENTRIES.forEach(_ref2 => {
97
81
  let region = _ref2.region;
98
-
99
82
  if (region) {
100
83
  // If the active region is triggering a new focus region that does not have
101
84
  // keyboard focusable content, don't deactivate the active region's keyboard
@@ -107,11 +90,9 @@ FocusRegionManager.addEntry = function (element) {
107
90
  }
108
91
  });
109
92
  region.activate();
110
-
111
93
  if (options.shouldFocusOnOpen) {
112
94
  region.focus();
113
95
  }
114
-
115
96
  const entry = {
116
97
  id: region.id,
117
98
  element,
@@ -120,63 +101,54 @@ FocusRegionManager.addEntry = function (element) {
120
101
  parent: activeEntry
121
102
  };
122
103
  ENTRIES.push(entry);
123
-
124
104
  if (activeEntry) {
125
105
  activeEntry.children.push(entry);
126
106
  }
127
-
128
107
  return entry;
129
108
  };
130
-
131
109
  FocusRegionManager.removeEntry = (element, id) => {
132
110
  const index = FocusRegionManager.findEntry(element, id);
133
111
  const entry = ENTRIES[index];
134
-
135
112
  if (index > -1) {
136
113
  ENTRIES.splice(index, 1);
137
114
  }
138
-
139
115
  return entry;
140
116
  };
141
-
142
117
  FocusRegionManager.isFocused = (element, id) => {
143
118
  const entry = FocusRegionManager.getActiveEntry();
144
-
145
119
  if (id) {
146
120
  return entry && entry.region && entry.id === id;
147
121
  } else {
148
122
  return entry && entry.region && entry.element === element;
149
123
  }
150
124
  };
151
-
152
125
  FocusRegionManager.clearEntries = () => {
153
126
  ENTRIES = [];
154
127
  };
155
-
156
128
  FocusRegionManager.blurRegion = (element, id) => {
157
129
  const entry = FocusRegionManager.removeEntry(element, id);
158
-
159
130
  if (entry) {
160
131
  const children = entry.children,
161
- region = entry.region,
162
- parent = entry.parent; // deactivate the region...
132
+ region = entry.region,
133
+ parent = entry.parent;
163
134
 
164
- region && region.deactivate(); // and any regions created from it
135
+ // deactivate the region...
136
+ region && region.deactivate();
165
137
 
138
+ // and any regions created from it
166
139
  if (children) {
167
140
  children.forEach(_ref3 => {
168
141
  let id = _ref3.id,
169
- element = _ref3.element;
142
+ element = _ref3.element;
170
143
  const entry = FocusRegionManager.removeEntry(element, id);
171
144
  entry && entry.region && entry.region.deactivate();
172
145
  });
173
- } // activate the region's parent if it exists
174
-
146
+ }
175
147
 
148
+ // activate the region's parent if it exists
176
149
  parent && parent.region && parent.region.activate();
177
150
  region && region.blur(); // this should focus the parent region
178
151
  }
179
152
  };
180
-
181
153
  var _default = FocusRegionManager;
182
154
  exports.default = _default;