Skip to content

Commit

Permalink
[Fix] label-has-associated-control: ignore undetermined label text
Browse files Browse the repository at this point in the history
Fixes jsx-eslint#966

The rule no longer errors if the existence of label text cannot be determined
  • Loading branch information
BillyLevin authored and ljharb committed Aug 22, 2024
1 parent 27ff7cb commit a08fbcc
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 1 deletion.
4 changes: 4 additions & 0 deletions __tests__/src/rules/label-has-associated-control-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ const htmlForValid = [
// Glob support for controlComponents option.
{ code: '<CustomLabel htmlFor="js_id" aria-label="A label" />', options: [{ controlComponents: ['Custom*'] }] },
{ code: '<CustomLabel htmlFor="js_id" aria-label="A label" />', options: [{ controlComponents: ['*Label'] }] },
// Rule does not error if presence of accessible label cannot be determined
{ code: '<div><label htmlFor="js_id"><CustomText /></label><input id="js_id" /></div>' },
];
const nestingValid = [
{ code: '<label>A label<input /></label>' },
Expand All @@ -74,6 +76,8 @@ const nestingValid = [
// Glob support for controlComponents option.
{ code: '<label><span>A label<CustomInput /></span></label>', options: [{ controlComponents: ['Custom*'] }] },
{ code: '<label><span>A label<CustomInput /></span></label>', options: [{ controlComponents: ['*Input'] }] },
// Rule does not error if presence of accessible label cannot be determined
{ code: '<label><CustomText /><input /></label>' },
];

const bothValid = [
Expand Down
2 changes: 2 additions & 0 deletions src/rules/control-has-associated-label.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ export default ({
node,
recursionDepth,
labelAttributes,
elementType,
controlComponents,
);
}

Expand Down
2 changes: 2 additions & 0 deletions src/rules/label-has-associated-control.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ export default ({
node,
recursionDepth,
options.labelAttributes,
elementType,
controlComponents,
);

if (hasAccessibleLabel) {
Expand Down
19 changes: 18 additions & 1 deletion src/util/mayHaveAccessibleLabel.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
*/

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

function tryTrim(value: any) {
return typeof value === 'string' ? value.trim() : value;
Expand Down Expand Up @@ -46,6 +47,8 @@ export default function mayHaveAccessibleLabel(
root: Node,
maxDepth: number = 1,
additionalLabellingProps?: Array<string> = [],
getElementType: ((node: JSXOpeningElement) => string) = rawElementType,
controlComponents: Array<string> = [],
): boolean {
function checkElement(
node: Node,
Expand Down Expand Up @@ -77,6 +80,20 @@ export default function mayHaveAccessibleLabel(
) {
return true;
}

if (node.type === 'JSXElement' && node.children.length === 0 && node.openingElement) {
// $FlowFixMe `node.openingElement` has `unknown` type
const name = getElementType(node.openingElement);
const isReactComponent = name.length > 0 && name[0] === name[0].toUpperCase();

if (
isReactComponent
&& !controlComponents.some((control) => minimatch(name, control))
) {
return true;
}
}

// Recurse into the child element nodes.
if (node.children) {
/* $FlowFixMe */
Expand Down

0 comments on commit a08fbcc

Please sign in to comment.