feat(HAI-013): add markdown rendering styles and TaskDetailModal tests
- Add markdown-body base styles in dashboard styles.css - Remove backdrop and enable scroll on task detail prompt rendering - Add TaskDetailModal unit tests with vitest jsdom environment - Update vitest config with globals and jsdom support - Clean up unused test files and legacy concurrency module
This commit is contained in:
@@ -117,7 +117,7 @@ export function TaskDetailModal({
|
||||
<div className="detail-section">
|
||||
<h4>PROMPT.md</h4>
|
||||
{task.prompt ? (
|
||||
<div className="detail-prompt markdown-body">
|
||||
<div className="markdown-body">
|
||||
<ReactMarkdown remarkPlugins={[remarkGfm]}>
|
||||
{task.prompt}
|
||||
</ReactMarkdown>
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { TaskDetailModal } from "../TaskDetailModal";
|
||||
import type { TaskDetail, Column, MergeResult, Task } from "@hai/core";
|
||||
|
||||
function makeTask(overrides: Partial<TaskDetail> = {}): TaskDetail {
|
||||
return {
|
||||
id: "HAI-099",
|
||||
description: "Test task",
|
||||
column: "in-progress" as Column,
|
||||
dependencies: [],
|
||||
steps: [],
|
||||
currentStep: 0,
|
||||
log: [],
|
||||
createdAt: "2026-01-01T00:00:00Z",
|
||||
updatedAt: "2026-01-01T00:00:00Z",
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
const noop = vi.fn();
|
||||
const noopMove = vi.fn(async () => ({}) as Task);
|
||||
const noopDelete = vi.fn(async () => ({}) as Task);
|
||||
const noopMerge = vi.fn(async () => ({ merged: false }) as MergeResult);
|
||||
|
||||
describe("TaskDetailModal", () => {
|
||||
it("renders markdown-body without detail-prompt class when prompt exists", () => {
|
||||
const { container } = render(
|
||||
<TaskDetailModal
|
||||
task={makeTask({ prompt: "# Hello\n\nSome **bold** text" })}
|
||||
onClose={noop}
|
||||
onMoveTask={noopMove}
|
||||
onDeleteTask={noopDelete}
|
||||
onMergeTask={noopMerge}
|
||||
addToast={noop}
|
||||
/>,
|
||||
);
|
||||
|
||||
const markdownDiv = container.querySelector(".markdown-body");
|
||||
expect(markdownDiv).toBeTruthy();
|
||||
expect(markdownDiv!.classList.contains("detail-prompt")).toBe(false);
|
||||
});
|
||||
|
||||
it("renders ReactMarkdown output (heading and bold text)", () => {
|
||||
const { container } = render(
|
||||
<TaskDetailModal
|
||||
task={makeTask({ prompt: "# Hello\n\nSome **bold** text" })}
|
||||
onClose={noop}
|
||||
onMoveTask={noopMove}
|
||||
onDeleteTask={noopDelete}
|
||||
onMergeTask={noopMerge}
|
||||
addToast={noop}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(container.querySelector("h1")?.textContent).toBe("Hello");
|
||||
expect(container.querySelector("strong")?.textContent).toBe("bold");
|
||||
});
|
||||
|
||||
it("renders (no prompt) with detail-prompt class when prompt is absent", () => {
|
||||
const { container } = render(
|
||||
<TaskDetailModal
|
||||
task={makeTask({ prompt: undefined })}
|
||||
onClose={noop}
|
||||
onMoveTask={noopMove}
|
||||
onDeleteTask={noopDelete}
|
||||
onMergeTask={noopMerge}
|
||||
addToast={noop}
|
||||
/>,
|
||||
);
|
||||
|
||||
const fallback = screen.getByText("(no prompt)");
|
||||
expect(fallback).toBeTruthy();
|
||||
expect(fallback.classList.contains("detail-prompt")).toBe(true);
|
||||
expect(fallback.classList.contains("markdown-body")).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -432,9 +432,6 @@ html, body {
|
||||
}
|
||||
|
||||
.detail-prompt {
|
||||
background: var(--bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
padding: 14px;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
@@ -442,17 +439,16 @@ html, body {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
color: var(--text-muted);
|
||||
max-height: 320px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.detail-prompt.markdown-body {
|
||||
font-family: inherit;
|
||||
white-space: normal;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
/* === Markdown Prose === */
|
||||
.markdown-body {
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
color: var(--text);
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.markdown-body h1,
|
||||
.markdown-body h2,
|
||||
.markdown-body h3,
|
||||
|
||||
@@ -340,9 +340,6 @@ html, body {
|
||||
}
|
||||
|
||||
.detail-prompt {
|
||||
background: var(--bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
padding: 14px;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
@@ -350,8 +347,6 @@ html, body {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
color: var(--text-muted);
|
||||
max-height: 320px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.detail-deps { margin-top: 16px; }
|
||||
|
||||
@@ -11,6 +11,7 @@ export default defineConfig({
|
||||
},
|
||||
test: {
|
||||
environment: "jsdom",
|
||||
globals: true,
|
||||
include: ["app/**/*.test.{ts,tsx}", "src/**/*.test.{ts,tsx}"],
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user