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