From 065ca8b983888ee8c10c3a1ecd88cf666a75276e Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 26 Oct 2026 21:51:18 +0900 Subject: [PATCH] =?UTF-8?q?refactor(D01=C2=B7D02):=20=EB=82=A0=20=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=20=C2=B7=20=EC=95=88=EB=82=B4=20=EA=B8=80=EC=9D=84=20?= =?UTF-8?q?=EB=B6=80=ED=92=88=EC=9C=BC=EB=A1=9C=20=E2=80=94=20=EC=B8=A1?= =?UTF-8?q?=EC=A0=90=20=ED=91=9C=EA=B8=B0=20=C2=B7=20=EC=97=B0=EB=8F=84=20?= =?UTF-8?q?=EC=A7=80=EC=A0=90=20=EC=B9=B8=20=C2=B7=20=EC=95=88=EB=82=B4=20?= =?UTF-8?q?=EC=A4=84=20(PLAN=20553-4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- D01_PlanRegister/D01_PlanRegister_UI_Page.ts | 4 +-- D02_PlanBase/D02_PlanBase_UI_Page.ts | 28 +++++++++------- D02_PlanBase/D02_PlanBase_UI_Style.css | 12 ------- resources/tester/test_553_4_de_parts.py | 34 ++++++++++++++++++++ 4 files changed, 52 insertions(+), 26 deletions(-) create mode 100644 resources/tester/test_553_4_de_parts.py diff --git a/D01_PlanRegister/D01_PlanRegister_UI_Page.ts b/D01_PlanRegister/D01_PlanRegister_UI_Page.ts index 49c819408..aad82e483 100644 --- a/D01_PlanRegister/D01_PlanRegister_UI_Page.ts +++ b/D01_PlanRegister/D01_PlanRegister_UI_Page.ts @@ -17,6 +17,7 @@ import { } from "@ui/ui_template_elements"; import { createGeneralLayout } from "@ui/ui_template_general_layout"; import { createHelpButton } from "@ui/ui_template_help"; +import { createText } from "@ui/ui_template_text"; import { t } from "@ui/ui_template_locale"; import { stationLabel } from "../common_util/common_util_station"; import { createGroupProject } from "../A00_Common/group_workflow"; @@ -70,8 +71,7 @@ export function renderD01PlanRegister(root: HTMLElement): void { const endM = createInputField({ label: t("DE_Reg_EndM"), type: "number", min: 0 }); // 누가거리 칸 옆 즉시 측점 표기(저장값 · 서버 검사는 m 그대로) for (const field of [startM, endM]) { - const shown = document.createElement("span"); - shown.style.cssText = "font-size:12px;color:var(--color-text-muted)"; + const shown = createText("", "muted"); shown.dataset.station = ""; field.root.insertBefore(shown, field.root.lastElementChild); field.input.addEventListener("input", () => { diff --git a/D02_PlanBase/D02_PlanBase_UI_Page.ts b/D02_PlanBase/D02_PlanBase_UI_Page.ts index 779805682..d4e9c89fb 100644 --- a/D02_PlanBase/D02_PlanBase_UI_Page.ts +++ b/D02_PlanBase/D02_PlanBase_UI_Page.ts @@ -11,12 +11,14 @@ import "./D02_PlanBase_UI_Style.css"; import { API_BASE_URL, CURRENT_PROJECT_ID_KEY, ROUTES } from "@config/config_frontend"; import { createButton, + createInputField, el, hideLoadingOverlay, showLoadingOverlay, showToast, } from "@ui/ui_template_elements"; import { createPanelCard } from "@ui/ui_template_collapsible"; +import { createText } from "@ui/ui_template_text"; import { editMap, type MapPin } from "@ui/ui_template_kakao_map"; import { t } from "@ui/ui_template_locale"; import { mountGroupStep } from "../A00_Common/group_workflow"; @@ -66,9 +68,11 @@ export async function renderD02PlanBase(root: HTMLElement): Promise { const projectId = localStorage.getItem(CURRENT_PROJECT_ID_KEY) ?? ""; const mapHost = el("div", { className: "d02-map" }); const table = el("div", { className: "d02-points" }); - const info = el("p", { className: "d02-info" }); + const info = createText("", "muted"); + info.classList.add("d02-info"); const card = createPanelCard(t("D02_Points_Title"), false); - const pointsHint = el("p", { className: "d02-info", text: t("D02_Points_Hint") }); + const pointsHint = createText(t("D02_Points_Hint"), "muted"); + pointsHint.classList.add("d02-info"); card.append(pointsHint, table, info); const save = createButton({ label: t("D02_Save"), @@ -139,12 +143,11 @@ export async function renderD02PlanBase(root: HTMLElement): Promise { function drawTable(): void { const rows = points.map((p, i) => { - const lon = el("input", { className: "d02-points__input" }) as HTMLInputElement; - const lat = el("input", { className: "d02-points__input" }) as HTMLInputElement; - lon.type = lat.type = "number"; + const lon = createInputField({ type: "number", placeholder: t("D02_Lon"), size: 24 }).input; + const lat = createInputField({ type: "number", placeholder: t("D02_Lat"), size: 24 }).input; + lon.classList.add("d02-points__input"); + lat.classList.add("d02-points__input"); lon.step = lat.step = "any"; - lon.placeholder = t("D02_Lon"); - lat.placeholder = t("D02_Lat"); lon.value = p ? String(p.lon) : ""; lat.value = p ? String(p.lat) : ""; lon.disabled = lat.disabled = !editable; @@ -154,8 +157,11 @@ export async function renderD02PlanBase(root: HTMLElement): Promise { const typed = (): void => { const at = { lon: Number.parseFloat(lon.value), lat: Number.parseFloat(lat.value) }; const ok = at.lon >= 120 && at.lon <= 135 && at.lat >= 30 && at.lat <= 45; - lon.classList.toggle("is-invalid", !ok); - lat.classList.toggle("is-invalid", !ok); + for (const box of [lon, lat]) { + box.classList.toggle("is-error", !ok); + if (ok) box.removeAttribute("aria-invalid"); + else box.setAttribute("aria-invalid", "true"); + } if (ok) place(i, at); }; lon.addEventListener("change", typed); @@ -254,8 +260,6 @@ export async function renderD02PlanBase(root: HTMLElement): Promise { active = firstEmpty(); paint(); } catch (error) { - table.replaceChildren( - el("p", { className: "d02-info", text: error instanceof Error ? error.message : "" }), - ); + table.replaceChildren(createText(error instanceof Error ? error.message : "", "muted")); } } diff --git a/D02_PlanBase/D02_PlanBase_UI_Style.css b/D02_PlanBase/D02_PlanBase_UI_Style.css index ff48d662e..39cf82ad8 100644 --- a/D02_PlanBase/D02_PlanBase_UI_Style.css +++ b/D02_PlanBase/D02_PlanBase_UI_Style.css @@ -35,17 +35,5 @@ } .d02-points__input { - width: 100%; min-width: 0; - font-size: var(--text-caption); -} - -.d02-points__input.is-invalid { - outline: 1px solid var(--color-danger, #d92d20); -} - -.d02-info { - margin: 0; - color: var(--color-text-secondary); - font-size: var(--text-caption); } diff --git a/resources/tester/test_553_4_de_parts.py b/resources/tester/test_553_4_de_parts.py new file mode 100644 index 000000000..e37297b87 --- /dev/null +++ b/resources/tester/test_553_4_de_parts.py @@ -0,0 +1,34 @@ +"""553-4 예정노선(D01~D04) · 안정성평가(E01~E07) 부품 옮김 지킴 시험 — 서버 · 화면 없이 소스만 본다. +옮긴 페이지는 날 `` · `` · 날 색 · 안 쓰는 변종 CSS 가 다시 생기면 안 됨.""" + +import re +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] + + +def _src(path: str) -> str: + return (ROOT / path).read_text(encoding="utf-8") + + +def _no_hex(path: str) -> None: + css = re.sub(r"/\*.*?\*/", "", _src(path), flags=re.S) + assert not re.search(r"#[0-9a-fA-F]{3,8}\b", css), f"{path}: 날 색" + + +def test_d01_station_label_is_text_part(): + ts = _src("D01_PlanRegister/D01_PlanRegister_UI_Page.ts") + assert "style.cssText" not in ts + assert 'createText("", "muted")' in ts + assert 'dataset.station = ""' in ts # 시험 · 촬영이 잡음 + + +def test_d02_parts(): + ts = _src("D02_PlanBase/D02_PlanBase_UI_Page.ts") + css = _src("D02_PlanBase/D02_PlanBase_UI_Style.css") + assert 'el("input"' not in ts + assert 'createInputField({ type: "number"' in ts + assert 'classList.add("d02-info")' in ts # 촬영 장면이 `p.d02-info` 로 잡음 + for dead in (".d02-info", "is-invalid", "font-size: var(--text-caption);\n}\n\n.d02-info"): + assert dead not in css + assert "data-axis" in ts or "dataset.axis" in ts