Skip to content

Commit 4b6b0f6

Browse files
committed
added rules for all swatch components
1 parent 3977d44 commit 4b6b0f6

14 files changed

+911
-44
lines changed

COVERAGE.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,10 @@ We currently cover the following components:
6464
- [x] SpinButton
6565
- [x] Spinner
6666
- [x] SwatchPicker
67-
- [] ColorSwatch
68-
- [] ImageSwatch
69-
- [] EmptySwatch
70-
- [] SwatchPickerRow
67+
- [x] ColorSwatch
68+
- [x] ImageSwatch
69+
- [x] EmptySwatch
70+
- [N/A] SwatchPickerRow
7171
- [x] Switch
7272
- [] SearchBox
7373
- [] Table
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# Accessibility: ColorSwatch must have an accessible name (`@microsoft/fluentui-jsx-a11y/colorswatch-needs-labelling`)
2+
3+
💼 This rule is enabled in the ✅ `recommended` config.
4+
5+
<!-- end auto-generated rule header -->
6+
7+
All interactive elements must have an accessible name.
8+
9+
`ColorSwatch` without a supported label lacks an accessible name for assistive technology users.
10+
11+
## Allowed labelling strategies
12+
13+
-`aria-label` **on `ColorSwatch`**
14+
-`aria-labelledby` **on `ColorSwatch`**
15+
-`htmlFor`/`id`
16+
- ✅ Wrapping native `<label>…</label>`
17+
-`Tooltip` parent with `relationship="label"`
18+
-`Field` parent label
19+
- ✅ Text content child
20+
- ❌ Container-only label (e.g., only the surrounding `SwatchPicker` is labelled)
21+
22+
## Ways to fix
23+
24+
- Add `aria-label` / `aria-labelledby`.
25+
- Use `<label htmlFor="…">` + `id="…"` on `ColorSwatch`.
26+
- Wrap with `<label>` or use a `Tooltip (relationship="label")`.
27+
- Provide a `Field label="…"` parent.
28+
- Provide meaningful text as the child of `ColorSwatch`.
29+
30+
## Rule Details
31+
32+
This rule ensures `ColorSwatch` is labelled using **any** supported strategy above.
33+
34+
### Examples of **incorrect** code
35+
36+
```jsx
37+
// No label
38+
<SwatchPicker>
39+
<ColorSwatch color="#FF1921" value="FF1921" />
40+
</SwatchPicker>
41+
```
42+
43+
```jsx
44+
// Container-only label: ColorSwatch itself is unnamed
45+
<SwatchPicker aria-label="Color picker">
46+
<ColorSwatch color="#FF1921" value="FF1921" />
47+
</SwatchPicker>
48+
```
49+
50+
### Examples of **correct** code
51+
52+
```jsx
53+
// aria-label
54+
<SwatchPicker>
55+
<ColorSwatch color="#FF1921" value="FF1921" aria-label="Red" />
56+
</SwatchPicker>
57+
```
58+
59+
```jsx
60+
// aria-labelledby
61+
<>
62+
<span id="red-swatch">Red</span>
63+
<SwatchPicker>
64+
<ColorSwatch color="#FF1921" value="FF1921" aria-labelledby="red-swatch" />
65+
</SwatchPicker>
66+
</>
67+
```
68+
69+
```jsx
70+
// htmlFor/id
71+
<>
72+
<label htmlFor="sw-red">Red</label>
73+
<SwatchPicker>
74+
<ColorSwatch id="sw-red" color="#FF1921" value="FF1921" />
75+
</SwatchPicker>
76+
</>
77+
```
78+
79+
```jsx
80+
// Wrapping native <label>
81+
<label>
82+
Red
83+
<ColorSwatch color="#FF1921" value="FF1921" />
84+
</label>
85+
```
86+
87+
```jsx
88+
// Tooltip (acts as a label)
89+
<SwatchPicker>
90+
<Tooltip relationship="label" content="Red">
91+
<ColorSwatch color="#FF1921" value="FF1921" />
92+
</Tooltip>
93+
</SwatchPicker>
94+
```
95+
96+
```jsx
97+
// Field parent
98+
<SwatchPicker>
99+
<Field label="Red">
100+
<ColorSwatch color="#FF1921" value="FF1921" />
101+
</Field>
102+
</SwatchPicker>
103+
```
104+
105+
```jsx
106+
// Text content child
107+
<SwatchPicker>
108+
<ColorSwatch color="#FF1921" value="FF1921">Red</ColorSwatch>
109+
</SwatchPicker>
110+
```
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# Accessibility: EmptySwatch must have an accessible name (`@microsoft/fluentui-jsx-a11y/emptyswatch-needs-labelling`)
2+
3+
💼 This rule is enabled in the ✅ `recommended` config.
4+
5+
<!-- end auto-generated rule header -->
6+
7+
All interactive elements must have an accessible name.
8+
9+
`EmptySwatch` without a supported label lacks an accessible name for assistive technology users.
10+
11+
## Allowed labelling strategies
12+
13+
-`aria-label` **on `EmptySwatch`**
14+
-`aria-labelledby` **on `EmptySwatch`**
15+
-`htmlFor`/`id` (native `<label htmlFor="…">``id="…"` on `EmptySwatch`)
16+
- ✅ Wrapping native `<label>…</label>`
17+
-`Tooltip` parent with `relationship="label"`
18+
- ✅ Text content child (e.g., `<EmptySwatch>None</EmptySwatch>`)
19+
-`Field` parent (not allowed for `EmptySwatch`)
20+
- ❌ Container-only label (e.g., only the surrounding `SwatchPicker` is labelled)
21+
22+
## Ways to fix
23+
24+
- Add `aria-label`/`aria-labelledby` to `EmptySwatch`.
25+
- Use `<label htmlFor="…">` + `id="…"` on `EmptySwatch`.
26+
- Wrap in a native `<label>` or `Tooltip (relationship="label")`.
27+
- Provide meaningful text **as the child** of `EmptySwatch`.
28+
29+
## Rule Details
30+
31+
This rule ensures `EmptySwatch` is labelled using **allowed** mechanisms.
32+
33+
### Examples of **incorrect** code
34+
35+
```jsx
36+
// No label
37+
<SwatchPicker>
38+
<EmptySwatch value="none" />
39+
</SwatchPicker>
40+
```
41+
42+
```jsx
43+
// Container-only label: EmptySwatch itself is unnamed
44+
<SwatchPicker aria-label="Color picker">
45+
<EmptySwatch value="none" />
46+
</SwatchPicker>
47+
```
48+
49+
```jsx
50+
// Not allowed: Field parent labelling
51+
<SwatchPicker>
52+
<Field label="No color">
53+
<EmptySwatch value="none" />
54+
</Field>
55+
</SwatchPicker>
56+
```
57+
58+
### Examples of **correct** code
59+
60+
```jsx
61+
// aria-label
62+
<SwatchPicker>
63+
<EmptySwatch value="none" aria-label="No color" />
64+
</SwatchPicker>
65+
```
66+
67+
```jsx
68+
// aria-labelledby
69+
<>
70+
<span id="empty-no-color">No color</span>
71+
<SwatchPicker>
72+
<EmptySwatch value="none" aria-labelledby="empty-no-color" />
73+
</SwatchPicker>
74+
</>
75+
```
76+
77+
```jsx
78+
// htmlFor/id
79+
<>
80+
<label htmlFor="empty-none">No color</label>
81+
<SwatchPicker>
82+
<EmptySwatch id="empty-none" value="none" />
83+
</SwatchPicker>
84+
</>
85+
```
86+
87+
```jsx
88+
// Wrapping native <label>
89+
<label>
90+
No color
91+
<EmptySwatch value="none" />
92+
</label>
93+
```
94+
95+
```jsx
96+
// Tooltip (acts as a label)
97+
<SwatchPicker>
98+
<Tooltip relationship="label" content="No color">
99+
<EmptySwatch value="none" />
100+
</Tooltip>
101+
</SwatchPicker>
102+
```
103+
104+
```jsx
105+
// Text content child
106+
<SwatchPicker>
107+
<EmptySwatch value="none">No color</EmptySwatch>
108+
</SwatchPicker>
109+
```
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# Accessibility: ImageSwatch must have an accessible name (`@microsoft/fluentui-jsx-a11y/imageswatch-needs-labelling`)
2+
3+
💼 This rule is enabled in the ✅ `recommended` config.
4+
5+
<!-- end auto-generated rule header -->
6+
7+
All interactive elements must have an accessible name.
8+
9+
`ImageSwatch` without a supported label lacks an accessible name for assistive technology users.
10+
11+
## Allowed labelling strategies
12+
13+
-`aria-label` **on `ImageSwatch`**
14+
-`aria-labelledby` **on `ImageSwatch`**
15+
- ✅ Wrapping native `<label>…</label>`
16+
-`Tooltip` parent with `relationship="label"` (e.g., from Fluent UI)
17+
-`Field` parent (not allowed for `ImageSwatch`)
18+
-`htmlFor`/`id` (not allowed for `ImageSwatch`)
19+
- ❌ Text content child (not allowed)
20+
- ❌ Container-only label (e.g., only the surrounding `SwatchPicker` is labelled)
21+
22+
## Ways to fix
23+
24+
- Add `aria-label` or `aria-labelledby` **directly** to `ImageSwatch`.
25+
- Wrap the swatch in a native `<label>…</label>` with descriptive text.
26+
- Wrap in a `Tooltip` with `relationship="label"` and meaningful `content`.
27+
28+
## Rule Details
29+
30+
This rule ensures `ImageSwatch` receives a name via **supported** mechanisms.
31+
32+
### Examples of **incorrect** code
33+
34+
```jsx
35+
// No label at all
36+
<SwatchPicker>
37+
<ImageSwatch src="/none.png" value="none" />
38+
</SwatchPicker>
39+
```
40+
41+
```jsx
42+
// Container-only label: ImageSwatch itself is unnamed
43+
<SwatchPicker aria-label="Color picker">
44+
<ImageSwatch src="/none.png" value="none" />
45+
</SwatchPicker>
46+
```
47+
48+
```jsx
49+
// Not allowed for ImageSwatch: htmlFor/id
50+
<>
51+
<label htmlFor="img-none">No color</label>
52+
<SwatchPicker>
53+
<ImageSwatch id="img-none" src="/none.png" value="none" />
54+
</SwatchPicker>
55+
</>
56+
```
57+
58+
```jsx
59+
// Not allowed: text content child
60+
<SwatchPicker>
61+
<ImageSwatch src="/none.png" value="none">No color</ImageSwatch>
62+
</SwatchPicker>
63+
```
64+
65+
```jsx
66+
// Not allowed: Field parent labelling
67+
<SwatchPicker>
68+
<Field label="No color">
69+
<ImageSwatch src="/none.png" value="none" />
70+
</Field>
71+
</SwatchPicker>
72+
```
73+
74+
### Examples of **correct** code
75+
76+
```jsx
77+
// aria-label
78+
<SwatchPicker>
79+
<ImageSwatch src="/none.png" value="none" aria-label="No color" />
80+
</SwatchPicker>
81+
```
82+
83+
```jsx
84+
// aria-labelledby
85+
<>
86+
<span id="img-no-color">No color</span>
87+
<SwatchPicker>
88+
<ImageSwatch src="/none.png" value="none" aria-labelledby="img-no-color" />
89+
</SwatchPicker>
90+
</>
91+
```
92+
93+
```jsx
94+
// Wrapping native <label>
95+
<label>
96+
No color
97+
<ImageSwatch src="/none.png" value="none" />
98+
</label>
99+
```
100+
101+
```jsx
102+
// Tooltip (acts as a label)
103+
<SwatchPicker>
104+
<Tooltip relationship="label" content="No color">
105+
<ImageSwatch src="/none.png" value="none" />
106+
</Tooltip>
107+
</SwatchPicker>
108+
```

lib/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,17 @@ module.exports = {
2323
"@microsoft/fluentui-jsx-a11y/badge-needs-accessible-name": "error",
2424
"@microsoft/fluentui-jsx-a11y/breadcrumb-needs-labelling": "error",
2525
"@microsoft/fluentui-jsx-a11y/checkbox-needs-labelling": "error",
26+
"@microsoft/fluentui-jsx-a11y/colorswatch-needs-labelling": "error",
2627
"@microsoft/fluentui-jsx-a11y/combobox-needs-labelling": "error",
2728
"@microsoft/fluentui-jsx-a11y/compound-button-needs-labelling": "error",
2829
"@microsoft/fluentui-jsx-a11y/counter-badge-needs-count": "error",
2930
"@microsoft/fluentui-jsx-a11y/dialogbody-needs-title-content-and-actions": "error",
3031
"@microsoft/fluentui-jsx-a11y/dialogsurface-needs-aria": "error",
3132
"@microsoft/fluentui-jsx-a11y/dropdown-needs-labelling": "error",
33+
"@microsoft/fluentui-jsx-a11y/emptyswatch-needs-labelling": "error",
3234
"@microsoft/fluentui-jsx-a11y/field-needs-labelling": "error",
3335
"@microsoft/fluentui-jsx-a11y/image-button-missing-aria": "error",
36+
"@microsoft/fluentui-jsx-a11y/imageswatch-needs-labelling": "error",
3437
"@microsoft/fluentui-jsx-a11y/input-components-require-accessible-name": "error",
3538
"@microsoft/fluentui-jsx-a11y/link-missing-labelling": "error",
3639
"@microsoft/fluentui-jsx-a11y/menu-item-needs-labelling": "error",
@@ -61,14 +64,17 @@ module.exports = {
6164
"badge-needs-accessible-name": rules.badgeNeedsAccessibleName,
6265
"breadcrumb-needs-labelling": rules.breadcrumbNeedsLabelling,
6366
"checkbox-needs-labelling": rules.checkboxNeedsLabelling,
67+
"colorswatch-needs-labelling": rules.colorSwatchNeedsLabelling,
6468
"combobox-needs-labelling": rules.comboboxNeedsLabelling,
6569
"compound-button-needs-labelling": rules.compoundButtonNeedsLabelling,
6670
"counter-badge-needs-count": rules.counterBadgeNeedsCount,
6771
"dialogbody-needs-title-content-and-actions": rules.dialogbodyNeedsTitleContentAndActions,
6872
"dialogsurface-needs-aria": rules.dialogsurfaceNeedsAria,
6973
"dropdown-needs-labelling": rules.dropdownNeedsLabelling,
74+
"emptyswatch-needs-labelling": rules.emptySwatchNeedsLabelling,
7075
"field-needs-labelling": rules.fieldNeedsLabelling,
7176
"image-button-missing-aria": rules.imageButtonMissingAria,
77+
"imageswatch-needs-labelling": rules.imageSwatchNeedsLabelling,
7278
"input-components-require-accessible-name": rules.inputComponentsRequireAccessibleName,
7379
"link-missing-labelling": rules.linkMissingLabelling,
7480
"menu-item-needs-labelling": rules.menuItemNeedsLabelling,

0 commit comments

Comments
 (0)