Skip to content

fix(frontend): show the search dropdown's loading state while a search runs - #1072

Merged
OlaGreat merged 2 commits into
OlaGreat:mainfrom
DSOTec:fix-1063-search-loading-state
Aug 29, 2026
Merged

fix(frontend): show the search dropdown's loading state while a search runs#1072
OlaGreat merged 2 commits into
OlaGreat:mainfrom
DSOTec:fix-1063-search-loading-state

Conversation

@DSOTec

@DSOTec DSOTec commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Problem

In app-shell.tsx, showDropdown was only ever set to true inside the search fetch's success branch — never before or during the request. The dropdown itself is gated on showDropdown, and the "Searching..." text lives inside it, gated on isSearching:

{showDropdown && (
  <div ...>
    {isSearching ? <div>Searching...</div> : ...}

So for any fresh search — the common case, where the dropdown starts closed — isSearching was true for the whole debounce-plus-fetch duration while showDropdown stayed false, and nothing rendered at all. By the time setShowDropdown(true) ran, setIsSearching(false) ran in the same tick and was batched with it, so the loading branch was never painted. Users never saw "Searching..."; results just popped in with no feedback.

Solution

Open the dropdown as soon as the query is non-empty, before the debounce starts, so the in-flight state is actually visible:

setIsSearching(true);
setShowDropdown(true);

Two follow-on details:

  • The success branch's setShowDropdown(true) is now redundant and drops out. A side benefit: a dropdown the user dismissed mid-request (Escape or click-outside) now stays dismissed instead of springing back open when results land.
  • Failed lookups still call setShowDropdown(false). I deliberately kept this: letting a failure fall through to the open dropdown would render "No results found", misreporting a network error as an empty result set.

Behaviour on the success path, empty results, and a cleared query is unchanged.

Testing

Added frontend/src/components/app-shell.test.tsx with 6 tests:

  1. "Searching..." is visible during the debounce — asserted immediately after typing, before the 300ms timer elapses
  2. ...and still visible while the request is outstanding — using a fetch double that never settles on its own, so the pending state can be observed directly
  3. the loading state is replaced by results once the request resolves
  4. "No results found" renders for an empty result set
  5. clearing the query closes the dropdown
  6. a failed request closes the dropdown and does not show "No results found"

Verified the tests actually catch the bug: against the code before this change, tests 1, 2 and 6 fail (Unable to find an element with the text: Searching...) while 3, 4 and 5 still pass — confirming the fix targets the loading state without regressing the paths that already worked.

Full suite from a clean npm ci, matching what CI runs:

npm test          13 files, 109 tests passed   (also verified with CI=true)
npm run lint      0 errors
npm run build     OK
npx tsc --noEmit  clean
npx playwright test   2 passed
npx lhci autorun      exit 0 (warnings only, no failed assertions)

Note on the first commit

This branch carries two commits. The second (fix(frontend): show the search dropdown's loading state...) is the actual fix for #1063 and is a 6-line change.

The first commit is the CI-pipeline repair already submitted as #1071, included here only because main is currently red — Frontend CI, Lighthouse CI and E2E (Playwright) all fail on main, so a branch based on it cannot go green on its own. If #1071 merges first, I'll rebase and that commit disappears from this PR, leaving just the app-shell.tsx change and its tests. Reviewing frontend/src/components/app-shell.tsx and app-shell.test.tsx alone gives the complete picture of this fix.

Closes #1063

DSOTec added 2 commits August 29, 2026 06:31
Frontend CI, Lighthouse CI and Playwright E2E had all been failing on main
for several merges. Each job died at a different point; this fixes the
whole chain so the suite runs green.

Build / lint (blocked Lighthouse CI and E2E, which both run `npm run build`)
- escape unescaped quotes and apostrophes in privacy/ and terms/ JSX
  (react/no-unescaped-entities, 22 errors) using entities that render
  identically, so the pages are unchanged visually
- dashboard: the dominant-asset reduce read `.value`/`.name`, which do not
  exist on AssetBreakdownEntry; use `.amount`/`.assetCode` as the chart's
  own dataKey/nameKey already do. This was a type error that only surfaced
  once lint stopped failing first.

Runtime CSP (all client-side JS was blocked on every static route)
- `script-src 'self' 'nonce-…'` cannot work with statically prerendered
  pages: their HTML is built ahead of time and cannot carry a per-request
  nonce, and a nonce makes browsers ignore 'unsafe-inline'. Every static
  page therefore loaded with no working JavaScript. Drop the nonce and
  allow inline scripts. JSON-LD is `application/ld+json`, which is not
  executable and was never gated by script-src, so its nonce goes too.
- build connect-src from lib/config instead of raw process.env: the client
  falls back to its own defaults (localhost:4001, Horizon, Soroban) that
  the policy then omitted, so every API request was refused.

Test suite (50 failures -> 0; 106 tests pass)
- vitest was collecting the Playwright specs in e2e/; exclude them
- add the missing @testing-library/user-event dev dependency
- replace full-module vi.mock factories with importOriginal-based partial
  mocks, so a stub no longer erases exports that transitive imports need
- mock next/navigation and focus-trap-react once in the shared setup
- make the framer-motion stubs cover any motion.<tag> via a Proxy
- wrap ActivityFeed snapshots in a QueryClientProvider
- pin Math.random for the MilestoneCard confetti so its snapshot is stable
- update assertions that described removed behaviour: the support panel
  reports wallet errors through a toast and opens the result modal, copy
  shows inline feedback, ProfileTabs empty-state copy is no longer
  personalised, and the create wizard collects the wallet on step 2
- restore a funded-account stub between support-panel tests, since
  clearAllMocks() clears calls but not implementations
- fix queries that matched several elements ("Failed", "XLM" vs "yXLM")
- commit the previously untracked snapshots; vitest does not write new
  ones under CI and fails instead

Also expose aria-pressed on the create wizard's asset toggles, which the
existing test asserted and screen readers need, and stub the profile API
in the home E2E spec so it does not depend on a live backend.
…h runs

showDropdown was only set inside the fetch's success branch, so for a fresh
search — the common case, with the dropdown closed — isSearching stayed true
for the whole debounce-plus-fetch while showDropdown stayed false and nothing
rendered. The dropdown then opened in the same tick that cleared isSearching,
so "Searching..." was never painted and results simply appeared with no
loading feedback.

Open the dropdown as soon as the query is non-empty, before the debounce
starts, so the in-flight state is actually visible. The success branch's
setShowDropdown(true) is now redundant and drops out, which also means a
dropdown the user dismissed mid-request stays dismissed instead of springing
back open when results land.

Failed lookups still close the dropdown rather than falling through to
"No results found", which would misreport a network error as an empty result.

Adds tests covering the loading state during the debounce and the request,
the handoff to results, the empty-result and cleared-query cases, and the
failure path.

Closes OlaGreat#1063
@drips-wave

drips-wave Bot commented Aug 29, 2026

Copy link
Copy Markdown

@DSOTec Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@OlaGreat
OlaGreat merged commit 59dff44 into OlaGreat:main Aug 29, 2026
3 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.

[Frontend] Search dropdown's "Searching..." loading state is effectively dead code

2 participants