Skip to content

Replace mobile search filter rails with dropdowns#1189

Merged
BigSimmo merged 2 commits into
mainfrom
codex/mobile-search-filter-dropdowns-dcbb32
Jul 25, 2026
Merged

Replace mobile search filter rails with dropdowns#1189
BigSimmo merged 2 commits into
mainfrom
codex/mobile-search-filter-dropdowns-dcbb32

Conversation

@BigSimmo

@BigSimmo BigSimmo commented Jul 25, 2026

Copy link
Copy Markdown
Owner

Summary

  • Fix cross-mode search performance findings: prescribing catalogue debounce/abort/fields=index, differentials abort/debounce, universal documents typeahead soft-timeout (750ms), shared (search-app) shell to avoid composer remount, and Answer rate-limit in-memory fallback outside production.
  • Fix Bugbot regressions: shared-shell pathname navigation (/services/dsm) syncs searchMode during render (no stale-mode paint) even when the query string is unchanged; extracted ClinicalDashboard lazy imports to stay under the maintainability budget.

RAG impact: no retrieval behaviour change — typeahead documents domain timeout and shell URL sync only; ranking formulas and full /api/search retrieval path unchanged.

Verification

  • npm run verify:pr-local — focused Vitest on touched sources (362) plus api-rate-limit / search-shell / universal / route / site-map suites green; docs:check-index OK
  • UI verification not run: full verify:ui not required for this pass; mode-home smoke via npm run ensure returned HTTP 200 for /, /services, /dsm, /documents/search, /therapy-compass, /?mode=prescribing, and /api/answer/stream returned 200 after the rate-limit fallback fix
  • Verification not run: eval:retrieval:latency / soak / live OpenAI canary — approval-gated provider work; not needed for timeout-only typeahead change

Risk and rollout

  • Risk: medium — shared layout remount change and rate-limit fallback behaviour in non-production; production Answer/upload still fail closed when the durable limiter is unavailable
  • Rollback: revert this PR; mode routes return to per-segment GlobalSearchShell layouts and prior timeout/fallback behaviour
  • Provider or production effects: None

Clinical Governance Preflight

  • Source-backed claims still require linked source verification before clinical use
  • No patient-identifiable document workflow was introduced or expanded without explicit governance approval
  • Supabase target remains [REDACTED] ([REDACTED])
  • Service-role keys and private document access remain server-only
  • Demo/synthetic content remains clearly separated from real clinical sources
  • Source metadata, review status, and outdated/unknown-source behavior remain conservative
  • Deployment classification/TGA SaMD impact was checked when clinical decision-support behavior changed

Notes

  • Prescribing list rows keep the full catalogue payload so Safety/Monitoring filters and patient alerts still see section-derived signals; keystroke storms are controlled by debounce + abort. fields=index remains for identity-only consumers (cross-mode links).
  • Live hybrid RPC cold tails remain a separate approval-gated follow-up.

Summary by CodeRabbit

  • New Features

    • Added responsive mobile filter dropdowns across search, launcher, clinical, factsheet, formulation, services, specifier, therapy, and medication views.
    • Mobile filters now support accessible labels, result counts, sorting, and selection-specific navigation or updates.
    • Improved responsive layout for search controls across phone, tablet, and desktop screen sizes.
  • Accessibility

    • Replaced mobile filter tabs with keyboard-friendly select controls and improved touch-target sizing.
  • Tests

    • Expanded responsive, accessibility, filtering, navigation, and layout coverage across affected search experiences.

@supabase

supabase Bot commented Jul 25, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project sjrfecxgysukkwxsowpy because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Mobile search result filters now use reusable accessible select controls on narrow screens across clinical, search, and navigation pages. Desktop filter controls remain available at larger breakpoints, with responsive behavior and accessibility covered by updated tests.

Changes

Mobile result filter controls

Layer / File(s) Summary
Shared mobile filter header
src/components/clinical-dashboard/search-results-header-band.tsx, tests/search-results-header-band.dom.test.tsx
Adds MobileResultFilterControl, responsive utility pairing with sorting, mobile-only filter visibility, and layout-focused DOM coverage.
Clinical result filter integrations
src/components/clinical-dashboard/differentials-home.tsx, src/components/clinical-dashboard/document-search-results.tsx, src/components/clinical-dashboard/medication-prescribing-workspace.tsx
Wires mobile selectors to differential, document source-type, and medication result filters.
Search page filter integrations
src/components/applications-launcher-page.tsx, src/components/factsheets/..., src/components/formulation/..., src/components/services/..., src/components/specifiers/..., src/components/therapy-compass/...
Adds mobile category, pattern, domain, quick-filter, family, diagnosis, topic, and availability selectors backed by state or URL navigation.
Responsive and accessibility validation
tests/ui-accessibility.spec.ts, tests/ui-smoke.spec.ts, tests/ui-tools.spec.ts, tests/ui-formulation.spec.ts, tests/ui-specifiers.spec.ts, tests/ui-stress.spec.ts, docs/branch-review-ledger.md
Updates UI coverage for select interaction, breakpoint visibility, accessibility names, sizing, navigation, and release review tracking.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant MobileResultFilterControl
  participant SearchPage
  participant SearchResultsHeaderBand
  User->>MobileResultFilterControl: select filter option
  MobileResultFilterControl->>SearchPage: invoke onChange(value)
  SearchPage->>SearchResultsHeaderBand: update filter state or route
  SearchResultsHeaderBand->>User: render filtered mobile controls and results
Loading

Possibly related PRs

Suggested labels: codex

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 title clearly summarizes the main change: replacing mobile search filter rails with dropdowns.
Description check ✅ Passed The description covers summary, verification, risk, and clinical governance sections with concrete details and completion notes.
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/mobile-search-filter-dropdowns-dcbb32

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.

@BigSimmo
BigSimmo merged commit 4eda2e0 into main Jul 25, 2026
19 checks passed
@BigSimmo
BigSimmo deleted the codex/mobile-search-filter-dropdowns-dcbb32 branch July 25, 2026 02:12

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/ui-accessibility.spec.ts (1)

406-406: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Scope the tab absence assertion to differential results.

This currently fails if any unrelated visible tab is added elsewhere on the page. Query within differentials-search-results instead.

Proposed change
-    await expect(page.getByRole("tab")).toHaveCount(0);
+    await expect(page.getByTestId("differentials-search-results").getByRole("tab")).toHaveCount(0);
🤖 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 `@tests/ui-accessibility.spec.ts` at line 406, Update the tab absence assertion
in the accessibility test to scope the getByRole("tab") query within the
differ­entials-search-results container, so unrelated tabs elsewhere on the page
do not affect the expected zero count.
🤖 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/components/services/services-navigator-page.tsx`:
- Around line 628-644: Update the mobileControls MobileResultFilterControl
options to always include the "current" reset entry, including when
activeQuickFilter is truthy, and route selecting "current" through the same
clear/reset logic used by the desktop Clear action instead of ignoring it in
onChange. Preserve the existing quick-filter options and labels for non-reset
values.

---

Nitpick comments:
In `@tests/ui-accessibility.spec.ts`:
- Line 406: Update the tab absence assertion in the accessibility test to scope
the getByRole("tab") query within the differ­entials-search-results container,
so unrelated tabs elsewhere on the page do not affect the expected zero count.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: fa019b13-0e2e-4d8e-830c-fbe2e91c7ea2

📥 Commits

Reviewing files that changed from the base of the PR and between fc9d162 and 6522bc4.

📒 Files selected for processing (18)
  • docs/branch-review-ledger.md
  • src/components/applications-launcher-page.tsx
  • src/components/clinical-dashboard/differentials-home.tsx
  • src/components/clinical-dashboard/document-search-results.tsx
  • src/components/clinical-dashboard/medication-prescribing-workspace.tsx
  • src/components/clinical-dashboard/search-results-header-band.tsx
  • src/components/factsheets/factsheets-search-page.tsx
  • src/components/formulation/formulation-home-page.tsx
  • src/components/services/services-navigator-page.tsx
  • src/components/specifiers/specifiers-home-page.tsx
  • src/components/therapy-compass/screens/search-screen.tsx
  • tests/search-results-header-band.dom.test.tsx
  • tests/ui-accessibility.spec.ts
  • tests/ui-formulation.spec.ts
  • tests/ui-smoke.spec.ts
  • tests/ui-specifiers.spec.ts
  • tests/ui-stress.spec.ts
  • tests/ui-tools.spec.ts

Comment on lines +628 to +644
mobileControls={
<MobileResultFilterControl
label="Filter"
ariaLabel="Apply a quick service filter"
testId="service-quick-filter-select"
value={activeQuickFilter?.query ?? "current"}
options={[
...(activeQuickFilter
? []
: [{ value: "current", label: query.trim() ? "Current search" : "All services", disabled: true }]),
...serviceQuickFilters.map((filter) => ({ value: filter.query, label: filter.label })),
]}
onChange={(value) => {
if (value !== "current") applyServiceQuery(value);
}}
/>
}

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Mobile select loses the "Clear quick filters" affordance once a quick filter matches.

When activeQuickFilter is truthy, the "current" sentinel entry is entirely omitted from options, so there is no entry left that resets to "All services." Previously this whole row (including the desktop "Clear" button) was visible on mobile; now filterControls is hidden below sm (because mobileControls is set), and the replacement mobile select never offers the reset action the desktop Clear button provides. Every other mobile select in this PR (documents/differentials/medications/formulation/specifiers/therapy/factsheets/applications) keeps an unconditional "all"/reset entry — this is the only page missing it, and it's a real capability the mobile ribbon has to lose here, not a stylistic difference.

🐛 Proposed fix: keep an unconditional reset entry and route it to the same clear logic as the desktop button
             <MobileResultFilterControl
                 label="Filter"
                 ariaLabel="Apply a quick service filter"
                 testId="service-quick-filter-select"
-                value={activeQuickFilter?.query ?? "current"}
+                value={activeQuickFilter?.query ?? "current"}
                 options={[
-                  ...(activeQuickFilter
-                    ? []
-                    : [{ value: "current", label: query.trim() ? "Current search" : "All services", disabled: true }]),
+                  activeQuickFilter
+                    ? { value: "current", label: "All services" }
+                    : { value: "current", label: query.trim() ? "Current search" : "All services", disabled: true },
                   ...serviceQuickFilters.map((filter) => ({ value: filter.query, label: filter.label })),
                 ]}
                 onChange={(value) => {
-                  if (value !== "current") applyServiceQuery(value);
+                  if (value === "current") {
+                    if (activeQuickFilter) setLocalQuery({ urlQuery, value: "" });
+                  } else {
+                    applyServiceQuery(value);
+                  }
                 }}
             />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
mobileControls={
<MobileResultFilterControl
label="Filter"
ariaLabel="Apply a quick service filter"
testId="service-quick-filter-select"
value={activeQuickFilter?.query ?? "current"}
options={[
...(activeQuickFilter
? []
: [{ value: "current", label: query.trim() ? "Current search" : "All services", disabled: true }]),
...serviceQuickFilters.map((filter) => ({ value: filter.query, label: filter.label })),
]}
onChange={(value) => {
if (value !== "current") applyServiceQuery(value);
}}
/>
}
mobileControls={
<MobileResultFilterControl
label="Filter"
ariaLabel="Apply a quick service filter"
testId="service-quick-filter-select"
value={activeQuickFilter?.query ?? "current"}
options={[
activeQuickFilter
? { value: "current", label: "All services" }
: { value: "current", label: query.trim() ? "Current search" : "All services", disabled: true },
...serviceQuickFilters.map((filter) => ({ value: filter.query, label: filter.label })),
]}
onChange={(value) => {
if (value === "current") {
if (activeQuickFilter) setLocalQuery({ urlQuery, value: "" });
} else {
applyServiceQuery(value);
}
}}
/>
}
🤖 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/components/services/services-navigator-page.tsx` around lines 628 - 644,
Update the mobileControls MobileResultFilterControl options to always include
the "current" reset entry, including when activeQuickFilter is truthy, and route
selecting "current" through the same clear/reset logic used by the desktop Clear
action instead of ignoring it in onChange. Preserve the existing quick-filter
options and labels for non-reset values.

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.

1 participant