refactor(header): unified menu navigation for all devices #11
Loading…
Reference in a new issue
No description provided.
Delete branch "refactor/header-unified-menu"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Что сделано
Реализован унифицированный header для issue #8 — меню ☰ одинаковое на всех устройствах.
Изменённые файлы
src/components/site-header.tsx— переписан: flex[бренд] ... [язык] [☰]вместо grid-cols-3 с условным swap. Бренд всегда слева (чинит «прыжок» шапки). Меню ☰ на чистом React (useState/useRef/useEffect, БЕЗ radix) с a11y.src/app/[locale]/[slug]/page.tsx— добавлена стрелка «← Утилиты» над<h1>(переехала из шапки). Paddingpt-10→pt-4(стрелка даёт отступ).src/lib/i18n.ts— ключиheader.menu(aria-label ☰),header.subscriptions(пункт меню) для ru/en.Новые файлы
src/__tests__/site-header.test.tsx— 9 тестов (рендер brand, open/close по Esc/click-outside, пункты меню ru/en, отсутствие backToUtilities в шапке).src/__tests__/slug-back-link.test.tsx— 2 теста (стрелка «← Утилиты» над h1 в ru/en).Меню ☰
aria-label,aria-expanded,aria-controls="nav-menu"./[loc]/apps, «Подписки» →/[loc]/subscriptions(масштабируется: элемент + i18n ключ).Почему
Шапка «прыгала» при переходе на карточку (лого слева → центр), стрелка «← Утилиты» ложно показывалась на /contacts и /subscriptions, навигация не масштабировалась (подписки, будущие статьи — тесно на мобильных без бургер-меню). Унифицированное меню решает все три проблемы.
Watch out
biome useExhaustiveDependencies: эффект закрытия меню по pathname требует ignore (dep — только pathname, но reset state при навигации).biome-ignoreна useEffect с пояснением. Альтернатива (deps массив с pathname + setOpen) ломает lint иначе."use client") — layout серверный, но header рендерится в него как клиентский island. SSR/hydration OK (статика не ломается,/[slug]остался SSG с ISR 1h)./contactsи/subscriptions: шапка унифицирована — стрелки на них НЕТ. Контент страниц не менялся.[slug]/page.tsx:next/linkсоscroll={false}(симметрично с/appsкарточками, ADR-008 scroll restoration).Pending
.github/workflows/не триггерится на Forgejo pull_request). Мерж по review APPROVE + локальным проверкам.Closes #8
Code Review Summary
Рефакторинг шапки выполнен чисто:
isAppPageполностью удалён, шапка теперь идентична на всех страницах ([бренд] ... [язык] [☰]), ☰-меню реализовано с полным a11y-контрактом. Все контракты issue #8 выполнены, тесты покрывают ключевые сценарии.Positives
type="button",aria-label,aria-expanded,aria-controls="nav-menu"; панель имеет совпадающийid="nav-menu"; фокус-менеджмент работает — фокус на первый пункт при открытии (site-header.tsx:21-25), возврат на кнопку при Esc (site-header.tsx:32)site-header.tsx:27-37), click-outside (site-header.tsx:39-48), смена pathname (site-header.tsx:51-53) — все с корректным cleanup в useEffectusePathnameиспользуется правильно — только для закрытия меню при навигации, НЕ для условного рендера brand. Статика не ломается (client hook, не dynamic API)[slug]/page.tsx:82-88, отсутствует в contacts/apps/subscriptions и в шапке. Тестsite-header.test.tsx:70-73явно проверяет отсутствиеisAppPageне осталось ни в одном файле (grep подтверждает), все импорты используютсяmenuиsubscriptionsдобавлены в тип Dict + ruDict + enDict,backToUtilitiesсохранён (перемещён consumer из шапки в [slug])anySuggestions (info, not blocking)
buttonRef.current?.focus()вonMouseDownhandler послеsetOpen(false)biome-ignoreдляuseExhaustiveDependenciesоправдан (нужен толькоpathname,setOpenстабилен), комментарий корректенCI не запустится на PR #11 — известная проблема issue #10 (ci.yml не триггерится), не связана с этим PR.
Verdict: APPROVE