feat(linter/jsx-a11y): implement no-noninteractive-element-to-interactive-role#21264
Conversation
camc314
left a comment
There was a problem hiding this comment.
hi @pedrotainha, thanks for this PR - CI is failing, please take a look.
Trying to replicate the pipeline checks in my local environment to prevent these failures at pr creation. Thanks |
95c9e53 to
8abe0ac
Compare
a229612 to
a4865a7
Compare
Meanwhile, as stated in the pr body: "Question for maintainers: Would it make sense to open a separate issue/PR to add these 4 roles to the shared INTERACTIVE_ROLES list? That would bring it in line with the WAI-ARIA spec and the original eslint-plugin-jsx-a11y, and allow no_noninteractive_tabindex to drop its local override as well." Does this make sense for you? I can open an issue and PR after this PR |
6affe24 to
5e53aa5
Compare
|
@camc314 can you approve the pipeline to run the checks? |
Merging this PR will not alter performance
Comparing Footnotes
|
5e53aa5 to
d632093
Compare
|
@camc314 now fixed in last commit |
02a734b to
f5f7109
Compare
…tive-role Implements the `jsx-a11y/no-noninteractive-element-to-interactive-role` rule which enforces that non-interactive HTML elements are not assigned interactive ARIA roles. Uses the shared `is_non_interactive_element` and `is_interactive_role` utilities. Adds `grid`, `tablist`, `tree`, `treegrid` as additional interactive roles to align with the original eslint-plugin-jsx-a11y rule and the WAI-ARIA spec. Supports configurable allowed role overrides per element. Default config matches the eslint-plugin-jsx-a11y recommended config (e.g., `ul` allows `menu`, `tablist`, etc.). Tests ported from the original eslint-plugin-jsx-a11y test suite, covering both recommended (with allowed overrides) and strict (no overrides) modes. Closes oxc-project#21195 AI Disclosure: This implementation was assisted by Claude Code (Opus 4.6). All code has been personally reviewed, tested, and validated against the original rule's logic and test suite.
…ent-to-interactive-role - Use oxc_str::CompactStr instead of oxc_span::CompactStr (private in MSRV 1.92.0) - Add explicit type annotation in closure for MSRV compatibility
…fy comment Revert the .DS_Store .gitignore addition (belongs in a separate PR). Reformulate the ADDITIONAL_INTERACTIVE_ROLES comment to remove the misleading mention of progressbar.
… for no-noninteractive-element-to-interactive-role Collapse nested if into single let-chain to satisfy clippy::collapsible_if. Add Deserialize + JsonSchema derives and config declaration to fix rule_configuration_documentation_test.
f5f7109 to
337c1e6
Compare
Hello, any chance of approving again the checks please? |
|
@camc314 do you have time to review this PR? I set a goal for myself to contribute to the a11y rules. |
|
@camchenry the pending checks take longer time? Or does it need some action/approval? |
yes, this sounds like a good idea to me. if there's anything missing that is in the ARIA spec, then we should implement it in oxlint. |
I think it needs a rebase, but it should be fine. |
|
@pedrotainha actually, this needs a rebase. it will require a few small fixes like adding |
|
Hi, I added these roles to
|
Co-authored-by: Copilot <copilot@github.com>
…nteractive-element-to-interactive-role
Co-authored-by: Copilot <copilot@github.com>
Removed this in 50b61c3 |
Merge activity
You must have a Graphite account in order to use the merge queue. Create an account and try again using this link |
Sorry, only get back to open source now again. I've been busy at work managing other priorities. |
No worries, thank you for contributing! |
# Oxlint ### 💥 BREAKING CHANGES - 00ce512 oxlint/lsp: [**BREAKING**] Don't fix suggestions on fixAll code actions & command (#22195) (Sysix) ### 🚀 Features - 0eeceaf linter/no-unused-vars: Rename parameter with initializer (#22308) (camc314) - fa0232b linter/no-unused-vars: Add param rename suggestion (#22285) (Ryota Misumi) - ae59305 linter/promise/no-promise-in-callback: Add `exemptDeclarations` option (#22275) (Mikhail Baev) - 60bed4a linter: Extends `no-redundant-roles` and `prefer-tag-over-role` support roles (#22069) (mehm8128) - 545c80f linter/eslint: Implement `prefer-regex-literals` rule (#22192) (Mikhail Baev) - cf86d7a linter: Bulk suppression (#19328) (Said Atrahouch) - 23abd22 linter/jsx-a11y: Implement no-noninteractive-element-to-interactive-role (#21264) (Pedro Tainha) - fbb8f22 linter: Support `ignores` in overrides (#22148) (camc314) - 5a4414d oxlint/lsp: Support `rulesCustomization` lsp option (#21858) (Sysix) ### 🐛 Bug Fixes - 610f4c7 linter/no-unused-vars: Avoid renaming captured vars (#22310) (camc314) - 6b50f23 oxlint/cli: Load root config by searching up parent directories (#22272) (Sysix) - 31a5de7 linter: Rename override `ignores` to `excludeFiles` (#22283) (camc314) - 26d5d7b linter: Add missing vitest/valid-describe-callback functionality (#22279) (camchenry) - 784530f linter: `valid-title`: detect `String.raw` strings (#22271) (Sysix) - 080d90e linter: Move `no-debugger` fix to suggestion (#22256) (Sysix) - 25b7017 linter: Undocument override `ignores` option (#22213) (camc314) - 7bb00dd linter: Fix role-has-required-aria-props (#22097) (mehm8128) - d25279e linter/disable-directives: Improve parsing of names, descriptions (#22184) (camc314) - a59e447 linter/disable-directives: Ignore invalid enable suffixes (#22179) (camc314) - aafef0f ci: Disable bulk supression test on big endian (#22175) (camc314) - 281daec linter/vue/define-props-destructuring: Add `only-when-assigned` config opt (#22142) (camc314) - 46ab679 linter/plugins: Trim leading newline for partial sources (#20928) (bab) - 29ff6d9 linter: Update docs for no_alias_methods rule to be Vitest-specific and add toThrowError alias (#22129) (camchenry) ### ⚡ Performance - 9414bee linter/role-has-required-aria-props: Avoid intermediate vec (#22212) (camc314) - 3883ea3 linter/no-useless-escape: Drop unnecessary Vec collect (#22171) (connorshea) - 42c3029 linter/check-property-names: Replace split-collect-pop-join with rfind (#22172) (connorshea) - 9551d53 linter: Remove unnecessary Vec collect in CFG edge traversal (#22167) (connorshea) - 26fa2fc linter/aria-role: Remove unnecessary string allocations in run method (#22168) (connorshea) - c9ce045 linter/getter-return: Remove unnecessary Vec collect in CFG edge traversal (#22166) (connorshea) - 72bd846 linter/no-this-in-sfc: Reorder cheap name check, avoid String allocation (#22164) (connorshea) ### 📚 Documentation - 4da212a linter/no-unused-vars: Add docs to `rename_unused_function_parameter` (#22311) (camc314) - 27c4628 linter/forbid-dom-props: Escape jsx examples in lint rule docs (#22254) (4MBL) - 3f81147 linter: Improve the `react/jsx-key` rule docs. (#22162) (connorshea) - 07f03cc linter/consistent-return: Add note about `noImplicitReturns` coverage (#22156) (camc314) - 7c1e049 oxlint/lsp: Improve autogenerated lsp docs (#22154) (Sysix) - 87b3e38 linter: Update docs to be vitest-specific for consistent-test-it (#22128) (camchenry) # Oxfmt ### 💥 BREAKING CHANGES - 5c6c390 oxfmt: [**BREAKING**] Respect more git ignore options, align with Oxlint (#22210) (leaysgur) ### 🚀 Features - 6e8e818 oxfmt: Experimental .svelte support (#21700) (leaysgur) ### 🐛 Bug Fixes - e2a20b6 formatter: Add space after commas in import attributes (#22274) (Leonabcd123) ### ⚡ Performance - b756682 oxfmt: Optimize nested config prescan (#22232) (Jovi De Croock) - f14e81e formatter/sort_imports: Skip sort for single import runs (#22204) (leaysgur) - 32255b1 formatter: Process `ImportDeclaration`s in a run (#22079) (overlookmotel) ### 📚 Documentation - 4da6f4c formatter: Correct comment (#22217) (overlookmotel) - ef3507d formatter/sort_imports: Refresh docs (#22203) (leaysgur) Co-authored-by: Cameron <cameron.clark@hey.com>
…2337) ## Summary Adds the `jsx-a11y/no-noninteractive-element-interactions` rule to oxlint. This rule reports mouse/keyboard event handlers on non-interactive elements or elements with non-interactive ARIA roles, while respecting interactive elements, interactive/abstract/presentation roles, hidden content, `contentEditable`, custom component mappings, and handler exception configuration. ## Notes - Test coverage is based on the upstream `eslint-plugin-jsx-a11y` rule fixtures. - There is an earlier open PR for this same rule at #17608; this PR is a fresh implementation/review pass for that rule. - This follows the structure and conventions from the related `jsx-a11y/no-noninteractive-element-to-interactive-role` work in #21264. - The local test suite intentionally keeps `separator` and `progressbar` behavior tied to Oxc's existing shared ARIA role utilities for now. - Upstream expects `role="separator"` to report and `role="progressbar"` to pass. Oxc's current shared role classification leads to the opposite behavior for those two cases, so I would like maintainer guidance on whether this rule should add rule-specific overrides or whether the shared utilities should change. ## Testing - `cargo +stable fmt --check` - `cargo +stable test -p oxc_linter -- no_noninteractive_element_interactions` - `cargo +stable insta pending-snapshots` - `git diff --check` ## Disclosure I used Pi and Codex with GPT-5.5 to help prepare these changes, and manually reviewed everything before opening this PR.
Summary
Implements the
jsx-a11y/no-noninteractive-element-to-interactive-rolerule, which enforces that non-interactive HTML elements (e.g.,<h1>,<li>,<article>) are not assigned interactive ARIA roles (e.g.,button,link,menuitem).Related umbrella: #1141
Implementation details
is_non_interactive_elementandis_interactive_roleutilities fromcrate::utils::react{ "ul": ["menu", "tablist"] })eslint-plugin-jsx-a11yrecommended configNote on
INTERACTIVE_ROLESgapWhile porting the tests, I noticed that the shared
INTERACTIVE_ROLESlist inutils/react.rsis missinggrid,tablist,tree, andtreegrid— all of which are interactive widget roles per WAI-ARIA and are treated as interactive by the originaleslint-plugin-jsx-a11y.The existing
no_noninteractive_tabindexrule works around this with its ownINTERACTIVE_HTML_ROLESconstant (29 entries, including these 4).For this PR, I added a local
ADDITIONAL_INTERACTIVE_ROLESconstant to align with the original rule's behavior without modifying shared utilities.Question for maintainers: Would it make sense to open a separate issue/PR to add these 4 roles to the shared
INTERACTIVE_ROLESlist? That would bring it in line with the WAI-ARIA spec and the original eslint-plugin-jsx-a11y, and allowno_noninteractive_tabindexto drop its local override as well.AI Disclosure
This implementation was assisted by Claude Code (Opus 4.6). All code has been personally reviewed, tested, and validated against the original rule's logic and test suite.
Test plan
cargo lintgen— regenerates correctly (719 variants)cargo test -p oxc_linter -- no_noninteractive_element_to_interactive_role— all tests pass