Skip to content

Commit 7d5511d

Browse files
hallzac2ljharb
authored andcommittedOct 8, 2020
[New] label-has-associated-control: Add glob support
Fixes #720.
1 parent 854da0c commit 7d5511d

File tree

5 files changed

+59
-3
lines changed

5 files changed

+59
-3
lines changed
 

Diff for: ‎__tests__/src/rules/label-has-associated-control-test.js

+6
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ const htmlForValid = [
3636
{ code: '<CustomLabel htmlFor="js_id" label="A label" />', options: [{ labelAttributes: ['label'], labelComponents: ['CustomLabel'] }] },
3737
// Custom label attributes.
3838
{ code: '<label htmlFor="js_id" label="A label" />', options: [{ labelAttributes: ['label'] }] },
39+
// Glob support for controlComponents option.
40+
{ code: '<CustomLabel htmlFor="js_id" aria-label="A label" />', options: [{ controlComponents: ['Custom*'] }] },
41+
{ code: '<CustomLabel htmlFor="js_id" aria-label="A label" />', options: [{ controlComponents: ['*Label'] }] },
3942
];
4043
const nestingValid = [
4144
{ code: '<label>A label<input /></label>' },
@@ -57,6 +60,9 @@ const nestingValid = [
5760
{ code: '<label><span>A label<CustomInput /></span></label>', options: [{ controlComponents: ['CustomInput'] }] },
5861
{ code: '<CustomLabel><span>A label<CustomInput /></span></CustomLabel>', options: [{ controlComponents: ['CustomInput'], labelComponents: ['CustomLabel'] }] },
5962
{ code: '<CustomLabel><span label="A label"><CustomInput /></span></CustomLabel>', options: [{ controlComponents: ['CustomInput'], labelComponents: ['CustomLabel'], labelAttributes: ['label'] }] },
63+
// Glob support for controlComponents option.
64+
{ code: '<label><span>A label<CustomInput /></span></label>', options: [{ controlComponents: ['Custom*'] }] },
65+
{ code: '<label><span>A label<CustomInput /></span></label>', options: [{ controlComponents: ['*Input'] }] },
6066
];
6167

6268
const bothValid = [

Diff for: ‎__tests__/src/util/mayContainChildComponent-test.js

+48
Original file line numberDiff line numberDiff line change
@@ -104,4 +104,52 @@ describe('mayContainChildComponent', () => {
104104
});
105105
});
106106
});
107+
108+
describe('Glob name matching', () => {
109+
describe('component name contains question mark ? - match any single character', () => {
110+
it('should return true', () => {
111+
expect(mayContainChildComponent(
112+
JSXElementMock('div', [], [
113+
JSXElementMock('FancyComponent'),
114+
]),
115+
'Fanc?Co??onent',
116+
)).toBe(true);
117+
});
118+
it('should return false', () => {
119+
expect(mayContainChildComponent(
120+
JSXElementMock('div', [], [
121+
JSXElementMock('FancyComponent'),
122+
]),
123+
'FancyComponent?',
124+
)).toBe(false);
125+
});
126+
});
127+
128+
describe('component name contains asterisk * - match zero or more characters', () => {
129+
it('should return true', () => {
130+
expect(mayContainChildComponent(
131+
JSXElementMock('div', [], [
132+
JSXElementMock('FancyComponent'),
133+
]),
134+
'Fancy*',
135+
)).toBe(true);
136+
});
137+
it('should return true', () => {
138+
expect(mayContainChildComponent(
139+
JSXElementMock('div', [], [
140+
JSXElementMock('FancyComponent'),
141+
]),
142+
'*Component',
143+
)).toBe(true);
144+
});
145+
it('should return true', () => {
146+
expect(mayContainChildComponent(
147+
JSXElementMock('div', [], [
148+
JSXElementMock('FancyComponent'),
149+
]),
150+
'Fancy*C*t',
151+
)).toBe(true);
152+
});
153+
});
154+
});
107155
});

Diff for: ‎docs/rules/label-has-associated-control.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ This rule takes one optional object argument of type object:
101101

102102
`labelComponents` is a list of custom React Component names that should be checked for an associated control.
103103
`labelAttributes` is a list of attributes to check on the label component and its children for a label. Use this if you have a custom component that uses a string passed on a prop to render an HTML `label`, for example.
104-
`controlComponents` is a list of custom React Components names that will output an input element.
104+
`controlComponents` is a list of custom React Components names that will output an input element. [Glob format](https://linuxhint.com/bash_globbing_tutorial/) is also supported for specifying names (e.g., `Label*` matches `LabelComponent` but not `CustomLabel`, `????Label` matches `LinkLabel` but not `CustomLabel`).
105105
`assert` asserts that the label has htmlFor, a nested label, both or either. Available options: `'htmlFor', 'nesting', 'both', 'either'`.
106106
`depth` (default 2, max 25) is an integer that determines how deep within a `JSXElement` label the rule should look for text content or an element with a label to determine if the `label` element will have an accessible label.
107107

Diff for: ‎package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@
6868
"emoji-regex": "^9.2.0",
6969
"has": "^1.0.3",
7070
"jsx-ast-utils": "^3.2.0",
71-
"language-tags": "^1.0.5"
71+
"language-tags": "^1.0.5",
72+
"minimatch": "^3.0.4"
7273
},
7374
"peerDependencies": {
7475
"eslint": "^3 || ^4 || ^5 || ^6 || ^7"

Diff for: ‎src/util/mayContainChildComponent.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import { elementType } from 'jsx-ast-utils';
1111
import type { Node } from 'ast-types-flow';
12+
import minimatch from 'minimatch';
1213

1314
export default function mayContainChildComponent(
1415
root: Node,
@@ -37,7 +38,7 @@ export default function mayContainChildComponent(
3738
if (
3839
childNode.type === 'JSXElement'
3940
&& childNode.openingElement
40-
&& elementType(childNode.openingElement) === componentName
41+
&& minimatch(elementType(childNode.openingElement), componentName)
4142
) {
4243
return true;
4344
}

0 commit comments

Comments
 (0)
Please sign in to comment.