From 9918a9d06b7bc3ad80d89b99261588cb8f53883f Mon Sep 17 00:00:00 2001 From: Inomdzhon Mirdzhamolov Date: Fri, 26 Jan 2024 08:41:51 +0300 Subject: [PATCH] fix(Cell): patch client height of html (see `draggable` mode) (#6456) --- packages/vkui/src/lib/dom.test.ts | 27 +++++++++++++++++++++++---- packages/vkui/src/lib/dom.tsx | 15 +++++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/packages/vkui/src/lib/dom.test.ts b/packages/vkui/src/lib/dom.test.ts index 3d0c06f062..27346c618b 100644 --- a/packages/vkui/src/lib/dom.test.ts +++ b/packages/vkui/src/lib/dom.test.ts @@ -28,7 +28,7 @@ const getChildElOfParentWithTransformedStyle = ( return { parentEl, parentElRect, childEl, childElRect }; }; -describe('getTransformedParentCoords', () => { +describe(getTransformedParentCoords, () => { const transformDefault = TRANSFORM_DEFAULT_VALUES.map((v) => ({ transform: v })); const willChangeDefault = WILL_CHANGE_DEFAULT_VALUES.map((v) => ({ willChange: v })); @@ -68,13 +68,16 @@ describe('getTransformedParentCoords', () => { }); }); -describe('getScrollRect', () => { +describe(getScrollRect, () => { test.each([ { scrollTop: 0, viewportHeight: 100 }, { scrollTop: 10, viewportHeight: 100 }, { scrollTop: 0, viewportHeight: 768 }, { scrollTop: 10, viewportHeight: 768 }, ])('[window] should return correct y edges for %j', ({ scrollTop, viewportHeight }) => { + jest + .spyOn(document.documentElement, 'clientHeight', 'get') + .mockImplementation(() => viewportHeight); const rect = new DOMRect(0, scrollTop > 0 ? -1 * scrollTop : scrollTop, 1280, viewportHeight); window.scrollY = document.documentElement.scrollTop = scrollTop; document.documentElement.getBoundingClientRect = jest.fn(() => rect); @@ -99,7 +102,7 @@ describe('getScrollRect', () => { }); }); -describe('getScrollHeight', () => { +describe(getScrollHeight, () => { const getScrollHeightMock = () => 1000; const scrollEl = document.createElement('div'); beforeEach(() => { @@ -116,7 +119,7 @@ describe('getScrollHeight', () => { }); }); -describe('getBoundingClientRect', () => { +describe(getBoundingClientRect, () => { it('should return rect without offset', () => { const { childEl, childElRect } = getChildElOfParentWithTransformedStyle(); expect(getBoundingClientRect(childEl)).toEqual(childElRect); @@ -144,6 +147,22 @@ describe('getBoundingClientRect', () => { }), ); }); + + it('should apply `clientHeight` to `height` of `getBoundingClientRect()` if element is HTML', () => { + const HTML_CLIENT_HEIGHT = 768; + const HTML_SCROLL_HEIGHT = 2000; + + jest + .spyOn(document.documentElement, 'clientHeight', 'get') + .mockImplementation(() => HTML_CLIENT_HEIGHT); + + // Симулируем, что на странице не указан `html, body { height: 100% }` (или `height: 100vh`). + document.documentElement.getBoundingClientRect = jest.fn( + () => new DOMRect(0, 0, 1280, HTML_SCROLL_HEIGHT), + ); + + expect(getBoundingClientRect(document.documentElement).height).toBe(HTML_CLIENT_HEIGHT); + }); }); describe(getDocumentBody, () => { diff --git a/packages/vkui/src/lib/dom.tsx b/packages/vkui/src/lib/dom.tsx index b0785e35c9..96ff86efcf 100644 --- a/packages/vkui/src/lib/dom.tsx +++ b/packages/vkui/src/lib/dom.tsx @@ -58,6 +58,12 @@ export const isBody = ( return node !== null && node !== undefined && 'tagName' in node && node.tagName === 'BODY'; }; +export const isDocumentElement = ( + node: Element | Window | VisualViewport | undefined | null, +): node is HTMLHtmlElement => { + return node !== null && node !== undefined && 'tagName' in node && node.tagName === 'HTML'; +}; + export function withDOM( Component: React.ComponentType, ): React.ComponentType { @@ -98,6 +104,15 @@ export const getBoundingClientRect = (node: Element | Window, isFixedStrategy = const element = isWindow(node) ? node.document.documentElement : node; const clientRect = element.getBoundingClientRect(); + if (isDocumentElement(element)) { + /** + * Если на странице не используется `html, body { height: 100% }` (или `height: 100vh`), то + * `height`, полученный из `document.documentElement.getBoundingClientRect()`, будет возвращать + * `scrollHeight`, а не `clientHeight`. Поэтому перебиваем `height` на `clientHeight`. + */ + clientRect.height = element.clientHeight; + } + let offsetX = 0; let offsetY = 0; if (isFixedStrategy) {