Skip to content

Commit 4ce252c

Browse files
committed
feat(linter): add dangerous suggestion for jsx-a11y/tabindex-no-positive (#12963)
There should be 3 different fixes, but I moved with the "safest" one: ### Change tabindex to 0 Still be focusable with the keyboard ### Changing tabindex to -1 Could work when the focus is only needed with the JS API `element.focus()` ### Removing tabindex This could break applications which have custom aria roles and need the tabindex to work correctly.
1 parent 6fe0bb5 commit 4ce252c

File tree

1 file changed

+38
-26
lines changed

1 file changed

+38
-26
lines changed

crates/oxc_linter/src/rules/jsx_a11y/tabindex_no_positive.rs

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use oxc_ast::{AstKind, ast::JSXAttributeItem};
22
use oxc_diagnostics::OxcDiagnostic;
33
use oxc_macros::declare_oxc_lint;
4-
use oxc_span::Span;
4+
use oxc_span::{GetSpan, Span};
55

66
use crate::{
77
AstNode,
@@ -46,7 +46,7 @@ declare_oxc_lint!(
4646
TabindexNoPositive,
4747
jsx_a11y,
4848
correctness,
49-
pending
49+
dangerous_suggestion
5050
);
5151

5252
impl Rule for TabindexNoPositive {
@@ -65,7 +65,10 @@ fn check_and_diagnose(attr: &JSXAttributeItem, ctx: &LintContext<'_>) {
6565
JSXAttributeItem::Attribute(attr) => attr.value.as_ref().map_or((), |value| {
6666
if let Ok(parsed_value) = parse_jsx_value(value) {
6767
if parsed_value > 0.0 {
68-
ctx.diagnostic(tabindex_no_positive_diagnostic(attr.span));
68+
ctx.diagnostic_with_dangerous_suggestion(
69+
tabindex_no_positive_diagnostic(attr.span),
70+
|fixer| fixer.replace(value.span(), r#""0""#),
71+
);
6972
}
7073
}
7174
}),
@@ -78,34 +81,43 @@ fn test() {
7881
use crate::tester::Tester;
7982

8083
let pass = vec![
81-
(r"<div />;", None),
82-
(r"<div {...props} />", None),
83-
(r#"<div id="main" />"#, None),
84-
(r"<div tabIndex={undefined} />", None),
85-
(r"<div tabIndex={`${undefined}`} />", None),
86-
(r"<div tabIndex={`${undefined}${undefined}`} />", None),
87-
(r"<div tabIndex={0} />", None),
88-
(r"<div tabIndex={-1} />", None),
89-
(r"<div tabIndex={null} />", None),
90-
(r"<div tabIndex={bar()} />", None),
91-
(r"<div tabIndex={bar} />", None),
92-
(r#"<div tabIndex={"foobar"} />"#, None),
93-
(r#"<div tabIndex="0" />"#, None),
94-
(r#"<div tabIndex="-1" />"#, None),
95-
(r#"<div tabIndex="-5" />"#, None),
96-
(r#"<div tabIndex="-5.5" />"#, None),
97-
(r"<div tabIndex={-5.5} />", None),
98-
(r"<div tabIndex={-5} />", None),
84+
r"<div />;",
85+
r"<div {...props} />",
86+
r#"<div id="main" />"#,
87+
r"<div tabIndex={undefined} />",
88+
r"<div tabIndex={`${undefined}`} />",
89+
r"<div tabIndex={`${undefined}${undefined}`} />",
90+
r"<div tabIndex={0} />",
91+
r"<div tabIndex={-1} />",
92+
r"<div tabIndex={null} />",
93+
r"<div tabIndex={bar()} />",
94+
r"<div tabIndex={bar} />",
95+
r#"<div tabIndex={"foobar"} />"#,
96+
r#"<div tabIndex="0" />"#,
97+
r#"<div tabIndex="-1" />"#,
98+
r#"<div tabIndex="-5" />"#,
99+
r#"<div tabIndex="-5.5" />"#,
100+
r"<div tabIndex={-5.5} />",
101+
r"<div tabIndex={-5} />",
99102
];
100103

101104
let fail = vec![
102-
(r#"<div tabIndex="1" />"#, None),
103-
(r"<div tabIndex={1} />", None),
104-
(r#"<div tabIndex={"1"} />"#, None),
105-
(r"<div tabIndex={`1`} />", None),
106-
(r"<div tabIndex={1.589} />", None),
105+
r#"<div tabIndex="1" />"#,
106+
r"<div tabIndex={1} />",
107+
r#"<div tabIndex={"1"} />"#,
108+
r"<div tabIndex={`1`} />",
109+
r"<div tabIndex={1.589} />",
110+
];
111+
112+
let fix = vec![
113+
(r#"<div tabIndex="1" />"#, r#"<div tabIndex="0" />"#),
114+
(r"<div tabIndex={1} />", r#"<div tabIndex="0" />"#),
115+
(r#"<div tabIndex={"1"} />"#, r#"<div tabIndex="0" />"#),
116+
(r"<div tabIndex={`1`} />", r#"<div tabIndex="0" />"#),
117+
(r"<div tabIndex={1.589} />", r#"<div tabIndex="0" />"#),
107118
];
108119

109120
Tester::new(TabindexNoPositive::NAME, TabindexNoPositive::PLUGIN, pass, fail)
121+
.expect_fix(fix)
110122
.test_and_snapshot();
111123
}

0 commit comments

Comments
 (0)