Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 
 

118 rader
3.4 KiB

  1. 'use strict';
  2. var _jsxAstUtils = require('jsx-ast-utils');
  3. var _schemas = require('../util/schemas');
  4. var _mayContainChildComponent = require('../util/mayContainChildComponent');
  5. var _mayContainChildComponent2 = _interopRequireDefault(_mayContainChildComponent);
  6. var _mayHaveAccessibleLabel = require('../util/mayHaveAccessibleLabel');
  7. var _mayHaveAccessibleLabel2 = _interopRequireDefault(_mayHaveAccessibleLabel);
  8. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  9. /**
  10. * @fileoverview Enforce label tags have an associated control.
  11. * @author Jesse Beach
  12. *
  13. *
  14. */
  15. // ----------------------------------------------------------------------------
  16. // Rule Definition
  17. // ----------------------------------------------------------------------------
  18. var errorMessage = 'A form label must be associated with a control.';
  19. var schema = (0, _schemas.generateObjSchema)({
  20. labelComponents: _schemas.arraySchema,
  21. labelAttributes: _schemas.arraySchema,
  22. controlComponents: _schemas.arraySchema,
  23. assert: {
  24. description: 'Assert that the label has htmlFor, a nested label, both or either',
  25. type: 'string',
  26. enum: ['htmlFor', 'nesting', 'both', 'either']
  27. },
  28. depth: {
  29. description: 'JSX tree depth limit to check for accessible label',
  30. type: 'integer',
  31. minimum: 0
  32. }
  33. });
  34. var validateId = function validateId(node) {
  35. var htmlForAttr = (0, _jsxAstUtils.getProp)(node.attributes, 'htmlFor');
  36. var htmlForValue = (0, _jsxAstUtils.getPropValue)(htmlForAttr);
  37. return htmlForAttr !== false && !!htmlForValue;
  38. };
  39. module.exports = {
  40. meta: {
  41. docs: {},
  42. schema: [schema]
  43. },
  44. create: function create(context) {
  45. var options = context.options[0] || {};
  46. var labelComponents = options.labelComponents || [];
  47. var assertType = options.assert || 'either';
  48. var componentNames = ['label'].concat(labelComponents);
  49. var rule = function rule(node) {
  50. if (componentNames.indexOf((0, _jsxAstUtils.elementType)(node.openingElement)) === -1) {
  51. return;
  52. }
  53. var controlComponents = ['input', 'textarea'].concat(options.controlComponents || []);
  54. // Prevent crazy recursion.
  55. var recursionDepth = Math.min(options.depth === undefined ? 2 : options.depth, 25);
  56. var hasLabelId = validateId(node.openingElement);
  57. // Check for multiple control components.
  58. var hasNestedControl = controlComponents.some(function (name) {
  59. return (0, _mayContainChildComponent2.default)(node, name, recursionDepth);
  60. });
  61. var hasAccessibleLabel = (0, _mayHaveAccessibleLabel2.default)(node, recursionDepth, options.labelAttributes);
  62. if (hasAccessibleLabel) {
  63. switch (assertType) {
  64. case 'htmlFor':
  65. if (hasLabelId) {
  66. return;
  67. }
  68. break;
  69. case 'nesting':
  70. if (hasNestedControl) {
  71. return;
  72. }
  73. break;
  74. case 'both':
  75. if (hasLabelId && hasNestedControl) {
  76. return;
  77. }
  78. break;
  79. case 'either':
  80. if (hasLabelId || hasNestedControl) {
  81. return;
  82. }
  83. break;
  84. default:
  85. break;
  86. }
  87. }
  88. // htmlFor case
  89. context.report({
  90. node: node.openingElement,
  91. message: errorMessage
  92. });
  93. };
  94. // Create visitor selectors.
  95. return {
  96. JSXElement: rule
  97. };
  98. }
  99. };