Skip to content

Commit d41b34a

Browse files
committed
🐛 (form-control-has-label): allow form-element to pass if it has an id
Closes #924
1 parent 6f1719d commit d41b34a

File tree

2 files changed

+43
-10
lines changed

2 files changed

+43
-10
lines changed

Diff for: src/rules/__tests__/form-control-has-label.test.ts

+22-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,22 @@ makeRuleTester("form-control-has-label", rule, {
55
valid: [
66
"<label for=''><input type='text' /></label>",
77
"<input type='text' aria-label='test' />",
8-
"<label for=''>text</label><input type='text' />",
98
"<input type='button'>",
9+
`
10+
<div class="checkbox">
11+
<label for="check">I agree</label>
12+
<input id="check" type="checkbox" />
13+
</div>
14+
`,
15+
`
16+
<div class="checkbox">
17+
<input id="myCheckbox" type="checkbox" aria-describedby="myCheckboxInfo" />
18+
<div class="checkbox-label">
19+
<label for="myCheckbox">I agree</label>
20+
<p id="myCheckboxInfo">Here is some extra info what I agree upon</p>
21+
</div>
22+
</div>
23+
`,
1024
`
1125
<label>
1226
<div>
@@ -29,9 +43,15 @@ makeRuleTester("form-control-has-label", rule, {
2943
"<b-form-input />"
3044
],
3145
invalid: [
46+
"<label for=''>text</label><input type='text' />",
47+
`
48+
<div class="checkbox">
49+
<input type="checkbox" />
50+
<label>I agree</label>
51+
</div>
52+
`,
3253
"<input type='text' />",
3354
"<textarea type='text'></textarea>",
34-
"<custom-label for='input'>text</custom-label><input type='text' id='input' />",
3555
{
3656
code: "<div><b-form-input /></div>",
3757
options: [{ controlComponents: ["b-form-input"] }],

Diff for: src/rules/form-control-has-label.ts

+21-8
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,30 @@ function isLabelElement(
2727
return isMatchingElement(node, allLabelComponents);
2828
}
2929

30-
function hasLabelElement(
30+
function hasNestedLabelElement(
3131
node: AST.VElement,
3232
options: FormControlHasLabelOptions
3333
): boolean {
3434
const { parent } = node;
3535

36-
return (
37-
[parent, ...parent.children].some((node) =>
38-
isLabelElement(node, options)
39-
) ||
40-
(parent && parent.type === "VElement" && hasLabelElement(parent, options))
41-
);
36+
if (isLabelElement(parent, options)) {
37+
return true;
38+
}
39+
40+
return (parent && parent.type === "VElement" && hasNestedLabelElement(parent, options));
41+
}
42+
43+
/**
44+
* Check if the form control at least has an "id" to be associated with a label
45+
* Can't really check for the label with a matching "for" attribute, because
46+
* checking every element in the file may lead to bad performance.
47+
*/
48+
function hasIdForLabelElement(
49+
node: AST.VElement
50+
): boolean {
51+
const id = getElementAttributeValue(node, "id");
52+
53+
return Boolean(id);
4254
}
4355

4456
const rule: Rule.RuleModule = {
@@ -104,7 +116,8 @@ const rule: Rule.RuleModule = {
104116
if (
105117
!isAriaHidden(node) &&
106118
!hasAriaLabel(node) &&
107-
!hasLabelElement(node, options)
119+
!hasNestedLabelElement(node, options) &&
120+
!hasIdForLabelElement(node)
108121
) {
109122
context.report({ node: node as any, messageId: "default" });
110123
}

0 commit comments

Comments
 (0)