label-has-for.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports["default"] = void 0;
  6. var _jsxAstUtils = require("jsx-ast-utils");
  7. var _schemas = require("../util/schemas");
  8. var _getElementType = _interopRequireDefault(require("../util/getElementType"));
  9. var _hasAccessibleChild = _interopRequireDefault(require("../util/hasAccessibleChild"));
  10. function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
  11. /**
  12. * @fileoverview Enforce label tags have htmlFor attribute.
  13. * @author Ethan Cohen
  14. */
  15. // ----------------------------------------------------------------------------
  16. // Rule Definition
  17. // ----------------------------------------------------------------------------
  18. var enumValues = ['nesting', 'id'];
  19. var schema = {
  20. type: 'object',
  21. properties: {
  22. components: _schemas.arraySchema,
  23. required: {
  24. oneOf: [{
  25. type: 'string',
  26. "enum": enumValues
  27. }, (0, _schemas.generateObjSchema)({
  28. some: (0, _schemas.enumArraySchema)(enumValues)
  29. }, ['some']), (0, _schemas.generateObjSchema)({
  30. every: (0, _schemas.enumArraySchema)(enumValues)
  31. }, ['every'])]
  32. },
  33. allowChildren: {
  34. type: 'boolean'
  35. }
  36. }
  37. };
  38. // Breadth-first search, assuming that HTML for forms is shallow.
  39. function validateNesting(node) {
  40. var queue = node.parent.children.slice();
  41. var child;
  42. var opener;
  43. while (queue.length) {
  44. child = queue.shift();
  45. opener = child.openingElement;
  46. if (child.type === 'JSXElement' && opener && (opener.name.name === 'input' || opener.name.name === 'textarea' || opener.name.name === 'select')) {
  47. return true;
  48. }
  49. if (child.children) {
  50. queue = queue.concat(child.children);
  51. }
  52. }
  53. return false;
  54. }
  55. function validateID(_ref, context) {
  56. var _settings$jsxA11y$at, _settings$jsxA11y, _settings$jsxA11y$att;
  57. var attributes = _ref.attributes;
  58. var settings = context.settings;
  59. var htmlForAttributes = (_settings$jsxA11y$at = (_settings$jsxA11y = settings['jsx-a11y']) === null || _settings$jsxA11y === void 0 ? void 0 : (_settings$jsxA11y$att = _settings$jsxA11y.attributes) === null || _settings$jsxA11y$att === void 0 ? void 0 : _settings$jsxA11y$att["for"]) !== null && _settings$jsxA11y$at !== void 0 ? _settings$jsxA11y$at : ['htmlFor'];
  60. for (var i = 0; i < htmlForAttributes.length; i += 1) {
  61. var attribute = htmlForAttributes[i];
  62. if ((0, _jsxAstUtils.hasProp)(attributes, attribute)) {
  63. var htmlForAttr = (0, _jsxAstUtils.getProp)(attributes, attribute);
  64. var htmlForValue = (0, _jsxAstUtils.getPropValue)(htmlForAttr);
  65. return htmlForAttr !== false && !!htmlForValue;
  66. }
  67. }
  68. return false;
  69. }
  70. function validate(node, required, allowChildren, elementType, context) {
  71. if (allowChildren === true) {
  72. return (0, _hasAccessibleChild["default"])(node.parent, elementType);
  73. }
  74. if (required === 'nesting') {
  75. return validateNesting(node);
  76. }
  77. return validateID(node, context);
  78. }
  79. function getValidityStatus(node, required, allowChildren, elementType, context) {
  80. if (Array.isArray(required.some)) {
  81. var _isValid = required.some.some(function (rule) {
  82. return validate(node, rule, allowChildren, elementType, context);
  83. });
  84. var _message = !_isValid ? "Form label must have ANY of the following types of associated control: ".concat(required.some.join(', ')) : null;
  85. return {
  86. isValid: _isValid,
  87. message: _message
  88. };
  89. }
  90. if (Array.isArray(required.every)) {
  91. var _isValid2 = required.every.every(function (rule) {
  92. return validate(node, rule, allowChildren, elementType, context);
  93. });
  94. var _message2 = !_isValid2 ? "Form label must have ALL of the following types of associated control: ".concat(required.every.join(', ')) : null;
  95. return {
  96. isValid: _isValid2,
  97. message: _message2
  98. };
  99. }
  100. var isValid = validate(node, required, allowChildren, elementType, context);
  101. var message = !isValid ? "Form label must have the following type of associated control: ".concat(required) : null;
  102. return {
  103. isValid,
  104. message
  105. };
  106. }
  107. var _default = exports["default"] = {
  108. meta: {
  109. deprecated: true,
  110. replacedBy: ['label-has-associated-control'],
  111. docs: {
  112. description: 'Enforce that `<label>` elements have the `htmlFor` prop.',
  113. url: 'https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/tree/HEAD/docs/rules/label-has-for.md'
  114. },
  115. schema: [schema]
  116. },
  117. create: function create(context) {
  118. var elementType = (0, _getElementType["default"])(context);
  119. return {
  120. JSXOpeningElement(node) {
  121. var options = context.options[0] || {};
  122. var componentOptions = options.components || [];
  123. var typesToValidate = ['label'].concat(componentOptions);
  124. var nodeType = elementType(node);
  125. // Only check 'label' elements and custom types.
  126. if (typesToValidate.indexOf(nodeType) === -1) {
  127. return;
  128. }
  129. var required = options.required || {
  130. every: ['nesting', 'id']
  131. };
  132. var allowChildren = options.allowChildren || false;
  133. var _getValidityStatus = getValidityStatus(node, required, allowChildren, elementType, context),
  134. isValid = _getValidityStatus.isValid,
  135. message = _getValidityStatus.message;
  136. if (!isValid) {
  137. context.report({
  138. node,
  139. message
  140. });
  141. }
  142. }
  143. };
  144. }
  145. };
  146. module.exports = exports.default;