From a08fbcc502d6a6fa7d01a48c5f0b895c61e8cdd5 Mon Sep 17 00:00:00 2001 From: Billy Levin Date: Thu, 22 Aug 2024 20:21:57 +0100 Subject: [PATCH] [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 --- .../label-has-associated-control-test.js | 4 ++++ src/rules/control-has-associated-label.js | 2 ++ src/rules/label-has-associated-control.js | 2 ++ src/util/mayHaveAccessibleLabel.js | 19 ++++++++++++++++++- 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/__tests__/src/rules/label-has-associated-control-test.js b/__tests__/src/rules/label-has-associated-control-test.js index d0b4c3b31..0bd14107e 100644 --- a/__tests__/src/rules/label-has-associated-control-test.js +++ b/__tests__/src/rules/label-has-associated-control-test.js @@ -49,6 +49,8 @@ const htmlForValid = [ // Glob support for controlComponents option. { code: '', options: [{ controlComponents: ['Custom*'] }] }, { code: '', options: [{ controlComponents: ['*Label'] }] }, + // Rule does not error if presence of accessible label cannot be determined + { code: '
' }, ]; const nestingValid = [ { code: '' }, @@ -74,6 +76,8 @@ const nestingValid = [ // Glob support for controlComponents option. { code: '', options: [{ controlComponents: ['Custom*'] }] }, { code: '', options: [{ controlComponents: ['*Input'] }] }, + // Rule does not error if presence of accessible label cannot be determined + { code: '' }, ]; const bothValid = [ diff --git a/src/rules/control-has-associated-label.js b/src/rules/control-has-associated-label.js index 512a5213f..4a691741d 100644 --- a/src/rules/control-has-associated-label.js +++ b/src/rules/control-has-associated-label.js @@ -101,6 +101,8 @@ export default ({ node, recursionDepth, labelAttributes, + elementType, + controlComponents, ); } diff --git a/src/rules/label-has-associated-control.js b/src/rules/label-has-associated-control.js index 7ee98ef65..fd66f2cf7 100644 --- a/src/rules/label-has-associated-control.js +++ b/src/rules/label-has-associated-control.js @@ -87,6 +87,8 @@ export default ({ node, recursionDepth, options.labelAttributes, + elementType, + controlComponents, ); if (hasAccessibleLabel) { diff --git a/src/util/mayHaveAccessibleLabel.js b/src/util/mayHaveAccessibleLabel.js index 31acee955..186ef5e0a 100644 --- a/src/util/mayHaveAccessibleLabel.js +++ b/src/util/mayHaveAccessibleLabel.js @@ -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; @@ -46,6 +47,8 @@ export default function mayHaveAccessibleLabel( root: Node, maxDepth: number = 1, additionalLabellingProps?: Array = [], + getElementType: ((node: JSXOpeningElement) => string) = rawElementType, + controlComponents: Array = [], ): boolean { function checkElement( node: Node, @@ -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 */