proposition pick: persistent lifted selection, live count, honest affordances
Root cause of 'doesn't register': the slice-6 floating-bar edit never landed (a silent no-match replace) — the Body accepted selectMode but the bar ignored it, so the picker showed Clear + Delete and nothing ever confirmed. Found by re-reading; the test suite never pinned the select-mode bar. The owner vision instead: the chain table's selection is PERSISTENT and caller-owned. - ChainTableOverlay gains controlledSelection/onSelectionChange (every check, clear, and delete-prune reports up immediately; uncontrolled fallback keeps the delete flow self-contained). - Select mode's floating bar: Done (closes; the pick already registered) — the delete fence never renders while picking. - GraphView threads the lifted set through, resolving ids → nodes against the live snapshot. - Home: the pick bar above the composer shows 'N propositions selected' + Clear in EITHER mode (dashed 'Pick propositions from the map' hint only in practice mode with nothing picked); visible hover (bg-accent) so the chip reads clickable. Practice sends still seed from the pick set.
This commit is contained in:
@@ -27,7 +27,7 @@ import React from "react";
|
||||
import assert from "node:assert/strict";
|
||||
import { renderToStaticMarkup } from "react-dom/server";
|
||||
import { describe, it } from "node:test";
|
||||
import { ChainTableBody } from "../components/chain-table.tsx";
|
||||
import { ChainTableBody, ChainTableOverlay } from "../components/chain-table.tsx";
|
||||
import type { ChainSort } from "../components/chain-table.tsx";
|
||||
import type { GraphChain, GraphNode } from "../src/core/contracts.ts";
|
||||
|
||||
@@ -319,3 +319,41 @@ describe("ChainTableBody — the fuzzy search (grouped + auto-expand)", () => {
|
||||
assert.ok(html.includes("No matching chains."));
|
||||
});
|
||||
});
|
||||
|
||||
describe("ChainTableOverlay — the lifted practice pick (owner vision, 2026-10-04)", () => {
|
||||
function overlay(extra: Record<string, unknown> = {}) {
|
||||
return renderToStaticMarkup(
|
||||
React.createElement(ChainTableOverlay, {
|
||||
open: true,
|
||||
nodes: NODES,
|
||||
chains: CHAINS,
|
||||
sessions: { chat1: "Ethics lesson", chat2: "Follow-up", notes1: "Notes" },
|
||||
fileSources: {},
|
||||
onClose: () => {},
|
||||
...extra,
|
||||
} as never),
|
||||
);
|
||||
}
|
||||
|
||||
it("CONTROLLED selection renders the caller's set — the bar counts it live", () => {
|
||||
const html = overlay({ controlledSelection: new Set(["a", "b", "c1"]) });
|
||||
assert.ok(html.includes("3 nodes → 1 chain"), "the bar reflects the lifted set, not internal state");
|
||||
});
|
||||
|
||||
it("select mode: Done replaces the delete fence — no way to delete while picking", () => {
|
||||
const html = overlay({
|
||||
selectMode: true,
|
||||
controlledSelection: new Set(["a", "b"]),
|
||||
});
|
||||
assert.ok(html.includes('data-testid="chain-practice-done"'));
|
||||
assert.ok(html.includes("Done"));
|
||||
assert.ok(!html.includes('data-testid="chain-delete"'), "no delete while picking");
|
||||
assert.ok(!html.includes('data-testid="chain-danger-divider"'));
|
||||
});
|
||||
|
||||
it("browse mode unchanged: the fenced Delete remains", () => {
|
||||
const html = overlay({ controlledSelection: new Set(["a"]) });
|
||||
assert.ok(html.includes('data-testid="chain-delete"'));
|
||||
assert.ok(!html.includes('data-testid="chain-practice-done"'));
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user