mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
fix: settle the new edit and HRV surfaces after a look at them
The HRV page's second chart reported its visible window into the same stat-strip slot as the first, so the two replaced each other on every render until React stopped it and the chart failed to load. Only the primary chart reports the window now; the strip describes that measure. The workout edit sheet no longer shows the "leave empty if you just finished" hint, which is about logging and not about a stored start. On the Apple Health card a type's name reads as the row's content above its arrival line, with room between rows.
This commit is contained in:
1 parent
196c331068
commit
1fe4a7af95
4 files changed
+46
-8
No files matched your search
@@ -22,11 +22,16 @@ vi.mock("@/hooks/use-ai-capability", () => ({
|
||||
}));
|
||||
vi.mock("next/dynamic", () => ({
|
||||
default: () => {
|
||||
const Stub = (props: { types?: string[]; title?: string }) => (
|
||||
const Stub = (props: {
|
||||
types?: string[];
|
||||
title?: string;
|
||||
onVisibleStats?: unknown;
|
||||
}) => (
|
||||
<div
|
||||
data-slot="chart-stub"
|
||||
data-types={(props.types ?? []).join(",")}
|
||||
data-title={props.title ?? ""}
|
||||
data-reports-stats={props.onVisibleStats ? "yes" : "no"}
|
||||
/>
|
||||
);
|
||||
Stub.displayName = "HealthChartStub";
|
||||
@@ -92,6 +97,24 @@ describe("/insights/hrv — SDNN and RMSSD kept apart (#1110)", () => {
|
||||
expect(found[1]!.title).toMatch(/RMSSD$/);
|
||||
});
|
||||
|
||||
it("lets only the primary chart write the stat strip's window", () => {
|
||||
// Two charts writing the one stats slot replaced each other's window on
|
||||
// every render: React's update-depth error, and the chart failed to load.
|
||||
analyticsMock.mockReturnValue({
|
||||
data: {
|
||||
summaries: {
|
||||
HEART_RATE_VARIABILITY: { count: 30 },
|
||||
HRV_RMSSD: { count: 28 },
|
||||
},
|
||||
},
|
||||
isEmpty: false,
|
||||
});
|
||||
const flags = [...render().matchAll(/data-reports-stats="(yes|no)"/g)].map(
|
||||
(m) => m[1],
|
||||
);
|
||||
expect(flags).toEqual(["yes", "no"]);
|
||||
});
|
||||
|
||||
it("keeps one unlabelled SDNN chart when there is no RMSSD", () => {
|
||||
analyticsMock.mockReturnValue({
|
||||
data: { summaries: { HEART_RATE_VARIABILITY: { count: 30 } } },
|
||||
|
||||
@@ -509,7 +509,9 @@ export function HealthKitMetricPage({
|
||||
userTimezone={user?.timezone}
|
||||
valueScale={resolvedScale}
|
||||
valueOffset={resolvedOffset}
|
||||
onVisibleStats={onVisibleStats}
|
||||
// No `onVisibleStats`: the strip describes the primary measure, and
|
||||
// two charts writing the one stats slot would replace each other's
|
||||
// window on every render without end.
|
||||
showDataTable
|
||||
/>
|
||||
) : null}
|
||||
|
||||
@@ -129,7 +129,7 @@ export function MetricFreshnessDisclosure<
|
||||
</button>
|
||||
|
||||
{open && (
|
||||
<ul id={panelId} className="space-y-1">
|
||||
<ul id={panelId} className={detail ? "space-y-2.5" : "space-y-1"}>
|
||||
{sorted.map((entry) => {
|
||||
const extra = detail?.(entry);
|
||||
return (
|
||||
@@ -151,7 +151,16 @@ export function MetricFreshnessDisclosure<
|
||||
className="size-3 shrink-0"
|
||||
/>
|
||||
) : null}
|
||||
<span className="truncate">{label(entry.type)}</span>
|
||||
{/* With a detail line under it, the type name is the
|
||||
row's content and the line its meta. */}
|
||||
<span
|
||||
className={cn(
|
||||
"truncate",
|
||||
extra && !entry.stale && "text-foreground",
|
||||
)}
|
||||
>
|
||||
{label(entry.type)}
|
||||
</span>
|
||||
</span>
|
||||
<time
|
||||
dateTime={entry.lastSeenAt}
|
||||
|
||||
@@ -309,16 +309,20 @@ export function ManualWorkoutForm({
|
||||
max={wallClockNow(new Date(), timezone)}
|
||||
aria-invalid={!!errors.start || undefined}
|
||||
aria-describedby={[
|
||||
`${ids.start}-hint`,
|
||||
edit ? undefined : `${ids.start}-hint`,
|
||||
describedBy("start", `${ids.start}-error`),
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" ")}
|
||||
data-testid="manual-workout-start"
|
||||
/>
|
||||
<p id={`${ids.start}-hint`} className="text-muted-foreground text-xs">
|
||||
{t("insights.workouts.manual.startHint")}
|
||||
</p>
|
||||
{/* "Leave empty if you just finished" is about logging; an edit
|
||||
opens with the stored start in place. */}
|
||||
{edit ? null : (
|
||||
<p id={`${ids.start}-hint`} className="text-muted-foreground text-xs">
|
||||
{t("insights.workouts.manual.startHint")}
|
||||
</p>
|
||||
)}
|
||||
<FieldError id={`${ids.start}-error`} message={fieldError("start")} />
|
||||
</div>
|
||||
|
||||
|
||||
Reference in new issue
Block a user