Skip to content

plugin-grid: order a grouped grid's groups by the level's orderBy (spec grouping.fields[].orderBy), the label still from field #25858

Open

Description

@objectstack-fleet

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?: string names 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.
  • compileListViewGroupQuery now adds one aggregation node per ordered level, min over orderBy for order: 'asc' and max for 'desc'. The node is aliased min_FIELD / max_FIELD. The grouping key stays the only groupBy column, 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 null sorts last in both directions;
    • equal order values fall back to the group key, compared in the same direction.
  • The header set is sorted whole. The header query is compiled unpaged, and a limit on it would be sliced by the server before any sort.

What this card does

  1. Server-grouped grid. When a level has an orderBy that differs from its field, sort that level's header rows by the column listViewGroupOrderColumn names. The direction is order, nulls go last, and ties fall back to the existing key comparison in the same direction. The group label is still rendered from the field value (one header per period_label), resolved as today.
  2. Client-side grouping path (useGroupedData over fetched rows, where it is still live). Apply the same rule from the rows: each group's order value is the min (asc) or max (desc) of orderBy over its rows. Each group then sits where its first row would sit if the rows were sorted by orderBy.
  3. Precedence. A level with orderBy orders 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.
  4. Type mirror. If objectui mirrors GroupingField, it gains orderBy?: string.

Acceptance

  • Take a list grouped by period_label with orderBy: '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 no period_start). It renders one header per label, in descending period_start order, with the undated group last.
  • A test feeds header rows shaped as compileListViewGroupQuery compiles them and asserts the order. A control with no orderBy asserts today's label or option order unchanged.
  • ⛔ Not this card: HotCRM's view edit. Its follow-up is to drop the outer period_start level in forecast.view.ts once this lands.

Activity

  1. objectstack-fleet commented on Oct 25, 2026

    @objectstack-fleet
    ContributorAuthor

    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.

    Blocked-by: #25786


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 25, 2026

    @objectstack-fleet
    ContributorAuthor
  3. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    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.ts is now ui/packages/plugin-grid/src/useGroupedData.ts; packages/plugin-grid/src/useServerGrouping.ts is now ui/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:dispatched or assignee to strip.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Unlock scan: unlocked → pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-26T01:56Z

    ⛔ Not a claim, ⛔ not a dispatch.


    Generated by Claude Code

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

    area:recordsBusiness objects, records, the views that show data, usable forms, searchdomain:uienhancementNew feature or requestpm:queuepriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions