fix(cupang): 센터 분배 드래그 실패 지점 제거·센터 목록 가나다 정렬

드래그가 브라우저에서 먹지 않던 문제를 추정 대신 실패 가능 지점별로
전부 막았다.

- dragstart 리스너를 카드 요소에 직접 바인딩(위임으로는 잡히지 않는
  환경 대비). 렌더할 때마다 다시 바인딩한다.
- dragenter/dragover 를 모두 preventDefault — 하나만 막으면 드롭을
  거부하는 브라우저가 있다.
- 드롭 대상 범위를 ③ 카드 전체로 확대하고, 대상 패널은
  closest → elementFromPoint → (센터가 하나면) 그 센터 순으로 찾는다.
  패널 사이 여백에 놓아도 담긴다.
- dataTransfer.setData/getData 가 막힌 환경 대비로 dragKey 를 따로 보관해
  드롭 시 사용한다. 그래도 못 읽으면 클릭으로 담으라는 안내를 띄운다.
- 드래그 중에는 ③ 영역에 점선 강조를 준다.
- 드래그 직후 발생하는 click 은 무시해 대화상자가 두 번 뜨지 않게 한다.

센터 선택 목록은 이름 가나다순 정렬(한글 음절은 코드포인트 순).

tests/js: 여백 드롭·dragenter·setData 차단 환경 시나리오 추가. 전체 통과.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-29 03:08:09 +09:00
parent ead1361790
commit ae7daabae5
10 changed files with 132 additions and 41 deletions
+31
View File
@@ -134,6 +134,37 @@ function fire(el, type, init) { el.dispatchEvent(new w.Event(type, Object.assign
check("센터 빼기", $$(".cpg-dist-center").length === 0);
check("센터 빼면 전량 잔여 복귀", text(".cpg-sum-prod .cpg-sum-prod-nums").startsWith("2박스16개"), text(".cpg-sum-prod .cpg-sum-prod-nums"));
// ── 드롭 실패 경로 방어 확인 ──────────────────────
$("#cpg-center-pick").value = "2";
$("#cpg-center-add").click();
check("센터 다시 추가", $$(".cpg-dist-center").length === 1, String($$(".cpg-dist-center").length));
// (1) 패널이 아닌 여백(리스트 자체)에 놓아도, 센터가 하나면 그 센터로 담긴다
const card2 = $(".cpg-sum-prod[draggable='true']");
const dt3 = { data: {}, setData(t, v) { this.data[t] = v; }, getData(t) { return this.data[t] || ""; }, types: ["text/plain"] };
const ds3 = new w.Event("dragstart", { bubbles: true }); ds3.dataTransfer = dt3;
card2.dispatchEvent(ds3);
const den = new w.Event("dragenter", { bubbles: true, cancelable: true }); den.dataTransfer = dt3;
$(".cpg-dist-card").dispatchEvent(den);
check("dragenter 도 기본동작을 막는다", den.defaultPrevented);
const drop3 = new w.Event("drop", { bubbles: true, cancelable: true }); drop3.dataTransfer = dt3;
$("#cpg-dist-list").dispatchEvent(drop3);
check("여백에 놓아도 대화상자가 열린다", $("#cpg-dlg").hidden === false);
$("#cpg-dlg-ok").click();
check("여백 드롭도 센터에 담긴다", $$(".cpg-dist-item").length === 1, String($$(".cpg-dist-item").length));
// (2) setData 를 막는 브라우저: dataTransfer 가 비어도 dragKey 로 담긴다
$(".cpg-dist-del").click();
const card3 = $(".cpg-sum-prod[draggable='true']");
const dtBlocked = { setData() { throw new Error("blocked"); }, getData() { return ""; }, types: [] };
const ds4 = new w.Event("dragstart", { bubbles: true }); ds4.dataTransfer = dtBlocked;
card3.dispatchEvent(ds4);
const drop4 = new w.Event("drop", { bubbles: true, cancelable: true }); drop4.dataTransfer = dtBlocked;
$(".cpg-dist-center").dispatchEvent(drop4);
check("setData 가 막혀도 드롭이 동작한다", $("#cpg-dlg").hidden === false, text("#cpg-calc-msg"));
$("#cpg-dlg-ok").click();
check("setData 차단 환경에서도 담긴다", $$(".cpg-dist-item").length === 1);
console.log("\n" + (fails.length ? "FAILED: " + fails.join(" | ") : "ALL PASS"));
process.exit(fails.length ? 1 : 0);
})();