feat: rule mouse-events-have-key-events
MODIFY
packages/@romejs/diagnostics/categories.ts
packages/@romejs/diagnostics/categories.ts
@@ -148,6 +148,7 @@ type LintDiagnosticCategory =
| "lint/jsx-a11y/iframeHasTitle"
| "lint/jsx-a11y/imgRedundantAlt"
| "lint/jsx-a11y/lang"
+ | "lint/jsx-a11y/mouseEventsHaveKeyEvents"
| "lint/jsx-a11y/noAccessKey"
| "lint/jsx-a11y/noAutofocus"
| "lint/jsx-a11y/noDistractingElements"

MODIFY
packages/@romejs/diagnostics/descriptions.ts
packages/@romejs/diagnostics/descriptions.ts
@@ -292,6 +292,13 @@ export const descriptions = createMessages({
},
// @romejs/js-compiler
LINT: {
+ JSX_A11Y_MOUSE_EVENTS_HAVE_KEY_EVENTS: (
+ mouseEvent: string,
+ keyboardEvent: string,
+ ) => ({
+ category: "lint/jsx-a11y/mouseEventsHaveKeyEvents",
+ message: `The mouse event ${mouseEvent} should be paired with the event ${keyboardEvent}`,
+ }),
REACT_NO_WILL_UPDATE_SET_STATE: {
category: "lint/react/noWillUpdateSetState",
message: "Avoid <emphasis>this.setState</emphasis> in <emphasis>componentWillUpdate</emphasis>",

MODIFY
packages/@romejs/js-compiler/lint/rules/index.ts
packages/@romejs/js-compiler/lint/rules/index.ts
@@ -62,6 +62,7 @@ import htmlHasLang from "./jsx-a11y/htmlHasLang";
import iframeHasTitle from "./jsx-a11y/iframeHasTitle";
import imgRedundantAlt from "./jsx-a11y/imgRedundantAlt";
import lang from "./jsx-a11y/lang";
+import mouseEventsHaveKeyEvents from "./jsx-a11y/mouseEventsHaveKeyEvents";
import noAccessKey from "./jsx-a11y/noAccessKey";
import noAutofocus from "./jsx-a11y/noAutofocus";
import noDistractingElements from "./jsx-a11y/noDistractingElements";
@@ -137,6 +138,7 @@ export const lintTransforms = [
iframeHasTitle,
imgRedundantAlt,
lang,
+ mouseEventsHaveKeyEvents,
noAccessKey,
noAutofocus,
noDistractingElements,

ADD
packages/@romejs/js-compiler/lint/rules/jsx-a11y/mouseEventsHaveKeyEvents.test.ts
+import {test} from "rome";
+import {testLintMultiple} from "../testHelpers";
+
+test(
+ "mouse events have key events",
+ async (t) => {
+ await testLintMultiple(
+ t,
+ [
+ // INVALID
+ "<div onMouseOver={ () => void 0 } />",
+ "<div onMouseOut={ () => void 0 } />",
+ "<div onMouseOver={ () => void 0 } >{props}</div>",
+ "<div onMouseOut={ () => void 0 } >{props}</div>",
+
+ // VALID
+ "<div onMouseOver={ () => void 0 } onFocus={() => void 0} />",
+ "<div onMouseOut={ () => void 0 } onBlur={() => void 0} />",
+ "<div onMouseOver={ () => void 0 } onFocus={() => void 0} >{props}</div>",
+ "<div onMouseOut={ () => void 0 } onBlur={() => void 0} >{props}</div>",
+ ],
+ {category: "lint/jsx-a11y/mouseEventsHaveKeyEvents"},
+ );
+ },
+);

ADD
packages/@romejs/js-compiler/lint/rules/jsx-a11y/mouseEventsHaveKeyEvents.ts
+import {Path, TransformExitResult} from "@romejs/js-compiler";
+import {descriptions} from "@romejs/diagnostics";
+import {hasJSXAttribute, isJSXElement} from "@romejs/js-ast-utils";
+
+export default {
+ name: "mouseEventsHaveKeyEvents",
+ enter(path: Path): TransformExitResult {
+ const {node} = path;
+
+ if (isJSXElement(node)) {
+ if (
+ hasJSXAttribute(node, "onMouseOver") &&
+ !hasJSXAttribute(node, "onFocus")
+ ) {
+ path.context.addNodeDiagnostic(
+ node,
+ descriptions.LINT.JSX_A11Y_MOUSE_EVENTS_HAVE_KEY_EVENTS(
+ "onMouseOver",
+ "onFocus",
+ ),
+ );
+ }
+
+ if (
+ hasJSXAttribute(node, "onMouseOut") &&
+ !hasJSXAttribute(node, "onBlur")
+ ) {
+ path.context.addNodeDiagnostic(
+ node,
+ descriptions.LINT.JSX_A11Y_MOUSE_EVENTS_HAVE_KEY_EVENTS(
+ "onMouseOut",
+ "onBlur",
+ ),
+ );
+ }
+ }
+
+ return node;
+ },
+};

feat: rule mouse-events-have-key-events