Skip to content

Linked, aligned patient timelines, and a View switch for the improved patient view - #3

Merged
JohnLevander merged 3 commits into
mainfrom
linked-patient-timelines
Sep 28, 2026
Merged

JohnLevander merged 3 commits into
mainfrom
linked-patient-timelines

Conversation

@JohnLevander

Copy link
Copy Markdown
Collaborator

Three commits, each green on tests, lint and build.

Linked, aligned overview + detail on both timelines (0427c87)

The document timeline's range slider was drawn against the full date range while sitting under a zoomed axis, so its handles read months off. Both timelines now use one overview + detail control: a strip under each chart shows every document or event across the full range on its own fixed axis, and two handles select what the chart above shows.

The two timelines are linked: one date range covering every document and event, one zoom and pan state, one screen-reader announcement. They share a horizontal frame, so their strips, handles and axes line up vertically from 620px wide.

  • New shared pieces: pure viewport math (utils/patientView/timelineViewport.js), a viewport hook and its linked wrapper, and the strip, axis and zoom controls under components/patient/timeline/.
  • Event Timeline: no mouse-wheel zoom (the page scrolls), a 16x zoom cap, pan by a fifth of the visible window rather than of the whole range, and evenly spaced axis labels. Drops d3-zoom, d3-brush, d3-selection and d3-axis; declares d3-scale and d3-interpolate, which it imports directly but were only resolving through @mui/x-charts.
  • Event and birth dates are built at local midnight like document dates, so linked timelines print the same calendar date in every timezone.
  • Accessibility: focus rings on the handles, multi-thumb slider semantics, a debounced range live region, explicit fills on every SVG text.

This commit also carries in-progress work that was already sitting in the working tree from other sessions (cohort filter layout and filter sets, patient grid and detail panel, patient drawer, document mentions, mock API).

A View switch, and the improved patient view behind it (bb2776c)

A radio in the patient header switches between Alpha (the reading ported from DeepPhe-Viz-v2-alpha) and Improved (default), and remembers the choice. Both show the same data.

Improved: cancers compared in one matrix with tumors nested; undocumented values as a muted dash rather than the weight of a real finding (never hidden, since "unknown" is itself a clinical finding); one containment level and four type steps; banded event lanes; negated marks dashed as well as red, since color alone fails WCAG 1.4.1 for the most common color blindness; both timelines name the range they show; zoom reads as + and − rather than two near-identical magnifiers; one date format for values.

Keep the cancer comparison short (b67e19f)

The matrix was ~530px tall for three cancers, pushing both timelines below the fold; it now reads in about a third of that. TNM reads as one row, rows with nothing to compare fold behind a count, and in the drawer the panel opens folded to a header that reports how many attributes differ.

Testing

424 tests across 50 suites, passing in the default timezone and under TZ=America/New_York; the timeline suites also pass under Tokyo, Los Angeles and UTC+14. Lint, the patient-ID check and the production build are clean (the bundle is ~11 kB smaller after dropping the d3 packages).

Verified in a browser as well as in jsdom: both strips span the same pixels and their handles line up; dragging either timeline's handle moves the other to the same date; a handle released near an end snaps to it; switching to Alpha restores the ported reading; no horizontal page scroll at 375, 820 and 1900px; dark themes checked.

Known gaps

  • Alignment holds from 620px up. Below that the document timeline moves its labels above the lanes and the Event Timeline has no narrow layout, so the two stay linked but do not line up.
  • The standalone patient view still opens the cancer panel expanded; only the drawer folds it.
  • Switching theme repaints the cohort page slowly (~0.5s for the page, 1–2s for an open drawer). That is the cohort page's own re-render cost, not new here, but it makes a theme switch look like it did not apply.

🤖 Generated with Claude Code

JohnLevander and others added 3 commits September 18, 2026 00:03
Replaces the document timeline's bare range slider with an overview + detail
control, and moves the Event Timeline onto the same implementation.

An overview strip under each chart shows every document or event across the
full date range on its own fixed axis, and two handles select the range the
chart above shows. This fixes the old slider, which was drawn against the full
range while sitting under a zoomed axis, so its handles read months off.

The two timelines are linked: one date range covering every document and event,
one zoom and pan state, and one announcement for screen readers. They share a
horizontal frame, so their strips, handles and axes line up vertically from
620px wide.

- New shared pieces: pure viewport math (utils/patientView/timelineViewport.js),
  a viewport hook and its linked wrapper, and the strip, axis and zoom controls
  under components/patient/timeline/
- Event Timeline: no mouse-wheel zoom (the page scrolls), 16x zoom cap, pan by a
  fifth of the visible window rather than of the whole range, and evenly spaced
  axis labels. Drops d3-zoom, d3-brush, d3-selection and d3-axis; declares
  d3-scale and d3-interpolate, which it imports directly
- Event and birth dates are built at local midnight like document dates, so
  linked timelines print the same calendar date in every time zone
- Accessibility: focus rings on the handles, multi-thumb slider semantics, a
  debounced range live region, and explicit fills on every SVG text

Also includes in-progress work already in the tree from other sessions: the
cohort filter layout and filter sets, the patient grid and its detail panel,
the patient drawer, document mentions, and the mock API.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A radio in the patient view header switches between two presentations and
remembers the choice: "Alpha", the reading ported from DeepPhe-Viz-v2-alpha, and
"Improved", the readability work. Both show the same data.

Improved:
- Cancers are compared in one matrix (attributes as rows, a column per cancer,
  tumors nested under their parent) instead of near-identical cards that hid
  their differences. Rows that differ are marked, a tumor value matching its
  cancer says so once rather than repeating, and each column reports how many of
  its fields are documented.
- Undocumented values read as a muted dash rather than occupying the weight of a
  real finding. They are never hidden: "unknown" is itself a clinical finding.
- One containment level (panel surface and spacing, not a border around a
  bordered card) and four type steps for titles, labels, values and axis ticks.
- Event Timeline: alternate lane groups are banded, negated marks are dashed as
  well as red (color alone fails WCAG 1.4.1 for the most common color
  blindness), the legend gains a key for it and names what the relations are
  relative to, and the status line explains why 56 relations draw as 34 spans.
- Both timelines name the range they show against the whole range, and zoom
  in/out read as + and - rather than two near-identical magnifiers.
- One date format for values: "Jan 23, 2010" rather than ISO here, slashes
  there.

New: constants/patientViewPresentation.js and patientViewTypography.js,
PatientViewPresentationProvider (mounted at the app root) with
usePatientViewPresentation, PatientViewPresentationToggle,
controllers/cancerComparison.js and CancerComparisonMatrix.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The matrix was ~530px tall for three cancers, pushing both timelines below the
fold. It now reads in about a third of that, without dropping anything:

- TNM reads as one row ("T1 M0"), the way a stage is read, rather than three
  rows a third full each.
- Rows with nothing to compare fold behind a count: nothing documented for any
  cancer, or a tumor row that only repeats its cancer. One click shows them, and
  the per-cancer completeness count still counts every field.
- Denser rows, with "differs" beside the label rather than on its own line.
- In the patient drawer the panel opens folded to its header, which reports how
  many attributes differ; the standalone view still opens it expanded.
- Cells render plain buttons instead of an MUI Button and Tooltip each, which
  also cuts the panel's share of the (slow) re-render when the theme changes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@JohnLevander
JohnLevander merged commit 4a4b75a into main Sep 28, 2026
2 checks passed
@JohnLevander
JohnLevander deleted the linked-patient-timelines branch September 28, 2026 17:20
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