Spell out the highlighted day under the food chart

A stacked bar cannot be read on a phone. There is nothing to hover, so the
bar's title is unreachable, and the only way to get a day's figure was to
estimate it against the axis — which a split bar makes harder, not easier.

Tapping a bar now writes that day out beneath the chart: "Sun, Sep 20 — Dry
260 g · Fresh 100 g · 360 g in total", or just the total where no kinds are in
play. The same problem exists without kinds, so it is not gated on them; the
unsplit form says the number once rather than "No kind 340 g · 340 g in total".

It reads off the day already selected rather than keeping a selection of its
own. Tapping a bar selects that day on every chart in the app and this one
already highlights it, so a second piece of "which day" state would only be
something to keep in step and eventually fail to. It falls out of that choice
that the arrows and the date picker move the readout too, which is the
behaviour you would want anyway.

Three cases say something rather than reading as blank: a day with no food, a
day marked not counted, and a day outside the window — which has no bar, so no
readout.

The changelog entry for this sits on its own rather than inside the food-kinds
one. It started life gated on kinds and is not any more, and "once you are
using kinds" would have been the wrong condition to file it under.
This commit is contained in:
Alexander Heldt
2026-09-22 10:47:58 +00:00
parent 3584758b86
commit 8c9ea45410
5 changed files with 131 additions and 0 deletions
+81
View File
@@ -251,4 +251,85 @@ suite("what the sentence is allowed to say");
}
}
// ------------------------------------------------- the highlighted day's readout
// Tapping a bar selects that day; this is what the selection says. It reads off
// the existing selection rather than keeping its own, so the two cannot drift.
{
let kinds = [];
let selected = "2026-09-20";
let el = { hidden: false, textContent: "" };
const info = load({
names: ["NO_KIND", "FOOD_COLORS", "foodTrend", "foodSeriesFor", "renderFoodDayInfo"],
stubs: {
document: { getElementById: () => el },
selectedDay: () => new Date(selected + "T12:00:00"),
ymd: (d) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`,
liveFoodKinds: () => kinds,
loadFoodKinds: () => kinds,
foodKindNames: () => new Map(kinds.map(k => [k.id, k.name])),
},
});
const day = (n, byKind, excluded = false) => ({
ymd: `2026-09-${String(n).padStart(2, "0")}`,
date: new Date(2026, 8, n),
grams: Object.values(byKind).reduce((s, v) => s + v, 0),
gramsByKind: byKind, excluded, meals: 0, mealsMissingGrams: 0,
});
const read = (days) => {
el = { hidden: false, textContent: "" };
info.renderFoodDayInfo(days, info.foodSeriesFor(days));
return el;
};
suite("the highlighted day's breakdown");
{
kinds = [{ id: "d", name: "Dry", colorIndex: 0 }, { id: "f", name: "Fresh", colorIndex: 1 }];
const days = [
day(18, { d: 200, f: 100 }), day(19, { d: 210, f: 90 }), day(20, { d: 260, f: 100 }),
];
selected = "2026-09-20";
const r = read(days);
eq(r.hidden, false, "the selected day gets a readout");
ok(/Dry 260 g · Fresh 100 g/.test(r.textContent), "each kind's amount, in the stack's order");
ok(/360 g in total/.test(r.textContent), "…and the total, so you needn't add them up");
ok(/Sep 20/.test(r.textContent), "…named, so it is clear which bar it belongs to");
selected = "2026-09-18";
ok(/Dry 200 g/.test(read(days).textContent), "selecting another bar moves the readout");
// Out of the window entirely: the chart is not showing that day at all.
selected = "2026-08-01";
eq(read(days).hidden, true, "a day outside the window has no bar and so no readout");
}
suite("the days that say something else");
{
kinds = [{ id: "d", name: "Dry", colorIndex: 0 }, { id: "f", name: "Fresh", colorIndex: 1 }];
selected = "2026-09-20";
const withEmpty = [day(18, { d: 200, f: 100 }), day(19, { d: 210 }), day(20, {})];
ok(/no food logged/.test(read(withEmpty).textContent), "a day with no food says so");
const withExcluded = [day(18, { d: 200, f: 100 }), day(19, { d: 210 }), day(20, {}, true)];
ok(/not counted/.test(read(withExcluded).textContent),
"a day marked not counted says that instead of reading as empty");
}
suite("an unsplit chart gets the day total too");
{
// No kinds defined: there is still no hover on a phone, so the figure was
// only readable by eye off the axis.
kinds = [];
selected = "2026-09-20";
const days = [day(18, { "": 300 }), day(19, { "": 320 }), day(20, { "": 340 })];
const r = read(days);
eq(r.hidden, false, "the readout appears without any kinds defined");
eq(r.textContent, "Sun, Sep 20 — 340 g.", "…as the plain total, named by day");
ok(!/No kind/.test(r.textContent),
"…without inventing a kind name for food that has none");
ok(!/in total/.test(r.textContent),
"…and without saying the same number twice");
}
}
export default report("food-trend");