diff --git a/README.md b/README.md index 330bed0756..a0216e05d2 100644 --- a/README.md +++ b/README.md @@ -184,6 +184,7 @@ rules in templates can be disabled with eslint directives with mustache or html | [template-no-abstract-roles](docs/rules/template-no-abstract-roles.md) | disallow abstract ARIA roles | | | | | [template-no-accesskey-attribute](docs/rules/template-no-accesskey-attribute.md) | disallow accesskey attribute | | 🔧 | | | [template-no-aria-unsupported-elements](docs/rules/template-no-aria-unsupported-elements.md) | disallow ARIA roles, states, and properties on elements that do not support them | | | | +| [template-no-autofocus-attribute](docs/rules/template-no-autofocus-attribute.md) | disallow autofocus attribute | | 🔧 | | ### Best Practices diff --git a/docs/rules/template-no-autofocus-attribute.md b/docs/rules/template-no-autofocus-attribute.md new file mode 100644 index 0000000000..8a20da2abd --- /dev/null +++ b/docs/rules/template-no-autofocus-attribute.md @@ -0,0 +1,48 @@ +# ember/template-no-autofocus-attribute + +🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). + + + +Disallows the use of `autofocus` attribute on elements. + +The `autofocus` attribute can cause usability issues for both sighted and non-sighted users by disrupting expected behavior and screen reader announcements. + +## Examples + +Examples of **incorrect** code for this rule: + +```gjs + +``` + +```gjs + +``` + +Examples of **correct** code for this rule: + +```gjs + +``` + +```gjs + +``` + +## When Not To Use It + +If you need to autofocus for specific accessibility or UX requirements and have thoroughly tested with assistive technologies, you may disable this rule for those specific cases. + +## References + +- [eslint-plugin-ember template-no-autofocus-attribute](https://github.com/ember-cli/eslint-plugin-ember/blob/master/docs/rules/template-no-autofocus-attribute.md) +- [MDN autofocus attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus) diff --git a/lib/rules/template-no-autofocus-attribute.js b/lib/rules/template-no-autofocus-attribute.js new file mode 100644 index 0000000000..b0d775d14a --- /dev/null +++ b/lib/rules/template-no-autofocus-attribute.js @@ -0,0 +1,48 @@ +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + type: 'suggestion', + docs: { + description: 'disallow autofocus attribute', + category: 'Accessibility', + strictGjs: true, + strictGts: true, + url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-autofocus-attribute.md', + }, + fixable: 'code', + schema: [], + messages: { + noAutofocus: + 'Avoid using autofocus attribute. Autofocusing elements can cause usability issues for sighted and non-sighted users.', + }, + }, + + create(context) { + return { + GlimmerElementNode(node) { + const autofocusAttr = node.attributes?.find((attr) => attr.name === 'autofocus'); + + if (autofocusAttr) { + context.report({ + node: autofocusAttr, + messageId: 'noAutofocus', + fix(fixer) { + // Remove the attribute including preceding whitespace + const sourceCode = context.sourceCode; + const text = sourceCode.getText(); + const attrStart = autofocusAttr.range[0]; + const attrEnd = autofocusAttr.range[1]; + + let removeStart = attrStart; + while (removeStart > 0 && /\s/.test(text[removeStart - 1])) { + removeStart--; + } + + return fixer.removeRange([removeStart, attrEnd]); + }, + }); + } + }, + }; + }, +}; diff --git a/tests/lib/rules/template-no-autofocus-attribute.js b/tests/lib/rules/template-no-autofocus-attribute.js new file mode 100644 index 0000000000..afee5842d2 --- /dev/null +++ b/tests/lib/rules/template-no-autofocus-attribute.js @@ -0,0 +1,57 @@ +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require('../../../lib/rules/template-no-autofocus-attribute'); +const RuleTester = require('eslint').RuleTester; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const ruleTester = new RuleTester({ + parser: require.resolve('ember-eslint-parser'), + parserOptions: { ecmaVersion: 2022, sourceType: 'module' }, +}); + +ruleTester.run('template-no-autofocus-attribute', rule, { + valid: [ + ``, + ``, + ], + + invalid: [ + { + code: ``, + output: ``, + errors: [ + { + message: + 'Avoid using autofocus attribute. Autofocusing elements can cause usability issues for sighted and non-sighted users.', + type: 'GlimmerAttrNode', + }, + ], + }, + { + code: ``, + output: ``, + errors: [ + { + type: 'GlimmerAttrNode', + }, + ], + }, + ], +});