Skip to content

Commit

Permalink
🐛 (form-control-has-label): allow form-element to pass if it has an id
Browse files Browse the repository at this point in the history
Closes #924
  • Loading branch information
vhoyer committed Jan 16, 2024
1 parent dfcbe6c commit 5c9c683
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 10 deletions.
24 changes: 22 additions & 2 deletions src/rules/__tests__/form-control-has-label.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,22 @@ makeRuleTester("form-control-has-label", rule, {
valid: [
"<label for=''><input type='text' /></label>",
"<input type='text' aria-label='test' />",
"<label for=''>text</label><input type='text' />",
"<input type='button'>",
`
<div class="checkbox">
<label for="check">I agree</label>
<input id="check" type="checkbox" />
</div>
`,
`
<div class="checkbox">
<input id="myCheckbox" type="checkbox" aria-describedby="myCheckboxInfo" />
<div class="checkbox-label">
<label for="myCheckbox">I agree</label>
<p id="myCheckboxInfo">Here is some extra info what I agree upon</p>
</div>
</div>
`,
`
<label>
<div>
Expand All @@ -29,9 +43,15 @@ makeRuleTester("form-control-has-label", rule, {
"<b-form-input />"
],
invalid: [
"<label for=''>text</label><input type='text' />",
`
<div class="checkbox">
<input type="checkbox" />
<label>I agree</label>
</div>
`,
"<input type='text' />",
"<textarea type='text'></textarea>",
"<custom-label for='input'>text</custom-label><input type='text' id='input' />",
{
code: "<div><b-form-input /></div>",
options: [{ controlComponents: ["b-form-input"] }],
Expand Down
29 changes: 21 additions & 8 deletions src/rules/form-control-has-label.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,30 @@ function isLabelElement(
return isMatchingElement(node, allLabelComponents);
}

function hasLabelElement(
function hasNestedLabelElement(
node: AST.VElement,
options: FormControlHasLabelOptions
): boolean {
const { parent } = node;

return (
[parent, ...parent.children].some((node) =>
isLabelElement(node, options)
) ||
(parent && parent.type === "VElement" && hasLabelElement(parent, options))
);
if (isLabelElement(parent, options)) {
return true;
}

return (parent && parent.type === "VElement" && hasNestedLabelElement(parent, options));
}

/**
* Check if the form control at least has an "id" to be associated with a label
* Can't really check for the label with a matching "for" attribute, because
* checking every element in the file may lead to bad performance.
*/
function hasIdForLabelElement(
node: AST.VElement
): boolean {
const id = getElementAttributeValue(node, "id");

return Boolean(id);
}

const rule: Rule.RuleModule = {
Expand Down Expand Up @@ -104,7 +116,8 @@ const rule: Rule.RuleModule = {
if (
!isAriaHidden(node) &&
!hasAriaLabel(node) &&
!hasLabelElement(node, options)
!hasNestedLabelElement(node, options) &&
!hasIdForLabelElement(node)
) {
context.report({ node: node as any, messageId: "default" });
}
Expand Down

0 comments on commit 5c9c683

Please sign in to comment.