Решения 28.10: пороги производительности — цель, недобор в follow-up; машина замеров Apple M4, пороги абсолютные; замеры выполняет контроллер после ночного прогона; пустая полоса при перетаскивании по умолчанию принимается как штатное поведение MRT. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
22 KiB
Purpose
Таблица листа формы (web/src/components/RealtimeTable). Здесь описано, как быстро она прокручивается, как остаются корректными закреплённые колонки, подсветки и состояние ячеек при мемоизации, и какой контракт обязана соблюдать любая правка таблицы.
ADDED Requirements
Requirement: Шаг прокрутки листа укладывается в 25 мс главного потока
Прокрутка таблицы листа SHALL стоить не больше 25 мс главного потока на один шаг колеса 200 px на листе FORM_2/AHR.
Условия замера:
- стенд профилирования
perf/на данных масштаба 1 (results/manifest-s1.json, лист доgrow, 1154 строки); - роль EXECUTOR_RF, prod-сборка, CPU 1×, без троттлинга;
- метрика — медиана
main_thread_ms_per_stepпо трём повторам сценарияperf/e2e/scenarios/scroll.spec.tsпо осиy.
Горизонтальная прокрутка (ось x) того же листа MUST быть не медленнее базы. База — ветка AURORA-1837, на которой стоит эта задача.
Замер — на той же машине Apple M4, на которой снят отчёт профилирования, пороги абсолютные.
Пороги этого требования и остальных требований с замерами — цель, а не условие слияния. Цифры до и после SHALL быть приведены в PR. Если цель не достигнута, SHALL быть заведена follow-up issue с разбором того, что съедает остаток. Слияние блокируют только требования корректности.
Scenario: Недобор до цели оформляется follow-up
- WHEN замер после изменения показывает медиану шага больше 25 мс или регрессию относительно базы
- THEN PR вливается по зелёным тестам и ручному регрессу, цифры приведены в PR
- AND заведена follow-up issue с разбором, какие работы главного потока занимают остаток
Scenario: Вертикальная прокрутка FORM_2/AHR после изменения
- WHEN на стенде прогоняется
PERF_TARGETS='FORM_2/AHR/*' PERF_ROLES=rf PERF_AXES=y PERF_TRACE=0 PERF_REPEATS=3 npx playwright test scenarios/scroll.spec.tsна сборке этой ветки - THEN медиана
main_thread_ms_per_stepпо трём повторам не больше 25 мс
Scenario: Горизонтальная прокрутка не хуже базы
- WHEN тот же сценарий прогоняется с
PERF_AXES=xна сборке ветки AURORA-1837 и на сборке этой ветки на тех же данных - THEN медиана
main_thread_ms_per_stepна этой ветке не больше, чем на ветке AURORA-1837 - AND обе цифры приведены в описании PR
Requirement: Прокрутка не перерисовывает ячейки, оставшиеся на экране
Ячейка таблицы листа, которая при прокрутке осталась в видимой области и у которой не изменились данные строки и определение колонки, SHALL NOT перерисовываться. Перерисовываются только ячейки, которые появились на экране. Строки таблицы и служебные колонки (раскрытие строк) перерисовываться могут.
Числовая цель по счётчику рендеров не блокирует слияние (см. требование «Шаг прокрутки…»). Неизменность вида ячеек при прокрутке — требование корректности.
Scenario: Счётчик рендеров ячеек на шаг падает
- WHEN на профилирующей сборке (
:8081) прогоняетсяPERF_HOT_MODE=reactperf/e2e/scenarios/hotspots.spec.tsдля FORM_2/AHR по осиy - THEN число рендеров
ForwardRef(Styled(td))на шаг кратно меньше 1 696, порядка числа ячеек, появившихся на экране за шаг - AND цифры до и после приведены в описании PR
Scenario: Вид ячеек при прокрутке не меняется
- WHEN пользователь прокручивает лист вверх, вниз и вбок и возвращается к исходной позиции
- THEN каждая видимая ячейка показывает то же значение, цвет, подсветку и признаки, что и до прокрутки
Requirement: Закреплённые колонки остаются на своих местах при изменении раскладки
После любого изменения раскладки закреплённых колонок таблица листа SHALL показывать закреплённые колонки примыкающими друг к другу, без наложения и без зазоров. Видимые ячейки закреплённых колонок SHALL оставаться на месте при горизонтальной прокрутке.
Изменения раскладки: закрепление колонки, открепление, скрытие закреплённой колонки, показ скрытой закреплённой колонки, смена ширины закреплённой колонки.
Пока пользователь тянет ширину закреплённой колонки, соседние закреплённые колонки MAY отставать от неё. После отпускания кнопки мыши раскладка MUST стать корректной без дополнительных действий пользователя.
Scenario: Закрепление колонки
- WHEN пользователь выделяет колонку в строке букв и нажимает «Закрепить область»
- THEN колонка встаёт сразу после ранее закреплённых и остаётся на месте при горизонтальной прокрутке
- AND её ячейки во всех видимых строках, включая уже отрисованные до закрепления, стоят на месте закреплённой колонки
Scenario: Открепление колонки
- WHEN пользователь открепляет закреплённую колонку
- THEN колонка возвращается в прокручиваемую область, а оставшиеся закреплённые колонки сдвигаются и примыкают друг к другу
Scenario: Скрытие и показ закреплённой колонки
- WHEN пользователь скрывает закреплённую колонку настройками видимости, а потом снова показывает её
- THEN в обоих состояниях закреплённые колонки примыкают друг к другу, без пустого места на месте скрытой и без наложения после показа
Scenario: Смена ширины закреплённой колонки
- WHEN пользователь тянет правую границу заголовка закреплённой колонки и отпускает кнопку мыши
- THEN закреплённые колонки правее неё сдвигаются на новую ширину и примыкают к ней без наложения
- AND до отпускания кнопки мыши допускается, что соседние закреплённые колонки ещё стоят по старой ширине
Scenario: Смена ширины и видимости незакреплённой колонки
- WHEN пользователь тянет ширину незакреплённой колонки или скрывает и показывает незакреплённую колонку
- THEN ширина и набор колонок меняются сразу, заголовок и тело таблицы остаются выровнены по колонкам
- AND закреплённые колонки не сдвигаются
Requirement: Подсветка поиска и фильтров соответствует текущему запросу
Подсветка совпадений в ячейках таблицы листа SHALL соответствовать текущему значению глобального поиска и фильтров по колонкам. Подсветка — это выделение текста <mark> и обводка текущего совпадения. При изменении или сбросе запроса подсветка MUST обновляться во всех видимых ячейках, включая уже отрисованные.
Scenario: Ввод и изменение поискового запроса
- WHEN пользователь вводит запрос в поле поиска, а затем меняет его
- THEN после применения запроса во всех видимых ячейках подсвечено совпадение именно с текущим запросом, подсветки прежнего запроса нет
Scenario: Сброс поиска
- WHEN пользователь очищает поле поиска
- THEN ни в одной видимой ячейке нет подсветки
<mark>и обводки текущего совпадения
Scenario: Переход к совпадению
- WHEN пользователь нажимает Enter или кнопки «следующий/предыдущий результат»
- THEN таблица прокручивается к совпадению, текущая ячейка обведена, обводка предыдущей снята
Scenario: Фильтр по колонке и его сброс
- WHEN пользователь задаёт значение фильтра по колонке, меняет его, а затем очищает
- THEN строки отфильтрованы по текущему значению, подсветка в ячейках этой колонки соответствует текущему значению, после очистки подсветки нет
Requirement: Состояние вне данных строки отображается в ячейках как раньше
Таблица листа SHALL отображать в ячейках состояние, которого нет в данных строки, так же, как до введения мемоизации:
- блокировки соавторов;
- выделение строки;
- ошибки валидации;
- разрядность (рубли, тысячи, миллионы);
- редактируемость по этапу и роли;
- открытый редактор ячейки.
Изменение этого состояния MUST отражаться в видимых ячейках без прокрутки и без перезагрузки страницы.
Scenario: Блокировка соавтора
- WHEN второй пользователь в другом браузере открывает ячейку на редактирование, а затем закрывает её
- THEN у первого пользователя ячейка показывает признак блокировки, пока её редактирует второй, и снимает его после закрытия, в том числе если ячейка была на экране всё это время
Scenario: Выделение строки
- WHEN пользователь кликает по строке или по номеру строки, а потом кликает ещё раз
- THEN строка затемняется при выделении и возвращается к обычному виду при снятии выделения
Scenario: Ошибка валидации
- WHEN пользователь вводит значение, нарушающее правило валидации, а затем исправляет его
- THEN ячейка подсвечивается как ошибочная и появляется плашка «Контроль ошибок», после исправления подсветка снимается
Scenario: Разрядность
- WHEN пользователь меняет разрядность в панели настроек
- THEN все видимые числовые ячейки сразу показывают значения в новой разрядности
Scenario: Редактирование ячейки
- WHEN пользователь открывает редактор двойным кликом (в колонке ВСП — выпадающий список) и сохраняет значение Enter или кликом вне ячейки
- THEN редактор открывается в этой ячейке, после сохранения ячейка показывает новое значение, соавтор видит его
Scenario: Добавление и удаление строки
- WHEN пользователь добавляет строку и удаляет строку
- THEN таблица показывает актуальный набор строк, значения и признаки ячеек соседних строк не перепутаны
Requirement: Контракт для правок таблицы листа: рендер ячейки зависит только от ячейки
Любая правка таблицы листа SHALL соблюдать контракт мемоизации ячеек. Контракт распространяется на AURORA-1889, 1890, 1891, 1893, 1910 и последующие задачи.
-
Содержимое и оформление ячейки MUST вычисляться только из объекта ячейки, то есть из данных строки и определения колонки.
-
Состояние вне данных строки MUST доходить до ячейки одним из трёх способов:
- CSS по атрибутам контейнера, строки или ячейки;
- React-контекст;
- внешнее хранилище с подпиской (
useSyncExternalStore).
Его MUST NOT замыкать в свойствах ячейки из конфигурации таблицы (
muiTableBodyCellProps,col.Cell,col.Edit). -
Обработчики событий ячейки, например будущие
onClick/onKeyDownиз AURORA-1891, MUST читать изменяемое состояние в момент события черезrefилиtable.getState(), а не из замыкания. -
Состояние, которое меняет определения колонок, MUST явно пересоздавать набор колонок таблицы и тем сбрасывать мемоизацию. Это редактируемость по этапу и роли, разрядность, правила валидации, раскладка закреплённых колонок, поиск и фильтры.
-
Прокрутка MUST NOT вызывать рендер компонента, который владеет экземпляром таблицы (
RealtimeTable).
Scenario: Новое оформление ячейки по состоянию вне данных
- WHEN правка добавляет оформление ячейки, зависящее от состояния вне данных строки (например, текущая ячейка навигации или ячейка, к которой перешли из плашки ошибок)
- THEN оформление задаётся CSS-правилом по атрибутам (
data-row-id,data-col-idи т.п.), контекстом или хранилищем с подпиской - AND уже отрисованные ячейки показывают его без прокрутки
Scenario: Обработчик видит актуальное состояние
- WHEN состояние таблицы (например, редактируемая ячейка) изменилось после последнего рендера ячейки, и пользователь кликает или нажимает клавишу в этой ячейке
- THEN обработчик принимает решение по текущему состоянию, а не по состоянию на момент рендера ячейки
Scenario: Прокрутка не рендерит владельца таблицы
- WHEN пользователь прокручивает таблицу листа
- THEN в профиле React за время прокрутки нет рендеров
RealtimeTable, есть только рендеры тела таблицы и появившихся ячеек
Requirement: Явный сброс мемоизации проверяется автотестом
Правила явного сброса мемоизации ячеек SHALL быть заданы чистой функцией (ключ сброса) и покрыты node-тестом в стандартной команде cd web && node --test src/components/RealtimeTable/utils/*.test.js.
Ключ MUST меняться:
- при закреплении и откреплении колонки;
- при скрытии и показе закреплённой колонки;
- при смене ширины закреплённой колонки (по отпусканию мыши);
- при изменении глобального поиска;
- при изменении значения фильтра по колонке.
Ключ MUST NOT меняться:
- при смене ширины незакреплённой колонки;
- при скрытии и показе незакреплённой колонки;
- при изменении данных;
- пока ширину колонки тянут.
Тест MUST также фиксировать допущение @tanstack/table-core: новый массив колонок даёт новые объекты ячеек, а смена закрепления при прежнем массиве колонок — нет.
Scenario: Ключ реагирует на раскладку закреплённых колонок и фильтры
- WHEN запускается
node --testдля модуля ключа сброса - THEN тесты подтверждают каждое условие «меняется» и «не меняется» из этого требования
Scenario: Допущение о пересоздании ячеек
- WHEN запускается
node --testдля теста допущения на@tanstack/table-core - THEN тест подтверждает: при прежних
columnsиdataобъектыrow.getVisibleCells()[i]те же; при новом массивеcolumns(даже с теми же определениями) — новые; при сменеcolumnPinningс прежним массивомcolumns— те же
Requirement: Перетаскивание ширины закреплённой колонки не медленнее базы
Перетаскивание ширины закреплённой колонки на листе FORM_2/AHR SHALL стоить не больше главного потока на шаг движения мыши, чем на ветке AURORA-1837. Медиана main_thread_ms_per_step по трём повторам сценария perf/e2e/scenarios/pinned-resize.spec.ts на тех же данных и сборке того же типа. Цифры до и после, а также p95 интервала кадра MUST быть приведены в описании PR.
Порог — цель, а не условие слияния; недобор оформляется follow-up issue (см. требование «Шаг прокрутки…»). Корректная раскладка закреплённых колонок после отпускания мыши — требование корректности.
Scenario: Замер перетаскивания ширины закреплённой колонки
- WHEN сценарий закрепляет на FORM_2/AHR (роль rf, CPU 1×) первую незакреплённую колонку данных и тянет её правую границу шагами по 4 px вправо и обратно, с ожиданием кадра после каждого шага
- THEN медиана
main_thread_ms_per_stepна этой ветке не больше, чем на ветке AURORA-1837 - AND после отпускания мыши закреплённые колонки примыкают друг к другу (проверка геометрии в сценарии проходит)