diff --git a/storefront/components/Basic/ExtendedNextLink/ExtendedNextLink.tsx b/storefront/components/Basic/ExtendedNextLink/ExtendedNextLink.tsx index 6769cbec06..d0fb8e9e29 100644 --- a/storefront/components/Basic/ExtendedNextLink/ExtendedNextLink.tsx +++ b/storefront/components/Basic/ExtendedNextLink/ExtendedNextLink.tsx @@ -18,6 +18,7 @@ export type ExtendedNextLinkProps = Omit, keyof Li queryParams?: Record; type?: PageType; skeletonType?: PageType; + onClickExtended?: MouseEventHandler; }; export const ExtendedNextLink: FC = ({ @@ -26,6 +27,7 @@ export const ExtendedNextLink: FC = ({ queryParams, as, onClick, + onClickExtended, type, skeletonType, ...props @@ -65,7 +67,7 @@ export const ExtendedNextLink: FC = ({ } : href } - onClick={handleOnClick} + onClick={onClickExtended ?? handleOnClick} {...props} > {children} diff --git a/storefront/components/Blocks/Banners/BannersSlider.tsx b/storefront/components/Blocks/Banners/BannersSlider.tsx index 47426fe5c8..0cf65e57f1 100644 --- a/storefront/components/Blocks/Banners/BannersSlider.tsx +++ b/storefront/components/Blocks/Banners/BannersSlider.tsx @@ -1,13 +1,15 @@ import { Banner } from './Banner'; import { BannersDot } from './BannersDot'; import { bannersReducer } from './bannersUtils'; +import { ExtendedNextLink } from 'components/Basic/ExtendedNextLink/ExtendedNextLink'; import { TIDs } from 'cypress/tids'; import { TypeSliderItemFragment } from 'graphql/requests/sliderItems/fragments/SliderItemFragment.generated'; -import { useEffect, useReducer, useRef } from 'react'; +import { useEffect, useReducer, useRef, useState } from 'react'; import { useSwipeable } from 'react-swipeable'; import { twJoin } from 'tailwind-merge'; +import { isTextSelected, preventClick } from 'utils/ui/disableClickWhenTextSelected'; -const SLIDER_STOP_SLIDE_TIMEOUT = 50 as const; +const SLIDER_STOP_SLIDE_TIMEOUT = 300 as const; const SLIDER_SLIDE_DURATION = 500 as const; const SLIDER_AUTOMATIC_SLIDE_INTERVAL = 5000 as const; @@ -22,13 +24,16 @@ export const BannersSlider: FC = ({ sliderItems }) => { isSliding: false, slideDirection: 'NEXT', }); + const [isMoved, setIsMoved] = useState(false); const intervalRef = useRef(null); const slide = (dir: 'PREV' | 'NEXT') => { + setIsMoved(true); checkAndClearInterval(); dispatchBannerSliderStateChange({ type: dir, numItems }); setTimeout(() => { dispatchBannerSliderStateChange({ type: 'STOP_SLIDING' }); + setIsMoved(false); }, SLIDER_STOP_SLIDE_TIMEOUT); startInterval(); }; @@ -59,8 +64,8 @@ export const BannersSlider: FC = ({ sliderItems }) => { }; const handlers = useSwipeable({ - onSwipedLeft: () => slide('NEXT'), - onSwipedRight: () => slide('PREV'), + onSwipedLeft: () => !isTextSelected() && slide('NEXT'), + onSwipedRight: () => !isTextSelected() && slide('PREV'), preventScrollOnSwipe: true, onTouchStartOrOnMouseDown: checkAndClearInterval, trackMouse: true, @@ -71,14 +76,23 @@ export const BannersSlider: FC = ({ sliderItems }) => { "vl:[-ms-overflow-style:'none'] vl:[scrollbar-width:'none'] vl:[&::-webkit-scrollbar]:hidden", ); + const handleClick = (e: React.MouseEvent) => { + if (isTextSelected() || isMoved) { + preventClick(e); + } + }; + return ( - +
( ref={ref} tid={TIDs.blocks_product_list_listeditem_ + product.catalogNumber} className={twMergeCustom( - 'group relative flex select-none flex-col gap-2.5 rounded-xl border border-backgroundMore bg-backgroundMore px-2.5 py-5 text-left transition sm:px-5', + 'group relative flex select-none flex-col gap-2.5 rounded-xl border border-backgroundMore bg-backgroundMore pb-2.5 text-left transition sm:pb-5', size === 'small' && 'p-5', 'hover:border-borderAccentLess hover:bg-background', className, @@ -94,11 +95,12 @@ export const ProductListItem = forwardRef( )} { + onClickExtended={disableClickWhenTextSelected} + onMouseUp={() => { onGtmProductClickEventHandler( product, gtmProductListName, @@ -109,67 +111,67 @@ export const ProductListItem = forwardRef( onClick?.(product, listIndex); }} > - +
+ -
- {product.fullName} -
- - {product.__typename === 'MainVariant' && ( -
- - {product.variantsCount} {t('variants count', { count: product.variantsCount })} +
+ {product.fullName}
- )} - {visibleItemsConfig.price && !(product.isMainVariant && product.isSellingDenied) && ( - - )} - - {visibleItemsConfig.storeAvailability && ( - - )} - + {product.__typename === 'MainVariant' && ( +
+ + {product.variantsCount} {t('variants count', { count: product.variantsCount })} +
+ )} - {(visibleItemsConfig.addToCart || visibleItemsConfig.productListButtons) && ( -
- {visibleItemsConfig.addToCart && ( - )} - {visibleItemsConfig.productListButtons && ( - <> - - - + {visibleItemsConfig.storeAvailability && ( + )}
- )} + + +
+ {visibleItemsConfig.addToCart && ( + + )} + + {visibleItemsConfig.productListButtons && ( + <> + + + + )} +
); }, diff --git a/storefront/components/Blocks/Product/ProductsSlider.tsx b/storefront/components/Blocks/Product/ProductsSlider.tsx index e310ab0c29..9bd46b3b18 100644 --- a/storefront/components/Blocks/Product/ProductsSlider.tsx +++ b/storefront/components/Blocks/Product/ProductsSlider.tsx @@ -9,6 +9,7 @@ import { RefObject, createRef, useEffect, useRef, useState } from 'react'; import { useSwipeable } from 'react-swipeable'; import { twJoin } from 'tailwind-merge'; import { twMergeCustom } from 'utils/twMerge'; +import { isTextSelected } from 'utils/ui/disableClickWhenTextSelected'; import { isWholeElementVisible } from 'utils/ui/isWholeElementVisible'; import { useMediaMin } from 'utils/ui/useMediaMin'; import { wait } from 'utils/wait'; @@ -85,7 +86,9 @@ export const ProductsSlider: FC = ({ const newActiveIndex = isFirstSlide ? productElementRefs!.length - 4 : prevIndex; - setActiveIndex(newActiveIndex); + if (!isTextSelected()) { + setActiveIndex(newActiveIndex); + } }; const handleNext = () => { @@ -98,7 +101,9 @@ export const ProductsSlider: FC = ({ const newActiveIndex = isEndSlide ? 0 : nextIndex; - setActiveIndex(newActiveIndex); + if (!isTextSelected()) { + setActiveIndex(newActiveIndex); + } }; const handlers = useSwipeable({ @@ -150,7 +155,7 @@ export const ProductsSlider: FC = ({ ])} productItemProps={{ className: twMergeCustom( - 'snap-center md:snap-start mx-1 md:mx-2 first:ml-0 last:mr-0', + 'snap-center md:snap-start mx-1 md:mx-2 first:ml-0 last:mr-0 p-0', productItemProps?.className, ), ...productItemProps, diff --git a/storefront/components/Pages/CategoryDetail/CategoryBestsellers/CategoryBestsellersListItem.tsx b/storefront/components/Pages/CategoryDetail/CategoryBestsellers/CategoryBestsellersListItem.tsx index 5545ec1898..65828deebe 100644 --- a/storefront/components/Pages/CategoryDetail/CategoryBestsellers/CategoryBestsellersListItem.tsx +++ b/storefront/components/Pages/CategoryDetail/CategoryBestsellers/CategoryBestsellersListItem.tsx @@ -8,9 +8,9 @@ import { TIDs } from 'cypress/tids'; import { TypeListedProductFragment } from 'graphql/requests/products/fragments/ListedProductFragment.generated'; import { GtmProductListNameType } from 'gtm/enums/GtmProductListNameType'; import { onGtmProductClickEventHandler } from 'gtm/handlers/onGtmProductClickEventHandler'; -import { twJoin } from 'tailwind-merge'; import { useFormatPrice } from 'utils/formatting/useFormatPrice'; import { isPriceVisible } from 'utils/mappers/price'; +import { disableClickWhenTextSelected } from 'utils/ui/disableClickWhenTextSelected'; type CategoryBestsellersListItemProps = { product: TypeListedProductFragment; @@ -31,11 +31,11 @@ export const CategoryBestsellersListItem: FC = return ( onGtmProductClickEventHandler( product, @@ -54,7 +54,7 @@ export const CategoryBestsellersListItem: FC = visibleItemsConfig={{ flags: false }} />
-
+
{!!product.flags.length && } {product.fullName} diff --git a/storefront/utils/ui/disableClickWhenTextSelected.ts b/storefront/utils/ui/disableClickWhenTextSelected.ts new file mode 100644 index 0000000000..839122c056 --- /dev/null +++ b/storefront/utils/ui/disableClickWhenTextSelected.ts @@ -0,0 +1,17 @@ +export const isTextSelected = () => { + const selection = window.getSelection(); + return selection && selection.toString().length > 0; +}; + +export const disableClickWhenTextSelected = (e: React.MouseEvent) => { + if (isTextSelected()) { + preventClick(e); + } + return true; +}; + +export const preventClick = (event: React.MouseEvent) => { + event.preventDefault(); + event.stopPropagation(); + return false; +};