mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 21:32:18 +00:00
fix(sleep): restore the last-night hypnogram timeline bar
The staged bar is gated on the night carrying more than one distinct stage boundary. The guard read the segment END instants, so an ordinary multi-stage night whose segments share a right edge — the shape a summary-derived writer reconstructs to — collapsed to the breakdown footer with no bar. Gate on the distinct START instants instead, so a night with timed stages keeps its timeline and only a session recorded on a single instant falls back to the breakdown. Covers the chart-vs-breakdown branch the test suite did not.
This commit is contained in:
1 parent
dd8ffffd91
commit
6a99f72384
2 files changed
+59
-6
No files matched your search
@@ -92,3 +92,54 @@ describe("<SleepHypnogram> — lane derivation", () => {
|
||||
expect(src).toContain("ticks={lanes.map((_, i) => lanes.length - 1 - i)}");
|
||||
});
|
||||
});
|
||||
|
||||
describe("<SleepHypnogram> — timeline bar visibility", () => {
|
||||
const sharedEnd = "2026-06-09T06:00:00.000Z";
|
||||
|
||||
it("draws the timeline bar for a multi-stage night whose segments share a right edge", () => {
|
||||
// Distinct STARTS but a shared END instant — the shape a summary-derived
|
||||
// writer reconstructs to. The earlier end-instant guard hid the bar here;
|
||||
// gating on the start instant restores it. (Regression: bar vanished,
|
||||
// leaving only the breakdown numbers.)
|
||||
const session: SleepHypnogramSession = {
|
||||
...baseSession,
|
||||
segments: [
|
||||
{
|
||||
stage: "CORE",
|
||||
start: "2026-06-08T22:30:00.000Z",
|
||||
end: sharedEnd,
|
||||
minutes: 200,
|
||||
},
|
||||
{
|
||||
stage: "DEEP",
|
||||
start: "2026-06-09T01:00:00.000Z",
|
||||
end: sharedEnd,
|
||||
minutes: 100,
|
||||
},
|
||||
{
|
||||
stage: "REM",
|
||||
start: "2026-06-09T03:00:00.000Z",
|
||||
end: sharedEnd,
|
||||
minutes: 120,
|
||||
},
|
||||
],
|
||||
};
|
||||
expect(render(session)).toContain('role="img"');
|
||||
});
|
||||
|
||||
it("collapses to the breakdown footer for a degenerate single-instant session", () => {
|
||||
// Every segment stamped on ONE instant — no real timeline, so only the
|
||||
// breakdown footer, no bar.
|
||||
const instant = "2026-06-09T06:00:00.000Z";
|
||||
const session: SleepHypnogramSession = {
|
||||
...baseSession,
|
||||
segments: [
|
||||
{ stage: "CORE", start: instant, end: instant, minutes: 200 },
|
||||
{ stage: "DEEP", start: instant, end: instant, minutes: 100 },
|
||||
],
|
||||
};
|
||||
const html = render(session);
|
||||
expect(html).not.toContain('role="img"');
|
||||
expect(html).toContain('data-slot="sleep-hypnogram-breakdown"');
|
||||
});
|
||||
});
|
||||
@@ -209,15 +209,17 @@ export function SleepHypnogram({ session }: SleepHypnogramProps) {
|
||||
// need any positive per-stage minutes. A legacy stage-summary night (no
|
||||
// segment spans) still shows its breakdown rather than collapsing the
|
||||
// whole card. "Unavailable" is reserved for a truly empty session.
|
||||
// Spans must also carry more than one distinct end instant: a summary-
|
||||
// shaped writer (per-stage night totals all stamped on the sleep end)
|
||||
// reconstructs into bars that all touch the night's right edge — that
|
||||
// is a breakdown wearing a timeline costume, so paint it as the
|
||||
// breakdown footer instead.
|
||||
// Spans must also carry more than one distinct START instant: a real
|
||||
// night with timed stages steps through the clock, so its segments begin
|
||||
// at different instants and earn the timeline bar. Only a degenerate
|
||||
// session whose every segment starts on a single instant collapses to the
|
||||
// breakdown footer. (Gating on the END instant instead hid the bar for an
|
||||
// ordinary multi-stage night whose segments happened to share a right
|
||||
// edge — the regression this restores.)
|
||||
const hasTrack =
|
||||
spans.length > 0 &&
|
||||
(spans.length === 1 ||
|
||||
new Set(spans.map((s) => s.x2)).size > 1);
|
||||
new Set(spans.map((s) => s.x1)).size > 1);
|
||||
const hasBreakdown = breakdown.length > 0;
|
||||
|
||||
return (
|
||||
|
||||
Reference in new issue
Block a user