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:
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user