feat(PanelHeader): use position sticky#9252
Merged
Conversation
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 утилиты для раскладки.
Contributor
size-limit report 📦
|
Contributor
Contributor
e2e tests
|
Contributor
👀 Docs deployed
📦 Package ✅yarn add @vkontakte/vkui@https://development.s3.prodcloud.vk.team/pull/9252/0742894f5c38c15581e01702d156f2e6a7b6912c/pkg/@vkontakte/vkui/_pkg.tgzCommit 0742894 |
Codecov Report❌ Patch coverage is
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
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Для реализации #3396
This was referenced Dec 3, 2025
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
FixedLayoutнаposition: sticky#9251[ ] Unit-тесты[ ] Дизайн-ревью[ ] Документация фичиОписание
PanelHeaderFixedLayoutнаposition: stickyпрямо в компоненте.z-indexперманентно в корень, т.к.position: stickyтеперь применяется на.host. Это исправляет также перекрытиеPanelHeaderContextпри<PanelHeader fixed={false} />.Spacingнаdelimiter="spacing"на отступ в CSS, т.к. из-за него высотаPanelHeaderстановится больше, появляется невидимая область и из-за п.2 это теперь блокирует взаимодействие с другими элементами в ситуации, когда частично зализает на них (<Spacing />добавили в v5 #3135).Separatorплавающим элементом, чтобы он не занимал лишние0.3-0.5pxв потоке.FixedLayoutне существующий класс.vkuiInternalPanelHeader__fixed.Note
Помимо скриншотов
PanelHeader, задело:Epic– из-за п.4Panel– из-за п.4PanelHeaderContext– из-за п.4FixedLayout– из-за п.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.Применение
position: absoluteна элементе на уровне<body>. Необходимозаменить на
position: fixed.Окружение
display: block, который они навешивают на<body>, потому что нам нуженdisplay: flex.AppDefaultWrapperиscreenshotWithClipToContent, чтобы была возможность скриншотить только область видимости.index.tsподключилimport '../src/styles/layout.css';, чтобы собирались новые CSS утилиты для раскладки.Ссылки
Release notes
BREAKING CHANGE
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)fixed, который закрепляет колонку в области видимости при скролле – вместоposition: fixedиспользуетсяposition: sticky.fixedобрезался – теперь высотаSplitColзависит от его содержимого. Чтобы растянуть потомок на всю колонку, используйтеflex-grow: 1на этом потомке.