Repository navigation
fix(plugin-kanban): a focused card opens on Enter and Space, as a click does - #12888
Merged
objectstack-fleet[bot] merged 2 commits intoOct 17, 2026
Merged
Conversation
…ck does Each kanban card is a keyboard tab stop (dnd-kit's tabIndex), but its only activation was onClick, so Enter and Space on a focused card did nothing. The card is now a button inside its listitem; Enter (keydown) and Space (keyup) call the same onCardClick a pointer click calls. A key on an element inside the card opens nothing. No keyboard sensor is added. Fixes objectui#12883. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XVp19E1MYUTFM3dnkwz8gB
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
This was referenced Oct 17, 2026
The card became a button inside its own listitem wrapper, so the authored board's census grew by one <div> per card (64 to 68, 86 to 90) while the text and its hash stayed byte-identical. The prior reading is kept as PRE_REPAIR_PRE_12883, the current one is written as its own literal, and a delta case asserts that exactly one <div> per card moved and nothing else. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XVp19E1MYUTFM3dnkwz8gB
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Contributor
CI:
|
objectstack-fleet
Bot
deleted the
claude/issue-12883-kanban-card-enter
branch
October 17, 2026 14:41
This was referenced Oct 17, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #12883
Clause-②: no
What changed
A focused kanban card now opens on Enter and on Space, through the same
onCardClicka pointer click takes. OnlySortableCardinpackages/plugin-kanban/src/KanbanImpl.tsxchanges, plus theonCardClickevent type in the same file.onCardClick(card, event)with the key event. The Space press ispreventDefault-ed so it does not scroll the board.event.target !== event.currentTarget).listitemthat was also the tab stop. It is now abutton(explicit; it is also the role dnd-kit gives a draggable) wrapped in alistitem. The APG shape for an activatable item in a list is a list item that contains the control, and abuttoncannot itself be thelistitemitsrole="list"needs. The wrapper keepsaria-label= card title, so the list item stays named as before. The sortable node, the dnd-kitattributes/listeners, the transform,aria-describedbyandaria-labelstay on the focusable button.onCardClick's second parameter is nowKanbanCardActivationEvent = React.MouseEvent | React.KeyboardEvent(three spellings in this file;KanbanBoardPropsis not exported from the package entry). The published faces (ObjectKanbanSchema.onCardClick,ObjectKanbanComponentProps.onCardClick,KanbanRendererProps) already type itany.KeyboardSensor; keyboard drag stays held for the maintainer per objectui#12837.Measurements
@dnd-kit/core6.3.1 (dist/core.esm.js) onlyKeyboardSensor.activatorsbinds a key (onKeyDown);PointerSensorbindsonPointerDown,TouchSensorbindsonTouchStart. The board builds pointer and touch only, whichdndInstructions-12837.test.tsxpins, so the card'slistenerscarry no key handler. Its control "Space on a focused card starts no drag" stays green.target,metaKey,ctrlKeyandbutton:useOverlayAnchor().captureAnchor(@object-ui/componentsnavigation-overlay.tsx) readsevent.target. For a key event that is the focused card, a valid anchor.useNavigationOverlay().handleClick(@object-ui/react) readsmetaKey || ctrlKey || button === 1.handleRecordClickinObjectDataPage.tsxandInterfaceListPage.tsx(app-shell), andObjectView'sonRowClick, read the same fields.metaKey/ctrlKeyand has nobutton, so Ctrl/Cmd+Enter opens a new tab like Ctrl/Cmd+click, and a key never reads as a middle click.aria-describedbyis still on the focusable element and still resolves to the bundle sentence in en and zh. The pin'scard()helper now queries thebuttonrole, and its three assertions are unchanged and green.Pins
New:
packages/plugin-kanban/src/__tests__/cardKeyboardActivation-12883.test.tsx(real board, realDndContext, no mocks):buttoninside the namedlistiteminsiderole="list", withtabindex="0"onCardClickonce, with that card and akeydowneventa, Tab, Escape, ArrowDown) open nothingonCardClickonce, with aclickeventThe in-package pins that read the sortable node's own attributes (
aria-describedby,aria-disabled, and the pointer-drag target) now query thebutton:dndInstructions-12837,dndAnnouncements-12716andObjectKanban.moveGrant-12082. The pins that query the namedlistitemfor card content need no change, including app-shelllistKanbanEveryRecord-12619, which was run and is green.Ablation, once, after commit
0550c733e4, throughobjectstack/scripts/ablation-replace.mjs:onKeyDown={(e) => {was renamed to a data attribute: anchor x1 to x0, blobe5e8881574f3to217dbcc095b9.e5e8881574f3equals the HEAD blob, andgit diff HEADis empty. These tests import../KanbanImplfrom source, so no dist is involved.Gates (at
0550c733e4)pnpm --filter '@object-ui/plugin-kanban^...' buildexit 0.pnpm --filter @object-ui/plugin-kanban type-checkexit 0 (tsconfig.test.jsonlists the new test file).pnpm exec vitest run --maxWorkers=1 packages/plugin-kanban/: Test Files 75 passed (75), Tests 506 passed (506).packages/app-shell/src/__tests__/listKanbanEveryRecord-12619.test.tsx: 2 passed.ESLint narrowed to the 5 changed TS files, exit 0: 0 errors, 1 warning, which is the existing
rules as anyingetCardStylesand not on a changed line. Population iseslint.config.jsfiles: ['**/*.{ts,tsx}']. The config has noprojectService/parserOptions.project, so it does no type-aware linting and this diff cannot move the verdict on any untouched file.Pre-tsc battery, all exit 0:
check:phantom-deps,self-import,unreferenced-sources,doc-example-readers,handler-key-reads,side-effects-array,element-data-source-declaration,spec-symbols,action-forward-parity,designer-field-key-parity,icon-record-names,i18n-keys,i18n-drift,i18n-designer-parity,control-bytes, pluscheck-hand-rolled-comment-mask.mjs,check-changeset-fixed.mjs,check-changeset-presence.mjsandcheck:new-line-citations.NOT MEASURED: a real-browser run on the showcase board, because this order's gate list does not ask for one. The pins drive real DOM events on the real board.
Patch round
2da6733da1: theexamples/schema-catalogkanban census (kanban-column-cards-6939) gains one wrapperdivper card (elements 64 → 68, 86 → 90;DIV+4 each, every other tag unchanged), with visible text and its sha256 unchanged. The pre-12883 reading is kept asPRE_REPAIR_PRE_12883and a delta case pins the difference; 17/17 green. (Added by the seat.)Acceptance notes
buttonare presentational in ARIA. A field cell that renders a link (url or email) sits inside the card's button, as it already sat inside a focusable element before. Keys on it are excluded from the card's open. This is the cost of the button role the order rules for Space activation; alinkrole would keep children semantic but has no Space activation.aria-roledescription="sortable"is unchanged, so a screen reader says "sortable" where it would say "button". The role is still exposed and Enter / Space work. Changing that text is a separate wording decision. Carrier: none.ObjectKanban.tsxis now stale. It is on theonCardClickprop and says "KanbanImpltypes the same channel asReact.MouseEvent". That file is outside this order's file surface, so it is unchanged. Carrier: none.Seat
domain:ui#2, sessionhttps://claude.ai/code/session_01XVp19E1MYUTFM3dnkwz8gB.Generated by Claude Code