Skip to content

Commit b89f94d

Browse files
authored
Merge pull request #163 from microsoft/users/sidhshar/accessibility-rules-for-infolabel-menubutton-splitbutton-and-card-components
Add accessibility rules for InfoLabel, MenuButton, SplitButton, and Card components
2 parents 2f2d469 + 0e69e5d commit b89f94d

15 files changed

+356
-6
lines changed

COVERAGE.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ We currently cover the following components:
1313
- [x] Button
1414
- [x] Button
1515
- [X] CompoundButton
16-
- [] MenuButton
16+
- [x] MenuButton
1717
- [X] MenuItem
1818
- [] SplitButton
1919
- [x] ToggleButton
2020
- [] ToolbarToggleButton
21-
- [] Card
22-
- [] Card
21+
- [x] Card
22+
- [x] Card
2323
- [] CardFooter
2424
- [] CardHeader
2525
- [] CardPreview
@@ -37,7 +37,7 @@ We currently cover the following components:
3737
- [x] Field
3838
- [N/A] FluentProvider
3939
- [] Image
40-
- [] InfoLabel
40+
- [x] InfoLabel
4141
- [x] Input
4242
- [x] Label
4343
- [x] Link

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ Any use of third-party trademarks or logos are subject to those third-party's po
178178
| [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. || | |
179179
| [badge-needs-accessible-name](docs/rules/badge-needs-accessible-name.md) | || | 🔧 |
180180
| [breadcrumb-needs-labelling](docs/rules/breadcrumb-needs-labelling.md) | All interactive elements must have an accessible name || | |
181+
| [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. || | |
181182
| [checkbox-needs-labelling](docs/rules/checkbox-needs-labelling.md) | Accessibility: Checkbox without label must have an accessible and visual label: aria-labelledby || | |
182183
| [colorswatch-needs-labelling](docs/rules/colorswatch-needs-labelling.md) | Accessibility: ColorSwatch must have an accessible name via aria-label, Tooltip, aria-labelledby, etc.. || | |
183184
| [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
191192
| [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 || | |
192193
| [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="". || | |
193194
| [imageswatch-needs-labelling](docs/rules/imageswatch-needs-labelling.md) | Accessibility: ImageSwatch must have an accessible name via aria-label, Tooltip, aria-labelledby, etc.. || | |
195+
| [infolabel-needs-labelling](docs/rules/infolabel-needs-labelling.md) | Accessibility: InfoLabel must have an accessible name via aria-label, text content, aria-labelledby, etc. || | |
194196
| [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 || | |
195197
| [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. || | 🔧 |
198+
| [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. || | |
196199
| [menu-item-needs-labelling](docs/rules/menu-item-needs-labelling.md) | Accessibility: MenuItem without label must have an accessible and visual label: aria-labelledby || | |
197200
| [no-empty-buttons](docs/rules/no-empty-buttons.md) | Accessibility: Button, ToggleButton, SplitButton, MenuButton, CompoundButton must either text content or icon or child component || | |
198201
| [no-empty-components](docs/rules/no-empty-components.md) | FluentUI components should not be empty || | |
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Accessibility: Interactive Card must have an accessible name via aria-label, aria-labelledby, etc (`@microsoft/fluentui-jsx-a11y/card-needs-accessible-name`)
2+
3+
💼 This rule is enabled in the ✅ `recommended` config.
4+
5+
<!-- end auto-generated rule header -->
6+
7+
Interactive Card components must have an accessible name for screen readers.
8+
9+
## Rule Details
10+
11+
This rule enforces that Card components have proper accessible names when they are interactive (clickable).
12+
13+
### Noncompliant
14+
15+
```jsx
16+
<Card>
17+
<CardHeader>
18+
<Text weight="semibold">Card title</Text>
19+
</CardHeader>
20+
</Card>
21+
```
22+
23+
### Compliant
24+
25+
```jsx
26+
<Card aria-label="Product details">
27+
<CardHeader>
28+
<Text weight="semibold">Card title</Text>
29+
</CardHeader>
30+
</Card>
31+
32+
<Card aria-labelledby="card-title">
33+
<CardHeader>
34+
<Text id="card-title" weight="semibold">Card title</Text>
35+
</CardHeader>
36+
</Card>
37+
```
38+
39+
## When Not To Use
40+
41+
If the Card is purely decorative and not interactive, this rule is not necessary.
42+
43+
## Accessibility guidelines
44+
45+
- [WCAG 4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value.html)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Accessibility: InfoLabel must have an accessible name via aria-label, text content, aria-labelledby, etc (`@microsoft/fluentui-jsx-a11y/infolabel-needs-labelling`)
2+
3+
💼 This rule is enabled in the ✅ `recommended` config.
4+
5+
<!-- end auto-generated rule header -->
6+
7+
InfoLabel components must have accessible labelling for screen readers.
8+
9+
## Rule Details
10+
11+
This rule enforces that InfoLabel components have proper accessible names through aria-label, aria-labelledby, or text content.
12+
13+
### Noncompliant
14+
15+
```jsx
16+
<InfoLabel />
17+
```
18+
19+
### Compliant
20+
21+
```jsx
22+
<InfoLabel aria-label="Additional information" />
23+
24+
<InfoLabel aria-labelledby="info-text">
25+
<span id="info-text">Help text</span>
26+
</InfoLabel>
27+
28+
<InfoLabel>Help information</InfoLabel>
29+
```
30+
31+
## When Not To Use
32+
33+
If the InfoLabel is purely decorative, this rule may not be necessary.
34+
35+
## Accessibility guidelines
36+
37+
- [WCAG 4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value.html)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Accessibility: MenuButton must have an accessible name via aria-label, text content, aria-labelledby, etc (`@microsoft/fluentui-jsx-a11y/menu-button-needs-labelling`)
2+
3+
💼 This rule is enabled in the ✅ `recommended` config.
4+
5+
<!-- end auto-generated rule header -->
6+
7+
MenuButton components must have accessible labelling for screen readers.
8+
9+
## Rule Details
10+
11+
This rule enforces that MenuButton components have proper accessible names through aria-label, aria-labelledby, or text content.
12+
13+
### Noncompliant
14+
15+
```jsx
16+
<MenuButton />
17+
```
18+
19+
### Compliant
20+
21+
```jsx
22+
<MenuButton aria-label="Menu options" />
23+
24+
<MenuButton aria-labelledby="menu-label">
25+
<span id="menu-label">Options</span>
26+
</MenuButton>
27+
28+
<MenuButton>Options</MenuButton>
29+
```
30+
31+
## When Not To Use
32+
33+
This rule should always be used for MenuButton components as they are interactive elements.
34+
35+
## Accessibility guidelines
36+
37+
- [WCAG 4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value.html)

lib/applicableComponents/buttonBasedComponents.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4-
const applicableComponents = ["Button", "ToggleButton", "CompoundButton"];
4+
const applicableComponents = ["Button", "ToggleButton", "CompoundButton", "MenuButton", "SplitButton"];
55

66
module.exports = {
77
applicableComponents

lib/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ module.exports = {
2222
"@microsoft/fluentui-jsx-a11y/avoid-using-aria-describedby-for-primary-labelling": "error",
2323
"@microsoft/fluentui-jsx-a11y/badge-needs-accessible-name": "error",
2424
"@microsoft/fluentui-jsx-a11y/breadcrumb-needs-labelling": "error",
25+
"@microsoft/fluentui-jsx-a11y/card-needs-accessible-name": "error",
2526
"@microsoft/fluentui-jsx-a11y/checkbox-needs-labelling": "error",
2627
"@microsoft/fluentui-jsx-a11y/colorswatch-needs-labelling": "error",
2728
"@microsoft/fluentui-jsx-a11y/combobox-needs-labelling": "error",
@@ -35,8 +36,10 @@ module.exports = {
3536
"@microsoft/fluentui-jsx-a11y/image-button-missing-aria": "error",
3637
"@microsoft/fluentui-jsx-a11y/image-needs-alt": "error",
3738
"@microsoft/fluentui-jsx-a11y/imageswatch-needs-labelling": "error",
39+
"@microsoft/fluentui-jsx-a11y/infolabel-needs-labelling": "error",
3840
"@microsoft/fluentui-jsx-a11y/input-components-require-accessible-name": "error",
3941
"@microsoft/fluentui-jsx-a11y/link-missing-labelling": "error",
42+
"@microsoft/fluentui-jsx-a11y/menu-button-needs-labelling": "error",
4043
"@microsoft/fluentui-jsx-a11y/menu-item-needs-labelling": "error",
4144
"@microsoft/fluentui-jsx-a11y/no-empty-buttons": "error",
4245
"@microsoft/fluentui-jsx-a11y/no-empty-components": "error",
@@ -66,6 +69,7 @@ module.exports = {
6669
"avoid-using-aria-describedby-for-primary-labelling": rules.avoidUsingAriaDescribedByForPrimaryLabelling,
6770
"badge-needs-accessible-name": rules.badgeNeedsAccessibleName,
6871
"breadcrumb-needs-labelling": rules.breadcrumbNeedsLabelling,
72+
"card-needs-accessible-name": rules.cardNeedsAccessibleName,
6973
"checkbox-needs-labelling": rules.checkboxNeedsLabelling,
7074
"colorswatch-needs-labelling": rules.colorSwatchNeedsLabelling,
7175
"combobox-needs-labelling": rules.comboboxNeedsLabelling,
@@ -79,8 +83,10 @@ module.exports = {
7983
"image-button-missing-aria": rules.imageButtonMissingAria,
8084
"image-needs-alt": rules.imageNeedsAlt,
8185
"imageswatch-needs-labelling": rules.imageSwatchNeedsLabelling,
86+
"infolabel-needs-labelling": rules.infoLabelNeedsLabelling,
8287
"input-components-require-accessible-name": rules.inputComponentsRequireAccessibleName,
8388
"link-missing-labelling": rules.linkMissingLabelling,
89+
"menu-button-needs-labelling": rules.menuButtonNeedsLabelling,
8490
"menu-item-needs-labelling": rules.menuItemNeedsLabelling,
8591
"no-empty-buttons": rules.noEmptyButtons,
8692
"no-empty-components": rules.noEmptyComponents,
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
import { ESLintUtils } from "@typescript-eslint/utils";
5+
import { makeLabeledControlRule } from "../../util/ruleFactory";
6+
7+
//------------------------------------------------------------------------------
8+
// Rule Definition
9+
//------------------------------------------------------------------------------
10+
11+
export default ESLintUtils.RuleCreator.withoutDocs(
12+
makeLabeledControlRule({
13+
component: "MenuButton",
14+
messageId: "menuButtonNeedsLabelling",
15+
description: "Accessibility: MenuButton must have an accessible name via aria-label, text content, aria-labelledby, etc.",
16+
labelProps: ["aria-label"],
17+
allowFieldParent: false,
18+
allowHtmlFor: false,
19+
allowLabelledBy: true,
20+
allowWrappingLabel: true,
21+
allowTooltipParent: true,
22+
allowDescribedBy: false,
23+
allowLabeledChild: true,
24+
allowTextContentChild: true
25+
})
26+
);
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
import { ESLintUtils } from "@typescript-eslint/utils";
5+
import { makeLabeledControlRule } from "../util/ruleFactory";
6+
7+
//------------------------------------------------------------------------------
8+
// Rule Definition
9+
//------------------------------------------------------------------------------
10+
11+
export default ESLintUtils.RuleCreator.withoutDocs(
12+
makeLabeledControlRule({
13+
component: "Card",
14+
messageId: "cardNeedsAccessibleName",
15+
description: "Accessibility: Interactive Card must have an accessible name via aria-label, aria-labelledby, etc.",
16+
labelProps: ["aria-label"],
17+
allowFieldParent: false,
18+
allowHtmlFor: false,
19+
allowLabelledBy: true,
20+
allowWrappingLabel: false,
21+
allowTooltipParent: true,
22+
allowDescribedBy: false,
23+
allowLabeledChild: true,
24+
allowTextContentChild: true
25+
})
26+
);

lib/rules/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export { default as avatarNeedsName } from "./avatar-needs-name";
77
export { default as avoidUsingAriaDescribedByForPrimaryLabelling } from "./avoid-using-aria-describedby-for-primary-labelling";
88
export { default as badgeNeedsAccessibleName } from "./badge-needs-accessible-name";
99
export { default as breadcrumbNeedsLabelling } from "./breadcrumb-needs-labelling";
10+
export { default as cardNeedsAccessibleName } from "./card-needs-accessible-name";
1011
export { default as checkboxNeedsLabelling } from "./checkbox-needs-labelling";
1112
export { default as comboboxNeedsLabelling } from "./combobox-needs-labelling";
1213
export { default as compoundButtonNeedsLabelling } from "./buttons/compound-button-needs-labelling";
@@ -17,8 +18,10 @@ export { default as dropdownNeedsLabelling } from "./dropdown-needs-labelling";
1718
export { default as fieldNeedsLabelling } from "./field-needs-labelling";
1819
export { default as imageButtonMissingAria } from "./buttons/image-button-missing-aria";
1920
export { default as imageNeedsAlt } from "./image-needs-alt";
21+
export { default as infoLabelNeedsLabelling } from "./infolabel-needs-labelling";
2022
export { default as inputComponentsRequireAccessibleName } from "./input-components-require-accessible-name";
2123
export { default as linkMissingLabelling } from "./link-missing-labelling";
24+
export { default as menuButtonNeedsLabelling } from "./buttons/menu-button-needs-labelling";
2225
export { default as menuItemNeedsLabelling } from "./menu-item-needs-labelling";
2326
export { default as noEmptyButtons } from "./buttons/no-empty-buttons";
2427
export { default as noEmptyComponents } from "./no-empty-components";

0 commit comments

Comments
 (0)