Skip to content

feat(map): simplify search UX/UI#1061

Open
escapedcat wants to merge 25 commits into
mainfrom
search-ux-ui
Open

feat(map): simplify search UX/UI#1061
escapedcat wants to merge 25 commits into
mainfrom
search-ux-ui

Conversation

@escapedcat

@escapedcat escapedcat commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Single search input for the map

Replaces the map's Worldwide/Nearby toggle with one search box, so people no longer have
to choose a scope before searching. On mobile it becomes a floating, draggable search
card. From the "Search UX — single input" design.

How search works now

  • Empty box → what's nearby. Opening search shows the merchants around your current view,
    with a count.
  • Type → search everywhere. Any query searches worldwide by name; nearby matches still
    rank first. Clearing the box returns you to the nearby list.
  • Toggle removed, along with the old "nothing nearby — search worldwide" prompt — typing
    already covers everywhere, so there's nothing to switch.
  • More results shown: the nearby list now goes up to 250 (was 99).
  • A failed search keeps what you typed (just shows an error) instead of wiping the box.

Mobile

  • Search is a floating card at the bottom; drag it up to expand into the full list, or tap
    it to open without the keyboard popping up — so you can browse nearby first.
  • Closes via a collapse caret (clearer button styling) or the drag handle, consistent with
    the merchant detail drawer.
  • The card sits above the scale bar and map credits, which stay pinned at the bottom; it
    never covers the tile-loading indicator.

Desktop

  • Same single-input behavior; the nearby count stays visible inside the open panel.
  • The search field reads more clearly as an input.

Map info

  • Nearby places/count now appear immediately on load (previously only after panning or
    opening the panel).
  • Map credits collapse to a small (i) on mobile to keep the bottom clear; full credits
    remain on desktop.

Worth confirming (intended, not bugs)

  • Typing 1–2 characters keeps showing the nearby list; worldwide search starts at 3
    characters.
  • Collapsing the mobile card back to its resting state clears the active search
    (re-opening starts fresh on nearby).

Analytics

  • Three events tied to the removed toggle/CTA can be retired: worldwide_mode_click,
    nearby_mode_click, search_worldwide_cta_click. New mobile-sheet events worth tracking:
    search_sheet_tap_expand, search_sheet_swipe_expand, search_sheet_swipe_collapse.

Summary by CodeRabbit

Release Notes

  • New Features

    • Mobile search sheet supports swipe/drag expand and collapse.
    • Nearby results count pill appears in the search UI.
  • Improvements

    • Merchant list expanded to 250 items, with badge behavior showing “>250” when capped.
    • Map attribution display improved for compact mobile layouts.
  • Updates

    • Search UI simplified to a single input (no mode toggle) with refined debounced behavior and safer handling of late results.
    • Analytics updated for search-sheet interactions.
  • Bug Fixes

    • Improved tap behavior inside the search input trailing area.
  • Documentation

    • Accessibility labels and search text updated across supported languages.

escapedcat and others added 9 commits June 12, 2026 18:32
Parameterize peek height, dismissability, and analytics event names so a
second bottom sheet (mobile search) can reuse the drawer snap mechanics.
Defaults preserve the merchant drawer behavior exactly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…mode tabs

The Worldwide/Nearby scope toggle now lives inside the list panel only.
At rest the input carries a teal '{n} nearby' pill; it yields the slot to
the clear button as soon as the user types.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
While a nearby filter is active the Nearby tab shows the filtered count,
including (0). A zero-match filter renders an empty state with a
'Search worldwide' CTA instead of a blank list. Panel placeholder becomes
the neutral 'Search places...' (single-input metaphor; scope lives in the
toggle), retiring the per-mode placeholder keys.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The merchant list panel becomes an always-present bottom sheet on
mobile: peek (grabber + single input + count pill) <-> full panel,
driven by a second drawer-gesture-controller instance (canDismiss
false, search_sheet_* analytics). Tapping the peek facade opens the
list without popping the keyboard; the floating search bar is now
desktop-only. Bottom map controls (scale, OSM attribution) are lifted
above the peek sheet on mobile.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…fter review

Review findings from the multi-agent pass:
- determineSnapState now honors the controller's peek height, so the
  search sheet snaps back to 110px instead of the drawer's 140px
  (+ regression test)
- reset the sheet gesture when the merchant drawer takes the bottom
  edge, so an unmount mid-drag can't strand the captured pointer
- the whole peek facade is a swipe surface, not just the grabber strip;
  facade exposes aria-expanded and its visible label (incl. count)
- floating bar and sheet now derive from one init-locked layout
  decision, so resizes can't yield zero or two search surfaces
- touchcancel handling on the list collapse-drag; grabber aria-controls
- trailing slot is click-transparent so the count pill no longer blocks
  taps on the input
- panel input emits search_input_focus {source: panel}
- attribution lift reads SEARCH_SHEET_PEEK_HEIGHT via a CSS custom
  property; sheet bottom padding uses the max() safe-area floor
- rewrite stale merchant-list-panel e2e specs for the single-input UX
  (9 passing) and add a zero-match CTA spec
- translation polish: terminology consistency for the new keys in
  fr/it/nl/ru/bg/pt-BR/es/de

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The expanded sheet header (search input row, scope toggle, category
chips) now acts as a drag surface like the grabber, mirroring the
merchant drawer's draggable header. An 8px vertical slop separates
taps from drags so the input, toggle and chips stay tappable; once
dragging, the pointer is captured so releasing over a chip doesn't
click it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Render height as calc(spring + env(safe-area-inset-bottom)) capped at
100dvh, so the home-indicator inset is added only on devices that have
one instead of being budgeted into the constant. Attribution lift
mirrors the same calc.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@qodo-code-review

Copy link
Copy Markdown
Contributor

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@netlify

netlify Bot commented Jun 12, 2026

Copy link
Copy Markdown

Deploy Preview for btcmap ready!

Name Link
🔨 Latest commit 553d26c
🔍 Latest deploy log https://app.netlify.com/projects/btcmap/deploys/6a3025f59a9b6600083f58f9
😎 Deploy Preview https://deploy-preview-1061--btcmap.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 51 (🔴 down 38 from production)
Accessibility: 97 (no change from production)
Best Practices: 92 (🔴 down 8 from production)
SEO: 96 (no change from production)
PWA: 90 (no change from production)
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR refactors the map search interface to use a mobile-optimized peek/expanded sheet model with configurable gesture controls, while simplifying the desktop search experience into a unified neutral-placeholder input that displays nearby merchant counts. Gesture infrastructure is made reusable through configurable peek heights and event names. UI constants and helpers are updated to support count badge formatting and 250-item display caps. Desktop layout conditionally hides the search bar when the panel is open. All locales receive unified search placeholder and nearby count label updates.

Changes

Mobile Search Sheet with Peek/Expand Gestures and Unified Interface

Layer / File(s) Summary
Gesture infrastructure: controller options, snap logic, and analytics events
src/lib/drawerConfig.ts, src/lib/drawerGestureController.ts, src/lib/drawerGestureUtils.ts, src/lib/analytics.ts
SEARCH_SHEET_PEEK_HEIGHT constant (72) introduced. createDrawerGestureController accepts configurable DrawerGestureOptions including peekHeight, canDismiss, and custom DrawerGestureEvents for analytics. determineSnapState parameterized to accept optional peekHeight in snap height computations. EventName exported with new gesture events (search_sheet_swipe_expand, search_sheet_swipe_collapse, search_sheet_tap_expand) replacing removed mode-click events.
Gesture controller pointer/touch event handlers
src/lib/drawerGestureController.ts
Pointer-up dismiss logic gates on canDismiss using peekHeight thresholds. Pointer-cancel snap-back collapses to peekHeight. Content-driven drag clamping applies peekHeight lower bound. Content-swipe collapse tracks analytics using configured event names. Public methods collapse() and resetToPeek() use peekHeight instead of hard-coded constants.
Input styling, nearby count helper, and merchant list cap
src/components/SearchInput.svelte, src/lib/utils.ts, src/lib/utils.test.ts, src/lib/constants.ts, src/lib/merchantListStore.test.ts
SearchInput trailing container wrapped in pointer-events-none to prevent passive content from blocking taps. formatNearbyPillCount helper introduced: returns empty string for counts ≤0, >250 for overflow, otherwise numeric string. MERCHANT_LIST_MAX_ITEMS increased from 99 to 250 with updated documentation. Store tests updated to expect 250-item display cap while totalCount reflects full input length.
Map page layout: responsive detection and search handler refactor
src/routes/map/+page.svelte
Computes isMobileLayout once from viewport width using BREAKPOINTS.md. Renders MapSearchBar desktop-only when !isMobileLayout. Passes mapReady={styleLoaded} and isMobile={isMobileLayout} to MerchantListPanel. Refactors handlePanelSearch to threshold model: ≥3 trimmed characters trigger debounced worldwide search; <3 characters abort and force nearby mode. MapLibre attribution control set to compact mode with CSS class removal for collapsed default. Initializes nearby list once on first successful map load.
MapSearchBar: single unified search input without mode toggle
src/routes/map/components/MapSearchBar.svelte
Removes Worldwide/Nearby segmented tabs and handleModeSwitch logic. Simplifies to single input with search.placeholderPlaces shown only when panel closed. Derives pillCount from nearbyCount via formatNearbyPillCount. Trailing slot displays clear button when typing or NearbyCountPill when available. Removes onNearbyClick and isLoadingCount exports.
NearbyCountPill component for merchant count display
src/routes/map/components/NearbyCountPill.svelte
New reusable component displaying location icon and localized nearby count text in a styled pill. Exports count string prop for use in MapSearchBar and MerchantListPanel.
CollapseButton component for drawer header controls
src/components/CollapseButton.svelte
New circular button component with down-arrow icon, onClick handler, and ariaLabel prop. Prevents pointerdown propagation to preserve parent drag behavior.
MerchantListPanel: sheet gesture integration and mobile peek/expand model
src/routes/map/components/MerchantListPanel.svelte
Exports mapReady and isMobile props. Introduces coordinated mobile peek/expanded sheet model with gesture controller, grabber with ARIA support, peek facade button opening sheet without focusing input, draggable header with tap-vs-drag detection. Removes local nearbyFilter-based name filtering; renders store merchants directly with empty state. Conditionally displays NearbyCountPill on mobile (empty search) or clear button (typing). Updated empty state for worldwide CTA. Gesture subscriptions cleaned up on destroy.
MerchantDrawerMobile: collapse button integration
src/routes/map/components/MerchantDrawerMobile.svelte
Replaces inline collapse button with CollapseButton component. Wires onClick to track drawer_collapse_button_click and call drawerGesture.collapse(). Sets accessibility label via ariaLabel.
Internationalization: unified placeholders and nearby counts across 9 locales
src/lib/i18n/locales/{bg,de,en,es,fr,it,nl,pt-BR,ru}.json
Unified search placeholder from mode-specific keys (placeholderWorldwide, placeholderNearby) to single placeholderPlaces across all 9 language files. Added nearbyCount label and aria.expandMerchantList accessibility string. Removed worldwide/nearby mode-click and filter-related keys (worldwide, nearby, switchToWorldwide, filterResults, aria.switchMode).
Test updates: peek-sheet and search behavior scenarios
tests/merchant-list-panel.spec.ts
Desktop coverage validates search.placeholderPlaces placeholder persists and no mode radios appear; floating search bar unmounts when panel opens and reappears after close. Mobile coverage reworked to peek-sheet model: facade visible on load, tap expansion reveals input, Escape collapses, drawer coordination hides sheet, merchant selection in both states. Added search behavior test: typing far-away place triggers worldwide CTA; clearing input returns to nearby browse.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • teambtcmap/btcmap.org#859: Both PRs extend the exported EventName union in src/lib/analytics.ts with new analytics event string literals.
  • teambtcmap/btcmap.org#695: Both PRs modify src/routes/map/components/MerchantListPanel.svelte including merchants list rendering and empty-state logic structures.
  • teambtcmap/btcmap.org#677: Both PRs modify i18n messaging and src/lib/analytics.ts EventName union for app-level event tracking.

Suggested labels

Review effort 4/5

Suggested reviewers

  • bubelov
  • dadofsambonzuki

Poem

🐰 A sheet that peeks and rises high,
With gestures smooth—no tap awry—
Nearby or wide, one prompt does stay,
Locales sing out in their own way! 🎵

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title clearly and concisely summarizes the main change: simplifying the search UX/UI by replacing the Worldwide/Nearby toggle with a single search input.
Description check ✅ Passed The pull request description is comprehensive and well-structured, covering the problem, solution, changes across desktop/mobile, search behavior details, and analytics impact, though it does not follow the provided template structure.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch search-ux-ui

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/routes/map/components/MerchantListPanel.svelte (1)

10-15: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Separate type imports from value imports.

The import statement mixes type and value imports using inline type keywords. According to the coding guidelines, type imports should be in separate import type statements.

♻️ Refactor to separate imports
+import type { CategoryCounts, CategoryKey } from "$lib/categoryMapping";
 import {
 	CATEGORY_ENTRIES,
-	type CategoryCounts,
-	type CategoryKey,
 	placeMatchesCategory,
 } from "$lib/categoryMapping";

As per coding guidelines: "Separate type imports from value imports using import type { ... } for types only and import { ... } for values only".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/routes/map/components/MerchantListPanel.svelte` around lines 10 - 15, The
current import mixes runtime values and TypeScript types; split them into two
imports: keep runtime symbols (CATEGORY_ENTRIES, placeMatchesCategory) in the
regular import and move types (CategoryCounts, CategoryKey) into a separate
"import type" statement. Update the import that currently references
CATEGORY_ENTRIES, CategoryCounts, CategoryKey, placeMatchesCategory so you have
one line importing CATEGORY_ENTRIES and placeMatchesCategory, and another
"import type" line importing CategoryCounts and CategoryKey.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/routes/map/components/MerchantListPanel.svelte`:
- Around line 892-916: The peek facade button currently (bound to facadeElement
and handled by handlePeekTap) only has aria-expanded; add aria-label and
aria-controls for screen readers by giving the button a descriptive aria-label
matching the grabber's label and set aria-controls to the id of the peek
panel/sheet element it toggles (use the same panel id used elsewhere for the
grabber). Ensure the aria-controls value exactly matches the target panel's id
and keep aria-expanded in sync when state changes in handlePeekTap/sheetGesture
logic.

---

Outside diff comments:
In `@src/routes/map/components/MerchantListPanel.svelte`:
- Around line 10-15: The current import mixes runtime values and TypeScript
types; split them into two imports: keep runtime symbols (CATEGORY_ENTRIES,
placeMatchesCategory) in the regular import and move types (CategoryCounts,
CategoryKey) into a separate "import type" statement. Update the import that
currently references CATEGORY_ENTRIES, CategoryCounts, CategoryKey,
placeMatchesCategory so you have one line importing CATEGORY_ENTRIES and
placeMatchesCategory, and another "import type" line importing CategoryCounts
and CategoryKey.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5c835d3a-1a75-45f1-9f30-d26bcfe811ac

📥 Commits

Reviewing files that changed from the base of the PR and between 91d29ed and 1a3bf65.

📒 Files selected for processing (22)
  • src/components/SearchInput.svelte
  • src/lib/analytics.ts
  • src/lib/drawerConfig.ts
  • src/lib/drawerGestureController.ts
  • src/lib/drawerGestureUtils.test.ts
  • src/lib/drawerGestureUtils.ts
  • src/lib/i18n/locales/bg.json
  • src/lib/i18n/locales/de.json
  • src/lib/i18n/locales/en.json
  • src/lib/i18n/locales/es.json
  • src/lib/i18n/locales/fr.json
  • src/lib/i18n/locales/it.json
  • src/lib/i18n/locales/nl.json
  • src/lib/i18n/locales/pt-BR.json
  • src/lib/i18n/locales/ru.json
  • src/lib/utils.test.ts
  • src/lib/utils.ts
  • src/routes/map/+page.svelte
  • src/routes/map/components/MapSearchBar.svelte
  • src/routes/map/components/MerchantListPanel.svelte
  • src/routes/map/components/NearbyCountPill.svelte
  • tests/merchant-list-panel.spec.ts

Comment thread src/routes/map/components/MerchantListPanel.svelte
escapedcat and others added 6 commits June 14, 2026 14:21
- Nearby name filter now searches the full fetched set (allMerchants,
  up to the fetch ceiling), not just the 99 displayed rows, so a match
  ranked past the display cap no longer shows a false 'nothing nearby'
  CTA. Display stays capped at 99 (#3)
- single nearbyFilter reset via reactive on !isOpen, dropping the four
  scattered clears that diverged (stale filter after item tap) (#1)
- collapsing the sheet cancels any pending/in-flight worldwide search so
  a late response can't reopen it (#2)
- remove unreachable isSearching spinner branch + dead prop in the
  desktop floating bar (#9)
- formatNearbyCount now delegates to formatNearbyPillCount (#7a)
- dismiss event is optional config, omitted when canDismiss:false (#7b)
- grabber is not a redundant tab stop / SR control at peek (#8)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the bottom-latched peek with a floating rounded input card
(side + bottom margins, raised off the bottom edge). Dragging up
continuously docks it into the full-width sheet (margins + corner
radius interpolate to 0 as it expands). Restore the drag handle on
top, slim the peek to 72px, and style the input as a clear filled
field so it reads as searchable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Empty input browses the nearby list; typing (>=3 chars) searches
worldwide by name, shorter falls back to nearby. Removes the mode
toggle, the 'nothing nearby matches' CTA, and the client-side nearby
name-filter (with its allMerchants backing list) — typing now searches
everywhere, so a nearby match is always findable and the toggle's
friction is gone. Raise the nearby display/badge cap from 99 to 250.
Style the expanded panel input as a filled field for visibility.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drops the now-unused search.worldwide/nearby, switchToWorldwide,
filterResults, noNearbyMatches(+Hint), searchWorldwide and aria.switchMode
keys from all 9 locales, plus the nearby_mode_click / worldwide_mode_click
/ search_worldwide_cta_click events that the removed mode toggle and CTA
used to fire.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Collapse the OSM/supporter attribution to a compact (i) button by
default (matching the merchant detail / area maps), drop the rule that
lifted the scale bar and attribution above the search card, and raise
the floating search peek (56px bottom inset) so it sits above the scale
bar, the compact attribution and the tile-loading indicator — the same
stacking order as the search bar on main.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- The initial camera is set programmatically (no moveend), so the nearby
  list/count stayed empty until the user panned or opened the panel —
  the peek pill showed no count. Trigger updateMerchantList once when the
  map and places are first ready.
- The compact AttributionControl renders expanded on load
  (maplibregl-compact-show); collapse it to the (i) button so the bottom
  edge stays clear.

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

Copy link
Copy Markdown
Member

Very nice 😍

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib/constants.ts`:
- Around line 69-73: Search the src/lib/constants.ts file for comments that
reference the old value of 99 related to merchant limits or nearby browse
functionality, particularly in the nearby-flow context. Update all instances of
these stale cap references from 99 to 250 to align with the updated
MERCHANT_LIST_MAX_ITEMS constant, ensuring consistency across all documentation
and comments in the file.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 50bd70e3-94dc-4fcb-be63-18680991362c

📥 Commits

Reviewing files that changed from the base of the PR and between 1a3bf65 and 5c43955.

📒 Files selected for processing (20)
  • src/components/SearchInput.svelte
  • src/lib/analytics.ts
  • src/lib/constants.ts
  • src/lib/drawerConfig.ts
  • src/lib/drawerGestureController.ts
  • src/lib/i18n/locales/bg.json
  • src/lib/i18n/locales/de.json
  • src/lib/i18n/locales/en.json
  • src/lib/i18n/locales/es.json
  • src/lib/i18n/locales/fr.json
  • src/lib/i18n/locales/it.json
  • src/lib/i18n/locales/nl.json
  • src/lib/i18n/locales/pt-BR.json
  • src/lib/i18n/locales/ru.json
  • src/lib/merchantListStore.test.ts
  • src/lib/utils.test.ts
  • src/lib/utils.ts
  • src/routes/map/+page.svelte
  • src/routes/map/components/MapSearchBar.svelte
  • src/routes/map/components/MerchantListPanel.svelte
💤 Files with no reviewable changes (11)
  • src/lib/i18n/locales/en.json
  • src/lib/i18n/locales/fr.json
  • src/lib/i18n/locales/it.json
  • src/lib/i18n/locales/es.json
  • src/lib/analytics.ts
  • src/lib/i18n/locales/de.json
  • src/lib/i18n/locales/pt-BR.json
  • src/routes/map/components/MapSearchBar.svelte
  • src/lib/i18n/locales/ru.json
  • src/lib/i18n/locales/bg.json
  • src/lib/i18n/locales/nl.json
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/components/SearchInput.svelte
  • src/lib/drawerGestureController.ts
  • src/lib/utils.test.ts

Comment thread src/lib/constants.ts
escapedcat and others added 2 commits June 14, 2026 19:21
Rewrite the toggle/CTA-based specs for the new behavior: empty input
browses nearby, typing searches worldwide (asserted via the search-only
'Show all on map' control), clearing returns to nearby. Disambiguate the
floating-bar-vs-panel input (now sharing an accessible name) by count
and scope instead of visibility.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Once the panel opens on desktop the floating bar (and its count pill)
unmounts, and the removed Nearby tab used to carry the count. Add a
muted 'N nearby' line under the search field — nearby mode only, hidden
while searching. Mobile keeps using the peek/input pills.

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

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/merchant-list-panel.spec.ts`:
- Around line 284-288: The test has a race condition where panelInput.fill() on
line 284 can trigger the `/api/search/places` request before the waitForResponse
listener is registered on line 285, causing intermittent timeouts. Reorder the
operations by registering the response waiter first (before the fill action),
then trigger the input fill while the listener is actively waiting. This ensures
the network request is captured reliably instead of being missed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4dbf050d-f8ff-4e2b-957a-6885f563bb68

📥 Commits

Reviewing files that changed from the base of the PR and between 5c43955 and 42fd7fe.

📒 Files selected for processing (1)
  • tests/merchant-list-panel.spec.ts

Comment thread tests/merchant-list-panel.spec.ts Outdated
escapedcat and others added 5 commits June 14, 2026 19:34
Desktop has room for the full credit; keep the compact (i) collapse
just for mobile where the floating search needs the bottom edge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Review feedback:
- delete the now-unused formatNearbyCount (the segmented tab that used it
  is gone; the pill uses formatNearbyPillCount)
- a transient search failure no longer wipes the user's typed query — keep
  the query + search mode and just toast, instead of exitSearchMode()
- guard against a late search response reopening a closed panel/sheet:
  bail after the awaits if the list is no longer open

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extract the drawer collapse caret into a reusable CollapseButton with a
round light background so it reads clearly as a button, and use it in
both the merchant drawer and the search sheet. The search sheet's caret
is now the keyboard/AT collapse control (grabber demoted to a decorative
drag affordance), and collapsing moves focus to the peek facade so AT
users aren't stranded on a hidden control. Gate the sheet gesture
subscription to mobile and drop the dead aria.expandMerchantList key.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- constants: update stale 'nearest 99' comments to 250 (cap was raised)
- e2e: arm the search-response waiter before typing so a fast response
  can't land before the wait registers (flaky-test fix)
- peek facade: add aria-controls to the expand button (matches the
  collapse caret); intentionally no aria-label — the visible
  'Search places… N nearby' text is the better accessible name

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

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
src/routes/map/components/MerchantDrawerMobile.svelte (1)

263-269: 💤 Low value

Consider adding aria-controls for consistency with MerchantListPanel.

The CollapseButton in MerchantListPanel (lines 520-526) includes aria-controls="merchant-sheet-content" and aria-expanded="true". For consistency and explicit association with the controlled content, consider adding these here:

 <CollapseButton
 	onClick={() => {
 		trackEvent('drawer_collapse_button_click');
 		drawerGesture.collapse();
 	}}
 	ariaLabel={$_('mapDrawer.collapseDrawer')}
+	aria-controls="drawer-content"
+	aria-expanded="true"
 />

The drawer handle already provides ARIA state, so this is optional.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/routes/map/components/MerchantDrawerMobile.svelte` around lines 263 -
269, Add aria-controls and aria-expanded attributes to the CollapseButton
component in MerchantDrawerMobile.svelte for consistency with the identical
CollapseButton in MerchantListPanel. Set aria-controls to reference the drawer
content element (use the same identifier as MerchantListPanel) and set
aria-expanded to the appropriate boolean state that reflects whether the drawer
is expanded or collapsed. This improves accessibility by explicitly associating
the button with the content it controls, matching the pattern already
established in MerchantListPanel.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/routes/map/components/MerchantDrawerMobile.svelte`:
- Around line 263-269: Add aria-controls and aria-expanded attributes to the
CollapseButton component in MerchantDrawerMobile.svelte for consistency with the
identical CollapseButton in MerchantListPanel. Set aria-controls to reference
the drawer content element (use the same identifier as MerchantListPanel) and
set aria-expanded to the appropriate boolean state that reflects whether the
drawer is expanded or collapsed. This improves accessibility by explicitly
associating the button with the content it controls, matching the pattern
already established in MerchantListPanel.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: be25790f-71b6-41a8-8237-47b5a4be0c22

📥 Commits

Reviewing files that changed from the base of the PR and between 42fd7fe and 113b316.

📒 Files selected for processing (15)
  • src/components/CollapseButton.svelte
  • src/lib/i18n/locales/bg.json
  • src/lib/i18n/locales/de.json
  • src/lib/i18n/locales/en.json
  • src/lib/i18n/locales/es.json
  • src/lib/i18n/locales/fr.json
  • src/lib/i18n/locales/it.json
  • src/lib/i18n/locales/nl.json
  • src/lib/i18n/locales/pt-BR.json
  • src/lib/i18n/locales/ru.json
  • src/lib/utils.ts
  • src/routes/map/+page.svelte
  • src/routes/map/components/MerchantDrawerMobile.svelte
  • src/routes/map/components/MerchantListPanel.svelte
  • tests/merchant-list-panel.spec.ts
💤 Files with no reviewable changes (9)
  • src/lib/i18n/locales/es.json
  • src/lib/i18n/locales/ru.json
  • src/lib/i18n/locales/en.json
  • src/lib/i18n/locales/nl.json
  • src/lib/i18n/locales/it.json
  • src/lib/i18n/locales/de.json
  • src/lib/i18n/locales/fr.json
  • src/lib/i18n/locales/pt-BR.json
  • src/lib/i18n/locales/bg.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/merchant-list-panel.spec.ts

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

This PR simplifies the map search UX by replacing the Worldwide/Nearby scope toggle with a single search input, and introducing a mobile bottom-sheet “peek → expand” interaction that mirrors the merchant drawer.

Changes:

  • Replace scope-toggle search with a single-input model (empty/short query shows nearby; ≥3 chars triggers worldwide search).
  • Introduce a draggable mobile search sheet + nearby count pill, and unify drawer/search collapse controls via a shared CollapseButton.
  • Update analytics events, nearby list cap (99 → 250), translations, and E2E/unit tests to match the new behavior.

Reviewed changes

Copilot reviewed 26 out of 26 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/merchant-list-panel.spec.ts Updates E2E assertions for the single-input + mobile peek sheet behavior.
src/routes/map/components/NearbyCountPill.svelte Adds reusable “nearby count” pill component for floating/peek UI.
src/routes/map/components/MerchantListPanel.svelte Reworks panel into a mobile bottom sheet with gestures; removes mode toggle; integrates count pill.
src/routes/map/components/MerchantDrawerMobile.svelte Replaces custom collapse button markup with shared CollapseButton.
src/routes/map/components/MapSearchBar.svelte Simplifies floating bar to single input + optional nearby count pill (desktop-only).
src/routes/map/+page.svelte Updates search flow (≥3 chars worldwide), abort behavior, initial nearby population, attribution compacting, and desktop-only floating bar.
src/lib/utils.ts Replaces nearby count formatter with pill-oriented formatNearbyPillCount.
src/lib/utils.test.ts Adds unit tests for formatNearbyPillCount.
src/lib/merchantListStore.test.ts Updates expectations for new nearby list cap (250).
src/lib/i18n/locales/bg.json Updates search strings for single-input + nearby count label.
src/lib/i18n/locales/de.json Updates search strings for single-input + nearby count label.
src/lib/i18n/locales/en.json Updates search strings for single-input + nearby count label.
src/lib/i18n/locales/es.json Updates search strings for single-input + nearby count label.
src/lib/i18n/locales/fr.json Updates search strings for single-input + nearby count label.
src/lib/i18n/locales/it.json Updates search strings for single-input + nearby count label.
src/lib/i18n/locales/nl.json Updates search strings for single-input + nearby count label.
src/lib/i18n/locales/pt-BR.json Updates search strings for single-input + nearby count label.
src/lib/i18n/locales/ru.json Updates search strings for single-input + nearby count label.
src/lib/drawerGestureUtils.ts Extends snap logic to support a custom peek height.
src/lib/drawerGestureUtils.test.ts Adds coverage for custom peek-height snap behavior.
src/lib/drawerGestureController.ts Adds options for peek height, dismiss disabling, and per-surface analytics events.
src/lib/drawerConfig.ts Adds SEARCH_SHEET_PEEK_HEIGHT.
src/lib/constants.ts Raises nearby list cap to 250 and updates docs/comments accordingly.
src/lib/analytics.ts Exports EventName and adds new search-sheet events; removes toggle events.
src/components/SearchInput.svelte Adds filled variant and pointer-events handling for trailing slot.
src/components/CollapseButton.svelte Adds shared collapse caret button for drawers/sheets.

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

Comment thread tests/merchant-list-panel.spec.ts Outdated
Comment thread tests/merchant-list-panel.spec.ts Outdated
Comment thread src/routes/map/components/MapSearchBar.svelte
Comment thread src/routes/map/components/MerchantListPanel.svelte
Comment thread src/routes/map/+page.svelte
escapedcat and others added 3 commits June 15, 2026 11:16
- e2e: assert the removed scope radios are absent via toHaveCount(0)
  (both Worldwide + Nearby) instead of not.toBeVisible(), which also
  passes for a merely-hidden element
- MapSearchBar: correct the stale comment — there's no scope toggle in
  the panel anymore (empty box browses nearby, typing searches worldwide)
- peek facade: drop the self-referential aria-controls (it pointed at its
  own wrapper); aria-expanded already conveys the disclosure

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The search sheet floats now — the safe-area clearance comes from the
card's bottom inset, not from adding env() to the height. Clarify both
peek-height constants.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mirror the body-branch !isLoadingList guard on the status-row branch so
the spinner takes precedence in both regions (same fix as #1067 on main,
applied here since this branch reworked the panel). Avoids a contradictory
'zoom in' link flashing next to the loading spinner.

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