add clickEventsHaveKeyEvents rule and tests
MODIFY
packages/@romejs/compiler/lint/rules/index.ts
packages/@romejs/compiler/lint/rules/index.ts
@@ -58,6 +58,7 @@ import unusedVariables from "./js/unusedVariables";
import altText from "./jsx-a11y/altText";
import anchorHasContent from "./jsx-a11y/anchorHasContent";
import ariaUnsupportedElements from "./jsx-a11y/ariaUnsupportedElements";
+import clickEventsHaveKeyEvents from "./jsx-a11y/clickEventsHaveKeyEvents";
import headingHasContent from "./jsx-a11y/headingHasContent";
import htmlHasLang from "./jsx-a11y/htmlHasLang";
import iframeHasTitle from "./jsx-a11y/iframeHasTitle";
@@ -144,6 +145,7 @@ export const lintTransforms = [
altText,
anchorHasContent,
ariaUnsupportedElements,
+ clickEventsHaveKeyEvents,
headingHasContent,
htmlHasLang,
iframeHasTitle,

ADD
packages/@romejs/compiler/lint/rules/jsx-a11y/clickEventsHaveKeyEvents.test.ts
+import {test} from "rome";
+import {testLintMultiple} from "../testHelpers";
+
+test(
+ "jsx a11y click events have key events",
+ async (t) => {
+ await testLintMultiple(
+ t,
+ [
+ // INVALID
+ "<div onClick={() => {}} />",
+ // VALID
+ "<div onClick={() => {}} onKeyDown={this.handleKeyDown} />",
+ "<div onClick={() => {}} onKeyUp={this.handleKeyUp} />",
+ "<div onClick={() => {}} onKeyPress={this.handleKeyPress} />",
+ ],
+ {category: "lint/jsx-a11y/clickEventsHaveKeyEvents"},
+ );
+ },
+);

ADD
packages/@romejs/compiler/lint/rules/jsx-a11y/clickEventsHaveKeyEvents.ts
+import {Path, TransformExitResult} from "@romejs/compiler";
+import {descriptions} from "@romejs/diagnostics";
+import {hasJSXAttribute, isJSXElement} from "@romejs/js-ast-utils";
+
+export default {
+ name: "clickEventsHaveKeyEvents",
+ enter(path: Path): TransformExitResult {
+ const {node} = path;
+
+ if (
+ isJSXElement(node) &&
+ hasJSXAttribute(node, "onClick") &&
+ !(hasJSXAttribute(node, "onKeyUp") ||
+ hasJSXAttribute(node, "onKeyDown") ||
+ hasJSXAttribute(node, "onKeyPress"))
+ ) {
+ path.context.addNodeDiagnostic(
+ node,
+ descriptions.LINT.JSX_A11Y_CLICK_EVENTS_HAVE_KEY_EVENTS,
+ );
+ }
+
+ return node;
+ },
+};

MODIFY
packages/@romejs/diagnostics/categories.ts
packages/@romejs/diagnostics/categories.ts
@@ -144,6 +144,7 @@ type LintDiagnosticCategory =
| "lint/jsx-a11y/altText"
| "lint/jsx-a11y/anchorHasContent"
| "lint/jsx-a11y/ariaUnsupportedElements"
+ | "lint/jsx-a11y/clickEventsHaveKeyEvents"
| "lint/jsx-a11y/headingHasContent"
| "lint/jsx-a11y/htmlHasLang"
| "lint/jsx-a11y/iframeHasTitle"

MODIFY
packages/@romejs/diagnostics/descriptions/lint.ts
packages/@romejs/diagnostics/descriptions/lint.ts
@@ -16,6 +16,17 @@ import {buildSuggestionAdvice} from "../helpers";
import {createDiagnosticsCategory, orJoin} from "./index";
export const lint = createDiagnosticsCategory({
+ JSX_A11Y_CLICK_EVENTS_HAVE_KEY_EVENTS: {
+ category: "lint/jsx-a11y/clickEventsHaveKeyEvents",
+ message: "Pair the <emphasis>onClick</emphasis> mouse event with the <emphasis>onKeyUp</emphasis>, the <emphasis>onKeyDown</emphasis>, or the <emphasis>onKeyPress</emphasis> keyboard event.",
+ advice: [
+ {
+ type: "log",
+ category: "info",
+ text: "Actions triggered using mouse events should have corresponding keyboard events to account for keyboard-only navigation.",
+ },
+ ],
+ },
REACT_JSX_NO_DUPLICATE_PROPS: (key: string) => ({
category: "lint/react/jsxNoDuplicateProps",
message: `React does not support duplicate props. The <emphasis>${key}</emphasis> prop is duplicated.`,

add clickEventsHaveKeyEvents rule and tests (#515)