Repository navigation
fix(plugin-kanban,plugin-list): a list view's kanban draws every record of the view, a page per lane, and cards carry the view's first columns (objectui#12619) - #12643
Conversation
…s carry the view's first columns The board under a list view drew the list's window (one grid page) bucketed into lanes. ListView now hands the board the view's query instead of rows (`lanePaging` host prop): ObjectKanban asks each lane for its first 50 records with the lane's count, plus one query for records in no lane, with a "Load more" at each lane's foot. Cards take the view's next two columns past the title, lane and owner, by name, and render label and value; the owner's avatar comes from the spec-declared owner field. Claude-Session: https://claude.ai/code/session_01Vk15bz8xHtKGUfXfCm16h4 Co-authored-by: Claude <noreply@anthropic.com>
…t handed it Since objectui#12619 a board whose lanes are known pages them itself and is handed no rows. The two query-count pins assert that instead; the row-cap banner control uses a board that keeps the window. Claude-Session: https://claude.ai/code/session_01Vk15bz8xHtKGUfXfCm16h4 Co-authored-by: Claude <noreply@anthropic.com>
…r avatar; changeset Claude-Session: https://claude.ai/code/session_01Vk15bz8xHtKGUfXfCm16h4 Co-authored-by: Claude <noreply@anthropic.com>
✅ 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
|
…wner to place The row that places the owner's avatar beside the title was drawn on every card, one element more per card on a board with no owners, which moved the schema-catalog render pin (kanban-column-cards-6939). A card with no owner draws its title exactly as before. Claude-Session: https://claude.ai/code/session_01Vk15bz8xHtKGUfXfCm16h4 Co-authored-by: Claude <noreply@anthropic.com>
✅ 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
|
Contract reviewServed-tier: The face: the ① Derived judgments
② Semver levelThe changeset declares ③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
…t; changeset says what the old card drew `sm:p-4` is emitted after the header's `pb-2` and the content's `pt-0`, so from `sm` up both sides padded back to 16px: a blank band between the title and the first field line on every card with field lines, now every list-view card. Both sides are restated at `sm:`, pinned as a rule over the rendered classes. The changeset no longer says the old card drew nothing below its title: it drew a derived subtitle where the record yields one, and on HotCRM's measured board the name alone. Claude-Session: https://claude.ai/code/session_01Vk15bz8xHtKGUfXfCm16h4 Co-authored-by: Claude <noreply@anthropic.com>
✅ 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
|
Contract reviewServed-tier: The face: the ① Derived judgments
② Semver levelThe changeset declares ③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
席位核对 — ACCEPT ·
|
Fixes #12619
Clause-②: yes (corrected by the seat from the claim's provisional
no, on the measurement below; corrected claim6211796112on objectui#12619)Clause-② measurement, for the seat to correct the line above. The board's paging needs two new optional exported props. Built
packages/plugin-kanban/dist/ObjectKanban.d.tsdeclareslanePaging?: KanbanLanePagingRequestandownerField?: stringonObjectKanbanComponentProps, and importsKanbanLanePagingRequestfrom./KanbanLanePaging.js, so that interface joins the published declaration closure through the props type (it is not re-exported by name from the barrel). No@object-ui/typesmember is added. The authored accept set does not change: no key is added to theobject-kanbannode or to the view's kanban block.What changed
@object-ui/plugin-kanban, newKanbanLanePaging.ts).ObjectKanbantakes a host prop,lanePaging({ search?, searchFields?, reloadKey? }). With it, the board asks each lane for its first 50 records: the node'sfilterAND[laneField, '=', laneId], the node'ssort,$count: true,$top: 50. The lane's header count is the answer'stotal. A lane holding more shows Load more at its foot ($skip= what the lane holds). One more query,or(nin lane ids, is_null), fills the trailing Uncategorized lane. A refresh (hostreloadKey, the board's ownonMutationor invalidation) asks each lane again at the depth it was loaded to. Counts follow an optimistic drag or delete until the next read. Lanes are paged only when they are known before the query (declared lanes, or the lane field's options) and the board draws no swimlanes. A board without the prop, the standaloneobject-kanbanincluded, keeps its one query capped bylimit.ListViewhands the board the query, not a page (@object-ui/plugin-list, kanban branch and the props it hands the board). NewkanbanLanesPaged: the lane field's options are known, no swimlane grouping, and this view fetches for itself. Then the board is handed nodata. Its node carries the effective filter (selfQueryFilter, as gantt, tree, chart and the server-grouped grid get it). It is handedlanePagingwith the toolbar term,searchableFieldsandrefreshKeyplusrefreshTrigger. The view's own window is still fetched for the empty state, the export and the filter option counts, as for the server-grouped grid. On such a board the footer shows the view's record count from that query'stotal, without the "Showing first N records" line or the Rows per page picker.kanban.columns, the board'scardFieldsare the view's next two columns past the card title (titleField,resolveNameField), the lane field and the owner field, read by name throughcolumnIdentity. Before, the view's columns went out whole, as{ field }entries that the card reads as field names, so a card drew no field lines; below its name it showed at most a subtitle the board derives from the record, and on HotCRM's measured board the name alone. An authoredkanban.columnsis the author's list and is used whole.cardFieldsstays the generated node's own spec key (ObjectKanbanPropsSchema: "Fields rendered on each card"). Nothing reads or writescardFieldsat the view level.KanbanImpl): a muted label, then the value. The label used to be ansr-onlyterm plus a tooltip. The card header restates its reduced foot (sm:pb-2) and the card content its reduced top (sm:pt-0):sm:p-4is emitted afterpb-2/pt-0, so fromsmup both padded back to 16px, a blank band between the title and the first field line on every card with field lines.ListViewnames the owner field from the spec's declaration, never a guessed name:resolveInjectedSystemColumns(objectDef).ownerandSystemFieldName.OWNER_ID, and the object definition must declare the field. The board draws the expanded owner's initials or picture, gated on field-level security for the owner field and filtered withwithoutDeniedFieldsfor the person (newKanbanCardOwner.ts). A bare id draws nothing.kanban.loadMorein all ten language packs; docs incontent/docs/plugins/plugin-kanban.mdx; one changeset.Mechanism hypotheses, measured
79c1996. The acceptance test below readexpected [ …(25) ] to have a length of 45 but got 25. The kanban branch is notpaginate, soeffectivePageSizeresolves the view's declaredpagination.pageSize(25). With no declared size it is the fetch batch, 100. That window went down asdata. The name-only cards are read in source (column entries as{ field }objects), not separately measured on base: the base run failed at the card count first.findreturnsQueryResult.total("Total number of records (if requested)"), andQueryParams.$countis the declared request, which the lane queries send.queryGroupHeadersalso counts, but it is optional, and onefindper lane gives both the rows and the count. A gap, not filed:@object-ui/data-objectstack'snormalizeQueryResultanswerstotal ?? count ?? records.length, so a server that sends no total would read as "total = page". ObjectStack'sfindDatasends a total.@objectstack/spec@18.0.0-next.1.KanbanConfigSchemais astrictObjectofgroupByField,summarizeField,titleField,columns("Fields to show on cards"); it has nocardFields.Seam:finding.update(OBJECT, 'open-000', { stage: 'won' }), with both lane counts following the card.ownership,resolveInjectedSystemColumns,SystemFieldName.OWNER_ID), so the avatar is wired. An object withownership: 'org'gets no owner field (pinned).Tests (at
73cab08, the merged tree)79c1996:packages/app-shell/src/__tests__/listKanbanEveryRecord-12619.test.tsxfailed 2 of 2 (25 of 45 cards).pnpm exec vitest run --maxWorkers=2 packages/plugin-kanban/ packages/plugin-list/plus five app-shell kanban files (the new acceptance test,displayPageSizeFromSpec-9853,listCalendarDateWindow-12081,ObjectView.kanbanSummarizeField-11629,ObjectView.kanbanLane-8193) gaveTest Files 214 passed (214)andTests 1975 passed (1975).ObjectKanban.lanePaging-12619.test.tsx(10 cases: per-lane counts past a window, the query shape, Load more, Uncategorized, search and refresh depth, two one-query controls, drag, owner avatar and its controls) andListView.kanbanOwnsLanes-12619.test.tsx(9 cases: data withheld, effective filter, footer count, card fields by name, the authored override, owner field and itsownership: 'org'control, and two window-keeping controls). The acceptance test asserts 45 cards across seven lanes with page size 25, and a card that shows Account and Amount with labels but not the third column.ListView.viewSwitchRefetch-7394§1 now asserts that the board got no rows plus the lane request; §5's banner control uses a board with no lane options.ListView.calendarDateWindow-12081's board control waits on the lane request. Each pin still counts the list's own query as before.pnpm --filter @object-ui/plugin-kanban type-checkandpnpm --filter @object-ui/plugin-list type-check: exit 0, after building the dependency closure.28ce461, merged asac87f7b): the title row that places the owner avatar is drawn only when the card has an owner, so a board with no owners keeps its markup.kanban-column-cards-6939is green against its unchangedPRE_REPAIRreadings (64 and 86 elements); it read 68 and 90 on73cab08.Test (shard 1/16)is green onac87f7b.62cc21c, merged as53c6200):cardPaddingEveryBreakpoint-12619pins the padding rule over the rendered classes;kanban-column-cards-6939green with its readings unchanged; vitest 76 files / 1116 tests green;pnpm --filter @object-ui/plugin-kanban type-check0.Gates (local subset; the rest is CI's)
node scripts/check-changeset-presence.mjs0 ·pnpm check:new-line-citations0 (0 new) ·check:i18n-keys/check:i18n-drift/check:i18n-dead-keys0 · eslint on the changed files: 0 errors ·check:control-bytes,check:phantom-deps,check:vi-mock-specifiers,check:vi-mock-inherit,check:vi-mock-override-shape,check:test-path-roots,check:handler-key-reads,check:unreferenced-sources,check:changeset-claims,check:pending-changeset-literals,check:doc-fences,check:unused-deps0. NOT MEASURED:check:readme-exports(population collapsed: unbuilt packages) andcheck:doc-snippets(exit 2, precondition not met);@object-ui/app-shelltype-check (closure unbuilt). CI runs all three.Screenshot
NOT MEASURED: screenshot, reason: this container has no backend to serve the board. HotCRM is not checked out, and the sibling objectstack checkout is uninstalled and unbuilt. The seat runs the 1280 px HotCRM board check at review.
Acceptance notes
Outside the named file surface:
content/docs/plugins/plugin-kanban.mdx(AGENTS.md Add automated testing infrastructure and CI/CD workflows #2, docs-driven) and the cross-package acceptance test inpackages/app-shell/src/__tests__/. That package is the one that depends on both halves (the reasonlistCalendarDateWindow-12081lives there).kanbanViewOptionsin app-shell is untouched.Design boundary: swimlane boards and boards whose lane field declares no options keep the list's window and its "Showing first N" banner. The standalone
object-kanbankeepslimit. Lane paging is a host request on purpose:limitis declared as the board's row cap.On a lane-paged board, the list still issues its own window query, which feeds the empty state, the export, the filter counts and the footer total. That is one extra query, the same arrangement as the server-grouped grid.
Lane queries match the stored lane value exactly. A record whose value differs only by case comes back from the in-no-lane query, and the client's case-folded bucketing puts it in its lane. Header counts still equal the cards drawn.
The label on each card field reverses a code comment that kept values only. The card asked for "label: value" lines.
Overlap with
ListView.tsxwork in flight today: PR fix(plugin-list): Display menu closes after a layout is chosen (objectui#12621) #12632 was already in this branch's base, and PR fix(plugin-grid,components,types): a grouped grid draws one header row, with group bands between the rows (objectui#12620) #12633 changed no file here.origin/main(a8560b2) was merged once with no conflict, and the gates above ran on the merged tree.Adds no product-specific widget, dialog, hook or string (see AGENTS.md › Product-neutral renderer)
Generated by Claude Code