From c95e07b33c26a8c9e26fab8c8aca2fda3c1d4c54 Mon Sep 17 00:00:00 2001 From: Courtney Nguyen Date: Wed, 26 Jan 2022 11:54:34 -0700 Subject: [PATCH 1/8] [New] no-disabled rule --- docs/rules/no-disabled.md | 38 +++++++++++++++++++++++++++ src/rules/no-disabled.js | 54 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 docs/rules/no-disabled.md create mode 100644 src/rules/no-disabled.js diff --git a/docs/rules/no-disabled.md b/docs/rules/no-disabled.md new file mode 100644 index 000000000..0f641cb08 --- /dev/null +++ b/docs/rules/no-disabled.md @@ -0,0 +1,38 @@ +# no-disabled + +Enforce that disabled prop is not used on elements. Disabling inputs remove the input from the accessibility tree and loses context. Use `aria-disabled` instead. + +## Rule details + +This rule takes one optional object argument of type object: + +```json +{ + "rules": { + "jsx-a11y/no-disabled": [ 2, { + "ignoreNonDOM": true + }], + } +} +``` + +For the `ignoreNonDOM` option, this determines if developer created components are checked. + +### Succeed +```jsx +
+``` + +### Fail +```jsx +
+
+
+
+``` + +## Accessibility guidelines +General best practice (reference resources) + +### Resources +- [MDN aria-disabled](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-disabled) diff --git a/src/rules/no-disabled.js b/src/rules/no-disabled.js new file mode 100644 index 000000000..158af2d67 --- /dev/null +++ b/src/rules/no-disabled.js @@ -0,0 +1,54 @@ +/** + * @fileoverview Enforce disabled prop is not used. + * @author Courtney Nguyen <@courtyen> + */ + +// ---------------------------------------------------------------------------- +// Rule Definition +// ---------------------------------------------------------------------------- + +import { propName, elementType } from "jsx-ast-utils"; +import { dom } from "aria-query"; +import { generateObjSchema } from "../util/schemas"; + +const errorMessage = "The disabled prop should not be used, as it can reduce usability and accessibility for users."; + +const schema = generateObjSchema({ + ignoreNonDOM: { + type: "boolean", + default: false, + }, +}); + +export default { + meta: { + docs: { + url: "https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/tree/HEAD/docs/rules/no-autofocus.md", + }, + 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; + + if (ignoreNonDOM) { + const type = elementType(attribute.parent); + if (!dom.get(type)) { + return; + } + } + + // Don't normalize, since React only recognizes autoFocus on low-level DOM elements. + if (propName(attribute) === "disabled") { + context.report({ + node: attribute, + message: errorMessage, + }); + } + }, + }), +}; From aa41427c887590b15b77c70b53752594b69501a1 Mon Sep 17 00:00:00 2001 From: Courtney Nguyen Date: Wed, 26 Jan 2022 14:38:12 -0700 Subject: [PATCH 2/8] Only target specific elements to run eslint rule on --- docs/rules/no-disabled.md | 12 ++++++------ src/rules/no-disabled.js | 30 +++++++++++++++++------------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/docs/rules/no-disabled.md b/docs/rules/no-disabled.md index 0f641cb08..ce0a5ed39 100644 --- a/docs/rules/no-disabled.md +++ b/docs/rules/no-disabled.md @@ -1,6 +1,6 @@ # no-disabled -Enforce that disabled prop is not used on elements. Disabling inputs remove the input from the accessibility tree and loses context. Use `aria-disabled` instead. +Enforce that `disabled` prop is not used on elements. Disabling interactive elements removes the element from the accessibility tree. Use `aria-disabled` instead. ## Rule details @@ -20,15 +20,15 @@ For the `ignoreNonDOM` option, this determines if developer created components a ### Succeed ```jsx -
+ ``` ### Fail ```jsx -
-
-
-
+ + + + ``` ## Accessibility guidelines diff --git a/src/rules/no-disabled.js b/src/rules/no-disabled.js index 158af2d67..67c301bfb 100644 --- a/src/rules/no-disabled.js +++ b/src/rules/no-disabled.js @@ -8,11 +8,22 @@ // ---------------------------------------------------------------------------- import { propName, elementType } from "jsx-ast-utils"; -import { dom } from "aria-query"; import { generateObjSchema } from "../util/schemas"; const errorMessage = "The disabled prop should not be used, as it can reduce usability and accessibility for users."; +const DEFAULT_ELEMENTS = [ + "button", + "command", + "fieldset", + "keygen", + "optgroup", + "option", + "select", + "textarea", + "input", +]; + const schema = generateObjSchema({ ignoreNonDOM: { type: "boolean", @@ -23,26 +34,19 @@ const schema = generateObjSchema({ export default { meta: { docs: { - url: "https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/tree/HEAD/docs/rules/no-autofocus.md", + url: "https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/tree/HEAD/docs/rules/no-disabled.md", }, 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; - - if (ignoreNonDOM) { - const type = elementType(attribute.parent); - if (!dom.get(type)) { - return; - } + // Only monitor elements with "disabled". + const type = elementType(attribute.parent); + if (!DEFAULT_ELEMENTS.includes(type)) { + return; } - // Don't normalize, since React only recognizes autoFocus on low-level DOM elements. if (propName(attribute) === "disabled") { context.report({ node: attribute, From 1d89958a13cb2a949f78f3b7f0735253b8467a5e Mon Sep 17 00:00:00 2001 From: Courtney Nguyen Date: Wed, 26 Jan 2022 21:21:01 -0700 Subject: [PATCH 3/8] Added tests, and switched rule from erroring to warning --- __tests__/src/rules/no-disabled-test.js | 51 +++++++++++++++++++++++++ src/index.js | 3 ++ src/rules/no-disabled.js | 46 ++++++++++------------ 3 files changed, 74 insertions(+), 26 deletions(-) create mode 100644 __tests__/src/rules/no-disabled-test.js diff --git a/__tests__/src/rules/no-disabled-test.js b/__tests__/src/rules/no-disabled-test.js new file mode 100644 index 000000000..4444eeff0 --- /dev/null +++ b/__tests__/src/rules/no-disabled-test.js @@ -0,0 +1,51 @@ +/* eslint-env jest */ +/** + * @fileoverview Enforce autoFocus prop is not used. + * @author Courtney Nguyen <@courtyenn> + */ + +// ----------------------------------------------------------------------------- +// Requirements +// ----------------------------------------------------------------------------- + +import { RuleTester } from 'eslint'; +import rule from '../../../src/rules/no-disabled'; +import parserOptionsMapper from '../../__util__/parserOptionsMapper'; + +// ----------------------------------------------------------------------------- +// Tests +// ----------------------------------------------------------------------------- + +const ruleTester = new RuleTester(); + +const expectedWarning = { + message: 'The disabled prop removes the element from being detected by screen readers.', + type: 'JSXAttribute', +}; + +ruleTester.run('no-disabled', rule, { + valid: [ + { code: '
' }, + { code: '
' }, + { code: '' }, + { code: '