feat(ui,b09): 공용 표 줄 꼴 곁값 · 일위대가표 제목 굵게 · 하위 「ㄴ」(155-2)

- 공용 표 rowClass(줄) 곁값 — 본문 줄 class · 안 주면 글 그대로 · is-title 굵게
- 공용 표 칸 앞 빈칸 · 겹 빈칸 접지 않음(white-space-collapse: preserve-spaces)
- B09 일위대가표 = 머리 · 합계 굵게 · 품셈 인용 · 줄 = 들여쓰기 「ㄴ」(2단)
- 시험 test_ui_data_table 줄 꼴 · 새 test_155_b09_tabs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015KYZ2HWWdUwZWYnn3tQz7X
This commit is contained in:
2026-10-09 19:10:17 +09:00
co-authored by Claude Opus 5.5
parent f52331265d
commit 4ebee1197a
8 changed files with 108 additions and 4 deletions
@@ -90,6 +90,12 @@ console.log(
}),
escaped: m.renderDataTableHtml({ columns: [{ key: "x", label: "<b>" }], rows: [{ x: "a&<" }] }),
noTotal: m.renderDataTableHtml({ columns: [tc], rows: [] }).includes("<tfoot>"),
rowClass: m.renderDataTableHtml({
columns: [{ key: "x", label: "X" }],
rows: [{ x: "제 1 호표" }, { x: " ㄴ 석공" }],
total: { x: 3 },
rowClass: (i) => (i === 0 ? "is-title" : null),
}),
scale: m.SCALE,
widths: m.renderDataTableHtml({
columns: [
+64
View File
@@ -0,0 +1,64 @@
"""155 B09 탭 화면 꼴 — 일위대가표 줄 꼴(155-2).
`B09_Estimation_UI_Tabs.ts` + `B09_Estimation_UI_Tabs_Labor.ts` 를 Node(타입 지움)로 돌려
견본 블록 하나(머리 · 품셈 인용 · 줄 둘 · 합계)의 표 줄 글 · 줄 꼴을 판정.
"""
import json
import re
import subprocess
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
IMPORTS = re.compile(r"^import[^;]*;\n", re.MULTILINE)
BLOCK = {
"id": "DG1",
"no": 1,
"name": "찰쌓기",
"spec": "",
"unit": "m2",
"kind": "품셈",
"cite": "건설표준품셈 7-1-2",
"lines": [
{"kind": "labor", "key": "L00005", "name": "석공", "qty": 0.072, "unit": "인",
"price": 258935, "amount": 18643.3, "ref_no": "노무3",
"prices": {"labor": 258935, "material": None, "expense": None},
"amounts": {"labor": 18643.3, "material": None, "expense": None}},
{"kind": "sangeun", "key": "SG1", "name": "뒷채움", "qty": 0.5, "unit": "m3",
"price": 8328, "amount": 4164, "ref_no": "산근 1호표",
"prices": {"labor": 6878, "material": 538, "expense": 912},
"amounts": {"labor": 3439, "material": 269, "expense": 456}},
],
"sum": 22807, "labor": 22082, "material": 269, "expense": 456,
} # fmt: skip
def _node(js_body):
src = "".join(
IMPORTS.sub("", (ROOT / "B09_Estimation" / f"B09_Estimation_{n}.ts").read_text("utf-8"))
for n in ("UI_Tabs", "UI_Tabs_Labor")
)
result = subprocess.run(
["node", "--experimental-strip-types", "--no-warnings",
"--input-type=module-typescript", "-e", src + js_body],
capture_output=True, text=True, encoding="utf-8", timeout=60,
) # fmt: skip
assert result.returncode == 0, result.stderr
return json.loads(result.stdout)
def test_155_2_일위대가표_제목_굵게_하위_ㄴ():
out = _node(
f"\nconst t = buildDaegaBlocks({{ sheets: {{ daega_blocks: [{json.dumps(BLOCK)}] }} }});"
"\nconsole.log(JSON.stringify({ names: t.rows.map((r) => r.name),"
" cls: t.rows.map((_, i) => t.rowClass(i) ?? null) }));"
)
assert out["names"] == [
"제 1 호표 찰쌓기",
" ㄴ 건설표준품셈 7-1-2",
" ㄴ 석공",
" ㄴ 뒷채움",
" 합 계",
]
assert out["cls"] == ["is-title", None, None, None, "is-title"]
+10
View File
@@ -112,3 +112,13 @@ def test_155_1_글자_14px_열_폭_같은_비율(r):
assert 'class="is-frozen is-edge" style="left:112px"' in w # 둘째 고정 열 left 도 같이
assert "font-size: var(--text-body-sm);" in r["css"]
assert "--dt-row-h: 28px;" in r["css"]
def test_155_2_줄_꼴_곁값(r):
# 155-2 — rowClass 가 낸 class 를 본문 줄에만 · 빈 값이면 안 붙임 · 앞 빈칸을 접지 않는 서식
rc = r["rowClass"]
assert '<tr class="is-title"><td>제 1 호표</td></tr>' in rc
assert '<tr class="is-alt"><td> ㄴ 석공</td></tr>' in rc
assert rc.count("is-title") == 1 # 합계 줄엔 안 붙음
assert "white-space-collapse: preserve-spaces;" in r["css"]
assert "tr.is-title td" in r["css"]