tab.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.tab = tab;
  6. var _dom = require("@testing-library/dom");
  7. var _utils = require("./utils");
  8. var _focus = require("./focus");
  9. var _blur = require("./blur");
  10. function getNextElement(currentIndex, shift, elements, focusTrap) {
  11. if (focusTrap === document && currentIndex === 0 && shift) {
  12. return document.body;
  13. } else if (focusTrap === document && currentIndex === elements.length - 1 && !shift) {
  14. return document.body;
  15. } else {
  16. const nextIndex = shift ? currentIndex - 1 : currentIndex + 1;
  17. const defaultIndex = shift ? elements.length - 1 : 0;
  18. return elements[nextIndex] || elements[defaultIndex];
  19. }
  20. }
  21. function tab({
  22. shift = false,
  23. focusTrap
  24. } = {}) {
  25. var _focusTrap$ownerDocum, _focusTrap;
  26. const previousElement = (0, _utils.getActiveElement)((_focusTrap$ownerDocum = (_focusTrap = focusTrap) == null ? void 0 : _focusTrap.ownerDocument) != null ? _focusTrap$ownerDocum : document);
  27. if (!focusTrap) {
  28. focusTrap = document;
  29. }
  30. const focusableElements = focusTrap.querySelectorAll(_utils.FOCUSABLE_SELECTOR);
  31. const enabledElements = [...focusableElements].filter(el => el === previousElement || el.getAttribute('tabindex') !== '-1' && !el.disabled && // Hidden elements are not tabable
  32. (0, _utils.isVisible)(el));
  33. if (enabledElements.length === 0) return;
  34. const orderedElements = enabledElements.map((el, idx) => ({
  35. el,
  36. idx
  37. })).sort((a, b) => {
  38. // tabindex has no effect if the active element has tabindex="-1"
  39. if (previousElement && previousElement.getAttribute('tabindex') === '-1') {
  40. return a.idx - b.idx;
  41. }
  42. const tabIndexA = a.el.getAttribute('tabindex');
  43. const tabIndexB = b.el.getAttribute('tabindex');
  44. const diff = tabIndexA - tabIndexB;
  45. return diff === 0 ? a.idx - b.idx : diff;
  46. }).map(({
  47. el
  48. }) => el);
  49. const checkedRadio = {};
  50. let prunedElements = [];
  51. orderedElements.forEach(el => {
  52. // For radio groups keep only the active radio
  53. // If there is no active radio, keep only the checked radio
  54. // If there is no checked radio, treat like everything else
  55. if (el.type === 'radio' && el.name) {
  56. // If the active element is part of the group, add only that
  57. if (previousElement && previousElement.type === el.type && previousElement.name === el.name) {
  58. if (el === previousElement) {
  59. prunedElements.push(el);
  60. }
  61. return;
  62. } // If we stumble upon a checked radio, remove the others
  63. if (el.checked) {
  64. prunedElements = prunedElements.filter(e => e.type !== el.type || e.name !== el.name);
  65. prunedElements.push(el);
  66. checkedRadio[el.name] = el;
  67. return;
  68. } // If we already found the checked one, skip
  69. if (checkedRadio[el.name]) {
  70. return;
  71. }
  72. }
  73. prunedElements.push(el);
  74. });
  75. const index = prunedElements.findIndex(el => el === previousElement);
  76. const nextElement = getNextElement(index, shift, prunedElements, focusTrap);
  77. const shiftKeyInit = {
  78. key: 'Shift',
  79. keyCode: 16,
  80. shiftKey: true
  81. };
  82. const tabKeyInit = {
  83. key: 'Tab',
  84. keyCode: 9,
  85. shiftKey: shift
  86. };
  87. let continueToTab = true; // not sure how to make it so there's no previous element...
  88. // istanbul ignore else
  89. if (previousElement) {
  90. // preventDefault on the shift key makes no difference
  91. if (shift) _dom.fireEvent.keyDown(previousElement, { ...shiftKeyInit
  92. });
  93. continueToTab = _dom.fireEvent.keyDown(previousElement, { ...tabKeyInit
  94. });
  95. }
  96. const keyUpTarget = !continueToTab && previousElement ? previousElement : nextElement;
  97. if (continueToTab) {
  98. if (nextElement === document.body) {
  99. (0, _blur.blur)(previousElement);
  100. } else {
  101. (0, _focus.focus)(nextElement);
  102. }
  103. }
  104. _dom.fireEvent.keyUp(keyUpTarget, { ...tabKeyInit
  105. });
  106. if (shift) {
  107. _dom.fireEvent.keyUp(keyUpTarget, { ...shiftKeyInit,
  108. shiftKey: false
  109. });
  110. }
  111. }
  112. /*
  113. eslint
  114. complexity: "off",
  115. max-statements: "off",
  116. */