Catalog: stop the facet "Sort by" control vanishing as you type (5217 stack 4/9) - #5262
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## stack/5217-3-search-ordering-accessible-name #5262 +/- ##
=============================================================================
Coverage 35.16% 35.16%
=============================================================================
Files 741 741
Lines 23806 23808 +2
Branches 6429 6431 +2
=============================================================================
+ Hits 8371 8373 +2
+ Misses 13692 13691 -1
- Partials 1743 1744 +1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
| }, | ||
| fromString: (input: string) => { | ||
| const json = parseFilterJson(input, 'Invalid date range in the search URL') | ||
| const json = JSON.parse(input) |
There was a problem hiding this comment.
Contextual filter errors removed
When a shared or bookmarked search URL contains malformed filter JSON, the direct JSON.parse call now sends a raw syntax error through the search error boundary, causing the page to display a context-free parsing message instead of identifying the invalid filter; removing parseFilterJson also drops the malformed value from diagnostic logging. The same regression applies to the number-range and keyword-list parsers.
Knowledge Base Used: Catalog client features
There was a problem hiding this comment.
Accepted — fixed in 39c2aac.
Correct, and it was wider than this call site. The layer also dropped parseFilterJson itself, the Log import in both model.ts and model.spec.ts, and the six-test describe('Predicates: malformed filter JSON') block — a straight revert of #5233, which is an ancestor of this PR's base. None of the four commit messages mentions any of it, so these two files look reconstructed from a pre-#5233 state rather than patched.
Restored all of it: the helper, the three call sites in Predicates.Datetime, Predicates.Number and Predicates.KeywordEnum, both Log imports, and the tests. This layer's own orderingOffered work is untouched — the two regions sit at opposite ends of the file. model.spec.ts is back to 45 tests, the Search suite is 114 green (was 108), and this layer's diff against its base is now ordering-only.
Worth adding the changelog half: the #5233 entry was still present in catalog/CHANGELOG.md, so as it stood the stack would have shipped the claim without the behaviour.
63ee89b to
1a644d7
Compare
544d8f4 to
f59b556
Compare
f59b556 to
247a363
Compare
Local code review — findings and dispositionsReviewed this layer only ( Accepted1. This layer reverted #5233's malformed-filter-JSON handling. (also flagged inline by the automated review)
It is user-visible. Fixed in 39c2aac, restored from this PR's base. The ordering work is untouched — the two regions sit at opposite ends of the file. 2. Comments narrated the change rather than the code. The DeclinedThe threshold no longer governs the count actually on screen. On the truncated path the control now stays mounted above a one- or two-field list, so Reviewed and found clean
Verification
Layers 5–9 were rebased onto the updated branch in order; |
07a0f23 to
723db2f
Compare
…arrows Two changes to when the "Sort by" control is offered: - the truncated-list-plus-server-query path passed the query's own result count as the total, so typing in "Find metadata" could drop it below the threshold and unmount the control mid-word; - the control was offered above an empty list, which sorts nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… latch addresses review findings f5, f36, f6, f47 The flicker was fixed twice over: a `Math.max` guess at the total, plus a monotonic per-mount ref that latched the highest count it had ever seen. The ref was written in the render body, which React documents as something not to do, and the latch was not scoped to the narrowing it was written for — any drop in the total made it stick, for the life of the mount. Fix it at the count instead. Only one of the four call sites was passing a total that moved: the truncated-list-plus-server-query path passed its own query result, which shrinks with every keystroke. It now passes `initial` — the list as it stood before the reader started typing. That is stable across the search by construction, and it is a genuine lower bound rather than a guess, because a list reaching that path is truncated, so the server holds at least that many. With the caller fixed, `useOrderingOffered` has no state to keep and becomes `orderingOffered`, a pure predicate: threshold on the pre-filter total, withheld while nothing is displayed. No ref, no mount-scoped memory, and the same answer whoever asks. The gate's contract comments at the declaration and the consumer described this behaviour all along and had gone stale against the implementation. They are true again, with the withhold rule added — it was never written down. The server answers no facet total, only `userMetaTruncated`, so a count taken from the source is genuinely unavailable on this path; `initial.length` is the strongest sound stand-in the client can compute. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
addresses review finding f7 The prop doc claimed `totalAvailable` is "the count before any filtering", which was the stated substitute for a test — and which the same diff falsified at two of the four call sites. Say what it is: a lower bound that excludes already-applied facet filters, understates a truncated list, and holds still while the filter box is typed in. That last property is the only one the threshold depends on. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
addresses review findings f44, f27 The visibility half of the "Sort by" entry, citing the PR that ships it. Both deltas the omnibus entry stated are named here — the flicker and the withhold-on-zero-results — rather than one standing in for the pair. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…opped This layer's two Search model files were reconstructed from a pre-#5233 file state rather than patched, which silently reverted merged PR #5233: `parseFilterJson` and its three call sites in `Predicates.Datetime`, `Predicates.Number` and `Predicates.KeywordEnum`, the `Log` import in both files, and the six tests covering them. None of this layer's commit messages mentions any of it, so the revert was collateral, not deliberate. It is user-visible. `SearchErrorFallback` and `PackageListErrorFallback` render `error.message` verbatim, so a shared or bookmarked `/search?modified={` showed "Unexpected end of JSON input" rather than "Invalid date range in the search URL" — nothing naming the parameter at fault. The `Log.error` line carrying the offending value was gone too, so all three filter types collapsed into indistinguishable Sentry groups with no payload. The CHANGELOG entry claiming the fix ships was untouched, so the stack would have shipped the claim without the behaviour. Restored from the layer below. This layer's own ordering-visibility work is untouched: the restored hunks sit around the `Predicates` block and the new `orderingOffered` work is at the other end of the file. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The comments this layer adds narrated the change rather than the code: an earlier `useOrderingOffered` that latched the highest count in a ref, and the flicker the new predicate replaces. That version never shipped, so a reader of this file has no way to reach it and no reason to. What survives is what the code cannot show: why the threshold and the withhold rule read different counts, why the truncated path passes `initial`, and that the predicate must answer the same whoever asks. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
723db2f to
ec3d8f9
Compare
Description
The search sidebar's "Sort by" control disappeared while you typed in "Find
metadata". On a stack whose facet list the server truncates, narrowing the list
dropped the count the control gates on below its threshold, and the control
unmounted mid-word — while the reader was using it to hunt for a field. It was
also offered above an empty list, where it has nothing to sort.
This is the visibility half of the "Sort by" work. The control's accessible name
is PR 3, a different file and a
different concern.
Review findings addressed
This unit drew most of the review's attention, and the approach here is the one
f5
asked for: fix the count, rather than patch the symptom twice.
Math.maxguess atthe total and a monotonic per-mount ref that latched the highest count it had
ever seen. Only one of the four call sites was actually passing a total that
moved. Fixing that one caller made both patches unnecessary.
— the latch wrote
maxSeen.currentin the render body, which React's"Referencing values with refs" documents as something not to do, and it was not
scoped to the narrowing it was written for: any drop in the total made it
stick for the life of the mount. The ref is gone;
useOrderingOfferedis noworderingOffered, a pure predicate.now drive the predicate directly, including the narrowing case that used to
flicker and the withhold-on-empty case.
declaration, the
totalAvailableprop, the consumer inPackageFilters.tsx)and all three had gone stale against the implementation. They describe it
correctly again, and the withhold-on-empty rule — which was never written down
anywhere — is now stated.
while shipping
Math.max(initial.length, available.length), whose own commentconceded neither operand is the pre-filter total. It now passes
initial.length,and the claim is true as stated.
the visibility rule on all four paths through
AvailablePackagesMetaFiltersGroup, not the one path the old description named.Three of them already passed a stable pre-filter count and are unaffected by the
count change, but all four now go through the same predicate and all four gain
the withhold-on-empty rule.
totalAvailableprop doc said "the count before any filtering",which was offered as the substitute for a test and which the diff falsified. It
now says what the value is: a lower bound that excludes already-applied facet
filters and understates a truncated list, whose one load-bearing property is
that it does not move while the filter box is typed in.
On f5, and what was not available
f5 recommended gating on "the count the server already answers". It does not
answer one.
PackagesSearchStatsexposesuserMeta(the possibly-truncatedlist) and
userMetaTruncated(a boolean) — there is no facet total in the schema;PackagesSearchResultSet.totalcounts packages, not fields. So the fallback f5allowed for applies, and
initial.lengthis the strongest sound stand-in theclient can compute: stable across the search, and a genuine lower bound because
the list reaching that path is truncated by definition.
Verification
107 tests. The five in
model.spec.tsunderorderingOfferedare the ones thisPR adds.
Position in the stack
PR 4 of 9, based on
stack/5217-3-search-ordering-accessible-name.Part of the split of #5217 asked for in
f27. It
stacks directly above PR 3 because f47's fix corrects a comment in
PackageFilters.tsx, which PR 3 owns — stacking avoids a cross-branch edit. Thetwo are deliberately not collapsed: PR 3 is an uncontested accessibility fix and
should not need this unit's review to land.
TODO
🤖 Generated with Claude Code
Greptile Summary
This PR stabilizes the metadata-facet “Sort by” control while users narrow truncated facet lists and hides it when no facets are displayed. It also unintentionally removes contextual malformed-filter error handling and associated regression tests.
orderingOfferedpredicate.JSON.parsecalls, degrading malformed-URL errors.Confidence Score: 4/5
The malformed-filter error regression should be fixed before merging so invalid shared or bookmarked search URLs retain actionable user-facing errors and diagnostics.
Direct
JSON.parsecalls now send raw syntax messages through the search error boundary and omit the malformed value from logs, replacing the existing filter-specific error contract.Files Needing Attention: catalog/app/containers/Search/model.ts, catalog/app/containers/Search/model.spec.ts
Important Files Changed
Reviews (1): Last reviewed commit: "docs(changelog): entry for the facet ord..." | Re-trigger Greptile
Context used: