Skip to content

Commit

Permalink
Added rule for dropdown accessibility
Browse files Browse the repository at this point in the history
  • Loading branch information
REDMOND\shwetasri committed Mar 11, 2024
1 parent 113e5dd commit 4ba5ef0
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 0 deletions.
31 changes: 31 additions & 0 deletions docs/rules/dropdown-needs-arialabeledby-and-label-v9.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Accessibility: Dropdown mising label or missing aria-labelledby (`@microsoft/fluentui-jsx-a11y/dropdown-needs-arialabeledby-and-label-v9`)

<!-- end auto-generated rule header -->

Accessibility: Dropdown menu must have a visual label and it needs to be linked via aria-labelledby

<https://www.w3.org/WAI/WCAG22/Techniques/aria/ARIA16>

## Ways to fix

- Add a label with an id, add the aria-labelledby having same value as id to dropdown.

## Rule Details

This rule aims to make dropdown accessible

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

```jsx
<Dropdown />
<Dropdown aria-labelledby="dropdown-id"></Dropdown>
```

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

```jsx
<>
<Label id="dropdown-id" />
<Dropdown aria-labelledby="dropdown-id"></Dropdown>
</>
```
53 changes: 53 additions & 0 deletions lib/rules/dropdown-needs-arialabeledby-and-label-v9.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

"use strict";

var elementType = require("jsx-ast-utils").elementType;
const { hasAssociatedLabelViaAriaLabelledBy } = require("../util/labelUtils");

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

module.exports = {
meta: {
// possible error messages for the rule
messages: {
missingLabelOrAriaLabeledByInDropdown: "Accessibility: Dropdown mising label or missing aria-labelledby"
},
// "problem" means the rule is identifying code that either will cause an error or may cause a confusing behavior: https://eslint.org/docs/latest/developer-guide/working-with-rules
type: "problem",
// docs for the rule
docs: {
description: "Accessibility: Dropdown menu must have a visual label and it needs to be linked via aria-labelledby",
recommended: true,
url: null
},
schema: []
},

// create (function) returns an object with methods that ESLint calls to “visit” nodes while traversing the abstract syntax tree
create(context) {
return {
// visitor functions for different types of nodes
JSXOpeningElement(node) {
// if it is not a Checkbox, return
if (elementType(node) !== "Dropdown") {
return;
}

// if the dropdown has a aria-LabeledBy with same valye return
if (hasAssociatedLabelViaAriaLabelledBy(node, context)) {
return;
}

// if it has no visual labelling, report error
context.report({
node,
messageId: `missingLabelOrAriaLabeledByInDropdown`
});
}
};
}
};
51 changes: 51 additions & 0 deletions tests/lib/rules/dropdown-needs-arialabeledby-and-label-v9.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

"use strict";

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

const RuleTester = require("eslint").RuleTester;

const rule = require("../../../lib/rules/dropdown-needs-arialabeledby-and-label-v9");

RuleTester.setDefaultConfig({
parserOptions: {
ecmaVersion: 6,
ecmaFeatures: {
jsx: true
}
}
});

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

const ruleTester = new RuleTester();
ruleTester.run("dropdown-needs-arialabeledby-and-label-v9", rule, {
valid: [
`<><Label id={comboId}>Best pet</Label> <Dropdown aria-labelledby={comboId} multiselect={true} placeholder="Select an animal" {...props} > {options.map((option) => ( <Option key={option} disabled={option === "Ferret"}> {option} </Option> ))}</Dropdown></>`,
`<><Label id={comboId2}>This is a Dropdown</Label><Dropdown aria-labelledby={comboId2} /></>`
],
invalid: [
{
code: `<Dropdown multiselect={true} placeholder="Select an animal" {...props} > {options.map((option) => ( <Option key={option} disabled={option === "Ferret"}> {option} </Option> ))}</Dropdown>`,
errors: [{ messageId: "missingLabelOrAriaLabeledByInDropdown" }]
},
{
code: `<Dropdown aria-labelledby={comboId} multiselect={true} placeholder="Select an animal" {...props} > {options.map((option) => ( <Option key={option} disabled={option === "Ferret"}> {option} </Option> ))}</Dropdown>`,
errors: [{ messageId: "missingLabelOrAriaLabeledByInDropdown" }]
},
{
code: `<><Label>This is a Dropdown</Label><Dropdown aria-labelledby={comboId} multiselect={true} placeholder="Select an animal" {...props} > {options.map((option) => ( <Option key={option} disabled={option === "Ferret"}> {option} </Option> ))}</Dropdown></>`,
errors: [{ messageId: "missingLabelOrAriaLabeledByInDropdown" }]
},
{
code: `<><Label id="another-id">This is a Dropdown</Label><Dropdown aria-labelledby={comboId} multiselect={true} placeholder="Select an animal" {...props} > {options.map((option) => ( <Option key={option} disabled={option === "Ferret"}> {option} </Option> ))}</Dropdown></>`,
errors: [{ messageId: "missingLabelOrAriaLabeledByInDropdown" }]
}
]
});

0 comments on commit 4ba5ef0

Please sign in to comment.