A collaborator hit the exact seam between the shipped hep-explorer and the original SafetyGraphics eDISH:
“Is there a way to drill down to patient level details with the composite plots? Hovering the cursor brings up some details, but it’s not like the original eDISH that opens up multiple graphs below when you click on a patient point.”
They are right, and the gap is bigger than the composite view. The original eDISH answered a fourth question that none of our population views currently answer well: after which arm?, how severe?, and which participants? comes “show me this one person’s whole story.” This report proposes carving that answer out as its own module — a participant profile that any safety.viz chart can open — and lays out four options (A–D) for how it gets surfaced, with a recommendation. Nothing here is built yet; this is the decision artifact.
Every selectable renderer already speaks the same event (participantsSelected, payload {detail:{data: ids}}), but what the user sees after a click is different in every chart — and only one view in the whole library plots labs over time for the clicked participant:
| Surface | Click today | Patient-level detail? |
|---|---|---|
| hep-explorer · scatter (eDISH) | Selects one participant: visit-path trace + axis styling on the chart, sidebar select syncs, and a single lab-over-time line chart renders below (drawDetail) | partial one chart, no header, no measure table |
| hep-explorer · composite | Multi-select toggle: point restyles across all panels, header count updates, event fires. No drill-down renders. Hover tooltip shows peak values + quadrant shift only | none — the collaborator’s gap |
| outlier-explorer / shift-plot | Selection overlay / brush → linked record listing below the chart | partial raw records, no graphs |
| ae-timelines | Swaps the whole view for a per-participant AE timeline + listing, with a Back button | yes (AE domain only) |
| delta-delta | Highlights the point, writes “Participant X selected.”, draws a measure table. No event dispatched | partial |
| histogram / results-over-time / qt-explorer / ae-explorer | No participant selection | none |
Planned surfaces raise the stakes: the migration Sankey (sv#92) deliberately ends in “Review these N in the composite plot” — a hand-off that today lands the reviewer in the one view with no individual drill-down. The waterfall (sv#93) is one bar per participant; a bar click has nowhere to go either.
The original’s click-to-drill-down was a four-part composition, mounted below the main chart with the chart still visible (verified in the hep-explorer source, src/callbacks/onResize/customizePoints/):
| Piece | Contents | safety.viz today |
|---|---|---|
| Participant header | Demographics from config.details, computed R Ratio (and P_ALT), a Clear button, optional “Full Participant Profile” link-out | Missing (the link-out is sv#53) |
| Spaghetti plot | “Standardized Lab Values by Study Day” — one line per key lab, ×ULN / ×baseline toggle, per-measure cut lines on hover, filled points above the cut, optional exposure strip | Scatter view has a single-participant version (drawDetail); no toggle, no cuts, no exposure |
| Measure summary table | All lab measures (key ones first, extras behind a toggle): N / min / median / max + an inline sparkline per measure; clicking a sparkline expands an inset line chart with the normal-range band and population-extent guides | Missing entirely |
| On-chart trace | Visit path + axis rugs drawn on the eDISH scatter itself; other points dimmed | Ported (scatter view) |
Two useful limits of the original worth not reproducing: it was strictly single-participant (each click overwrote the last), and the profile code was welded into the eDISH renderer — which is why none of the other seven safetyGraphics charts ever got it. That welding is the thing to fix.
One new shared module — working name participant-profile — that owns the drill-down regardless of which chart opens it. The four options in the next section only disagree about where it appears; the module itself is the same:
Header (id, demographics from config.details, computed stats, Clear, templated profile link-out — absorbing sv#53) · labs-over-time spaghetti (×ULN / ×baseline toggle, per-measure reference cuts) · measure summary table with inline sparklines · optional record listing reusing the shared listing renderer. Sections are individually toggleable per chart config.
The hard parts already exist as pure functions inside hep-explorer’s data layer: participantMeasureSeries (day-indexed series per measure), measureSummary (per-measure N/min/median/max), and the retained cleaned rows every renderer keeps after render. The planned hep-core extraction (sv#91) moves exactly these reducers into a shared layer — the profile module is their second consumer, ready-made.
The module listens for the existing participantsSelected event on the chart’s outer container (which catches all current dispatch targets) and renders when the selection resolves to one focused participant. It needs no new chart APIs — though the rollout piggybacks on sv#88’s event-parity work (every selectable chart dispatching the same payload, including on clear). Because seven of the nine renderers ingest the same long measure-per-visit records, the same module serves the whole lab family — hep-explorer, histogram, outlier-explorer, shift-plot, results-over-time, qt-explorer, delta-delta — not just eDISH.
“Faithful to the original: click a point, the story opens below, the chart stays put.”
The shell grows a sv-profile slot under the chart card (above the record listing). Any renderer that opts in gets the full profile there when a single participant is focused. Chart and profile are visible together, so the on-chart trace and the labs-over-time read as one linked view — the interplay reviewers liked in the original.
What changes
profile: true (or per-section config) per rendererdrawDetail is deleted in favor of the moduleTrade-offs
Long pages on small screens — profile pushes the listing down. Multi-panel views (composite) already scroll; the dock adds more. Needs the stepper (§5) for multi-select.
Choose when
Parity with the original eDISH experience is the point, and charts are reviewed on desktop reports — which is our reviewers’ reality.
| Measure | N | Max | |
|---|---|---|---|
| ALT | 12 | 8.1× | |
| TB | 12 | 3.4× | |
| AST | 12 | 5.0× | |
| ALP | 11 | 1.2× |
“The chart is the workspace; the profile slides in beside it and slides away.”
The profile opens as a right-hand slide-over panel on top of the chart card, dismissed with ✕ or Escape. The population view stays in place at reduced width; the reviewer flicks between individuals without the page ever reflowing.
What changes
Trade-offs
Covers part of the chart it annotates — the dimmed-points + trace choreography loses half its audience. Sparkline-to-inset expansion gets cramped at drawer width. Overlay a11y (focus trap, scroll lock) is real new surface for the library.
Choose when
Screen economy beats chart–profile adjacency, or profiles are opened dozens of times in quick succession (triage workflows).
| ALT | 8.1× | |
| TB | 3.4× |
“Profile is a destination: a full view in the view selector, like Scatter and Composite.”
The profile becomes another entry in each chart family’s view list (the shared view selector of hub#41). Clicking a participant — or picking one in the participant selector (sv#87) — switches to a full-canvas Profile view with a Back affordance, the ae-timelines pattern generalized.
What changes
Trade-offs
The population chart disappears while reading the profile — click, teleport, Back, re-orient. Exactly the context-break the original avoided by rendering below. Weakest match for compare-against-the-population review.
Choose when
The profile grows past what a dock can hold (AE track, exposure, conmeds) and becomes a report of its own.
“The profile is a tenth renderer; apps wire it next to any chart with one event listener.”
Ship SafetyViz.participantProfile(el, data, config) as a standalone renderer and change the charts not at all. Host pages — the gallery demos, gsm.safety widgets in Shiny, open.gismo — mount it wherever they want and pipe participantsSelected into it. The library’s first linked-charts demo makes it discoverable.
What changes
Widget_ParticipantProfile with crosstalk-style wiring for freeTrade-offs
A bare chart embed still has no drill-down — the collaborator’s question stays unanswered out of the box, deferred to whoever builds the page. Two data ingests (chart + profile) unless the host shares one cleaned dataset.
Choose when
You believe composition in the host app (open.gismo) is where linked views belong, and single-chart embeds are a secondary case.
Build the profile once, as Option D’s standalone module — its own factory, data contract, evidence page, and event wiring, so gsm.safety widgets and open.gismo can compose it freely. Then give every lab-family chart Option A’s dock as the built-in default: a profile config (on by default for the hep family) that mounts that same module into a new shell slot below the chart and feeds it the chart’s own cleaned rows — no second ingest, no host wiring. A bare hepExplorer(el, data) embed then answers the collaborator’s question out of the box, exactly the way the original did, while the standalone form stays available for linked-page composition.
The drawer (B) remains cheap to add later as profile: {mode: 'drawer'} if triage-style review demands it — same module, different mount. The full view (C) is the one I’d pass on for now: it breaks chart–profile adjacency, and its one real payoff (a bigger canvas) matters only after the profile grows AE/conmed/exposure tracks in v2.
Multi-select and cohorts (§5 below): the dock renders the full profile when exactly one participant is focused. With N selected, it collapses to a one-line stepper — ◀ 3 of 12 · CLD-2013 ▶ — stepping through the cohort in ranked order (worst quadrant first). That makes the Sankey hand-off (sv#92) a complete chain for the first time: ribbon → cohort in composite → step through individuals → record listing. Population to single lab value in four clicks.
| Surface | Entry gesture | Notes |
|---|---|---|
| hep-explorer · scatter | Point click (today’s gesture) | Replaces drawDetail; on-chart trace + rugs stay |
| hep-explorer · composite | Click with one focused; stepper when N selected | Closes the collaborator’s gap. Points exist in baseline + peak cards and ×BLN panels — dock highlights the focused id in all of them |
| migration Sankey (sv#92) | Via the composite hand-off (“Review these N…”) → stepper | Profile is the last mile of the paper’s two-step review |
| hep-waterfall (sv#93) | Bar click | One bar = one participant — the most natural fit in the family |
| outlier-explorer / shift-plot / histogram / results-over-time / qt-explorer / delta-delta | Existing selection gestures + the shared participant selector (sv#87) | Same long-lab contract, so the dock is config-on; rolls out with sv#88’s batches |
| ae-timelines / ae-explorer | Deferred | AE-domain profile section is v2; ae-timelines keeps its detail swap until then |
Yesterday’s filings put every prerequisite on the board; the profile module slots in without reshuffling any of them:
| Step | Work | Status |
|---|---|---|
| 1 | sv#91 hep-core split — moves participantMeasureSeries / measureSummary / cleaned-row layer into the shared core the profile consumes | Filed, first PR of the hub#43 stack |
| 2 | sv#87 shared participant selector — the non-click way to focus a participant; chips become the stepper’s siblings | Filed |
| 3 | Profile module + dock (this report) — new requirement, lands first in hep-explorer composite + scatter; absorbs sv#53 (link-out lives in the profile header, templated by id rather than the original’s static URL) | Awaiting direction (D1–D4 below) |
| 4 | sv#88 selection-note rollout — its event-parity work (SELN-4) is exactly the wiring the profile listens to; dock adoption rides the same per-renderer batches | Filed, depends on sv#87 |
| 5 | sv#92 migration view + sv#93 waterfall pick the dock up on arrival | Filed, depend on sv#91 |
Dock below the chart (A), drawer (B), full view (C), or host-composed only (D)?
Recommend: the hybrid — standalone module (D packaging) with the dock (A) as the built-in default; drawer later if wanted; skip C.Header + spaghetti + measure table with sparklines + optional listing is proposed v1. The original’s sparkline-click inset chart, exposure strip, and AE/conmed tracks would be v2.
Recommend: v1 as proposed — it already exceeds the original’s three blocks minus the inset expansion.When a cohort is selected (composite multi-select, Sankey ribbon, shift-plot brush): stepper strip that pages through the cohort, or profile only when exactly one is selected?
Recommend: the stepper — it is what makes the Sankey→composite hand-off end in individuals, per the paper’s review model.File as a new hub requirement (with safety.viz sub-issues, after sv#91 lands) or fold into an existing issue? And: “participant profile” (house/CDISC style, matches participantsSelected) vs the collaborator’s “patient profile”?
participant-profile.“Not yet in the composite view — click currently selects participants (and feeds the linked views) but doesn’t open the drill-down; the scatter view has a first slice of it (a labs-over-time chart below the plot on click). You’ve hit exactly the gap we’re closing next: we’re carving the original eDISH click-profile out as a shared module — header, standardized-labs-over-time, and the measure table with sparklines — that opens below every chart in the family, composite included, and steps through a selected cohort one participant at a time. Design options are drafted and it’s queued right behind the current hep-explorer refactor.”