brendanmorrell/eslint-plugin-styled-components-a11y

label-has-associated-control Rule is Triggered Even when Associated Control is Present

Closed this issue · 9 comments

Integrating this plugin to our code base there was one occasion where the a11y/label-has-associated-control rule is triggered even when an associated control was present just below the label. We couldn't understand exactly why this is the case? 🤔 Maybe this plugin couldn't understand the underlying react.input as a proper input control?

Associated PR Discussion: opencollective/opencollective-frontend#5961 (comment)

Interesting. The code looks normal. I'll try and take a look soon. Thanks for pointing it out

Interesting. The code looks normal. I'll try and take a look soon. Thanks for pointing it out

You are welcome; let us know if there's anything you need from our end. 👍🏽

42tte commented

@SudharakaP It looks like your eslintconfig extends styled-components-a11y which extends eslint-config-airbnb which force both id & nesting rules to be followed. However in your code you only associated them by id. You can override the rule to check for only one of them with this:

'jsx-a11y/label-has-associated-control': [
      'error',
      {
        required: {
          some: ['nesting', 'id'],
        },
      },
    ],

Or you can change your code to something like this:

<label for="id">
    My label
    <input id="id">
</label>

@42tte : Thanks much for looking into this issue. 🥇 I've opened a PR which fixes this in our repos, but my understanding is that this is still an issue with this plugin as ideally it should be handled automatically without the user doing manual configurations for this rule. 🤔

cc: @brendanmorrell

I'm running into this now myself: I have a simple set of a styled label wrapping a styled input, and this rule is triggered.

The specific code triggering the error (some parts elided):

import styled from 'styled-components';

const ProfilePictureLabel = styled.label`
    display: block;

    & > img {
        cursor: pointer;
        height: 160px;
        position: relative;
        transition: opacity 50ms ease-in-out;
        width: 160px;
        &.round {
            border-radius: 50%;
        }
        &:hover {
            opacity: 0.3;
        }
    }
`;

const ProfilePictureInput = styled.input`
    display: none;
`;

export function ProfileSummary({
    [...]
}: Props): JSX.Element {
    [...]
    return (
        [...]
            <ProfilePictureLabel>
                <img
                    alt={displayName}
                    className={accountType === 'BUSINESS' ? undefined : 'round'}
                    src={profileImageUrl}
                />
                <ProfilePictureInput
                    accept="image/*"
                    disabled={actionState == 'loading'}
                    onChange={event => setActionInput(event.target?.files?.[0])}
                    type="file"
                />
            </ProfilePictureLabel>
        [...]
    );
}

Is it because it is a file input? And/or because it is set to display: none?

This rule does seem to be imperfect at the moment. I am traveling the next couple months, but when I get back I can take a closer look and see if I can find anything

42tte commented

Since you state this plugin is meant to be the equivalent to eslint-plugin-jsx-a11y you should probably not extend eslint-config-airbnb/rules/react-a11y since the latter apparently have at least one different rule.