- ui_template_button.ts · .css 새로 — createButton 을 옮김(ui_template_elements 가 다시 내보냄 · 부르는 길 그대로) · createIconButton 새로(고스트 · 선 · 채움 · aria-pressed · 글자 기호 · title + aria-label) - 크기 = data-size 18 · 20 · 22 · 24 · 28 · 32 · 36 · 42 · 단마다 높이 · 글자 · 여백 · 아이콘 · 둥글기 값(--ui-size-*) · 입력 · 고르기 · 분할 · 탭이 같은 값을 읽음 - 단추 높이 = 글 한 줄 + 여백 + 테두리(높이를 박지 않음) — 크기를 안 준 옛 단추 · 페이지 여백 덮어쓰기 그대로 · 옛 size "sm" = 18 단(같은 꼴) · 글 링크 꼴 더함 - 상태 = 마우스 · 눌림을 data-state 로도 받음(부품 모음이 굳혀 보임) · 마우스 색 = --color-primary-hover · --color-accent-soft(옛 보라 별칭 대신) · 눌림 = 한 칸 내려앉음 - 아이콘 = Tabler 32 더함(지도 · 길 · 자 · 합 · 크레인 · 핀 · 나침반 · 설정 · 닫기 · 메뉴 · 되돌리기 …) + PageKit 여섯을 옮김 · .ui-icon 규칙 전역(ui_template_icons.css) - 부품 모음 = 단추 · 아이콘 단추 · 아이콘 견본 · 부품마다 여덟 단 한 줄 - 시험 test_552_2_button 새로(단 값 · 높이 식 · 옛 이름 · 상태 · 날 색 없음 · 아이콘) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
103 lines
4.9 KiB
Python
103 lines
4.9 KiB
Python
"""552-2 부품 「단추」 · 「아이콘 단추」 · 「아이콘」 · 크기 여덟 단 지킴 시험 — 화면 확인은 ORCA.
|
|
|
|
· 크기 = data-size 여덟 단(18 ~ 42) · 단마다 높이 · 글자 · 여백 · 아이콘 · 둥글기 · 옛 sm = 18
|
|
· 단추 높이 = 글 한 줄 + 여백 + 테두리 = 단(높이를 박지 않음 — 페이지 여백 덮어쓰기 그대로)
|
|
· 상태 = :hover · :active 를 data-state 로도 받음 · 아이콘 단추 = title + aria-label
|
|
· 색 = 공통 값만(날 색 · --b01-* · --draw-* 없음) · 옛 클래스 이름 그대로 · 부르는 길(ui_template_elements) 그대로
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
UI = ROOT / "ui_template"
|
|
SIZES = [18, 20, 22, 24, 28, 32, 36, 42]
|
|
|
|
|
|
def _text(path: Path) -> str:
|
|
return path.read_text(encoding="utf-8")
|
|
|
|
|
|
def _rungs(css: str) -> dict[int, dict[str, str]]:
|
|
out = {}
|
|
for size, body in re.findall(r':where\(\[data-size="(\d+)"\]\) \{([^}]*)\}', css):
|
|
out[int(size)] = dict(re.findall(r"--ui-size-([\w-]+):\s*([^;]+);", body))
|
|
return out
|
|
|
|
|
|
def test_여덟_단():
|
|
src = _text(UI / "ui_template_button.ts")
|
|
assert "UI_SIZES = [18, 20, 22, 24, 28, 32, 36, 42] as const" in src
|
|
rungs = _rungs(_text(UI / "ui_template_button.css"))
|
|
assert sorted(rungs) == SIZES
|
|
for size, v in rungs.items():
|
|
assert v["h"] == f"{size}px"
|
|
assert set(v) == {"h", "font", "px", "icon", "radius", "gap"}
|
|
# 글자 짝 — 18 ~ 24 = 12 · 28 이상 = 14
|
|
assert v["font"] == ("var(--text-caption)" if size <= 24 else "var(--text-body-sm)")
|
|
# 아이콘 크기 — TS 짝과 같음
|
|
px = dict(re.findall(r"(\d+): (\d+)", re.search(r"ICON_PX[^{]*\{([^}]*)\}", src).group(1)))
|
|
assert {int(k): f"{v}px" for k, v in px.items()} == {s: rungs[s]["icon"] for s in SIZES}
|
|
|
|
|
|
def test_단추_높이_식():
|
|
css = _text(UI / "ui_template_button.css")
|
|
rule = re.search(r"\.ui-btn\[data-size\] \{([^}]*)\}", css).group(1)
|
|
assert "calc((var(--ui-size-h) - 2px - var(--ui-size-font)) / 2) var(--ui-size-px)" in rule
|
|
assert "height" not in rule # 높이를 박지 않음
|
|
# 아이콘은 글 높이 안 — 단 높이가 아이콘 때문에 늘지 않음
|
|
assert "margin-block: calc((var(--ui-size-font) - var(--ui-size-icon)) / 2)" in css
|
|
# 32 단 = 옛 기본(여백 8 · 글 14) · 18 단 = 옛 sm(여백 2 · 글 12)
|
|
rungs = _rungs(css)
|
|
assert (32 - 2 - 14) / 2 == 8 and rungs[32]["px"] == "16px"
|
|
assert (18 - 2 - 12) / 2 == 2 and rungs[18]["px"] == "8px" and rungs[18]["radius"] == "4px"
|
|
|
|
|
|
def test_옛_이름_부르는_길():
|
|
src = _text(UI / "ui_template_button.ts")
|
|
assert 'opts.size === "sm" ? 18' in src
|
|
assert 'opts.size === "sm" ? " ui-btn--sm" : ""' in src # 옛 클래스 그대로
|
|
for cls in ("ui-btn ui-btn--", "ui-btn__icon", "ui-btn__label", "ui-icon-btn ui-icon-btn--"):
|
|
assert cls in src, cls
|
|
elements = _text(UI / "ui_template_elements.ts")
|
|
assert 'export * from "./ui_template_button";' in elements
|
|
assert "export function createButton" not in elements # 한 벌
|
|
|
|
|
|
def test_상태():
|
|
styles = _text(UI / "ui_template_elements_styles.ts")
|
|
for variant in ("filled", "ghost", "pill", "danger", "danger-ghost", "glass"):
|
|
assert f'.ui-btn--{variant}:is(:hover, [data-state="hover"]):not(:disabled)' in styles
|
|
assert '.ui-btn:is(:active, [data-state="active"]):not(:disabled)' in styles
|
|
assert "--color-primary-hover" in styles
|
|
css = _text(UI / "ui_template_button.css")
|
|
assert '.ui-icon-btn:is(:hover, [data-state="hover"]):not(:disabled)' in css
|
|
assert '.ui-icon-btn:is(:active, [data-state="active"]):not(:disabled)' in css
|
|
assert '.ui-icon-btn[aria-pressed="true"]' in css and ".ui-icon-btn:disabled" in css
|
|
src = _text(UI / "ui_template_button.ts")
|
|
assert 'title: opts.title, "aria-label": opts.title' in src
|
|
|
|
|
|
def test_날_색_없음():
|
|
for name in ("ui_template_button.css", "ui_template_icons.css"):
|
|
css = _text(UI / name)
|
|
assert not re.search(r"#[0-9a-fA-F]{3,8}\b|rgba?\(", css), name
|
|
assert "--b01-" not in css and "--draw-" not in css and "font-family" not in css
|
|
for name in ("ui_template_button.ts", "ui_template_button.css", "ui_template_icons.ts"):
|
|
assert len(_text(UI / name).splitlines()) <= 700
|
|
|
|
|
|
def test_아이콘():
|
|
src = _text(UI / "ui_template_icons.ts")
|
|
names = re.findall(r"^ (\w+):", src[src.index("const PATHS") : src.index("} as const")], re.M)
|
|
assert len(names) == len(set(names)) >= 60
|
|
for n in ("settings", "x", "menu", "undo", "redo", "mapPin", "dots", "plus", "inbox"):
|
|
assert n in names, n
|
|
assert not re.search(
|
|
r"[\U0001F300-\U0001FAFF☀-➿]", src[src.index("const PATHS") :]
|
|
) # 그림 이모지 없음
|
|
kit = _text(ROOT / "B01_Dashboard" / "B01_Dashboard_UI_PageKit.ts")
|
|
assert "KIT_PATHS" not in kit and "return icon(name, size);" in kit # 한 벌
|