Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/branch-review-ledger.md
Original file line number Diff line number Diff line change
Expand Up @@ -794,4 +794,6 @@ This file is append-only. Never rewrite or delete an existing review record; app
| 2026-07-24 | cursor/pr-queue-hygiene-72ec | pending-push | PR queue hygiene | Add pr-branch-sync workflow + sync:pr-branches helper; bump postcss to clear npm audit high; document anti-churn guidance in AGENTS/process-hardening/pr-babysit/run-pr. | check:github-actions PASS; docs:check-scripts/index PASS; vitest sync-open-pr-branches 3/3; npm audit high clean; no provider-backed checks run |
| 2026-07-24 | codex/apply-phone-layout-to-all-home-pages (PR #1124) | pending | Babysit: ledger dedupe + merge readiness | Before: Static PR failed on exact duplicate ledger rows after main sync. After: removed duplicate rows; squash auto-merge armed. | check:branch-review-ledger PASS; no provider-backed checks run |
| 2026-07-25 | cursor/pdf-crop-malformed-repro-9b3e (PR #1176) | c31543f5b5862dbe6911079029f28500a6af2a59 | Run PR sweep: CI fix + threads + drift | Before: GitHub reported DIRTY and Static PR checks failed formatting `docs/outstanding-issues.md` and `tests/pdf-extractor.test.ts`; 0 unresolved threads. After: merged current `origin/main` cleanly and formatted both failing files. | Prettier check pass; `git diff --check` pass; no provider-backed checks run. |
| 2026-07-25 | codex/search-results-filters-20260725 | 88131e7267efd33059766dec80355a9246fbb2bf | Search result filters and document Sources merge-readiness review | APPROVE. No P0-P2 finding after current-main sync. Documents open Sources as an on-screen filtering surface with source-type controls; the shared results ribbon is applied across search pages. Highest residual risk: unusual real-content combinations may alter perceived density, while responsive, forced-colors, focus, and overflow paths are browser-covered. RAG impact: no retrieval behaviour change - UI controls and source browsing only. | `npm run verify:ui` pass 268/268; `npm run verify:cheap` pass (377 files, 3340 passed, 1 skipped); post-sync `npm run verify:pr-local` pass (378 files, 3349 passed, 1 skipped, production build, bundle-secret scan, offline RAG fixtures); `npm run check:production-readiness` pass with OPENAI_SAFETY_IDENTIFIER_SECRET warning; no live/provider-backed app checks run. |

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.

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Do not record an unrun UI gate as passed.

This row says npm run verify:ui passed 268/268, but the PR objectives say full UI verification was not run. Remove that claim or mark the gate as not run before using this as merge-readiness evidence. As per PR objectives, “Full UI verification … [was] not run.”

🤖 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 `@docs/branch-review-ledger.md` at line 797, Update the 2026-07-25 ledger entry
for codex/search-results-filters-20260725 so it no longer claims npm run
verify:ui passed 268/268. Mark the UI gate as not run, consistent with the PR
objectives, while preserving the other verification results and merge-readiness
details.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Correct the future-dated review records. Both newly added entries are dated July 25, 2026, which is after the current review date of July 24, 2026.

  • docs/branch-review-ledger.md#L797-L797: use the actual review date or defer this record until July 25, 2026.
  • docs/branch-review-ledger.md#L799-L799: use the actual review date or defer this record until July 25, 2026.
📍 Affects 1 file
  • docs/branch-review-ledger.md#L797-L797 (this comment)
  • docs/branch-review-ledger.md#L799-L799
🤖 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 `@docs/branch-review-ledger.md` at line 797, Correct both future-dated review
records in docs/branch-review-ledger.md: the entries at lines 797-797 and
799-799 must use the actual review date of July 24, 2026, or be deferred until
July 25, 2026. Update only the date fields while preserving the remaining review
details.

| 2026-07-25 | `codex/therapy-page-polish-ad78b4` | `157559aa0678f02de09c14f66d544b62a5138c4a` | Targeted release review: Therapy naming, centred navigation, and white canvas | APPROVE. No P0-P3 findings. The production Therapy route consistently uses the title Therapy, the shared page background token, and a centred overflow-safe section navigation. The latest `origin/main` merge was clean and retained both upstream responsive/home-composer assertions. Highest residual risk is visual drift at an untested browser engine; exact desktop and phone Chromium measurements were stable. | Focused Vitest 40/40; pre-sync `verify:cheap` 378 files / 3342 passed / 1 skipped; pre-sync `verify:ui` passed; integrated runtime, Prettier, lint, and typecheck passed; integrated Vitest was interrupted by the shared heavyweight-test queue after an independent 378-file / 3342-pass run. Required hosted checks must pass on the published exact head before merge. No clinical/provider workflow ran. |
| 2026-07-25 | codex/search-results-filters-20260725 (PR #1184) | 8f74d8bd40810ede34ad4b155973b598c1be0101 | Superseding merge-readiness review after Sources focus repair | APPROVE. Supersedes the 88131e72 row: the automated P2 showed a transient Daily Actions menu item could disconnect before Sources restored focus. Closing Sources now falls back after unmount to the currently rendered action trigger, and the regression requires the visible Documents trigger to own focus. No P0-P2 finding remains. RAG impact: no retrieval behaviour change - UI focus restoration only. | Post-fix isolated production Chromium 1/1; post-current-main local Chromium 1/1; `npm run verify:cheap` pass (378 files, 3350 passed, 1 skipped); targeted Prettier and ESLint pass; required hosted checks must rerun on the published exact head; no live clinical/provider workflow ran. |
6 changes: 3 additions & 3 deletions src/app/(search-app)/documents/search/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ export default function DocumentsSearchRoute() {
return (
<section className="mx-auto flex min-h-[55dvh] max-w-3xl flex-col items-center justify-center px-4 py-12 text-center">
<p className="text-xs font-bold uppercase tracking-[0.08em] text-[color:var(--clinical-accent)]">
Indexed library
Indexed sources
</p>
<h1 className="mt-3 text-3xl font-semibold text-[color:var(--text-heading)]">Search clinical documents</h1>
<p className="mt-3 max-w-xl text-sm leading-6 text-[color:var(--text-muted)]">
Enter a query in the Documents composer to search the live indexed library. Results open the source document at
the matching page and passage.
Enter a query in the Documents composer to search the indexed sources. Results open the source document at the
matching page and passage.
</p>
</section>
);
Expand Down
38 changes: 24 additions & 14 deletions src/components/ClinicalDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,7 @@ export function ClinicalDashboard({
const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
const [sidebarCollapsed, setSidebarCollapsed] = useSidebarCollapsed();
const [documentsDrawerOpen, setDocumentsDrawerOpen] = useState(false);
const documentsDrawerReturnFocusRef = useRef<HTMLElement | null>(null);
const [documentScopeOpen, setDocumentScopeOpen] = useState(false);
const [documentsDrawerMode, setDocumentsDrawerMode] = useState<DocumentDrawerMode>("library");
const [uploadDrawerOpen, setUploadDrawerOpen] = useState(false);
Expand Down Expand Up @@ -2659,18 +2660,13 @@ export function ClinicalDashboard({
}

function openDocumentsDrawer(mode: DocumentDrawerMode) {
documentsDrawerReturnFocusRef.current =
document.activeElement instanceof HTMLElement ? document.activeElement : null;
closeDashboardTransientSurfaces("documents");
setSearchMode("documents");
setDocumentDrawerStatusFilter("indexed");
setDocumentsDrawerMode(mode);
setDocumentsDrawerOpen(true);
if (window.matchMedia("(min-width: 1024px)").matches) {
window.requestAnimationFrame(() => {
document
.getElementById("dashboard-documents-drawer")
?.scrollIntoView({ block: "start", behavior: resolveScrollBehavior() });
});
}
}

function openRecentDocuments() {
Expand All @@ -2690,7 +2686,7 @@ export function ClinicalDashboard({
openDocumentsDrawer("library");
setActionNotice({
tone: "warning",
message: "Upload and indexing tools are admin-only. Use the source library to open indexed documents.",
message: "Upload and indexing tools are admin-only. Use Sources to open indexed documents.",
});
return;
}
Expand Down Expand Up @@ -3204,7 +3200,7 @@ export function ClinicalDashboard({
? "Source PDFs"
: documentsDrawerIsAdmin
? "Document admin"
: "Source library";
: "Sources";
const documentsDrawerSummary = dashboardDataLoading
? "Loading indexed document status."
: documentsDrawerMode === "recent"
Expand All @@ -3215,14 +3211,14 @@ export function ClinicalDashboard({
? `${indexedDocumentTotal.toLocaleString()} indexed documents available.`
: "Search and open indexed clinical sources.";
const documentsDrawerMobileSummary = dashboardDataLoading
? "Loading library"
? "Loading sources"
: documentsDrawerMode === "recent"
? "Recent sources"
: documentsDrawerMode === "source"
? "PDF sources"
: documentsDrawerIsAdmin
? "Admin"
: "Library";
: "Sources";
const DocumentsDrawerIcon =
documentsDrawerMode === "recent"
? Clock3
Expand All @@ -3231,7 +3227,7 @@ export function ClinicalDashboard({
: documentsDrawerIsAdmin
? UploadCloud
: FolderOpen;
const drawerGroupTitle = uploadDrawerOpen || documentsDrawerIsAdmin ? "Library and admin" : "Sources";
const drawerGroupTitle = uploadDrawerOpen || documentsDrawerIsAdmin ? "Sources and admin" : "Sources";

// Stable-identity handlers for the React.memo children (StagedAnswerResultSurface,
// DocumentSearchResultsPanel). These close over the draft `query` or call the
Expand All @@ -3247,6 +3243,19 @@ export function ClinicalDashboard({
const handleDocumentTagSearch = useEventCallback(handleTagSearch);
const handleOpenRecentDocuments = useEventCallback(openRecentDocuments);
const handleOpenSourceLibrary = useEventCallback(openSourceLibrary);
const handleDocumentsDrawerOpenChange = useEventCallback((nextOpen: boolean) => {
setDocumentsDrawerOpen(nextOpen);
if (nextOpen) return;

const returnTarget = documentsDrawerReturnFocusRef.current;
window.requestAnimationFrame(() => {
const fallbackTarget = Array.from(
document.querySelectorAll<HTMLElement>('button[aria-haspopup="menu"][aria-label$=" options"]'),
).find((element) => element.isConnected && element.getClientRects().length > 0);
const focusTarget = returnTarget?.isConnected ? returnTarget : fallbackTarget;
focusTarget?.focus({ preventScroll: true });
});
});
Comment on lines +3246 to +3258

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 | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for other elements matching the fallback selector pattern.
rg -nP 'aria-haspopup="menu"' -A2 -B2 src/components
rg -nP 'aria-label=.*options"' -A2 -B2 src/components

Repository: BigSimmo/Database

Length of output: 2178


🏁 Script executed:

#!/bin/bash
set -u

echo "== ClinicalDashboard outline (focused on drawer/search refs/labels) =="
ast-grep outline src/components/ClinicalDashboard.tsx --match 'handleDocumentsDrawerOpenChange\|documentsDrawerReturnFocusRef\|Documents drawer\|search' 2>/dev/null | sed -n '1,220p' || true

echo
echo "== Relevant ClinicalDashboard sections =="
sed -n '2600,2720p' src/components/ClinicalDashboard.tsx
echo
sed -n '3220,3280p' src/components/ClinicalDashboard.tsx
echo
sed -n '3500,3750p' src/components/ClinicalDashboard.tsx

echo
echo "== Other aria-haspopup menu labels =="
python3 - <<'PY'
from pathlib import Path
import re
for p in Path('src/components').rglob('*.tsx'):
    txt = p.read_text(errors='ignore')
    for m in re.finditer(r'<button[^>]*aria-haspopup="menu"[^>]+>(?:.</+?)</button>|<button[^>]*(?:.</+?)>', txt, flags=re.S|re.I):
        t = m.group(0)
        if 'aria-haspopup="menu"' in t:
            label = re.search(r'aria-label=`?\$?\{?([^"'\'}]+)`|aria-label="([^"]+)"', t)
            print(f'{p}:{txt[:m.start()].count(chr(10))+1}: label={label.group(1) or label.group(2)} text={t.strip()[:200]}')
PY

Repository: BigSimmo/Database

Length of output: 21013


🏁 Script executed:

#!/bin/bash
set -u

echo "== Locate button trigger for DocumentsDrawerOpenChange =="
rg -n 'handleDocumentsDrawerOpenChange|onOpenChange|Documents.*Drawer|Documents.*Options|Sources.*Options|aria-label=.*[Oo]ption|aria-label=.*Document|aria-label=.*Library' src/components/ClinicalDashboard.tsx | sed -n '1,240p'

echo
echo "== Search exact/dynamic options label generators =="
python3 - <<'PY'
from pathlib import Path
import re

for p in [Path('src/components/ClinicalDashboard.tsx')]:
    text = p.read_text(errors='ignore')
    lines = text.splitlines()
    needles = ['aria-haspopup="menu"', 'More actions', 'options', 'Documents']
    for i, line in enumerate(lines, 1):
        if any(n in line for n in needles) and i < 4100:
            start = max(1, i-4)
            end = min(len(lines), i+4)
            print(f'--- {p}:{i} ---')
            for j in range(start, end+1):
                print(f'{j:5}: {lines[j-1]}')
            # Also search for string templates producing labels containing options within nearby 80 lines
            m = re.search(r'(\w+[\s.]*[=:]\s*)([^;`\n]*)', text, pos=max(0, text.find(line)-800), count=1)
PY

echo
echo "== Find JSX for DocumentsDrawerIcon area in ClinicalDashboard.tsx =="
rg -n 'DocumentsDrawerIcon|drawerGroupTitle|Sources and admin|Sources' src/components/ClinicalDashboard.tsx | sed -n '1,240p'

echo
echo "== Programmatic scan: aria-haspopup menu buttons with label-like attributes nearby =="
python3 - <<'PY'
from pathlib import Path
import re

p = Path('src/components/ClinicalDashboard.tsx')
text = p.read_text(errors='ignore')
lines = text.splitlines()
for pat in re.finditer(r'<button[^>]*aria-haspopup="menu"[^>]*>(?:.(?!</button>))*</button>', text, flags=re.S):
    line_no = text[:pat.start()].count('\n') + 1
    seg = pat.group(0)
    label = re.search(r'aria-label=(?:`.*?`|".*?")', seg)
    print(f'{path}:{line_no}: aria-label={label.group(0) if m else None}')
PY

Repository: BigSimmo/Database

Length of output: 3815


🏁 Script executed:

#!/bin/bash
set -u

echo "== ClinicalDashboard.tsx around Documents drawer JSX =="
sed -n '3820,3875p' src/components/ClinicalDashboard.tsx

echo
echo "== ClinicalDashboard.tsx component definitions/usages for DocumentsDrawerIcon =="
rg -n 'function DocumentsDrawerIcon|const DocumentsDrawerIcon|<DocumentsDrawerIcon|aria-label|onOpenChange' src/components/ClinicalDashboard.tsx | sed -n '1,220p'

echo
echo "== All aria-haspopup menu buttons across src/components, with line context =="
python3 - <<'PY'
from pathlib import Path
import re

for p in Path('src/components').rglob('*.tsx'):
    text = p.read_text(errors='ignore')
    for m in re.finditer(r'<button[^>]*aria-haspopup="menu"', text):
        line = text[:m.start()].count('\n') + 1
        # scan until </button> or next newline without button close, plus a bit of next lines
        end = text.find('>', m.start())
        seg = text[m.start(): (text.find(chr(10), end) if end != -1 else end) + 40 or len(text)]
        seg = seg.replace('\n', ' ')
        print(f'{p}:{line}: {seg.strip()[:320]}')
PY

Repository: BigSimmo/Database

Length of output: 5544


Scope the fallback focus-restore target to the actual drawer trigger.

The current fallback queries the whole document for button[aria-haspopup="menu"][aria-label$=" options"], but the Documents drawer trigger has no matching aria-label, and the only options menu button in src/components is a favourites action menu labeled {item.title}. If documentsDrawerReturnFocusRef or an aria-label$=" options" target is unavailable, this can restore focus to a wrong control; use a target tied to the drawer trigger, such as the relevant data-*/id/container data.

🤖 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/ClinicalDashboard.tsx` around lines 3246 - 3258, Update
handleDocumentsDrawerOpenChange so its fallback focus target is resolved from
the Documents drawer trigger using the trigger’s existing data attribute, id, or
containing element. Remove the document-wide aria-label$=" options" query, and
preserve the returnTarget preference and preventScroll focus behavior.

const handleOpenSourcePdfBrowser = useEventCallback(openSourcePdfBrowser);
const handleCopyAnswer = useEventCallback(() => {
copyText("answer", answerRenderModel?.copyText || safeAnswerText || answer?.answer || "");
Expand Down Expand Up @@ -3838,8 +3847,9 @@ export function ClinicalDashboard({
summary={documentsDrawerSummary}
mobileSummary={documentsDrawerMobileSummary}
open={documentsDrawerOpen}
onOpenChange={setDocumentsDrawerOpen}
sheetBreakpoint="lg"
onOpenChange={handleDocumentsDrawerOpenChange}
sheetBreakpoint={documentsDrawerIsAdmin ? "lg" : "all"}
sheetReturnFocusRef={documentsDrawerReturnFocusRef}
Comment thread
BigSimmo marked this conversation as resolved.
sheetHeaderLeading={
<span className="grid h-10 w-10 place-items-center rounded-xl border border-[color:var(--clinical-accent-border)] bg-[color:var(--clinical-accent-soft)] text-[color:var(--clinical-accent)] shadow-[var(--shadow-inset)]">
<DocumentsDrawerIcon className="h-5 w-5" aria-hidden="true" />
Expand Down
45 changes: 31 additions & 14 deletions src/components/applications-launcher-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
import { type FormEvent, useMemo, useState } from "react";

import { ModeHomeHero, ModeHomeVerificationFooter } from "@/components/mode-home-template";
import { SearchResultsHeaderBand } from "@/components/clinical-dashboard/search-results-header-band";
import { useSearchCommand } from "@/components/clinical-dashboard/search-command-context";
import { useFavouritesAccess } from "@/components/clinical-dashboard/use-favourites-access";
import { cn, toneInfo, toneSuccess, toneWarning } from "@/components/ui-primitives";
Expand Down Expand Up @@ -829,21 +830,37 @@ export function ApplicationsLauncherWorkspace({
data-testid="tools-all-tools"
className="mx-auto mt-8 grid max-w-[86rem] grid-cols-1 gap-4 sm:mt-10"
>
<div className="flex flex-col gap-3 lg:flex-row lg:items-end lg:justify-between">
<div className="text-left">
<h2 className="text-lg font-extrabold text-[color:var(--text-heading)]">{copy.allSectionLabel}</h2>
</div>
<div className="flex items-center gap-3">
<FilterTabs
activeFilter={effectiveFilter}
onFilterChange={setActiveFilter}
canAccessFavourites={canAccessFavourites}
/>
<p className="hidden min-h-10 items-center rounded-lg px-1 text-xs font-bold text-[color:var(--text-muted)] lg:inline-flex">
Sorted A to Z
</p>
{normalizedQuery ? (
<SearchResultsHeaderBand
modeId="tools"
query={query}
matchCount={filteredApps.length}
filterLabel="Filter tools by category"
filterControls={
<FilterTabs
activeFilter={effectiveFilter}
onFilterChange={setActiveFilter}
canAccessFavourites={canAccessFavourites}
/>
}
/>
) : (
<div className="flex flex-col gap-3 lg:flex-row lg:items-end lg:justify-between">
<div className="text-left">
<h2 className="text-lg font-extrabold text-[color:var(--text-heading)]">{copy.allSectionLabel}</h2>
</div>
<div className="flex items-center gap-3">
<FilterTabs
activeFilter={effectiveFilter}
onFilterChange={setActiveFilter}
canAccessFavourites={canAccessFavourites}
/>
<p className="hidden min-h-10 items-center rounded-lg px-1 text-xs font-bold text-[color:var(--text-muted)] lg:inline-flex">
Sorted A to Z
</p>
</div>
</div>
</div>
)}

<div id="launcher-results-panel" role="group" aria-label={resultsPanelLabel} className="grid grid-cols-1 gap-4">
{filteredApps.length === 0 ? (
Expand Down
13 changes: 9 additions & 4 deletions src/components/clinical-dashboard/dashboard-shell.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
"use client";

import { BookOpen, ChevronDown, type LucideIcon } from "lucide-react";
import { useCallback, useEffect, useRef, useState, type CSSProperties, type ReactNode } from "react";
import { useCallback, useEffect, useRef, useState, type CSSProperties, type ReactNode, type RefObject } from "react";

import { Sheet, type SheetMobileSize } from "@/components/ui/sheet";
import { clinicalDivider, cn, iconTilePremium, navPill, panelSubtle, textMuted } from "@/components/ui-primitives";

const sheetMediaQueries = {
sm: "(max-width: 639px)",
lg: "(max-width: 1023px)",
all: "(min-width: 0px)",
} as const;

type UtilityDrawerSheetBreakpoint = keyof typeof sheetMediaQueries;
Expand Down Expand Up @@ -87,6 +88,7 @@ export function UtilityDrawer({
sheetDescription,
sheetBreakpoint = "sm",
sheetMobileSize,
sheetReturnFocusRef,
}: {
id?: string;
title: string;
Expand All @@ -113,13 +115,16 @@ export function UtilityDrawer({
sheetDescription?: string | null;
sheetBreakpoint?: UtilityDrawerSheetBreakpoint;
sheetMobileSize?: SheetMobileSize;
sheetReturnFocusRef?: RefObject<HTMLElement | null>;
}) {
const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);
const [usesSheet, setUsesSheet] = useState(false);
const mobileTriggerRef = useRef<HTMLButtonElement>(null);
const open = controlledOpen ?? uncontrolledOpen;
const sheetTriggerClassName = sheetBreakpoint === "lg" ? "lg:hidden" : "sm:hidden";
const inlineDrawerClassName = sheetBreakpoint === "lg" ? "hidden lg:block" : "hidden sm:block";
const sheetTriggerClassName =
sheetBreakpoint === "all" ? "block" : sheetBreakpoint === "lg" ? "lg:hidden" : "sm:hidden";
const inlineDrawerClassName =
sheetBreakpoint === "all" ? "hidden" : sheetBreakpoint === "lg" ? "hidden lg:block" : "hidden sm:block";
Comment on lines +118 to +127

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 | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect the Sheet component's backdrop / focus-trap / inert handling.
fd -e tsx sheet.tsx --exec cat -n {}

Repository: BigSimmo/Database

Length of output: 20273


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## Locate dashboard shell and clinical dashboard"
fd -e tsx 'dashboard-shell\.tsx|ClinicalDashboard\.tsx' .

echo
echo "## dashboard-shell relevant sections"
file="$(fd -e tsx 'dashboard-shell\.tsx' . | head -n 1)"
echo "FILE=$file"
wc -l "$file"
sed -n '1,260p' "$file" | cat -n

echo
echo "## ClinicalDashboard UtilityDrawer usages"
cli="$(fd -e tsx 'ClinicalDashboard\.tsx' . | head -n 1)"
if [ -n "${cli:-}" ]; then
  rg -n "UtilityDrawer|UtilityDrawerProps|useDrawer|documentsDrawerOpen|documentsDrawer|sheetBreakpoint|sheetReturnFocusRef" "$cli"
  sed -n '1,260p' "$cli" | cat -n
fi

Repository: BigSimmo/Database

Length of output: 25184


🌐 Web query:

ShadCN Sheet component React Dialog trigger visible outside modal does not block click behind backdrop

💡 Result:

The Shadcn Sheet component is a wrapper around the Radix UI Dialog primitive [1][2]. By default, it functions as a modal, which enforces specific behaviors: it blocks interaction with the rest of the application, captures focus, and prevents clicks on the background [3][4]. To allow interaction with the application while the Sheet is open—or to keep a trigger outside the modal interactive—you must disable the modal behavior [1][5]. Key solution: Set modal={false} on the <Sheet> component [1][5]. This single change achieves three primary outcomes: 1. It allows you to interact with elements outside the Sheet (including buttons or other triggers) while it remains open [5]. 2. It prevents the Sheet from automatically closing when you click outside of it [5]. 3. It removes the default backdrop overlay, making the background fully interactive [5]. If you need to customize these behaviors individually (e.g., keeping the backdrop but allowing interaction, or disabling the "click outside to close" behavior while remaining a modal), you can use the following props on the <SheetContent> component [6][4]: - To prevent closing on outside clicks: Use onPointerDownOutside={(e) => e.preventDefault()} [6][4]. (Note: onPointerDownOutside is generally more reliable than onInteractOutside for mobile touch events [6].) - To keep the backdrop but allow interaction: Using modal={false} is usually sufficient for most use cases [5]. If you face persistent issues with pointer-events: none being applied to the body, ensure that nested components are not causing conflicts with Radix's dismissible-layer [7][8]. If you are encountering a scenario where a button outside the Sheet is not clickable even after the Sheet is open, and modal={false} does not resolve it, ensure your button has a high enough z-index and that pointer-events-auto is explicitly set on it, as some Radix implementations may inadvertently apply pointer-events: none to the background during specific states [9][8].

Citations:


🌐 Web query:

Radix DialogBackdrop z-index aria-hidden "aria-hidden" React DOM "pointer-events"

💡 Result:

Radix UI's Dialog component manages modality and accessibility by dynamically manipulating DOM styles and attributes, which can lead to specific behaviors regarding z-index, aria-hidden, and pointer-events. When a Radix Dialog is open in modal mode, it applies pointer-events: none to the body element or other parts of the document to prevent interaction with background content [1][2][3]. This is a core part of its modality logic, ensuring that users can only interact with the active dialog [4][5]. Key aspects of this implementation include: Pointer Events: The DialogContent component, when modal, utilizes disableOutsidePointerEvents={true} [1][3]. This sets pointer-events: none on the document body to block interaction outside the dialog [2][3]. If you encounter issues where this does not revert correctly or conflicts with third-party components (like external modal libraries or complex dropdowns), it is often due to version mismatches between packages or specific nested interaction patterns [2][6]. If a third-party component is not responding, it may require setting pointer-events: auto on that specific component's container [1][3]. Aria-hidden: Radix Dialog handles accessibility by ensuring the content behind the dialog is inert to screen readers [7][5]. It utilizes aria-hidden attributes to hide the background content [2]. Recent updates have addressed issues where "focus guards"—invisible elements used by Radix to trap keyboard focus—were incorrectly receiving aria-hidden attributes, which caused accessibility validation errors [8]. Z-index: Radix Primitives generally do not enforce a global z-index strategy, leaving it to the developer to manage via CSS [1][3]. Developers typically assign a high z-index to the DialogContent and DialogOverlay to ensure they render above other page content [1][3]. Because Radix components are often rendered via a Portal into the body, they exist outside the standard DOM flow of the parent component, necessitating careful management of stacking contexts [4][5]. For complex scenarios, such as nested modals or integrating with third-party libraries, Radix recommends keeping packages aligned to the latest versions [6] and manually adjusting pointer-events on external containers when necessary [1][3]. If accessibility errors occur, ensure you are using the latest version of the primitives, as fixes for focus-guard-related aria-hidden issues have been implemented [8].

Citations:


🌐 Web query:

Radix Dialog Modal aria-hidden disabled pointer-events: none body focus trap trigger button outside modal

💡 Result:

Radix UI Dialog manages focus trapping and body interaction as part of its default modal behavior to ensure WAI-ARIA compliance [1][2]. Focus Trapping and Trigger Interaction: When the Dialog is in modal mode (modal={true}), Radix automatically traps focus within the dialog content [1][2]. Upon closing the dialog, Radix is designed to automatically return focus to the trigger button that opened it [1][3]. Issues where the trigger does not receive focus or keyboard navigation escapes the dialog often stem from the component configuration or the use of modal={false}, which disables certain trapping features [4]. Body Inertia and Pointer Events: To prevent interactions with the background when a modal is open, Radix applies pointer-events: none to the body element [5][6]. This style is managed automatically by the component [5]. If pointer-events: none persists on the body after a dialog closes, it is often due to an interrupted lifecycle (e.g., closing animations or unmounting before the cleanup effect finishes) [5][6]. Developers encountering this persistence have used onCloseAutoFocus to manually reset the property [5]: <Dialog.Content onCloseAutoFocus={(event) => { event.preventDefault; document.body.style.pointerEvents = ''; }} /> Aria-Hidden and Focus Guards: Radix manages the aria-hidden state of the application container to ensure screen readers focus only on the modal [7][8]. The library also utilizes "focus guards"—invisible elements placed before and after the modal—to ensure that if a user tabs out of the dialog, the focus is immediately looped back into the trap [4][8]. If accessibility testing tools report aria-hidden errors, they may be flagging these guards if they are incorrectly positioned or improperly attributed; however, recent patches have aimed to resolve these specific focus-guard attribute conflicts [8].

Citations:


🌐 Web query:

Radix Dialog focus trap body pointer-events none return focus trigger outside modal open trigger clicks inside modal

💡 Result:

In Radix UI Dialog components, the pointer-events: none style is intentionally applied to the body element when the dialog is in modal={true} mode to prevent interaction with background content while the focus is trapped within the modal [1][2]. If you are experiencing issues where this style persists after the dialog closes, or if it interferes with triggers located inside the modal (or other third-party modals), consider the following information: 1. Persistence Issues: While intended to be removed automatically, users have reported edge cases where pointer-events: none remains on the body after a dialog closes [3][4][5]. If this occurs, a common workaround involves using onCloseAutoFocus on the Dialog.Content component to explicitly reset the style: <Dialog.Content onCloseAutoFocus={(event) => { event.preventDefault; // Optional: prevent default focus return if custom logic is needed document.body.style.pointerEvents = ''; }} > 2. Triggering Outside or Nested Modals: The modal={true} prop enforces strict focus and pointer-event trapping [2]. If you require interaction with elements outside the modal or need to open other modals (such as third-party services) from within the dialog, consider setting modal={false} on the Radix Dialog [1][2]. This disables the automatic pointer-events: none and focus-trap behavior, allowing for more flexible interaction [1]. 3. Trigger Focus: Radix UI Dialog is designed to automatically return focus to the Trigger when the modal is closed [1][2]. If the trigger is not receiving focus, ensure you are not obstructing the default onCloseAutoFocus behavior [2]. You can customize the return focus behavior by using the onCloseAutoFocus event handler on Dialog.Content [1][2]. For complex scenarios where you must keep modal={true} but need specific elements to remain interactive, you may need to manually set pointer-events: auto on those specific third-party containers [6]. Always ensure you are using the latest version of the Radix Primitives, as many pointer-event cleanup behaviors have been refined in recent releases [4][5].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

cli="$(fd -e tsx 'ClinicalDashboard\.tsx' . | head -n 1)"
echo "## ClinicalDashboard wrapper and UtilityDrawer props"
sed -n '3780,3915p' "$cli" | cat -n

echo
echo "## UtilityDrawer return focus props"
sed -n '3008,3035p' "$cli" | cat -n
sed -n '3090,3125p' "$cli" | cat -n

Repository: BigSimmo/Database

Length of output: 12657


Hide the externally controlled trigger for sheetBreakpoint="all".

At sm+ breakpoints, ClinicalDashboard passes open directly into this UtilityDrawer, and the drawer is only rendered while already open. sheetBreakpoint="all" makes the internal <button> visible/focusable at all breakpoints while never participating in opening that consumer’s state; use hidden for that trigger in this path, or hide the button when controlledOpen is set.

🤖 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/clinical-dashboard/dashboard-shell.tsx` around lines 118 -
127, Update the `sheetTriggerClassName` logic in the `UtilityDrawer` component
so the internal trigger uses `hidden` when `sheetBreakpoint` is `"all"` (or when
`controlledOpen` is provided). Preserve the existing responsive classes for
`"lg"` and `"sm"` breakpoints.

const triggerClassName = cn(
"flex min-h-[56px] w-full cursor-pointer list-none items-center justify-between gap-3 rounded-lg px-4 py-3 text-left transition motion-safe:duration-150 hover:bg-[color:var(--surface-subtle)]",
className,
Expand Down Expand Up @@ -227,7 +232,7 @@ export function UtilityDrawer({
contentStyle={sheetContentStyle}
bodyClassName={sheetBodyClassName}
mobileSize={sheetMobileSize}
returnFocusRef={mobileTriggerRef}
returnFocusRef={sheetReturnFocusRef ?? mobileTriggerRef}
portal
>
<div className={cn("space-y-3", sheetChildrenClassName)}>{children}</div>
Expand Down
17 changes: 10 additions & 7 deletions src/components/clinical-dashboard/differentials-home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -907,6 +907,16 @@ function SearchResultsView({
loading={loading || catalogLoading}
sortValue={sortValue}
onSortChange={setSortValue}
filterLabel="Filter differential result type"
filterControls={
<ResultTypeTabs
activeFilter={kindFilter}
onFilterChange={setKindFilter}
allCount={results.length}
presentationCount={presentationCount}
diagnosisCount={diagnosisCount}
/>
}
/>
<p
data-testid="differentials-catalogue-notice"
Expand Down Expand Up @@ -1036,13 +1046,6 @@ function SearchResultsView({
</span>
</p>
) : null}
<ResultTypeTabs
activeFilter={kindFilter}
onFilterChange={setKindFilter}
allCount={results.length}
presentationCount={presentationCount}
diagnosisCount={diagnosisCount}
/>
<div className="flex items-center justify-between gap-2 text-sm font-medium text-[color:var(--text-muted)]">
<span className="min-w-0 truncate">
<strong className="text-[color:var(--text-heading)]">
Expand Down
Loading