mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-06-16 10:43:18 +03:00
refactor(web-test): этап B.6 — table/grid-toggle.mjs (icon detection shared)
В clickElement две ветки (gridGroup/gridParent + gridTreeNode) имели
почти идентичные page.evaluate-блоки: найти gridLine под target.y,
получить иконку-разворачивалку, вернуть её центр + isExpanded.
table/grid-toggle.mjs:
- getGridToggleIcon(target, formNum, { iconSelector, isExpandedExpr })
- shouldClickToggle(iconInfo, expand, toggle)
Поведение 1-в-1. Селекторы и isExpanded-критерий передаются параметрами:
- groups: '.gridListH, .gridListV' + icon.classList.contains('gridListV')
- trees: '.gridBoxImg [tree="true"]' + bg.includes('gx=0')
Экономия ~30 LOC дублей.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -146,6 +146,7 @@ import {
|
||||
safeClick, findFieldInputId, readEdd, returnFormState,
|
||||
detectNewForm as helperDetectNewForm,
|
||||
} from './core/helpers.mjs';
|
||||
import { getGridToggleIcon, shouldClickToggle } from './table/grid-toggle.mjs';
|
||||
// Re-export only what was publicly exported before the refactor.
|
||||
// waitForStable/waitForCondition/startNetworkMonitor/closeModals/checkForErrors/
|
||||
// dismissPendingErrors are internal helpers — imported above for local use only.
|
||||
@@ -1891,31 +1892,13 @@ export async function clickElement(text, { dblclick, table, toggle, expand, modi
|
||||
// Grid row targets — use coordinate click (single or double)
|
||||
if (target.kind === 'gridGroup' || target.kind === 'gridParent') {
|
||||
if (expand != null || toggle) {
|
||||
// Expand/collapse group in hierarchy mode — click the triangle icon (.gridListH/.gridListV)
|
||||
// expand=true: only expand (skip if already expanded), expand=false: only collapse, toggle: always click
|
||||
const levelIconInfo = await page.evaluate(`(() => {
|
||||
const p = ${JSON.stringify(`form${formNum}_`)};
|
||||
const gridSel = ${JSON.stringify(target.gridId ? '#' + target.gridId : null)};
|
||||
const grid = gridSel ? document.querySelector(gridSel) : document.querySelector('[id^="' + p + '"].grid');
|
||||
const body = grid?.querySelector('.gridBody');
|
||||
if (!body) return null;
|
||||
const targetY = ${target.y};
|
||||
const lines = [...body.querySelectorAll('.gridLine')];
|
||||
for (const line of lines) {
|
||||
const lr = line.getBoundingClientRect();
|
||||
if (targetY < lr.top || targetY > lr.bottom) continue;
|
||||
const icon = line.querySelector('.gridListH, .gridListV');
|
||||
if (icon) {
|
||||
const r = icon.getBoundingClientRect();
|
||||
const isExpanded = !!icon.classList.contains('gridListV');
|
||||
return { x: Math.round(r.x + r.width / 2), y: Math.round(r.y + r.height / 2), isExpanded };
|
||||
}
|
||||
}
|
||||
return null;
|
||||
})()`);
|
||||
const shouldClick = toggle || !levelIconInfo
|
||||
|| (expand === true && !levelIconInfo.isExpanded)
|
||||
|| (expand === false && levelIconInfo.isExpanded);
|
||||
// Expand/collapse group in hierarchy mode — click the triangle icon (.gridListH/.gridListV).
|
||||
// expand=true: only expand (skip if already expanded), expand=false: only collapse, toggle: always click.
|
||||
const levelIconInfo = await getGridToggleIcon(target, formNum, {
|
||||
iconSelector: '.gridListH, .gridListV',
|
||||
isExpandedExpr: "icon.classList.contains('gridListV')",
|
||||
});
|
||||
const shouldClick = shouldClickToggle(levelIconInfo, expand, toggle);
|
||||
if (shouldClick) {
|
||||
if (levelIconInfo) {
|
||||
await modClick(levelIconInfo.x, levelIconInfo.y);
|
||||
@@ -1939,32 +1922,13 @@ export async function clickElement(text, { dblclick, table, toggle, expand, modi
|
||||
}
|
||||
if (target.kind === 'gridTreeNode') {
|
||||
if (expand != null || toggle) {
|
||||
// Expand/collapse tree node — click the tree icon [tree="true"]
|
||||
// expand=true: only expand (skip if already expanded), expand=false: only collapse, toggle: always click
|
||||
const treeIconInfo = await page.evaluate(`(() => {
|
||||
const p = ${JSON.stringify(`form${formNum}_`)};
|
||||
const gridSel = ${JSON.stringify(target.gridId ? '#' + target.gridId : null)};
|
||||
const grid = gridSel ? document.querySelector(gridSel) : document.querySelector('[id^="' + p + '"].grid');
|
||||
const body = grid?.querySelector('.gridBody');
|
||||
if (!body) return null;
|
||||
const targetY = ${target.y};
|
||||
const lines = [...body.querySelectorAll('.gridLine')];
|
||||
for (const line of lines) {
|
||||
const lr = line.getBoundingClientRect();
|
||||
if (targetY < lr.top || targetY > lr.bottom) continue;
|
||||
const treeIcon = line.querySelector('.gridBoxImg [tree="true"]');
|
||||
if (treeIcon) {
|
||||
const r = treeIcon.getBoundingClientRect();
|
||||
const bg = treeIcon.style.backgroundImage || '';
|
||||
const isExpanded = bg.includes('gx=0');
|
||||
return { x: Math.round(r.x + r.width / 2), y: Math.round(r.y + r.height / 2), isExpanded };
|
||||
}
|
||||
}
|
||||
return null;
|
||||
})()`);
|
||||
const shouldClick = toggle || !treeIconInfo
|
||||
|| (expand === true && !treeIconInfo.isExpanded)
|
||||
|| (expand === false && treeIconInfo.isExpanded);
|
||||
// Expand/collapse tree node — click the tree icon [tree="true"].
|
||||
// expand=true: only expand (skip if already expanded), expand=false: only collapse, toggle: always click.
|
||||
const treeIconInfo = await getGridToggleIcon(target, formNum, {
|
||||
iconSelector: '.gridBoxImg [tree="true"]',
|
||||
isExpandedExpr: '(icon.style.backgroundImage || "").includes("gx=0")',
|
||||
});
|
||||
const shouldClick = shouldClickToggle(treeIconInfo, expand, toggle);
|
||||
if (shouldClick) {
|
||||
if (treeIconInfo) {
|
||||
await modClick(treeIconInfo.x, treeIconInfo.y);
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
// web-test table/grid-toggle v1.16 — shared icon-detection for grid expand/
|
||||
// collapse toggles. Used by clickElement's gridGroup/gridParent and
|
||||
// gridTreeNode branches; the actual mouse click stays in the caller because
|
||||
// it depends on the caller-local modifier-key handling.
|
||||
// Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import { page } from '../core/state.mjs';
|
||||
|
||||
/**
|
||||
* Locate the toggle icon for the grid row at `target.y`. Inspects the row
|
||||
* under that Y-coordinate inside the resolved grid, returns the icon's
|
||||
* center coordinates and current expanded state — or `null` if no toggle
|
||||
* icon is present (e.g. leaf node or detached row).
|
||||
*
|
||||
* @param {{y:number, gridId?:string}} target
|
||||
* @param {number} formNum
|
||||
* @param {object} opts
|
||||
* @param {string} opts.iconSelector — CSS selector inside .gridLine
|
||||
* (e.g. '.gridListH, .gridListV' for groups, '.gridBoxImg [tree="true"]' for tree nodes)
|
||||
* @param {string} opts.isExpandedExpr — JS expression evaluated in browser
|
||||
* context where `icon` is the matched element; must yield a boolean
|
||||
* (e.g. "icon.classList.contains('gridListV')" or "(icon.style.backgroundImage || '').includes('gx=0')")
|
||||
* @returns {Promise<{x:number, y:number, isExpanded:boolean}|null>}
|
||||
*/
|
||||
export async function getGridToggleIcon(target, formNum, { iconSelector, isExpandedExpr }) {
|
||||
return await page.evaluate(`(() => {
|
||||
const p = ${JSON.stringify(`form${formNum}_`)};
|
||||
const gridSel = ${JSON.stringify(target.gridId ? '#' + target.gridId : null)};
|
||||
const grid = gridSel ? document.querySelector(gridSel) : document.querySelector('[id^="' + p + '"].grid');
|
||||
const body = grid?.querySelector('.gridBody');
|
||||
if (!body) return null;
|
||||
const targetY = ${target.y};
|
||||
const lines = [...body.querySelectorAll('.gridLine')];
|
||||
for (const line of lines) {
|
||||
const lr = line.getBoundingClientRect();
|
||||
if (targetY < lr.top || targetY > lr.bottom) continue;
|
||||
const icon = line.querySelector(${JSON.stringify(iconSelector)});
|
||||
if (icon) {
|
||||
const r = icon.getBoundingClientRect();
|
||||
const isExpanded = ${isExpandedExpr};
|
||||
return { x: Math.round(r.x + r.width / 2), y: Math.round(r.y + r.height / 2), isExpanded };
|
||||
}
|
||||
}
|
||||
return null;
|
||||
})()`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard expand/toggle decision: should we click the toggle icon?
|
||||
* - `toggle:true` → always click.
|
||||
* - `expand:true` → click only if not already expanded.
|
||||
* - `expand:false` → click only if currently expanded.
|
||||
* - If no icon found (`iconInfo` is null) → click anyway (caller falls back to dblclick).
|
||||
*
|
||||
* @param {{isExpanded:boolean}|null} iconInfo
|
||||
* @param {boolean|undefined} expand
|
||||
* @param {boolean|undefined} toggle
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function shouldClickToggle(iconInfo, expand, toggle) {
|
||||
return toggle || !iconInfo
|
||||
|| (expand === true && !iconInfo.isExpanded)
|
||||
|| (expand === false && iconInfo.isExpanded);
|
||||
}
|
||||
Reference in New Issue
Block a user