@@ -29,9 +43,15 @@ makeRuleTester("form-control-has-label", rule, {
"
"
],
invalid: [
+ "
",
+ `
+
+
+
+
+ `,
"
",
"
",
- "
text",
{
code: "
",
options: [{ controlComponents: ["b-form-input"] }],
diff --git a/src/rules/form-control-has-label.ts b/src/rules/form-control-has-label.ts
index 9b147400..55ab71ec 100644
--- a/src/rules/form-control-has-label.ts
+++ b/src/rules/form-control-has-label.ts
@@ -27,20 +27,34 @@ function isLabelElement(
return isMatchingElement(node, allLabelComponents);
}
-function hasLabelElement(
+function hasNestedLabelElement(
node: AST.VElement,
options: FormControlHasLabelOptions
): boolean {
const { parent } = node;
+ if (isLabelElement(parent, options)) {
+ return true;
+ }
+
return (
- [parent, ...parent.children].some((node) =>
- isLabelElement(node, options)
- ) ||
- (parent && parent.type === "VElement" && hasLabelElement(parent, options))
+ 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 = {
meta: {
type: "problem",
@@ -81,6 +95,9 @@ const rule: Rule.RuleModule = {
"input",
"textarea",
"select",
+ "meter",
+ "output",
+ "progress",
...(options.controlComponents || [])
];
@@ -101,7 +118,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" });
}
diff --git a/tsconfig.json b/tsconfig.json
index 8f674cd6..c4df42ce 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,7 +1,8 @@
{
"compilerOptions": {
+ "moduleResolution": "nodenext",
+ "module": "nodenext",
"target": "es2019",
- "module": "commonjs",
"strict": true,
"esModuleInterop": true,
"resolveJsonModule": true,