From b6616942c08271bb9ef593c3b3ff7da33ac767ee Mon Sep 17 00:00:00 2001 From: "e.muhamethanov" Date: Sat, 2 Nov 2024 12:55:18 +0300 Subject: [PATCH] fix(HorizontalScroll): fix scroll when mouse over arrow --- .../HorizontalScroll.test.tsx | 33 ++++++++++++ .../HorizontalScroll/HorizontalScroll.tsx | 51 +++++++++---------- 2 files changed, 56 insertions(+), 28 deletions(-) diff --git a/packages/vkui/src/components/HorizontalScroll/HorizontalScroll.test.tsx b/packages/vkui/src/components/HorizontalScroll/HorizontalScroll.test.tsx index 314ab9e082..8cb19c26d5 100644 --- a/packages/vkui/src/components/HorizontalScroll/HorizontalScroll.test.tsx +++ b/packages/vkui/src/components/HorizontalScroll/HorizontalScroll.test.tsx @@ -19,6 +19,11 @@ const setup = (element: HTMLElement, startScrollLeft = 0) => { jest.spyOn(element.firstElementChild!, 'scrollWidth', 'get').mockImplementation(() => 500); + // @ts-expect-error: TS2322 есть другой тип, но в компоненте он не используется + element.scrollBy = (options?: ScrollToOptions) => { + scrollLeft = scrollLeft + (options?.left || 0); + }; + return { get scrollLeft() { return scrollLeft; @@ -176,6 +181,34 @@ describe('HorizontalScroll', () => { expect(mockedData.scrollLeft).toBe(200); }); }); + + it('scroll by arrow', () => { + const ref: React.MutableRefObject = { + current: null, + }; + render( + +
+ , + ); + + const mockedData = setup(ref.current!, 50); + + fireEvent.mouseEnter(screen.getByTestId('horizontal-scroll')); + + const arrowLeft = screen.getByTestId('ScrollArrowLeft'); + const arrowRight = screen.getByTestId('ScrollArrowRight'); + + fireEvent.wheel(arrowRight, { + deltaX: 20, + }); + expect(mockedData.scrollLeft).toBe(70); + + fireEvent.wheel(arrowLeft, { + deltaX: 20, + }); + expect(mockedData.scrollLeft).toBe(90); + }); }); function mockRef(element: HTMLDivElement) { diff --git a/packages/vkui/src/components/HorizontalScroll/HorizontalScroll.tsx b/packages/vkui/src/components/HorizontalScroll/HorizontalScroll.tsx index 449cc6311b..2dab857fea 100644 --- a/packages/vkui/src/components/HorizontalScroll/HorizontalScroll.tsx +++ b/packages/vkui/src/components/HorizontalScroll/HorizontalScroll.tsx @@ -1,10 +1,9 @@ 'use client'; import * as React from 'react'; -import { classNames, noop } from '@vkontakte/vkjs'; +import { classNames } from '@vkontakte/vkjs'; import { useAdaptivityHasPointer } from '../../hooks/useAdaptivityHasPointer'; import { useDirection } from '../../hooks/useDirection'; -import { useEventListener } from '../../hooks/useEventListener'; import { useExternRef } from '../../hooks/useExternRef'; import { easeInOutSine } from '../../lib/fx'; import type { HasRef, HTMLAttributesWithRootRef } from '../../types'; @@ -233,44 +232,33 @@ export const HorizontalScroll = ({ } }, [showArrows, hasPointer, scrollerRef, setCanScrollStart, setCanScrollEnd]); - const scrollEvent = useEventListener('scroll', calculateArrowsVisibility); - React.useEffect( - function addScrollerRefToScrollEvent() { - if (!scrollerRef.current) { - return noop; - } + React.useEffect(calculateArrowsVisibility, [calculateArrowsVisibility, children]); - scrollEvent.add(scrollerRef.current); - return scrollEvent.remove; + const _onWheel = React.useCallback( + (e: React.WheelEvent) => { + scrollerRef.current!.scrollBy({ left: e.deltaX + e.deltaY, behavior: 'auto' }); }, - [scrollEvent, scrollerRef], + [scrollerRef], ); - React.useEffect(calculateArrowsVisibility, [calculateArrowsVisibility, children]); - /** * Прокрутка с помощью любого колеса мыши */ - const onwheel = React.useCallback( - (e: WheelEvent) => { - scrollerRef.current!.scrollBy({ left: e.deltaX + e.deltaY, behavior: 'auto' }); + const onScrollWheel = React.useCallback( + (e: React.WheelEvent) => { + _onWheel(e); e.preventDefault(); }, - [scrollerRef], + [_onWheel], ); - const wheelEvent = useEventListener('wheel', onwheel); - React.useEffect( - function addScrollerRefToWheelEvent() { - if (!scrollerRef.current || !scrollOnAnyWheel) { - return noop; + const onArrowWheel = React.useCallback( + (e: React.WheelEvent) => { + if (e.deltaX || (e.deltaY && scrollOnAnyWheel)) { + _onWheel(e); } - - wheelEvent.add(scrollerRef.current); - - return wheelEvent.remove; }, - [wheelEvent, scrollerRef, scrollOnAnyWheel], + [_onWheel, scrollOnAnyWheel], ); return ( @@ -293,6 +281,7 @@ export const HorizontalScroll = ({ tabIndex={-1} className={classNames(styles.arrow, styles.arrowLeft)} onClick={scrollToLeft} + onWheel={onArrowWheel} /> )} {showArrows && (hasPointer || hasPointer === undefined) && canScrollRight && ( @@ -305,9 +294,15 @@ export const HorizontalScroll = ({ tabIndex={-1} className={classNames(styles.arrow, styles.arrowRight)} onClick={scrollToRight} + onWheel={onArrowWheel} /> )} -
+
{children}