fix(list,detail): sorting a lookup column stops ordering by an invisible key (#3096) - #3102
Merged
Merged
Conversation
…ble key (#3096) A relational column (lookup / master_detail / user / tree) never holds the string its cell shows: it holds the $expand-ed record, or a raw foreign-key id whose label was resolved separately. Every sort path took that raw value as its key, so a column of names came back in an order with no relation to the names — sorting looked broken, and nothing said the key was something else. The two halves can order by different things, so they are fixed differently. Client-side sorts (grid column headers, any data-table, a non-windowed related list) now key off the label the cell renders, via getSortValue / compareSortValues in @object-ui/core — which resolves an expanded record through getRecordDisplayName (ADR-0079), so the sort key and the lookup cell agree on which field names a record. That replaces two broken comparators: `a[col] < b[col]` is always false between two objects (the comparator collapsed to a constant and permuted the rows), and `String(a[col])` is "[object Object]" (every row compared equal, so the sort silently did nothing). Server $orderby sorts cannot be fixed here — the key is the stored id by construction, and objectstack#4256 settled that no relation join is coming. So those entry points stop offering the illusion: the ListView toolbar sort picker withholds relational fields and explains why (pointing at a formula field as the supported way to sort by a related name), and a windowed related list renders no sort button for them. A relational field the view's CURRENT sort already uses stays listed, labelled "(by ID)", so view metadata authored or saved with such a sort round-trips instead of rendering a blank row and losing the sort on the next edit. Each regression test was run against the unfixed code first: the data-table one showed the constant comparator leaving rows untouched, the RelatedList one showed the old raw-id order, the ListView one showed all four relational fields in the picker. Verified live in the console against a CRM backend: crm_lead's two lookups are gone from the sort picker with the hint rendered beneath it, and the contacts grid's account column now sorts Acme/Globex/Initech ↔ reverse by the shown company name. Co-Authored-By: Claude Opus 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.
Fixes #3096.
A relational column (
lookup/master_detail/user/tree) never holds the string its cell shows — it holds the$expand-ed record, or a raw foreign-key id whose label was resolved separately. Every sort path took that raw value as its key, so a column of names came back in an order with no relation to the names, and nothing said the key was something else.The two halves can order by different things, so they are fixed differently — that split is the whole design:
A relational column may be sorted where the key is the label the cell shows; it may not be offered where the key would be the raw id.
Client-side sorts — now order by the label
Grid column headers, any
data-table, and a non-windowed related list all sort rows already in memory, where the label IS available. They now key off it via the newgetSortValue/compareSortValuesin@object-ui/core, which resolves an expanded record throughgetRecordDisplayName(ADR-0079) so the sort key and the lookup cell agree on which field names a record. A raw-id cell goes through the surface's ownid → labelmap (RelatedList resolves labels client-side rather than via$expand).That replaces two separately-broken comparators:
a[col] < b[col]is always false between two objects → the comparator collapsed to the constant1, and the rows were permuted into an order unrelated to anything on screen (data-table);String(a[col])is"[object Object]"→ every row compared equal, so the sort silently did nothing (RelatedList).Server
$orderbysorts — stop offering the illusionThese cannot be fixed here: the key is the stored id by construction, and objectstack-ai/objectstack#4256 settled that no relation join is coming. So the entry points withhold them.
A relational field that the view's current sort already uses stays listed, labelled
(by ID). Without that, opening the popover on a view authored (or saved before this change) with a relational sort would render a blank row and drop the sort on the next edit.This is issue option 1 + 3. Option 1's "annotate" variant is used only for the already-in-use case above; new sorts are simply not offered.
Verification
Every regression test was run against the unfixed code first, and each failed with the exact symptom it describes:
data-table: the constant comparator left rows in the incoming order (fixture picked so incoming order, id order, and name order are all distinct);RelatedList: the old raw-id orderZoe / Alice / Malloryinstead ofAlice / Mallory / Zoe;ListView: all four relational fields present in the picker.Live in the console (CRM backend, this worktree's console):
crm_lead's two lookups (account,converted_opportunity) are gone from the sort picker, with the hint rendered under it;assigned_to— a text field holding an email, not a lookup — correctly stays.Acme Corp / Globex Ltd / Initech↔ reversed by the shown company name, and sends nosort=param (client-side, as designed). All list/meta requests 200.Full suites for
core,components,plugin-list,plugin-detail,plugin-grid,plugin-dashboard,plugin-report,i18nandapp-shellpass; locale parity holds across all ten packs;tsc --noEmitclean for every touched package.🤖 Generated with Claude Code