All bars now share one baseline, because each column has a label row of the same height. Before, only columns with a day number had a label row, so their bars sat higher. Only the peak bar has a value label, so labels no longer overlap in a 31-day month. A readout line above the chart shows the date and value of the bar that the user hovers, taps or focuses. Day numbers are no longer cut, a period with no time has no bar, the axis line sits at the bar baseline, and bars are 24px wide or less. Co-Authored-By: Claude Opus 5.5 <[email protected]>
57 lines
2.5 KiB
TypeScript
57 lines
2.5 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
import { rangeWindow, type RangeKind } from "../src/client/analytics.ts";
|
|
import { trendChart } from "../src/client/trend-chart.ts";
|
|
|
|
const HOUR = 3_600_000;
|
|
const format = (ms: number) => `${ms / HOUR}h`;
|
|
const count = (html: string, text: string) => html.split(text).length - 1;
|
|
const base = new Date(2026, 10, 8, 12);
|
|
|
|
describe("trend chart", () => {
|
|
for (const kind of ["day", "week", "month", "year"] as RangeKind[]) {
|
|
test(`${kind}: every column has a label row, so all bars share one baseline`, () => {
|
|
const range = rangeWindow(kind, 0, base);
|
|
const html = trendChart(range.bins, range.bins.map(() => 0), format, +base);
|
|
expect(count(html, 'class="trend-label"')).toBe(range.bins.length);
|
|
});
|
|
|
|
test(`${kind}: every bin has a full name for the readout`, () => {
|
|
for (const bin of rangeWindow(kind, 0, base).bins) expect(bin.name.length).toBeGreaterThan(1);
|
|
});
|
|
}
|
|
|
|
test("only the peak bar has a value label", () => {
|
|
const { bins } = rangeWindow("month", 0, base);
|
|
const trend = bins.map((_, i) => (i === 3 ? 3 * HOUR : i < 8 ? HOUR : 0));
|
|
const html = trendChart(bins, trend, format, +base);
|
|
expect(count(html, 'class="trend-value"')).toBe(1);
|
|
expect(html).toContain('<span class="trend-value">3h</span>');
|
|
});
|
|
|
|
test("a bin with no time has no bar", () => {
|
|
const { bins } = rangeWindow("week", 0, base);
|
|
const trend = bins.map((_, i) => (i === 0 ? HOUR : 0));
|
|
expect(count(trendChart(bins, trend, format, +base), 'class="trend-fill"')).toBe(1);
|
|
});
|
|
|
|
test("each column gives its date and value to the readout", () => {
|
|
const { bins } = rangeWindow("month", 0, base);
|
|
const html = trendChart(bins, bins.map((_, i) => (i === 5 ? 2 * HOUR : 0)), format, +base);
|
|
expect(html).toContain(`data-readout="${bins[5]!.name} · 2h"`);
|
|
expect(html).toContain(`data-readout="${bins[6]!.name} · 0h"`);
|
|
});
|
|
|
|
test("the readout starts on the peak, or says the period is empty", () => {
|
|
const { bins } = rangeWindow("year", 0, base);
|
|
const html = trendChart(bins, bins.map((_, i) => (i === 10 ? 5 * HOUR : 0)), format, +base);
|
|
expect(html).toContain(`>Peak · ${bins[10]!.name} · 5h<`);
|
|
expect(trendChart(bins, bins.map(() => 0), format, +base)).toContain(">No time logged in this period.<");
|
|
});
|
|
|
|
test("the current bin is marked", () => {
|
|
const { bins } = rangeWindow("week", 0, base);
|
|
const html = trendChart(bins, bins.map(() => 0), format, +base);
|
|
expect(count(html, "trend-col current")).toBe(1);
|
|
});
|
|
});
|