Skip to content

bug(plugin-kanban): an onCardClick supplied to object-kanban runs TWICE per card click — ObjectKanban passes it to useNavigationOverlay AND calls it #9341

Description

@os-tesla

Found while measuring objectui#7804's plugin-kanban slice (PR objectui#9338). ⛔ Not fixed there — it is a behaviour defect in ObjectKanban's click wiring, and that PR changes no renderer.

The defect

An onCardClick supplied to an object-kanban board runs twice for one card click.

ObjectKanban hands the same function to useNavigationOverlay as its onRowClick, and then calls it again itself:

const externalClick = onRowClick ?? onCardClick;
const navigation = useNavigationOverlay({ navigation: navConfig, objectName: schema.objectName, onRowClick: externalClick });
...
onCardClick: (card, event) => {
  navigation.handleClick(card, event);
  onCardClick?.(card);
},

useNavigationOverlay's handleClick gives onRowClick full priority — it calls it and returns — so when a host supplies onCardClick and no onRowClick, that one function is the value of externalClick AND the one the wrapper calls on the next line. One click, two calls.

Measured

Driven through the real SchemaRenderer on the production path (packages/plugin-kanban/src/__tests__/handlerKeyDispositionsMeasured-7804.test.tsx, the leg whose subject is the sibling key): a document { type: 'object-kanban', columns: [...], onCardClick: spy } rendered, then the handler the board implementation was handed invoked once. The spy's call count came back 2, where the leg had predicted 1. That reading is what made the control fail; the control was then relaxed to "it RAN", deliberately, so that fixing this card does not redden a file that is not about it.

Firing control on the same run: the sibling onCardMove spy came back 0 on the same document and the same render, so the 2 is a reading about this key and not a recorder that counts everything twice.

Why it matters

The two call sites are not interchangeable. handleClick forwards the modifier event (onRowClick(record, event)) so a host can implement Cmd/Ctrl/middle-click; the wrapper's second call passes the record only. So a host handler that navigates fires once with modifiers and once without — a duplicate navigation, a duplicate analytics event, or a double-open, depending on what it does.

⚠️ Not reached when the board is embedded in an ObjectView: that parent supplies onRowClick, so externalClick resolves to the parent's function and the author's onCardClick is called exactly once by the wrapper. The defect is specific to a host that supplies onCardClick alone.

Not the same as its neighbours

  • objectui#8285 — quickAdd is inert on object-kanban because ObjectKanban supplies no onQuickAdd. Different key, opposite shape (nothing fires, rather than one thing firing twice).
  • objectui#7804 — whether the arm DECLARES these keys. This one is about how many times a declared, reaching one runs.

What is not decided here

Which of the two calls should go. Dropping the wrapper's onCardClick?.(card) keeps the modifier event and makes the board behave like the grid; dropping externalClick's onCardClick arm keeps the wrapper's plain-record signature but changes which handler wins when a parent also supplies onRowClick. They are not equivalent for a host, so this is a ruling rather than a mechanical edit.

Reported by the os-dev seat implementing the objectui#7804 plugin-kanban slice, with Claude Code — session session_01UzHd6hDYatoDn17BuwKxnZ.


Generated by 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 seatpluginplugin: kanbanpriority:p2

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions