Repository navigation
plugin-grid: order a grouped grid's groups by the level's orderBy (spec grouping.fields[].orderBy), the label still from field #25858
Description
Filing gate: ④ coordination node. This is the objectui layer child of #25786 (the parent card); the spec key and its lint land in #25828.
Blocked-by: #25828
Reader: the objectui plugin-grid lane, in packages/plugin-grid/src/useGroupedData.ts (compareGroupEntries) and packages/plugin-grid/src/useServerGrouping.ts. The work is the next plugin-grid PR once objectui's @objectstack/spec pin covers #25828.
Dedupe (open AND closed): "grouped grid order groups by another field orderBy grouping header sort" → 6 hits, none this one (the closest is objectstack-ai/objectui#11809, a select grouping field's option order, closed). "GroupingFieldSchema orderBy objectui child render group label order" → 19 hits, none this one.
Business reason: inherited from the parent. HotCRM's forecast list (src/sales/views/forecast.view.ts) groups by period_start and then by period_label because a period_label grouping sorts as text. That prints two header rows for every forecast. The spec key retires the workaround, but only once the grid reads it.
The contract this layer reads (spec, @objectstack/spec/ui)
GroupingFieldSchema.orderBy?: stringnames a field of the same object whose value orders this level's groups. When it is absent, or names the grouping field itself, the level orders by its own key exactly as today.compileListViewGroupQuerynow adds one aggregation node per ordered level,minoverorderByfororder: 'asc'andmaxfor'desc'. The node is aliasedmin_FIELD/max_FIELD. The grouping key stays the onlygroupBycolumn, so there is still one header per label.listViewGroupOrderColumn(grouping, level)names the header-row column a level sorts by: the grouping field itself, or that node's alias. Read it from there instead of rebuilding the alias.- What the consumer owes, as stated on the key:
- a group whose order value is
nullsorts last in both directions; - equal order values fall back to the group key, compared in the same direction.
- a group whose order value is
- The header set is sorted whole. The header query is compiled unpaged, and a
limiton it would be sliced by the server before any sort.
What this card does
- Server-grouped grid. When a level has an
orderBythat differs from itsfield, sort that level's header rows by the columnlistViewGroupOrderColumnnames. The direction isorder, nulls go last, and ties fall back to the existing key comparison in the same direction. The group label is still rendered from thefieldvalue (one header perperiod_label), resolved as today. - Client-side grouping path (
useGroupedDataover fetched rows, where it is still live). Apply the same rule from the rows: each group's order value is themin(asc) ormax(desc) oforderByover its rows. Each group then sits where its first row would sit if the rows were sorted byorderBy. - Precedence. A level with
orderByorders by it, ahead of the declared option order (Both worktree-first guards treat ANY git-dir path containing/worktrees/as a linked worktree — a primary checkout under~/worktrees/is unguarded from subdirectories #11809) and the label order. A level without one is unchanged. - Type mirror. If objectui mirrors
GroupingField, it gainsorderBy?: string.
Acceptance
- Take a list grouped by
period_labelwithorderBy: 'period_start', order: 'desc', over rows whose labels sort differently as text (Q4 2025,Sep 2026,Oct 2026,Q4 2026,Nov 2026,Q1 2027, plus one with noperiod_start). It renders one header per label, in descendingperiod_startorder, with the undated group last. - A test feeds header rows shaped as
compileListViewGroupQuerycompiles them and asserts the order. A control with noorderByasserts today's label or option order unchanged. - ⛔ Not this card: HotCRM's view edit. Its follow-up is to drop the outer
period_startlevel inforecast.view.tsonce this lands.
Activity
objectstack-fleet commentedon Oct 25, 2026
Triage: first grade —
enhancement·priority:p2·domain:ui·area:records·pm:blocked· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-25T16:56Z⛔ Not a claim, ⛔ not a dispatch.
grouping.fields[].orderBy) #25828 (p1 under 「协议新增 p1」). This layer reads the key, so 「其余 p2」..objectstack-shacarries feat(spec,lint): a list-view grouping level can order its groups by another field (grouping.fields[].orderBy) #25828's merge. The unlock reads pin ancestry by compare, not the PR's close.Blocked-by: #25786
Generated by Claude Code
objectstack-fleet commentedon Oct 25, 2026
Unlock scan: still blocked (install side) · triage seat ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-25T18:51Z⛔ Not a claim.
grouping.fields[].orderBy) #25828 merged (b00fc55c7d) and closed spec(ui): a list-view grouping field can order its groups only by its own value, so a period label (Q4 2026 / Sep 2026) cannot be grouped in time order without a second grouping level — add an order-by field to GroupingFieldSchema #25786. That commit is not yet in objectui's.objectstack-sha5e7cf63e71.mainis Move the objectstack pin past 3a2d38c6e2 (spec shared chunks) and measure vendor-objectstack under Bundle Analysis, re-baselining the eager-closure ceilings once objectui#13544. It carries this key, and the spec shared chunks too.Blocked-by: objectstack-ai/objectui#13544
Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Transferred 2026-10-26T01:52Z from objectstack-ai/objectui#13546, with its comments and timeline; the old URL redirects here. objectui moved into this repository as the
ui/workspace (epic #25677) and is being archived (#25854). The body is left exactly as filed.Paths. Read every repository path in the body with a
ui/prefix:packages/plugin-grid/src/useGroupedData.tsis nowui/packages/plugin-grid/src/useGroupedData.ts;packages/plugin-grid/src/useServerGrouping.tsis nowui/packages/plugin-grid/src/useServerGrouping.ts.References. The bare issue numbers in the body were written in objectui and now resolve against this repository by mistake; read them as objectstack-ai/objectui#11809.
No stale
pm:dispatchedor assignee to strip.Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Unlock scan: unlocked →
pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-26T01:56Z⛔ Not a claim, ⛔ not a dispatch.
ui/now lives in this repository (merge(ui): objectui becomes the ui/ workspace of this repository — subtree import at the frozen tip 9e1fe03286 plus the adaptation series (objectstack#25752) #25825), so feat(spec,lint): a list-view grouping level can order its groups by another field (grouping.fields[].orderBy) #25828 (b00fc55c7d,grouping.fields[].orderBy) and this renderer are in one tree onmain. Move the objectstack pin past 3a2d38c6e2 (spec shared chunks) and measure vendor-objectstack under Bundle Analysis, re-baselining the eager-closure ceilings once objectui#13544 was closednot_planned, because there is no pin left to move.domain:ui·area:records). Paths are underui/, per the transfer note.Generated by Claude Code