feat(timeline): offer both readings of a board with the future on it

"In their own group" is the shape of a patch — what is on now, and what
is queued behind it. "Mixed in" is the question a Gantt chart exists for:
what runs out first, whether or not it has opened. On real data that is
not a cosmetic difference — mixed, five events currently running sort
below an Arknights rerun that has not started, because it closes before
they do.

Which is why it is a pair of pills and not a checkbox: neither answer is
the absence of the other, and "mixed in" is a different order rather than
the heading switched off. It sits under the row that puts the future on
the board at all, and only while that row is ticked — a choice about
arranging unstarted events is unanswerable with none of them on screen.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
Lucas Winther
2026-08-19 06:05:58 +02:00
co-authored by Claude Opus 5
parent 9dde82801a
commit 3db93a1ccf
5 changed files with 145 additions and 3 deletions
+27
View File
@@ -93,6 +93,33 @@ describe("Controls: what am I allowed to look at", () => {
expect(on.filter(Boolean)).toHaveLength(2);
});
test("how unstarted events sit on the board is offered only when they are", () => {
// A choice about arranging them is unanswerable with none on the board,
// and a control that changes nothing visible is worse than none.
expect(render(PREFS)).not.toContain("Mixed in");
const on = render({ ...PREFS, timelineUpcoming: true });
expect(on).toContain("In their own group");
expect(on).toContain("Mixed in");
});
test("it is a pair of answers, not one answer and its absence", () => {
// "Mixed in" is a different order, not a heading switched off, so both
// states name themselves and the panel says which is on.
const split = render({ ...PREFS, timelineUpcoming: true });
const mixed = render({
...PREFS,
timelineUpcoming: true,
timelineSplitUpcoming: false,
});
const pressed = (html: string) =>
[...html.matchAll(/aria-pressed="true"[^>]*>([^<]+)</g)].map((m) => m[1]);
expect(pressed(split)).toContain("In their own group");
expect(pressed(mixed)).toContain("Mixed in");
// And the line under them describes the answer that is actually on.
expect(mixed).toContain("One deadline order");
expect(split).not.toContain("One deadline order");
});
test("the ignored row appears only once something is ignored", () => {
// Nothing to restore means nothing to offer — the row would be a filter
// over an empty set.