Files
learnharness/tests/graph-view-filters.test.ts
T
amosblomqvist 094bafaf2d Legend/filters panel: search + per-state visibility toggles
The top-right overlay doubles as the state legend and the filter
surface: a search box (matches name or statement, case-insensitive)
plus one row per node state in reading order (taught, known, missed,
don't know, candidate) with its color dot, label, and total count.
Clicking a row toggles that state's visibility.

Filtering flows through sigma's official nodeReducer/edgeReducer
settings: hidden nodes drop out of rendering, hover, picking, AND
labels; edges drop when either endpoint is hidden; an open detail card
closes if its node is filtered out. Pure predicate (isNodeVisible) is
exported and covered by 7 tests; the reducers just consume its
verdict. Verified live: search 'Nietzsche' 5457->1687->5457 label px
(exact restore), known toggle drops 13 discs+labels and restores,
nonsense query shows the 'No matching nodes.' hint with zero labels,
counts 0/13/0/0/10 match the ledger.
2026-09-05 21:35:07 +03:00

48 lines
1.9 KiB
TypeScript

/**
* Legend/filters predicate tests — the pure half of the graph-view filters
* panel (isNodeVisible); the sigma reducers just consume its verdict.
*/
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import { isNodeVisible } from "../components/graph-view.tsx";
import type { NodeStateClass } from "../src/core/index.ts";
const none: ReadonlySet<NodeStateClass> = new Set();
const node = { name: "Money multiplier", statement: "The money multiplier is the maximum ratio of broad money to base money." };
describe("isNodeVisible (legend/filters predicate)", () => {
it("is visible with no filters", () => {
assert.equal(isNodeVisible(node, "known", none, ""), true);
});
it("is hidden when its state is toggled off, even if the query matches", () => {
const hidden = new Set<NodeStateClass>(["known"]);
assert.equal(isNodeVisible(node, "known", hidden, "money"), false);
});
it("a toggled-off state does not affect other states", () => {
const hidden = new Set<NodeStateClass>(["known"]);
assert.equal(isNodeVisible(node, "taught", hidden, ""), true);
});
it("search matches the name case-insensitively", () => {
assert.equal(isNodeVisible(node, "candidate", none, "MONEY multiplier"), true);
});
it("search matches the statement when the name does not", () => {
assert.equal(isNodeVisible(node, "candidate", none, "maximum ratio"), true);
assert.equal(isNodeVisible(node, "candidate", none, " velocity"), false);
});
it("whitespace-only query is no filter", () => {
assert.equal(isNodeVisible(node, "missed", none, " "), true);
});
it("a node with no state entry defaults to candidate", () => {
// candidate hidden → invisible; visible otherwise.
const hidden = new Set<NodeStateClass>(["candidate"]);
assert.equal(isNodeVisible(node, "candidate", hidden, ""), false);
assert.equal(isNodeVisible(node, "candidate", none, ""), true);
});
});