Skip to content

feat(PanelHeader): use position sticky#9252

Merged
inomdzhon merged 4 commits intomasterfrom
imirdzhamolov/fix/PanelHeader-fixed
Dec 8, 2025
Merged

feat(PanelHeader): use position sticky#9252
inomdzhon merged 4 commits intomasterfrom
imirdzhamolov/fix/PanelHeader-fixed

Conversation

@inomdzhon
Copy link
Contributor

@inomdzhon inomdzhon commented Dec 2, 2025


  • [ ] Unit-тесты
  • e2e-тесты
  • [ ] Дизайн-ревью
  • [ ] Документация фичи
  • Release notes

Описание

PanelHeader

  1. Заменил FixedLayout на position: sticky прямо в компоненте.
  2. Перенёс z-index перманентно в корень, т.к. position: sticky теперь применяется на .host. Это исправляет также перекрытие PanelHeaderContext при <PanelHeader fixed={false} />.
  3. Заменил компонент Spacing на delimiter="spacing" на отступ в CSS, т.к. из-за него высота PanelHeader становится больше, появляется невидимая область и из-за п.2 это теперь блокирует взаимодействие с другими элементами в ситуации, когда частично зализает на них (<Spacing /> добавили в v5 #3135).
  4. Сделал Separator плавающим элементом, чтобы он не занимал лишние 0.3-0.5px в потоке.
  5. Удалил в стилях FixedLayout не существующий класс .vkuiInternalPanelHeader__fixed.

Note

Помимо скриншотов PanelHeader, задело:

  • Epic – из-за п.4
  • Panel – из-за п.4
  • PanelHeaderContext – из-за п.4
  • FixedLayout – из-за п.1 и п.4

Обновил их.

SplitCol

Для <SplitCol fixed /> также заменяем position: fixed на position: sticky.

Также задаем минимальную высоту через dvh (с фолбеком на 100vh с вычетом safe-area) и делаем его Flexbox'ом, чтобы потомки могли растягиваться на всю высоту через flex: 1 вместо block-size: 100%.

SplitLayout

Чтобы решить проблему когда стики шапка при <SplitLayout header={<PanelHeader delimiter="none" />} /> остается в рамках 100vh (см. #6609 (comment)), избавляемся от height: 100% у <html> и <body>. Вместо этого используем магию с Flexbox.

  • на <html> display: flex + min-height: 100%, чтобы страница растягивалась с учётом контента, а не области видимости;
  • для <body> удаляем block-size: 100% и задаём display: flex, чтобы высота зависела от содержимого, а потомки продолжали растягиваться через block-size: 100%.

height: 100% у .vkui__root, AppRoot, SplitLayout и так далее по каскаду – не трогаем, их логика остаётся прежней. Элементы должны продолжать наследовать height: 100%.

Ориентировался на статью https://stackoverflow.com/questions/30962863/holy-grail-layout-with-flex-and-always-visible-header.

Добавил скриншотных тестов для проверки стики элементов. Заодно удалил dark старых скриншотов, т.к. она не нужна в этих проверках.

Помимо этого, решил перенести удаление свойств popout и modal на VKUI v9. А для getRef добавил эту пометку.

Ожидаемый эффект

Теперь высота всей страницы зависит от контента. На это можно повлиять через указание какому-либо элементу высоту через единицы vh или dvh.

В частности, из-за этого для скриншотов FixedLayout делаем теперь expectScreenshotClippedToContent({ fullPage: false }), т.к. внутри функции getBoundingClientRect() отдаёт абсолютную высоту элемента.

Что может сломаться?

  • Получение области видимости через document.body.clientHeight или его потомков с height: 100%. Необходимо заменить либо на document.documentElement.clientHeight, либо на window.innerHeight.

    - document.body.clientHeight
    + document.documentElement.clientHeight
    // или
    + window.innerHeight
  • Применение position: absolute на элементе на уровне <body>. Необходимо
    заменить на position: fixed.

Окружение

  • Storybook: потребовалось перебить стиль display: block, который они навешивают на <body>, потому что нам нужен display: flex.
  • Playwright:
    • добавил новых параметров для AppDefaultWrapper и screenshotWithClipToContent, чтобы была возможность скриншотить только область видимости.
    • index.ts подключил import '../src/styles/layout.css';, чтобы собирались новые CSS утилиты для раскладки.

Ссылки


Release notes

BREAKING CHANGE

  • PanelHeader: изменена реализация fixed, который закрепляет шапку в области видимости при скролле – вместо position: fixed используется position: sticky. Это потребовало изменение раскладки всей страницы – height: 100% на <html> и <body> удалён в пользу display: flex и теперь высота страницы зависит от содержимого.
    • Если вы получали высоту области видимости через document.body.clientHeight или на элементе с height: 100% по каскаду ниже, то замените такой код либо на VIsualViewport, либо на document.documentElement.clientHeight, либо на window.innerHeight.
    • Если вам нужно, чтобы какой-то из элементов растягивался на высоты области видимости, то используйте единицы измерения vh или dvh вместо % (height: 100%height: 100dvh)
  • SplitCol:
    • Изменена реализация fixed, который закрепляет колонку в области видимости при скролле – вместо position: fixed используется position: sticky.
    • Исправлена проблема когда переполненный контент при fixed обрезался – теперь высота SplitCol зависит от его содержимого. Чтобы растянуть потомок на всю колонку, используйте flex-grow: 1 на этом потомке.

h2. `PanelHeader`

1. Заменил `FixedLayout` на `position: sticky` прямо в компоненте.
2. Перенёс `z-index` перманентно в корень, т.к. `position: sticky`
  теперь применяется на `.host`. Это исправляет также  перекрытие
  `PanelHeaderContext` при `<PanelHeader fixed={false} />`.
3. Заменил компонент `Spacing` на `delimiter="spacing"` на отступ в CSS,
  т.к. из-за него высота `PanelHeader` становится больше, появляется
  невидимая область и из-за п.2 это теперь блокирует взаимодействие
  с другими элементами в ситуации, когда частично зализает на них
  (`<Spacing />` добавили в #3135).
4. Сделал `Separator` плавающим элементом, чтобы он не занимал лишние
  `0.3-0.5px` в потоке.
5. Удалил в стилях `FixedLayout` не существующий класс `.vkuiInternalPanelHeader__fixed`.

> [!NOTE]
>
> Помимо скриншотов `PanelHeader`, задело:
>
> - `Epic` – из-за п.4
> - `Panel` – из-за п.4
> - `PanelHeaderContext` – из-за п.4
> - `FixedLayout` – из-за п.1 и п.4
>
> Обновил их.

h2. `SplitCol`

Для `<SplitCol fixed />` также заменяем `position: fixed` на `position: sticky`.

Также добавляем растягивание на `100vh` с вычетом `safe-area`
и с Graceful Degradation на [dvh](https://caniuse.com/?search=dvh).

h2. `SplitLayout`

Чтобы решить проблему когда стики шапка при `<SplitLayout header={<PanelHeader delimiter="none" />} />`
остается в рамках `100vh` (см. #6609 (comment)),
избавляемся от `height: 100%` у `<html>` и `<body>`.
Вместо этого используем магию с Flexbox.

- на `<html>` `display: flex` + `min-height: 100%`, чтобы страница
  растягивалась с учётом контента, а не области видимости;
- для `<body>` удаляем `block-size: 100%` и задаём `display: flex`,
  чтобы растягивался также потомки растягивались на всю высоту.

`height: 100%` у `.vkui__root`, `AppRoot`, `SplitLayout` и так далее
по каскаду – не трогаем, их логика остаётся прежней. Элементы должны
продолжать наследовать `height: 100%`.

Ориентировался на статью https://stackoverflow.com/questions/30962863/holy-grail-layout-with-flex-and-always-visible-header.

Добавил скриншотных тестов для проверки стики элементов. Заодно удалил
`dark` старых скриншотов, т.к. она не нужна в этих проверках.

Помимо этого, решил перенести удаление свойств `popout` и `modal` на VKUI v9. А для `getRef` добавил эту пометку.

h3. Ожидаемый эффект

Теперь высота всей страницы зависит от контента. На это можно повлиять
через указание какому-либо элементу высоту через единицы `vh` или `dvh`.

В частности, из-за этого для скриншотов `FixedLayout` делаем теперь
`expectScreenshotClippedToContent({ fullPage: false })`, т.к. внутри
функции `getBoundingClientRect()` отдаёт абсолютную высоту элемента.

h3. Что может сломаться?

- Получение области видимости через `document.body.clientHeight` или его
  потомков с `height: 100%`. Необходимо заменить либо на
  `document.documentElement.clientHeight`, либо на `window.innerHeight`.

  ```diff
  - document.body.clientHeight
  + document.documentElement.clientHeight
  // или
  + window.innerHeight
  ```

- Применение `position: absolute` на элементе на уровне `<body>`. Необходимо
  заменить на `position: fixed`.

h2. Окружение

- **Storybook**: потребовалось перебить стиль `display: block`, который
  они навешивают на `<body>`, потому что нам нужен `display: flex`.
- **Playwright**:
  - добавил новых параметров для `AppDefaultWrapper` и
    `screenshotWithClipToContent`, чтобы была возможность скриншотить
    только область видимости.
  - `index.ts` подключил `import '../src/styles/layout.css';`, чтобы
    собирались новые CSS утилиты для раскладки.
@inomdzhon inomdzhon requested a review from a team as a code owner December 2, 2025 16:08
@github-actions
Copy link
Contributor

github-actions bot commented Dec 2, 2025

size-limit report 📦

Path Size
JS 426.07 KB (-0.08% 🔽)
JS (gzip) 129.09 KB (-0.02% 🔽)
JS (brotli) 105.47 KB (-0.08% 🔽)
JS import Div (tree shaking) 1.67 KB (0%)
CSS 371.2 KB (+0.09% 🔺)
CSS (gzip) 45.97 KB (+0.12% 🔺)
CSS (brotli) 36.43 KB (+0.11% 🔺)

@github-actions
Copy link
Contributor

github-actions bot commented Dec 2, 2025

📊 Найдены изменения в собранных файлах: Отчет

Commit 0742894

@github-actions
Copy link
Contributor

github-actions bot commented Dec 2, 2025

e2e tests

⚠️ Some screenshots were failed. See Playwright Report.

Playwright Report

@github-actions
Copy link
Contributor

github-actions bot commented Dec 2, 2025

👀 Docs deployed

📦 Package ✅

yarn add @vkontakte/vkui@https://development.s3.prodcloud.vk.team/pull/9252/0742894f5c38c15581e01702d156f2e6a7b6912c/pkg/@vkontakte/vkui/_pkg.tgz

Commit 0742894

@codecov
Copy link

codecov bot commented Dec 2, 2025

Codecov Report

❌ Patch coverage is 50.00000% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 95.02%. Comparing base (abdd35c) to head (0742894).
⚠️ Report is 43 commits behind head on master.

Files with missing lines Patch % Lines
...es/vkui/src/components/SplitLayout/SplitLayout.tsx 12.50% 7 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #9252      +/-   ##
==========================================
- Coverage   95.04%   95.02%   -0.03%     
==========================================
  Files         420      418       -2     
  Lines       11222    11191      -31     
  Branches     4227     4209      -18     
==========================================
- Hits        10666    10634      -32     
- Misses        556      557       +1     
Flag Coverage Δ
unittests 95.02% <50.00%> (-0.03%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

@EldarMuhamethanov EldarMuhamethanov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔥🔥🔥

@inomdzhon inomdzhon merged commit 5a0f0fc into master Dec 8, 2025
48 of 52 checks passed
@inomdzhon inomdzhon deleted the imirdzhamolov/fix/PanelHeader-fixed branch December 8, 2025 16:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment