feat(b09): 좌측 하단 고정 줄 — [박스보기|호표보기] 한 설정(묶음 탭 셋 · 옛 탭별 값 하나로 · 탭 줄 토글 걷음) · [하이퍼링크|팝업] 스위치(pref global · 기본 팝업) · DRAFT.actions 자리 · [?][저장][내역 초기화] 그대로 (303-1 · 304-1)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ji4nuyLVHcxrXWHTdb5nRD
This commit is contained in:
@@ -64,9 +64,11 @@ export const STATE_REGISTRY = {
|
||||
"m02-card-pills": { bucket: "pref", scope: "global" },
|
||||
/** M01 단가산출 로직 상세 오른쪽 — 열린 패널 하나(flow · source · none) */
|
||||
"m01-logic-panel": { bucket: "pref", scope: "global" },
|
||||
/** B09 묶음 탭(171-3) 보기 `{탭: cards · table}` · 가로 열 수 `{탭: 1~4}` */
|
||||
/** B09 묶음 탭(171-3) 보기 `cards · table`(304-1 셋 탭 한 값) · 가로 열 수 `{탭: 1~4}` */
|
||||
"b09-group-view": { bucket: "pref", scope: "global" },
|
||||
"b09-group-cols": { bucket: "pref", scope: "global" },
|
||||
/** B09 행 누름(303-1) `popup · link` */
|
||||
"b09-link-mode": { bucket: "pref", scope: "global" },
|
||||
/** B05 계획노선 편집 창 — 보던 화면(회전 · 가운데 · 배율). 프로젝트마다 따로 */
|
||||
"routeedit-view": { bucket: "pref", scope: "project" },
|
||||
/** B05 계획노선 편집 창 — 시점 · 종점 · 교각점 고정. [저장] 대상이 아니라 draft 에 안 둠 */
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/* =============================================================================
|
||||
* B09_Estimation_UI_Dock.ts
|
||||
* B09 좌측 하단 고정 줄(PLAN 303-1 · 304-1) — 위에서부터
|
||||
* ① [박스보기|호표보기] 묶음 탭 셋 보기(304-1 · 한 값)
|
||||
* ② [하이퍼링크|팝업] 행 누름(303 · 기본 팝업)
|
||||
* ③ `DRAFT.actions` 단추(초안 · 비면 줄 없음)
|
||||
* ④ [?][저장][내역 초기화](도움말 줄 `data-help-row`)
|
||||
*
|
||||
* 스위치 값 = pref global(계정 공용 · [저장] 필요 없음) · 바닥 고정 = 공용 `.ui-sidebar-actions` 한 벌
|
||||
* ========================================================================== */
|
||||
|
||||
import { createButton } from "@ui/ui_template_elements";
|
||||
import { readState, writeState } from "../A00_Common/b_page_state";
|
||||
import { DRAFT } from "./B09_Estimation_UI_Tabs";
|
||||
import {
|
||||
chooseGroupView,
|
||||
groupView,
|
||||
viewLabel,
|
||||
type GroupView,
|
||||
} from "./B09_Estimation_UI_Tabs_Groups";
|
||||
|
||||
export type LinkMode = "popup" | "link";
|
||||
|
||||
const LINK_LABEL: Record<LinkMode, string> = { link: "하이퍼링크", popup: "팝업" };
|
||||
|
||||
/** 행 누름 길 — 모르는 값 · 못 읽음 = 팝업 */
|
||||
export function linkMode(): LinkMode {
|
||||
try {
|
||||
return readState<unknown>("b09-link-mode") === "link" ? "link" : "popup";
|
||||
} catch {
|
||||
return "popup";
|
||||
}
|
||||
}
|
||||
|
||||
function setLinkMode(mode: LinkMode): void {
|
||||
try {
|
||||
writeState("b09-link-mode", mode);
|
||||
} catch {
|
||||
// 기억 못 하면 기본(팝업)으로 돌아감
|
||||
}
|
||||
}
|
||||
|
||||
/** 단추 둘 스위치 — 켜진 쪽 filled · aria-pressed */
|
||||
function switchRow<T extends string>(
|
||||
name: string,
|
||||
values: T[],
|
||||
label: (value: T) => string,
|
||||
current: () => T,
|
||||
pick: (value: T) => void,
|
||||
): HTMLElement {
|
||||
const row = document.createElement("div");
|
||||
row.className = "ui-btn-group b09-dock__row";
|
||||
row.dataset.b09Switch = name;
|
||||
const buttons = values.map((value) => {
|
||||
const button = createButton({
|
||||
label: label(value),
|
||||
variant: "ghost",
|
||||
onClick: () => {
|
||||
if (value === current()) return;
|
||||
pick(value);
|
||||
sync();
|
||||
},
|
||||
});
|
||||
button.dataset.value = value;
|
||||
return button;
|
||||
});
|
||||
const sync = (): void =>
|
||||
buttons.forEach((button, k) => {
|
||||
const on = values[k] === current();
|
||||
button.classList.toggle("ui-btn--filled", on);
|
||||
button.classList.toggle("ui-btn--ghost", !on);
|
||||
button.setAttribute("aria-pressed", String(on));
|
||||
});
|
||||
sync();
|
||||
row.append(...buttons);
|
||||
return row;
|
||||
}
|
||||
|
||||
/** 하단 고정 줄 — onView = 보기 바뀜(묶음 탭이면 다시 그림) */
|
||||
export function createDock(buttons: HTMLElement[], onView: () => void): HTMLElement {
|
||||
const views: GroupView[] = ["cards", "table"];
|
||||
const view = switchRow("view", views, viewLabel, groupView, (value) => {
|
||||
chooseGroupView(value);
|
||||
onView();
|
||||
});
|
||||
const link = switchRow<LinkMode>(
|
||||
"link",
|
||||
["link", "popup"],
|
||||
(mode) => LINK_LABEL[mode],
|
||||
linkMode,
|
||||
setLinkMode,
|
||||
);
|
||||
const extra = DRAFT.actions.map((make) => make());
|
||||
const draft = document.createElement("div");
|
||||
draft.className = "b09-dock__row";
|
||||
draft.dataset.b09DraftActions = "";
|
||||
draft.append(...extra);
|
||||
const main = document.createElement("div");
|
||||
main.className = "b09-dock__row";
|
||||
main.dataset.helpRow = "";
|
||||
main.append(...buttons);
|
||||
const dock = document.createElement("div");
|
||||
dock.className = "ui-sidebar-actions b09-dock";
|
||||
dock.append(view, link, ...(extra.length ? [draft] : []), main);
|
||||
return dock;
|
||||
}
|
||||
@@ -248,3 +248,24 @@
|
||||
.b09-estimation [data-b09-table="bill"] tbody tr.is-drag-src td {
|
||||
opacity: 0.45;
|
||||
}
|
||||
|
||||
/* 303-1 · 304-1 좌측 하단 고정 줄 — 줄 여럿 세로(보기 · 행 누름 스위치 · 초안 단추 · [?][저장][내역 초기화]) */
|
||||
.b09-dock.ui-sidebar-actions {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.b09-dock.ui-sidebar-actions > * {
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.b09-dock .b09-dock__row {
|
||||
display: flex;
|
||||
gap: var(--btn-group-gap);
|
||||
}
|
||||
|
||||
.b09-dock .b09-dock__row > .ui-btn:not(.ui-help-btn) {
|
||||
flex: 1 1 0;
|
||||
min-width: auto;
|
||||
padding-inline: var(--spacing-4);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import { computeTotal } from "./B09_Estimation_Calc_Total";
|
||||
import { createTabBars } from "./B09_Estimation_UI_TabBar";
|
||||
import { createCheckPanel } from "./B09_Estimation_UI_Check";
|
||||
import { tableOptionsOf, tabTableOf } from "./B09_Estimation_UI_Builders";
|
||||
import { createDock } from "./B09_Estimation_UI_Dock";
|
||||
import {
|
||||
DRAFT,
|
||||
rowIndex,
|
||||
@@ -216,9 +217,8 @@ export function renderB09Estimation(root: HTMLElement): void {
|
||||
return made;
|
||||
};
|
||||
const hint = spec.hint ? lineHint(note(spec.hint)) : null; // 236-1 안내 글 = 한 줄
|
||||
// 171-3 묶음 탭(일위대가표 · 중기사용료 · 단가산출근거) = 묶음 카드 기본 · 머리 단추로 표
|
||||
// 171-3 묶음 탭(일위대가표 · 중기사용료 · 단가산출근거) = 묶음 카드 기본 · 좌측 하단 스위치로 표(304-1)
|
||||
if (isGroupTab(tab)) {
|
||||
const redraw = (): void => show(tab);
|
||||
return void panel.replaceChildren(
|
||||
...renderGroupPanel({
|
||||
tab,
|
||||
@@ -227,7 +227,6 @@ export function renderB09Estimation(root: HTMLElement): void {
|
||||
focus,
|
||||
goto,
|
||||
makeTable,
|
||||
redraw,
|
||||
setTools: bars.setTools,
|
||||
}),
|
||||
);
|
||||
@@ -321,9 +320,8 @@ export function renderB09Estimation(root: HTMLElement): void {
|
||||
// 카드 = 좌측 패널 공용 카드(269 · 「구조물 배치」 양식)
|
||||
const section = createPanelCard("데이터 점검");
|
||||
section.append(refresh, check.list);
|
||||
const actions = document.createElement("div");
|
||||
actions.className = "ui-sidebar-actions";
|
||||
actions.append(save, reset);
|
||||
// 303-1 · 304-1 하단 고정 줄 — [박스보기|호표보기] · [하이퍼링크|팝업] · 초안 단추 · [?][저장][내역 초기화]
|
||||
const actions = createDock([save, reset], () => isGroupTab(current) && show(current));
|
||||
const left = document.createElement("div");
|
||||
left.className = "ui-sidebar-panel b09-options";
|
||||
left.append(section, ...DRAFT.panels.map((make) => make(() => user, setUser)), actions);
|
||||
|
||||
@@ -123,12 +123,14 @@ export type TabBuild = (ctx: TabContext) => TabTable | string;
|
||||
|
||||
/** 초안 탭(252 · `draft/*_UI.ts` 가 TABS · TAB_GROUPS 와 함께 넣음 · 폴더째 지우면 빈 채로) —
|
||||
* builders = 탭 짜기 · extend = 브라우저 셈 곁(셈 결과에 더함) ·
|
||||
* panels = 왼쪽 패널 카드(264 · 「데이터 점검」 아래 · 지금 사용자 값 · 고침 → 캐시 · 다시 셈) */
|
||||
* panels = 왼쪽 패널 카드(264 · 「데이터 점검」 아래 · 지금 사용자 값 · 고침 → 캐시 · 다시 셈) ·
|
||||
* actions = 좌측 하단 고정 줄 단추(303-1 · [저장] 줄 위 한 줄 · 비면 줄 없음) */
|
||||
export const DRAFT: {
|
||||
builders: Record<string, TabBuild>;
|
||||
extend: Array<(payload: Row, user: Row, sheets: Row) => void>;
|
||||
panels: Array<(user: () => Row, setUser: (next: Row) => void) => HTMLElement>;
|
||||
} = { builders: {}, extend: [], panels: [] };
|
||||
actions: Array<() => HTMLElement>;
|
||||
} = { builders: {}, extend: [], panels: [], actions: [] };
|
||||
|
||||
/** 201-1 호표 묶음 레벨(보기 전용) — 머리 · 합계(제목 줄) = 1 · 그 밖 = 2 · key 열을 들여씀 */
|
||||
export const blockLevel = (key: string, top: (index: number) => boolean): TabTable["level"] => ({
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
/* =============================================================================
|
||||
* B09_Estimation_UI_Tabs_Groups.ts
|
||||
* B09 묶음 탭 셋(PLAN 171-3) — 일위대가표 · 중기사용료 · 단가산출근거 = 공용 묶음 카드(171-2) 기본 ·
|
||||
* [표로 보기] = 공용 표 그대로(맞은편 단추 = [박스보기]).
|
||||
* 호표보기 = 공용 표 그대로 · 고름 = 좌측 하단 [박스보기|호표보기] 한 벌(304-1 · 셋 탭 함께).
|
||||
*
|
||||
* 카드 = 탭 표 줄(`TabTable.rows`)을 호표(줄 키 앞머리)마다 묶음 — 값 · 풍선 · 누름 이동은 표와 같음
|
||||
* 머리 줄 = 묶음 첫 줄 · 합계 줄 = 키 끝 `|합계` · `|총계` · 그 밖 = 몸 줄(표 줄 번호 그대로)
|
||||
* 목록표에서 온 호표(181-2) = 박스 붉은 외곽선 · 표 보기 = 그 호표 줄 구간 전체 붉은 외곽선
|
||||
* 248-1 빨강 하나(공용 고름 강조 안 붙임 · 스크롤만) · 줄 · 카드 누름 · 방향키 · Esc = 빨강 풂 ·
|
||||
* 누른 줄만 고름(is-focus) · Esc = 고름도 풂
|
||||
* 보기 · 가로 열 수 기억 = `b_page_state`(`b09-group-view` · `b09-group-cols` · 탭마다) · 못 읽으면 카드 · 2
|
||||
* 보기 단추 · 가로 열 수(202-1) = 페이지 보조 탭 줄 오른쪽(`setTools`) · 안내 글 = 표 · 카드 판 맨 아래
|
||||
* 보기 기억 = `b_page_state`(`b09-group-view` · 한 값 · 옛 탭별 값은 첫 읽기에 하나로) · 가로 열 수 =
|
||||
* `b09-group-cols`(탭마다) · 못 읽으면 카드 · 2
|
||||
* 가로 열 수(202-1) = 페이지 보조 탭 줄 오른쪽(`setTools`) · 안내 글 = 표 · 카드 판 맨 아래
|
||||
* 안내 글 길면 한 줄 줄임표 + 공용 호버로 전문(200-1)
|
||||
* 278-2 단가산출근거 박스 = 머리 줄(`|머리n`)을 운반 단가 창 ③ 칸 꼴로(`headSteps` 한 벌 · 몸 위 칸) ·
|
||||
* 출처 = 그 칸 맨 위 · 호표 줄 · 총계 = 그대로 · 묶음 자료(`tabGroups`) · 표는 안 바뀜
|
||||
* ========================================================================== */
|
||||
|
||||
import type { DataRow, DataTable } from "@ui/ui_template_data_table";
|
||||
import { createButton } from "@ui/ui_template_elements";
|
||||
import { attachHoverInfo } from "@ui/ui_template_hover_info";
|
||||
import {
|
||||
clampColumns,
|
||||
@@ -38,10 +38,8 @@ export const GROUP_TABS: TabId[] = ["dg", "machine_blocks", "sg"];
|
||||
|
||||
export const isGroupTab = (tab: TabId): boolean => GROUP_TABS.includes(tab);
|
||||
|
||||
/** 단추 글 = 바꿀 보기 이름 */
|
||||
export const viewLabel = (view: GroupView): string => (view === "cards" ? "표로 보기" : "박스보기");
|
||||
|
||||
export const flipView = (view: GroupView): GroupView => (view === "cards" ? "table" : "cards");
|
||||
/** 단추 글 = 그 보기 이름(304-1 좌측 스위치) */
|
||||
export const viewLabel = (view: GroupView): string => (view === "cards" ? "박스보기" : "호표보기");
|
||||
|
||||
type Saved = Record<string, unknown> | null;
|
||||
|
||||
@@ -53,19 +51,39 @@ const saved = (name: "b09-group-view" | "b09-group-cols"): Saved => {
|
||||
}
|
||||
};
|
||||
|
||||
/** 고른 보기 — 모르는 값 · 못 읽음 = 카드 */
|
||||
export const readGroupView = (tab: TabId, kept: Saved = saved("b09-group-view")): GroupView =>
|
||||
kept?.[tab] === "table" ? "table" : "cards";
|
||||
/** 고른 보기(셋 탭 한 값) — 옛 탭별 값 = dg · 다른 탭 차례 · 모르는 값 · 못 읽음 = 카드 */
|
||||
export function readGroupView(kept: unknown = saved("b09-group-view")): GroupView {
|
||||
const old = kept && typeof kept === "object" ? (kept as Record<string, unknown>) : null;
|
||||
const value = old ? (old.dg ?? old.machine_blocks ?? old.sg) : kept;
|
||||
return value === "table" ? "table" : "cards";
|
||||
}
|
||||
|
||||
/** 이 화면 안 보기(기억이 안 돼도 고른 보기 유지) */
|
||||
let chosen: GroupView | null = null;
|
||||
|
||||
/** 지금 보기 — 첫 읽기에 옛 탭별 값을 한 값으로 옮겨 적음 */
|
||||
export function groupView(): GroupView {
|
||||
if (chosen) return chosen;
|
||||
const kept = saved("b09-group-view");
|
||||
chosen = readGroupView(kept);
|
||||
if (kept && typeof kept === "object") chooseGroupView(chosen);
|
||||
return chosen;
|
||||
}
|
||||
|
||||
export function chooseGroupView(view: GroupView): void {
|
||||
chosen = view;
|
||||
try {
|
||||
writeState("b09-group-view", view);
|
||||
} catch {
|
||||
// 기억 못 해도 이 화면 안에서는 고른 값 그대로
|
||||
}
|
||||
}
|
||||
|
||||
/** 고른 가로 열 수 — 1~4 · 모르는 값 = 2 */
|
||||
export const readGroupCols = (tab: TabId, kept: Saved = saved("b09-group-cols")): number =>
|
||||
clampColumns(kept?.[tab]);
|
||||
|
||||
export function rememberGroup(
|
||||
name: "b09-group-view" | "b09-group-cols",
|
||||
tab: TabId,
|
||||
value: unknown,
|
||||
): void {
|
||||
export function rememberGroup(name: "b09-group-cols", tab: TabId, value: unknown): void {
|
||||
try {
|
||||
writeState(name, { ...(saved(name) ?? {}), [tab]: value });
|
||||
} catch {
|
||||
@@ -244,33 +262,18 @@ export function unpickOnRelease(root: HTMLElement): void {
|
||||
listenKeys();
|
||||
}
|
||||
|
||||
/** 이 화면 안 보기(기억이 안 돼도 바꾼 보기 유지) */
|
||||
const chosen = new Map<TabId, GroupView>();
|
||||
|
||||
/** 묶음 탭 — 카드 = 카드 판 + 안내 글 · 표 = 표 + 안내 글 · 보기 단추 · 열 수 = `setTools`(보조 탭 줄 오른쪽) */
|
||||
/** 묶음 탭 — 카드 = 카드 판 + 안내 글 · 표 = 표 + 안내 글 · 열 수 = `setTools`(보조 탭 줄 오른쪽) */
|
||||
export function renderGroupPanel(o: {
|
||||
tab: TabId;
|
||||
spec: TabTable;
|
||||
hint: HTMLElement | null;
|
||||
focus?: string;
|
||||
goto: (link: Link | null) => void;
|
||||
goto: (link: Link | null, anchor?: Element | null) => void;
|
||||
makeTable: (rowClass?: (index: number) => string | null) => DataTable;
|
||||
redraw: () => void;
|
||||
setTools: (tools: HTMLElement[]) => void;
|
||||
}): HTMLElement[] {
|
||||
const view = chosen.get(o.tab) ?? readGroupView(o.tab);
|
||||
const view = groupView();
|
||||
const lead = lineHint(o.hint);
|
||||
const toggle = createButton({
|
||||
label: viewLabel(view),
|
||||
variant: "ghost",
|
||||
size: "sm",
|
||||
onClick: () => {
|
||||
chosen.set(o.tab, flipView(view));
|
||||
rememberGroup("b09-group-view", o.tab, flipView(view));
|
||||
o.redraw();
|
||||
},
|
||||
});
|
||||
toggle.dataset.b09GroupView = view;
|
||||
const at = o.focus === undefined ? -1 : rowIndex(o.spec.rows, o.focus);
|
||||
if (view === "cards") {
|
||||
const first = o.spec.columns[0]?.key ?? "";
|
||||
@@ -281,19 +284,18 @@ export function renderGroupPanel(o: {
|
||||
: tabGroups(o.tab, o.spec),
|
||||
columns: readGroupCols(o.tab),
|
||||
onColumns: (n) => rememberGroup("b09-group-cols", o.tab, n),
|
||||
onPick: (i) => o.goto(o.spec.link?.(i, first) ?? null),
|
||||
onPick: (i) =>
|
||||
o.goto(o.spec.link?.(i, first) ?? null, cards.querySelector(`[data-at="${i}"]`)),
|
||||
focus: at,
|
||||
money: numberFormat(o.spec, "sum"),
|
||||
qty: numberFormat(o.spec, "qty"),
|
||||
actions: [toggle],
|
||||
placeTools: o.setTools, // 202-1 가로 열 수 · 보기 단추 = 보조 탭 줄 오른쪽
|
||||
placeTools: o.setTools, // 202-1 가로 열 수 = 보조 탭 줄 오른쪽
|
||||
});
|
||||
cards.dataset.b09Table = o.tab;
|
||||
live = { root: cards, unpick: (esc) => unpickCards(cards, esc) };
|
||||
listenKeys();
|
||||
return [cards, ...(lead ? [lead] : [])];
|
||||
}
|
||||
o.setTools([toggle]);
|
||||
let range = pickRange(o.spec.rows, o.focus);
|
||||
let chose = -1; // 누른 줄(다시 그려도 유지)
|
||||
const table = o.makeTable((i) => (i === chose ? "is-focus" : pickClass(range, i)));
|
||||
|
||||
@@ -62,20 +62,19 @@ def test_157_2_두_보기_같은_값():
|
||||
|
||||
|
||||
def test_157_2_단추_바꿈():
|
||||
out = _node(
|
||||
"\nconsole.log(JSON.stringify([viewLabel('table'), viewLabel('cards'),"
|
||||
" flipView('table'), flipView(flipView('table'))]));"
|
||||
)
|
||||
assert out == ["박스보기", "표로 보기", "cards", "table"]
|
||||
out = _node("\nconsole.log(JSON.stringify([viewLabel('table'), viewLabel('cards')]));")
|
||||
assert out == ["호표보기", "박스보기"] # 304-1 단추 글 = 그 보기 이름
|
||||
|
||||
|
||||
def test_157_2_기억_실패_때_카드():
|
||||
out = _node(
|
||||
"\nconst a = [readGroupView('sg', null), readGroupView('sg', { sg: 'x' }),"
|
||||
" readGroupView('sg', { dg: 'table' }), readGroupView('sg', { sg: 'table' })];"
|
||||
"\nconst a = [readGroupView(null), readGroupView({ sg: 'x' }), readGroupView({ dg: 'table' }),"
|
||||
" readGroupView({ sg: 'table' }), readGroupView('table'), readGroupView('x'),"
|
||||
" readGroupView({ dg: 'cards', sg: 'table' })];"
|
||||
"\nconsole.log(JSON.stringify(a));"
|
||||
)
|
||||
assert out == ["cards", "cards", "cards", "table"]
|
||||
# 304-1 한 값 · 옛 탭별 값 = dg 먼저 · 없으면 다른 탭 · 모르는 값 = 카드
|
||||
assert out == ["cards", "cards", "table", "table", "table", "cards", "cards"]
|
||||
|
||||
|
||||
def test_157_2_화면_잇기():
|
||||
|
||||
@@ -118,27 +118,27 @@ def test_단가산출근거_카드():
|
||||
assert out["links"] == [None, {"tab": "labor", "key": "LB2"}]
|
||||
|
||||
|
||||
def test_보기_기억_탭마다():
|
||||
def test_보기_한_값_열_수_탭마다():
|
||||
out = run(
|
||||
[COMMON, TABS, GROUPS],
|
||||
"\nconst a = [readGroupView('dg'), readGroupCols('dg')];"
|
||||
"\nrememberGroup('b09-group-view', 'dg', 'table');"
|
||||
"\nconst a = [readGroupView(), readGroupCols('dg')];"
|
||||
"\nkept.set('b09-group-view', JSON.stringify({ machine_blocks: 'table' }));" # 옛 탭별 값
|
||||
"\nrememberGroup('b09-group-cols', 'sg', 3);"
|
||||
"\nconst b = [readGroupView('dg'), readGroupView('sg'), readGroupView('machine_blocks'),"
|
||||
"\nconst b = [groupView(), readGroupView(), readGroupView(),"
|
||||
" readGroupCols('sg'), readGroupCols('dg'), readGroupCols('dg', { dg: 9 })];"
|
||||
"\nconsole.log(JSON.stringify({ a, b, kept: Object.fromEntries(kept),"
|
||||
" tabs: GROUP_TABS, other: ['bill', 'sum_material', 'sum_labor', 'sum_expense',"
|
||||
" 'sum_machine', 'dg_list'].map(isGroupTab) }));",
|
||||
)
|
||||
assert out["a"] == ["cards", 2] # 기본 = 카드 · 가로 2
|
||||
assert out["b"] == ["table", "cards", "cards", 3, 2, 2]
|
||||
assert json.loads(out["kept"]["b09-group-view"]) == {"dg": "table"}
|
||||
assert out["b"] == ["table", "table", "table", 3, 2, 2] # 304-1 셋 탭 한 값
|
||||
assert json.loads(out["kept"]["b09-group-view"]) == "table" # 첫 읽기에 옛 값 → 한 값
|
||||
assert json.loads(out["kept"]["b09-group-cols"]) == {"sg": 3}
|
||||
assert out["tabs"] == ["dg", "machine_blocks", "sg"]
|
||||
assert out["other"] == [False] * 6 # 집계표 넷 · 설계내역서 · 목록표 = 대상 밖
|
||||
|
||||
|
||||
def test_화면_보기_바꿈_누름_이동():
|
||||
def test_화면_보기_고름_누름_이동():
|
||||
out = run(
|
||||
[COMMON, TABS, f"{B09}Labor.ts", GROUPS],
|
||||
f"\nconst spec = buildDaegaBlocks({{ sheets: {{ daega_blocks: [{json.dumps(DG, ensure_ascii=False)}] }} }});"
|
||||
@@ -147,26 +147,25 @@ def test_화면_보기_바꿈_누름_이동():
|
||||
" redraw: () => redraws++, setTools: (t) => calls.push(t), makeTable: () => ({ root: document.createElement('table'),"
|
||||
" focusRow: (i) => focused.push(i) }) };"
|
||||
"\nconst [view] = renderGroupPanel(o);"
|
||||
"\nconst btn = calls[0].find((e) => e.dataset.b09GroupView);"
|
||||
"\nconst label = btn.textContent;"
|
||||
"\nconst label = calls.flat().filter((e) => e.dataset.b09GroupView).length;"
|
||||
"\nconst lit = view.querySelector('[data-at=\"3\"]').classList.contains('is-focus');"
|
||||
"\nconst box = view.querySelector('.ui-group-card').classList.contains('is-pick');"
|
||||
"\nview.querySelector('[data-at=\"2\"]').click();"
|
||||
"\nview.querySelector('[data-at=\"0\"]').click();"
|
||||
"\nbtn.click();"
|
||||
"\nconst before = calls.length; chooseGroupView('table');"
|
||||
"\nconst [view2] = renderGroupPanel(o);"
|
||||
"\nconsole.log(JSON.stringify({ label, table: view.dataset.b09Table, gone, lit, box, redraws,"
|
||||
" label2: calls[1].find((e) => e.dataset.b09GroupView).textContent, tag2: view2.tagName, focused,"
|
||||
" tools2: calls.length - before, tag2: view2.tagName, focused,"
|
||||
" kept: Object.fromEntries(kept) }));",
|
||||
RAW,
|
||||
)
|
||||
assert out["label"] == "표로 보기" and out["table"] == "dg" # 기본 = 카드
|
||||
assert out["label"] == 0 and out["table"] == "dg" # 기본 = 카드 · 304-1 탭 줄 보기 단추 없음
|
||||
assert out["gone"] == [{"tab": "labor", "key": "L00005"}, None] # 줄 누름 = 표와 같은 이동
|
||||
assert out["lit"] is False # 248-1 이동해 온 줄 = 빨강 하나(줄 강조 안 붙임)
|
||||
assert out["box"] is True # 그 호표 박스 = 붉은 외곽선(181-2)
|
||||
assert out["redraws"] == 1
|
||||
assert out["label2"] == "박스보기" and out["tag2"] == "TABLE" and out["focused"] == [3]
|
||||
assert json.loads(out["kept"]["b09-group-view"]) == {"dg": "table"}
|
||||
assert out["redraws"] == 0
|
||||
assert out["tools2"] == 0 and out["tag2"] == "TABLE" and out["focused"] == [3]
|
||||
assert json.loads(out["kept"]["b09-group-view"]) == "table"
|
||||
|
||||
|
||||
def test_잇기():
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""181-2 박스보기 — 이름 · 접기 없음 · 목록표에서 고른 호표 = 박스 붉은 외곽선 · 표 보기 = 줄 구간 전체.
|
||||
|
||||
- 단추 글 「박스보기」 ↔ 「표로 보기」 · 요약(접기) 보기 지움
|
||||
- 단추 글 「박스보기」 · 「호표보기」(304-1 좌측 스위치) · 요약(접기) 보기 지움
|
||||
- 표 보기 = 고른 호표 줄 구간(키 `|` 앞머리 같은 줄) 모두 `is-pick` · 첫 줄 `is-pick-top` · 끝 줄 `is-pick-end`
|
||||
- 붉은 색 = `--color-danger`(B06 횡단 카드 고름 꼴)
|
||||
"""
|
||||
@@ -52,7 +52,7 @@ def test_표_보기_줄_class_넘김():
|
||||
"\nconst o = { tab: 'dg', spec, hint: null, focus: 'B|1', goto: () => {}, redraw: () => {}, setTools: () => {},"
|
||||
" makeTable: (e) => { extra = e; return { root: document.createElement('table'),"
|
||||
" focusRow: () => {} }; } };"
|
||||
"\nchosen.set('dg', 'table');"
|
||||
"\nchooseGroupView('table');"
|
||||
"\nrenderGroupPanel(o);"
|
||||
"\nconsole.log(JSON.stringify(spec.rows.map((_, i) => extra(i))));",
|
||||
)
|
||||
@@ -63,7 +63,7 @@ def test_표_보기_줄_class_넘김():
|
||||
|
||||
def test_글_접기_없음_CSS_잇기():
|
||||
groups = (ROOT / GROUPS).read_text("utf-8")
|
||||
assert '"표로 보기" : "박스보기"' in groups and "호표별 보기" not in groups
|
||||
assert '"박스보기" : "호표보기"' in groups and "호표별 보기" not in groups # 304-1 글
|
||||
common = (ROOT / COMMON).read_text("utf-8")
|
||||
for gone in ("isSummary", "GROUP_SUMMARY_OVER", "is-closed", "모두 펼침", "모두 접기"):
|
||||
assert gone not in common
|
||||
|
||||
@@ -286,4 +286,4 @@ def test_내역_초기화_배선():
|
||||
assert "showConfirmDialog(" in block and "if (!ok) return;" in block
|
||||
assert "const { bill: _bill, ...rest } = user;" in block
|
||||
assert 'run("/estimation", "PUT"' in block and "{ user: rest }" in block
|
||||
assert "actions.append(save, reset);" in src
|
||||
assert "createDock([save, reset]," in src # 303-1 하단 고정 줄 맨 아래 줄
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""185-2 B09 묶음 탭 보기 바꿈 단추 글 한 줄 · 가로 열 수 단추와 같은 줄.
|
||||
"""185-2 B09 묶음 탭 가로 열 수 단추 한 줄 · 보기 고름 = 좌측 하단(304-1 · 탭 줄에 없음).
|
||||
|
||||
- 보기 바꿈 단추 · 가로 열 수 = 도구 묶음(`setTools` · 202-1 로 페이지 보조 탭 줄 오른쪽) · 카드 판 머리 띠 = 묶음 수만
|
||||
- 가로 열 수 = 도구 묶음(`setTools` · 202-1 로 페이지 보조 탭 줄 오른쪽) · 카드 판 머리 띠 = 묶음 수만
|
||||
- 단추 · 라벨 = `white-space: nowrap` · 도구 묶음 = 줄바꿈 없음(`flex-wrap: nowrap`)
|
||||
"""
|
||||
|
||||
@@ -31,7 +31,7 @@ def _panel(view: str, hint: str):
|
||||
return run(
|
||||
[COMMON, TABS, f"{B09}Labor.ts", GROUPS],
|
||||
f"\nconst spec = buildDaegaBlocks({{ sheets: {{ daega_blocks: [{json.dumps(DG, ensure_ascii=False)}] }} }});"
|
||||
f"\nchosen.set('dg', '{view}');"
|
||||
f"\nchooseGroupView('{view}');"
|
||||
"\nconst tools = [];"
|
||||
f"\nconst o = {{ tab: 'dg', spec, hint: {hint}, goto: () => {{}}, redraw: () => {{}},"
|
||||
" setTools: (t) => tools.push(...t),"
|
||||
@@ -39,7 +39,7 @@ def _panel(view: str, hint: str):
|
||||
"\nconst out = renderGroupPanel(o);"
|
||||
"\nconst btn = tools.find((e) => e.dataset.b09GroupView);"
|
||||
"\nconst bar = out[0].all('.ui-group-cards__bar')[0] ?? null;"
|
||||
"\nconsole.log(JSON.stringify({ n: out.length, first: out[0].className, label: btn.textContent,"
|
||||
"\nconsole.log(JSON.stringify({ n: out.length, first: out[0].className, label: btn ? btn.textContent : null, nTools: tools.length,"
|
||||
" cols: tools.flatMap((e) => e.all('[data-group-cols]')).map((b) => b.dataset.groupCols),"
|
||||
" barBtn: bar ? bar.all('[data-b09-group-view]').length : null,"
|
||||
" barCols: bar ? bar.all('[data-group-cols]').length : null }));",
|
||||
@@ -49,7 +49,7 @@ def _panel(view: str, hint: str):
|
||||
|
||||
def test_카드_보기_단추가_열수_단추와_같은_도구_묶음에():
|
||||
out = _panel("cards", "null")
|
||||
assert out["label"] == "표로 보기"
|
||||
assert out["label"] is None # 304-1 보기 단추 = 좌측 하단(탭 줄에 없음)
|
||||
assert out["cols"] == ["1", "2", "3", "4"] # 같은 묶음에 열 수 단추 넷
|
||||
assert out["barBtn"] == 0 and out["barCols"] == 0 # 카드 판 머리 띠에는 없음
|
||||
assert out["n"] == 1 # 안내 글 없으면 카드 판 하나뿐
|
||||
@@ -63,8 +63,8 @@ def test_카드_보기_안내_글은_카드_판_아래에():
|
||||
|
||||
def test_표_보기_단추는_도구_묶음에():
|
||||
out = _panel("table", "null")
|
||||
assert out["label"] == "박스보기" and out["n"] == 1
|
||||
assert out["cols"] == [] # 표 보기 = 열 수 단추 없음
|
||||
assert out["label"] is None and out["n"] == 1
|
||||
assert out["cols"] == [] and out["nTools"] == 0 # 표 보기 = 도구 없음
|
||||
|
||||
|
||||
def test_CSS_한_줄_nowrap():
|
||||
@@ -81,4 +81,4 @@ def test_CSS_한_줄_nowrap():
|
||||
page, ".b09-estimation__tools .ui-btn,\n.b09-estimation__tools .ui-group-cards__label"
|
||||
)
|
||||
assert "white-space: nowrap" in tools
|
||||
assert "actions: [toggle]" in (ROOT / GROUPS).read_text("utf-8")
|
||||
assert "toggle" not in (ROOT / GROUPS).read_text("utf-8") # 304-1 탭 줄 토글 걷음
|
||||
|
||||
@@ -36,7 +36,7 @@ def _panel(view: str, hint: str):
|
||||
return run(
|
||||
[COMMON, TABS, f"{B09}Labor.ts", GROUPS],
|
||||
f"\nconst spec = buildDaegaBlocks({{ sheets: {{ daega_blocks: [{json.dumps(DG, ensure_ascii=False)}] }} }});"
|
||||
f"\nchosen.set('dg', '{view}');"
|
||||
f"\nchooseGroupView('{view}');"
|
||||
f"\nconst hint = {hint};"
|
||||
"\nconst o = { tab: 'dg', spec, hint, goto: () => {}, redraw: () => {}, setTools: () => {},"
|
||||
" makeTable: () => ({ root: document.createElement('table'), focusRow: () => {} }) };"
|
||||
|
||||
@@ -80,7 +80,7 @@ def test_묶음_탭_단추가_보조_줄에_안내는_아래에():
|
||||
"\nconst hint = document.createElement('p');"
|
||||
"\nconst res = {};"
|
||||
"\nfor (const view of ['cards', 'table']) {"
|
||||
"\n chosen.set('dg', view);"
|
||||
"\n chooseGroupView(view);"
|
||||
"\n bars.draw('dg'); bars.setTools([]);"
|
||||
"\n const panel = renderGroupPanel({ tab: 'dg', spec, hint, goto: () => {}, redraw: () => {},"
|
||||
" makeTable: () => ({ root: document.createElement('table'), focusRow: () => {} }),"
|
||||
@@ -95,8 +95,9 @@ def test_묶음_탭_단추가_보조_줄에_안내는_아래에():
|
||||
"\nconsole.log(JSON.stringify(res));",
|
||||
RAW,
|
||||
)
|
||||
assert out["cards"]["inRow"] == ["표로 보기"] and out["cards"]["cols"] == 4
|
||||
assert out["table"]["inRow"] == ["박스보기"] and out["table"]["cols"] == 0
|
||||
# 304-1 보기 고름 = 좌측 하단 스위치(보조 줄에 없음) · 가로 열 수 = 보조 줄 그대로
|
||||
assert out["cards"]["inRow"] == [] and out["cards"]["cols"] == 4
|
||||
assert out["table"]["inRow"] == [] and out["table"]["cols"] == 0
|
||||
for view in ("cards", "table"):
|
||||
assert out[view]["inPanel"] == 0 # 판 안에는 단추 없음
|
||||
assert out[view]["hintLast"] and out[view]["n"] == 2 # 판 + 안내 줄(맨 아래)
|
||||
|
||||
@@ -60,7 +60,7 @@ def test_표_빨강_하나_누름_키_풂():
|
||||
" return { root, focusRow: (i, mark) => scrolled.push([i, mark]) }; };"
|
||||
"\nconst o = { tab: 'dg', spec, hint: null, focus: 'B|1', goto: () => {}, redraw: () => {},"
|
||||
" setTools: () => {}, makeTable };"
|
||||
"\nchosen.set('dg', 'table');"
|
||||
"\nchooseGroupView('table');"
|
||||
"\nconst marks = () => [root.children.map((tr) => tr.className || null),"
|
||||
" spec.rows.map((_, i) => extra(i))];"
|
||||
"\nconst out = {};"
|
||||
|
||||
@@ -205,6 +205,6 @@ def test_패널_훅_draft_없으면_빈칸():
|
||||
tabs = (ROOT / "B09_Estimation/B09_Estimation_UI_Tabs.ts").read_text("utf-8")
|
||||
shell = (ROOT / "B09_Estimation/B09_Estimation_UI_Shell.ts").read_text("utf-8")
|
||||
ui = (ROOT / "B09_Estimation/draft/B09_Draft_Schedule_UI.ts").read_text("utf-8")
|
||||
assert "{ builders: {}, extend: [], panels: [] }" in tabs # draft 없으면 카드 없음
|
||||
assert "{ builders: {}, extend: [], panels: [], actions: [] }" in tabs # draft 없으면 카드 없음
|
||||
assert "...DRAFT.panels.map((make) => make(() => user, setUser))" in shell
|
||||
assert "DRAFT.panels.push(" in ui
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
"""303 B09 행 고름 = 팝업 · 하이퍼링크 스위치 · 좌측 하단 줄.
|
||||
|
||||
303-1 좌측 하단 고정 줄 — [박스보기|호표보기] · [하이퍼링크|팝업] · `DRAFT.actions`(비면 줄 없음) ·
|
||||
[?][저장][내역 초기화](도움말 줄) · 스위치 = pref global · 기본 = 팝업
|
||||
303-2 표 짜기 = `…_UI_Builders`(화면 · 팝업 · 인쇄 같은 옵션)
|
||||
"""
|
||||
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
if str(ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from resources.tester.helper_b09_group_cards import COMMON, GROUPS, TABS, run # noqa: E402
|
||||
|
||||
B09 = ROOT / "B09_Estimation"
|
||||
DOCK = "B09_Estimation/B09_Estimation_UI_Dock.ts"
|
||||
BUILDERS = "B09_Estimation/B09_Estimation_UI_Builders.ts"
|
||||
|
||||
DOCK_JS = (
|
||||
"\nconst save = document.createElement('button'); save.textContent = '저장';"
|
||||
"\nconst reset = document.createElement('button'); reset.textContent = '내역 초기화';"
|
||||
"\nconst shape = (dock) => dock.children.map((r) => ({ sw: r.dataset.b09Switch ?? null,"
|
||||
" draft: 'b09DraftActions' in r.dataset, help: 'helpRow' in r.dataset,"
|
||||
" text: r.children.map((b) => b.textContent) }));"
|
||||
)
|
||||
|
||||
|
||||
def test_하단_줄_차례_초안_단추_없으면_줄_없음():
|
||||
out = run(
|
||||
[COMMON, TABS, GROUPS, DOCK],
|
||||
DOCK_JS + "\nconst a = createDock([save, reset], () => {});"
|
||||
"\nDRAFT.actions.push(() => { const b = document.createElement('button');"
|
||||
" b.textContent = '인쇄 미리보기'; return b; });"
|
||||
"\nconst b = createDock([save, reset], () => {});"
|
||||
"\nconsole.log(JSON.stringify({ cls: a.className, a: shape(a), b: shape(b) }));",
|
||||
)
|
||||
assert out["cls"] == "ui-sidebar-actions b09-dock" # 바닥 고정 = 공용 액션 줄 한 벌
|
||||
view = {"sw": "view", "draft": False, "help": False, "text": ["박스보기", "호표보기"]}
|
||||
link = {"sw": "link", "draft": False, "help": False, "text": ["하이퍼링크", "팝업"]}
|
||||
main = {"sw": None, "draft": False, "help": True, "text": ["저장", "내역 초기화"]}
|
||||
assert out["a"] == [view, link, main] # 초안 단추 없음 = 줄 없음
|
||||
draft = {"sw": None, "draft": True, "help": False, "text": ["인쇄 미리보기"]}
|
||||
assert out["b"] == [view, link, draft, main] # 초안 단추 = [저장] 줄 위
|
||||
|
||||
|
||||
def test_스위치_기본_팝업_누름_pref():
|
||||
out = run(
|
||||
[COMMON, TABS, GROUPS, DOCK],
|
||||
DOCK_JS + "\nconst dock = createDock([save, reset], () => {});"
|
||||
"\nconst row = dock.children[1];"
|
||||
"\nconst pressed = () => row.children.map((b) => b.attrs['aria-pressed']);"
|
||||
"\nconst a = [linkMode(), pressed()];"
|
||||
"\nrow.children[0].click();"
|
||||
"\nconst b = [linkMode(), pressed(), kept.get('b09-link-mode')];"
|
||||
"\nrow.children[1].click();"
|
||||
"\nconst c = [linkMode(), pressed(), kept.get('b09-link-mode')];"
|
||||
"\nkept.set('b09-link-mode', '\"x\"');"
|
||||
"\nconsole.log(JSON.stringify({ a, b, c, odd: linkMode() }));",
|
||||
)
|
||||
assert out["a"] == ["popup", ["false", "true"]] # 기본 = 팝업
|
||||
assert out["b"] == ["link", ["true", "false"], '"link"']
|
||||
assert out["c"] == ["popup", ["false", "true"], '"popup"']
|
||||
assert out["odd"] == "popup" # 모르는 값 = 팝업
|
||||
|
||||
|
||||
def test_DRAFT_actions_자리():
|
||||
tabs = (B09 / "B09_Estimation_UI_Tabs.ts").read_text("utf-8")
|
||||
assert "actions: Array<() => HTMLElement>;" in tabs
|
||||
assert "{ builders: {}, extend: [], panels: [], actions: [] }" in tabs
|
||||
state = (ROOT / "A00_Common/b_page_state.ts").read_text("utf-8")
|
||||
assert '"b09-link-mode": { bucket: "pref", scope: "global" },' in state
|
||||
css = (B09 / "B09_Estimation_UI_Page.css").read_text("utf-8")
|
||||
m = re.search(r"\.b09-dock\.ui-sidebar-actions\s*\{([^}]*)\}", css)
|
||||
assert m and "flex-direction: column" in m.group(1) # 줄 여럿 세로
|
||||
|
||||
|
||||
# 표 짜기 — 탭 짜기 18 개 대신 가짜(줄 하나) · 옵션 = 표 그대로 넘김
|
||||
FAKE_BUILDS = "".join(
|
||||
f"\nconst {name} = () => ({{ columns: [], rows: [{{ key: '{name}' }}] }});"
|
||||
for name in re.findall(
|
||||
r":\s*(build\w+),",
|
||||
(ROOT / BUILDERS).read_text("utf-8"),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def test_Builders_화면_같은_옵션():
|
||||
out = run(
|
||||
[TABS, BUILDERS],
|
||||
"\nDRAFT.builders.draft_x = () => '안내';"
|
||||
"\nconst spec = { columns: [{ key: 'a', label: 'A' }], rows: [{ key: 'r' }], frozen: 2,"
|
||||
" editable: () => true, onEdit: () => {}, rowClass: () => 'x', cellClass: () => 'y',"
|
||||
" level: { key: 'a', of: () => 1 }, mergeSame: ['a'], link: () => null, menu: () => [] };"
|
||||
"\nconst o = tableOptionsOf(spec, 'bill');"
|
||||
"\nconst plain = tableOptionsOf({ columns: [], rows: [], editOnDouble: false });"
|
||||
"\nconsole.log(JSON.stringify({ keys: Object.keys(o).sort(), caption: o.caption,"
|
||||
" same: ['columns', 'rows', 'editable', 'onEdit', 'rowClass', 'cellClass', 'level', 'mergeSame']"
|
||||
".every((k) => o[k] === spec[k]), frozen: o.frozen, total: o.total, empty: o.empty,"
|
||||
" dbl: [o.editOnDouble, plain.editOnDouble], key: [o.keyEdit, plain.keyEdit],"
|
||||
" plainCaption: plain.caption ?? null, plainFrozen: plain.frozen,"
|
||||
" bill: tabTableOf('bill', {}).rows[0].key, draft: tabTableOf('draft_x', {}),"
|
||||
" none: tabTableOf('nope', {}) }));",
|
||||
FAKE_BUILDS,
|
||||
)
|
||||
assert out["keys"] == sorted(
|
||||
[
|
||||
"columns",
|
||||
"rows",
|
||||
"total",
|
||||
"frozen",
|
||||
"empty",
|
||||
"caption",
|
||||
"editable",
|
||||
"editOnDouble",
|
||||
"keyEdit",
|
||||
"onEdit",
|
||||
"rowClass",
|
||||
"cellClass",
|
||||
"level",
|
||||
"mergeSame",
|
||||
]
|
||||
) # 누름 · 메뉴 · 풍선 없음(그리는 쪽 몫)
|
||||
assert out["caption"] == "설계내역서" and out["same"] is True
|
||||
assert out["frozen"] == 2 and out["total"] is None and out["empty"] == ""
|
||||
assert out["dbl"] == [True, False] # 243-1 기본 = 더블클릭
|
||||
assert out["key"] == [False, True] # 설계내역서(메뉴) Enter · F2 = 레벨 키 몫
|
||||
assert out["plainCaption"] is None and out["plainFrozen"] == 0
|
||||
assert out["bill"] == "buildBill" and out["draft"] == "안내" and out["none"] is None
|
||||
|
||||
|
||||
def test_화면이_Builders_옵션을_씀():
|
||||
shell = (B09 / "B09_Estimation_UI_Shell.ts").read_text("utf-8")
|
||||
assert "...tableOptionsOf(spec, tab)," in shell
|
||||
assert "const spec = tabTableOf(tab, ctx);" in shell
|
||||
assert "BUILDERS" not in shell
|
||||
@@ -0,0 +1,103 @@
|
||||
"""304-1 B09 박스보기 · 호표보기 = 좌측 하단 한 설정(묶음 탭 셋 함께).
|
||||
|
||||
- 한 값(`b09-group-view` · pref global)이 dg · machine_blocks · sg 셋에 함께 · 기본 = 박스(카드)
|
||||
- 옛 탭별 값(`{탭: 보기}`) = 첫 읽기에 한 값으로(dg · 없으면 다른 탭 · 없으면 기본) 옮겨 적음
|
||||
- 보조 탭 줄 보기 토글 걷음 · 가로 열 수 단추(박스 보기)는 그대로
|
||||
- 좌측 하단 스위치 [박스보기|호표보기] 누름 = 값 바꿈 · 다시 그림 부름
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
if str(ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from resources.tester.helper_b09_group_cards import COMMON, GROUPS, RAW, TABS, run # noqa: E402
|
||||
|
||||
LABOR = "B09_Estimation/B09_Estimation_UI_Tabs_Labor.ts"
|
||||
DOCK = "B09_Estimation/B09_Estimation_UI_Dock.ts"
|
||||
DG = {"id": "DG1", "no": 1, "name": "찰쌓기", "unit": "m2", "lines": [], "sum": 0}
|
||||
SPEC = (
|
||||
"\nconst spec = buildDaegaBlocks({ sheets: { daega_blocks: ["
|
||||
+ json.dumps(DG, ensure_ascii=False)
|
||||
+ "] } });"
|
||||
"\nconst tools = [];"
|
||||
"\nconst draw = (tab) => renderGroupPanel({ tab, spec, hint: null, goto: () => {},"
|
||||
" setTools: (t) => tools.push(...t),"
|
||||
" makeTable: () => ({ root: document.createElement('table'), focusRow: () => {} }) })[0];"
|
||||
"\nconst kinds = () => ['dg', 'machine_blocks', 'sg'].map((t) => draw(t).tagName);"
|
||||
)
|
||||
|
||||
|
||||
def test_한_설정_셋_탭_함께():
|
||||
out = run(
|
||||
[COMMON, TABS, LABOR, GROUPS],
|
||||
SPEC + "\nconst a = kinds();"
|
||||
"\nchooseGroupView('table');"
|
||||
"\nconst b = kinds();"
|
||||
"\nconsole.log(JSON.stringify({ a, b, kept: Object.fromEntries(kept),"
|
||||
" toggles: tools.filter((e) => e.dataset.b09GroupView).length,"
|
||||
" cols: tools.flatMap((e) => e.all('[data-group-cols]')).length }));",
|
||||
RAW,
|
||||
)
|
||||
assert out["a"] == ["DIV"] * 3 # 기본 = 박스
|
||||
assert out["b"] == ["TABLE"] * 3 # 한 값이 셋 탭 함께
|
||||
assert json.loads(out["kept"]["b09-group-view"]) == "table"
|
||||
assert out["toggles"] == 0 # 탭 줄 보기 토글 없음
|
||||
assert out["cols"] == 4 * 3 # 박스 보기 가로 열 수 단추 = 탭 줄 그대로
|
||||
|
||||
|
||||
def test_옛_탭별_값_첫_읽기에_한_값으로():
|
||||
cases = [
|
||||
({"dg": "table", "sg": "cards"}, "table"),
|
||||
({"dg": "cards", "sg": "table"}, "cards"), # dg 먼저
|
||||
({"sg": "table"}, "table"), # dg 없으면 다른 탭
|
||||
({"machine_blocks": "table", "sg": "cards"}, "table"),
|
||||
({}, "cards"), # 없으면 기본
|
||||
]
|
||||
for old, want in cases:
|
||||
out = run(
|
||||
[COMMON, TABS, LABOR, GROUPS],
|
||||
"\nkept.set('b09-group-view', "
|
||||
+ json.dumps(json.dumps(old))
|
||||
+ ");"
|
||||
+ SPEC
|
||||
+ "\nconst got = kinds();"
|
||||
"\nconsole.log(JSON.stringify({ got, kept: Object.fromEntries(kept) }));",
|
||||
RAW,
|
||||
)
|
||||
assert out["got"] == ["TABLE" if want == "table" else "DIV"] * 3, old
|
||||
assert json.loads(out["kept"]["b09-group-view"]) == want, old # 옮겨 적음
|
||||
|
||||
|
||||
def test_좌측_스위치_누름():
|
||||
out = run(
|
||||
[COMMON, TABS, GROUPS, DOCK],
|
||||
"\nlet redraws = 0;"
|
||||
"\nconst dock = createDock([document.createElement('button')], () => redraws++);"
|
||||
"\nconst row = dock.children[0];"
|
||||
"\nconst pressed = () => row.children.map((b) => b.attrs['aria-pressed']);"
|
||||
"\nconst labels = row.children.map((b) => b.textContent);"
|
||||
"\nconst a = pressed();"
|
||||
"\nrow.children[0].click();" # 이미 박스 = 그대로
|
||||
"\nconst b = [redraws, pressed()];"
|
||||
"\nrow.children[1].click();"
|
||||
"\nconsole.log(JSON.stringify({ name: row.dataset.b09Switch, labels, a, b, c: [redraws, pressed()],"
|
||||
" view: groupView(), kept: Object.fromEntries(kept) }));",
|
||||
)
|
||||
assert out["name"] == "view" and out["labels"] == ["박스보기", "호표보기"]
|
||||
assert out["a"] == ["true", "false"] # 기본 = 박스보기
|
||||
assert out["b"] == [0, ["true", "false"]]
|
||||
assert out["c"] == [1, ["false", "true"]] and out["view"] == "table"
|
||||
assert json.loads(out["kept"]["b09-group-view"]) == "table" # [저장] 없이 pref
|
||||
|
||||
|
||||
def test_잇기():
|
||||
groups = (ROOT / GROUPS).read_text("utf-8")
|
||||
assert "toggle" not in groups and "redraw" not in groups and "createButton" not in groups
|
||||
shell = (ROOT / "B09_Estimation/B09_Estimation_UI_Shell.ts").read_text("utf-8")
|
||||
assert "createDock([save, reset], () => isGroupTab(current) && show(current))" in shell
|
||||
state = (ROOT / "A00_Common/b_page_state.ts").read_text("utf-8")
|
||||
assert '"b09-group-view": { bucket: "pref", scope: "global" },' in state
|
||||
Reference in New Issue
Block a user