feat(rules): add prefer-keybind-library draft rule#743
Draft
NisargIO wants to merge 4 commits into
Draft
Conversation
Co-authored-by: Nisarg Patel <NisargIO@users.noreply.github.com>
Flags hand-rolled keyboard shortcuts (addEventListener('keydown'|'keyup'|'keypress')
whose handler inspects a KeyboardEvent shortcut property) and recommends a dedicated
keybind library (react-hotkeys-hook by default, or one already imported). Registered
as defaultEnabled: false (opt-in draft).
Co-authored-by: Nisarg Patel <NisargIO@users.noreply.github.com>
Co-authored-by: Nisarg Patel <NisargIO@users.noreply.github.com>
…arison Require the handler to COMPARE a KeyboardEvent key-identity property (key/code/keyCode/which/charCode) rather than merely read one, and exempt Tab-only focus trapping. Eliminates false positives found while validating against open-source repos (MUI useIsFocusVisible modality detection and FocusTrap/Excalidraw/tldraw Tab focus trapping). Adds regression tests and a changeset. Co-authored-by: Nisarg Patel <NisargIO@users.noreply.github.com>
commit: |
Contributor
|
No React Doctor issues found. 🎉 Reviewed by React Doctor for commit |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Catches a hand-rolled keyboard shortcut, a
keydown/keyup/keypressaddEventListenerwhose handler compares aKeyboardEventkey-identity property to decide which combination fired.Hand-rolling shortcuts means re-implementing platform
metavsctrlnormalization, scoping, key sequences, text-input exclusion, and the add/remove listener lifecycle by hand, and it's easy to get those wrong. A dedicated library (react-hotkeys-hook and friends) owns all of that.Before:
After:
What changed
prefer-keybind-library(bucket:architecture, severitywarn).defaultEnabled: false), so it ships in the plugin but stays out of the recommended preset until teams opt in viaseverityControls.addEventListener("keydown" | "keyup" | "keypress", handler)where the handler compares a key-identity property (key,code,keyCode,which,charCode). Comparison forms supported: equality (event.key === "Escape"),switch (event.code), and membership/string tests (["j","k"].includes(event.key),event.code.startsWith("Arrow")). Resolves the handler inline, through one binding hop, and via destructured / renamed event params (({ key: pressedKey }) =>,const { key } = event).react-hotkeys-hookby default, or one the file already imports (tinykeys,hotkeys-js,mousetrap,@mantine/hooks,@github/hotkey,react-hotkeys).event.metaKey || event.altKey) with no key-identity comparison.event.key === "Tab",=== KEYS.TAB,keyCode === 9).onKeyDownhandlers (element-level a11y), non-keyboard listeners, computed event names, unresolved/imported handlers, shadowed inner params, and.keyaccess on unrelated objects.constants/dom.ts,constants/library.ts), 31 co-located unit tests, and 5 end-to-end regression tests.Eval results
Validated against 7 large open-source React codebases (scoped to files with keyboard
addEventListenercalls, rule force-enabled, output filtered to this rule):7(excalidraw, cal.com, outline, lobe-chat, tldraw, mui, supabase)prefer-keybind-library440(after refinement; an earlier draft surfaced 4, all now exempted)The earlier draft flagged 57; manual inspection found 4 false positives (MUI
useIsFocusVisiblemodality detection, MUIFocusTrap, ExcalidrawDialog, and tldrawA11yTab focus trapping). The detector was tightened to require a real key comparison and exempt Tab-only focus trapping, dropping those to 0 while keeping every true positive (Escape-to-close, Cmd+K palettes, single-key shortcuts,useKeyPress-style hooks).Test plan
vp test runonprefer-keybind-library.test.ts(31 unit cases: positives, alias/destructure resolution, library suggestion, and false-positive traps).vp test runontests/regressions/prefer-keybind-library.test.ts(5 end-to-end cases through the real oxlint pipeline).tsc --noEmitforoxlint-plugin-react-doctor(passes).vp lint+vp fmt --checkon all touched files (pass).pnpm genregistry regeneration (committed).Note: the repo's broader plugin test run has ~38 pre-existing failures in unrelated
react-builtinsrules (jsx-no-new-*,no-multi-comp, …) that also fail on a cleanmaincheckout in this environment; they are not affected by this change.