fix: structure discussion modal regions

This commit is contained in:
lda
2026-07-09 05:09:02 +07:00 Verified
parent e93a91a0d5
commit e63b17877a
2 changed files with 80 additions and 42 deletions
@@ -113,6 +113,27 @@ describe("DiscussionPanel", () => {
expect(screen.getByLabelText("answer provenance")).toHaveTextContent(/Workflow Automation Platforms/i); expect(screen.getByLabelText("answer provenance")).toHaveTextContent(/Workflow Automation Platforms/i);
}); });
it("renders Q&A discussion as shell, body, support, and actions regions", () => {
render(<DiscussionPanel branchId="where-is-ai-agent" onClose={onClose} />);
expect(screen.getByRole("dialog")).toHaveAttribute("data-discussion-layout", "qna");
expect(screen.getByLabelText("discussion shell")).toBeInTheDocument();
expect(screen.getByLabelText("discussion body")).toHaveTextContent("Where is the AI agent in this thesis?");
expect(screen.getByLabelText("discussion support")).toHaveTextContent("Evidence");
expect(screen.getByLabelText("discussion actions")).toContainElement(
screen.getByRole("button", { name: /return to thesis/i }),
);
});
it("renders context-only discussion with a compact support column", () => {
render(<DiscussionPanel branchId="hosted-automation" onClose={onClose} />);
expect(screen.getByRole("dialog")).toHaveAttribute("data-discussion-layout", "context");
expect(screen.getByLabelText("discussion body")).toHaveTextContent(/Hosted triggers/i);
expect(screen.getByLabelText("discussion support")).toHaveTextContent(/Workflow Automation Platforms/i);
expect(screen.queryByLabelText("defense question")).not.toBeInTheDocument();
});
it("shows mcp-agent-scale links to Anthropic and Cloudflare", () => { it("shows mcp-agent-scale links to Anthropic and Cloudflare", () => {
render(<DiscussionPanel branchId="mcp-agent-scale" onClose={onClose} />); render(<DiscussionPanel branchId="mcp-agent-scale" onClose={onClose} />);
const anthropic = screen.getByText("Anthropic MCP"); const anthropic = screen.getByText("Anthropic MCP");
@@ -59,55 +59,72 @@ export const DiscussionPanel = ({ branchId, onClose }: DiscussionPanelProps) =>
aria-label={branch.title} aria-label={branch.title}
onKeyDown={trapKeyboardWithinDialog} onKeyDown={trapKeyboardWithinDialog}
> >
<header> <div className="discussion-panel__shell" aria-label="discussion shell">
<h2>{branch.title}</h2> <header className="discussion-panel__header">
<span className="discussion-panel__badge">{branch.claimClass}</span> <div>
</header> <span className="discussion-panel__badge">{branch.claimClass}</span>
<section className="discussion-panel__provenance" aria-label="answer provenance"> <h2>{branch.title}</h2>
<span>Evidence</span> </div>
<p>{branch.evidencePointer}</p> <p>{branch.summary}</p>
</section> </header>
<p className="discussion-panel__summary">{branch.summary}</p>
{hasQuestion && ( <main className="discussion-panel__body" aria-label="discussion body">
<section className="discussion-panel__qna" aria-label="defense question"> {hasQuestion ? (
<p className="discussion-panel__question">{branch.question}</p> <section className="discussion-panel__qna" aria-label="defense question">
{branch.shortAnswer && ( <p className="discussion-panel__question">{branch.question}</p>
<article className="discussion-panel__answer-card" aria-label="short defense answer"> {branch.shortAnswer && (
<span>Short answer</span> <article className="discussion-panel__answer-card" aria-label="short defense answer">
<p>{branch.shortAnswer}</p> <span>Short answer</span>
</article> <p>{branch.shortAnswer}</p>
)} </article>
{branch.expandedAnswer && ( )}
<article className="discussion-panel__answer-card discussion-panel__answer-card--expanded" aria-label="answer expansion"> {branch.expandedAnswer && (
<span>Expanded answer</span> <article className="discussion-panel__answer-card discussion-panel__answer-card--expanded" aria-label="answer expansion">
<p>{branch.expandedAnswer}</p> <span>Expanded answer</span>
</article> <p>{branch.expandedAnswer}</p>
</article>
)}
</section>
) : (
<section className="discussion-panel__context" aria-label="discussion context">
<p>{branch.summary}</p>
{branch.detail && (
<div className="discussion-panel__detail" aria-label="additional context">
<p>
{branch.detail.links?.map((link, index) => (
<span key={link.href}>
{index > 0 && " · "}
<a href={link.href} target="_blank" rel="noopener noreferrer">{link.label}</a>
</span>
))}
{branch.detail.links && branch.detail.links.length > 0 ? " — " : ""}
{branch.detail.text}
</p>
</div>
)}
</section>
)} )}
</main>
<aside className="discussion-panel__aside" aria-label="discussion support">
<section className="discussion-panel__provenance" aria-label="answer provenance">
<span>Evidence</span>
<p>{branch.evidencePointer}</p>
</section>
{branch.speakerHint && ( {branch.speakerHint && (
<aside className="discussion-panel__presenter-note" aria-label="presenter note"> <aside className="discussion-panel__presenter-note" aria-label="presenter note">
<span>Presenter note</span> <span>Presenter note</span>
<p>{branch.speakerHint}</p> <p>{branch.speakerHint}</p>
</aside> </aside>
)} )}
</section> </aside>
)}
{branch.detail && ( <footer className="discussion-panel__actions" aria-label="discussion actions">
<section className="discussion-panel__detail" aria-label="additional context"> <button ref={returnButtonRef} type="button" onClick={onClose} className="discussion-panel__return">
<p> Return to {parentScene?.title ?? "scene"}
{branch.detail.links?.map((link, index) => ( </button>
<span key={link.href}> </footer>
{index > 0 && " · "} </div>
<a href={link.href} target="_blank" rel="noopener noreferrer">{link.label}</a>
</span>
))}
{branch.detail.links && branch.detail.links.length > 0 ? " — " : ""}
{branch.detail.text}
</p>
</section>
)}
<button ref={returnButtonRef} type="button" onClick={onClose} className="discussion-panel__return">
Return to {parentScene?.title ?? "scene"}
</button>
</div> </div>
); );
}; };