Skip to content

Commit 47ec452

Browse files
committed
controlComponents option for label-has-for
1 parent 90e3538 commit 47ec452

File tree

5 files changed

+39
-14
lines changed

5 files changed

+39
-14
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
66

77
## [Unreleased]
88

9+
### Added
10+
11+
- The `controlComponents` option for the `label-has-for` rule, which allows you to configure the rule to allow additional control components.
12+
913
## [0.3.1] - 2020-07-10
1014

1115
### Changed

docs/label-has-for.md

+5-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ This rule takes one optional object argument of type object:
1717
"vuejs-accessibility/label-has-for": [
1818
"error",
1919
{
20-
"components": ["Label"],
20+
"components": ["VLabel"],
21+
"controlComponents": ["VInput"],
2122
"required": {
2223
"every": ["nesting", "id"]
2324
},
@@ -28,7 +29,9 @@ This rule takes one optional object argument of type object:
2829
}
2930
```
3031

31-
For the `components` option, these strings determine which JSX elements (**always including** `<label>`) should be checked for having the `for` prop. This is a good use case when you have a wrapper component that simply renders a `label` element.
32+
For the `components` option, these strings determine which elements (**always including** `<label>`) should be checked for having the `for` prop. This is a good use case when you have a wrapper component that simply renders a `label` element.
33+
34+
For the `controlComponents` option, these strings determine which elements should be counted as form control elements. By default, this includes `input`, `meter`, `progress`, `select`, and `textarea`. This is a good use case when you have a wrapper component that simplify renders a `input` element.
3235

3336
The `required` option (defaults to `"required": { "every": ["nesting", "id"] }`) determines which checks are activated. You're allowed to pass in one of the following types:
3437

src/rules/__tests__/label-has-for.test.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ makeRuleTester("label-has-for", rule, {
1313
{
1414
code: "<label for='id'><slot /></label>",
1515
options: [{ allowChildren: true }]
16+
},
17+
{
18+
code: "<label for='id'><VInput /></label>",
19+
options: [{ controlComponents: ["VInput"] }]
1620
}
1721
],
1822
invalid: [
@@ -21,8 +25,8 @@ makeRuleTester("label-has-for", rule, {
2125
"<label for='id'><slot /></label>",
2226
"<label for='id'><div /></label>",
2327
{
24-
code: "<Label for='id' />",
25-
options: [{ components: ["Label"] }],
28+
code: "<VLabel for='id' />",
29+
options: [{ components: ["VLabel"] }],
2630
errors: [{ messageId: "default" }]
2731
}
2832
]

src/rules/__tests__/makeRuleTester.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const makeValidExample = (example) => {
88
return { filename, code: makeTemplate(example) };
99
}
1010

11-
return Object.assign(example, { filename });
11+
return Object.assign(example, { filename, code: makeTemplate(example.code) });
1212
};
1313

1414
const makeInvalidExample = (example) => {

src/rules/label-has-for.js

+23-9
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const {
88

99
const controlTypes = ["input", "meter", "progress", "select", "textarea"];
1010

11-
const validateNesting = (node, allowChildren) =>
11+
const validateNesting = (node, allowChildren, controlComponents) =>
1212
node.children.some((child) => {
1313
if (child.rawName === "slot") {
1414
return allowChildren;
@@ -17,35 +17,41 @@ const validateNesting = (node, allowChildren) =>
1717
if (child.type === "VElement") {
1818
return (
1919
!isHiddenFromScreenReader(child) &&
20-
(controlTypes.includes(getElementType(child)) ||
20+
(controlTypes
21+
.concat(controlComponents)
22+
.includes(getElementType(child)) ||
2123
validateNesting(child, allowChildren))
2224
);
2325
}
2426

2527
return false;
2628
});
2729

28-
const validate = (node, rule, allowChildren) => {
30+
const validate = (node, rule, allowChildren, controlComponents) => {
2931
switch (rule) {
3032
case "nesting":
31-
return validateNesting(node, allowChildren);
33+
return validateNesting(node, allowChildren, controlComponents);
3234
case "id":
3335
return getElementAttributeValue(node, "for");
3436
default:
3537
return false;
3638
}
3739
};
3840

39-
const isValidLabel = (node, required, allowChildren) => {
41+
const isValidLabel = (node, required, allowChildren, controlComponents) => {
4042
if (Array.isArray(required.some)) {
41-
return required.some.some((rule) => validate(node, rule, allowChildren));
43+
return required.some.some((rule) =>
44+
validate(node, rule, allowChildren, controlComponents)
45+
);
4246
}
4347

4448
if (Array.isArray(required.every)) {
45-
return required.every.every((rule) => validate(node, rule, allowChildren));
49+
return required.every.every((rule) =>
50+
validate(node, rule, allowChildren, controlComponents)
51+
);
4652
}
4753

48-
return validate(node, required, allowChildren);
54+
return validate(node, required, allowChildren, controlComponents);
4955
};
5056

5157
module.exports = {
@@ -67,6 +73,13 @@ module.exports = {
6773
},
6874
uniqueItems: true
6975
},
76+
controlComponents: {
77+
type: "array",
78+
items: {
79+
type: "string"
80+
},
81+
uniqueItems: true
82+
},
7083
required: {
7184
oneOf: [
7285
{
@@ -116,12 +129,13 @@ module.exports = {
116129
const {
117130
allowChildren = false,
118131
components = [],
132+
controlComponents = [],
119133
required = { every: ["nesting", "id"] }
120134
} = context.options[0] || {};
121135

122136
if (
123137
["label"].concat(components).includes(getElementType(node)) &&
124-
!isValidLabel(node, required, allowChildren)
138+
!isValidLabel(node, required, allowChildren, controlComponents)
125139
) {
126140
context.report({ node, messageId: "default" });
127141
}

0 commit comments

Comments
 (0)