Skip to content

Commit

Permalink
Merge pull request #61 from microsoft/joliveira/issue-60
Browse files Browse the repository at this point in the history
Added rule for Avatar in FluentUI v9
  • Loading branch information
aubreyquinn authored Mar 13, 2024
2 parents 7a6d74a + dc5305e commit 92fa63e
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 3 deletions.
4 changes: 2 additions & 2 deletions COVERAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
We currently cover the following components:

- [] Accordion
- [] Avatar
- [] AvatarGroup
- [x] Avatar
- [x] AvatarGroup
- [] Badge
- [x] Button
- [] CompoundButton
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ Any use of third-party trademarks or logos are subject to those third-party's po

| Name                                          | Description | 🔧 |
| :----------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :- |
| [avatar-needs-name-v9](docs/rules/avatar-needs-name-v9.md) | Accessibility: Avatar must have an accessible labelling: name, aria-label, aria-labelledby | |
| [checkbox-needs-labelling-v9](docs/rules/checkbox-needs-labelling-v9.md) | Accessibility: Checkbox without label must have an accessible and visual label: aria-labelledby | |
| [combobox-needs-labelling-v9](docs/rules/combobox-needs-labelling-v9.md) | All interactive elements must have an accessible name | |
| [icon-text-content-button-does-not-need-aria](docs/rules/icon-text-content-button-does-not-need-aria.md) | Accessibility: an image button with text content does not need aria labelling. The button already has an accessible name and the aria-label or aria-labelledby will override the text content for screen reader users. | |
Expand Down
33 changes: 33 additions & 0 deletions docs/rules/avatar-needs-name-v9.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Accessibility: Avatar must have an accessible labelling: name, aria-label, aria-labelledby (`@microsoft/fluentui-jsx-a11y/avatar-needs-name-v9`)

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

All interactive elements must have an accessible name.

Avatar lacks an accessible name without a name or accessible labelling.

<https://www.w3.org/TR/html-aria/>

## Rule Details

This rule aims to prevent an avatar from not having an accessible name.

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

```jsx
<Avatar image={{ src: "example-image" }} />
<Avatar image={{ src: "example-image" }}></Avatar>

<Label>Start date</Label>
<Avatar image={{ src: "example-image" }} />
```

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

```jsx
<Avatar image={{ src: "example-image" }} name="Jane Doe" />
<Avatar image={{ src: "example-image" }} aria-label="Jane Doe" />

<Label id="label-1">Jane Doe</Label>
<Avatar image={{ src: "example-image" }} aria-labelledby="label-1" />
```
58 changes: 58 additions & 0 deletions lib/rules/avatar-needs-name-v9.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

"use strict";

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

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

module.exports = {
meta: {
// possible error messages for the rule
messages: {
missingAriaLabel: "Accessibility: Avatar must have an accessible name"
},
// "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: {
// DONE
description: "Accessibility: Avatar must have an accessible labelling: name, aria-label, aria-labelledby",
recommended: true,
url: "https://www.w3.org/TR/html-aria/" // URL to the documentation page for this rule
},
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 an Avatar, return
if (elementType(node) !== "Avatar") {
return;
}

// if the Avatar has a name, aria-label or aria-labelledby, return
if (
hasNonEmptyProp(node.attributes, "name") ||
hasNonEmptyProp(node.attributes, "aria-label") ||
hasAssociatedLabelViaAriaLabelledBy(node, context)
) {
return;
}

// no aria
context.report({
node,
messageId: `missingAriaLabel`
});
}
};
}
};
3 changes: 2 additions & 1 deletion lib/rules/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ module.exports = {
"image-button-missing-aria-v9": require("./image-button-missing-aria-v9"),
"toolbar-missing-aria-v9": require("./toolbar-missing-aria-v9"),
"combobox-needs-labelling-v9": require("./combobox-needs-labelling-v9"),
"no-empty-components-v9": require("./no-empty-components-v9")
"no-empty-components-v9": require("./no-empty-components-v9"),
"avatar-needs-name-v9": require("./avatar-needs-name-v9"),
};
65 changes: 65 additions & 0 deletions tests/lib/rules/avatar-needs-name-v9.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

"use strict";

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

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

const rule = require("../../../lib/rules/avatar-needs-name-v9");

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

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

const ruleTester = new RuleTester();
ruleTester.run("avatar-needs-name-v9", rule, {
valid: [
// give me some code that won't trigger a warning
'<Avatar name="Jane Doe" />',
'<Avatar aria-label="Jane Doe" />',
'<><Label id="label-id">Jane Doe</Label><Avatar aria-labelledby="label-id" /></>',
'<Avatar name="Jane Doe"></Avatar>',
'<Avatar aria-label="Jane Doe"></Avatar>',
'<><Label id="label-id">Jane Doe</Label><Avatar aria-labelledby="label-id"></Avatar></>'
],

invalid: [
{
code: "<Avatar />",
errors: [{ messageId: "missingAriaLabel" }]
},
{
code: "<Avatar></Avatar>",
errors: [{ messageId: "missingAriaLabel" }]
},
{
code: "<Avatar icon={<CloseIcon />}></Avatar>",
errors: [{ messageId: "missingAriaLabel" }]
},
{
code: "<Avatar icon={<CloseIcon />} />",
errors: [{ messageId: "missingAriaLabel" }]
},
{
code: "<Avatar image={{ src: \"example-image\" }}></Avatar>",
errors: [{ messageId: "missingAriaLabel" }]
},
{
code: "<Avatar image={{ src: \"example-image\" }} />",
errors: [{ messageId: "missingAriaLabel" }]
}
]
});

0 comments on commit 92fa63e

Please sign in to comment.