fix(detail): a related list has one sorting semantics instead of two (#3106) - #3113
Merged
Conversation
…t, not the page you can see (#3106) Clicking a column header under server pagination sorted the CURRENT PAGE. The user saw "sorted by this column" and got "these fifty rows are in order; page 2 starts over". The sort was real — its scope was not the one the screen implied — and it had no way out of `data-table` at all: it lived in two `useState`s with no callback, so the layer that issues the request could not see it even in principle. DataTable gains `manualSorting` + a controlled `sort` + `onSortChange`. In that mode it sorts nothing, reports what a header click asks for, and renders `sort` as the indicator — holding NO sort state of its own, because a private copy beside a controlled prop is the shape the defect had. `manualSorting` is independent of `manualPagination`: what matters is whether `data` is a window, not who owns the pager. ObjectGrid turns that into a `$orderby` in both of its server modes (its own fetch and a parent-driven one) and returns to page 1, since a new ordering makes the old page index a different set of rows. ListView lands it in `currentSort` — the same state the toolbar's sort builder writes. One sort with two controls onto it is what makes "does a header sort outrank the saved view's sort?" a non-question rather than a precedence rule someone has to remember. Three decisions worth naming: - A header click REPLACES the order rather than appending, so the column under the cursor is the one the list is sorted by. Multi-key orders still come from the sort builder, and the headers render them numbered. - It cannot ask for "no sort". The third click clears in client mode, which is meaningful there (rows return to the order they arrived in); across a server-paged collection there is no such order (objectstack#4363), so a header offering it would hand the user a worse lie than the one being fixed. - Relational columns render no sort affordance under server sorting: a `lookup` column shows a related record's name while `$orderby` can only order by the stored id (objectstack#4256) — the same reason #3096 removed them from the toolbar's picker. Client-side sorting keys off the rendered label, so those headers stay live there. The header context menu's Sort Ascending/Descending items now route through the same write path; they wrote internal state directly, which under `manualSorting` would have been a menu item that highlights, closes, and changes nothing. Client-side tables are untouched: same three-state cycle, same local sort. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…3106) A related list carried two. Its own sort-button row (opt-in via `sortable`) went out as a server `$orderby` over the whole child collection; the `data-table` it embeds took `sortable`'s default of `true` and sorted the rows it was holding — which, in windowed mode, is ONE PAGE. Turning `sortable` on put both in the same card with nothing saying they meant different things. Leaving it off — the default — was worse: the page-local sort was then the only one the user could reach, and it looked exactly like the list being sorted. The embedded table's column headers now drive this list's sort in both modes: - Windowed: the header sort becomes the server `$orderby` and resets to page one, the same path the buttons took. - Client mode: this list keeps sorting in memory, where the key is the label resolved through its own id → name map (#3096) — a key the table cannot see, so its sort was the worse of the two even where both were possible. The button row survives only where there are no headers to click: a `list` (`data-list`) related list, or a caller-supplied `schema` whose contents we cannot assume. `sortable`'s doc comment now says that is what it controls. Relational columns keep #3096's rule, moved to the header: withheld while the sort is a server `$orderby` (the key would be the stored foreign-key id while the cell shows a name), live in client mode where the key is the label. Three existing suites asserted through the sort buttons — two on #3096's own guarantee, one using them as a proxy for column order. They now read the column's `sortable` flag and drive the table's `onSortChange`, so the guarantees they encode survive the control that carried them. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
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.
What was wrong
A related list carried two sorting semantics at once:
$orderbyover the whole child collectionsortable, default false)data-table's headersTurning
sortableon put both in the same card, with nothing saying they meant different things. Leaving it off — the default — was worse: the page-local sort was then the only one a user could reach, and it looked exactly like the list being sorted.How
The table's column headers now drive this list's sort in both modes, so there is one order behind them:
$orderbyand resets to page one, the same path the buttons took.The button row survives only where there are no headers to click: a
list(data-list) related list, or a caller-suppliedschemawhose contents we cannot assume.sortable's doc comment now says that is what it controls, rather than implying it governs sorting as a whole (which it never did for a table list — the headers were always live).Relational columns keep #3096's rule, moved to the header: withheld while the sort is a server
$orderby(the key would be the stored foreign-key id while the cell shows a name), live in client mode where the key is the label.About the three modified test files
Worth a look, since they are pre-existing assertions I changed rather than added:
RelatedList.relationalSort.test.tsx(both cases) — encodes lookup 列排序按裸外键 id 排:服务端 $orderby 发平面 FK 字段,单元格却显示 $expand 后的标签 #3096's guarantee, asserted by clicking sort buttons. Same guarantee, now read off the column'ssortableflag and driven through the table'sonSortChange.RelatedList.serverpagination.test.tsx— one case that clicked a sort button to prove the$orderby+ page reset. Same assertion, triggered through the header.RelatedList.systemcolumns.test.tsx— used the button row as a proxy for column order. It now reads the column order directly off the schema, which is what it was actually testing.None of them lost coverage; each moved to the control that now carries the behavior.
Tests
9 new cases in
RelatedList.headerSort.test.tsx, including the two-modes split and "the button row is gone for a table list but stays for alistone".plugin-detailfull suite: 294 passed (34 files). Lint: 0 errors.Related
$orderbyreaches the server as a sort, not a list of character indices (#3106) #3109 — string$orderbyserialization, independent.🤖 Generated with Claude Code