fix: structure narrow conclusion flow
This commit is contained in:
@@ -0,0 +1,23 @@
|
|||||||
|
# Narrow Conclusion Flow Fix Report
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
- Refactored the conclusion contribution flow into planner, substrate stack, and runtime semantic grid units.
|
||||||
|
- Kept the typed substrate and persisted evidence vertically attached within the substrate stack.
|
||||||
|
- Assigned wide and narrow outer connectors to the planner and substrate-stack units so narrow flow is planner, substrate stack, runtime.
|
||||||
|
|
||||||
|
## TDD Record
|
||||||
|
|
||||||
|
- RED: `pnpm --dir web\\apps\\console test -- src/presentation/conclusion/ConclusionScene.test.tsx src/presentation/presentation-css.test.ts`
|
||||||
|
- Result: 2 files failed, 3 tests failed. The component still rendered four direct flow children and CSS still owned outer connectors at individual nodes.
|
||||||
|
- GREEN: `pnpm --dir web\\apps\\console test -- src/presentation/conclusion/conclusion-model.test.ts src/presentation/conclusion/ConclusionScene.test.tsx src/presentation/presentation-css.test.ts src/presentation/SceneBody.test.tsx; pnpm --dir web\\apps\\console typecheck`
|
||||||
|
- Result: 4 files passed, 38 tests passed; typecheck passed.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- `git diff --check` passed.
|
||||||
|
- No unrelated files changed.
|
||||||
|
|
||||||
|
## Concerns
|
||||||
|
|
||||||
|
- None.
|
||||||
@@ -79,14 +79,15 @@ describe("ConclusionScene", () => {
|
|||||||
|
|
||||||
it("attaches evidence vertically beneath the typed substrate rather than extending the contribution line", () => {
|
it("attaches evidence vertically beneath the typed substrate rather than extending the contribution line", () => {
|
||||||
render(<ConclusionScene scene={scene} beat={beat("future")} />);
|
render(<ConclusionScene scene={scene} beat={beat("future")} />);
|
||||||
const executionNodes = [...screen.getByLabelText("contribution flow").children]
|
const flow = screen.getByLabelText("contribution flow");
|
||||||
.filter((node) => node.getAttribute("data-evidence-attachment") !== "vertical");
|
expect([...flow.children].map((unit) => unit.getAttribute("data-flow-unit"))).toEqual([
|
||||||
expect(executionNodes.map((node) => node.getAttribute("data-node-id"))).toEqual([
|
|
||||||
"planner",
|
"planner",
|
||||||
"substrate",
|
"substrate-stack",
|
||||||
"runtime",
|
"runtime",
|
||||||
]);
|
]);
|
||||||
expect(screen.getByText("Persisted, inspectable evidence").closest("[data-node-id='evidence']"))
|
expect(flow.querySelector('[data-flow-unit="substrate-stack"] [data-node-id="substrate"]'))
|
||||||
|
.toHaveTextContent("Typed workflow substrate");
|
||||||
|
expect(flow.querySelector('[data-flow-unit="substrate-stack"] [data-node-id="evidence"]'))
|
||||||
.toHaveAttribute("data-evidence-attachment", "vertical");
|
.toHaveAttribute("data-evidence-attachment", "vertical");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -35,25 +35,44 @@ export const ConclusionScene: FC<ConclusionSceneProps> = ({ scene, beat }) => {
|
|||||||
</StageCaption>
|
</StageCaption>
|
||||||
<section className="conclusion-map" aria-label="thesis contribution boundary" data-conclusion-beat={beatId}>
|
<section className="conclusion-map" aria-label="thesis contribution boundary" data-conclusion-beat={beatId}>
|
||||||
<div className="conclusion-map__flow" aria-label="contribution flow">
|
<div className="conclusion-map__flow" aria-label="contribution flow">
|
||||||
{contributionNodes.map((node, index) => (
|
<div className="conclusion-map__flow-unit conclusion-map__flow-unit--planner" data-flow-unit="planner">
|
||||||
<div
|
<div
|
||||||
className={`conclusion-map__node conclusion-map__node--${node.id}`}
|
className="conclusion-map__node conclusion-map__node--planner"
|
||||||
data-node-id={node.id}
|
data-node-id={contributionNodes[0].id}
|
||||||
data-emphasis={node.id === "substrate" ? "substrate" : "neutral"}
|
data-emphasis="neutral"
|
||||||
key={node.id}
|
|
||||||
>
|
>
|
||||||
<span className="conclusion-map__node-index">0{index + 1}</span>
|
<span className="conclusion-map__node-index">01</span>
|
||||||
<strong>{node.label}</strong>
|
<strong>{contributionNodes[0].label}</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="conclusion-map__flow-unit conclusion-map__flow-unit--substrate-stack" data-flow-unit="substrate-stack">
|
||||||
|
<div
|
||||||
|
className="conclusion-map__node conclusion-map__node--substrate"
|
||||||
|
data-node-id={contributionNodes[1].id}
|
||||||
|
data-emphasis="substrate"
|
||||||
|
>
|
||||||
|
<span className="conclusion-map__node-index">02</span>
|
||||||
|
<strong>{contributionNodes[1].label}</strong>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={`conclusion-map__node conclusion-map__node--${evidenceNode.id}`}
|
||||||
|
data-evidence-attachment="vertical"
|
||||||
|
data-node-id={evidenceNode.id}
|
||||||
|
>
|
||||||
|
<span className="conclusion-map__node-index">04</span>
|
||||||
|
<strong>{evidenceNode.label}</strong>
|
||||||
|
<small>saved traces and receipts</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="conclusion-map__flow-unit conclusion-map__flow-unit--runtime" data-flow-unit="runtime">
|
||||||
|
<div
|
||||||
|
className="conclusion-map__node conclusion-map__node--runtime"
|
||||||
|
data-node-id={contributionNodes[2].id}
|
||||||
|
data-emphasis="neutral"
|
||||||
|
>
|
||||||
|
<span className="conclusion-map__node-index">03</span>
|
||||||
|
<strong>{contributionNodes[2].label}</strong>
|
||||||
</div>
|
</div>
|
||||||
))}
|
|
||||||
<div
|
|
||||||
className={`conclusion-map__node conclusion-map__node--${evidenceNode.id}`}
|
|
||||||
data-evidence-attachment="vertical"
|
|
||||||
data-node-id={evidenceNode.id}
|
|
||||||
>
|
|
||||||
<span className="conclusion-map__node-index">04</span>
|
|
||||||
<strong>{evidenceNode.label}</strong>
|
|
||||||
<small>saved traces and receipts</small>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -31,12 +31,10 @@ describe("presentation.css", () => {
|
|||||||
expect(boardBlock).toContain("flex-shrink: 0");
|
expect(boardBlock).toContain("flex-shrink: 0");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("keeps evidence vertically attached beneath substrate from wide desktop through the 1080px breakpoint", () => {
|
it("keeps evidence inside a substrate stack from wide desktop through the 1080px breakpoint", () => {
|
||||||
expect(css).toMatch(/\.conclusion-map__flow\s*\{\s*display: grid;\s*grid-template-columns: repeat\(3, minmax\(0, 1fr\)\);\s*grid-template-rows: auto auto;/);
|
expect(css).toMatch(/\.conclusion-map__flow\s*\{\s*display: grid;\s*grid-template-columns: repeat\(3, minmax\(0, 1fr\)\);/);
|
||||||
expect(css).toMatch(/\.conclusion-map__node--planner\s*\{\s*grid-column: 1;\s*grid-row: 1;/);
|
expect(css).toMatch(/\.conclusion-map__flow-unit--substrate-stack\s*\{[\s\S]*?grid-template-rows: auto auto;/);
|
||||||
expect(css).toMatch(/\.conclusion-map__node--substrate\s*\{\s*grid-column: 2;\s*grid-row: 1;/);
|
expect(css).toMatch(/\.conclusion-map__flow-unit--planner::after,\s*\.conclusion-map__flow-unit--substrate-stack::after/);
|
||||||
expect(css).toMatch(/\.conclusion-map__node--runtime\s*\{\s*grid-column: 3;\s*grid-row: 1;/);
|
|
||||||
expect(css).toMatch(/\.conclusion-map__node--evidence\s*\{\s*grid-column: 2;\s*grid-row: 2;/);
|
|
||||||
expect(css).not.toMatch(/\.conclusion-map__node--runtime::after/);
|
expect(css).not.toMatch(/\.conclusion-map__node--runtime::after/);
|
||||||
expect(css).toMatch(/\.conclusion-map__node--evidence::before\s*\{[\s\S]*?content: "↓";/);
|
expect(css).toMatch(/\.conclusion-map__node--evidence::before\s*\{[\s\S]*?content: "↓";/);
|
||||||
});
|
});
|
||||||
@@ -52,10 +50,11 @@ describe("presentation.css", () => {
|
|||||||
expect(css).not.toMatch(/\.conclusion-map__future svg\s*\{[^}]*color: var\(--accent-cyan\);/s);
|
expect(css).not.toMatch(/\.conclusion-map__future svg\s*\{[^}]*color: var\(--accent-cyan\);/s);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("resets conclusion node placement and keeps connectors node-specific on narrow screens", () => {
|
it("stacks the same three flow units on narrow screens while keeping the evidence connector internal", () => {
|
||||||
expect(css).toMatch(
|
expect(css).toMatch(
|
||||||
/@media \(max-width: 640px\) \{[\s\S]*?\.conclusion-map__node--planner,\s*\.conclusion-map__node--substrate,\s*\.conclusion-map__node--runtime,\s*\.conclusion-map__node--evidence\s*\{\s*grid-column: auto;\s*grid-row: auto;/,
|
/@media \(max-width: 640px\) \{[\s\S]*?\.conclusion-map__flow\s*\{\s*grid-template-columns: 1fr;[\s\S]*?\.conclusion-map__flow-unit--planner::after,\s*\.conclusion-map__flow-unit--substrate-stack::after\s*\{[\s\S]*?content: "↓";/,
|
||||||
);
|
);
|
||||||
|
expect(css).toMatch(/@media \(max-width: 640px\) \{[\s\S]*?\.conclusion-map__node--evidence::before\s*\{[\s\S]*?content: "↓";/);
|
||||||
expect(css).not.toContain(".conclusion-map__node:not(:last-child)::after");
|
expect(css).not.toContain(".conclusion-map__node:not(:last-child)::after");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -276,11 +276,22 @@
|
|||||||
.conclusion-map__flow {
|
.conclusion-map__flow {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
grid-template-rows: auto auto;
|
|
||||||
gap: 0.8rem 1.7rem;
|
gap: 0.8rem 1.7rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.conclusion-map__flow-unit {
|
||||||
|
position: relative;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Evidence stays inside the substrate unit at every breakpoint. */
|
||||||
|
.conclusion-map__flow-unit--substrate-stack {
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: auto auto;
|
||||||
|
gap: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
.conclusion-map__node {
|
.conclusion-map__node {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: grid;
|
display: grid;
|
||||||
@@ -292,10 +303,11 @@
|
|||||||
background: oklch(0.19 0.03 250 / 88%);
|
background: oklch(0.19 0.03 250 / 88%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.conclusion-map__node--planner::after,
|
.conclusion-map__flow-unit--planner::after,
|
||||||
.conclusion-map__node--substrate::after {
|
.conclusion-map__flow-unit--substrate-stack::after {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
/* Align wide connectors with the substrate node, not the full two-node stack. */
|
||||||
|
top: 2.75rem;
|
||||||
right: -0.85rem;
|
right: -0.85rem;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
width: 1.7rem;
|
width: 1.7rem;
|
||||||
@@ -321,11 +333,6 @@
|
|||||||
line-height: 0;
|
line-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.conclusion-map__node--planner { grid-column: 1; grid-row: 1; }
|
|
||||||
.conclusion-map__node--substrate { grid-column: 2; grid-row: 1; }
|
|
||||||
.conclusion-map__node--runtime { grid-column: 3; grid-row: 1; }
|
|
||||||
.conclusion-map__node--evidence { grid-column: 2; grid-row: 2; }
|
|
||||||
|
|
||||||
.conclusion-map__node[data-emphasis="substrate"] {
|
.conclusion-map__node[data-emphasis="substrate"] {
|
||||||
border-color: var(--accent-cyan);
|
border-color: var(--accent-cyan);
|
||||||
box-shadow: inset 0 0 0 1px oklch(0.72 0.17 195 / 20%);
|
box-shadow: inset 0 0 0 1px oklch(0.72 0.17 195 / 20%);
|
||||||
@@ -409,16 +416,9 @@
|
|||||||
|
|
||||||
@media (max-width: 640px) {
|
@media (max-width: 640px) {
|
||||||
.conclusion-map__flow { grid-template-columns: 1fr; gap: 0.85rem; }
|
.conclusion-map__flow { grid-template-columns: 1fr; gap: 0.85rem; }
|
||||||
.conclusion-map__node--planner,
|
|
||||||
.conclusion-map__node--substrate,
|
|
||||||
.conclusion-map__node--runtime,
|
|
||||||
.conclusion-map__node--evidence {
|
|
||||||
grid-column: auto;
|
|
||||||
grid-row: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.conclusion-map__node--planner::after,
|
.conclusion-map__flow-unit--planner::after,
|
||||||
.conclusion-map__node--substrate::after {
|
.conclusion-map__flow-unit--substrate-stack::after {
|
||||||
top: auto;
|
top: auto;
|
||||||
right: 50%;
|
right: 50%;
|
||||||
bottom: -0.85rem;
|
bottom: -0.85rem;
|
||||||
@@ -426,6 +426,9 @@
|
|||||||
border-top: 0;
|
border-top: 0;
|
||||||
content: "↓";
|
content: "↓";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Reassert the internal attachment without turning evidence into a flow unit. */
|
||||||
|
.conclusion-map__node--evidence::before { content: "↓"; }
|
||||||
.conclusion-map__non-claims,
|
.conclusion-map__non-claims,
|
||||||
.conclusion-map__future { grid-template-columns: 1fr; }
|
.conclusion-map__future { grid-template-columns: 1fr; }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user