@telus-uds/components-web 2.34.2 → 2.35.0
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 +28 -2
- package/lib/Card/Card.js +2 -2
- package/lib/DatePicker/DatePicker.js +17 -2
- package/lib/Footnote/Footnote.js +32 -15
- package/lib/baseExports.js +6 -0
- package/lib/utils/index.js +7 -0
- package/lib/utils/isElementFocusable.js +15 -0
- package/lib-module/Card/Card.js +2 -2
- package/lib-module/DatePicker/DatePicker.js +17 -2
- package/lib-module/Footnote/Footnote.js +33 -16
- package/lib-module/baseExports.js +1 -1
- package/lib-module/utils/index.js +2 -1
- package/lib-module/utils/isElementFocusable.js +8 -0
- package/package.json +3 -3
- package/src/Card/Card.jsx +4 -2
- package/src/DatePicker/DatePicker.jsx +26 -1
- package/src/Footnote/Footnote.jsx +36 -16
- package/src/baseExports.js +1 -0
- package/src/utils/index.js +2 -0
- package/src/utils/isElementFocusable.js +13 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,12 +1,38 @@
|
|
|
1
1
|
# Change Log - @telus-uds/components-web
|
|
2
2
|
|
|
3
|
-
This log was last generated on
|
|
3
|
+
This log was last generated on Mon, 24 Jun 2024 16:19:29 GMT and should not be manually modified.
|
|
4
4
|
|
|
5
5
|
<!-- Start content -->
|
|
6
6
|
|
|
7
|
+
## 2.35.0
|
|
8
|
+
|
|
9
|
+
Mon, 24 Jun 2024 16:19:29 GMT
|
|
10
|
+
|
|
11
|
+
### Minor changes
|
|
12
|
+
|
|
13
|
+
- `ActionCard`: make interactive card render body function (guillermo.peitzner@telus.com)
|
|
14
|
+
- Bump @telus-uds/components-base to v1.87.0
|
|
15
|
+
- Bump @telus-uds/system-theme-tokens to v2.58.0
|
|
16
|
+
|
|
17
|
+
### Patches
|
|
18
|
+
|
|
19
|
+
- `DatePicker`: fix focus life cycle (guillermo.peitzner@telus.com)
|
|
20
|
+
- List: misalignment issue fixed when the text was too long and the container has display: 'flex' (35577399+JoshHC@users.noreply.github.com)
|
|
21
|
+
|
|
22
|
+
## 2.34.3
|
|
23
|
+
|
|
24
|
+
Fri, 07 Jun 2024 22:42:09 GMT
|
|
25
|
+
|
|
26
|
+
### Patches
|
|
27
|
+
|
|
28
|
+
- Fixes for Footnote accesibility (Mauricio.BatresMontejo@telus.com)
|
|
29
|
+
- `Footnote`: fix hydration errors (guillermo.peitzner@telus.com)
|
|
30
|
+
- Bump @telus-uds/components-base to v1.86.0
|
|
31
|
+
- Bump @telus-uds/system-theme-tokens to v2.57.0
|
|
32
|
+
|
|
7
33
|
## 2.34.2
|
|
8
34
|
|
|
9
|
-
Tue, 28 May 2024
|
|
35
|
+
Tue, 28 May 2024 11:16:51 GMT
|
|
10
36
|
|
|
11
37
|
### Patches
|
|
12
38
|
|
package/lib/Card/Card.js
CHANGED
|
@@ -110,7 +110,7 @@ const Card = /*#__PURE__*/_react.default.forwardRef(function () {
|
|
|
110
110
|
dataSet: dataSet,
|
|
111
111
|
onPress: onPress,
|
|
112
112
|
...selectProps(rest),
|
|
113
|
-
children: interactiveCard === null || interactiveCard === void 0 ? void 0 : interactiveCard.body
|
|
113
|
+
children: typeof (interactiveCard === null || interactiveCard === void 0 ? void 0 : interactiveCard.body) === 'function' ? interactiveCard.body() : interactiveCard.body
|
|
114
114
|
}) : null, children || fullBleedContentPosition !== 'none' ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_componentsBase.StackView, {
|
|
115
115
|
direction: contentStackDirection,
|
|
116
116
|
tokens: {
|
|
@@ -185,7 +185,7 @@ Card.propTypes = {
|
|
|
185
185
|
* Object to set interactive card's properties
|
|
186
186
|
*/
|
|
187
187
|
interactiveCard: _propTypes.default.shape({
|
|
188
|
-
body: _propTypes.default.node,
|
|
188
|
+
body: _propTypes.default.oneOfType([_propTypes.default.node, _propTypes.default.func]),
|
|
189
189
|
tokens: (0, _componentsBase.getTokensPropType)('Card'),
|
|
190
190
|
variant: _componentsBase.variantProp.propType
|
|
191
191
|
})
|
|
@@ -119,6 +119,7 @@ const DatePicker = /*#__PURE__*/_react.default.forwardRef((_ref3, ref) => {
|
|
|
119
119
|
const [inputDate, setInputDate] = _react.default.useState(date instanceof _moment.default ? date : undefined);
|
|
120
120
|
const [inputText, setInputText] = _react.default.useState(date instanceof _moment.default ? date.format(dateFormat) : '');
|
|
121
121
|
const textInputRef = _react.default.useRef();
|
|
122
|
+
const prevButtonRef = _react.default.useRef();
|
|
122
123
|
const [datePickerPosition, setDatePickerPosition] = _react.default.useState({
|
|
123
124
|
left: 0,
|
|
124
125
|
top: 0
|
|
@@ -159,6 +160,19 @@ const DatePicker = /*#__PURE__*/_react.default.forwardRef((_ref3, ref) => {
|
|
|
159
160
|
setInputText(date instanceof _moment.default ? date.format(dateFormat) : '');
|
|
160
161
|
}
|
|
161
162
|
}, [date, inputDate]);
|
|
163
|
+
_react.default.useEffect(() => {
|
|
164
|
+
let timeoutId;
|
|
165
|
+
if (prevButtonRef.current && isFocused) {
|
|
166
|
+
timeoutId = setTimeout(() => prevButtonRef.current.focus(), 100);
|
|
167
|
+
}
|
|
168
|
+
return () => clearTimeout(timeoutId);
|
|
169
|
+
}, [isFocused]);
|
|
170
|
+
_react.default.useEffect(() => {
|
|
171
|
+
if (inputText !== '' && inputDate !== undefined) {
|
|
172
|
+
var _textInputRef$current;
|
|
173
|
+
textInputRef === null || textInputRef === void 0 ? void 0 : (_textInputRef$current = textInputRef.current) === null || _textInputRef$current === void 0 ? void 0 : _textInputRef$current.focus();
|
|
174
|
+
}
|
|
175
|
+
}, [inputDate, inputText]);
|
|
162
176
|
const onFocusChange = _ref4 => {
|
|
163
177
|
let {
|
|
164
178
|
focused
|
|
@@ -169,7 +183,7 @@ const DatePicker = /*#__PURE__*/_react.default.forwardRef((_ref3, ref) => {
|
|
|
169
183
|
setIsClickedInside(false);
|
|
170
184
|
};
|
|
171
185
|
const handleFocus = event => {
|
|
172
|
-
if (event.target.tagName === 'INPUT' && !disabled) {
|
|
186
|
+
if (event.target.tagName === 'INPUT' && !disabled && inputDate === undefined && inputText === '') {
|
|
173
187
|
setIsFocused(true);
|
|
174
188
|
}
|
|
175
189
|
};
|
|
@@ -265,7 +279,8 @@ const DatePicker = /*#__PURE__*/_react.default.forwardRef((_ref3, ref) => {
|
|
|
265
279
|
variant: {
|
|
266
280
|
size: 'small'
|
|
267
281
|
},
|
|
268
|
-
testID: prevTestID
|
|
282
|
+
testID: prevTestID,
|
|
283
|
+
ref: prevButtonRef
|
|
269
284
|
});
|
|
270
285
|
};
|
|
271
286
|
const renderNextButton = _ref6 => {
|
package/lib/Footnote/Footnote.js
CHANGED
|
@@ -308,7 +308,7 @@ const Footnote = /*#__PURE__*/_react.default.forwardRef((props, ref) => {
|
|
|
308
308
|
const headerRef = _react.default.useRef(null);
|
|
309
309
|
const bodyRef = _react.default.useRef(null);
|
|
310
310
|
const contentRef = _react.default.useRef(null);
|
|
311
|
-
const
|
|
311
|
+
const closeButtonRef = _react.default.useRef(null);
|
|
312
312
|
const [data, setData] = _react.default.useState({
|
|
313
313
|
content: null,
|
|
314
314
|
number: null
|
|
@@ -329,8 +329,14 @@ const Footnote = /*#__PURE__*/_react.default.forwardRef((props, ref) => {
|
|
|
329
329
|
onClose(event, options);
|
|
330
330
|
}, [onClose]);
|
|
331
331
|
|
|
332
|
-
|
|
333
|
-
|
|
332
|
+
/**
|
|
333
|
+
* When listen for ESCAPE, close button clicks, and clicks outside of the Footnote. Call onClose.
|
|
334
|
+
* When the event type is a 'keydonw' and the event key is a 'Tab', using a 'querySelectorAll we obtain all
|
|
335
|
+
* the interactive elements within the footnote, we order and save the first and the last,
|
|
336
|
+
* if the footnote is active the focus will be inside the footnote until it is closed,
|
|
337
|
+
* if there are no interactive elements the focus will remain inside the close button.
|
|
338
|
+
*/
|
|
339
|
+
const manageFootnoteFocusAndClose = _react.default.useCallback(event => {
|
|
334
340
|
var _footnoteRef$current, _footnoteRef$current2;
|
|
335
341
|
if (!isVisible) {
|
|
336
342
|
return;
|
|
@@ -340,6 +346,17 @@ const Footnote = /*#__PURE__*/_react.default.forwardRef((props, ref) => {
|
|
|
340
346
|
closeFootnote(event, {
|
|
341
347
|
returnFocus: true
|
|
342
348
|
});
|
|
349
|
+
} else if (event.key === 'Tab') {
|
|
350
|
+
const focusableElements = Array.from(footnoteRef.current.querySelectorAll('*')).filter(_utils.isElementFocusable);
|
|
351
|
+
const firstElement = focusableElements[0];
|
|
352
|
+
const lastElement = focusableElements[focusableElements.length - 1];
|
|
353
|
+
if (event.shiftKey && document.activeElement === firstElement) {
|
|
354
|
+
event.preventDefault();
|
|
355
|
+
lastElement.focus();
|
|
356
|
+
} else if (!event.shiftKey && document.activeElement === lastElement) {
|
|
357
|
+
event.preventDefault();
|
|
358
|
+
firstElement.focus();
|
|
359
|
+
}
|
|
343
360
|
}
|
|
344
361
|
} else if ((event.type === 'click' || event.type === 'mousedown') && footnoteRef !== null && footnoteRef !== void 0 && footnoteRef.current && event.target && !(footnoteRef !== null && footnoteRef !== void 0 && (_footnoteRef$current = footnoteRef.current) !== null && _footnoteRef$current !== void 0 && _footnoteRef$current.contains(event.target)) && event.target.getAttribute('data-tds-id') !== 'footnote-link') {
|
|
345
362
|
closeFootnote(event, {
|
|
@@ -356,8 +373,8 @@ const Footnote = /*#__PURE__*/_react.default.forwardRef((props, ref) => {
|
|
|
356
373
|
setBodyHeight(oldHeight);
|
|
357
374
|
};
|
|
358
375
|
const focusHeading = () => {
|
|
359
|
-
if (Boolean(content) && isVisible &&
|
|
360
|
-
|
|
376
|
+
if (Boolean(content) && isVisible && closeButtonRef && closeButtonRef.current !== null) {
|
|
377
|
+
closeButtonRef.current.focus();
|
|
361
378
|
}
|
|
362
379
|
};
|
|
363
380
|
const handleStyledFootnoteTransitionEnd = event => {
|
|
@@ -410,24 +427,24 @@ const Footnote = /*#__PURE__*/_react.default.forwardRef((props, ref) => {
|
|
|
410
427
|
_react.default.useEffect(() => {
|
|
411
428
|
if (isOpen) {
|
|
412
429
|
setIsVisible(true);
|
|
413
|
-
document.addEventListener('mousedown',
|
|
414
|
-
window.addEventListener('click',
|
|
415
|
-
window.addEventListener('keydown',
|
|
416
|
-
window.addEventListener('touchstart',
|
|
430
|
+
document.addEventListener('mousedown', manageFootnoteFocusAndClose);
|
|
431
|
+
window.addEventListener('click', manageFootnoteFocusAndClose);
|
|
432
|
+
window.addEventListener('keydown', manageFootnoteFocusAndClose);
|
|
433
|
+
window.addEventListener('touchstart', manageFootnoteFocusAndClose);
|
|
417
434
|
window.addEventListener('touchmove', preventDefault, {
|
|
418
435
|
passive: false
|
|
419
436
|
});
|
|
420
437
|
}
|
|
421
438
|
return () => {
|
|
422
439
|
if (isOpen) {
|
|
423
|
-
document.removeEventListener('mousedown',
|
|
424
|
-
window.removeEventListener('click',
|
|
425
|
-
window.removeEventListener('keydown',
|
|
426
|
-
window.removeEventListener('touchstart',
|
|
440
|
+
document.removeEventListener('mousedown', manageFootnoteFocusAndClose);
|
|
441
|
+
window.removeEventListener('click', manageFootnoteFocusAndClose);
|
|
442
|
+
window.removeEventListener('keydown', manageFootnoteFocusAndClose);
|
|
443
|
+
window.removeEventListener('touchstart', manageFootnoteFocusAndClose);
|
|
427
444
|
window.removeEventListener('touchmove', preventDefault);
|
|
428
445
|
}
|
|
429
446
|
};
|
|
430
|
-
}, [
|
|
447
|
+
}, [manageFootnoteFocusAndClose, isOpen]);
|
|
431
448
|
|
|
432
449
|
// Set data if opening a new footnote
|
|
433
450
|
_react.default.useEffect(() => {
|
|
@@ -517,7 +534,6 @@ const Footnote = /*#__PURE__*/_react.default.forwardRef((props, ref) => {
|
|
|
517
534
|
ref: headerRef,
|
|
518
535
|
viewport: viewport,
|
|
519
536
|
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(StyledHeader, {
|
|
520
|
-
ref: headingRef,
|
|
521
537
|
footnoteHeaderPaddingLeft: footnoteHeaderPaddingLeft,
|
|
522
538
|
footnoteHeaderPaddingRight: footnoteHeaderPaddingRight,
|
|
523
539
|
footnoteHeaderPaddingTop: footnoteHeaderPaddingTop,
|
|
@@ -533,6 +549,7 @@ const Footnote = /*#__PURE__*/_react.default.forwardRef((props, ref) => {
|
|
|
533
549
|
},
|
|
534
550
|
children: getCopy('heading')
|
|
535
551
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(CloseButton, {
|
|
552
|
+
ref: closeButtonRef,
|
|
536
553
|
closeButtonBorder: `${closeButtonBorderSize} solid ${closeButtonBorderColor}`,
|
|
537
554
|
closeButtonWidth: `${closeButtonWidth}px`,
|
|
538
555
|
closeButtonHeight: `${closeButtonHeight}px`,
|
package/lib/baseExports.js
CHANGED
|
@@ -15,6 +15,12 @@ Object.defineProperty(exports, "A11yText", {
|
|
|
15
15
|
return _componentsBase.A11yText;
|
|
16
16
|
}
|
|
17
17
|
});
|
|
18
|
+
Object.defineProperty(exports, "ActionCard", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function () {
|
|
21
|
+
return _componentsBase.ActionCard;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
18
24
|
Object.defineProperty(exports, "ActivityIndicator", {
|
|
19
25
|
enumerable: true,
|
|
20
26
|
get: function () {
|
package/lib/utils/index.js
CHANGED
|
@@ -15,6 +15,12 @@ Object.defineProperty(exports, "htmlAttrs", {
|
|
|
15
15
|
return _componentsBase.htmlAttrs;
|
|
16
16
|
}
|
|
17
17
|
});
|
|
18
|
+
Object.defineProperty(exports, "isElementFocusable", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function () {
|
|
21
|
+
return _isElementFocusable.default;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
18
24
|
Object.defineProperty(exports, "media", {
|
|
19
25
|
enumerable: true,
|
|
20
26
|
get: function () {
|
|
@@ -63,6 +69,7 @@ var _transforms = require("./transforms");
|
|
|
63
69
|
var _useTypographyTheme = _interopRequireDefault(require("./useTypographyTheme"));
|
|
64
70
|
var _media = _interopRequireDefault(require("./media"));
|
|
65
71
|
var _ssr = _interopRequireDefault(require("./ssr"));
|
|
72
|
+
var _isElementFocusable = _interopRequireDefault(require("./isElementFocusable"));
|
|
66
73
|
var _renderStructuredContent = _interopRequireDefault(require("./renderStructuredContent"));
|
|
67
74
|
var _useOverlaidPosition = _interopRequireDefault(require("./useOverlaidPosition"));
|
|
68
75
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* Returns focusable elements inside of the Footnote
|
|
9
|
+
*/
|
|
10
|
+
const isElementFocusable = element => {
|
|
11
|
+
const focusableElements = `a[href], button, textarea, input, select, form, label, audio, video, source, track, canvas, rect, polygon, iframe[data-src], [tabindex]:not([tabindex="-1"]), [contenteditable]`;
|
|
12
|
+
return element.matches(focusableElements) && !element.hasAttribute('disabled') && !element.matches('[tabindex="-1"]');
|
|
13
|
+
};
|
|
14
|
+
var _default = isElementFocusable;
|
|
15
|
+
exports.default = _default;
|
package/lib-module/Card/Card.js
CHANGED
|
@@ -103,7 +103,7 @@ const Card = /*#__PURE__*/React.forwardRef(function () {
|
|
|
103
103
|
dataSet: dataSet,
|
|
104
104
|
onPress: onPress,
|
|
105
105
|
...selectProps(rest),
|
|
106
|
-
children: interactiveCard === null || interactiveCard === void 0 ? void 0 : interactiveCard.body
|
|
106
|
+
children: typeof (interactiveCard === null || interactiveCard === void 0 ? void 0 : interactiveCard.body) === 'function' ? interactiveCard.body() : interactiveCard.body
|
|
107
107
|
}) : null, children || fullBleedContentPosition !== 'none' ? /*#__PURE__*/_jsxs(StackView, {
|
|
108
108
|
direction: contentStackDirection,
|
|
109
109
|
tokens: {
|
|
@@ -178,7 +178,7 @@ Card.propTypes = {
|
|
|
178
178
|
* Object to set interactive card's properties
|
|
179
179
|
*/
|
|
180
180
|
interactiveCard: PropTypes.shape({
|
|
181
|
-
body: PropTypes.node,
|
|
181
|
+
body: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
|
|
182
182
|
tokens: getTokensPropType('Card'),
|
|
183
183
|
variant: variantProp.propType
|
|
184
184
|
})
|
|
@@ -114,6 +114,7 @@ const DatePicker = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
|
|
|
114
114
|
const [inputDate, setInputDate] = React.useState(date instanceof moment ? date : undefined);
|
|
115
115
|
const [inputText, setInputText] = React.useState(date instanceof moment ? date.format(dateFormat) : '');
|
|
116
116
|
const textInputRef = React.useRef();
|
|
117
|
+
const prevButtonRef = React.useRef();
|
|
117
118
|
const [datePickerPosition, setDatePickerPosition] = React.useState({
|
|
118
119
|
left: 0,
|
|
119
120
|
top: 0
|
|
@@ -154,6 +155,19 @@ const DatePicker = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
|
|
|
154
155
|
setInputText(date instanceof moment ? date.format(dateFormat) : '');
|
|
155
156
|
}
|
|
156
157
|
}, [date, inputDate]);
|
|
158
|
+
React.useEffect(() => {
|
|
159
|
+
let timeoutId;
|
|
160
|
+
if (prevButtonRef.current && isFocused) {
|
|
161
|
+
timeoutId = setTimeout(() => prevButtonRef.current.focus(), 100);
|
|
162
|
+
}
|
|
163
|
+
return () => clearTimeout(timeoutId);
|
|
164
|
+
}, [isFocused]);
|
|
165
|
+
React.useEffect(() => {
|
|
166
|
+
if (inputText !== '' && inputDate !== undefined) {
|
|
167
|
+
var _textInputRef$current;
|
|
168
|
+
textInputRef === null || textInputRef === void 0 ? void 0 : (_textInputRef$current = textInputRef.current) === null || _textInputRef$current === void 0 ? void 0 : _textInputRef$current.focus();
|
|
169
|
+
}
|
|
170
|
+
}, [inputDate, inputText]);
|
|
157
171
|
const onFocusChange = _ref4 => {
|
|
158
172
|
let {
|
|
159
173
|
focused
|
|
@@ -164,7 +178,7 @@ const DatePicker = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
|
|
|
164
178
|
setIsClickedInside(false);
|
|
165
179
|
};
|
|
166
180
|
const handleFocus = event => {
|
|
167
|
-
if (event.target.tagName === 'INPUT' && !disabled) {
|
|
181
|
+
if (event.target.tagName === 'INPUT' && !disabled && inputDate === undefined && inputText === '') {
|
|
168
182
|
setIsFocused(true);
|
|
169
183
|
}
|
|
170
184
|
};
|
|
@@ -260,7 +274,8 @@ const DatePicker = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
|
|
|
260
274
|
variant: {
|
|
261
275
|
size: 'small'
|
|
262
276
|
},
|
|
263
|
-
testID: prevTestID
|
|
277
|
+
testID: prevTestID,
|
|
278
|
+
ref: prevButtonRef
|
|
264
279
|
});
|
|
265
280
|
};
|
|
266
281
|
const renderNextButton = _ref6 => {
|
|
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
|
|
3
3
|
import styled, { createGlobalStyle } from 'styled-components';
|
|
4
4
|
import { Icon, Portal, selectSystemProps, Typography, useCopy, useTheme, useResponsiveProp, useThemeTokens, useViewport, getTokensPropType } from '@telus-uds/components-base';
|
|
5
5
|
import OrderedListBase from '../OrderedList/OrderedListBase';
|
|
6
|
-
import { htmlAttrs, media, renderStructuredContent } from '../utils';
|
|
6
|
+
import { htmlAttrs, media, renderStructuredContent, isElementFocusable } from '../utils';
|
|
7
7
|
import defaultDictionary from './dictionary';
|
|
8
8
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
9
9
|
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
@@ -300,7 +300,7 @@ const Footnote = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
300
300
|
const headerRef = React.useRef(null);
|
|
301
301
|
const bodyRef = React.useRef(null);
|
|
302
302
|
const contentRef = React.useRef(null);
|
|
303
|
-
const
|
|
303
|
+
const closeButtonRef = React.useRef(null);
|
|
304
304
|
const [data, setData] = React.useState({
|
|
305
305
|
content: null,
|
|
306
306
|
number: null
|
|
@@ -321,8 +321,14 @@ const Footnote = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
321
321
|
onClose(event, options);
|
|
322
322
|
}, [onClose]);
|
|
323
323
|
|
|
324
|
-
|
|
325
|
-
|
|
324
|
+
/**
|
|
325
|
+
* When listen for ESCAPE, close button clicks, and clicks outside of the Footnote. Call onClose.
|
|
326
|
+
* When the event type is a 'keydonw' and the event key is a 'Tab', using a 'querySelectorAll we obtain all
|
|
327
|
+
* the interactive elements within the footnote, we order and save the first and the last,
|
|
328
|
+
* if the footnote is active the focus will be inside the footnote until it is closed,
|
|
329
|
+
* if there are no interactive elements the focus will remain inside the close button.
|
|
330
|
+
*/
|
|
331
|
+
const manageFootnoteFocusAndClose = React.useCallback(event => {
|
|
326
332
|
var _footnoteRef$current, _footnoteRef$current2;
|
|
327
333
|
if (!isVisible) {
|
|
328
334
|
return;
|
|
@@ -332,6 +338,17 @@ const Footnote = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
332
338
|
closeFootnote(event, {
|
|
333
339
|
returnFocus: true
|
|
334
340
|
});
|
|
341
|
+
} else if (event.key === 'Tab') {
|
|
342
|
+
const focusableElements = Array.from(footnoteRef.current.querySelectorAll('*')).filter(isElementFocusable);
|
|
343
|
+
const firstElement = focusableElements[0];
|
|
344
|
+
const lastElement = focusableElements[focusableElements.length - 1];
|
|
345
|
+
if (event.shiftKey && document.activeElement === firstElement) {
|
|
346
|
+
event.preventDefault();
|
|
347
|
+
lastElement.focus();
|
|
348
|
+
} else if (!event.shiftKey && document.activeElement === lastElement) {
|
|
349
|
+
event.preventDefault();
|
|
350
|
+
firstElement.focus();
|
|
351
|
+
}
|
|
335
352
|
}
|
|
336
353
|
} else if ((event.type === 'click' || event.type === 'mousedown') && footnoteRef !== null && footnoteRef !== void 0 && footnoteRef.current && event.target && !(footnoteRef !== null && footnoteRef !== void 0 && (_footnoteRef$current = footnoteRef.current) !== null && _footnoteRef$current !== void 0 && _footnoteRef$current.contains(event.target)) && event.target.getAttribute('data-tds-id') !== 'footnote-link') {
|
|
337
354
|
closeFootnote(event, {
|
|
@@ -348,8 +365,8 @@ const Footnote = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
348
365
|
setBodyHeight(oldHeight);
|
|
349
366
|
};
|
|
350
367
|
const focusHeading = () => {
|
|
351
|
-
if (Boolean(content) && isVisible &&
|
|
352
|
-
|
|
368
|
+
if (Boolean(content) && isVisible && closeButtonRef && closeButtonRef.current !== null) {
|
|
369
|
+
closeButtonRef.current.focus();
|
|
353
370
|
}
|
|
354
371
|
};
|
|
355
372
|
const handleStyledFootnoteTransitionEnd = event => {
|
|
@@ -402,24 +419,24 @@ const Footnote = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
402
419
|
React.useEffect(() => {
|
|
403
420
|
if (isOpen) {
|
|
404
421
|
setIsVisible(true);
|
|
405
|
-
document.addEventListener('mousedown',
|
|
406
|
-
window.addEventListener('click',
|
|
407
|
-
window.addEventListener('keydown',
|
|
408
|
-
window.addEventListener('touchstart',
|
|
422
|
+
document.addEventListener('mousedown', manageFootnoteFocusAndClose);
|
|
423
|
+
window.addEventListener('click', manageFootnoteFocusAndClose);
|
|
424
|
+
window.addEventListener('keydown', manageFootnoteFocusAndClose);
|
|
425
|
+
window.addEventListener('touchstart', manageFootnoteFocusAndClose);
|
|
409
426
|
window.addEventListener('touchmove', preventDefault, {
|
|
410
427
|
passive: false
|
|
411
428
|
});
|
|
412
429
|
}
|
|
413
430
|
return () => {
|
|
414
431
|
if (isOpen) {
|
|
415
|
-
document.removeEventListener('mousedown',
|
|
416
|
-
window.removeEventListener('click',
|
|
417
|
-
window.removeEventListener('keydown',
|
|
418
|
-
window.removeEventListener('touchstart',
|
|
432
|
+
document.removeEventListener('mousedown', manageFootnoteFocusAndClose);
|
|
433
|
+
window.removeEventListener('click', manageFootnoteFocusAndClose);
|
|
434
|
+
window.removeEventListener('keydown', manageFootnoteFocusAndClose);
|
|
435
|
+
window.removeEventListener('touchstart', manageFootnoteFocusAndClose);
|
|
419
436
|
window.removeEventListener('touchmove', preventDefault);
|
|
420
437
|
}
|
|
421
438
|
};
|
|
422
|
-
}, [
|
|
439
|
+
}, [manageFootnoteFocusAndClose, isOpen]);
|
|
423
440
|
|
|
424
441
|
// Set data if opening a new footnote
|
|
425
442
|
React.useEffect(() => {
|
|
@@ -509,7 +526,6 @@ const Footnote = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
509
526
|
ref: headerRef,
|
|
510
527
|
viewport: viewport,
|
|
511
528
|
children: /*#__PURE__*/_jsxs(StyledHeader, {
|
|
512
|
-
ref: headingRef,
|
|
513
529
|
footnoteHeaderPaddingLeft: footnoteHeaderPaddingLeft,
|
|
514
530
|
footnoteHeaderPaddingRight: footnoteHeaderPaddingRight,
|
|
515
531
|
footnoteHeaderPaddingTop: footnoteHeaderPaddingTop,
|
|
@@ -525,6 +541,7 @@ const Footnote = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
525
541
|
},
|
|
526
542
|
children: getCopy('heading')
|
|
527
543
|
}), /*#__PURE__*/_jsx(CloseButton, {
|
|
544
|
+
ref: closeButtonRef,
|
|
528
545
|
closeButtonBorder: `${closeButtonBorderSize} solid ${closeButtonBorderColor}`,
|
|
529
546
|
closeButtonWidth: `${closeButtonWidth}px`,
|
|
530
547
|
closeButtonHeight: `${closeButtonHeight}px`,
|
|
@@ -5,7 +5,7 @@ export {
|
|
|
5
5
|
/**
|
|
6
6
|
* Most base components should be re-exported as-is.
|
|
7
7
|
*/
|
|
8
|
-
A11yText, ActivityIndicator, Autocomplete, Box, Button, BaseProvider, A11yInfoProvider, ViewportProvider, ThemeProvider, ButtonDropdown, ButtonGroup, ButtonLink, CardGroup, Carousel, CarouselTabs, Checkbox, CheckboxGroup, ChevronLink, ColourToggle, Divider, ExpandCollapse, Feedback, FlexGrid, Icon, InputLabel, InputSupports, Link, Listbox, Modal, MultiSelectFilter, Notification, Pagination, Portal, QuickLinks, QuickLinksFeature, Radio, RadioGroup, RadioCard, RadioCardGroup, Responsive, Search, Select, SideNav, Skeleton, SkipLink, Spacer, StackView, StackWrap, StepTracker, Tabs, Tags, TextButton, TextArea, TextInput, Timeline, ToggleSwitch, ToggleSwitchGroup, TooltipButton, Tooltip, Typography, Validator,
|
|
8
|
+
ActionCard, A11yText, ActivityIndicator, Autocomplete, Box, Button, BaseProvider, A11yInfoProvider, ViewportProvider, ThemeProvider, ButtonDropdown, ButtonGroup, ButtonLink, CardGroup, Carousel, CarouselTabs, Checkbox, CheckboxGroup, ChevronLink, ColourToggle, Divider, ExpandCollapse, Feedback, FlexGrid, Icon, InputLabel, InputSupports, Link, Listbox, Modal, MultiSelectFilter, Notification, Pagination, Portal, QuickLinks, QuickLinksFeature, Radio, RadioGroup, RadioCard, RadioCardGroup, Responsive, Search, Select, SideNav, Skeleton, SkipLink, Spacer, StackView, StackWrap, StepTracker, Tabs, Tags, TextButton, TextArea, TextInput, Timeline, ToggleSwitch, ToggleSwitchGroup, TooltipButton, Tooltip, Typography, Validator,
|
|
9
9
|
/*
|
|
10
10
|
* Most utilities exported from @telus-uds/components-base are for building systems, not apps.
|
|
11
11
|
* Re-export only those utilities with a stable API and known use cases within apps / pages.
|
|
@@ -4,6 +4,7 @@ import { transformGradient } from './transforms';
|
|
|
4
4
|
import useTypographyTheme from './useTypographyTheme';
|
|
5
5
|
import media from './media';
|
|
6
6
|
import ssrStyles from './ssr';
|
|
7
|
+
import isElementFocusable from './isElementFocusable';
|
|
7
8
|
import renderStructuredContent from './renderStructuredContent';
|
|
8
9
|
import useOverlaidPosition from './useOverlaidPosition';
|
|
9
|
-
export { deprecate, htmlAttrs, transformGradient, useTypographyTheme, warn, media, renderStructuredContent, ssrStyles, useOverlaidPosition };
|
|
10
|
+
export { deprecate, htmlAttrs, transformGradient, useTypographyTheme, warn, media, renderStructuredContent, ssrStyles, isElementFocusable, useOverlaidPosition };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns focusable elements inside of the Footnote
|
|
3
|
+
*/
|
|
4
|
+
const isElementFocusable = element => {
|
|
5
|
+
const focusableElements = `a[href], button, textarea, input, select, form, label, audio, video, source, track, canvas, rect, polygon, iframe[data-src], [tabindex]:not([tabindex="-1"]), [contenteditable]`;
|
|
6
|
+
return element.matches(focusableElements) && !element.hasAttribute('disabled') && !element.matches('[tabindex="-1"]');
|
|
7
|
+
};
|
|
8
|
+
export default isElementFocusable;
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
],
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"@gorhom/portal": "^1.0.14",
|
|
8
|
-
"@telus-uds/components-base": "1.
|
|
8
|
+
"@telus-uds/components-base": "1.87.0",
|
|
9
9
|
"@telus-uds/system-constants": "^1.3.0",
|
|
10
10
|
"fscreen": "^1.2.0",
|
|
11
11
|
"lodash.omit": "^4.5.0",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"react-dates": "^21.8.0",
|
|
14
14
|
"react-helmet-async": "^1.3.0",
|
|
15
15
|
"react-moment-proptypes": "^1.8.1",
|
|
16
|
-
"@telus-uds/system-theme-tokens": "^2.
|
|
16
|
+
"@telus-uds/system-theme-tokens": "^2.58.0",
|
|
17
17
|
"prop-types": "^15.7.2",
|
|
18
18
|
"lodash.throttle": "^4.1.1",
|
|
19
19
|
"react-youtube": "^10.1.0",
|
|
@@ -83,5 +83,5 @@
|
|
|
83
83
|
"skip": true
|
|
84
84
|
},
|
|
85
85
|
"types": "types/index.d.ts",
|
|
86
|
-
"version": "2.
|
|
86
|
+
"version": "2.35.0"
|
|
87
87
|
}
|
package/src/Card/Card.jsx
CHANGED
|
@@ -116,7 +116,9 @@ const Card = React.forwardRef(
|
|
|
116
116
|
onPress={onPress}
|
|
117
117
|
{...selectProps(rest)}
|
|
118
118
|
>
|
|
119
|
-
{interactiveCard?.body
|
|
119
|
+
{typeof interactiveCard?.body === 'function'
|
|
120
|
+
? interactiveCard.body()
|
|
121
|
+
: interactiveCard.body}
|
|
120
122
|
</PressableCardBase>
|
|
121
123
|
) : null}
|
|
122
124
|
{children || fullBleedContentPosition !== 'none' ? (
|
|
@@ -197,7 +199,7 @@ Card.propTypes = {
|
|
|
197
199
|
* Object to set interactive card's properties
|
|
198
200
|
*/
|
|
199
201
|
interactiveCard: PropTypes.shape({
|
|
200
|
-
body: PropTypes.node,
|
|
202
|
+
body: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
|
|
201
203
|
tokens: getTokensPropType('Card'),
|
|
202
204
|
variant: variantProp.propType
|
|
203
205
|
})
|
|
@@ -109,6 +109,7 @@ const DatePicker = React.forwardRef(
|
|
|
109
109
|
date instanceof moment ? date.format(dateFormat) : ''
|
|
110
110
|
)
|
|
111
111
|
const textInputRef = React.useRef()
|
|
112
|
+
const prevButtonRef = React.useRef()
|
|
112
113
|
const [datePickerPosition, setDatePickerPosition] = React.useState({ left: 0, top: 0 })
|
|
113
114
|
|
|
114
115
|
useSafeLayoutEffect(() => {
|
|
@@ -130,6 +131,7 @@ const DatePicker = React.forwardRef(
|
|
|
130
131
|
const [isFocused, setIsFocused] = React.useState(false)
|
|
131
132
|
const [isClickedInside, setIsClickedInside] = React.useState(false)
|
|
132
133
|
const getCopy = useCopy({ dictionary, copy })
|
|
134
|
+
|
|
133
135
|
React.useEffect(() => {
|
|
134
136
|
/**
|
|
135
137
|
* `date` could be passed as `null` to reset the value so explicitly
|
|
@@ -140,6 +142,23 @@ const DatePicker = React.forwardRef(
|
|
|
140
142
|
setInputText(date instanceof moment ? date.format(dateFormat) : '')
|
|
141
143
|
}
|
|
142
144
|
}, [date, inputDate])
|
|
145
|
+
|
|
146
|
+
React.useEffect(() => {
|
|
147
|
+
let timeoutId
|
|
148
|
+
|
|
149
|
+
if (prevButtonRef.current && isFocused) {
|
|
150
|
+
timeoutId = setTimeout(() => prevButtonRef.current.focus(), 100)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return () => clearTimeout(timeoutId)
|
|
154
|
+
}, [isFocused])
|
|
155
|
+
|
|
156
|
+
React.useEffect(() => {
|
|
157
|
+
if (inputText !== '' && inputDate !== undefined) {
|
|
158
|
+
textInputRef?.current?.focus()
|
|
159
|
+
}
|
|
160
|
+
}, [inputDate, inputText])
|
|
161
|
+
|
|
143
162
|
const onFocusChange = ({ focused }) => {
|
|
144
163
|
if (!isClickedInside) {
|
|
145
164
|
setIsFocused(focused)
|
|
@@ -147,7 +166,12 @@ const DatePicker = React.forwardRef(
|
|
|
147
166
|
setIsClickedInside(false)
|
|
148
167
|
}
|
|
149
168
|
const handleFocus = (event) => {
|
|
150
|
-
if (
|
|
169
|
+
if (
|
|
170
|
+
event.target.tagName === 'INPUT' &&
|
|
171
|
+
!disabled &&
|
|
172
|
+
inputDate === undefined &&
|
|
173
|
+
inputText === ''
|
|
174
|
+
) {
|
|
151
175
|
setIsFocused(true)
|
|
152
176
|
}
|
|
153
177
|
}
|
|
@@ -248,6 +272,7 @@ const DatePicker = React.forwardRef(
|
|
|
248
272
|
icon={previousIcon}
|
|
249
273
|
variant={{ size: 'small' }}
|
|
250
274
|
testID={prevTestID}
|
|
275
|
+
ref={prevButtonRef}
|
|
251
276
|
/>
|
|
252
277
|
)
|
|
253
278
|
const renderNextButton = ({ onClick }) => (
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
} from '@telus-uds/components-base'
|
|
16
16
|
|
|
17
17
|
import OrderedListBase from '../OrderedList/OrderedListBase'
|
|
18
|
-
import { htmlAttrs, media, renderStructuredContent } from '../utils'
|
|
18
|
+
import { htmlAttrs, media, renderStructuredContent, isElementFocusable } from '../utils'
|
|
19
19
|
import defaultDictionary from './dictionary'
|
|
20
20
|
|
|
21
21
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([htmlAttrs])
|
|
@@ -263,7 +263,7 @@ const Footnote = React.forwardRef((props, ref) => {
|
|
|
263
263
|
const headerRef = React.useRef(null)
|
|
264
264
|
const bodyRef = React.useRef(null)
|
|
265
265
|
const contentRef = React.useRef(null)
|
|
266
|
-
const
|
|
266
|
+
const closeButtonRef = React.useRef(null)
|
|
267
267
|
const [data, setData] = React.useState({ content: null, number: null })
|
|
268
268
|
const [headerHeight, setHeaderHeight] = React.useState('auto')
|
|
269
269
|
const [bodyHeight, setBodyHeight] = React.useState('auto')
|
|
@@ -283,8 +283,14 @@ const Footnote = React.forwardRef((props, ref) => {
|
|
|
283
283
|
[onClose]
|
|
284
284
|
)
|
|
285
285
|
|
|
286
|
-
|
|
287
|
-
|
|
286
|
+
/**
|
|
287
|
+
* When listen for ESCAPE, close button clicks, and clicks outside of the Footnote. Call onClose.
|
|
288
|
+
* When the event type is a 'keydonw' and the event key is a 'Tab', using a 'querySelectorAll we obtain all
|
|
289
|
+
* the interactive elements within the footnote, we order and save the first and the last,
|
|
290
|
+
* if the footnote is active the focus will be inside the footnote until it is closed,
|
|
291
|
+
* if there are no interactive elements the focus will remain inside the close button.
|
|
292
|
+
*/
|
|
293
|
+
const manageFootnoteFocusAndClose = React.useCallback(
|
|
288
294
|
(event) => {
|
|
289
295
|
if (!isVisible) {
|
|
290
296
|
return
|
|
@@ -293,6 +299,20 @@ const Footnote = React.forwardRef((props, ref) => {
|
|
|
293
299
|
if (event.type === 'keydown') {
|
|
294
300
|
if (event.key === 'Escape' || event.key === 27) {
|
|
295
301
|
closeFootnote(event, { returnFocus: true })
|
|
302
|
+
} else if (event.key === 'Tab') {
|
|
303
|
+
const focusableElements = Array.from(footnoteRef.current.querySelectorAll('*')).filter(
|
|
304
|
+
isElementFocusable
|
|
305
|
+
)
|
|
306
|
+
const firstElement = focusableElements[0]
|
|
307
|
+
const lastElement = focusableElements[focusableElements.length - 1]
|
|
308
|
+
|
|
309
|
+
if (event.shiftKey && document.activeElement === firstElement) {
|
|
310
|
+
event.preventDefault()
|
|
311
|
+
lastElement.focus()
|
|
312
|
+
} else if (!event.shiftKey && document.activeElement === lastElement) {
|
|
313
|
+
event.preventDefault()
|
|
314
|
+
firstElement.focus()
|
|
315
|
+
}
|
|
296
316
|
}
|
|
297
317
|
} else if (
|
|
298
318
|
(event.type === 'click' || event.type === 'mousedown') &&
|
|
@@ -321,8 +341,8 @@ const Footnote = React.forwardRef((props, ref) => {
|
|
|
321
341
|
}
|
|
322
342
|
|
|
323
343
|
const focusHeading = () => {
|
|
324
|
-
if (Boolean(content) && isVisible &&
|
|
325
|
-
|
|
344
|
+
if (Boolean(content) && isVisible && closeButtonRef && closeButtonRef.current !== null) {
|
|
345
|
+
closeButtonRef.current.focus()
|
|
326
346
|
}
|
|
327
347
|
}
|
|
328
348
|
|
|
@@ -376,22 +396,22 @@ const Footnote = React.forwardRef((props, ref) => {
|
|
|
376
396
|
React.useEffect(() => {
|
|
377
397
|
if (isOpen) {
|
|
378
398
|
setIsVisible(true)
|
|
379
|
-
document.addEventListener('mousedown',
|
|
380
|
-
window.addEventListener('click',
|
|
381
|
-
window.addEventListener('keydown',
|
|
382
|
-
window.addEventListener('touchstart',
|
|
399
|
+
document.addEventListener('mousedown', manageFootnoteFocusAndClose)
|
|
400
|
+
window.addEventListener('click', manageFootnoteFocusAndClose)
|
|
401
|
+
window.addEventListener('keydown', manageFootnoteFocusAndClose)
|
|
402
|
+
window.addEventListener('touchstart', manageFootnoteFocusAndClose)
|
|
383
403
|
window.addEventListener('touchmove', preventDefault, { passive: false })
|
|
384
404
|
}
|
|
385
405
|
return () => {
|
|
386
406
|
if (isOpen) {
|
|
387
|
-
document.removeEventListener('mousedown',
|
|
388
|
-
window.removeEventListener('click',
|
|
389
|
-
window.removeEventListener('keydown',
|
|
390
|
-
window.removeEventListener('touchstart',
|
|
407
|
+
document.removeEventListener('mousedown', manageFootnoteFocusAndClose)
|
|
408
|
+
window.removeEventListener('click', manageFootnoteFocusAndClose)
|
|
409
|
+
window.removeEventListener('keydown', manageFootnoteFocusAndClose)
|
|
410
|
+
window.removeEventListener('touchstart', manageFootnoteFocusAndClose)
|
|
391
411
|
window.removeEventListener('touchmove', preventDefault)
|
|
392
412
|
}
|
|
393
413
|
}
|
|
394
|
-
}, [
|
|
414
|
+
}, [manageFootnoteFocusAndClose, isOpen])
|
|
395
415
|
|
|
396
416
|
// Set data if opening a new footnote
|
|
397
417
|
React.useEffect(() => {
|
|
@@ -496,7 +516,6 @@ const Footnote = React.forwardRef((props, ref) => {
|
|
|
496
516
|
<ContentContainer maxWidth={maxWidth}>
|
|
497
517
|
<StyledFootnoteHeader ref={headerRef} viewport={viewport}>
|
|
498
518
|
<StyledHeader
|
|
499
|
-
ref={headingRef}
|
|
500
519
|
footnoteHeaderPaddingLeft={footnoteHeaderPaddingLeft}
|
|
501
520
|
footnoteHeaderPaddingRight={footnoteHeaderPaddingRight}
|
|
502
521
|
footnoteHeaderPaddingTop={footnoteHeaderPaddingTop}
|
|
@@ -513,6 +532,7 @@ const Footnote = React.forwardRef((props, ref) => {
|
|
|
513
532
|
{getCopy('heading')}
|
|
514
533
|
</Typography>
|
|
515
534
|
<CloseButton
|
|
535
|
+
ref={closeButtonRef}
|
|
516
536
|
closeButtonBorder={`${closeButtonBorderSize} solid ${closeButtonBorderColor}`}
|
|
517
537
|
closeButtonWidth={`${closeButtonWidth}px`}
|
|
518
538
|
closeButtonHeight={`${closeButtonHeight}px`}
|
package/src/baseExports.js
CHANGED
package/src/utils/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { transformGradient } from './transforms'
|
|
|
4
4
|
import useTypographyTheme from './useTypographyTheme'
|
|
5
5
|
import media from './media'
|
|
6
6
|
import ssrStyles from './ssr'
|
|
7
|
+
import isElementFocusable from './isElementFocusable'
|
|
7
8
|
import renderStructuredContent from './renderStructuredContent'
|
|
8
9
|
import useOverlaidPosition from './useOverlaidPosition'
|
|
9
10
|
|
|
@@ -16,5 +17,6 @@ export {
|
|
|
16
17
|
media,
|
|
17
18
|
renderStructuredContent,
|
|
18
19
|
ssrStyles,
|
|
20
|
+
isElementFocusable,
|
|
19
21
|
useOverlaidPosition
|
|
20
22
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns focusable elements inside of the Footnote
|
|
3
|
+
*/
|
|
4
|
+
const isElementFocusable = (element) => {
|
|
5
|
+
const focusableElements = `a[href], button, textarea, input, select, form, label, audio, video, source, track, canvas, rect, polygon, iframe[data-src], [tabindex]:not([tabindex="-1"]), [contenteditable]`
|
|
6
|
+
return (
|
|
7
|
+
element.matches(focusableElements) &&
|
|
8
|
+
!element.hasAttribute('disabled') &&
|
|
9
|
+
!element.matches('[tabindex="-1"]')
|
|
10
|
+
)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default isElementFocusable
|