fix: stabilize conclusion map layout
This commit is contained in:
@@ -58,3 +58,69 @@ The model test passed with 3 tests. The component test passed with 7 tests. The
|
|||||||
- Confirmed the limits beat marks non-claims, and the conclusion beat marks future work as `receded`.
|
- Confirmed the limits beat marks non-claims, and the conclusion beat marks future work as `receded`.
|
||||||
- Confirmed the diagram uses semantic HTML and CSS connectors rather than React Flow, with responsive wrapping and a single cyan substrate emphasis.
|
- Confirmed the diagram uses semantic HTML and CSS connectors rather than React Flow, with responsive wrapping and a single cyan substrate emphasis.
|
||||||
- Confirmed no factual production, scheduler, or broad benchmark claim was added.
|
- Confirmed no factual production, scheduler, or broad benchmark claim was added.
|
||||||
|
|
||||||
|
## Review Fixes
|
||||||
|
|
||||||
|
### RED
|
||||||
|
|
||||||
|
Command:
|
||||||
|
|
||||||
|
```text
|
||||||
|
pnpm --dir web --filter @lda/console test -- src/presentation/conclusion/ConclusionScene.test.tsx src/presentation/presentation-css.test.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
Result: `3 failed, 10 passed` across 2 files. The failures were the missing explicit 1080px node placements, the cyan future-work SVG rule, and missing neutral icon attributes.
|
||||||
|
|
||||||
|
### GREEN
|
||||||
|
|
||||||
|
Command:
|
||||||
|
|
||||||
|
```text
|
||||||
|
pnpm --dir web --filter @lda/console test -- src/presentation/conclusion/ConclusionScene.test.tsx src/presentation/presentation-css.test.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
Result: `2 passed`, `13 passed`.
|
||||||
|
|
||||||
|
### Requested Verification
|
||||||
|
|
||||||
|
Command:
|
||||||
|
|
||||||
|
```text
|
||||||
|
pnpm --dir web --filter @lda/console test -- src/presentation/conclusion/conclusion-model.test.ts src/presentation/conclusion/ConclusionScene.test.tsx
|
||||||
|
```
|
||||||
|
|
||||||
|
Result: `2 passed`, `12 passed`.
|
||||||
|
|
||||||
|
Command:
|
||||||
|
|
||||||
|
```text
|
||||||
|
pnpm --dir web --filter @lda/console test -- src/presentation/presentation-css.test.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
Result: `1 passed`, `4 passed`.
|
||||||
|
|
||||||
|
Command:
|
||||||
|
|
||||||
|
```text
|
||||||
|
pnpm --dir web --filter @lda/console typecheck
|
||||||
|
```
|
||||||
|
|
||||||
|
Result: `tsc -b --pretty false` passed.
|
||||||
|
|
||||||
|
### Final Rerun After Connector Contract
|
||||||
|
|
||||||
|
```text
|
||||||
|
pnpm --dir web --filter @lda/console test -- src/presentation/conclusion/conclusion-model.test.ts src/presentation/conclusion/ConclusionScene.test.tsx
|
||||||
|
Test Files 2 passed (2)
|
||||||
|
Tests 12 passed (12)
|
||||||
|
|
||||||
|
pnpm --dir web --filter @lda/console test -- src/presentation/presentation-css.test.ts
|
||||||
|
Test Files 1 passed (1)
|
||||||
|
Tests 4 passed (4)
|
||||||
|
|
||||||
|
pnpm --dir web --filter @lda/console typecheck
|
||||||
|
$ tsc -b --pretty false
|
||||||
|
|
||||||
|
git diff --check
|
||||||
|
passed
|
||||||
|
```
|
||||||
|
|||||||
@@ -76,4 +76,24 @@ describe("ConclusionScene", () => {
|
|||||||
expect(screen.getByText("Planner proposes; runtime executes.")).toBeInTheDocument();
|
expect(screen.getByText("Planner proposes; runtime executes.")).toBeInTheDocument();
|
||||||
expect(screen.getByRole("list", { name: "future work layers" })).toHaveAttribute("data-state", "receded");
|
expect(screen.getByRole("list", { name: "future work layers" })).toHaveAttribute("data-state", "receded");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("keeps evidence as a separately identified node after the runtime", () => {
|
||||||
|
render(<ConclusionScene scene={scene} beat={beat("future")} />);
|
||||||
|
const nodes = [...screen.getByLabelText("contribution flow").children];
|
||||||
|
expect(nodes.map((node) => node.getAttribute("data-node-id"))).toEqual([
|
||||||
|
"planner",
|
||||||
|
"substrate",
|
||||||
|
"runtime",
|
||||||
|
"evidence",
|
||||||
|
]);
|
||||||
|
expect(nodes[2]).toHaveAttribute("data-node-id", "runtime");
|
||||||
|
expect(nodes[3]).toHaveAttribute("data-node-id", "evidence");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("marks every future-work icon neutral while substrate stays the sole emphasis", () => {
|
||||||
|
render(<ConclusionScene scene={scene} beat={beat("future")} />);
|
||||||
|
const map = screen.getByRole("region", { name: "thesis contribution boundary" });
|
||||||
|
expect(map.querySelector('[data-node-id="substrate"]')).toHaveAttribute("data-emphasis", "substrate");
|
||||||
|
expect([...map.querySelectorAll(".conclusion-map__future svg")].every((icon) => icon.getAttribute("data-emphasis") === "neutral")).toBe(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ export const ConclusionScene: FC<ConclusionSceneProps> = ({ scene, beat }) => {
|
|||||||
const Icon = futureWorkIcons[branch.icon];
|
const Icon = futureWorkIcons[branch.icon];
|
||||||
return (
|
return (
|
||||||
<li key={branch.id} data-future-work-id={branch.id}>
|
<li key={branch.id} data-future-work-id={branch.id}>
|
||||||
<Icon aria-hidden="true" />
|
<Icon aria-hidden="true" data-emphasis="neutral" />
|
||||||
<span>
|
<span>
|
||||||
<strong>{branch.label}</strong>
|
<strong>{branch.label}</strong>
|
||||||
<small>{branch.example}</small>
|
<small>{branch.example}</small>
|
||||||
|
|||||||
@@ -21,4 +21,17 @@ describe("presentation.css", () => {
|
|||||||
|
|
||||||
expect(breakpointBlock).toMatch(/\.evaluation-board__audit-row\s*\{\s*grid-template-columns: 1fr;/);
|
expect(breakpointBlock).toMatch(/\.evaluation-board__audit-row\s*\{\s*grid-template-columns: 1fr;/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("places the conclusion evidence beneath substrate at the 1080px breakpoint", () => {
|
||||||
|
expect(css).toMatch(/\.conclusion-map__node--planner\s*\{\s*grid-column: 1;\s*grid-row: 1;/);
|
||||||
|
expect(css).toMatch(/\.conclusion-map__node--substrate\s*\{\s*grid-column: 2;\s*grid-row: 1;/);
|
||||||
|
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/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps future-work icons neutral by default", () => {
|
||||||
|
expect(css).toMatch(/\.conclusion-map__future svg\s*\{[^}]*color: var\(--text-secondary\);/s);
|
||||||
|
expect(css).not.toMatch(/\.conclusion-map__future svg\s*\{[^}]*color: var\(--accent-cyan\);/s);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -287,7 +287,8 @@
|
|||||||
background: oklch(0.19 0.03 250 / 88%);
|
background: oklch(0.19 0.03 250 / 88%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.conclusion-map__node:not(:last-child)::after {
|
.conclusion-map__node--planner::after,
|
||||||
|
.conclusion-map__node--substrate::after {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
right: -0.85rem;
|
right: -0.85rem;
|
||||||
@@ -352,7 +353,12 @@
|
|||||||
transition: opacity 180ms ease, transform 180ms ease;
|
transition: opacity 180ms ease, transform 180ms ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.conclusion-map__future svg { flex: 0 0 auto; width: 1.1rem; height: 1.1rem; color: var(--accent-cyan); }
|
.conclusion-map__future svg {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
width: 1.1rem;
|
||||||
|
height: 1.1rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
.conclusion-map__future span { display: grid; gap: 0.2rem; }
|
.conclusion-map__future span { display: grid; gap: 0.2rem; }
|
||||||
.conclusion-map__future strong { font-size: 0.8rem; }
|
.conclusion-map__future strong { font-size: 0.8rem; }
|
||||||
.conclusion-map__future small { color: var(--text-secondary); font-family: "Source Sans 3", sans-serif; font-size: 0.76rem; }
|
.conclusion-map__future small { color: var(--text-secondary); font-family: "Source Sans 3", sans-serif; font-size: 0.76rem; }
|
||||||
@@ -371,6 +377,19 @@
|
|||||||
.conclusion-map__future { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
.conclusion-map__future { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1080px) {
|
||||||
|
.conclusion-map__flow {
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
grid-template-rows: auto auto;
|
||||||
|
gap: 0.8rem 1.7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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; }
|
||||||
|
}
|
||||||
|
|
||||||
@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:not(:last-child)::after {
|
.conclusion-map__node:not(:last-child)::after {
|
||||||
|
|||||||
Reference in New Issue
Block a user