8747074e52
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
129 lines
4.3 KiB
JavaScript
129 lines
4.3 KiB
JavaScript
// The grid's plan: the decisions `gridTable` used to make while creating
|
|
// elements, where a wrong answer showed up as a wrong pixel.
|
|
|
|
import { test } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
import { gridPlan } from "../shell.js";
|
|
|
|
const cell = (text, extra = {}) => ({
|
|
text,
|
|
numeric: true,
|
|
error: false,
|
|
selected: false,
|
|
editable: true,
|
|
...extra,
|
|
});
|
|
|
|
/// A grid with a group header between its data rows — the shape the row
|
|
/// index has to survive. Two column levels under one group run.
|
|
function grouped() {
|
|
return {
|
|
row_levels: 2,
|
|
col_levels: [["Q1", ""], ["Revenue", "Cost"]],
|
|
col_groups: [{ span: 2, label: "2026", collapsed: false }],
|
|
totals: [],
|
|
editing: null,
|
|
rows: [
|
|
{ kind: "group", label: "Hot", collapsed: false },
|
|
{ kind: "data", labels: ["Hot", "Espresso"], cells: [cell("1"), cell("2")] },
|
|
{ kind: "data", labels: ["", "Latte"], cells: [cell("3"), cell("4")] },
|
|
{ kind: "group", label: "Cold", collapsed: true },
|
|
{ kind: "data", labels: ["Cold", "Cold Brew"], cells: [cell("5"), cell("6")] },
|
|
],
|
|
};
|
|
}
|
|
|
|
test("a group header consumes no data row index", () => {
|
|
const plan = gridPlan(grouped());
|
|
const data = plan.body.filter((r) => r.kind === "data");
|
|
assert.deepEqual(data.map((r) => r.row), [0, 1, 2]);
|
|
// And every cell in a row carries that row's index, not its position in
|
|
// `rows` — the coordinate a click sends.
|
|
assert.deepEqual(
|
|
data.at(-1).cells.map((c) => [c.row, c.col]),
|
|
[[2, 0], [2, 1]],
|
|
);
|
|
});
|
|
|
|
test("a group header spans the labels and the cells together", () => {
|
|
const plan = gridPlan(grouped());
|
|
const groups = plan.body.filter((r) => r.kind === "group");
|
|
assert.deepEqual(groups.map((r) => r.colSpan), [4, 4]);
|
|
assert.deepEqual(groups.map((r) => r.text), ["▼ Hot", "▶ Cold"]);
|
|
assert.equal(plan.cornerSpan, 2);
|
|
});
|
|
|
|
test("a column run in no group is blank, and an empty name is a name", () => {
|
|
const model = grouped();
|
|
model.col_groups = [
|
|
{ span: 1, label: null, collapsed: false },
|
|
{ span: 1, label: "", collapsed: true },
|
|
];
|
|
const plan = gridPlan(model);
|
|
assert.deepEqual(plan.groups, [
|
|
{ span: 1, text: "" },
|
|
{ span: 1, text: "▶ " },
|
|
]);
|
|
});
|
|
|
|
test("the typed text replaces only the selected cell, and carries a caret", () => {
|
|
const model = grouped();
|
|
model.editing = "12";
|
|
model.rows[1].cells[1].selected = true;
|
|
const plan = gridPlan(model);
|
|
const row = plan.body[1];
|
|
assert.equal(row.cells[0].text, "1");
|
|
assert.equal(row.cells[1].text, "12▏");
|
|
assert.equal(row.cells[1].classes.typing, true);
|
|
// A cell being typed into is not drawn as a number: the buffer is text.
|
|
assert.equal(row.cells[1].classes.numeric, false);
|
|
assert.equal(row.cells[0].classes.numeric, true);
|
|
});
|
|
|
|
test("a selected cell with no editor open is not typing", () => {
|
|
const model = grouped();
|
|
model.editing = null;
|
|
model.rows[1].cells[0].selected = true;
|
|
const plan = gridPlan(model);
|
|
const first = plan.body[1].cells[0];
|
|
assert.equal(first.classes.typing, false);
|
|
assert.equal(first.classes.selected, true);
|
|
assert.equal(first.text, "1");
|
|
});
|
|
|
|
test("an empty edit buffer still means an editor is open", () => {
|
|
const model = grouped();
|
|
model.editing = "";
|
|
model.rows[1].cells[0].selected = true;
|
|
const plan = gridPlan(model);
|
|
assert.equal(plan.body[1].cells[0].classes.typing, true);
|
|
assert.equal(plan.body[1].cells[0].text, "▏");
|
|
});
|
|
|
|
test("an empty totals strip is no strip at all, not a row of blanks", () => {
|
|
// Gotcha 33: an EMPTY strip means this grid has no totals — records mode,
|
|
// or an empty grid — so there is nothing to draw and nothing to pad.
|
|
assert.equal(gridPlan(grouped()).totals, null);
|
|
});
|
|
|
|
test("the totals strip is relayed, and its label spans the row headers", () => {
|
|
const model = grouped();
|
|
model.totals = [
|
|
{ text: "9,001", numeric: true, error: false },
|
|
{ text: "err", numeric: false, error: true },
|
|
];
|
|
const plan = gridPlan(model);
|
|
assert.equal(plan.totals.span, 2, "the label spans every row-header level");
|
|
assert.equal(plan.totals.label, "Total");
|
|
assert.deepEqual(
|
|
plan.totals.cells.map((c) => [c.text, c.classes.numeric, c.classes.error]),
|
|
[
|
|
["9,001", true, false],
|
|
["err", false, true],
|
|
],
|
|
);
|
|
// And it carries no coordinates: a total is not a cell a click can name.
|
|
assert.ok(!("row" in plan.totals.cells[0]));
|
|
});
|