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_modal": { openModal: () => undefined },
|
||||
"./ui_template_help_edit": { attachHelpEditing: () => ({ flush: () => undefined }) },
|
||||
"./ui_template_elements": {
|
||||
showToast: (message) => toasts.push(message),
|
||||
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) {
|
||||
body.classList.add("ui-workflow-overlay__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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user