', options: [{ includeRoles: ['alert'] }], errors: [expectedError] },
+ ))
+ .map(parserOptionsMapper),
+});
diff --git a/docs/rules/control-has-associated-label.md b/docs/rules/control-has-associated-label.md
index d28d1d80..cba91c87 100644
--- a/docs/rules/control-has-associated-label.md
+++ b/docs/rules/control-has-associated-label.md
@@ -85,6 +85,10 @@ This rule takes one optional object argument of type object:
"tree",
"treegrid",
],
+ "includeRoles": [
+ "alert",
+ "dialog",
+ ],
"depth": 3,
}],
}
@@ -95,6 +99,7 @@ This rule takes one optional object argument of type object:
- `controlComponents` is a list of custom React Components names that will render down to an interactive element.
- `ignoreElements` is an array of elements that should not be considered control (interactive) elements and therefore they do not require a text label.
- `ignoreRoles` is an array of ARIA roles that should not be considered control (interactive) roles and therefore they do not require a text label.
+- `includeRoles` is an array of ARIA roles that are not interactive by default but should still require an accessible text label. For example, `['alert', 'dialog']` will enforce that elements with these roles have a label. If a role appears in both `ignoreRoles` and `includeRoles`, `ignoreRoles` takes precedence.
- `depth` (default 2, max 25) is an integer that determines how deep within a `JSXElement` the rule should look for text content or an element with a label to determine if the interactive element will have an accessible label.
### Succeed
diff --git a/src/rules/control-has-associated-label.js b/src/rules/control-has-associated-label.js
index 4a691741..59c72c3c 100644
--- a/src/rules/control-has-associated-label.js
+++ b/src/rules/control-has-associated-label.js
@@ -30,6 +30,7 @@ const schema = generateObjSchema({
controlComponents: arraySchema,
ignoreElements: arraySchema,
ignoreRoles: arraySchema,
+ includeRoles: arraySchema,
depth: {
description: 'JSX tree depth limit to check for accessible label',
type: 'integer',
@@ -54,6 +55,7 @@ export default ({
controlComponents = [],
ignoreElements = [],
ignoreRoles = [],
+ includeRoles = [],
} = options;
const newIgnoreElements = new Set([].concat(ignoreElements, ignoreList));
@@ -77,6 +79,7 @@ export default ({
const nodeIsInteractiveElement = isInteractiveElement(tag, props);
const nodeIsInteractiveRole = isInteractiveRole(tag, props);
const nodeIsControlComponent = controlComponents.indexOf(tag) > -1;
+ const nodeIsIncludedRole = includes(includeRoles, role);
if (nodeIsHiddenFromScreenReader) {
return;
@@ -90,7 +93,7 @@ export default ({
&& nodeIsInteractiveRole
)
|| nodeIsControlComponent
-
+ || nodeIsIncludedRole
) {
// Prevent crazy recursion.
const recursionDepth = Math.min(