eslint-plugin-jsx-a11y
eslint-plugin-jsx-a11y copied to clipboard
control-has-associated-label error with label/input?
We are getting linting errors with this rule for simple JSX such as:
<label htmlFor="foo">Foo</label>
<input id="foo" type="text" />
// or
<label>
Foo
<input type="text" />
</label>
Both result in Error: A control must be associated with a text label jsx-a11y/control-has-associated-label
Our rule is setup as:
'jsx-a11y/control-has-associated-label': 'error',
Do we need to pass the second argument object to the rule? or should this be supported out of the box?
OK, adding the second argument seemed to fix - however we still can't seem to satisfy this rule using a htmlFor and id to create a relationship. For example, we have a Textbox component, that renders down to an input element:
<label htmlFor="foo">My label</label>
<Textbox id="foo" />
Our config looks like:
{
labelAttributes: ['label'],
controlComponents: [
'Textbox',
],
ignoreElements: [
'audio',
'canvas',
'embed',
'input',
'textarea',
'tr',
'video',
],
ignoreRoles: [
'grid',
'listbox',
'menu',
'menubar',
'radiogroup',
'row',
'tablist',
'toolbar',
'tree',
'treegrid',
],
depth: 2,
}
Am I missing something here?
The input has to be nested inside the label and have an id/for link in order to be maximally accessible and support all assistive devices and browsers.
It has to have both? So this rule doesn't support related labels/controls via id/for only?
It does - but you have to configure whether you want both, or just nesting, or just IDs.
However, I'd encourage you to follow the spirit of a11y by using both, so you can reach all human beings.
Ideally I'd like to configure it for either scenario, nested, both or ID's... I'm struggling to see how I configure it to support ID's.
I'd love to support doing both, but it would be too much of a limitation on our visual design where labels are often removed from the container of the control - and sadly this is out of my control.
Out of interest - if an input is wrapped in a label, why does it also need the for/ID? Are there some browsers / assistive devices that don't create a relationship of controls wrapped in a label?
@leepowellcouk yes, the latest Dragon NaturallySpeaking, i believe, requires for/ID linking, whereas for most browsers/devices, nesting is sufficient. Using either one covers 95%; the only way to get 100% is to use both.
Thanks. Just looks through the source code - I can't see any configuration options for assert as per the label-has-associated-control rule. So I'm not sure it's possible to configure to nested, for/id (both or either) in the same way as that rule - I'm sure they're intended to serve the similar purposes just coming from different directions.
Yes that’s for ‘label-has-associated-control’, not ‘control-has-associated-label’ which is what I’m talking about 🙂
On 15 Mar 2019, at 21:26, Jordan Harband [email protected] wrote:
See assert here:
Available options: 'htmlFor', 'nesting', 'both', 'either'
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.
Whoops, i think i misunderstood from the start :-) your code seems correct.
Should this be considered a feature request as per ‘label-has-associated-control’, - or would it be to difficult to work out the reverse relationship via id/htmlFor.
Currently we're having to disable this rule based on our code usage shown above.
cc @jessebeach - this rule might need the same “assert” options as the inverse rule.
We are having this warning thrown up as well:
[
<label key="label" htmlFor="element_id">Label</label>,
<DatePicker {...defaultProps} key="picker" id="element_id" name="element_name" />
]
==> 37:5 error A form label must be associated with a control jsx-a11y/label-has-associated-control
For now, we're turning off the jsx-a11y/label-has-associated-control rule.
Likewise
'jsx-a11y/label-has-associated-control': [
'error',
{ labelComponents: ['Label'], labelAttributes: ['for'], controlComponents: ['Input'], depth: 4 },
],
"eslint-plugin-jsx-a11y": "~6.2.1",
Offending code:
import Label from './Label';
import Input from './Input';
...
const requiredProps = { children: 'Yo!', for: <Input />, id: 'abc' };
it('renders the component when passed required props', () => {
const { container } = render(<Label {...requiredProps} />); // <-- this line has lint error
expect(container.firstChild).toMatchSnapshot();
});
Tried a few variations of the for property with no success.
It’s called htmlFor, not for
Our control (for some reason) was taking in for but passing that value to htmlFor. I changed that but it didn't fix the issue.
I did fix it though:
render(<Label {...requiredProps} />);
to
render(<Label htmlFor="anyValueHere" {...requiredProps} />);
I believe I'm seeing the same thing with the following snippet on eslint-plugin-jsx-a11y 6.2.3. I believe we're going to disable the rule for now and just re-enable after it gets resolved.
<div className="field">
<label htmlFor="nameInput" className="label">Name</label>
<div className="control has-icons-left">
<input
id="nameInput"
className="input"
disabled={isPosting}
type="text"
name="name"
placeholder="How should we address you"
value={name}
onChange={this.onChange}
/>
<span className="icon is-small is-left">
<i className="fas fa-tag" />
</span>
</div>
</div>
@fritogotlayed what issue? The input also needs to be inside the label.
@ljharb My understanding from the docs here under "Case: The label is a sibling of the control" for section "How do I resolve this error?" indicates the above snippet should be fine. Am I misunderstanding the docs or could they be out of date?
ah, the default for "assert" is indeed "either" (although for complete a11y coverage, it needs to be "both"), so yes I'd expect your snippet to work.
Can you use eslint --print-config to confirm how your rule is configured? the airbnb config, for example, sets it to "both".
It's a pretty big dump so I created a Gist. https://gist.github.com/fritogotlayed/ac7d1ab14b09c987da85f4ae0684288a
Also, huge thanks for taking some time to help though this!
EDIT:
I just realized that the output for lint is label-has-associated-control not control-has-associated-label. I must of had word dyslexia when I commented this morning. I'm still puzzled though as to why I'm getting the error when I believe it should still work from the docs.
/[path]/src/screens/RegisterScreen.jsx
153:13 error A form label must be associated with a control jsx-a11y/label-has-associated-control
172:13 error A form label must be associated with a control jsx-a11y/label-has-associated-control
193:13 error A form label must be associated with a control jsx-a11y/label-has-associated-control
213:13 error A form label must be associated with a control jsx-a11y/label-has-associated-control
@fritogotlayed in that case, can you file a new issue for your problem, since this issue is about control-has, not label-has?
@fritogotlayed however the answer is likely because the default depth is 2; try setting it to 3.
Definitely will do. Let me try manually setting the depth first so I don't create an issue that immediately closes X-D
EDIT: It appears the default depth I'm working with is 25. I'll create a new issue.
"jsx-a11y/label-has-associated-control": [
"error",
{
"labelComponents": [],
"labelAttributes": [],
"controlComponents": [],
"assert": "both",
"depth": 25
}
],
@balibebas in general, you need the input to both be inside the label and also linked to it with htmlFor. I'm not sure why that default eslint setup would have it configured that way - the airbnb config has it set to require both, though.
No, the Airbnb config (which i maintain) did that because there's some devices that only work with nesting, and others that only work with linking, so in order to reach 100% of human beings, you have to always do both.
Yes, I'm suggesting that example shouldn't warn on any configuration. Can you provide the output of eslint --print-config path/to/file for the file that has that warning?
That's very strange; maybe Gatsby has some nonstandard way of applying an eslint config (it should be in package.json, or in .eslintrc, or .eslintrc.js, usually)
Accessibility is about many things; performance is one of the least important ones - and for development, the network effect is one of the most important.
Code:
<label className="options-toolbar__setting-btn">
<input
type="checkbox"
defaultChecked={showSimpleSKUS}
onChange={() => {
StorageHelper.saveToStorage('showSimpleSKUS', !showSimpleSKUS);
setShowSimpleSKUS(!showSimpleSKUS);
}}
/>
Show Simple SKUs
</label>
/app/components/Toolbar/SettingsForm.jsx
33:9 error A form label must be associated with a control jsx-a11y/label-has-associated-control
44:9 error A form label must be associated with a control jsx-a11y/label-has-associated-control
55:9 error A form label must be associated with a control jsx-a11y/label-has-associated-control
66:9 error A form label must be associated with a control jsx-a11y/label-has-associated-control
77:9 error A form label must be associated with a control jsx-a11y/label-has-associated-control
89:9 error A form label must be associated with a control jsx-a11y/label-has-associated-control
101:9 error A form label must be associated with a control jsx-a11y/label-has-associated-control
113:9 error A form label must be associated with a control jsx-a11y/label-has-associated-control
https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/master/docs/rules/label-has-associated-control.md#case-i-just-want-a-text-label-associated-with-an-input <- doesn't seem to work...
@OZZlE depending on your config, you may need to also for-ID link the two, as well as nest them.