Skip to content

Commit

Permalink
Merge branch 'master' into formfield-name
Browse files Browse the repository at this point in the history
  • Loading branch information
taysea committed Nov 14, 2021
2 parents 1e62cd8 + a5d908d commit bdf7d2e
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 0 deletions.
35 changes: 35 additions & 0 deletions docs/rules/formfield-htmlfor-id.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Rule to enforce matching htmlFor and id on FormField and its child input, respectively (formfield-htmlfor-id)

It is necessary to associate a label with its input. This allows assistive technology to refer to the correct label when presenting a form control.

Read more about [W3C Form Control Accessibility](https://www.w3.org/WAI/tutorials/forms/labels/).

## Rule Details

This rule aims to ensure that FormField has an `htmlFor` prop that matches the `id` prop of its child input.

Examples of **incorrect** code for this rule:

```js

<FormField label="Test Input">
<TestInput />
</FormField>

<FormField label="Test Input" htmlFor="test-input">
<TestInput />
</FormField>

<FormField label="Test Input">
<TestInput id="test-input" />
</FormField>

```

Examples of **correct** code for this rule:

```js
<FormField label="Test Input" htmlFor="test-input">
<TestInput id="test-input" />
</FormField>
```
1 change: 1 addition & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ module.exports.configs = {
'grommet/button-single-kind': 'error',
'grommet/datatable-aria-describedby': 'error',
'grommet/datatable-groupby-onmore': 'error',
'grommet/formfield-htmlfor-id': 'error',
'grommet/formfield-name': 'error',
'grommet/formfield-prefer-children': 'error',
'grommet/image-alt-text': 'error',
Expand Down
61 changes: 61 additions & 0 deletions lib/rules/formfield-htmlfor-id.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* @fileoverview Rule to enforce matching htmlFor and id on FormField and its child input, respectively
* @author Taylor Seamans
*/
'use strict';

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = {
meta: {
docs: {
description:
'Rule to enforce matching htmlFor and id on FormField and its child input, respectively',
category: 'Best Practices',
recommended: true,
},
fixable: null, // or "code" or "whitespace"
messages: {
'formfield-htmlfor-id':
'FormField requires `htmlFor` prop and its child input requires an `id` prop with matching value. This associates a FormField label with its input as is required for assistive technology.',
},
},

create: function (context) {
return {
JSXElement(node) {
if (node.openingElement.name.name === 'FormField') {
let associatedLabel;

node.children.forEach((child) => {
node?.openingElement?.attributes?.forEach((attribute) => {
if (attribute?.name?.name === 'htmlFor') {
if (
child.type === 'JSXElement' &&
child.openingElement.attributes
) {
child.openingElement.attributes.forEach((childAttribute) => {
if (
childAttribute?.name?.name === 'id' &&
childAttribute?.value?.value === attribute?.value?.value
) {
associatedLabel = true;
}
});
}
}
});
});

if (!associatedLabel)
context.report({
node: node,
messageId: 'formfield-htmlfor-id',
});
}
},
};
},
};
60 changes: 60 additions & 0 deletions tests/lib/rules/formfield-htmlfor-id.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/**
* @fileoverview Rule to enforce matching htmlFor and id on FormField and its child input, respectively
* @author Taylor Seamans
*/
'use strict';

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

var rule = require('../../../lib/rules/formfield-htmlfor-id'),
RuleTester = require('eslint').RuleTester;

//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------

var ruleTester = new RuleTester({
parserOptions: { ecmaFeatures: { jsx: true } },
});
ruleTester.run('formfield-htmlfor-id', rule, {
valid: [
'<FormField label="Test Input" htmlFor="test-input"><TextInput id="test-input" /></FormField>',
],

invalid: [
{
code: '<FormField label="Test Input"><TextInput /></FormField>',
errors: [
{
messageId: 'formfield-htmlfor-id',
},
],
},
{
code: '<FormField label="Test Input" htmlFor="test-input"><TextInput /></FormField>',
errors: [
{
messageId: 'formfield-htmlfor-id',
},
],
},
{
code: '<FormField label="Test Input"><TextInput id="test-input" /></FormField>',
errors: [
{
messageId: 'formfield-htmlfor-id',
},
],
},
{
code: '<FormField label="Test Input" htmlFor="other-input"><TextInput id="test-input" /></FormField>',
errors: [
{
messageId: 'formfield-htmlfor-id',
},
],
},
],
});

0 comments on commit bdf7d2e

Please sign in to comment.