fix(가이드): 좌측 패널 [?] 늦게 붙인 단추 줄에도 자리 — E03 · E04 · E06 · E07(요구 359-2 · 360-2)
- 까닭 = 패널을 세운 뒤 단추 줄을 붙이는 화면은 쪼갬 · [?] [<] [>] 자리를 못 찾음 - 고침 = 공용 좌측 패널이 단추 줄이 붙는 때를 지켜보다 한 번 쪼갬 · 붙으면 지켜보기 끊음 - 시험 = 늦게 붙인 줄 · 먼저 붙인 줄 [?] 한 벌(새) · 280-2 헬퍼 대역에 538-3 고침 모듈 더함(낡음) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -123,6 +123,7 @@ const STUBS = {
|
|||||||
},
|
},
|
||||||
"./ui_template_locale": { t: (key) => key },
|
"./ui_template_locale": { t: (key) => key },
|
||||||
"./ui_template_modal": { openModal: () => undefined },
|
"./ui_template_modal": { openModal: () => undefined },
|
||||||
|
"./ui_template_help_edit": { attachHelpEditing: () => ({ flush: () => undefined }) },
|
||||||
"./ui_template_elements": {
|
"./ui_template_elements": {
|
||||||
showToast: (message) => toasts.push(message),
|
showToast: (message) => toasts.push(message),
|
||||||
el: (tag) => new FakeNode(tag),
|
el: (tag) => new FakeNode(tag),
|
||||||
|
|||||||
@@ -0,0 +1,141 @@
|
|||||||
|
/* 요구 359-2 · 360-2 — 좌측 패널 [?] 자리. `ui_template_overlay.ts` 를 트랜스파일해 가짜 DOM 위에서
|
||||||
|
* 액션 줄을 ① 패널 세우기 전(E05 꼴) ② 세운 뒤(E03 · E04 · E06 · E07 꼴)에 붙여 쪼갬 · [?] 를 JSON 한 벌로 냄.
|
||||||
|
*/
|
||||||
|
const path = require("path");
|
||||||
|
const { makeLoader } = require("./helper_ts_stub_loader.cjs");
|
||||||
|
|
||||||
|
const ROOT = path.join(__dirname, "..", "..");
|
||||||
|
|
||||||
|
class FakeNode {
|
||||||
|
constructor(tag) {
|
||||||
|
this.tagName = tag;
|
||||||
|
this.children = [];
|
||||||
|
this.parent = null;
|
||||||
|
this.style = {};
|
||||||
|
this._classes = new Set();
|
||||||
|
const self = this;
|
||||||
|
this.classList = {
|
||||||
|
add: (...n) => n.forEach((x) => self._classes.add(x)),
|
||||||
|
remove: (...n) => n.forEach((x) => self._classes.delete(x)),
|
||||||
|
toggle: (n, f) =>
|
||||||
|
(f === undefined ? !self._classes.has(n) : !!f)
|
||||||
|
? self._classes.add(n)
|
||||||
|
: self._classes.delete(n),
|
||||||
|
contains: (n) => self._classes.has(n),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
set className(v) {
|
||||||
|
this._classes = new Set(String(v).split(/\s+/).filter(Boolean));
|
||||||
|
}
|
||||||
|
get className() {
|
||||||
|
return [...this._classes].join(" ");
|
||||||
|
}
|
||||||
|
get parentElement() {
|
||||||
|
return this.parent;
|
||||||
|
}
|
||||||
|
get firstChild() {
|
||||||
|
return this.children[0] ?? null;
|
||||||
|
}
|
||||||
|
get childElementCount() {
|
||||||
|
return this.children.length;
|
||||||
|
}
|
||||||
|
_adopt(kid) {
|
||||||
|
if (kid.parent) kid.parent.children = kid.parent.children.filter((c) => c !== kid);
|
||||||
|
kid.parent = this;
|
||||||
|
return kid;
|
||||||
|
}
|
||||||
|
append(...kids) {
|
||||||
|
kids.forEach((k) => this.children.push(this._adopt(k)));
|
||||||
|
mutated();
|
||||||
|
}
|
||||||
|
prepend(...kids) {
|
||||||
|
this.children.unshift(...kids.map((k) => this._adopt(k)));
|
||||||
|
mutated();
|
||||||
|
}
|
||||||
|
setAttribute() {}
|
||||||
|
addEventListener() {}
|
||||||
|
querySelector(selector) {
|
||||||
|
const cls = selector.slice(1);
|
||||||
|
let hit = null;
|
||||||
|
const walk = (n) =>
|
||||||
|
n.children.forEach((k) => {
|
||||||
|
if (!hit && k._classes.has(cls)) hit = k;
|
||||||
|
if (!hit) walk(k);
|
||||||
|
});
|
||||||
|
walk(this);
|
||||||
|
return hit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 진짜 MutationObserver 처럼 바뀐 뒤 한꺼번에 — `flush()` 가 부름(지켜보는 것만 · 끊으면 안 부름)
|
||||||
|
const observers = new Set();
|
||||||
|
let dirty = false;
|
||||||
|
function mutated() {
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
function flush() {
|
||||||
|
while (dirty) {
|
||||||
|
dirty = false;
|
||||||
|
[...observers].forEach((o) => o.cb());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
globalThis.MutationObserver = class {
|
||||||
|
constructor(cb) {
|
||||||
|
this.cb = cb;
|
||||||
|
}
|
||||||
|
observe() {
|
||||||
|
observers.add(this);
|
||||||
|
}
|
||||||
|
disconnect() {
|
||||||
|
observers.delete(this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
globalThis.document = { createElement: (tag) => new FakeNode(tag) };
|
||||||
|
globalThis.window = { innerWidth: 1600 };
|
||||||
|
|
||||||
|
const { loadTs } = makeLoader({
|
||||||
|
root: ROOT,
|
||||||
|
stubs: {
|
||||||
|
"./ui_template_locale": { t: (key) => key },
|
||||||
|
"./ui_template_help": {
|
||||||
|
placeHelpButton: (actions) => {
|
||||||
|
const button = new FakeNode("button");
|
||||||
|
button.className = "ui-help-btn";
|
||||||
|
actions.prepend(button);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"./ui_template_overlay_drag": { makePanelDraggable: () => ({ refresh() {} }) },
|
||||||
|
"/b_workflow_nav": { fetchWorkflowState: async () => ({}) },
|
||||||
|
"/b_page_state": { readByKey: () => null, writeByKey: () => undefined },
|
||||||
|
"/device_context": { SIZE_BREAKS: { expanded: 840 } },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const overlay = loadTs(path.join(ROOT, "ui_template", "ui_template_overlay.ts"));
|
||||||
|
|
||||||
|
function mount(late) {
|
||||||
|
const panel = new FakeNode("div");
|
||||||
|
const actions = new FakeNode("div");
|
||||||
|
actions.className = "ui-sidebar-actions";
|
||||||
|
panel.append(new FakeNode("p"));
|
||||||
|
if (!late) panel.append(actions);
|
||||||
|
const { root } = overlay.createWorkflowOverlays({
|
||||||
|
title: "t",
|
||||||
|
optionsContent: panel,
|
||||||
|
showProjectName: false,
|
||||||
|
});
|
||||||
|
const before = !!actions.querySelector(".ui-help-btn");
|
||||||
|
if (late) panel.append(actions);
|
||||||
|
flush();
|
||||||
|
const body = root.querySelector(".ui-workflow-overlay__body");
|
||||||
|
return {
|
||||||
|
before,
|
||||||
|
help: actions.querySelector(".ui-help-btn") ? 1 : 0,
|
||||||
|
split: body.classList.contains("ui-sidebar-split"),
|
||||||
|
scroll: !!panel.querySelector(".ui-sidebar-scroll"),
|
||||||
|
lastIsActions: panel.children[panel.children.length - 1] === actions,
|
||||||
|
watching: observers.size,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const early = mount(false);
|
||||||
|
const late = mount(true);
|
||||||
|
process.stdout.write(JSON.stringify({ early, late }));
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""요구 359-2 · 360-2 — 좌측 패널 아래 [?] 는 액션 줄을 언제 붙여도 한 벌.
|
||||||
|
|
||||||
|
① 패널 세우기 전에 붙인 줄(E05 꼴) = 바로 쪼갬 · [?]
|
||||||
|
② 세운 뒤에 붙인 줄(E03 · E04 · E06 · E07 꼴) = 붙는 때 쪼갬 · [?] · 지켜보기 끊음
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="module")
|
||||||
|
def results():
|
||||||
|
proc = subprocess.run(
|
||||||
|
["node", os.path.join(HERE, "helper_359_2_late_actions.cjs")],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
encoding="utf-8",
|
||||||
|
timeout=60,
|
||||||
|
)
|
||||||
|
assert proc.returncode == 0, proc.stderr
|
||||||
|
return json.loads(proc.stdout)
|
||||||
|
|
||||||
|
|
||||||
|
def test_먼저_붙인_줄은_바로_물음(results):
|
||||||
|
early = results["early"]
|
||||||
|
assert early["before"] is True
|
||||||
|
assert early["help"] == 1 and early["split"] and early["scroll"] and early["lastIsActions"]
|
||||||
|
assert early["watching"] == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_늦게_붙인_줄도_물음_한_벌(results):
|
||||||
|
late = results["late"]
|
||||||
|
assert late["before"] is False
|
||||||
|
assert late["help"] == 1 and late["split"] and late["scroll"] and late["lastIsActions"]
|
||||||
|
assert late["watching"] == 0
|
||||||
@@ -201,6 +201,14 @@ function createPanel(
|
|||||||
if (body.childElementCount > 0) {
|
if (body.childElementCount > 0) {
|
||||||
body.classList.add("ui-workflow-overlay__body");
|
body.classList.add("ui-workflow-overlay__body");
|
||||||
if (isSidebar) splitSidebarActions(body);
|
if (isSidebar) splitSidebarActions(body);
|
||||||
|
// 액션 줄을 패널을 세운 뒤에 붙이는 화면(E03 · E04 · E06 · E07) — 붙는 때 쪼갬 · [?] 자리
|
||||||
|
if (isSidebar && !body.classList.contains("ui-sidebar-split")) {
|
||||||
|
const watch = new MutationObserver(() => {
|
||||||
|
splitSidebarActions(body);
|
||||||
|
if (body.classList.contains("ui-sidebar-split")) watch.disconnect();
|
||||||
|
});
|
||||||
|
watch.observe(body, { childList: true, subtree: true });
|
||||||
|
}
|
||||||
root.append(body);
|
root.append(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user