diff --git a/__tests__/src/rules/aria-role-test.js b/__tests__/src/rules/aria-role-test.js
index 1286ad898..a584c2f21 100644
--- a/__tests__/src/rules/aria-role-test.js
+++ b/__tests__/src/rules/aria-role-test.js
@@ -40,6 +40,10 @@ const invalidTests = createTests(invalidRoles).map((test) => {
return invalidTest;
});
+const allowedInvalidRoles = [{
+ allowedInvalidRoles: ['invalid-role', 'other-invalid-role'],
+}];
+
const ignoreNonDOMSchema = [{
ignoreNonDOM: true,
}];
@@ -57,6 +61,9 @@ ruleTester.run('aria-role', rule, {
{ code: '
' },
{ code: '' },
{ code: '' },
+ { code: '', options: allowedInvalidRoles },
+ { code: '', options: allowedInvalidRoles },
+ { code: '', options: allowedInvalidRoles },
{ code: '', options: ignoreNonDOMSchema },
{ code: '', options: ignoreNonDOMSchema },
{ code: '', options: ignoreNonDOMSchema },
@@ -72,6 +79,7 @@ ruleTester.run('aria-role', rule, {
{ code: '', errors: [errorMessage] },
{ code: '', errors: [errorMessage] },
{ code: '', errors: [errorMessage] },
+ { code: '', errors: [errorMessage], options: allowedInvalidRoles },
{ code: '', errors: [errorMessage] },
{ code: '', errors: [errorMessage] },
{ code: '', errors: [errorMessage] },
diff --git a/docs/rules/aria-role.md b/docs/rules/aria-role.md
index f8e22f115..0167752b0 100644
--- a/docs/rules/aria-role.md
+++ b/docs/rules/aria-role.md
@@ -10,12 +10,15 @@ This rule takes one optional object argument of type object:
{
"rules": {
"jsx-a11y/aria-role": [ 2, {
+ "allowedInvalidRoles": ["text"],
"ignoreNonDOM": true
}],
}
}
```
+`allowedInvalidRules` is an optional string array of custom roles that should be allowed in addition to the ARIA spec, such as for cases when you [need to use a non-standard role](https://axesslab.com/text-splitting).
+
For the `ignoreNonDOM` option, this determines if developer created components are checked.
### Succeed
diff --git a/src/rules/aria-role.js b/src/rules/aria-role.js
index 52bed2b86..57e4096bd 100644
--- a/src/rules/aria-role.js
+++ b/src/rules/aria-role.js
@@ -14,6 +14,13 @@ import { generateObjSchema } from '../util/schemas';
const errorMessage = 'Elements with ARIA roles must use a valid, non-abstract ARIA role.';
const schema = generateObjSchema({
+ allowedInvalidRoles: {
+ items: {
+ type: 'string',
+ },
+ type: 'array',
+ uniqueItems: true,
+ },
ignoreNonDOM: {
type: 'boolean',
default: false,
@@ -28,42 +35,44 @@ export default {
schema: [schema],
},
- create: (context) => ({
- JSXAttribute: (attribute) => {
- // Determine if ignoreNonDOM is set to true
- // If true, then do not run rule.
- const options = context.options[0] || {};
- const ignoreNonDOM = !!options.ignoreNonDOM;
+ create: (context) => {
+ const options = context.options[0] || {};
+ const ignoreNonDOM = !!options.ignoreNonDOM;
+ const allowedInvalidRoles = new Set(options.allowedInvalidRoles || []);
+ const validRoles = new Set([...roles.keys()].filter((role) => roles.get(role).abstract === false));
- if (ignoreNonDOM) {
- const type = elementType(attribute.parent);
- if (!dom.get(type)) {
- return;
+ return ({
+ JSXAttribute: (attribute) => {
+ // If ignoreNonDOM and the parent isn't DOM, don't run rule.
+ if (ignoreNonDOM) {
+ const type = elementType(attribute.parent);
+ if (!dom.get(type)) {
+ return;
+ }
}
- }
- // Get prop name
- const name = propName(attribute).toUpperCase();
+ // Get prop name
+ const name = propName(attribute).toUpperCase();
- if (name !== 'ROLE') { return; }
+ if (name !== 'ROLE') { return; }
- const value = getLiteralPropValue(attribute);
+ const value = getLiteralPropValue(attribute);
- // If value is undefined, then the role attribute will be dropped in the DOM.
- // If value is null, then getLiteralAttributeValue is telling us that the
- // value isn't in the form of a literal.
- if (value === undefined || value === null) { return; }
+ // If value is undefined, then the role attribute will be dropped in the DOM.
+ // If value is null, then getLiteralAttributeValue is telling us that the
+ // value isn't in the form of a literal.
+ if (value === undefined || value === null) { return; }
- const values = String(value).split(' ');
- const validRoles = [...roles.keys()].filter((role) => roles.get(role).abstract === false);
- const isValid = values.every((val) => validRoles.indexOf(val) > -1);
+ const values = String(value).split(' ');
+ const isValid = values.every((val) => allowedInvalidRoles.has(val) || validRoles.has(val));
- if (isValid === true) { return; }
+ if (isValid === true) { return; }
- context.report({
- node: attribute,
- message: errorMessage,
- });
- },
- }),
+ context.report({
+ node: attribute,
+ message: errorMessage,
+ });
+ },
+ });
+ },
};