isInteractiveElement.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. "use strict";
  2. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  3. Object.defineProperty(exports, "__esModule", {
  4. value: true
  5. });
  6. exports["default"] = void 0;
  7. var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
  8. var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
  9. var _ariaQuery = require("aria-query");
  10. var _axobjectQuery = require("axobject-query");
  11. var _arrayIncludes = _interopRequireDefault(require("array-includes"));
  12. var _attributesComparator = _interopRequireDefault(require("./attributesComparator"));
  13. var domKeys = (0, _toConsumableArray2["default"])(_ariaQuery.dom.keys());
  14. var roleKeys = (0, _toConsumableArray2["default"])(_ariaQuery.roles.keys());
  15. var elementRoleEntries = (0, _toConsumableArray2["default"])(_ariaQuery.elementRoles);
  16. var nonInteractiveRoles = new Set(roleKeys.filter(function (name) {
  17. var role = _ariaQuery.roles.get(name);
  18. return !role["abstract"] // 'toolbar' does not descend from widget, but it does support
  19. // aria-activedescendant, thus in practice we treat it as a widget.
  20. && name !== 'toolbar' && !role.superClass.some(function (classes) {
  21. return (0, _arrayIncludes["default"])(classes, 'widget');
  22. });
  23. }).concat( // The `progressbar` is descended from `widget`, but in practice, its
  24. // value is always `readonly`, so we treat it as a non-interactive role.
  25. 'progressbar'));
  26. var interactiveRoles = new Set(roleKeys.filter(function (name) {
  27. var role = _ariaQuery.roles.get(name);
  28. return !role["abstract"] // The `progressbar` is descended from `widget`, but in practice, its
  29. // value is always `readonly`, so we treat it as a non-interactive role.
  30. && name !== 'progressbar' && role.superClass.some(function (classes) {
  31. return (0, _arrayIncludes["default"])(classes, 'widget');
  32. });
  33. }).concat( // 'toolbar' does not descend from widget, but it does support
  34. // aria-activedescendant, thus in practice we treat it as a widget.
  35. 'toolbar'));
  36. var nonInteractiveElementRoleSchemas = elementRoleEntries.reduce(function (accumulator, _ref) {
  37. var _ref2 = (0, _slicedToArray2["default"])(_ref, 2),
  38. elementSchema = _ref2[0],
  39. roleSet = _ref2[1];
  40. if ((0, _toConsumableArray2["default"])(roleSet).every(function (role) {
  41. return nonInteractiveRoles.has(role);
  42. })) {
  43. accumulator.push(elementSchema);
  44. }
  45. return accumulator;
  46. }, []);
  47. var interactiveElementRoleSchemas = elementRoleEntries.reduce(function (accumulator, _ref3) {
  48. var _ref4 = (0, _slicedToArray2["default"])(_ref3, 2),
  49. elementSchema = _ref4[0],
  50. roleSet = _ref4[1];
  51. if ((0, _toConsumableArray2["default"])(roleSet).some(function (role) {
  52. return interactiveRoles.has(role);
  53. })) {
  54. accumulator.push(elementSchema);
  55. }
  56. return accumulator;
  57. }, []);
  58. var interactiveAXObjects = new Set((0, _toConsumableArray2["default"])(_axobjectQuery.AXObjects.keys()).filter(function (name) {
  59. return _axobjectQuery.AXObjects.get(name).type === 'widget';
  60. }));
  61. var interactiveElementAXObjectSchemas = (0, _toConsumableArray2["default"])(_axobjectQuery.elementAXObjects).reduce(function (accumulator, _ref5) {
  62. var _ref6 = (0, _slicedToArray2["default"])(_ref5, 2),
  63. elementSchema = _ref6[0],
  64. AXObjectSet = _ref6[1];
  65. if ((0, _toConsumableArray2["default"])(AXObjectSet).every(function (role) {
  66. return interactiveAXObjects.has(role);
  67. })) {
  68. accumulator.push(elementSchema);
  69. }
  70. return accumulator;
  71. }, []);
  72. function checkIsInteractiveElement(tagName, attributes) {
  73. function elementSchemaMatcher(elementSchema) {
  74. return tagName === elementSchema.name && (0, _attributesComparator["default"])(elementSchema.attributes, attributes);
  75. } // Check in elementRoles for inherent interactive role associations for
  76. // this element.
  77. var isInherentInteractiveElement = interactiveElementRoleSchemas.some(elementSchemaMatcher);
  78. if (isInherentInteractiveElement) {
  79. return true;
  80. } // Check in elementRoles for inherent non-interactive role associations for
  81. // this element.
  82. var isInherentNonInteractiveElement = nonInteractiveElementRoleSchemas.some(elementSchemaMatcher);
  83. if (isInherentNonInteractiveElement) {
  84. return false;
  85. } // Check in elementAXObjects for AX Tree associations for this element.
  86. var isInteractiveAXElement = interactiveElementAXObjectSchemas.some(elementSchemaMatcher);
  87. if (isInteractiveAXElement) {
  88. return true;
  89. }
  90. return false;
  91. }
  92. /**
  93. * Returns boolean indicating whether the given element is
  94. * interactive on the DOM or not. Usually used when an element
  95. * has a dynamic handler on it and we need to discern whether or not
  96. * it's intention is to be interacted with on the DOM.
  97. */
  98. var isInteractiveElement = function isInteractiveElement(tagName, attributes) {
  99. // Do not test higher level JSX components, as we do not know what
  100. // low-level DOM element this maps to.
  101. if (!(0, _arrayIncludes["default"])(domKeys, tagName)) {
  102. return false;
  103. }
  104. return checkIsInteractiveElement(tagName, attributes);
  105. };
  106. var _default = isInteractiveElement;
  107. exports["default"] = _default;