draft: the pick bar comes to the new-practice screen (round 10 slice 2)
The SAME pill the home screen carries — above the composer, dashed 'Pick propositions from the map' when empty, count + Clear when picked — now lives on the /chat/new?mode=practice draft. The draft has no graph behind it, so the pill opens the standalone ChainTableOverlay (select mode, controlled selection, Done bar) over the /api/graph snapshot, fetched lazily on first open. The first send carries the picks as graphContext: the created session is born WITH its ids-first context marker (target validation's truth set) instead of pickless — the same contract as the home screen's practice send. Teach drafts render no pill; practice with no picks stays lean on the wire.
This commit is contained in:
@@ -124,6 +124,61 @@ describe("the practice draft (?mode=practice, 2026-10-04)", () => {
|
||||
assert.match(html, /What do you want to practice\?/);
|
||||
});
|
||||
|
||||
it("THE PICK BAR (owner round 10): the practice draft carries the same pill above its composer", () => {
|
||||
const html = renderToStaticMarkup(
|
||||
<DraftShell
|
||||
busy={false}
|
||||
text=""
|
||||
onChange={() => {}}
|
||||
onSend={() => {}}
|
||||
mode="practice"
|
||||
/>,
|
||||
);
|
||||
assert.match(html, /Pick propositions from the map/);
|
||||
// picked: count + Clear, the home register verbatim
|
||||
const picked = renderToStaticMarkup(
|
||||
<DraftShell
|
||||
busy={false}
|
||||
text=""
|
||||
onChange={() => {}}
|
||||
onSend={() => {}}
|
||||
mode="practice"
|
||||
picks={[{ id: "n_1", name: "Parity", statement: "Odd + odd = even." }]}
|
||||
/>,
|
||||
);
|
||||
assert.match(picked, /1 proposition selected/);
|
||||
assert.match(picked, /Clear/);
|
||||
// teach mode with nothing picked renders nothing (home's law)
|
||||
const teach = renderToStaticMarkup(
|
||||
<DraftShell busy={false} text="" onChange={() => {}} onSend={() => {}} mode="teach" />,
|
||||
);
|
||||
assert.ok(!teach.includes("Pick propositions from the map"));
|
||||
});
|
||||
|
||||
it("startDraftSession carries the picks as graphContext (the home practice contract)", async () => {
|
||||
let body: Record<string, unknown> | null = null;
|
||||
const f = (async (_url: string, init?: RequestInit) => {
|
||||
body = JSON.parse(String(init?.body));
|
||||
return new Response(JSON.stringify({ id: "c10" }), { status: 201 });
|
||||
}) as unknown as typeof fetch;
|
||||
await startDraftSession("drill me", f, "practice", [{ id: "n_1", name: "Parity", statement: "Odd + odd = even." }]);
|
||||
assert.deepEqual(body, {
|
||||
title: "drill me",
|
||||
kind: "practice",
|
||||
graphContext: { propositions: [{ id: "n_1", name: "Parity", statement: "Odd + odd = even." }] },
|
||||
});
|
||||
});
|
||||
|
||||
it("practice with NO picks stays lean — no graphContext key on the wire", async () => {
|
||||
let body: Record<string, unknown> | null = null;
|
||||
const f = (async (_url: string, init?: RequestInit) => {
|
||||
body = JSON.parse(String(init?.body));
|
||||
return new Response(JSON.stringify({ id: "c11" }), { status: 201 });
|
||||
}) as unknown as typeof fetch;
|
||||
await startDraftSession("free practice", f, "practice", []);
|
||||
assert.deepEqual(body, { title: "free practice", kind: "practice" });
|
||||
});
|
||||
|
||||
it("startDraftSession carries kind:\"practice\" to the create route", async () => {
|
||||
let body: Record<string, unknown> | null = null;
|
||||
const f = (async (_url: string, init?: RequestInit) => {
|
||||
|
||||
Reference in New Issue
Block a user