From 21f8425f1efa4911634e3b68d5f371742af1303e Mon Sep 17 00:00:00 2001 From: DongHyeonka Date: Fri, 21 Aug 2026 15:54:28 +0900 Subject: [PATCH] fix: keep line breaks in the fields that are not Markdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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
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. --- .../shared/public-render/inline-renderer.tsx | 9 ++++++ .../public-render/public-record-renderer.tsx | 29 ++++++++++--------- tests/component/inline-line-breaks.test.tsx | 21 +++++++++++++- 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/src/features/tech-log/presentation/shared/public-render/inline-renderer.tsx b/src/features/tech-log/presentation/shared/public-render/inline-renderer.tsx index d6e8fd1..5216346 100644 --- a/src/features/tech-log/presentation/shared/public-render/inline-renderer.tsx +++ b/src/features/tech-log/presentation/shared/public-render/inline-renderer.tsx @@ -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": diff --git a/src/features/tech-log/presentation/shared/public-render/public-record-renderer.tsx b/src/features/tech-log/presentation/shared/public-render/public-record-renderer.tsx index 8d5d3e6..ab68645 100644 --- a/src/features/tech-log/presentation/shared/public-render/public-record-renderer.tsx +++ b/src/features/tech-log/presentation/shared/public-render/public-record-renderer.tsx @@ -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}

{model.title}

-

{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> diff --git a/tests/component/inline-line-breaks.test.tsx b/tests/component/inline-line-breaks.test.tsx index c7e2797..7dc635a 100644 --- a/tests/component/inline-line-breaks.test.tsx +++ b/tests/component/inline-line-breaks.test.tsx @@ -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); + }); +});