Skip to content

Commit a08fbcc

Browse files
BillyLevinljharb
authored andcommitted
[Fix] label-has-associated-control: ignore undetermined label text
Fixes #966 The rule no longer errors if the existence of label text cannot be determined
1 parent 27ff7cb commit a08fbcc

File tree

4 files changed

+26
-1
lines changed

4 files changed

+26
-1
lines changed

__tests__/src/rules/label-has-associated-control-test.js

+4
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ const htmlForValid = [
4949
// Glob support for controlComponents option.
5050
{ code: '<CustomLabel htmlFor="js_id" aria-label="A label" />', options: [{ controlComponents: ['Custom*'] }] },
5151
{ code: '<CustomLabel htmlFor="js_id" aria-label="A label" />', options: [{ controlComponents: ['*Label'] }] },
52+
// Rule does not error if presence of accessible label cannot be determined
53+
{ code: '<div><label htmlFor="js_id"><CustomText /></label><input id="js_id" /></div>' },
5254
];
5355
const nestingValid = [
5456
{ code: '<label>A label<input /></label>' },
@@ -74,6 +76,8 @@ const nestingValid = [
7476
// Glob support for controlComponents option.
7577
{ code: '<label><span>A label<CustomInput /></span></label>', options: [{ controlComponents: ['Custom*'] }] },
7678
{ code: '<label><span>A label<CustomInput /></span></label>', options: [{ controlComponents: ['*Input'] }] },
79+
// Rule does not error if presence of accessible label cannot be determined
80+
{ code: '<label><CustomText /><input /></label>' },
7781
];
7882

7983
const bothValid = [

src/rules/control-has-associated-label.js

+2
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ export default ({
101101
node,
102102
recursionDepth,
103103
labelAttributes,
104+
elementType,
105+
controlComponents,
104106
);
105107
}
106108

src/rules/label-has-associated-control.js

+2
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ export default ({
8787
node,
8888
recursionDepth,
8989
options.labelAttributes,
90+
elementType,
91+
controlComponents,
9092
);
9193

9294
if (hasAccessibleLabel) {

src/util/mayHaveAccessibleLabel.js

+18-1
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
*/
1010

1111
import includes from 'array-includes';
12-
import { getPropValue, propName } from 'jsx-ast-utils';
12+
import { getPropValue, propName, elementType as rawElementType } from 'jsx-ast-utils';
1313
import type { JSXOpeningElement, Node } from 'ast-types-flow';
14+
import minimatch from 'minimatch';
1415

1516
function tryTrim(value: any) {
1617
return typeof value === 'string' ? value.trim() : value;
@@ -46,6 +47,8 @@ export default function mayHaveAccessibleLabel(
4647
root: Node,
4748
maxDepth: number = 1,
4849
additionalLabellingProps?: Array<string> = [],
50+
getElementType: ((node: JSXOpeningElement) => string) = rawElementType,
51+
controlComponents: Array<string> = [],
4952
): boolean {
5053
function checkElement(
5154
node: Node,
@@ -77,6 +80,20 @@ export default function mayHaveAccessibleLabel(
7780
) {
7881
return true;
7982
}
83+
84+
if (node.type === 'JSXElement' && node.children.length === 0 && node.openingElement) {
85+
// $FlowFixMe `node.openingElement` has `unknown` type
86+
const name = getElementType(node.openingElement);
87+
const isReactComponent = name.length > 0 && name[0] === name[0].toUpperCase();
88+
89+
if (
90+
isReactComponent
91+
&& !controlComponents.some((control) => minimatch(name, control))
92+
) {
93+
return true;
94+
}
95+
}
96+
8097
// Recurse into the child element nodes.
8198
if (node.children) {
8299
/* $FlowFixMe */

0 commit comments

Comments
 (0)