Skip to content

finding(react): UseNavigationOverlayOptions.onRowClick declares ONE parameter and handleClick casts it away to call it with TWO — every consumer copies the understated spelling #9357

Description

@os-tesla

Found while implementing objectui#9341 (PR objectui#9356), which repairs exactly one leaf of this tree. ⛔ Not fixed there — widening the other declarations would be a second, unrequested move on published surfaces, and that PR already carries needs:contract-review for the one the ruling named.

The contract, and the line that contradicts it

packages/react/src/hooks/useNavigationOverlay.ts declares the option with one parameter:

export interface UseNavigationOverlayOptions {
  /** External onRowClick callback — if set, takes full priority */
  onRowClick?: (record: Record<string, unknown>) => void;
}

Its own handleClick then casts the declaration away to call it with two:

if (onRowClick) {
  (onRowClick as (r: Record<string, unknown>, e?: HandleClickModifiers) => void)(record, event);
  return;
}

The sibling member on the same interface file states what the second argument is for, so the intent is not in doubt:

handleClick: (record: Record<string, unknown>, event?: HandleClickModifiers) => void;

and the comment above the cast says why it matters — "Forward the modifier event so parent handlers (e.g. ObjectView) can still implement Cmd/Ctrl/middle-click → open in new tab."

So the declared contract says the callback receives a record; the implementation guarantees it receives a record and the modifier payload. The as is what makes the disagreement compile, and it is the only thing holding the two apart.

Why it is worth a card rather than a shrug

The understated spelling is copied outward. Each of these declares its own pass-through of the same channel with one parameter, while the value that flows through it is invoked with two:

  • packages/plugin-kanban/src/ObjectKanban.tsxObjectKanbanComponentProps.onRowClick?: (record: any) => void
  • packages/plugin-list/src/ObjectGallery.tsx — both onCardClick and onRowClick, feeding the identical onRowClick: props.onRowClick ?? props.onCardClick
  • packages/plugin-kanban/src/index.tsxKanbanRendererProps.schema.onCardClick?: (card: any) => void

The consequence is not a crash; it is that the modifier event is invisible on the declaration a host reads. A host writing a Cmd/Ctrl/middle-click handler has to discover the second argument from the implementation, and must then spell its own parameter optional ((record: any, event?: any)) to stay assignable — which is exactly what ObjectView.tsx does at three call sites (:2734, :3120, :3163). That workaround is the tell: the repo already knows the second argument is there, in the one place that could not avoid knowing.

objectui#9341 is the same class reaching a user: the board's published onCardClick declared one argument while the channel delivered two, and the one-argument declaration turned out to describe a defective second call rather than the real channel.

What is not decided here

Whether to widen the hook's option and let the consumers follow, or to name the payload type on each consumer. HandleClickModifiers is exported from @object-ui/react, so consumers inside that dependency direction can name it — but @object-ui/types, where ObjectKanbanSchema.onCardClick lives, cannot: it is named in no dependency field of @object-ui/types, and @object-ui/react depends on @object-ui/types, so the import is a phantom dependency and a cycle. objectui#9341 resolved that one face as event?: any, matching BaseSchema's own onClick / onChange / onSubmit. Whether the whole family converges on that spelling or on the named interface where it is reachable is a ruling, not a mechanical edit.

⚠️ Any fix here moves several published declarations at once and should carry needs:contract-review.

Reported by the os-dev seat implementing objectui#9341, with Claude Code.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpackage: reactpriority:p2

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions