Skip to content

Commit

Permalink
fix(Gallery): navigation doesn't work when align="center" and slides …
Browse files Browse the repository at this point in the history
…overflow just a little (#7862)

 Добавлено особое условия в режиме align="center" для подсчёта флага отключения навигации, учитывающее отступ, появляющийся при выравнивании по центру.

В условиях, когда слайды выравниваются по центру, у активного слайда слева появляется отступ от левого края, за счёт которого слайды смещаются вправо.

В такой ситуации перестаёт правильно работать условие, выключающее переключение между слайдами, когда общая ширина слайдов меньше контейнера.

Если общая ширина слайдов меньше контейнера и они за счёт отступа всё же не помещаются в контейнере, то нужно также учесть этот отступ в условии, иначе при отключении навигации будет невозможно полностью увидеть скрытый слайд.
  • Loading branch information
andrey-medvedev-vk authored Oct 29, 2024
1 parent a539c9c commit 0369ebd
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 17 deletions.
9 changes: 8 additions & 1 deletion packages/vkui/src/components/BaseGallery/BaseGallery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -161,7 +168,7 @@ export const BaseGallery = ({
align,
}),
slides: localSlides,
isFullyVisible: localLayerWidth <= localContainerWidth,
isFullyVisible,
};

setShiftState((prevState) => ({
Expand Down
98 changes: 82 additions & 16 deletions packages/vkui/src/components/Gallery/Gallery.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,20 +73,22 @@ const setup = ({
looped,
containerWidth: defaultContainerWidth,
isCustomSlideWidth = false,
viewPortWidth,
viewPortWidth: defaultViewPortWidth,
align,
onChange,
onNext,
onPrev,
onDragStart,
onDragEnd,
numberOfSlides = 5,
}: {
defaultSlideIndex: number;
looped: boolean;
slideWidth: number;
isCustomSlideWidth?: boolean;
containerWidth: number;
viewPortWidth: number;
numberOfSlides?: number;
align?: AlignType;
onChange: VoidFunction;
onNext?: VoidFunction;
Expand All @@ -99,6 +101,7 @@ const setup = ({
let layerTransform = '';
let viewPort: HTMLDivElement;
let containerWidth = defaultContainerWidth;
let viewPortWidth = defaultViewPortWidth;

const mockContainerData = (element: HTMLDivElement) => {
if (!element) {
Expand Down Expand Up @@ -161,21 +164,15 @@ const setup = ({
getRootRef={mockContainerData}
getRef={mockViewportData}
>
<Slide data-testid="slide-1" getRef={(e: HTMLDivElement) => mockSlideData(e, 0)}>
1
</Slide>
<Slide data-testid="slide-2" getRef={(e: HTMLDivElement) => mockSlideData(e, 1)}>
2
</Slide>
<Slide data-testid="slide-3" getRef={(e: HTMLDivElement) => mockSlideData(e, 2)}>
3
</Slide>
<Slide data-testid="slide-4" getRef={(e: HTMLDivElement) => mockSlideData(e, 3)}>
4
</Slide>
<Slide data-testid="slide-5" getRef={(e: HTMLDivElement) => mockSlideData(e, 4)}>
5
</Slide>
{Array.from({ length: numberOfSlides }).map((_v, index) => (
<Slide
key={index}
data-testid={`slide-${index + 1}`}
getRef={(e: HTMLDivElement) => mockSlideData(e, index)}
>
{index + 1}
</Slide>
))}
</Gallery>
);

Expand All @@ -200,6 +197,9 @@ const setup = ({
set containerWidth(newWidth: number) {
containerWidth = newWidth;
},
set viewPortWidth(newWidth: number) {
viewPortWidth = newWidth;
},
};
};

Expand Down Expand Up @@ -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();
});
});

0 comments on commit 0369ebd

Please sign in to comment.