@instructure/ui-a11y-utils 11.7.8 → 11.7.9-snapshot-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.
- package/CHANGELOG.md +8 -0
- package/lib/FocusRegion.js +11 -15
- package/lib/KeyboardFocusRegion.js +21 -29
- package/lib/hasVisibleChildren.js +3 -3
- package/lib/scopeTab.js +6 -9
- package/package.json +9 -9
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
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
|
+
## [11.7.9-snapshot-1](https://github.com/instructure/instructure-ui/compare/v11.7.8...v11.7.9-snapshot-1) (2026-10-06)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @instructure/ui-a11y-utils
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
6
14
|
## [11.7.8](https://github.com/instructure/instructure-ui/compare/v11.7.7...v11.7.8) (2026-10-02)
|
|
7
15
|
|
|
8
16
|
**Note:** Version bump only for package @instructure/ui-a11y-utils
|
package/lib/FocusRegion.js
CHANGED
|
@@ -6,11 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
});
|
|
7
7
|
exports.default = exports.FocusRegion = void 0;
|
|
8
8
|
var _keycode = _interopRequireDefault(require("keycode"));
|
|
9
|
-
var
|
|
10
|
-
var _addEventListener = require("@instructure/ui-dom-utils/lib/addEventListener.js");
|
|
11
|
-
var _ownerDocument = require("@instructure/ui-dom-utils/lib/ownerDocument.js");
|
|
12
|
-
var _findTabbable = require("@instructure/ui-dom-utils/lib/findTabbable.js");
|
|
13
|
-
var _canUseDOM = require("@instructure/ui-dom-utils/lib/canUseDOM.js");
|
|
9
|
+
var _uiDomUtils = require("@instructure/ui-dom-utils");
|
|
14
10
|
var _uid = require("@instructure/uid");
|
|
15
11
|
var _console = require("@instructure/console");
|
|
16
12
|
var _ScreenReaderFocusRegion = require("./ScreenReaderFocusRegion.js");
|
|
@@ -97,7 +93,7 @@ class FocusRegion {
|
|
|
97
93
|
};
|
|
98
94
|
captureDocumentMousedown = event => {
|
|
99
95
|
this._documentClickTarget = event.target;
|
|
100
|
-
this._contextContainsTarget = (0,
|
|
96
|
+
this._contextContainsTarget = (0, _uiDomUtils.contains)(this._contextElement, this._documentClickTarget);
|
|
101
97
|
// Preemptively remove aria-hidden before focus moves to the clicked element
|
|
102
98
|
// outside the region, preventing the "aria-hidden on focused ancestor" warning.
|
|
103
99
|
if (!this._contextContainsTarget && this._options.shouldCloseOnDocumentClick) {
|
|
@@ -112,12 +108,12 @@ class FocusRegion {
|
|
|
112
108
|
// if event.detail is 0 then this is a keyboard and not a mouse press
|
|
113
109
|
!this._contextContainsTarget &&
|
|
114
110
|
//this prevents clicking on Tooltip from closing the parent dialog
|
|
115
|
-
!(
|
|
111
|
+
!(_uiDomUtils.canUseDOM && this._documentClickTarget instanceof HTMLElement && this._documentClickTarget.closest('[role="tooltip"]'))) {
|
|
116
112
|
this.handleDismiss(event, true);
|
|
117
113
|
}
|
|
118
114
|
};
|
|
119
115
|
handleFrameClick = (event, frame) => {
|
|
120
|
-
if (!(0,
|
|
116
|
+
if (!(0, _uiDomUtils.contains)(this._contextElement, frame)) {
|
|
121
117
|
// dismiss if frame is not within the region
|
|
122
118
|
this.handleDismiss(event, true);
|
|
123
119
|
}
|
|
@@ -127,7 +123,7 @@ class FocusRegion {
|
|
|
127
123
|
// If a dialog contains an <input type="file"/> element and the user opens the file picker and closes it with the
|
|
128
124
|
// escape key then Firefox passes through that event which could close the parent dialog. This code prevents that
|
|
129
125
|
// from happening (listening for a `cancel` event doesn't seem to work in firefox)
|
|
130
|
-
const activeElement = (0,
|
|
126
|
+
const activeElement = (0, _uiDomUtils.ownerDocument)(this._contextElement)?.activeElement;
|
|
131
127
|
const fileInputFocused = activeElement?.tagName === 'INPUT' && activeElement.type === 'file';
|
|
132
128
|
if (fileInputFocused) {
|
|
133
129
|
;
|
|
@@ -151,7 +147,7 @@ class FocusRegion {
|
|
|
151
147
|
return this._active;
|
|
152
148
|
}
|
|
153
149
|
get keyboardFocusable() {
|
|
154
|
-
return ((0,
|
|
150
|
+
return ((0, _uiDomUtils.findTabbable)(this._contextElement) || []).length > 0;
|
|
155
151
|
}
|
|
156
152
|
|
|
157
153
|
/**
|
|
@@ -160,19 +156,19 @@ class FocusRegion {
|
|
|
160
156
|
* @param shouldCloseOnEscape Should the region be closed if ESC is pressed?
|
|
161
157
|
*/
|
|
162
158
|
addOrRemoveListeners(shouldCloseOnDocumentClick, shouldCloseOnEscape) {
|
|
163
|
-
const doc = (0,
|
|
159
|
+
const doc = (0, _uiDomUtils.ownerDocument)(this._contextElement);
|
|
164
160
|
if (shouldCloseOnDocumentClick) {
|
|
165
161
|
if (!this._mouseDownListener) {
|
|
166
|
-
this._mouseDownListener = (0,
|
|
162
|
+
this._mouseDownListener = (0, _uiDomUtils.addEventListener)(doc, 'mousedown', this.captureDocumentMousedown);
|
|
167
163
|
}
|
|
168
164
|
if (!this._clickListener) {
|
|
169
|
-
this._clickListener = (0,
|
|
165
|
+
this._clickListener = (0, _uiDomUtils.addEventListener)(doc, 'click', this.handleDocumentClick);
|
|
170
166
|
}
|
|
171
167
|
Array.from(doc.getElementsByTagName('iframe')).forEach(el => {
|
|
172
168
|
// listen for mouseup events on any iframes in the document
|
|
173
169
|
const frameDoc = el.contentDocument;
|
|
174
170
|
if (frameDoc && !this._mouseUpListener) {
|
|
175
|
-
this._mouseUpListener = (0,
|
|
171
|
+
this._mouseUpListener = (0, _uiDomUtils.addEventListener)(frameDoc, 'mouseup', event => {
|
|
176
172
|
this.handleFrameClick(event, el);
|
|
177
173
|
});
|
|
178
174
|
}
|
|
@@ -191,7 +187,7 @@ class FocusRegion {
|
|
|
191
187
|
//because listeners in the capturing phase are called before other event listeners (like that of the Modal's Escape listener)
|
|
192
188
|
//so a Modal with a Tooltip will not get closed when closing the Tooltip with Escape
|
|
193
189
|
const useCapture = this._options.isTooltip;
|
|
194
|
-
this._keyUpListener = (0,
|
|
190
|
+
this._keyUpListener = (0, _uiDomUtils.addEventListener)(doc, 'keyup', this.handleKeyUp, useCapture);
|
|
195
191
|
}
|
|
196
192
|
} else {
|
|
197
193
|
this._keyUpListener?.remove();
|
|
@@ -5,15 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
7
|
exports.default = exports.KeyboardFocusRegion = void 0;
|
|
8
|
-
var
|
|
9
|
-
var _findTabbable = require("@instructure/ui-dom-utils/lib/findTabbable.js");
|
|
10
|
-
var _findFocusable = require("@instructure/ui-dom-utils/lib/findFocusable.js");
|
|
11
|
-
var _ownerWindow = require("@instructure/ui-dom-utils/lib/ownerWindow.js");
|
|
12
|
-
var _getActiveElement = require("@instructure/ui-dom-utils/lib/getActiveElement.js");
|
|
13
|
-
var _addEventListener = require("@instructure/ui-dom-utils/lib/addEventListener.js");
|
|
14
|
-
var _ownerDocument = require("@instructure/ui-dom-utils/lib/ownerDocument.js");
|
|
15
|
-
var _containsActiveElement = require("@instructure/ui-dom-utils/lib/containsActiveElement.js");
|
|
16
|
-
var _requestAnimationFrame = require("@instructure/ui-dom-utils/lib/requestAnimationFrame.js");
|
|
8
|
+
var _uiDomUtils = require("@instructure/ui-dom-utils");
|
|
17
9
|
var _console = require("@instructure/console");
|
|
18
10
|
var _keycode = _interopRequireDefault(require("keycode"));
|
|
19
11
|
var _scopeTab = require("./scopeTab.js");
|
|
@@ -62,18 +54,18 @@ class KeyboardFocusRegion {
|
|
|
62
54
|
_wasDocumentClick;
|
|
63
55
|
_contextElement;
|
|
64
56
|
constructor(element, options) {
|
|
65
|
-
this._contextElement = (0,
|
|
57
|
+
this._contextElement = (0, _uiDomUtils.findDOMNode)(element);
|
|
66
58
|
this._options = options || {
|
|
67
59
|
shouldContainFocus: true,
|
|
68
60
|
shouldReturnFocus: true,
|
|
69
61
|
defaultFocusElement: null
|
|
70
62
|
};
|
|
71
63
|
if (this._options.shouldReturnFocus) {
|
|
72
|
-
this._focusLaterElement = (0,
|
|
64
|
+
this._focusLaterElement = (0, _uiDomUtils.getActiveElement)(this.doc);
|
|
73
65
|
}
|
|
74
66
|
}
|
|
75
67
|
get focused() {
|
|
76
|
-
return (0,
|
|
68
|
+
return (0, _uiDomUtils.containsActiveElement)(this._contextElement);
|
|
77
69
|
}
|
|
78
70
|
get shouldContainFocus() {
|
|
79
71
|
const {
|
|
@@ -82,10 +74,10 @@ class KeyboardFocusRegion {
|
|
|
82
74
|
return shouldContainFocus === true || Array.isArray(shouldContainFocus) && shouldContainFocus.includes('keyboard');
|
|
83
75
|
}
|
|
84
76
|
get focusable() {
|
|
85
|
-
return (0,
|
|
77
|
+
return (0, _uiDomUtils.findFocusable)(this._contextElement, () => true, true) || [];
|
|
86
78
|
}
|
|
87
79
|
get tabbable() {
|
|
88
|
-
return (0,
|
|
80
|
+
return (0, _uiDomUtils.findTabbable)(this._contextElement) || [];
|
|
89
81
|
}
|
|
90
82
|
get firstTabbable() {
|
|
91
83
|
return this.tabbable[0];
|
|
@@ -100,16 +92,16 @@ class KeyboardFocusRegion {
|
|
|
100
92
|
return this.focusable.pop();
|
|
101
93
|
}
|
|
102
94
|
get doc() {
|
|
103
|
-
return (0,
|
|
95
|
+
return (0, _uiDomUtils.ownerDocument)(this._contextElement);
|
|
104
96
|
}
|
|
105
97
|
get win() {
|
|
106
|
-
return (0,
|
|
98
|
+
return (0, _uiDomUtils.ownerWindow)(this._contextElement);
|
|
107
99
|
}
|
|
108
100
|
get defaultFocusElement() {
|
|
109
101
|
const {
|
|
110
102
|
defaultFocusElement
|
|
111
103
|
} = this._options;
|
|
112
|
-
const element = (0,
|
|
104
|
+
const element = (0, _uiDomUtils.findDOMNode)(typeof defaultFocusElement === 'function' ? defaultFocusElement() : defaultFocusElement);
|
|
113
105
|
if (element && this._contextElement && this._contextElement.contains(element)) {
|
|
114
106
|
return element;
|
|
115
107
|
}
|
|
@@ -122,7 +114,7 @@ class KeyboardFocusRegion {
|
|
|
122
114
|
return null;
|
|
123
115
|
}
|
|
124
116
|
updateElement(element) {
|
|
125
|
-
this._contextElement = (0,
|
|
117
|
+
this._contextElement = (0, _uiDomUtils.findDOMNode)(element);
|
|
126
118
|
}
|
|
127
119
|
focusDefaultElement() {
|
|
128
120
|
if (this.defaultFocusElement) {
|
|
@@ -136,7 +128,7 @@ class KeyboardFocusRegion {
|
|
|
136
128
|
} else {
|
|
137
129
|
if (this.shouldContainFocus) {
|
|
138
130
|
// Blur the active element to place focus on the document body
|
|
139
|
-
(0,
|
|
131
|
+
(0, _uiDomUtils.getActiveElement)(this.doc) && (0, _uiDomUtils.getActiveElement)(this.doc).blur();
|
|
140
132
|
(0, _console.logError)(true, `
|
|
141
133
|
[KeyboardFocusRegion] No \`defaultFocusElement\` was provided and
|
|
142
134
|
\`shouldContainFocus\` was set to \`true\` or \`keyboard\`. Focus has
|
|
@@ -161,7 +153,7 @@ class KeyboardFocusRegion {
|
|
|
161
153
|
if (!isHiddenOutside) {
|
|
162
154
|
focusElement.focus();
|
|
163
155
|
} else {
|
|
164
|
-
this._raf.push((0,
|
|
156
|
+
this._raf.push((0, _uiDomUtils.requestAnimationFrame)(() => {
|
|
165
157
|
this.tryFocusOnPositionedElement(positionContainer, focusElement, tries - 1);
|
|
166
158
|
}));
|
|
167
159
|
}
|
|
@@ -170,7 +162,7 @@ class KeyboardFocusRegion {
|
|
|
170
162
|
if (this.focused) {
|
|
171
163
|
return;
|
|
172
164
|
}
|
|
173
|
-
this._raf.push((0,
|
|
165
|
+
this._raf.push((0, _uiDomUtils.requestAnimationFrame)(() => {
|
|
174
166
|
this.focusDefaultElement();
|
|
175
167
|
}));
|
|
176
168
|
}
|
|
@@ -214,8 +206,8 @@ class KeyboardFocusRegion {
|
|
|
214
206
|
// the element outside of a setTimeout. Side-effect of this implementation
|
|
215
207
|
// is that the document.body gets focus, and then we focus our element right
|
|
216
208
|
// after, seems fine.
|
|
217
|
-
this._raf.push((0,
|
|
218
|
-
if ((0,
|
|
209
|
+
this._raf.push((0, _uiDomUtils.requestAnimationFrame)(() => {
|
|
210
|
+
if ((0, _uiDomUtils.containsActiveElement)(this._contextElement)) {
|
|
219
211
|
return;
|
|
220
212
|
}
|
|
221
213
|
this.focusDefaultElement();
|
|
@@ -240,14 +232,14 @@ class KeyboardFocusRegion {
|
|
|
240
232
|
if (!this._active) {
|
|
241
233
|
if (defaultFocusElement || shouldContainFocus) {
|
|
242
234
|
if (shouldContainFocus) {
|
|
243
|
-
this._listeners.push((0,
|
|
235
|
+
this._listeners.push((0, _uiDomUtils.addEventListener)(this.doc, 'keydown', this.handleKeyDown));
|
|
244
236
|
} else {
|
|
245
|
-
this._listeners.push((0,
|
|
246
|
-
this._listeners.push((0,
|
|
237
|
+
this._listeners.push((0, _uiDomUtils.addEventListener)(this.firstTabbable || defaultFocusElement, 'keydown', this.handleFirstTabbableKeyDown));
|
|
238
|
+
this._listeners.push((0, _uiDomUtils.addEventListener)(this.lastTabbable || defaultFocusElement, 'keydown', this.handleLastTabbableKeyDown));
|
|
247
239
|
}
|
|
248
|
-
this._listeners.push((0,
|
|
249
|
-
this._listeners.push((0,
|
|
250
|
-
this._listeners.push((0,
|
|
240
|
+
this._listeners.push((0, _uiDomUtils.addEventListener)(this.doc, 'click', this.handleClick, true));
|
|
241
|
+
this._listeners.push((0, _uiDomUtils.addEventListener)(this.win, 'blur', this.handleWindowBlur, false));
|
|
242
|
+
this._listeners.push((0, _uiDomUtils.addEventListener)(this.doc, 'focus', this.handleFocus, true));
|
|
251
243
|
this._active = true;
|
|
252
244
|
}
|
|
253
245
|
}
|
|
@@ -6,8 +6,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
exports.hasVisibleChildren = hasVisibleChildren;
|
|
8
8
|
var _react = require("react");
|
|
9
|
-
var
|
|
10
|
-
var
|
|
9
|
+
var _uiReactUtils = require("@instructure/ui-react-utils");
|
|
10
|
+
var _uiA11yContent = require("@instructure/ui-a11y-content");
|
|
11
11
|
/*
|
|
12
12
|
* The MIT License (MIT)
|
|
13
13
|
*
|
|
@@ -44,7 +44,7 @@ var _ScreenReaderContent = require("@instructure/ui-a11y-content/lib/ScreenReade
|
|
|
44
44
|
function hasVisibleChildren(children) {
|
|
45
45
|
let visible = false;
|
|
46
46
|
_react.Children.forEach(children, child => {
|
|
47
|
-
if (child && !(0,
|
|
47
|
+
if (child && !(0, _uiReactUtils.matchComponentTypes)(child, [_uiA11yContent.ScreenReaderContent])) {
|
|
48
48
|
visible = true;
|
|
49
49
|
}
|
|
50
50
|
});
|
package/lib/scopeTab.js
CHANGED
|
@@ -5,10 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
exports.scopeTab = scopeTab;
|
|
8
|
-
var
|
|
9
|
-
var _findTabbable = require("@instructure/ui-dom-utils/lib/findTabbable.js");
|
|
10
|
-
var _isActiveElement = require("@instructure/ui-dom-utils/lib/isActiveElement.js");
|
|
11
|
-
var _containsActiveElement = require("@instructure/ui-dom-utils/lib/containsActiveElement.js");
|
|
8
|
+
var _uiDomUtils = require("@instructure/ui-dom-utils");
|
|
12
9
|
/*
|
|
13
10
|
* The MIT License (MIT)
|
|
14
11
|
*
|
|
@@ -46,8 +43,8 @@ var _containsActiveElement = require("@instructure/ui-dom-utils/lib/containsActi
|
|
|
46
43
|
* @param onLeavingFinalTabbable function executed when leaving final tabbable instead of the default behavior
|
|
47
44
|
*/
|
|
48
45
|
function scopeTab(element, event, onLeavingFinalTabbable) {
|
|
49
|
-
const node = (0,
|
|
50
|
-
const tabbable = (0,
|
|
46
|
+
const node = (0, _uiDomUtils.findDOMNode)(element);
|
|
47
|
+
const tabbable = (0, _uiDomUtils.findTabbable)(node);
|
|
51
48
|
if (!tabbable.length) {
|
|
52
49
|
event.preventDefault();
|
|
53
50
|
return;
|
|
@@ -58,11 +55,11 @@ function scopeTab(element, event, onLeavingFinalTabbable) {
|
|
|
58
55
|
// when its focused and tab is pressed, leaving one element
|
|
59
56
|
// (the menu container) in the tabbable array, which is not the currently
|
|
60
57
|
// active element
|
|
61
|
-
tabbable.length < 2 || (0,
|
|
58
|
+
tabbable.length < 2 || (0, _uiDomUtils.isActiveElement)(finalTabbable) ||
|
|
62
59
|
// handle immediate shift+tab after opening with mouse
|
|
63
|
-
(0,
|
|
60
|
+
(0, _uiDomUtils.isActiveElement)(node) ||
|
|
64
61
|
// already left final tabbable
|
|
65
|
-
!(0,
|
|
62
|
+
!(0, _uiDomUtils.containsActiveElement)(element);
|
|
66
63
|
if (!leavingFinalTabbable) return;
|
|
67
64
|
if (typeof onLeavingFinalTabbable === 'function') {
|
|
68
65
|
onLeavingFinalTabbable();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@instructure/ui-a11y-utils",
|
|
3
|
-
"version": "11.7.
|
|
3
|
+
"version": "11.7.9-snapshot-1",
|
|
4
4
|
"description": "A collection of utilities for managing focus and screen reader behavior",
|
|
5
5
|
"author": "Instructure, Inc. Engineering and Product Design",
|
|
6
6
|
"module": "./es/index.js",
|
|
@@ -16,17 +16,17 @@
|
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@babel/runtime": "^7.29.7",
|
|
18
18
|
"keycode": "^2",
|
|
19
|
-
"@instructure/
|
|
20
|
-
"@instructure/
|
|
21
|
-
"@instructure/
|
|
22
|
-
"@instructure/
|
|
23
|
-
"@instructure/ui-
|
|
24
|
-
"@instructure/
|
|
19
|
+
"@instructure/ui-a11y-content": "11.7.9-snapshot-1",
|
|
20
|
+
"@instructure/console": "11.7.9-snapshot-1",
|
|
21
|
+
"@instructure/shared-types": "11.7.9-snapshot-1",
|
|
22
|
+
"@instructure/ui-dom-utils": "11.7.9-snapshot-1",
|
|
23
|
+
"@instructure/ui-react-utils": "11.7.9-snapshot-1",
|
|
24
|
+
"@instructure/uid": "11.7.9-snapshot-1"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"vitest": "^5.0.0",
|
|
28
|
-
"@instructure/ui-babel-preset": "11.7.
|
|
29
|
-
"@instructure/ui-color-utils": "11.7.
|
|
28
|
+
"@instructure/ui-babel-preset": "11.7.9-snapshot-1",
|
|
29
|
+
"@instructure/ui-color-utils": "11.7.9-snapshot-1"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
32
|
"react": ">=18 <=19"
|