fix: align prepared lifecycle discussion rail

This commit is contained in:
lda
2026-07-13 13:58:41 +07:00 Verified
parent 801f82b3b3
commit 4b7c01c69d
7 changed files with 111 additions and 16 deletions
@@ -1,16 +1,24 @@
import { cleanup, render, screen, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { afterEach, describe, expect, it, vi } from "vitest";
import type { ReactNode } from "react";
import { findBeat, findScene } from "../storyboard.js";
import { PreparedAuthoringLifecycleScene } from "./PreparedAuthoringLifecycleScene.js";
afterEach(() => cleanup());
const renderBeat = (beatId: string, onAdvance?: () => void) => {
const renderBeat = (beatId: string, onAdvance?: () => void, discussionRail?: ReactNode) => {
const scene = findScene("prepared-lifecycle");
const beat = findBeat("prepared-lifecycle", beatId);
if (!scene || !beat) throw new Error(`missing prepared-lifecycle/${beatId}`);
return render(<PreparedAuthoringLifecycleScene scene={scene} beat={beat} onAdvance={onAdvance} />);
return render(
<PreparedAuthoringLifecycleScene
scene={scene}
beat={beat}
onAdvance={onAdvance}
discussionRail={discussionRail}
/>,
);
};
describe("PreparedAuthoringLifecycleScene", () => {
@@ -206,6 +214,23 @@ describe("PreparedAuthoringLifecycleScene", () => {
expect(within(rail).getByText("Sources, capabilities, schemas")).toBeInTheDocument();
});
it("places defense questions in a presentation-only grid row", () => {
renderBeat(
"diagnose",
undefined,
<aside aria-label="defense discussion topics">Defense questions</aside>,
);
const workspace = screen.getByRole("region", { name: "prepared workflow authoring lifecycle" });
const discussion = workspace.querySelector(".prepared-lifecycle-scene__discussion");
expect(discussion?.tagName).toBe("SECTION");
expect(discussion).toHaveAttribute("data-discussion-placement", "presentation-column");
expect(discussion?.parentElement).toBe(workspace);
expect(discussion).toContainElement(screen.getByLabelText("defense discussion topics"));
expect(workspace.querySelector(".presentation-assistant-pane")?.parentElement).toBe(workspace);
});
it("highlights the active phase in the rail", () => {
renderBeat("deployment");
const rail = screen.getByRole("list", { name: /prepared authoring lifecycle/i });
@@ -1,4 +1,5 @@
import { useReducer } from "react";
import type { ReactNode } from "react";
import {
projectPreparedLifecycleStep,
type PreparedLifecycleStepId,
@@ -17,6 +18,7 @@ type PreparedAuthoringLifecycleSceneProps = {
readonly scene: SceneDefinition;
readonly beat: SceneBeatDefinition;
readonly onAdvance?: (() => void) | undefined;
readonly discussionRail?: ReactNode;
};
const steps = [
@@ -38,7 +40,7 @@ const steps = [
* Each beat shows a persistent prepared assistant beside one dominant phase
* projection sourced from the prepared authoring recording.
*/
export const PreparedAuthoringLifecycleScene = ({ scene, beat, onAdvance }: PreparedAuthoringLifecycleSceneProps) => {
export const PreparedAuthoringLifecycleScene = ({ scene, beat, onAdvance, discussionRail }: PreparedAuthoringLifecycleSceneProps) => {
const [messageState, dispatch] = useReducer(
preparedLifecycleMessageReducer,
initialPreparedLifecycleMessageState,
@@ -128,6 +130,15 @@ export const PreparedAuthoringLifecycleScene = ({ scene, beat, onAdvance }: Prep
<AuthoringPhaseVisual projection={projection} focus={projection.focus} />
</article>
</div>
{discussionRail && (
<section
className="prepared-lifecycle-scene__discussion"
data-discussion-placement="presentation-column"
aria-label="prepared lifecycle defense questions"
>
{discussionRail}
</section>
)}
</section>
);
};