From 8fe4f760b3aece4061126026d56afb8b9b1b8d64 Mon Sep 17 00:00:00 2001 From: Andrey Medvedev Date: Mon, 28 Oct 2024 18:18:45 +0300 Subject: [PATCH] Fix Gallery navigation when align="center" When slides are center aligned and they overflow container just a little we disable arrows and drag navigation. Because we doesn't count for slide offset from left side of container. Here we calculate the width of slides layer together with offset if align="center" --- .../components/BaseGallery/BaseGallery.tsx | 9 +- .../src/components/Gallery/Gallery.test.tsx | 98 ++++++++++++++++--- 2 files changed, 90 insertions(+), 17 deletions(-) diff --git a/packages/vkui/src/components/BaseGallery/BaseGallery.tsx b/packages/vkui/src/components/BaseGallery/BaseGallery.tsx index 0926ab9122..eaeceeedc2 100644 --- a/packages/vkui/src/components/BaseGallery/BaseGallery.tsx +++ b/packages/vkui/src/components/BaseGallery/BaseGallery.tsx @@ -143,6 +143,13 @@ export const BaseGallery = ({ localSlides.length <= layoutState.current.slides.length || layoutState.current.slides[slideIndex]?.coordX !== localSlides[slideIndex]?.coordX; + const currentSlideOffsetOnCenterAlignment = + (localContainerWidth - (localSlides[slideIndex]?.width ?? 0)) / 2; + const isFullyVisible = + align === 'center' + ? localLayerWidth + currentSlideOffsetOnCenterAlignment <= localContainerWidth + : localLayerWidth <= localContainerWidth; + layoutState.current = { containerWidth: localContainerWidth, viewportOffsetWidth: localViewportOffsetWidth, @@ -161,7 +168,7 @@ export const BaseGallery = ({ align, }), slides: localSlides, - isFullyVisible: localLayerWidth <= localContainerWidth, + isFullyVisible, }; setShiftState((prevState) => ({ diff --git a/packages/vkui/src/components/Gallery/Gallery.test.tsx b/packages/vkui/src/components/Gallery/Gallery.test.tsx index 67b81eeb37..3131864f76 100644 --- a/packages/vkui/src/components/Gallery/Gallery.test.tsx +++ b/packages/vkui/src/components/Gallery/Gallery.test.tsx @@ -73,13 +73,14 @@ const setup = ({ looped, containerWidth: defaultContainerWidth, isCustomSlideWidth = false, - viewPortWidth, + viewPortWidth: defaultViewPortWidth, align, onChange, onNext, onPrev, onDragStart, onDragEnd, + numberOfSlides = 5, }: { defaultSlideIndex: number; looped: boolean; @@ -87,6 +88,7 @@ const setup = ({ isCustomSlideWidth?: boolean; containerWidth: number; viewPortWidth: number; + numberOfSlides?: number; align?: AlignType; onChange: VoidFunction; onNext?: VoidFunction; @@ -99,6 +101,7 @@ const setup = ({ let layerTransform = ''; let viewPort: HTMLDivElement; let containerWidth = defaultContainerWidth; + let viewPortWidth = defaultViewPortWidth; const mockContainerData = (element: HTMLDivElement) => { if (!element) { @@ -161,21 +164,15 @@ const setup = ({ getRootRef={mockContainerData} getRef={mockViewportData} > - mockSlideData(e, 0)}> - 1 - - mockSlideData(e, 1)}> - 2 - - mockSlideData(e, 2)}> - 3 - - mockSlideData(e, 3)}> - 4 - - mockSlideData(e, 4)}> - 5 - + {Array.from({ length: numberOfSlides }).map((_v, index) => ( + mockSlideData(e, index)} + > + {index + 1} + + ))} ); @@ -200,6 +197,9 @@ const setup = ({ set containerWidth(newWidth: number) { containerWidth = newWidth; }, + set viewPortWidth(newWidth: number) { + viewPortWidth = newWidth; + }, }; }; @@ -636,4 +636,70 @@ describe('Gallery', () => { process.env.NODE_ENV = 'test'; }); }); + + it('checks gallery arrows and navigation in center alignment', () => { + const onChange = jest.fn(); + const onDragStart = jest.fn(); + const onDragEnd = jest.fn(); + + // в контейнере недостаточно места для + // двух слайдов с выравниванием по центру + // поэтому мы показываем кнопки и позволяем drag + const mockedData = setup({ + numberOfSlides: 2, + defaultSlideIndex: 2, + slideWidth: 180, + containerWidth: 300, + viewPortWidth: 300, + align: 'center', + looped: false, + onDragStart, + onDragEnd, + onChange, + }); + const { + component: { container }, + rerender, + } = mockedData; + + checkActiveSlide(container, 1); + expect(Array.from(container.getElementsByClassName(styles.arrow))).toHaveLength(1); + + simulateDrag(mockedData.viewPort, [150, 0]); + + expect(onDragStart).toHaveBeenCalledTimes(1); + expect(onDragEnd).toHaveBeenCalledTimes(1); + + // это пограничное состояние при котором слайды ещё + // не помещаются в контейнер, + // при ширине контейнера 540 они уже будут влезать + mockedData.containerWidth = 539; + mockedData.viewPortWidth = 539; + onDragStart.mockClear(); + onDragEnd.mockClear(); + + rerender({ slideIndex: 2 }); + + expect(Array.from(container.getElementsByClassName(styles.arrow))).toHaveLength(1); + + simulateDrag(mockedData.viewPort, [150, 0]); + + expect(onDragStart).toHaveBeenCalledTimes(1); + expect(onDragEnd).toHaveBeenCalledTimes(1); + + // слайды полностью помещаются, поэтому мы отключаем drag и не показываем стрелочки + mockedData.containerWidth = 540; + mockedData.viewPortWidth = 540; + onDragStart.mockClear(); + onDragEnd.mockClear(); + + rerender({ slideIndex: 2 }); + + expect(Array.from(container.getElementsByClassName(styles.arrow))).toHaveLength(0); + + simulateDrag(mockedData.viewPort, [150, 0]); + + expect(onDragStart).not.toHaveBeenCalled(); + expect(onDragEnd).not.toHaveBeenCalled(); + }); });