feat(device): 로그인 뒤 ☰ 상태 위 줄 걷기 · ☰ 는 위 오른쪽 모서리에 떠 있음 · 진행 단계를 ☰ 서랍 묶음으로(같은 요소를 빌려 옴 · 한 벌) · 서랍 차례 로고 ─ 진행 단계 ─ 메뉴 ─ 설정 · 위 줄 높이 = --app-header-h 하나 · data-burger (기기대응 6)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ji4nuyLVHcxrXWHTdb5nRD
This commit is contained in:
2026-10-13 14:44:21 +09:00
co-authored by Claude Opus 5.5
parent 68e82f9de9
commit 3fc50044e1
19 changed files with 227 additions and 63 deletions
+1
View File
@@ -117,6 +117,7 @@ AUDIT_JS = r"""
}
out.pointer = matchMedia('(pointer: coarse)').matches ? 'coarse' : 'fine';
out.rendered = !!(outlet && outlet.childElementCount);
out.outlet_top = outlet ? Math.round(outlet.getBoundingClientRect().top) : null; // 본문 위 끝(위 줄 걷힘 = 0 · 6-3)
return out;
}
"""
+99
View File
@@ -0,0 +1,99 @@
"""☰ 서랍 · 위 줄 걷기(PLAN_기기대응 6 · `app_shell.ts` · `ui_template_workflow_layout.ts`) — 실제 브라우저.
① 휴대폰 · 로그인 뒤 B 쪽 = 위 줄 걷힘(본문 위 끝 0) · ☰ 는 위 오른쪽 모서리 · 떠 있는 진행 판 없음
② ☰ 서랍 = 로고 · 홈 ─ 진행 단계 ─ 메뉴 ─ 언어 · 테마 · 로그인 · 터치 모드 차례 · 진행 단계는 진행 판의 **그 요소**(한 벌)
지금 단계 강조 그대로 · 닫으면 제자리(진행 판)로 돌아감 · 단계 누름 = 이동 + 서랍 닫힘
③ 휴대폰 · 공개 쪽(A01) = 위 줄 그대로(본문 위 끝 64)
④ 데스크탑(1440 · 마우스) = 위 줄 · 진행 판 그대로 · ☰ 없음
"""
import json
import sys
from pathlib import Path
import pytest
sys.path.insert(0, str(Path(__file__).resolve().parent))
import test_device_audit as audit # noqa: E402
OUTLET_TOP = "Math.round(document.getElementById('app-outlet').getBoundingClientRect().top)"
GROUPS = "[...document.querySelectorAll('.app-drawer > *')].map(e => e.className)"
def _ctx(browser, cred, size):
spec = audit.SIZES[size]
ctx = browser.new_context(
viewport=spec["viewport"], has_touch=spec["touch"], is_mobile=spec["touch"]
)
audit._login(ctx, cred)
pid = audit._project_id(ctx)
ctx.add_init_script(f"localStorage.setItem('frd_current_project_id', {json.dumps(pid)})")
return ctx
@pytest.mark.skipif(not audit._server_up(), reason="sub1 화면 서버(5174) 꺼짐")
def test_drawer_holds_steps_and_header_folds():
from playwright.sync_api import sync_playwright
cred = json.loads((audit.ROOT / "tmp" / "aislo_cred.json").read_text(encoding="utf-8"))
with sync_playwright() as p:
browser = p.chromium.launch(channel="msedge", headless=True)
page = _ctx(browser, cred, "phone_390x844_touch").new_page()
audit._open(page, "b07-design-detail")
# ①
assert page.evaluate(OUTLET_TOP) == 0
burger = page.evaluate(
"(() => { const r = document.querySelector('.app-burger').getBoundingClientRect();"
" return [Math.round(r.right), Math.round(r.top), Math.round(r.width)]; })()"
)
assert burger[0] >= 390 - 12 and burger[1] <= 12 and burger[2] >= 44, burger
assert page.evaluate(
"!document.querySelector('.ui-workflow-overlay__panel--progress').getClientRects().length"
)
# ②
page.evaluate(
"window.__steps = document.querySelector('.ui-workflow-overlay__panel--progress .ui-workflow-layout__steps')"
)
page.evaluate("document.querySelector('.app-burger').click()")
groups = page.evaluate(GROUPS)
order = [
next((k for k in ("brand", "steps", "nav", "touch") if k in c), "settings")
for c in groups
]
assert order == ["brand", "steps", "nav", "settings"], groups
assert page.evaluate(
"document.querySelector('.app-drawer__steps').firstElementChild === window.__steps"
)
active = page.evaluate(
"document.querySelector('.app-drawer__steps .ui-workflow-layout__step.is-active')?.textContent"
)
assert active and "상세" in active, active
page.evaluate("document.querySelector('.app-drawer__scrim').click()") # 닫기 = 제자리
assert page.evaluate(
"window.__steps.closest('.ui-workflow-overlay__panel--progress') !== null"
)
page.evaluate("document.querySelector('.app-burger').click()")
page.evaluate(
"[...document.querySelectorAll('.app-drawer__steps .ui-workflow-layout__step.is-enabled')]"
".find(b => b.textContent.includes('수량')).click()"
)
page.wait_for_function("location.hash.includes('b08-quantity')", timeout=10000)
assert not page.evaluate(
"document.querySelector('.app-header')?.classList.contains('is-drawer-open')"
)
# ③
audit._open(page, "a01-home")
assert page.evaluate(OUTLET_TOP) == 64
# ④
desk = _ctx(browser, cred, audit.DESKTOP).new_page()
audit._open(desk, "b07-design-detail")
assert desk.evaluate(OUTLET_TOP) == 64
assert not desk.evaluate("document.querySelector('.app-burger').getClientRects().length")
assert desk.evaluate(
"document.querySelector('.ui-workflow-overlay__panel--progress').getClientRects().length > 0"
)
browser.close()
+1 -1
View File
@@ -155,6 +155,6 @@ def test_항목_추가_창_카드_격자_표_접기():
assert "grid-auto-rows: var(--m02-struct-card-h);" in css
assert "--m02-struct-card-h: min(640px, 70vh);" in css # 31-6 화면 높이에 맞춤
assert "flex: 1 0 var(--m02-struct-grid-min);" in css # 엑셀 격자가 카드 높이를 채움
assert "max-height: calc(100dvh - var(--spacing-64));\n overflow-y: auto;" in css
assert "max-height: calc(100dvh - var(--app-header-h));\n overflow-y: auto;" in css
main = (m02 / "M02_MasterTemplete_UI_Main.ts").read_text("utf-8")
assert "host.hidden = !host.hidden;" in main and 'title.classList.toggle("is-folded"' in main