From 384858b9edc1bf6837dfac7edc124212950d75ce Mon Sep 17 00:00:00 2001 From: Tyler Earls Date: Sun, 29 Dec 2024 11:14:37 -0600 Subject: [PATCH] feat(linter): implement `jsx-a11y/no-noninteractive-tabindex` (#8167) This PR implements the [jsx-a11y/no-noninteractive-tabindex](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/91e39b45ade789c86ae14df869a86b0ea468ed95/docs/rules/no-noninteractive-tabindex.md) rule. I set the default values as the ones that are in the recommended config. I used this [html spec section](https://html.spec.whatwg.org/multipage/dom.html#interactive-content) to source the interactive elements. The interactive roles came from [this article in the MDN docs](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles#2._widget_roles). --- crates/oxc_linter/src/rules.rs | 2 + .../jsx_a11y/no_noninteractive_tabindex.rs | 217 ++++++++++++++++++ .../jsx_a11y_no_noninteractive_tabindex.snap | 31 +++ 3 files changed, 250 insertions(+) create mode 100644 crates/oxc_linter/src/rules/jsx_a11y/no_noninteractive_tabindex.rs create mode 100644 crates/oxc_linter/src/snapshots/jsx_a11y_no_noninteractive_tabindex.snap diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index 2724a3eb142ea..a11826c8441f2 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -413,6 +413,7 @@ mod jsx_a11y { pub mod no_aria_hidden_on_focusable; pub mod no_autofocus; pub mod no_distracting_elements; + pub mod no_noninteractive_tabindex; pub mod no_redundant_roles; pub mod prefer_tag_over_role; pub mod role_has_required_aria_props; @@ -752,6 +753,7 @@ oxc_macros::declare_all_lint_rules! { jsx_a11y::lang, jsx_a11y::media_has_caption, jsx_a11y::mouse_events_have_key_events, + jsx_a11y::no_noninteractive_tabindex, jsx_a11y::no_access_key, jsx_a11y::no_aria_hidden_on_focusable, jsx_a11y::no_autofocus, diff --git a/crates/oxc_linter/src/rules/jsx_a11y/no_noninteractive_tabindex.rs b/crates/oxc_linter/src/rules/jsx_a11y/no_noninteractive_tabindex.rs new file mode 100644 index 0000000000000..2251324f9ee4d --- /dev/null +++ b/crates/oxc_linter/src/rules/jsx_a11y/no_noninteractive_tabindex.rs @@ -0,0 +1,217 @@ +use oxc_ast::{ + ast::{JSXAttributeItem, JSXAttributeValue}, + AstKind, +}; +use oxc_diagnostics::OxcDiagnostic; +use oxc_macros::declare_oxc_lint; +use oxc_span::{CompactStr, Span}; +use phf::phf_set; + +use crate::{ + context::LintContext, + rule::Rule, + utils::{get_element_type, has_jsx_prop_ignore_case}, + AstNode, +}; + +fn no_noninteractive_tabindex_diagnostic(span: Span) -> OxcDiagnostic { + OxcDiagnostic::warn("tabIndex should only be declared on interactive elements") + .with_help("tabIndex attribute should be removed") + .with_label(span) +} + +#[derive(Debug, Clone)] +pub struct NoNoninteractiveTabindex(Box); + +#[derive(Debug, Clone)] +struct NoNoninteractiveTabindexConfig { + tags: Vec, + roles: Vec, + allow_expression_values: bool, +} + +impl Default for NoNoninteractiveTabindex { + fn default() -> Self { + Self(Box::new(NoNoninteractiveTabindexConfig { + roles: vec![CompactStr::new("tabpanel")], + allow_expression_values: true, + tags: vec![], + })) + } +} + +declare_oxc_lint!( + /// ### What it does + /// This rule checks that non-interactive elements don't have a tabIndex which would make them interactive via keyboard navigation. + /// + /// ### Why is this bad? + /// + /// Tab key navigation should be limited to elements on the page that can be interacted with. + /// Thus it is not necessary to add a tabindex to items in an unordered list, for example, + /// to make them navigable through assistive technology. + /// + /// These applications already afford page traversal mechanisms based on the HTML of the page. + /// Generally, we should try to reduce the size of the page's tab ring rather than increasing it. + /// + /// ### Examples + /// + /// Examples of **incorrect** code for this rule: + /// ```jsx + ///
+ ///
+ ///
+ ///
+ /// ``` + /// + /// Examples of **correct** code for this rule: + /// ```jsx + ///
+ /// + ///