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
5 changes: 3 additions & 2 deletions src/components/clinical-dashboard/master-search-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -986,7 +986,8 @@ export function MasterSearchHeader({
window.requestAnimationFrame(() => modeButtonRef.current?.focus());
}}
className={cn(
"grid min-h-[3.25rem] w-full grid-cols-[2rem_minmax(0,1fr)_auto] items-center gap-2 rounded-md px-2.5 py-2 text-left transition focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)]",
"grid w-full grid-cols-[2rem_minmax(0,1fr)_auto] items-center gap-2 rounded-md px-2.5 py-2 text-left transition focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)]",
usesPhoneSearchLayout ? "min-h-12" : "min-h-[3.25rem]",
active
? "border-l-2 border-l-[color:var(--clinical-accent)] bg-[color:var(--surface-chrome)] text-[color:var(--text)]"
: "text-[color:var(--text-muted)] hover:bg-[color:var(--surface-subtle)] hover:text-[color:var(--text)]",
Expand Down Expand Up @@ -2184,7 +2185,7 @@ export function MasterSearchHeader({
mobilePlacement="bottom"
mobileSize="content"
testId="app-mode-menu-sheet"
contentClassName="max-h-[min(88dvh,36rem)] sm:max-w-md"
contentClassName="max-h-[calc(100dvh-0.5rem)] sm:max-w-md"
Comment thread
cursor[bot] marked this conversation as resolved.
bodyClassName="p-2"
headerClassName="bg-[color:var(--surface-lux)] px-4 py-3"
>
Expand Down
2 changes: 2 additions & 0 deletions tests/audit-navigation-auth-regressions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ describe("audit navigation and auth regressions", () => {
expect(masterSearchHeaderSource).toContain("{!usesPhoneSearchLayout && modeMenuOpen ? (");
expect(masterSearchHeaderSource).toContain('aria-haspopup={usesPhoneSearchLayout ? "dialog" : "menu"}');
expect(masterSearchHeaderSource).toContain('mobilePlacement="bottom"');
expect(masterSearchHeaderSource).toContain('contentClassName="max-h-[calc(100dvh-0.5rem)] sm:max-w-md"');
expect(masterSearchHeaderSource).toContain('usesPhoneSearchLayout ? "min-h-12" : "min-h-[3.25rem]"');
expect(masterSearchHeaderSource).toContain("phoneLayoutGateRef");
// Hydration-safe: do not read matchMedia in useState (SSR/client mismatch → React #418).
expect(masterSearchHeaderSource).toContain(
Expand Down
21 changes: 16 additions & 5 deletions tests/ui-smoke.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1623,7 +1623,7 @@ test.describe("Clinical KB UI smoke coverage", () => {
await expectNoPageHorizontalOverflow(page);
});

test("phone mode menu opens as a scrollable bottom sheet with the full mode list", async ({ page }) => {
test("phone mode menu opens tall enough to show the full mode list without scrolling", async ({ page }) => {
await page.setViewportSize({ width: 390, height: 844 });
await mockPrivateUnauthenticatedApi(page);
await gotoApp(page, "/");
Expand All @@ -1641,16 +1641,27 @@ test.describe("Clinical KB UI smoke coverage", () => {
await expect(appModeTrigger).toHaveAttribute("aria-expanded", "true");
await expect(appModeTrigger).toHaveAttribute("aria-controls", "app-mode-menu");

// Full list must be present (not clipped out of the DOM by the old max-height panel).
// Full list must be present and visible without sheet-body scrolling on this viewport.
const modeOptions = appModeMenu.getByRole("menuitemradio");
expect(await modeOptions.count()).toBeGreaterThanOrEqual(10);
const modeCount = await modeOptions.count();
expect(modeCount).toBeGreaterThanOrEqual(10);
await expect(appModeMenu.getByRole("menuitemradio", { name: /^Tools\b/ })).toBeAttached();
await expect(appModeMenu.getByRole("menuitemradio", { name: /^Medication\b/ })).toBeAttached();
await expect(modeOptions.first()).toBeInViewport();
await expect(modeOptions.nth(modeCount - 1)).toBeInViewport();

const sheetBodyNeedsScroll = await modeSheet.evaluate((panel) => {
const body = [...panel.querySelectorAll("div")].find((element) => {
const { overflowY } = getComputedStyle(element);
return overflowY === "auto" || overflowY === "scroll";
});
if (!body) return true;
return body.scrollHeight > body.clientHeight + 1;
});
expect(sheetBodyNeedsScroll).toBe(false);

// Scroll the sheet body so a lower mode is interactable, then select it.
// Tools is canonical at /tools (PT-11); selecting it navigates off the dashboard.
const toolsMode = appModeMenu.getByRole("menuitemradio", { name: /^Tools\b/ });
await toolsMode.scrollIntoViewIfNeeded();
await expect(toolsMode).toBeVisible();
await toolsMode.click();

Expand Down
Loading