From 028b86d03ae7e8c226067213a304566526f90cfd Mon Sep 17 00:00:00 2001 From: Sidharth Sharma Date: Fri, 19 Sep 2025 01:14:39 -0400 Subject: [PATCH 01/11] Add accessibility rules for InfoLabel, MenuButton, SplitButton, and Card components. Add required test cases. Update coverage.md. --- COVERAGE.md | 10 ++-- .../buttonBasedComponents.js | 2 +- lib/index.ts | 8 +++ .../buttons/menu-button-needs-labelling.ts | 26 +++++++++ .../buttons/split-button-needs-labelling.ts | 26 +++++++++ lib/rules/card-needs-accessible-name.ts | 26 +++++++++ lib/rules/index.ts | 4 ++ lib/rules/infolabel-needs-labelling.ts | 26 +++++++++ lib/util/labelUtils.ts | 2 +- .../menu-button-needs-labelling.test.ts | 51 +++++++++++++++++ .../split-button-needs-labelling.test.ts | 51 +++++++++++++++++ .../rules/card-needs-accessible-name.test.ts | 49 ++++++++++++++++ .../rules/infolabel-needs-labelling.test.ts | 56 +++++++++++++++++++ 13 files changed, 330 insertions(+), 7 deletions(-) create mode 100644 lib/rules/buttons/menu-button-needs-labelling.ts create mode 100644 lib/rules/buttons/split-button-needs-labelling.ts create mode 100644 lib/rules/card-needs-accessible-name.ts create mode 100644 lib/rules/infolabel-needs-labelling.ts create mode 100644 tests/lib/rules/buttons/menu-button-needs-labelling.test.ts create mode 100644 tests/lib/rules/buttons/split-button-needs-labelling.test.ts create mode 100644 tests/lib/rules/card-needs-accessible-name.test.ts create mode 100644 tests/lib/rules/infolabel-needs-labelling.test.ts diff --git a/COVERAGE.md b/COVERAGE.md index 872b914..f4d7b9a 100644 --- a/COVERAGE.md +++ b/COVERAGE.md @@ -13,13 +13,13 @@ We currently cover the following components: - [x] Button - [x] Button - [X] CompoundButton - - [] MenuButton + - [x] MenuButton - [X] MenuItem - - [] SplitButton + - [x] SplitButton - [x] ToggleButton - [] ToolbarToggleButton - - [] Card - - [] Card + - [x] Card + - [x] Card - [] CardFooter - [] CardHeader - [] CardPreview @@ -37,7 +37,7 @@ We currently cover the following components: - [x] Field - [N/A] FluentProvider - [] Image - - [] InfoLabel + - [x] InfoLabel - [x] Input - [x] Label - [x] Link diff --git a/lib/applicableComponents/buttonBasedComponents.js b/lib/applicableComponents/buttonBasedComponents.js index 469972e..ac3116d 100644 --- a/lib/applicableComponents/buttonBasedComponents.js +++ b/lib/applicableComponents/buttonBasedComponents.js @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -const applicableComponents = ["Button", "ToggleButton", "CompoundButton"]; +const applicableComponents = ["Button", "ToggleButton", "CompoundButton", "MenuButton", "SplitButton"]; module.exports = { applicableComponents diff --git a/lib/index.ts b/lib/index.ts index 1c063e5..3087665 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -22,6 +22,7 @@ module.exports = { "@microsoft/fluentui-jsx-a11y/avoid-using-aria-describedby-for-primary-labelling": "error", "@microsoft/fluentui-jsx-a11y/badge-needs-accessible-name": "error", "@microsoft/fluentui-jsx-a11y/breadcrumb-needs-labelling": "error", + "@microsoft/fluentui-jsx-a11y/card-needs-accessible-name": "error", "@microsoft/fluentui-jsx-a11y/checkbox-needs-labelling": "error", "@microsoft/fluentui-jsx-a11y/colorswatch-needs-labelling": "error", "@microsoft/fluentui-jsx-a11y/combobox-needs-labelling": "error", @@ -35,8 +36,10 @@ module.exports = { "@microsoft/fluentui-jsx-a11y/image-button-missing-aria": "error", "@microsoft/fluentui-jsx-a11y/image-needs-alt": "error", "@microsoft/fluentui-jsx-a11y/imageswatch-needs-labelling": "error", + "@microsoft/fluentui-jsx-a11y/infolabel-needs-labelling": "error", "@microsoft/fluentui-jsx-a11y/input-components-require-accessible-name": "error", "@microsoft/fluentui-jsx-a11y/link-missing-labelling": "error", + "@microsoft/fluentui-jsx-a11y/menu-button-needs-labelling": "error", "@microsoft/fluentui-jsx-a11y/menu-item-needs-labelling": "error", "@microsoft/fluentui-jsx-a11y/no-empty-buttons": "error", "@microsoft/fluentui-jsx-a11y/no-empty-components": "error", @@ -49,6 +52,7 @@ module.exports = { "@microsoft/fluentui-jsx-a11y/spin-button-needs-labelling": "error", "@microsoft/fluentui-jsx-a11y/spin-button-unrecommended-labelling": "error", "@microsoft/fluentui-jsx-a11y/spinner-needs-labelling": "error", + "@microsoft/fluentui-jsx-a11y/split-button-needs-labelling": "error", "@microsoft/fluentui-jsx-a11y/swatchpicker-needs-labelling": "error", "@microsoft/fluentui-jsx-a11y/switch-needs-labelling": "error", "@microsoft/fluentui-jsx-a11y/tablist-and-tabs-need-labelling": "error", @@ -65,6 +69,7 @@ module.exports = { "avoid-using-aria-describedby-for-primary-labelling": rules.avoidUsingAriaDescribedByForPrimaryLabelling, "badge-needs-accessible-name": rules.badgeNeedsAccessibleName, "breadcrumb-needs-labelling": rules.breadcrumbNeedsLabelling, + "card-needs-accessible-name": rules.cardNeedsAccessibleName, "checkbox-needs-labelling": rules.checkboxNeedsLabelling, "colorswatch-needs-labelling": rules.colorSwatchNeedsLabelling, "combobox-needs-labelling": rules.comboboxNeedsLabelling, @@ -78,8 +83,10 @@ module.exports = { "image-button-missing-aria": rules.imageButtonMissingAria, "image-needs-alt": rules.imageNeedsAlt, "imageswatch-needs-labelling": rules.imageSwatchNeedsLabelling, + "infolabel-needs-labelling": rules.infoLabelNeedsLabelling, "input-components-require-accessible-name": rules.inputComponentsRequireAccessibleName, "link-missing-labelling": rules.linkMissingLabelling, + "menu-button-needs-labelling": rules.menuButtonNeedsLabelling, "menu-item-needs-labelling": rules.menuItemNeedsLabelling, "no-empty-buttons": rules.noEmptyButtons, "no-empty-components": rules.noEmptyComponents, @@ -92,6 +99,7 @@ module.exports = { "spin-button-needs-labelling": rules.spinButtonNeedsLabelling, "spin-button-unrecommended-labelling": rules.spinButtonUnrecommendedLabelling, "spinner-needs-labelling": rules.spinnerNeedsLabelling, + "split-button-needs-labelling": rules.splitButtonNeedsLabelling, "swatchpicker-needs-labelling": rules.swatchpickerNeedsLabelling, "switch-needs-labelling": rules.switchNeedsLabelling, "tablist-and-tabs-need-labelling": rules.tablistAndTabsNeedLabelling, diff --git a/lib/rules/buttons/menu-button-needs-labelling.ts b/lib/rules/buttons/menu-button-needs-labelling.ts new file mode 100644 index 0000000..e1cc841 --- /dev/null +++ b/lib/rules/buttons/menu-button-needs-labelling.ts @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { ESLintUtils } from "@typescript-eslint/utils"; +import { makeLabeledControlRule } from "../../util/ruleFactory"; + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +export default ESLintUtils.RuleCreator.withoutDocs( + makeLabeledControlRule({ + component: "MenuButton", + messageId: "menuButtonNeedsLabelling", + description: "Accessibility: MenuButton must have an accessible name via aria-label, text content, aria-labelledby, etc.", + labelProps: ["aria-label"], + allowFieldParent: false, + allowHtmlFor: false, + allowLabelledBy: true, + allowWrappingLabel: true, + allowTooltipParent: true, + allowDescribedBy: false, + allowLabeledChild: true, + allowTextContentChild: true + }) +); diff --git a/lib/rules/buttons/split-button-needs-labelling.ts b/lib/rules/buttons/split-button-needs-labelling.ts new file mode 100644 index 0000000..b7c3a2c --- /dev/null +++ b/lib/rules/buttons/split-button-needs-labelling.ts @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { ESLintUtils } from "@typescript-eslint/utils"; +import { makeLabeledControlRule } from "../../util/ruleFactory"; + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +export default ESLintUtils.RuleCreator.withoutDocs( + makeLabeledControlRule({ + component: "SplitButton", + messageId: "splitButtonNeedsLabelling", + description: "Accessibility: SplitButton must have an accessible name via aria-label, text content, aria-labelledby, etc.", + labelProps: ["aria-label"], + allowFieldParent: false, + allowHtmlFor: false, + allowLabelledBy: true, + allowWrappingLabel: true, + allowTooltipParent: true, + allowDescribedBy: false, + allowLabeledChild: true, + allowTextContentChild: true + }) +); diff --git a/lib/rules/card-needs-accessible-name.ts b/lib/rules/card-needs-accessible-name.ts new file mode 100644 index 0000000..6095db2 --- /dev/null +++ b/lib/rules/card-needs-accessible-name.ts @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { ESLintUtils } from "@typescript-eslint/utils"; +import { makeLabeledControlRule } from "../util/ruleFactory"; + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +export default ESLintUtils.RuleCreator.withoutDocs( + makeLabeledControlRule({ + component: "Card", + messageId: "cardNeedsAccessibleName", + description: "Accessibility: Interactive Card must have an accessible name via aria-label, aria-labelledby, etc.", + labelProps: ["aria-label"], + allowFieldParent: false, + allowHtmlFor: false, + allowLabelledBy: true, + allowWrappingLabel: false, + allowTooltipParent: true, + allowDescribedBy: false, + allowLabeledChild: true, + allowTextContentChild: true + }) +); diff --git a/lib/rules/index.ts b/lib/rules/index.ts index c31ac56..73cf920 100644 --- a/lib/rules/index.ts +++ b/lib/rules/index.ts @@ -7,6 +7,7 @@ export { default as avatarNeedsName } from "./avatar-needs-name"; export { default as avoidUsingAriaDescribedByForPrimaryLabelling } from "./avoid-using-aria-describedby-for-primary-labelling"; export { default as badgeNeedsAccessibleName } from "./badge-needs-accessible-name"; export { default as breadcrumbNeedsLabelling } from "./breadcrumb-needs-labelling"; +export { default as cardNeedsAccessibleName } from "./card-needs-accessible-name"; export { default as checkboxNeedsLabelling } from "./checkbox-needs-labelling"; export { default as comboboxNeedsLabelling } from "./combobox-needs-labelling"; export { default as compoundButtonNeedsLabelling } from "./buttons/compound-button-needs-labelling"; @@ -17,8 +18,10 @@ export { default as dropdownNeedsLabelling } from "./dropdown-needs-labelling"; export { default as fieldNeedsLabelling } from "./field-needs-labelling"; export { default as imageButtonMissingAria } from "./buttons/image-button-missing-aria"; export { default as imageNeedsAlt } from "./image-needs-alt"; +export { default as infoLabelNeedsLabelling } from "./infolabel-needs-labelling"; export { default as inputComponentsRequireAccessibleName } from "./input-components-require-accessible-name"; export { default as linkMissingLabelling } from "./link-missing-labelling"; +export { default as menuButtonNeedsLabelling } from "./buttons/menu-button-needs-labelling"; export { default as menuItemNeedsLabelling } from "./menu-item-needs-labelling"; export { default as noEmptyButtons } from "./buttons/no-empty-buttons"; export { default as noEmptyComponents } from "./no-empty-components"; @@ -33,6 +36,7 @@ export { default as imageSwatchNeedsLabelling } from "./imageswatch-needs-labell export { default as spinButtonNeedsLabelling } from "./spin-button-needs-labelling"; export { default as spinButtonUnrecommendedLabelling } from "./spin-button-unrecommended-labelling"; export { default as spinnerNeedsLabelling } from "./spinner-needs-labelling"; +export { default as splitButtonNeedsLabelling } from "./buttons/split-button-needs-labelling"; export { default as swatchpickerNeedsLabelling } from "./swatchpicker-needs-labelling"; export { default as switchNeedsLabelling } from "./switch-needs-labelling"; export { default as tablistAndTabsNeedLabelling } from "./tablist-and-tabs-need-labelling"; diff --git a/lib/rules/infolabel-needs-labelling.ts b/lib/rules/infolabel-needs-labelling.ts new file mode 100644 index 0000000..3587e10 --- /dev/null +++ b/lib/rules/infolabel-needs-labelling.ts @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { ESLintUtils } from "@typescript-eslint/utils"; +import { makeLabeledControlRule } from "../util/ruleFactory"; + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +export default ESLintUtils.RuleCreator.withoutDocs( + makeLabeledControlRule({ + component: "InfoLabel", + messageId: "infoLabelNeedsLabelling", + description: "Accessibility: InfoLabel must have an accessible name via aria-label, text content, aria-labelledby, etc.", + labelProps: ["aria-label"], + allowFieldParent: false, + allowHtmlFor: false, + allowLabelledBy: true, + allowWrappingLabel: false, + allowTooltipParent: true, + allowDescribedBy: false, + allowLabeledChild: true, + allowTextContentChild: true + }) +); diff --git a/lib/util/labelUtils.ts b/lib/util/labelUtils.ts index b7a4b96..e41435d 100644 --- a/lib/util/labelUtils.ts +++ b/lib/util/labelUtils.ts @@ -42,7 +42,7 @@ const idLiteralDouble = '"([^"]*)"'; const idLiteralSingle = "'([^']*)'"; const exprStringDouble = '\\{\\s*"([^"]*)"\\s*\\}'; const exprStringSingle = "\\{\\s*'([^']*)'\\s*\\}"; -const exprIdentifier = "\\{\\s*([A-Za-z_$][A-Za-l0-9_$]*)\\s*\\}"; +const exprIdentifier = "\\{\\s*([A-Za-z_$][A-Za-z0-9_$]*)\\s*\\}"; // FIXED: l -> z const idOrExprRegex = new RegExp( `(?:${idLiteralDouble}|${idLiteralSingle}|${exprStringDouble}|${exprStringSingle}|${exprIdentifier})`, diff --git a/tests/lib/rules/buttons/menu-button-needs-labelling.test.ts b/tests/lib/rules/buttons/menu-button-needs-labelling.test.ts new file mode 100644 index 0000000..de54387 --- /dev/null +++ b/tests/lib/rules/buttons/menu-button-needs-labelling.test.ts @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +import { Rule } from "eslint"; +import ruleTester from "../helper/ruleTester"; +import rule from "../../../../lib/rules/buttons/menu-button-needs-labelling"; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +ruleTester.run("menu-button-needs-labelling", rule as unknown as Rule.RuleModule, { + valid: [ + // MenuButton with text content + `Menu`, + // MenuButton with aria-label + ``, + // MenuButton with aria-labelledby + `<>`, + // MenuButton wrapped in Label + ``, + // MenuButton wrapped in Tooltip + ``, + // MenuButton with labeled child + `Menu icon`, + // MenuButton with Icon child + `` + ], + invalid: [ + { + code: ``, + errors: [{ messageId: "menuButtonNeedsLabelling" }] + }, + { + code: ``, + errors: [{ messageId: "menuButtonNeedsLabelling" }] + }, + { + code: ``, + errors: [{ messageId: "menuButtonNeedsLabelling" }] + }, + { + code: `<>`, + errors: [{ messageId: "menuButtonNeedsLabelling" }] + } + ] +}); diff --git a/tests/lib/rules/buttons/split-button-needs-labelling.test.ts b/tests/lib/rules/buttons/split-button-needs-labelling.test.ts new file mode 100644 index 0000000..39a1a11 --- /dev/null +++ b/tests/lib/rules/buttons/split-button-needs-labelling.test.ts @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +import { Rule } from "eslint"; +import ruleTester from "../helper/ruleTester"; +import rule from "../../../../lib/rules/buttons/split-button-needs-labelling"; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +ruleTester.run("split-button-needs-labelling", rule as unknown as Rule.RuleModule, { + valid: [ + // SplitButton with text content + `Save`, + // SplitButton with aria-label + ``, + // SplitButton with aria-labelledby + `<>`, + // SplitButton wrapped in Label + ``, + // SplitButton wrapped in Tooltip + ``, + // SplitButton with labeled child + `Save icon`, + // SplitButton with Icon child + `` + ], + invalid: [ + { + code: ``, + errors: [{ messageId: "splitButtonNeedsLabelling" }] + }, + { + code: ``, + errors: [{ messageId: "splitButtonNeedsLabelling" }] + }, + { + code: ``, + errors: [{ messageId: "splitButtonNeedsLabelling" }] + }, + { + code: `<>`, + errors: [{ messageId: "splitButtonNeedsLabelling" }] + } + ] +}); diff --git a/tests/lib/rules/card-needs-accessible-name.test.ts b/tests/lib/rules/card-needs-accessible-name.test.ts new file mode 100644 index 0000000..48e692b --- /dev/null +++ b/tests/lib/rules/card-needs-accessible-name.test.ts @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +import { Rule } from "eslint"; +import ruleTester from "./helper/ruleTester"; +import rule from "../../../lib/rules/card-needs-accessible-name"; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +ruleTester.run("card-needs-accessible-name", rule as unknown as Rule.RuleModule, { + valid: [ + // Card with text content + `Product details`, + // Card with aria-label + ``, + // Card with aria-labelledby + `<>`, + // Card wrapped in Tooltip + ``, + // Card with labeled child + `Product image`, + // Card with Icon child + `` + ], + invalid: [ + { + code: ``, + errors: [{ messageId: "cardNeedsAccessibleName" }] + }, + { + code: ``, + errors: [{ messageId: "cardNeedsAccessibleName" }] + }, + { + code: ``, + errors: [{ messageId: "cardNeedsAccessibleName" }] + }, + { + code: `<>`, + errors: [{ messageId: "cardNeedsAccessibleName" }] + } + ] +}); diff --git a/tests/lib/rules/infolabel-needs-labelling.test.ts b/tests/lib/rules/infolabel-needs-labelling.test.ts new file mode 100644 index 0000000..231636e --- /dev/null +++ b/tests/lib/rules/infolabel-needs-labelling.test.ts @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +import { Rule } from "eslint"; +import ruleTester from "./helper/ruleTester"; +import rule from "../../../lib/rules/infolabel-needs-labelling"; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +ruleTester.run("infolabel-needs-labelling", rule as unknown as Rule.RuleModule, { + valid: [ + // InfoLabel with aria-label + ``, + // InfoLabel with text content + `Help text`, + `?`, + `â„šī¸`, + // InfoLabel with aria-labelledby that references existing element + `<>`, + // InfoLabel wrapped in Tooltip + ``, + // InfoLabel with labeled child + `Help icon`, + // InfoLabel with Icon child + `` + ], + invalid: [ + { + code: ``, + errors: [{ messageId: "infoLabelNeedsLabelling" }] + }, + { + code: ``, + errors: [{ messageId: "infoLabelNeedsLabelling" }] + }, + { + code: ``, + errors: [{ messageId: "infoLabelNeedsLabelling" }] + }, + { + code: ``, + errors: [{ messageId: "infoLabelNeedsLabelling" }] + }, + { + // aria-labelledby references ID that doesn't exist + code: `<>`, + errors: [{ messageId: "infoLabelNeedsLabelling" }] + } + ] +}); From f4915513ac0d95f591f779d759ddb06fd5512e79 Mon Sep 17 00:00:00 2001 From: Sidharth Sharma Date: Fri, 19 Sep 2025 01:19:02 -0400 Subject: [PATCH 02/11] Add rule doc. --- .../buttons/menu-button-needs-labelling.md | 33 +++++++++++++++ .../buttons/split-button-needs-labelling.md | 33 +++++++++++++++ docs/rules/card-needs-accessible-name.md | 41 +++++++++++++++++++ docs/rules/infolabel-needs-labelling.md | 33 +++++++++++++++ 4 files changed, 140 insertions(+) create mode 100644 docs/rules/buttons/menu-button-needs-labelling.md create mode 100644 docs/rules/buttons/split-button-needs-labelling.md create mode 100644 docs/rules/card-needs-accessible-name.md create mode 100644 docs/rules/infolabel-needs-labelling.md diff --git a/docs/rules/buttons/menu-button-needs-labelling.md b/docs/rules/buttons/menu-button-needs-labelling.md new file mode 100644 index 0000000..478702a --- /dev/null +++ b/docs/rules/buttons/menu-button-needs-labelling.md @@ -0,0 +1,33 @@ +# menu-button-needs-labelling + +MenuButton components must have accessible labelling for screen readers. + +## Rule Details + +This rule enforces that MenuButton components have proper accessible names through aria-label, aria-labelledby, or text content. + +### Noncompliant + +```jsx + +``` + +### Compliant + +```jsx + + + + Options + + +Options +``` + +## When Not To Use + +This rule should always be used for MenuButton components as they are interactive elements. + +## Accessibility guidelines + +- [WCAG 4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value.html) \ No newline at end of file diff --git a/docs/rules/buttons/split-button-needs-labelling.md b/docs/rules/buttons/split-button-needs-labelling.md new file mode 100644 index 0000000..f17b37b --- /dev/null +++ b/docs/rules/buttons/split-button-needs-labelling.md @@ -0,0 +1,33 @@ +# split-button-needs-labelling + +SplitButton components must have accessible labelling for screen readers. + +## Rule Details + +This rule enforces that SplitButton components have proper accessible names through aria-label, aria-labelledby, or text content. + +### Noncompliant + +```jsx + +``` + +### Compliant + +```jsx + + + + Save + + +Save +``` + +## When Not To Use + +This rule should always be used for SplitButton components as they are interactive elements. + +## Accessibility guidelines + +- [WCAG 4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value.html) \ No newline at end of file diff --git a/docs/rules/card-needs-accessible-name.md b/docs/rules/card-needs-accessible-name.md new file mode 100644 index 0000000..5690a02 --- /dev/null +++ b/docs/rules/card-needs-accessible-name.md @@ -0,0 +1,41 @@ +# card-needs-accessible-name + +Interactive Card components must have an accessible name for screen readers. + +## Rule Details + +This rule enforces that Card components have proper accessible names when they are interactive (clickable). + +### Noncompliant + +```jsx + + + Card title + + +``` + +### Compliant + +```jsx + + + Card title + + + + + + Card title + + +``` + +## When Not To Use + +If the Card is purely decorative and not interactive, this rule is not necessary. + +## Accessibility guidelines + +- [WCAG 4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value.html) \ No newline at end of file diff --git a/docs/rules/infolabel-needs-labelling.md b/docs/rules/infolabel-needs-labelling.md new file mode 100644 index 0000000..d3b901d --- /dev/null +++ b/docs/rules/infolabel-needs-labelling.md @@ -0,0 +1,33 @@ +# infolabel-needs-labelling + +InfoLabel components must have accessible labelling for screen readers. + +## Rule Details + +This rule enforces that InfoLabel components have proper accessible names through aria-label, aria-labelledby, or text content. + +### Noncompliant + +```jsx + +``` + +### Compliant + +```jsx + + + + Help text + + +Help information +``` + +## When Not To Use + +If the InfoLabel is purely decorative, this rule may not be necessary. + +## Accessibility guidelines + +- [WCAG 4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value.html) \ No newline at end of file From 4c121814ce20401857d2408f59c5edc533860e92 Mon Sep 17 00:00:00 2001 From: Sidharth Sharma Date: Fri, 19 Sep 2025 01:21:12 -0400 Subject: [PATCH 03/11] Add rule docs. --- docs/rules/menu-button-needs-labelling.md | 33 ++++++++++++++++++++++ docs/rules/split-button-needs-labelling.md | 33 ++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 docs/rules/menu-button-needs-labelling.md create mode 100644 docs/rules/split-button-needs-labelling.md diff --git a/docs/rules/menu-button-needs-labelling.md b/docs/rules/menu-button-needs-labelling.md new file mode 100644 index 0000000..478702a --- /dev/null +++ b/docs/rules/menu-button-needs-labelling.md @@ -0,0 +1,33 @@ +# menu-button-needs-labelling + +MenuButton components must have accessible labelling for screen readers. + +## Rule Details + +This rule enforces that MenuButton components have proper accessible names through aria-label, aria-labelledby, or text content. + +### Noncompliant + +```jsx + +``` + +### Compliant + +```jsx + + + + Options + + +Options +``` + +## When Not To Use + +This rule should always be used for MenuButton components as they are interactive elements. + +## Accessibility guidelines + +- [WCAG 4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value.html) \ No newline at end of file diff --git a/docs/rules/split-button-needs-labelling.md b/docs/rules/split-button-needs-labelling.md new file mode 100644 index 0000000..f17b37b --- /dev/null +++ b/docs/rules/split-button-needs-labelling.md @@ -0,0 +1,33 @@ +# split-button-needs-labelling + +SplitButton components must have accessible labelling for screen readers. + +## Rule Details + +This rule enforces that SplitButton components have proper accessible names through aria-label, aria-labelledby, or text content. + +### Noncompliant + +```jsx + +``` + +### Compliant + +```jsx + + + + Save + + +Save +``` + +## When Not To Use + +This rule should always be used for SplitButton components as they are interactive elements. + +## Accessibility guidelines + +- [WCAG 4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value.html) \ No newline at end of file From 95bc83aef5ff90a042dc2f12a4077514b2ac3987 Mon Sep 17 00:00:00 2001 From: Sidharth Sharma Date: Fri, 19 Sep 2025 01:23:14 -0400 Subject: [PATCH 04/11] Add Readme. --- README.md | 4 ++++ docs/rules/card-needs-accessible-name.md | 6 +++++- docs/rules/infolabel-needs-labelling.md | 6 +++++- docs/rules/menu-button-needs-labelling.md | 6 +++++- docs/rules/split-button-needs-labelling.md | 6 +++++- 5 files changed, 24 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 420bf64..dc82d96 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,7 @@ Any use of third-party trademarks or logos are subject to those third-party's po | [avoid-using-aria-describedby-for-primary-labelling](docs/rules/avoid-using-aria-describedby-for-primary-labelling.md) | aria-describedby provides additional context and is not meant for primary labeling. | ✅ | | | | [badge-needs-accessible-name](docs/rules/badge-needs-accessible-name.md) | | ✅ | | 🔧 | | [breadcrumb-needs-labelling](docs/rules/breadcrumb-needs-labelling.md) | All interactive elements must have an accessible name | ✅ | | | +| [card-needs-accessible-name](docs/rules/card-needs-accessible-name.md) | Accessibility: Interactive Card must have an accessible name via aria-label, aria-labelledby, etc. | ✅ | | | | [checkbox-needs-labelling](docs/rules/checkbox-needs-labelling.md) | Accessibility: Checkbox without label must have an accessible and visual label: aria-labelledby | ✅ | | | | [colorswatch-needs-labelling](docs/rules/colorswatch-needs-labelling.md) | Accessibility: ColorSwatch must have an accessible name via aria-label, Tooltip, aria-labelledby, etc.. | ✅ | | | | [combobox-needs-labelling](docs/rules/combobox-needs-labelling.md) | All interactive elements must have an accessible name | ✅ | | | @@ -128,8 +129,10 @@ Any use of third-party trademarks or logos are subject to those third-party's po | [image-button-missing-aria](docs/rules/image-button-missing-aria.md) | Accessibility: Image buttons must have accessible labelling: title, aria-label, aria-labelledby, aria-describedby | ✅ | | | | [image-needs-alt](docs/rules/image-needs-alt.md) | Accessibility: Image must have alt attribute with a meaningful description of the image. If the image is decorative, use alt="". | ✅ | | | | [imageswatch-needs-labelling](docs/rules/imageswatch-needs-labelling.md) | Accessibility: ImageSwatch must have an accessible name via aria-label, Tooltip, aria-labelledby, etc.. | ✅ | | | +| [infolabel-needs-labelling](docs/rules/infolabel-needs-labelling.md) | Accessibility: InfoLabel must have an accessible name via aria-label, text content, aria-labelledby, etc. | ✅ | | | | [input-components-require-accessible-name](docs/rules/input-components-require-accessible-name.md) | Accessibility: Input fields must have accessible labelling: aria-label, aria-labelledby or an associated label | ✅ | | | | [link-missing-labelling](docs/rules/link-missing-labelling.md) | Accessibility: Image links must have an accessible name. Add either text content, labelling to the image or labelling to the link itself. | ✅ | | 🔧 | +| [menu-button-needs-labelling](docs/rules/menu-button-needs-labelling.md) | Accessibility: MenuButton must have an accessible name via aria-label, text content, aria-labelledby, etc. | ✅ | | | | [menu-item-needs-labelling](docs/rules/menu-item-needs-labelling.md) | Accessibility: MenuItem without label must have an accessible and visual label: aria-labelledby | ✅ | | | | [no-empty-buttons](docs/rules/no-empty-buttons.md) | Accessibility: Button, ToggleButton, SplitButton, MenuButton, CompoundButton must either text content or icon or child component | ✅ | | | | [no-empty-components](docs/rules/no-empty-components.md) | FluentUI components should not be empty | ✅ | | | @@ -142,6 +145,7 @@ Any use of third-party trademarks or logos are subject to those third-party's po | [spin-button-needs-labelling](docs/rules/spin-button-needs-labelling.md) | Accessibility: SpinButtons must have an accessible label | ✅ | | | | [spin-button-unrecommended-labelling](docs/rules/spin-button-unrecommended-labelling.md) | Accessibility: Unrecommended accessibility labelling - SpinButton | ✅ | | | | [spinner-needs-labelling](docs/rules/spinner-needs-labelling.md) | Accessibility: Spinner must have either aria-label or label, aria-live and aria-busy attributes | ✅ | | | +| [split-button-needs-labelling](docs/rules/split-button-needs-labelling.md) | Accessibility: SplitButton must have an accessible name via aria-label, text content, aria-labelledby, etc. | ✅ | | | | [swatchpicker-needs-labelling](docs/rules/swatchpicker-needs-labelling.md) | Accessibility: SwatchPicker must have an accessible name via aria-label, aria-labelledby, Field component, etc.. | ✅ | | | | [switch-needs-labelling](docs/rules/switch-needs-labelling.md) | Accessibility: Switch must have an accessible label | ✅ | | | | [tablist-and-tabs-need-labelling](docs/rules/tablist-and-tabs-need-labelling.md) | This rule aims to ensure that Tabs with icons but no text labels have an accessible name and that Tablist is properly labeled. | ✅ | | | diff --git a/docs/rules/card-needs-accessible-name.md b/docs/rules/card-needs-accessible-name.md index 5690a02..8c0eb93 100644 --- a/docs/rules/card-needs-accessible-name.md +++ b/docs/rules/card-needs-accessible-name.md @@ -1,4 +1,8 @@ -# card-needs-accessible-name +# Accessibility: Interactive Card must have an accessible name via aria-label, aria-labelledby, etc (`@microsoft/fluentui-jsx-a11y/card-needs-accessible-name`) + +đŸ’ŧ This rule is enabled in the ✅ `recommended` config. + + Interactive Card components must have an accessible name for screen readers. diff --git a/docs/rules/infolabel-needs-labelling.md b/docs/rules/infolabel-needs-labelling.md index d3b901d..bef35c7 100644 --- a/docs/rules/infolabel-needs-labelling.md +++ b/docs/rules/infolabel-needs-labelling.md @@ -1,4 +1,8 @@ -# infolabel-needs-labelling +# Accessibility: InfoLabel must have an accessible name via aria-label, text content, aria-labelledby, etc (`@microsoft/fluentui-jsx-a11y/infolabel-needs-labelling`) + +đŸ’ŧ This rule is enabled in the ✅ `recommended` config. + + InfoLabel components must have accessible labelling for screen readers. diff --git a/docs/rules/menu-button-needs-labelling.md b/docs/rules/menu-button-needs-labelling.md index 478702a..93ca98b 100644 --- a/docs/rules/menu-button-needs-labelling.md +++ b/docs/rules/menu-button-needs-labelling.md @@ -1,4 +1,8 @@ -# menu-button-needs-labelling +# Accessibility: MenuButton must have an accessible name via aria-label, text content, aria-labelledby, etc (`@microsoft/fluentui-jsx-a11y/menu-button-needs-labelling`) + +đŸ’ŧ This rule is enabled in the ✅ `recommended` config. + + MenuButton components must have accessible labelling for screen readers. diff --git a/docs/rules/split-button-needs-labelling.md b/docs/rules/split-button-needs-labelling.md index f17b37b..fdc976c 100644 --- a/docs/rules/split-button-needs-labelling.md +++ b/docs/rules/split-button-needs-labelling.md @@ -1,4 +1,8 @@ -# split-button-needs-labelling +# Accessibility: SplitButton must have an accessible name via aria-label, text content, aria-labelledby, etc (`@microsoft/fluentui-jsx-a11y/split-button-needs-labelling`) + +đŸ’ŧ This rule is enabled in the ✅ `recommended` config. + + SplitButton components must have accessible labelling for screen readers. From c818570b0671bbe5a544faef90a082153ef84e18 Mon Sep 17 00:00:00 2001 From: Sidharth Sharma Date: Fri, 19 Sep 2025 01:27:27 -0400 Subject: [PATCH 05/11] Remove unused files --- .../buttons/menu-button-needs-labelling.md | 33 ------------------- .../buttons/split-button-needs-labelling.md | 33 ------------------- 2 files changed, 66 deletions(-) delete mode 100644 docs/rules/buttons/menu-button-needs-labelling.md delete mode 100644 docs/rules/buttons/split-button-needs-labelling.md diff --git a/docs/rules/buttons/menu-button-needs-labelling.md b/docs/rules/buttons/menu-button-needs-labelling.md deleted file mode 100644 index 478702a..0000000 --- a/docs/rules/buttons/menu-button-needs-labelling.md +++ /dev/null @@ -1,33 +0,0 @@ -# menu-button-needs-labelling - -MenuButton components must have accessible labelling for screen readers. - -## Rule Details - -This rule enforces that MenuButton components have proper accessible names through aria-label, aria-labelledby, or text content. - -### Noncompliant - -```jsx - -``` - -### Compliant - -```jsx - - - - Options - - -Options -``` - -## When Not To Use - -This rule should always be used for MenuButton components as they are interactive elements. - -## Accessibility guidelines - -- [WCAG 4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value.html) \ No newline at end of file diff --git a/docs/rules/buttons/split-button-needs-labelling.md b/docs/rules/buttons/split-button-needs-labelling.md deleted file mode 100644 index f17b37b..0000000 --- a/docs/rules/buttons/split-button-needs-labelling.md +++ /dev/null @@ -1,33 +0,0 @@ -# split-button-needs-labelling - -SplitButton components must have accessible labelling for screen readers. - -## Rule Details - -This rule enforces that SplitButton components have proper accessible names through aria-label, aria-labelledby, or text content. - -### Noncompliant - -```jsx - -``` - -### Compliant - -```jsx - - - - Save - - -Save -``` - -## When Not To Use - -This rule should always be used for SplitButton components as they are interactive elements. - -## Accessibility guidelines - -- [WCAG 4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value.html) \ No newline at end of file From 15715093cd32e1f2701a069e2216dad243f9ec8d Mon Sep 17 00:00:00 2001 From: Sidharth Sharma Date: Fri, 19 Sep 2025 01:29:05 -0400 Subject: [PATCH 06/11] Fix lint issues. --- docs/rules/card-needs-accessible-name.md | 2 +- docs/rules/infolabel-needs-labelling.md | 2 +- docs/rules/menu-button-needs-labelling.md | 2 +- docs/rules/split-button-needs-labelling.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/rules/card-needs-accessible-name.md b/docs/rules/card-needs-accessible-name.md index 8c0eb93..25fedaf 100644 --- a/docs/rules/card-needs-accessible-name.md +++ b/docs/rules/card-needs-accessible-name.md @@ -42,4 +42,4 @@ If the Card is purely decorative and not interactive, this rule is not necessary ## Accessibility guidelines -- [WCAG 4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value.html) \ No newline at end of file +- [WCAG 4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value.html) diff --git a/docs/rules/infolabel-needs-labelling.md b/docs/rules/infolabel-needs-labelling.md index bef35c7..ddc01b4 100644 --- a/docs/rules/infolabel-needs-labelling.md +++ b/docs/rules/infolabel-needs-labelling.md @@ -34,4 +34,4 @@ If the InfoLabel is purely decorative, this rule may not be necessary. ## Accessibility guidelines -- [WCAG 4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value.html) \ No newline at end of file +- [WCAG 4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value.html) diff --git a/docs/rules/menu-button-needs-labelling.md b/docs/rules/menu-button-needs-labelling.md index 93ca98b..d2dca03 100644 --- a/docs/rules/menu-button-needs-labelling.md +++ b/docs/rules/menu-button-needs-labelling.md @@ -34,4 +34,4 @@ This rule should always be used for MenuButton components as they are interactiv ## Accessibility guidelines -- [WCAG 4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value.html) \ No newline at end of file +- [WCAG 4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value.html) diff --git a/docs/rules/split-button-needs-labelling.md b/docs/rules/split-button-needs-labelling.md index fdc976c..d6d246c 100644 --- a/docs/rules/split-button-needs-labelling.md +++ b/docs/rules/split-button-needs-labelling.md @@ -34,4 +34,4 @@ This rule should always be used for SplitButton components as they are interacti ## Accessibility guidelines -- [WCAG 4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value.html) \ No newline at end of file +- [WCAG 4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value.html) From ab0c28e939c14b820864173e58f826e92af008a6 Mon Sep 17 00:00:00 2001 From: Sidharth Sharma Date: Fri, 19 Sep 2025 01:40:36 -0400 Subject: [PATCH 07/11] Comment some tests for hasLabeledChild PR to merge. --- .../menu-button-needs-labelling.test.ts | 19 +++++++++---------- .../split-button-needs-labelling.test.ts | 15 +++++++-------- .../rules/card-needs-accessible-name.test.ts | 15 ++++++--------- .../rules/infolabel-needs-labelling.test.ts | 7 ++++--- 4 files changed, 26 insertions(+), 30 deletions(-) diff --git a/tests/lib/rules/buttons/menu-button-needs-labelling.test.ts b/tests/lib/rules/buttons/menu-button-needs-labelling.test.ts index de54387..5e4812f 100644 --- a/tests/lib/rules/buttons/menu-button-needs-labelling.test.ts +++ b/tests/lib/rules/buttons/menu-button-needs-labelling.test.ts @@ -15,20 +15,19 @@ import rule from "../../../../lib/rules/buttons/menu-button-needs-labelling"; ruleTester.run("menu-button-needs-labelling", rule as unknown as Rule.RuleModule, { valid: [ - // MenuButton with text content - `Menu`, // MenuButton with aria-label - ``, - // MenuButton with aria-labelledby - `<>`, - // MenuButton wrapped in Label - ``, + ``, + // MenuButton with text content + `Options`, + // MenuButton with aria-labelledby that references existing element + `<>`, // MenuButton wrapped in Tooltip - ``, + `` + // TODO: Uncomment when hasLabeledChild is implemented // MenuButton with labeled child - `Menu icon`, + // `Menu icon`, // MenuButton with Icon child - `` + // `` ], invalid: [ { diff --git a/tests/lib/rules/buttons/split-button-needs-labelling.test.ts b/tests/lib/rules/buttons/split-button-needs-labelling.test.ts index 39a1a11..47180c7 100644 --- a/tests/lib/rules/buttons/split-button-needs-labelling.test.ts +++ b/tests/lib/rules/buttons/split-button-needs-labelling.test.ts @@ -15,20 +15,19 @@ import rule from "../../../../lib/rules/buttons/split-button-needs-labelling"; ruleTester.run("split-button-needs-labelling", rule as unknown as Rule.RuleModule, { valid: [ - // SplitButton with text content - `Save`, // SplitButton with aria-label ``, - // SplitButton with aria-labelledby + // SplitButton with text content + `Save`, + // SplitButton with aria-labelledby that references existing element `<>`, - // SplitButton wrapped in Label - ``, // SplitButton wrapped in Tooltip - ``, + `` + // TODO: Uncomment when hasLabeledChild is implemented // SplitButton with labeled child - `Save icon`, + // `Save icon`, // SplitButton with Icon child - `` + // `` ], invalid: [ { diff --git a/tests/lib/rules/card-needs-accessible-name.test.ts b/tests/lib/rules/card-needs-accessible-name.test.ts index 48e692b..bf0a85e 100644 --- a/tests/lib/rules/card-needs-accessible-name.test.ts +++ b/tests/lib/rules/card-needs-accessible-name.test.ts @@ -15,18 +15,15 @@ import rule from "../../../lib/rules/card-needs-accessible-name"; ruleTester.run("card-needs-accessible-name", rule as unknown as Rule.RuleModule, { valid: [ - // Card with text content - `Product details`, // Card with aria-label - ``, - // Card with aria-labelledby - `<>`, - // Card wrapped in Tooltip - ``, + ``, + // Card with aria-labelledby that references existing element + `<>` + // TODO: Uncomment when hasLabeledChild is implemented // Card with labeled child - `Product image`, + // `Product image`, // Card with Icon child - `` + // `` ], invalid: [ { diff --git a/tests/lib/rules/infolabel-needs-labelling.test.ts b/tests/lib/rules/infolabel-needs-labelling.test.ts index 231636e..a3125a1 100644 --- a/tests/lib/rules/infolabel-needs-labelling.test.ts +++ b/tests/lib/rules/infolabel-needs-labelling.test.ts @@ -24,11 +24,12 @@ ruleTester.run("infolabel-needs-labelling", rule as unknown as Rule.RuleModule, // InfoLabel with aria-labelledby that references existing element `<>`, // InfoLabel wrapped in Tooltip - ``, + `` + // TODO: Uncomment when hasLabeledChild is implemented // InfoLabel with labeled child - `Help icon`, + // `Help icon`, // InfoLabel with Icon child - `` + // `` ], invalid: [ { From a8409bdf4ef9cffd0d6f724c42795b2ba647dcd9 Mon Sep 17 00:00:00 2001 From: Sidharth Sharma Date: Fri, 19 Sep 2025 11:12:46 -0400 Subject: [PATCH 08/11] Remove Split-button functionality --- COVERAGE.md | 2 +- docs/rules/split-button-needs-labelling.md | 37 -------------- lib/index.ts | 2 - .../buttons/split-button-needs-labelling.ts | 26 ---------- lib/rules/index.ts | 1 - .../split-button-needs-labelling.test.ts | 50 ------------------- 6 files changed, 1 insertion(+), 117 deletions(-) delete mode 100644 docs/rules/split-button-needs-labelling.md delete mode 100644 lib/rules/buttons/split-button-needs-labelling.ts delete mode 100644 tests/lib/rules/buttons/split-button-needs-labelling.test.ts diff --git a/COVERAGE.md b/COVERAGE.md index f4d7b9a..834a913 100644 --- a/COVERAGE.md +++ b/COVERAGE.md @@ -15,7 +15,7 @@ We currently cover the following components: - [X] CompoundButton - [x] MenuButton - [X] MenuItem - - [x] SplitButton + - [] SplitButton - [x] ToggleButton - [] ToolbarToggleButton - [x] Card diff --git a/docs/rules/split-button-needs-labelling.md b/docs/rules/split-button-needs-labelling.md deleted file mode 100644 index d6d246c..0000000 --- a/docs/rules/split-button-needs-labelling.md +++ /dev/null @@ -1,37 +0,0 @@ -# Accessibility: SplitButton must have an accessible name via aria-label, text content, aria-labelledby, etc (`@microsoft/fluentui-jsx-a11y/split-button-needs-labelling`) - -đŸ’ŧ This rule is enabled in the ✅ `recommended` config. - - - -SplitButton components must have accessible labelling for screen readers. - -## Rule Details - -This rule enforces that SplitButton components have proper accessible names through aria-label, aria-labelledby, or text content. - -### Noncompliant - -```jsx - -``` - -### Compliant - -```jsx - - - - Save - - -Save -``` - -## When Not To Use - -This rule should always be used for SplitButton components as they are interactive elements. - -## Accessibility guidelines - -- [WCAG 4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value.html) diff --git a/lib/index.ts b/lib/index.ts index 3087665..ccb89c1 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -52,7 +52,6 @@ module.exports = { "@microsoft/fluentui-jsx-a11y/spin-button-needs-labelling": "error", "@microsoft/fluentui-jsx-a11y/spin-button-unrecommended-labelling": "error", "@microsoft/fluentui-jsx-a11y/spinner-needs-labelling": "error", - "@microsoft/fluentui-jsx-a11y/split-button-needs-labelling": "error", "@microsoft/fluentui-jsx-a11y/swatchpicker-needs-labelling": "error", "@microsoft/fluentui-jsx-a11y/switch-needs-labelling": "error", "@microsoft/fluentui-jsx-a11y/tablist-and-tabs-need-labelling": "error", @@ -99,7 +98,6 @@ module.exports = { "spin-button-needs-labelling": rules.spinButtonNeedsLabelling, "spin-button-unrecommended-labelling": rules.spinButtonUnrecommendedLabelling, "spinner-needs-labelling": rules.spinnerNeedsLabelling, - "split-button-needs-labelling": rules.splitButtonNeedsLabelling, "swatchpicker-needs-labelling": rules.swatchpickerNeedsLabelling, "switch-needs-labelling": rules.switchNeedsLabelling, "tablist-and-tabs-need-labelling": rules.tablistAndTabsNeedLabelling, diff --git a/lib/rules/buttons/split-button-needs-labelling.ts b/lib/rules/buttons/split-button-needs-labelling.ts deleted file mode 100644 index b7c3a2c..0000000 --- a/lib/rules/buttons/split-button-needs-labelling.ts +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -import { ESLintUtils } from "@typescript-eslint/utils"; -import { makeLabeledControlRule } from "../../util/ruleFactory"; - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -export default ESLintUtils.RuleCreator.withoutDocs( - makeLabeledControlRule({ - component: "SplitButton", - messageId: "splitButtonNeedsLabelling", - description: "Accessibility: SplitButton must have an accessible name via aria-label, text content, aria-labelledby, etc.", - labelProps: ["aria-label"], - allowFieldParent: false, - allowHtmlFor: false, - allowLabelledBy: true, - allowWrappingLabel: true, - allowTooltipParent: true, - allowDescribedBy: false, - allowLabeledChild: true, - allowTextContentChild: true - }) -); diff --git a/lib/rules/index.ts b/lib/rules/index.ts index 73cf920..0dd9865 100644 --- a/lib/rules/index.ts +++ b/lib/rules/index.ts @@ -36,7 +36,6 @@ export { default as imageSwatchNeedsLabelling } from "./imageswatch-needs-labell export { default as spinButtonNeedsLabelling } from "./spin-button-needs-labelling"; export { default as spinButtonUnrecommendedLabelling } from "./spin-button-unrecommended-labelling"; export { default as spinnerNeedsLabelling } from "./spinner-needs-labelling"; -export { default as splitButtonNeedsLabelling } from "./buttons/split-button-needs-labelling"; export { default as swatchpickerNeedsLabelling } from "./swatchpicker-needs-labelling"; export { default as switchNeedsLabelling } from "./switch-needs-labelling"; export { default as tablistAndTabsNeedLabelling } from "./tablist-and-tabs-need-labelling"; diff --git a/tests/lib/rules/buttons/split-button-needs-labelling.test.ts b/tests/lib/rules/buttons/split-button-needs-labelling.test.ts deleted file mode 100644 index 47180c7..0000000 --- a/tests/lib/rules/buttons/split-button-needs-labelling.test.ts +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -import { Rule } from "eslint"; -import ruleTester from "../helper/ruleTester"; -import rule from "../../../../lib/rules/buttons/split-button-needs-labelling"; - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -ruleTester.run("split-button-needs-labelling", rule as unknown as Rule.RuleModule, { - valid: [ - // SplitButton with aria-label - ``, - // SplitButton with text content - `Save`, - // SplitButton with aria-labelledby that references existing element - `<>`, - // SplitButton wrapped in Tooltip - `` - // TODO: Uncomment when hasLabeledChild is implemented - // SplitButton with labeled child - // `Save icon`, - // SplitButton with Icon child - // `` - ], - invalid: [ - { - code: ``, - errors: [{ messageId: "splitButtonNeedsLabelling" }] - }, - { - code: ``, - errors: [{ messageId: "splitButtonNeedsLabelling" }] - }, - { - code: ``, - errors: [{ messageId: "splitButtonNeedsLabelling" }] - }, - { - code: `<>`, - errors: [{ messageId: "splitButtonNeedsLabelling" }] - } - ] -}); From e87634b829b095474148ac8f479aba1cfb677d12 Mon Sep 17 00:00:00 2001 From: Sidharth Sharma Date: Fri, 19 Sep 2025 11:14:08 -0400 Subject: [PATCH 09/11] Update Readme --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index dc82d96..a571bc1 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,6 @@ Any use of third-party trademarks or logos are subject to those third-party's po | [spin-button-needs-labelling](docs/rules/spin-button-needs-labelling.md) | Accessibility: SpinButtons must have an accessible label | ✅ | | | | [spin-button-unrecommended-labelling](docs/rules/spin-button-unrecommended-labelling.md) | Accessibility: Unrecommended accessibility labelling - SpinButton | ✅ | | | | [spinner-needs-labelling](docs/rules/spinner-needs-labelling.md) | Accessibility: Spinner must have either aria-label or label, aria-live and aria-busy attributes | ✅ | | | -| [split-button-needs-labelling](docs/rules/split-button-needs-labelling.md) | Accessibility: SplitButton must have an accessible name via aria-label, text content, aria-labelledby, etc. | ✅ | | | | [swatchpicker-needs-labelling](docs/rules/swatchpicker-needs-labelling.md) | Accessibility: SwatchPicker must have an accessible name via aria-label, aria-labelledby, Field component, etc.. | ✅ | | | | [switch-needs-labelling](docs/rules/switch-needs-labelling.md) | Accessibility: Switch must have an accessible label | ✅ | | | | [tablist-and-tabs-need-labelling](docs/rules/tablist-and-tabs-need-labelling.md) | This rule aims to ensure that Tabs with icons but no text labels have an accessible name and that Tablist is properly labeled. | ✅ | | | From 740e75bbb52870c29b83f09dc4ffb106f78a73c6 Mon Sep 17 00:00:00 2001 From: Sidharth Sharma Date: Fri, 19 Sep 2025 11:21:26 -0400 Subject: [PATCH 10/11] Update Readme --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index eead4c4..999ad2b 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,7 @@ Any use of third-party trademarks or logos are subject to those third-party's po | [avoid-using-aria-describedby-for-primary-labelling](docs/rules/avoid-using-aria-describedby-for-primary-labelling.md) | aria-describedby provides additional context and is not meant for primary labeling. | ✅ | | | | [badge-needs-accessible-name](docs/rules/badge-needs-accessible-name.md) | | ✅ | | 🔧 | | [breadcrumb-needs-labelling](docs/rules/breadcrumb-needs-labelling.md) | All interactive elements must have an accessible name | ✅ | | | +| [card-needs-accessible-name](docs/rules/card-needs-accessible-name.md) | Accessibility: Interactive Card must have an accessible name via aria-label, aria-labelledby, etc. | ✅ | | | | [checkbox-needs-labelling](docs/rules/checkbox-needs-labelling.md) | Accessibility: Checkbox without label must have an accessible and visual label: aria-labelledby | ✅ | | | | [colorswatch-needs-labelling](docs/rules/colorswatch-needs-labelling.md) | Accessibility: ColorSwatch must have an accessible name via aria-label, Tooltip, aria-labelledby, etc.. | ✅ | | | | [combobox-needs-labelling](docs/rules/combobox-needs-labelling.md) | All interactive elements must have an accessible name | ✅ | | | @@ -191,8 +192,10 @@ Any use of third-party trademarks or logos are subject to those third-party's po | [image-button-missing-aria](docs/rules/image-button-missing-aria.md) | Accessibility: Image buttons must have accessible labelling: title, aria-label, aria-labelledby, aria-describedby | ✅ | | | | [image-needs-alt](docs/rules/image-needs-alt.md) | Accessibility: Image must have alt attribute with a meaningful description of the image. If the image is decorative, use alt="". | ✅ | | | | [imageswatch-needs-labelling](docs/rules/imageswatch-needs-labelling.md) | Accessibility: ImageSwatch must have an accessible name via aria-label, Tooltip, aria-labelledby, etc.. | ✅ | | | +| [infolabel-needs-labelling](docs/rules/infolabel-needs-labelling.md) | Accessibility: InfoLabel must have an accessible name via aria-label, text content, aria-labelledby, etc. | ✅ | | | | [input-components-require-accessible-name](docs/rules/input-components-require-accessible-name.md) | Accessibility: Input fields must have accessible labelling: aria-label, aria-labelledby or an associated label | ✅ | | | | [link-missing-labelling](docs/rules/link-missing-labelling.md) | Accessibility: Image links must have an accessible name. Add either text content, labelling to the image or labelling to the link itself. | ✅ | | 🔧 | +| [menu-button-needs-labelling](docs/rules/menu-button-needs-labelling.md) | Accessibility: MenuButton must have an accessible name via aria-label, text content, aria-labelledby, etc. | ✅ | | | | [menu-item-needs-labelling](docs/rules/menu-item-needs-labelling.md) | Accessibility: MenuItem without label must have an accessible and visual label: aria-labelledby | ✅ | | | | [no-empty-buttons](docs/rules/no-empty-buttons.md) | Accessibility: Button, ToggleButton, SplitButton, MenuButton, CompoundButton must either text content or icon or child component | ✅ | | | | [no-empty-components](docs/rules/no-empty-components.md) | FluentUI components should not be empty | ✅ | | | From 0e69e5dd86a9efd84585f705b4c7dd24bd9d3d7f Mon Sep 17 00:00:00 2001 From: Sidharth Sharma Date: Fri, 19 Sep 2025 11:31:00 -0400 Subject: [PATCH 11/11] Uncomment test cases after the recent merge. --- .../menu-button-needs-labelling.test.ts | 13 +++-------- .../rules/card-needs-accessible-name.test.ts | 11 +++------- .../rules/infolabel-needs-labelling.test.ts | 22 +++++++++---------- 3 files changed, 17 insertions(+), 29 deletions(-) diff --git a/tests/lib/rules/buttons/menu-button-needs-labelling.test.ts b/tests/lib/rules/buttons/menu-button-needs-labelling.test.ts index 5e4812f..113dfb0 100644 --- a/tests/lib/rules/buttons/menu-button-needs-labelling.test.ts +++ b/tests/lib/rules/buttons/menu-button-needs-labelling.test.ts @@ -15,19 +15,12 @@ import rule from "../../../../lib/rules/buttons/menu-button-needs-labelling"; ruleTester.run("menu-button-needs-labelling", rule as unknown as Rule.RuleModule, { valid: [ - // MenuButton with aria-label ``, - // MenuButton with text content `Options`, - // MenuButton with aria-labelledby that references existing element `<>`, - // MenuButton wrapped in Tooltip - `` - // TODO: Uncomment when hasLabeledChild is implemented - // MenuButton with labeled child - // `Menu icon`, - // MenuButton with Icon child - // `` + ``, + `Menu icon`, + `` ], invalid: [ { diff --git a/tests/lib/rules/card-needs-accessible-name.test.ts b/tests/lib/rules/card-needs-accessible-name.test.ts index bf0a85e..4cddd19 100644 --- a/tests/lib/rules/card-needs-accessible-name.test.ts +++ b/tests/lib/rules/card-needs-accessible-name.test.ts @@ -15,15 +15,10 @@ import rule from "../../../lib/rules/card-needs-accessible-name"; ruleTester.run("card-needs-accessible-name", rule as unknown as Rule.RuleModule, { valid: [ - // Card with aria-label ``, - // Card with aria-labelledby that references existing element - `<>` - // TODO: Uncomment when hasLabeledChild is implemented - // Card with labeled child - // `Product image`, - // Card with Icon child - // `` + `<>`, + `Product image`, + `` ], invalid: [ { diff --git a/tests/lib/rules/infolabel-needs-labelling.test.ts b/tests/lib/rules/infolabel-needs-labelling.test.ts index a3125a1..ac1ee2a 100644 --- a/tests/lib/rules/infolabel-needs-labelling.test.ts +++ b/tests/lib/rules/infolabel-needs-labelling.test.ts @@ -15,21 +15,14 @@ import rule from "../../../lib/rules/infolabel-needs-labelling"; ruleTester.run("infolabel-needs-labelling", rule as unknown as Rule.RuleModule, { valid: [ - // InfoLabel with aria-label ``, - // InfoLabel with text content `Help text`, `?`, `â„šī¸`, - // InfoLabel with aria-labelledby that references existing element `<>`, - // InfoLabel wrapped in Tooltip - `` - // TODO: Uncomment when hasLabeledChild is implemented - // InfoLabel with labeled child - // `Help icon`, - // InfoLabel with Icon child - // `` + ``, + `Help icon`, + `` ], invalid: [ { @@ -49,9 +42,16 @@ ruleTester.run("infolabel-needs-labelling", rule as unknown as Rule.RuleModule, errors: [{ messageId: "infoLabelNeedsLabelling" }] }, { - // aria-labelledby references ID that doesn't exist code: `<>`, errors: [{ messageId: "infoLabelNeedsLabelling" }] + }, + { + code: ``, + errors: [{ messageId: "infoLabelNeedsLabelling" }] + }, + { + code: ``, + errors: [{ messageId: "infoLabelNeedsLabelling" }] } ] });