AURORA-1842: свод 2 — виртуализация колонок и статичные стили #227

Open
aep wants to merge 3 commits from feat/220-aurora-1842-svod2-column-virtual into feat/210-aurora-1835-svod1-formatter
Owner

Closes #220

Задача Jira: AURORA-1842. Стек: база PR — feat/210-aurora-1835-svod1-formatter (AURORA-1835, PR #217), не test.

Что сделано

  • enableColumnVirtualization + columnVirtualizerOptions на базе существующего getColumnVirtualizerOptions (columnLayoutUtils.js не менялся), overscan колонок 3 переопределён снаружи.
  • Статичные стили ячеек и заголовков вынесены в одно CSS-правило на контейнере (muiTableContainerProps.sx): td/th, полоса svod2-band-1, толстая граница группы svod2-group-end, zIndex закреплённых через [data-pinned="true"].
  • muiTableBodyCellProps / muiTableHeadCellProps возвращают только className из карты columnId -> className, посчитанной один раз на лист в getColumnConfig.
  • Бейджи smeta_type и org_name — span с классами вместо Box sx.
  • Чистые функции вынесены в web/src/pages/Svod2Page/svod2Styles.js.
  • ROW_VIRTUALIZER_OPTIONS и numberFormatter не тронуты (AURORA-1836).

Тесты

  • Новый svod2Styles.test.js (полосы, границы групп, классы, карта классов, бейджи, наличие правил контейнера).
  • node --test svod2Styles.test.js columnLayoutUtils.test.js: 9 pass, 0 fail. npm run build проходит. Бэк не менялся.
  • biome: новые файлы чистые; в Svod2Page.jsx остаётся одна ранее существующая претензия форматтера к scaleNumericColumns/Tab.

Проверить руками

  • Горизонтальный скролл GO и RF до конца и обратно: заголовки групп в 2–3 ряда совпадают по ширине с телом.
  • Полосы и толстые границы групп на месте.
  • 4 закреплённые колонки не прыгают, их фон непрозрачный.
  • Select-фильтры в закреплённых колонках работают.
  • «Найдено N из M» при поиске.
  • Смена единиц.
  • Бейджи «Вид сметы» и «ССП» выглядят как раньше.

Замер до/после

Будет добавлен после ночного прогона на стенде perf/. Сценарий: PERF_RUN_ID=1842-before PERF_TARGETS='svod/svod2_GO' PERF_ROLES=dfip PERF_REPEATS=3 npx playwright test scenarios/scroll.spec.ts --grep-invert filter, затем aggregate. Критерий main_thread_ms_per_step не больше 60 (база 184,0). Если 60 не наберётся, PR не блокируется: фиксируем факт, остаток на 1836/1837.

Rulings I made

  • R-1: класс svod2-band-1 только для полосы 1, полоса 0 — базовый фон в правиле td/th. Цена ошибки: неверный фон ячеек, виден визуально.
  • R-2: opacity: 1 и position: sticky; top: 0 для th перенесены в правило контейнера (были в sx). Цена ошибки: смещ��ние шапки.

🤖 Generated with Claude Code

Closes #220 Задача Jira: AURORA-1842. Стек: база PR — `feat/210-aurora-1835-svod1-formatter` (AURORA-1835, PR #217), не `test`. ## Что сделано - `enableColumnVirtualization` + `columnVirtualizerOptions` на базе существующего `getColumnVirtualizerOptions` (`columnLayoutUtils.js` не менялся), overscan колонок 3 переопределён снаружи. - Статичные стили ячеек и заголовков вынесены в одно CSS-правило на контейнере (`muiTableContainerProps.sx`): `td`/`th`, полоса `svod2-band-1`, толстая граница группы `svod2-group-end`, zIndex закреплённых через `[data-pinned="true"]`. - `muiTableBodyCellProps` / `muiTableHeadCellProps` возвращают только `className` из карты `columnId -> className`, посчитанной один раз на лист в `getColumnConfig`. - Бейджи `smeta_type` и `org_name` — `span` с классами вместо `Box sx`. - Чистые функции вынесены в `web/src/pages/Svod2Page/svod2Styles.js`. - `ROW_VIRTUALIZER_OPTIONS` и `numberFormatter` не тронуты (AURORA-1836). ## Тесты - Новый `svod2Styles.test.js` (полосы, границы групп, классы, карта классов, бейджи, наличие правил контейнера). - `node --test svod2Styles.test.js columnLayoutUtils.test.js`: 9 pass, 0 fail. `npm run build` проходит. Бэк не менялся. - biome: новые файлы чистые; в `Svod2Page.jsx` остаётся одна ранее существующая претензия форматтера к `scaleNumericColumns`/`Tab`. ## Проверить руками - Горизонтальный скролл GO и RF до конца и обратно: заголовки групп в 2–3 ряда совпадают по ширине с телом. - Полосы и толстые границы групп на месте. - 4 закреплённые колонки не прыгают, их фон непрозрачный. - Select-фильтры в закреплённых колонках работают. - «Найдено N из M» при поиске. - Смена единиц. - Бейджи «Вид сметы» и «ССП» выглядят как раньше. ## Замер до/после Будет добавлен после ночного прогона на стенде perf/. Сценарий: `PERF_RUN_ID=1842-before PERF_TARGETS='svod/svod2_GO' PERF_ROLES=dfip PERF_REPEATS=3 npx playwright test scenarios/scroll.spec.ts --grep-invert filter`, затем `aggregate`. Критерий `main_thread_ms_per_step` не больше 60 (база 184,0). Если 60 не наберётся, PR не блокируется: фиксируем факт, остаток на 1836/1837. ## Rulings I made - R-1: класс `svod2-band-1` только для полосы 1, полоса 0 — базовый фон в правиле `td`/`th`. Цена ошибки: неверный фон ячеек, виден визуально. - R-2: `opacity: 1` и `position: sticky; top: 0` для `th` перенесены в правило контейнера (были в sx). Цена ошибки: смещ��ние шапки. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
aep added 1 commit 2026-10-28 17:13:05 +03:00
Author
Owner

Ночной замер на стенде perf/ (29.10, масштаб 1, D0 без grow, CPU 1x, arm-ka-* остановлены; Apple M4)

Сборки: «до» — test 2a28748 (свод 2 у 1835 не меняется, базу сняли на test); «после» — этот PR 399234a (= 1835 + 1842). Данные и API одни (0049).
Команда: PERF_TARGETS='svod/svod2_GO' PERF_ROLES=dfip PERF_REPEATS=2 PERF_TRACE=0 npx playwright test scenarios/scroll.spec.ts --grep-invert filter — полная прокрутка без PERF_MAX_STEPS (2074 шага), 2 повтора вместо 3 ради времени ночи (разброс внутри пары < 1 %).

свод 2 GO, dfip до после критерий
main_thread_ms_per_step, медиана [min–max] 180.7 [180.0–181.5] 39.7 [39.6–39.8] ≤ 60 — выполнен (−78 %)
main_thread_ms за прокрутку 374 826 82 362
frame_p95_ms / heavy_frame_n 116.6 / 4132 33.3 / 2
загрузка до 20 строк (скрипт скриншотов) 1342 мс 1331 мс без изменений

Рендеры (профилирующая сборка :8081, hotspots.spec.ts PERF_HOT_MODE=react PERF_HOT_TARGETS='svod/svod2_GO:y', 80 шагов): рендеров компонентов на шаг 39 768 → 8 272, из них Styled(td) 7 810 → 1 326; self React 10 179 → 2 055 мс.
Высота строки (getBoundingClientRect, 1600×900): 37.44 px до и после.

Скриншоты (1600×900; 3 позиции по горизонтали x0/x1/x2 и по вертикали y0/y1): до — night/base/shots/base-svod2-1600-*.png, после — night/1842/shots/p1842-svod2-1600-*.png; каталог /private/tmp/claude-501/-Users-andrey-epifancev-projects-fomichev-claude/6fa9c75e-eb11-4e5d-98c1-05eb590ab14f/scratchpad/night/. Попиксельная сверка — в итоговом комментарии.

🤖 Generated with Claude Code

## Ночной замер на стенде perf/ (29.10, масштаб 1, D0 без grow, CPU 1x, arm-ka-* остановлены; Apple M4) **Сборки:** «до» — `test` 2a28748 (свод 2 у 1835 не меняется, базу сняли на `test`); «после» — этот PR 399234a (= 1835 + 1842). Данные и API одни (0049). **Команда:** `PERF_TARGETS='svod/svod2_GO' PERF_ROLES=dfip PERF_REPEATS=2 PERF_TRACE=0 npx playwright test scenarios/scroll.spec.ts --grep-invert filter` — полная прокрутка без `PERF_MAX_STEPS` (2074 шага), **2 повтора вместо 3** ради времени ночи (разброс внутри пары < 1 %). | свод 2 GO, dfip | до | после | критерий | |---|---|---|---| | main_thread_ms_per_step, медиана [min–max] | 180.7 [180.0–181.5] | **39.7** [39.6–39.8] | ≤ 60 — **выполнен** (−78 %) | | main_thread_ms за прокрутку | 374 826 | 82 362 | | | frame_p95_ms / heavy_frame_n | 116.6 / 4132 | 33.3 / 2 | | | загрузка до 20 строк (скрипт скриншотов) | 1342 мс | 1331 мс | без изменений | Рендеры (профилирующая сборка :8081, `hotspots.spec.ts PERF_HOT_MODE=react PERF_HOT_TARGETS='svod/svod2_GO:y'`, 80 шагов): рендеров компонентов на шаг 39 768 → **8 272**, из них `Styled(td)` 7 810 → **1 326**; self React 10 179 → 2 055 мс. Высота строки (getBoundingClientRect, 1600×900): 37.44 px до и после. Скриншоты (1600×900; 3 позиции по горизонтали x0/x1/x2 и по вертикали y0/y1): до — `night/base/shots/base-svod2-1600-*.png`, после — `night/1842/shots/p1842-svod2-1600-*.png`; каталог `/private/tmp/claude-501/-Users-andrey-epifancev-projects-fomichev-claude/6fa9c75e-eb11-4e5d-98c1-05eb590ab14f/scratchpad/night/`. Попиксельная сверка — в итоговом комментарии. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Author
Owner

Ночной замер — часть 2 (итог)

Сборки как в части 1 (до — test, после — 399234a = 1835 + 1842). Добавлено: попиксельная сверка и combo.

combo: свод 2 GO полный (2046 шагов, 1 повтор — время ночи): 180.7 → 1842 39.7 → combo 32.6; CPU-профиль GO, main thread за 80 шагов: base 12 953 мс → combo 2 283 мс.

Сверка скриншотов 1600×900 (base против p1842): 10,2 – 13,1 % по 6 позициям (top, x0, x1, x2, y0, y1); в combo 12,1 – 15,2 %. Это не только высота строки. На картинках:

  • base: в начале таблицы видны Вид сметы, Направление, ССП, Наименование статьи, ID статьи, ID группы номенклатуры, «Конкретный вид расхода», «1 кв.»; при прокрутке вправо — колонки «Корректировка внутри квартала» и далее;
  • 1842, top/x0: в DOM только первые 3 колонки, растянутые на всю ширину окна (Вид сметы / Направление / ССП);
  • 1842, x1 и x2: 4 колонки (+«Наименование статьи»), справа пустое поле; кадры x1 и x2 побайтно равны, а в base они разные.
    То есть числовые колонки на скриншотах свода 2 не видны ни в начале, ни при прокрутке вправо (скриншот делался через 700 мс после установки scrollLeft). Причина не установлена. Если это не артефакт скриншота, то цифра 39.7 снята на таблице с 4 колонками вместо полной. Проверить руками до мержа.
    Сравнить: /private/tmp/claude-501/-Users-andrey-epifancev-projects-fomichev-claude/6fa9c75e-eb11-4e5d-98c1-05eb590ab14f/scratchpad/night/base/shots/base-svod2-1600-x1.png и /private/tmp/claude-501/-Users-andrey-epifancev-projects-fomichev-claude/6fa9c75e-eb11-4e5d-98c1-05eb590ab14f/scratchpad/night/1842/shots/p1842-svod2-1600-x1.png; разница — /private/tmp/claude-501/-Users-andrey-epifancev-projects-fomichev-claude/6fa9c75e-eb11-4e5d-98c1-05eb590ab14f/scratchpad/night/imgdiff/diff-p1842-svod2-1600-top.png.

Вердикт по цели Jira (свод 2 GO ≤ 60 мс/шаг): по цифре выполнен (39.7; combo 32.6), с оговоркой выше.

## Ночной замер — часть 2 (итог) Сборки как в части 1 (до — `test`, после — 399234a = 1835 + 1842). Добавлено: попиксельная сверка и combo. **combo:** свод 2 GO полный (2046 шагов, 1 повтор — время ночи): 180.7 → 1842 39.7 → **combo 32.6**; CPU-профиль GO, main thread за 80 шагов: base 12 953 мс → combo 2 283 мс. **Сверка скриншотов 1600×900 (base против p1842): 10,2 – 13,1 % по 6 позициям (top, x0, x1, x2, y0, y1); в combo 12,1 – 15,2 %.** Это не только высота строки. На картинках: - base: в начале таблицы видны Вид сметы, Направление, ССП, Наименование статьи, ID статьи, ID группы номенклатуры, «Конкретный вид расхода», «1 кв.»; при прокрутке вправо — колонки «Корректировка внутри квартала» и далее; - 1842, top/x0: в DOM только первые 3 колонки, растянутые на всю ширину окна (Вид сметы / Направление / ССП); - 1842, x1 и x2: 4 колонки (+«Наименование статьи»), справа пустое поле; **кадры x1 и x2 побайтно равны**, а в base они разные. То есть числовые колонки на скриншотах свода 2 не видны ни в начале, ни при прокрутке вправо (скриншот делался через 700 мс после установки `scrollLeft`). Причина не установлена. Если это не артефакт скриншота, то цифра 39.7 снята на таблице с 4 колонками вместо полной. **Проверить руками до мержа.** Сравнить: `/private/tmp/claude-501/-Users-andrey-epifancev-projects-fomichev-claude/6fa9c75e-eb11-4e5d-98c1-05eb590ab14f/scratchpad/night/base/shots/base-svod2-1600-x1.png` и `/private/tmp/claude-501/-Users-andrey-epifancev-projects-fomichev-claude/6fa9c75e-eb11-4e5d-98c1-05eb590ab14f/scratchpad/night/1842/shots/p1842-svod2-1600-x1.png`; разница — `/private/tmp/claude-501/-Users-andrey-epifancev-projects-fomichev-claude/6fa9c75e-eb11-4e5d-98c1-05eb590ab14f/scratchpad/night/imgdiff/diff-p1842-svod2-1600-top.png`. **Вердикт по цели Jira (свод 2 GO ≤ 60 мс/шаг):** по цифре **выполнен** (39.7; combo 32.6), с оговоркой выше.
aep added 1 commit 2026-10-29 11:08:07 +03:00
aep added 1 commit 2026-10-29 11:18:50 +03:00
MRT при виртуализации колонок рисует каждую строку заголовка по индексам
листовых колонок, из-за чего двухуровневые группы разъезжались. Штатный thead
отключён (enableTableHead: false), тот же MRT_TableHead без columnVirtualizer
вставляется порталом в <table>: все заголовки сразу, стили и встроенные фильтры
MRT сохраняются. Тело по-прежнему виртуализировано по строкам и колонкам.

Стили: order -1 для портального thead, flexShrink 0 у td/th (распорки
виртуализации сжимались и сдвигали тело на 2-8 px), скрыта подложка ::before
закреплённых ячеек (перекрывала чередование фона).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Author
Owner

Разбор ночной находки: свод 2 без числовых колонок — исправлено

Причины (проверено в браузере и по коду MRT 3.2.1):

  1. При виртуализации MRT ставит layoutMode: 'grid', ячейки растягивались flex-grow (×3,7).
  2. Без enableColumnPinning MRT не учитывает закреплённые колонки в левом отступе виртуализатора — колонки уезжали за левый край (ночная картинка).
  3. Двухуровневые заголовки MRT при виртуализации рисует по индексу листовой колонки — группы не над своими колонками / пропадают.

Исправления:

  • b834f10 — grid-no-grow + enableColumnPinning (как у листов форм).
  • 4cb0862 — заголовок MRT без виртуализации порталом в <table> (enableTableHead: false + MRT_TableHead), flexShrink: 0 у ячеек (распорки сжимались, сдвиг тела до 7,9 px), скрыта подложка ::before закреплённых ячеек.

Проверка: границы 26 групп и 111 колонок совпадают (0 px) на ССП и РФ при scrollLeft 0/3000/8000/max; скриншоты против test совпадают (ССП и РФ), фильтры работают так же. Скриншоты: scratchpad/fix1842/.
Rulings исполнителя: R-1 портал штатного MRT_TableHead; R-2 flexShrink 0 на td/th; R-3 скрыт ::before у закреплённых; R-4 тест конфигурации стилей написан после правки; R-5 портал живёт, пока показана таблица.
Ночные 39.7 мс сняты на сломанной таблице — замер 1842 будет повторён.

## Разбор ночной находки: свод 2 без числовых колонок — исправлено Причины (проверено в браузере и по коду MRT 3.2.1): 1. При виртуализации MRT ставит `layoutMode: 'grid'`, ячейки растягивались flex-grow (×3,7). 2. Без `enableColumnPinning` MRT не учитывает закреплённые колонки в левом отступе виртуализатора — колонки уезжали за левый край (ночная картинка). 3. Двухуровневые заголовки MRT при виртуализации рисует по индексу листовой колонки — группы не над своими колонками / пропадают. Исправления: - b834f10 — `grid-no-grow` + `enableColumnPinning` (как у листов форм). - 4cb0862 — заголовок MRT без виртуализации порталом в `<table>` (`enableTableHead: false` + `MRT_TableHead`), `flexShrink: 0` у ячеек (распорки сжимались, сдвиг тела до 7,9 px), скрыта подложка `::before` закреплённых ячеек. Проверка: границы 26 групп и 111 колонок совпадают (0 px) на ССП и РФ при scrollLeft 0/3000/8000/max; скриншоты против test совпадают (ССП и РФ), фильтры работают так же. Скриншоты: `scratchpad/fix1842/`. Rulings исполнителя: R-1 портал штатного MRT_TableHead; R-2 flexShrink 0 на td/th; R-3 скрыт ::before у закреплённых; R-4 тест конфигурации стилей написан после правки; R-5 портал живёт, пока показана таблица. Ночные 39.7 мс сняты на сломанной таблице — замер 1842 будет повторён.
PotapovaA approved these changes 2026-10-30 14:01:46 +03:00
This pull request can be merged automatically.
You are not authorized to merge this pull request.

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin feat/220-aurora-1842-svod2-column-virtual:feat/220-aurora-1842-svod2-column-virtual
git checkout feat/220-aurora-1842-svod2-column-virtual
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: RSHB/DFiP_Budget_planing#227
No description provided.