Files
enduro-trails/docs/work-items/ET-014/02-trz.md
claude-bot e796a6cb03
All checks were successful
CI / lint (push) Successful in 5s
CI / test (push) Successful in 10s
CI / build (push) Successful in 2s
analyst(ET): auto-commit from analyst run_id=87
2026-06-04 11:03:45 +00:00

8.6 KiB
Raw Blame History

ТРЗ — ET-014: Z-index конфликт terrain-popup vs sheet-gps-filters

Work Item: ET-014 Связан с BRD: 01-brd.md Тип задачи: Bug-fix (UI / стили / DOM-stacking)


1. Анализ текущего состояния

1.1 DOM-структура (как есть)

  • #terrain-popup (src/web/index.html:43) — position: fixed, z-index: 500 (src/web/app.css:785-795). Открывается по клику на кнопку «Рельеф» (#terrain-toggle в #map-controls-r). Содержит чекбоксы слоёв, переключатели подложки и единиц, а также кнопку-ссылку #public-tracks-filters-btn с текстом «Фильтры…».
  • #sheet-gps-filters (src/web/index.html:478) — класс .bottom-sheet, position: fixed, z-index: 400 (src/web/app.css:183-196). Открывается через togglePublicTracksFiltersSheet() в src/web/gps_tracks.js:737, который вызывает openSheet('sheet-gps-filters').
  • #sheet-backdrop (src/web/index.html:19) — z-index: 390 (src/web/app.css:222-228). На mobile перекрывает экран при открытом sheet'е; на desktop скрыт (#sheet-backdrop { display: none; } в media-query, src/web/app.css:543).

1.2 Стек z-index в проекте (для ориентира)

Элемент z-index Файл/строка
#map 0 app.css:68
#no-data-warning 200 app.css:410
#sheet-backdrop 390 app.css:225
.bottom-sheet 400 app.css:188
#map-controls-r 400 app.css:129
.terrain-popup 500 app.css:787
#marker-dialog 500 app.css:399
#search-panel 600 app.css:1101
#ruler-info 600 app.css:1122

1.3 Корень проблемы

  1. togglePublicTracksFiltersSheet() открывает sheet (z=400), но не закрывает #terrain-popup (z=500). Popup остаётся на экране и визуально/event-but перекрывает sheet.
  2. Клик по ссылке «Фильтры…» внутри popup не триггерит closeTerrainOnOutside (popup.contains(target) === true), поэтому popup не закрывается сам.
  3. Backdrop sheet'а (z=390) тоже ниже popup'а (z=500), поэтому даже на mobile нет визуальной индикации, что popup стал «фоном».

2. Требования к решению

2.1 Функциональные (REQ-F)

ID Требование
REQ-F-01 При открытии #sheet-gps-filters из «Фильтры…» панель #terrain-popup НЕ должна перекрывать sheet ни визуально, ни для событий ввода.
REQ-F-02 Когда #sheet-gps-filters открыт, состояние кнопки #terrain-toggle (класс .active) должно быть консистентно с состоянием popup: если popup скрывается / закрывается на время открытия фильтров — кнопка не должна оставаться визуально «прижатой».
REQ-F-03 После закрытия #sheet-gps-filters (через , свайп вниз, клик по backdrop на mobile, либо closeAllSheets()) пользователь возвращается к карте. Возврат панели слоёв — на усмотрение архитектора (см. §3 «Варианты решения»). В любом случае не должно оставаться «фантомных» оверлеев / неактивных DOM в видимой области.
REQ-F-04 Решение должно работать единообразно при инициации фильтров повторно (открыли → закрыли → открыли снова).
REQ-F-05 Поведение #terrain-popup для всех других сценариев (открыть/закрыть кнопкой, кликнуть вне popup'а, переключить чекбокс/подложку/единицы) не должно регрессировать.
REQ-F-06 Поведение остальных bottom-sheets (#sheet-route, #sheet-recon, #sheet-scenic, #sheet-link, #sheet-gpx) не должно регрессировать.
REQ-F-07 Решение должно одинаково корректно работать в светлой и тёмной теме.

2.2 Нефункциональные (REQ-NF)

ID Требование
REQ-NF-01 Изменения локализованы во фронте (src/web/). Backend (src/api/) не затрагивается.
REQ-NF-02 Нет регрессий по производительности (никаких новых тяжёлых обработчиков resize/scroll).
REQ-NF-03 Если решение меняет z-index — оно не должно ломать стекинг #marker-dialog (z=500), #search-panel (z=600), #ruler-info (z=600).
REQ-NF-04 Решение совместимо с PWA-режимом (PH-9, в работе): в standalone display и при наличии safe-area-inset.
REQ-NF-05 Решение работает на mobile viewport 360414 px (Chrome Android), desktop ≥1024 px (Chrome desktop).

3. Варианты решения (на усмотрение архитектора)

Аналитик не выбирает архитектуру. Перечисляю опции, которые могут быть рассмотрены реализатором/архитектором:

  • Вариант A — закрывать #terrain-popup при открытии sheet-gps-filters. В togglePublicTracksFiltersSheet() перед openSheet(...) явно скрыть popup (как делает closeTerrainOnOutside) и снять .active с #terrain-toggle. Backdrop sheet'а корректно затемнит фон на mobile.
  • Вариант B — поднять z-index sheet'ов выше terrain-popup. Например, .bottom-sheet { z-index: 510; } и #sheet-backdrop { z-index: 505; }. Тогда sheet физически окажется поверх popup'а. Требует проверки на не- конфликт с marker-dialog (z=500) и не-перекрытие toolbar / search-panel.
  • Вариант C — точечно поднять z-index только #sheet-gps-filters и его backdrop. Узкий хак: #sheet-gps-filters { z-index: 510; }. Менее системно, но минимальные риски регрессии для других sheet'ов.

Решение фиксируется архитектором в ADR работы (06-adr/).

4. Acceptance hooks

См. полные критерии в 03-acceptance-criteria.md.

Краткая выжимка:

  • Открытие фильтров → панель полностью видна, кликабельна (mobile и desktop).
  • Панель слоёв не перекрывает фильтры (визуально и для событий).
  • Закрытие фильтров → возврат к карте без артефактов.
  • Остальные оверлеи (marker-dialog, search-panel, ruler-info, остальные sheets) — без регрессий.

5. Тесты

См. 04-test-plan.yaml (функциональные тесты) и 04b-ui-test-cases.md (Playwright UI тест-кейсы).

6. Артефакты для модификации (ожидание аналитика)

  • src/web/app.css — стили stacking-context (если выбран вариант B/C).
  • src/web/gps_tracks.js — логика togglePublicTracksFiltersSheet() (если выбран вариант A).
  • Возможно src/web/app.js — если в openSheet / closeAllSheets требуется хук «при открытии sheet закрыть popup» как универсальное решение для будущих кейсов.

Это рекомендация, конкретный набор файлов определит архитектор.