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: 4 additions & 1 deletion docs/search-chrome-behaviour.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -46,7 +46,10 @@ This repo uses one shared search experience across the global shell, dashboard r
the top bar is hidden; section anchors use `--document-anchor-offset`, published from the live collapse-row height
plus that sticky header when the shared bar is away, instead of a fixed `scroll-mt`. Observe the shared header
from the viewer; never edit it for this. The in-column section index card is `lg+` only — phones use the header
disclosure and section sheet.
disclosure and section sheet. Exactly one element may own `id="document-overview"` (the DocumentViewer overview
landing wrapper); `DocumentClinicalSummary` must not reuse that id. The phone sheet lists only present sections —
omit `source-images` when `visualCount === 0`, and do not require a "Tables and diagrams" sheet row in smoke for
the empty-images lithium demo doc.
23. Safari's status bar, collapsing address bar, and pixels outside `window.innerHeight` are native browser/system controls. Do not use negative safe-area overscan, a fixed app root, synthetic document padding, or an opaque viewport slab to make CSS appear to own those pixels. Acceptance is no contrasting **app-owned** band around the native controls, with a matching opaque root canvas. Use the labelled physical-device matrix in [phone-chrome-physical-acceptance.md](phone-chrome-physical-acceptance.md).

## Results band (`SearchResultsHeaderBand`)
Expand Down
11 changes: 11 additions & 0 deletions tests/document-clinical-summary.dom.test.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -78,4 +78,15 @@ describe("DocumentClinicalSummary", () => {
expect(screen.getByText("A structured clinical summary has not been indexed for this document yet.")).toBeVisible();
expect(screen.queryByText("Source-backed")).not.toBeInTheDocument();
});

it("does not claim the document-overview section id", () => {
// DocumentViewer owns id="document-overview" on the overview landing wrapper.
// A second claim here duplicates the id and fails Production UI DOM integrity.
const { container } = render(
<DocumentClinicalSummary document={document} pageHref={(page) => `?page=${page}`} onPageChange={vi.fn()} />,
);

expect(container.querySelector("#document-overview")).toBeNull();
expect(screen.getByTestId("document-clinical-summary")).not.toHaveAttribute("id", "document-overview");
});
});
67 changes: 67 additions & 0 deletions tests/document-section-nav-contract.test.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
import { readFileSync } from "node:fs";

import { describe, expect, it } from "vitest";

/**
* Cheap wiring contract for document section navigation.
*
* Production UI on PR #1311 failed twice for the same class of drift:
* 1. `DocumentClinicalSummary` and the DocumentViewer overview wrapper both
* claimed `id="document-overview"`, so `expectDomIntegrity` failed.
* 2. ui-smoke still drove the retired in-flow "Document viewer sections" nav
* and required a "Tables and diagrams" sheet row for the lithium demo doc,
* which omits that row when `visualCount === 0`.
*
* Chromium proof stays in `tests/ui-smoke.spec.ts`; this file keeps the cheap
* suite honest about the ownership and selectors that proof depends on.
*/

const read = (relativePath: string) => readFileSync(new URL(`../${relativePath}`, import.meta.url), "utf8");

const documentViewerSource = read("src/components/DocumentViewer.tsx");
const clinicalSummarySource = read("src/components/document-viewer/document-clinical-summary.tsx");
const sectionIndexSource = read("src/components/document-viewer/section-index.ts");
const sectionNavSource = read("src/components/document-viewer/section-nav.tsx");
const uiSmokeSource = read("tests/ui-smoke.spec.ts");
const lithiumDemoSource = read("src/lib/demo-data.ts");

describe("document section navigation ownership", () => {
it("keeps a single document-overview section id owner", () => {
// The overview landing wrapper is the scroll target. A second id on the
// clinical summary card duplicates the DOM id and fails ui-smoke integrity.
expect(documentViewerSource).toContain("id={documentOverviewSectionId}");
expect(clinicalSummarySource).not.toMatch(/\bid=["']document-overview["']/);
expect(clinicalSummarySource).not.toContain("id={documentOverviewSectionId}");
expect(clinicalSummarySource).toContain('data-testid="document-clinical-summary"');
});

it("exposes phone section navigation through the title sheet, not an in-flow nav row", () => {
expect(documentViewerSource).toContain('data-testid="document-section-trigger"');
expect(sectionNavSource).toContain('testId="document-section-sheet"');
expect(documentViewerSource).not.toMatch(/Document viewer sections/);
expect(sectionNavSource).not.toMatch(/Document viewer sections/);
});

it("omits Tables and diagrams from the index when a ready document has no visuals", () => {
// The lithium demo document used by ui-smoke has image_count: 0.
// Requiring that label in the phone sheet will red Production UI again.
expect(sectionIndexSource).toContain("input.visualCount > 0");
expect(lithiumDemoSource).toMatch(/id:\s*"11111111-1111-4111-8111-111111111111"[\s\S]*?image_count:\s*0/);
});

it("keeps document-viewer ui-smoke on the current phone section chrome", () => {
const documentViewerSmoke = uiSmokeSource.slice(
uiSmokeSource.indexOf('test("document viewer puts the PDF preview first'),
uiSmokeSource.indexOf('test("answer glass header overlays main'),
);

expect(documentViewerSmoke).toContain('getByTestId("document-section-trigger")');
expect(documentViewerSmoke).toContain('getByTestId("document-section-sheet")');
expect(documentViewerSmoke).not.toMatch(
/getByRole\(\s*["']navigation["']\s*,\s*\{\s*name:\s*["']Document viewer sections["']/,
);
// Demo docs without visuals omit Images from the sheet; open via summary.
expect(documentViewerSmoke).toContain("openImagesDisclosure");
expect(documentViewerSmoke).toContain('images.locator("summary")');
});
});
Loading