Skip to content

Unified search: the v2 <SearchResults> component + host consumer#5237

Merged
habdelra merged 2 commits into
mainfrom
cs-11437-unified-search-searchresults-component-host-consumer-v2
Jun 15, 2026
Merged

Unified search: the v2 <SearchResults> component + host consumer#5237
habdelra merged 2 commits into
mainfrom
cs-11437-unified-search-searchresults-component-host-consumer-v2

Conversation

@habdelra

Copy link
Copy Markdown
Contributor

The one v2 search component family, <SearchResults @query=… />, plus the host-side pieces it needs. Phase 1 of the unified-search project. Additive — it sits alongside the existing prerendered-card-search component and the live SearchContent tree; no call site is migrated in this PR (each surface migrates as its own shippable step).

What it does

<SearchResults> consumes the heterogeneous search-entry stream from getSearchEntriesResource and renders it transparently:

  • a prerendered html rendering shows inert (the fast path) and hydrates lazily on interaction (hover/click/touch, or none),
  • an item-only row renders as a live card.

Each result is a RenderableSearchEntry exposing id / isError / html / item / component. The component wraps the merged HydratableCard mechanism, so a consumer renders <entry.component /> without ever branching on prerendered-vs-live — that's what makes the split transparent. It's curried in JS via the same custom-manager pattern html-component.ts already uses to yield a renderable .component; provide/consume context still reaches the nested HydratableCard because the glimmer VM tracks context per component instance regardless of manager.

Used with a block it yields a results object (entries / isLoading / meta / errors); used without one it renders the default stream of entry.components itself.

<SearchResults @query={{this.query}} as |results|>
  {{#each results.entries key='id' as |entry|}}
    <entry.component />
  {{/each}}
</SearchResults>

Selective Store inflate

StoreService.inflateSearchEntryItem deposits only full item serializations (so a by-URL read or the hydration GET resolves without a round-trip). A sparse item (one carrying meta.sparseFields) and the search-entry itself are never deposited — a sparse item would misrepresent the instance and could clobber a correctly-loaded full one. <SearchResults> runs this as a render-side effect over the live entry set, so an item-bearing row deposits whenever one lands on a re-run.

Render type

An html row hydrates/renders as the type its rendering carries; an item-only fallback renders as the query's requested ancestor (echoed once in the document meta) so it matches its HTML siblings; files render natively. View-models are memoized by the resource's stable entry identity, so a live re-run never re-mounts an unchanged row's component.

Tests

tests/integration/components/search-results-test.gts (7 tests, all green against the realm stack):

  • prerendered HTML renders inert and hydrates into a live card on hover;
  • a heterogeneous stream renders the html row inert and the item-only row live;
  • selective inflate — a full item is deposited, a sparse item never is;
  • the component inflates a full-item row into the store;
  • an error rendering never hydrates;
  • the block-yield shape (results.entries / meta / each entry.component);
  • an idle query renders nothing, then activates when set.

Add <SearchResults @query=…/>, the one v2 search component family. It
consumes the heterogeneous search-entry stream from
getSearchEntriesResource and renders it transparently: a prerendered html
rendering shows inert and hydrates lazily on interaction, an item-only
row renders as a live card. Each entry exposes a curried .component
(wrapping HydratableCard) so a consumer renders <entry.component />
without branching on prerendered-vs-live, plus the raw html/item
branches. Used with a block it yields a results object
(entries/isLoading/meta/errors); used without one it renders the default
stream itself.

StoreService.inflateSearchEntryItem deposits only full item
serializations; a sparse item (carrying meta.sparseFields) and the
search-entry itself are never stored. <SearchResults> runs this selective
inflate over the live entry set.

Additive — no call site is migrated; the prerendered-card-search
component and the live SearchContent tree remain until each surface moves.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1ac96da3a4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/host/app/components/card-search/search-results.gts
Comment thread packages/host/app/components/card-search/search-results.gts
@github-actions

github-actions Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Preview deployments

Host Test Results

    1 files      1 suites   1h 52m 2s ⏱️
3 073 tests 3 058 ✅ 15 💤 0 ❌
3 092 runs  3 077 ✅ 15 💤 0 ❌

Results for commit 619531d.

Realm Server Test Results

    1 files      1 suites   10m 17s ⏱️
1 715 tests 1 715 ✅ 0 💤 0 ❌
1 808 runs  1 808 ✅ 0 💤 0 ❌

Results for commit 619531d.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds the first “unified search” v2 <SearchResults @query=… /> component family for host usage, providing a single render surface for heterogeneous search-entry results (prerendered inert HTML that hydrates lazily, plus item-only rows that render live), along with the supporting host-side selective Store inflation behavior and integration tests.

Changes:

  • Introduces <SearchResults> (block-yield or default rendering) that wraps each entry in a ready-to-render <entry.component /> abstraction.
  • Adds a curried hydratableEntryComponent() wrapper around HydratableCard using a custom component manager (similar to html-component.ts).
  • Adds StoreService.inflateSearchEntryItem() to selectively deposit only full item serializations (skipping sparse ones) into the Store, plus integration coverage.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
packages/host/app/components/card-search/search-results.gts New v2 SearchResults component: entry view-modeling, default vs block rendering, render-side selective inflate.
packages/host/app/lib/hydratable-entry-component.ts New curried component wrapper that renders HydratableCard with per-entry args via a custom manager.
packages/host/app/services/store.ts Adds inflateSearchEntryItem() that skips sparse items and inflates full items into the store from search data.
packages/host/tests/integration/components/search-results-test.gts New integration tests covering inert HTML, hydration, heterogeneous streams, and selective inflation behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/host/app/components/card-search/search-results.gts
Comment thread packages/host/app/components/card-search/search-results.gts
Comment thread packages/host/app/services/store.ts Outdated
- Thread the selected HTML format through to the live/hydrated card:
  HydratableCard takes an optional @Format (default fitted), and
  <SearchResults> passes each row's format (the rendering's own for an
  html row, the query's requested format for an item-only fallback) so a
  hydrated/fallback row matches the HTML the query selected instead of
  always rendering fitted.
- Invalidate the view-model memo when the captured render inputs (gesture
  mode + document-level fallback render type/format) change, so a row is
  not reused with stale hydration settings.
- inflateFullItems now catches and logs a deposit failure instead of
  rejecting unhandled mid-render.
- Reword the inflateSearchEntryItem doc comment (it returns void).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@habdelra
habdelra merged commit cdd7c65 into main Jun 15, 2026
72 of 73 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants