From dc5305e96918193431b17c60f35812b19e3932f2 Mon Sep 17 00:00:00 2001 From: Joana Oliveira Date: Tue, 12 Mar 2024 17:08:40 +0000 Subject: [PATCH] Added rule for Avatar in FluentUI v9 --- COVERAGE.md | 4 +- README.md | 1 + docs/rules/avatar-needs-name-v9.md | 33 +++++++++++++ lib/rules/avatar-needs-name-v9.js | 58 ++++++++++++++++++++++ lib/rules/index.js | 3 +- tests/lib/rules/avatar-needs-name-v9.js | 65 +++++++++++++++++++++++++ 6 files changed, 161 insertions(+), 3 deletions(-) create mode 100644 docs/rules/avatar-needs-name-v9.md create mode 100644 lib/rules/avatar-needs-name-v9.js create mode 100644 tests/lib/rules/avatar-needs-name-v9.js diff --git a/COVERAGE.md b/COVERAGE.md index 4fd4c56..f6ba1c9 100644 --- a/COVERAGE.md +++ b/COVERAGE.md @@ -3,8 +3,8 @@ We currently cover the following components: - [] Accordion - - [] Avatar - - [] AvatarGroup + - [x] Avatar + - [x] AvatarGroup - [] Badge - [x] Button - [] CompoundButton diff --git a/README.md b/README.md index e43f21d..36b58fb 100644 --- a/README.md +++ b/README.md @@ -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. | | diff --git a/docs/rules/avatar-needs-name-v9.md b/docs/rules/avatar-needs-name-v9.md new file mode 100644 index 0000000..819cb4b --- /dev/null +++ b/docs/rules/avatar-needs-name-v9.md @@ -0,0 +1,33 @@ +# Accessibility: Avatar must have an accessible labelling: name, aria-label, aria-labelledby (`@microsoft/fluentui-jsx-a11y/avatar-needs-name-v9`) + + + +All interactive elements must have an accessible name. + +Avatar lacks an accessible name without a name or accessible labelling. + + + +## Rule Details + +This rule aims to prevent an avatar from not having an accessible name. + +Examples of **incorrect** code for this rule: + +```jsx + + + + + +``` + +Examples of **correct** code for this rule: + +```jsx + + + + + +``` diff --git a/lib/rules/avatar-needs-name-v9.js b/lib/rules/avatar-needs-name-v9.js new file mode 100644 index 0000000..4578b03 --- /dev/null +++ b/lib/rules/avatar-needs-name-v9.js @@ -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` + }); + } + }; + } +}; diff --git a/lib/rules/index.js b/lib/rules/index.js index a46ba7c..f6a2875 100644 --- a/lib/rules/index.js +++ b/lib/rules/index.js @@ -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"), }; diff --git a/tests/lib/rules/avatar-needs-name-v9.js b/tests/lib/rules/avatar-needs-name-v9.js new file mode 100644 index 0000000..44ca2f0 --- /dev/null +++ b/tests/lib/rules/avatar-needs-name-v9.js @@ -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 + '', + '', + '<>', + '', + '', + '<>' + ], + + invalid: [ + { + code: "", + errors: [{ messageId: "missingAriaLabel" }] + }, + { + code: "", + errors: [{ messageId: "missingAriaLabel" }] + }, + { + code: "}>", + errors: [{ messageId: "missingAriaLabel" }] + }, + { + code: "} />", + errors: [{ messageId: "missingAriaLabel" }] + }, + { + code: "", + errors: [{ messageId: "missingAriaLabel" }] + }, + { + code: "", + errors: [{ messageId: "missingAriaLabel" }] + } + ] +});