Skip to content

Commit e7ba24c

Browse files
committed
Change every to some in label-has-for
1 parent d4bea02 commit e7ba24c

File tree

3 files changed

+109
-53
lines changed

3 files changed

+109
-53
lines changed

__tests__/src/rules/label-has-for-test.js

Lines changed: 91 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ const expectedEveryError = {
3535
};
3636

3737

38-
const array = [{
38+
const optionsComponents = [{
3939
components: ['Label', 'Descriptor'],
4040
}];
4141
const optionsRequiredNesting = [{
@@ -63,18 +63,18 @@ ruleTester.run('label-has-for', rule, {
6363
{ code: '<UX.Layout>test</UX.Layout>' },
6464

6565
// CUSTOM ELEMENT ARRAY OPTION TESTS
66-
{ code: '<Label htmlFor="foo" />', options: [assign({}, array[0], optionsRequiredSome[0])] },
67-
{ code: '<Label htmlFor={"foo"} />', options: [assign({}, array[0], optionsRequiredSome[0])] },
68-
{ code: '<Label htmlFor={foo} />', options: [assign({}, array[0], optionsRequiredSome[0])] },
69-
{ code: '<Label htmlFor={`${id}`} />', options: [assign({}, array[0], optionsRequiredSome[0])] },
70-
{ code: '<div />', options: array },
71-
{ code: '<Label htmlFor="something"><input /></Label>', options: array },
72-
{ code: '<Label htmlFor="foo">Test!</Label>', options: [assign({}, array[0], optionsRequiredSome[0])] },
73-
{ code: '<Descriptor htmlFor="foo" />', options: [assign({}, array[0], optionsRequiredSome[0])] },
74-
{ code: '<Descriptor htmlFor={"foo"} />', options: [assign({}, array[0], optionsRequiredSome[0])] },
75-
{ code: '<Descriptor htmlFor={foo} />', options: [assign({}, array[0], optionsRequiredSome[0])] },
76-
{ code: '<Descriptor htmlFor={`${id}`} />', options: [assign({}, array[0], optionsRequiredSome[0])] },
77-
{ code: '<Descriptor htmlFor="foo">Test!</Descriptor>', options: [assign({}, array[0], optionsRequiredSome[0])] },
66+
{ code: '<Label htmlFor="foo" />', options: [assign({}, optionsComponents[0], optionsRequiredSome[0])] },
67+
{ code: '<Label htmlFor={"foo"} />', options: [assign({}, optionsComponents[0], optionsRequiredSome[0])] },
68+
{ code: '<Label htmlFor={foo} />', options: [assign({}, optionsComponents[0], optionsRequiredSome[0])] },
69+
{ code: '<Label htmlFor={`${id}`} />', options: [assign({}, optionsComponents[0], optionsRequiredSome[0])] },
70+
{ code: '<div />', options: optionsComponents },
71+
{ code: '<Label htmlFor="something"><input /></Label>', options: optionsComponents },
72+
{ code: '<Label htmlFor="foo">Test!</Label>', options: [assign({}, optionsComponents[0], optionsRequiredSome[0])] },
73+
{ code: '<Descriptor htmlFor="foo" />', options: [assign({}, optionsComponents[0], optionsRequiredSome[0])] },
74+
{ code: '<Descriptor htmlFor={"foo"} />', options: [assign({}, optionsComponents[0], optionsRequiredSome[0])] },
75+
{ code: '<Descriptor htmlFor={foo} />', options: [assign({}, optionsComponents[0], optionsRequiredSome[0])] },
76+
{ code: '<Descriptor htmlFor={`${id}`} />', options: [assign({}, optionsComponents[0], optionsRequiredSome[0])] },
77+
{ code: '<Descriptor htmlFor="foo">Test!</Descriptor>', options: [assign({}, optionsComponents[0], optionsRequiredSome[0])] },
7878
{ code: '<label htmlFor="foo" />', options: optionsRequiredSome },
7979
{ code: '<label htmlFor={"foo"} />', options: optionsRequiredSome },
8080
{ code: '<label htmlFor={foo} />', options: optionsRequiredSome },
@@ -84,82 +84,125 @@ ruleTester.run('label-has-for', rule, {
8484
{ code: '<label><input /></label>', options: optionsRequiredNesting },
8585
{ code: '<label htmlFor="input"><input /></label>', options: optionsRequiredEvery },
8686
{ code: '<label><input /></label>', options: optionsChildrenAllowed },
87-
{ code: '<Descriptor htmlFor="foo">Test!</Descriptor>', options: [assign({}, array, optionsChildrenAllowed)] },
87+
{ code: '<Descriptor htmlFor="foo">Test!</Descriptor>', options: [assign({}, optionsComponents, optionsChildrenAllowed)] },
8888
{ code: '<label>Test!</label>', options: optionsChildrenAllowed },
8989
{ code: '<label htmlFor="foo">Test!</label>', options: optionsChildrenAllowed },
9090
{ code: '<label>{children}</label>', options: optionsChildrenAllowed },
9191
{ code: '<label htmlFor="children">{children}</label>', options: optionsChildrenAllowed },
92+
{ code: '<label htmlFor={id}>{ labelText }<div><input id={id} type="checkbox" name={id} value={value} /></div></label>', options: optionsRequiredEvery },
93+
{ code: '<label htmlFor={id}>{ labelText }<div><div><div><div><input id={id} type="checkbox" name={id} value={value} /></div></div></div></div></label>', options: optionsRequiredEvery },
9294
].map(parserOptionsMapper),
9395
invalid: [
9496
// DEFAULT ELEMENT 'label' TESTS
95-
{ code: '<label id="foo" />', errors: [expectedEveryError] },
96-
{ code: '<label htmlFor={undefined} />', errors: [expectedEveryError] },
97-
{ code: '<label htmlFor={`${undefined}`} />', errors: [expectedEveryError] },
98-
{ code: '<label>First Name</label>', errors: [expectedEveryError] },
99-
{ code: '<label {...props}>Foo</label>', errors: [expectedEveryError] },
100-
{ code: '<label><input /></label>', errors: [expectedEveryError] },
101-
{ code: '<label>{children}</label>', errors: [expectedEveryError] },
102-
{ code: '<label htmlFor="foo" />', errors: [expectedEveryError] },
103-
{ code: '<label htmlFor={"foo"} />', errors: [expectedEveryError] },
104-
{ code: '<label htmlFor={foo} />', errors: [expectedEveryError] },
105-
{ code: '<label htmlFor={`${id}`} />', errors: [expectedEveryError] },
106-
{ code: '<label htmlFor="foo">Test!</label>', errors: [expectedEveryError] },
97+
{ code: '<label id="foo" />', errors: [expectedEveryError], options: optionsRequiredEvery },
98+
{ code: '<label htmlFor={undefined} />', errors: [expectedEveryError], options: optionsRequiredEvery },
99+
{ code: '<label htmlFor={`${undefined}`} />', errors: [expectedEveryError], options: optionsRequiredEvery },
100+
{ code: '<label>First Name</label>', errors: [expectedEveryError], options: optionsRequiredEvery },
101+
{ code: '<label {...props}>Foo</label>', errors: [expectedEveryError], options: optionsRequiredEvery },
102+
{ code: '<label><input /></label>', errors: [expectedEveryError], options: optionsRequiredEvery },
103+
{ code: '<label>{children}</label>', errors: [expectedEveryError], options: optionsRequiredEvery },
104+
{ code: '<label htmlFor="foo" />', errors: [expectedEveryError], options: optionsRequiredEvery },
105+
{ code: '<label htmlFor={"foo"} />', errors: [expectedEveryError], options: optionsRequiredEvery },
106+
{ code: '<label htmlFor={foo} />', errors: [expectedEveryError], options: optionsRequiredEvery },
107+
{ code: '<label htmlFor={`${id}`} />', errors: [expectedEveryError], options: optionsRequiredEvery },
108+
{ code: '<label htmlFor="foo">Test!</label>', errors: [expectedEveryError], options: optionsRequiredEvery },
109+
{ code: '<label htmlFor={id}>{ labelText }<div><div><div><div><div id={id} type="checkbox" name={id} value={value} /></div></div></div></div></label>', errors: [expectedEveryError], options: optionsRequiredEvery },
107110
//
108111
// // CUSTOM ELEMENT ARRAY OPTION TESTS
109-
{ code: '<Label></Label>', errors: [expectedEveryError], options: array },
110-
{ code: '<Label htmlFor="foo" />', errors: [expectedEveryError], options: array },
111-
{ code: '<Label htmlFor={"foo"} />', errors: [expectedEveryError], options: array },
112-
{ code: '<Label htmlFor={foo} />', errors: [expectedEveryError], options: array },
113-
{ code: '<Label htmlFor={`${id}`} />', errors: [expectedEveryError], options: array },
114-
{ code: '<Label htmlFor="foo">Test!</Label>', errors: [expectedEveryError], options: array },
115-
{ code: '<Descriptor htmlFor="foo" />', errors: [expectedEveryError], options: array },
116-
{ code: '<Descriptor htmlFor={"foo"} />', errors: [expectedEveryError], options: array },
117-
{ code: '<Descriptor htmlFor={foo} />', errors: [expectedEveryError], options: array },
118-
{ code: '<Descriptor htmlFor={`${id}`} />', errors: [expectedEveryError], options: array },
112+
{
113+
code: '<Label></Label>',
114+
errors: [expectedEveryError],
115+
options: optionsComponents,
116+
},
117+
{
118+
code: '<Label htmlFor="foo" />',
119+
errors: [expectedEveryError],
120+
options: [{ ...optionsComponents[0], ...optionsRequiredEvery[0] }],
121+
},
122+
{
123+
code: '<Label htmlFor={"foo"} />',
124+
errors: [expectedEveryError],
125+
options: [{ ...optionsComponents[0], ...optionsRequiredEvery[0] }],
126+
},
127+
{
128+
code: '<Label htmlFor={foo} />',
129+
errors: [expectedEveryError],
130+
options: [{ ...optionsComponents[0], ...optionsRequiredEvery[0] }],
131+
},
132+
{
133+
code: '<Label htmlFor={`${id}`} />',
134+
errors: [expectedEveryError],
135+
options: [{ ...optionsComponents[0], ...optionsRequiredEvery[0] }],
136+
},
137+
{
138+
code: '<Label htmlFor="foo">Test!</Label>',
139+
errors: [expectedEveryError],
140+
options: [{ ...optionsComponents[0], ...optionsRequiredEvery[0] }],
141+
},
142+
{
143+
code: '<Descriptor htmlFor="foo" />',
144+
errors: [expectedEveryError],
145+
options: [{ ...optionsComponents[0], ...optionsRequiredEvery[0] }],
146+
},
147+
{
148+
code: '<Descriptor htmlFor={"foo"} />',
149+
errors: [expectedEveryError],
150+
options: [{ ...optionsComponents[0], ...optionsRequiredEvery[0] }],
151+
},
152+
{
153+
code: '<Descriptor htmlFor={foo} />',
154+
errors: [expectedEveryError],
155+
options: [{ ...optionsComponents[0], ...optionsRequiredEvery[0] }],
156+
},
157+
{
158+
code: '<Descriptor htmlFor={`${id}`} />',
159+
errors: [expectedEveryError],
160+
options: [{ ...optionsComponents[0], ...optionsRequiredEvery[0] }],
161+
},
119162
{
120163
code: '<Descriptor htmlFor="foo">Test!</Descriptor>',
121164
errors: [expectedEveryError],
122-
options: array,
165+
options: [{ ...optionsComponents[0], ...optionsRequiredEvery[0] }],
123166
},
124-
{ code: '<Label id="foo" />', errors: [expectedEveryError], options: array },
167+
{ code: '<Label id="foo" />', errors: [expectedEveryError], options: optionsComponents },
125168
{
126169
code: '<Label htmlFor={undefined} />',
127170
errors: [expectedEveryError],
128-
options: array,
171+
options: optionsComponents,
129172
},
130173
{
131174
code: '<Label htmlFor={`${undefined}`} />',
132175
errors: [expectedEveryError],
133-
options: array,
176+
options: optionsComponents,
134177
},
135-
{ code: '<Label>First Name</Label>', errors: [expectedEveryError], options: array },
178+
{ code: '<Label>First Name</Label>', errors: [expectedEveryError], options: optionsComponents },
136179
{
137180
code: '<Label {...props}>Foo</Label>',
138181
errors: [expectedEveryError],
139-
options: array,
182+
options: optionsComponents,
140183
},
141-
{ code: '<Descriptor id="foo" />', errors: [expectedEveryError], options: array },
184+
{ code: '<Descriptor id="foo" />', errors: [expectedEveryError], options: optionsComponents },
142185
{
143186
code: '<Descriptor htmlFor={undefined} />',
144187
errors: [expectedEveryError],
145-
options: array,
188+
options: optionsComponents,
146189
},
147190
{
148191
code: '<Descriptor htmlFor={`${undefined}`} />',
149192
errors: [expectedEveryError],
150-
options: array,
193+
options: optionsComponents,
151194
},
152195
{
153196
code: '<Descriptor>First Name</Descriptor>',
154197
errors: [expectedEveryError],
155-
options: array,
198+
options: optionsComponents,
156199
},
157200
{
158201
code: '<Descriptor {...props}>Foo</Descriptor>',
159202
errors: [expectedEveryError],
160-
options: array,
203+
options: optionsComponents,
161204
},
162-
{ code: '<label>{children}</label>', errors: [expectedEveryError], options: array },
205+
{ code: '<label>{children}</label>', errors: [expectedEveryError], options: optionsComponents },
163206
{ code: '<label htmlFor="foo" />', errors: [expectedNestingError], options: optionsRequiredNesting },
164207
{ code: '<label>First Name</label>', errors: [expectedNestingError], options: optionsRequiredNesting },
165208
{ code: '<label>First Name</label>', errors: [expectedSomeError], options: optionsRequiredSome },

src/rules/label-has-for.js

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,23 @@ const schema = {
2626
allowChildren: { type: 'boolean' },
2727
},
2828
};
29-
30-
const validateNesting = node => node.parent.children.some((child) => {
31-
const opener = child.openingElement;
32-
return child.type === 'JSXElement' && opener && opener.name.name === 'input';
33-
});
29+
// Breadth-first search, assuming that HTML for forms is shallow.
30+
function validateNesting(node) {
31+
let queue = [...node.parent.children];
32+
let child;
33+
let opener;
34+
while (queue.length) {
35+
child = queue.shift();
36+
opener = child.openingElement;
37+
if (child.type === 'JSXElement' && opener && opener.name.name === 'input') {
38+
return true;
39+
}
40+
if (child.children) {
41+
queue = queue.concat(child.children);
42+
}
43+
}
44+
return false;
45+
}
3446

3547
const validateId = (node) => {
3648
const htmlForAttr = getProp(node.attributes, 'htmlFor');

src/util/mayHaveAccessibleLabel.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ export default function mayHaveAccessibleLabel(
6161
return true;
6262
}
6363
// Check for JSXText.
64+
// $FlowFixMe Remove after updating ast-types-flow
6465
if (node.type === 'JSXText' && !!node.value) {
6566
return true;
6667
}

0 commit comments

Comments
 (0)