fix(webapp): enforce keyboard interaction safeguards (#4702)
## Summary Enable keyboard-event and static-element interaction safeguards across the dashboard. Earlier stack changes move actionable behavior to native controls. This final enforcement keeps narrowly documented exceptions for focus forwarding, scoped Escape handling, CodeMirror focus, and pointer-driven table column resizing. `jsx-a11y/no-autofocus` remains disabled. Base: [#4701](https://github.com/triggerdotdev/trigger.dev/pull/4701)
This commit is contained in:
+2
-2
@@ -76,7 +76,7 @@
|
||||
"react/no-unknown-property": "error",
|
||||
"jsx-a11y/alt-text": "error",
|
||||
"jsx-a11y/aria-role": "error",
|
||||
"jsx-a11y/click-events-have-key-events": "off",
|
||||
"jsx-a11y/click-events-have-key-events": "error",
|
||||
"jsx-a11y/control-has-associated-label": [
|
||||
"error",
|
||||
{
|
||||
@@ -87,7 +87,7 @@
|
||||
"jsx-a11y/label-has-associated-control": "error",
|
||||
"jsx-a11y/no-autofocus": "off",
|
||||
"jsx-a11y/no-noninteractive-element-interactions": "error",
|
||||
"jsx-a11y/no-static-element-interactions": "off",
|
||||
"jsx-a11y/no-static-element-interactions": "error",
|
||||
"jsx-a11y/prefer-tag-over-role": "off",
|
||||
"jsx-a11y/anchor-ambiguous-text": "error",
|
||||
"jsx-a11y/anchor-has-content": "error",
|
||||
|
||||
@@ -264,6 +264,7 @@ export function TSQLEditor(opts: TSQLEditorProps) {
|
||||
|
||||
const showButtons = showClearButton || showCopyButton || showFormatButton || additionalActions;
|
||||
|
||||
/* oxlint-disable jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions -- The CodeMirror mount forwards pointer focus to CodeMirror's own keyboard-accessible editor. */
|
||||
return (
|
||||
<div
|
||||
className={cn("relative flex h-full flex-col", opts.className)}
|
||||
@@ -337,6 +338,7 @@ export function TSQLEditor(opts: TSQLEditorProps) {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
/* oxlint-enable jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */
|
||||
|
||||
// SQL keywords that legitimately appear before parentheses with a space
|
||||
const SQL_KEYWORDS_BEFORE_PAREN = new Set([
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
type ColumnFiltersState,
|
||||
type ColumnResizeMode,
|
||||
type FilterFn,
|
||||
type Header,
|
||||
type SortDirection,
|
||||
type SortingState,
|
||||
} from "@tanstack/react-table";
|
||||
@@ -1045,6 +1046,24 @@ function FilterCell({
|
||||
);
|
||||
}
|
||||
|
||||
/* oxlint-disable jsx-a11y/no-static-element-interactions -- Column resizing is a pointer-drag interaction provided by TanStack Table. */
|
||||
function ColumnResizeHandle({ header }: { header: Header<RowData, unknown> }) {
|
||||
return (
|
||||
<div
|
||||
onDoubleClick={() => header.column.resetSize()}
|
||||
onMouseDown={header.getResizeHandler()}
|
||||
onTouchStart={header.getResizeHandler()}
|
||||
className={cn(
|
||||
"absolute right-0 top-0 h-full w-0.5 cursor-col-resize touch-none select-none",
|
||||
"opacity-0 group-hover/header:opacity-100",
|
||||
"bg-surface-control hover:bg-indigo-500",
|
||||
header.column.getIsResizing() && "bg-indigo-500 opacity-100"
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
/* oxlint-enable jsx-a11y/no-static-element-interactions */
|
||||
|
||||
export const TSQLResultsTable = memo(function TSQLResultsTable({
|
||||
rows,
|
||||
columns,
|
||||
@@ -1237,18 +1256,7 @@ export const TSQLResultsTable = memo(function TSQLResultsTable({
|
||||
>
|
||||
{flexRender(header.column.columnDef.header, header.getContext())}
|
||||
</HeaderCellContent>
|
||||
{/* Column resizer */}
|
||||
<div
|
||||
onDoubleClick={() => header.column.resetSize()}
|
||||
onMouseDown={header.getResizeHandler()}
|
||||
onTouchStart={header.getResizeHandler()}
|
||||
className={cn(
|
||||
"absolute right-0 top-0 h-full w-0.5 cursor-col-resize touch-none select-none",
|
||||
"opacity-0 group-hover/header:opacity-100",
|
||||
"bg-surface-control hover:bg-indigo-500",
|
||||
header.column.getIsResizing() && "bg-indigo-500 opacity-100"
|
||||
)}
|
||||
/>
|
||||
<ColumnResizeHandle header={header} />
|
||||
</th>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -581,6 +581,7 @@ export function DashboardAgentPanel({
|
||||
// Not filtered to active: the wake banner needs watches that already fired.
|
||||
const chatWatches = activeChat?.watches ?? [];
|
||||
|
||||
/* oxlint-disable jsx-a11y/no-static-element-interactions -- Escape handling intentionally bubbles from focused controls inside the panel. */
|
||||
return (
|
||||
<div
|
||||
ref={panelRef}
|
||||
@@ -667,3 +668,4 @@ export function DashboardAgentPanel({
|
||||
</div>
|
||||
);
|
||||
}
|
||||
/* oxlint-enable jsx-a11y/no-static-element-interactions */
|
||||
|
||||
@@ -95,6 +95,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
||||
const inputClassName = variants[variant].input;
|
||||
const variantIconClassName = variants[variant].iconSize;
|
||||
|
||||
/* oxlint-disable jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions -- The wrapper only forwards pointer focus to its nested input. */
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
@@ -125,6 +126,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
||||
);
|
||||
}
|
||||
);
|
||||
/* oxlint-enable jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */
|
||||
Input.displayName = "Input";
|
||||
|
||||
export { Input };
|
||||
|
||||
Reference in New Issue
Block a user