fix: keep line breaks in the fields that are not Markdown

The earlier fix covered the Markdown body and stopped there, so the preview
still ran lines together — which is exactly what it looked like from the
outside: nothing had changed.

Summary, problem, conclusion, environment and the rest are plain text. They
never pass through the Markdown parser, so their newlines sit in a text node
and HTML collapses them, and the renderer that now emits <br> for the body was
never asked about them.

They render through the same rule now. One helper, one behaviour: a line the
author broke stays broken, wherever they typed it.
This commit is contained in:
DongHyeonka
2026-08-21 15:54:28 +09:00
parent 7093d84ab5
commit 21f8425f1e
3 changed files with 44 additions and 15 deletions
@@ -33,6 +33,15 @@ function renderText(text: string, key: number) {
);
}
/**
* 작성자가 직접 쓴 평문. 요약·문제·결론·환경처럼 Markdown 을 거치지 않고 그대로 그려지는 칸들이
* 여기 온다 — 그 칸들은 텍스트 노드에 줄바꿈이 그대로 들어가고, HTML 이 그것을 공백으로 접어
* 작성자가 나눠 쓴 줄이 이어져 보였다. 본문(Markdown)만 고쳤을 때 이 칸들이 남아 있던 이유다.
*/
export function PlainText({ text }: { text: string }) {
return renderText(text, 0);
}
function renderInline(inline: Inline, key: number) {
switch (inline.type) {
case "TEXT":
@@ -2,6 +2,7 @@ import { Link } from "react-router-dom";
import type { components } from "../../../contracts/studio/generated.ts";
import { inlinePlainText } from "../../../domain/content-format/inline-plain-text.ts";
import { PlainText } from "./inline-renderer.tsx";
import type {
ResolveEvidenceAsset,
ResolvePublishedLabel,
@@ -92,7 +93,7 @@ function ModelDocumentHeader({
) : null}
</nav>
<h1>{model.title}</h1>
<p>{model.summary}</p>
<p><PlainText text={model.summary} /></p>
<dl>
<div>
<dt></dt>
@@ -149,21 +150,21 @@ function GenericCase({
<section className="document-snapshot" aria-label="문제와 결론">
<div>
<p className="snapshot-label"></p>
<p>{model.problem}</p>
<p><PlainText text={model.problem} /></p>
</div>
<div>
<p className="snapshot-label snapshot-label--answer"></p>
<p>{model.conclusion}</p>
<p><PlainText text={model.conclusion} /></p>
</div>
</section>
<dl className="document-facts">
<div>
<dt> </dt>
<dd>{model.environment}</dd>
<dd><PlainText text={model.environment} /></dd>
</div>
<div>
<dt> </dt>
<dd>{model.reproduction}</dd>
<dd><PlainText text={model.reproduction} /></dd>
</div>
</dl>
<article className="public-document-body">
@@ -213,27 +214,27 @@ function FetchJoinCase({
</nav>
<h1>{model.title}</h1>
<p className="case-summary">{model.summary}</p>
<p className="case-summary"><PlainText text={model.summary} /></p>
<section className="case-snapshot" aria-label="문제와 결론">
<div>
<p className="snapshot-label"></p>
<p>{model.problem}</p>
<p><PlainText text={model.problem} /></p>
</div>
<div>
<p className="snapshot-label snapshot-label--answer"></p>
<p>{model.conclusion}</p>
<p><PlainText text={model.conclusion} /></p>
</div>
</section>
<dl className="case-meta">
<div>
<dt> </dt>
<dd>{model.environment}</dd>
<dd><PlainText text={model.environment} /></dd>
</div>
<div>
<dt></dt>
<dd>{model.reproduction.replace(/^Dataset:\s*/, "")}</dd>
<dd><PlainText text={model.reproduction.replace(/^Dataset:\s*/, "")} /></dd>
</div>
<div>
<dt></dt>
@@ -287,7 +288,7 @@ function ReferenceDocument({
<section className="reference-purpose" aria-labelledby="purpose-title">
<p className="section-kicker">Purpose</p>
<h2 id="purpose-title"> </h2>
<p>{model.purpose}</p>
<p><PlainText text={model.purpose} /></p>
</section>
<article className="public-document-body reference-body">
<section aria-labelledby="rules-title">
@@ -434,7 +435,7 @@ function QuestionDocument({
>
<p className="section-kicker">Next</p>
<h2 id="next-validation-title"> </h2>
<p>{model.nextValidation}</p>
<p><PlainText text={model.nextValidation} /></p>
</section>
</article>
<PublicDocumentRelations relations={publicRelations(model.relations)} />
@@ -476,11 +477,11 @@ function ProjectDecisionDocument({
<time dateTime={model.decidedOn}>{displayDate(model.decidedOn)}</time>
</div>
<h2>{model.title}</h2>
<p>{model.statement}</p>
<p><PlainText text={model.statement} /></p>
</header>
<section>
<h3> </h3>
<p>{model.rationale}</p>
<p><PlainText text={model.rationale} /></p>
</section>
<section>
<h3></h3>
+20 -1
View File
@@ -3,7 +3,10 @@
import { render } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import { InlineRenderer } from "../../src/features/tech-log/presentation/shared/public-render/inline-renderer.tsx";
import {
InlineRenderer,
PlainText,
} from "../../src/features/tech-log/presentation/shared/public-render/inline-renderer.tsx";
/**
* 작성자가 엔터로 나눠 쓴 글이 한 줄로 이어져 보였다. Markdown 이 한 번의 줄바꿈을 문단 내 공백으로
@@ -34,3 +37,19 @@ describe("문단 안의 줄바꿈", () => {
expect(container.querySelectorAll("br")).toHaveLength(2);
});
});
/**
* 요약·문제·결론·환경은 Markdown 을 거치지 않고 그대로 그려진다. 본문만 고쳤을 때 이 칸들이
* 여전히 이어져 보인 이유이고, 작성자가 "아직 안 고쳐졌다" 고 본 것도 이쪽이다.
*/
describe("Markdown 을 거치지 않는 평문 칸", () => {
it("작성자가 나눠 쓴 줄을 지킨다", () => {
const { container } = render(<PlainText text={"문제 첫 줄\n문제 둘째 줄"} />);
expect(container.querySelectorAll("br")).toHaveLength(1);
});
it("한 줄이면 <br> 를 넣지 않는다", () => {
const { container } = render(<PlainText text="한 줄" />);
expect(container.querySelectorAll("br")).toHaveLength(0);
});
});