Skip to content

Commit

Permalink
feat(Gallery): add testid's to elements and refactor (#8015)
Browse files Browse the repository at this point in the history
* feat(Gallery): add testid's to elements and refactor

* fix(Gallery): reslove conflicts

* doc(Gallery): fix JSDocs

* fix(Gallery): remove object

* fix(Gallery): fix formatting
  • Loading branch information
EldarMuhamethanov authored Dec 5, 2024
1 parent efa568a commit c52e9bf
Show file tree
Hide file tree
Showing 7 changed files with 209 additions and 126 deletions.
51 changes: 24 additions & 27 deletions packages/vkui/src/components/BaseGallery/BaseGallery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import { useGlobalEventListener } from '../../hooks/useGlobalEventListener';
import { useDOM } from '../../lib/dom';
import { useIsomorphicLayoutEffect } from '../../lib/useIsomorphicLayoutEffect';
import { RootComponent } from '../RootComponent/RootComponent';
import { type CustomTouchEvent, Touch } from '../Touch/Touch';
import { type CustomTouchEvent } from '../Touch/Touch';
import { Bullets } from './Bullets';
import { GalleryViewPort } from './GalleryViewPort';
import { ScrollArrows } from './ScrollArrows';
import { calcMax, calcMin } from './helpers';
import type { BaseGalleryProps, GallerySlidesState, LayoutState, ShiftingState } from './types';
Expand All @@ -34,10 +36,6 @@ const SHIFT_DEFAULT_STATE = {
indent: 0,
};

const stylesBullets = {
dark: styles.bulletsDark,
light: styles.bulletsLight,
};
export const BaseGallery = ({
bullets = false,
getRootRef,
Expand All @@ -55,6 +53,10 @@ export const BaseGallery = ({
getRef,
arrowSize,
arrowAreaHeight,
slideTestId,
bulletTestId,
nextArrowTestId,
prevArrowTestId,
...restProps
}: BaseGalleryProps): React.ReactNode => {
const slidesStore = React.useRef<Record<string, HTMLDivElement | null>>({});
Expand Down Expand Up @@ -331,33 +333,26 @@ export const BaseGallery = ({
)}
getRootRef={rootRef}
>
<Touch
className={styles.viewport}
onStartX={onStart}
<GalleryViewPort
slideWidth={slideWidth}
slideTestId={slideTestId}
onStart={onStart}
onMoveX={onMoveX}
onEnd={onEnd}
style={{ width: slideWidth === 'custom' ? '100%' : slideWidth }}
getRootRef={viewportRef}
noSlideClick
viewportRef={viewportRef}
layerStyle={layerStyle}
setSlideRef={setSlideRef}
>
<div className={styles.layer} style={layerStyle}>
{React.Children.map(children, (item: React.ReactNode, i: number) => (
<div className={styles.slide} key={`slide-${i}`} ref={(el) => setSlideRef(el, i)}>
{item}
</div>
))}
</div>
</Touch>
{children}
</GalleryViewPort>

{bullets && (
<div aria-hidden className={classNames(styles.bullets, stylesBullets[bullets])}>
{React.Children.map(children, (_item: React.ReactNode, index: number) => (
<div
className={classNames(styles.bullet, index === slideIndex && styles.bulletActive)}
key={index}
/>
))}
</div>
<Bullets
bullets={bullets}
slideIndex={slideIndex}
count={React.Children.count(children)}
bulletTestId={bulletTestId}
/>
)}
<ScrollArrows
hasPointer={hasPointer}
Expand All @@ -368,6 +363,8 @@ export const BaseGallery = ({
showArrows={showArrows}
arrowSize={arrowSize}
arrowAreaHeight={arrowAreaHeight}
nextArrowTestId={nextArrowTestId}
prevArrowTestId={prevArrowTestId}
/>
</RootComponent>
);
Expand Down
36 changes: 36 additions & 0 deletions packages/vkui/src/components/BaseGallery/Bullets.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import * as React from 'react';
import { classNames } from '@vkontakte/vkjs';
import { type BaseGalleryProps } from './types';
import styles from './BaseGallery.module.css';

export interface BulletsTestIds {
/**
* Передает атрибут `data-testid` для bullets
*/
bulletTestId?: (index: number, active: boolean) => string;
}

interface BulletsProps extends BulletsTestIds {
bullets: Exclude<BaseGalleryProps['bullets'], false | undefined>;
slideIndex: number;
count: number;
}

const stylesBullets = {
dark: styles.bulletsDark,
light: styles.bulletsLight,
};

export const Bullets: React.FC<BulletsProps> = ({ bullets, slideIndex, count, bulletTestId }) => {
return (
<div aria-hidden className={classNames(styles.bullets, stylesBullets[bullets])}>
{Array.from({ length: count }).map((_, index) => (
<div
className={classNames(styles.bullet, index === slideIndex && styles.bulletActive)}
data-testid={bulletTestId?.(index, index === slideIndex)}
key={index}
/>
))}
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import { useDOM } from '../../../lib/dom';
import { useIsomorphicLayoutEffect } from '../../../lib/useIsomorphicLayoutEffect';
import { warnOnce } from '../../../lib/warnOnce';
import { RootComponent } from '../../RootComponent/RootComponent';
import { type CustomTouchEvent, Touch } from '../../Touch/Touch';
import { type CustomTouchEvent } from '../../Touch/Touch';
import { Bullets } from '../Bullets';
import { GalleryViewPort } from '../GalleryViewPort';
import { ScrollArrows } from '../ScrollArrows';
import { type BaseGalleryProps, type GallerySlidesState } from '../types';
import {
Expand All @@ -24,11 +26,6 @@ import { useSlideAnimation } from './hooks';
import type { ControlElementsState, SlidesManagerState } from './types';
import styles from '../BaseGallery.module.css';

const stylesBullets = {
dark: styles.bulletsDark,
light: styles.bulletsLight,
};

const warn = warnOnce('Gallery');

export const CarouselBase = ({
Expand All @@ -48,6 +45,10 @@ export const CarouselBase = ({
getRef,
arrowSize,
arrowAreaHeight,
slideTestId,
bulletTestId,
nextArrowTestId,
prevArrowTestId,
...restProps
}: BaseGalleryProps): React.ReactNode => {
const slidesStore = React.useRef<Record<string, HTMLDivElement | null>>({});
Expand Down Expand Up @@ -350,33 +351,26 @@ export const CarouselBase = ({
)}
getRootRef={rootRef}
>
<Touch
className={styles.viewport}
onStartX={onStart}
<GalleryViewPort
slideWidth={slideWidth}
slideTestId={slideTestId}
onStart={onStart}
onMoveX={onMoveX}
onEnd={onEnd}
style={{ width: slideWidth === 'custom' ? '100%' : slideWidth }}
getRootRef={viewportRef}
noSlideClick
viewportRef={viewportRef}
layerRef={layerRef}
setSlideRef={setSlideRef}
>
<div className={styles.layer} ref={layerRef}>
{React.Children.map(children, (item: React.ReactNode, i: number) => (
<div className={styles.slide} key={`slide-${i}`} ref={(el) => setSlideRef(el, i)}>
{item}
</div>
))}
</div>
</Touch>
{children}
</GalleryViewPort>

{bullets && (
<div aria-hidden className={classNames(styles.bullets, stylesBullets[bullets])}>
{React.Children.map(children, (_item: React.ReactNode, index: number) => (
<div
className={classNames(styles.bullet, index === slideIndex && styles.bulletActive)}
key={index}
/>
))}
</div>
<Bullets
bullets={bullets}
slideIndex={slideIndex}
count={React.Children.count(children)}
bulletTestId={bulletTestId}
/>
)}
<ScrollArrows
hasPointer={hasPointer}
Expand All @@ -387,6 +381,8 @@ export const CarouselBase = ({
showArrows={showArrows}
arrowSize={arrowSize}
arrowAreaHeight={arrowAreaHeight}
prevArrowTestId={prevArrowTestId}
nextArrowTestId={nextArrowTestId}
/>
</RootComponent>
);
Expand Down
56 changes: 56 additions & 0 deletions packages/vkui/src/components/BaseGallery/GalleryViewPort.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
'use client';

import * as React from 'react';
import { type HasChildren } from '../../types';
import { type CustomTouchEvent, Touch } from '../Touch/Touch';
import { type BaseGalleryProps } from './types';
import styles from './BaseGallery.module.css';

type GalleryViewPortProps = Pick<BaseGalleryProps, 'slideWidth' | 'slideTestId'> &
HasChildren & {
onStart: (e: CustomTouchEvent) => void;
onMoveX: (e: CustomTouchEvent) => void;
onEnd: (e: CustomTouchEvent) => void;
viewportRef: React.Ref<HTMLElement>;
setSlideRef: (slideRef: HTMLDivElement | null, slideIndex: number) => void;
layerRef?: React.Ref<HTMLDivElement>;
layerStyle?: React.CSSProperties;
};

export const GalleryViewPort: React.FC<GalleryViewPortProps> = ({
slideTestId,
slideWidth,
onStart,
onMoveX,
onEnd,
viewportRef,
layerRef,
layerStyle,
children,
setSlideRef,
}) => {
return (
<Touch
className={styles.viewport}
onStartX={onStart}
onMoveX={onMoveX}
onEnd={onEnd}
style={{ width: slideWidth === 'custom' ? '100%' : slideWidth }}
getRootRef={viewportRef}
noSlideClick
>
<div className={styles.layer} ref={layerRef} style={layerStyle}>
{React.Children.map(children, (item: React.ReactNode, i: number) => (
<div
className={styles.slide}
key={`slide-${i}`}
data-testid={slideTestId?.(i)}
ref={(el) => setSlideRef(el, i)}
>
{item}
</div>
))}
</div>
</Touch>
);
};
18 changes: 17 additions & 1 deletion packages/vkui/src/components/BaseGallery/ScrollArrows.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,20 @@ export const getArrowClassName = (
);
};

export interface ScrollArrowsTestIds {
/**
* Передает атрибут `data-testid` для кнопки перехода к следующему слайду
*/
nextArrowTestId?: string;
/**
* Передает атрибут `data-testid` для кнопки перехода к предыдущему слайду
*/
prevArrowTestId?: string;
}

interface ScrollArrowsProps
extends Pick<BaseGalleryProps, 'showArrows' | 'arrowSize' | 'arrowAreaHeight'> {
extends Pick<BaseGalleryProps, 'showArrows' | 'arrowSize' | 'arrowAreaHeight'>,
ScrollArrowsTestIds {
hasPointer?: boolean;
canSlideLeft: boolean;
canSlideRight: boolean;
Expand All @@ -40,6 +52,8 @@ export const ScrollArrows: React.FC<ScrollArrowsProps> = ({
showArrows = false,
arrowSize = 'm',
arrowAreaHeight = 'stretch',
nextArrowTestId,
prevArrowTestId,
}) => {
return showArrows && hasPointer ? (
<>
Expand All @@ -49,6 +63,7 @@ export const ScrollArrows: React.FC<ScrollArrowsProps> = ({
direction="left"
onClick={onSlideLeft}
size={arrowSize}
data-testid={prevArrowTestId}
/>
)}
{canSlideRight && (
Expand All @@ -57,6 +72,7 @@ export const ScrollArrows: React.FC<ScrollArrowsProps> = ({
direction="right"
onClick={onSlideRight}
size={arrowSize}
data-testid={nextArrowTestId}
/>
)}
</>
Expand Down
10 changes: 9 additions & 1 deletion packages/vkui/src/components/BaseGallery/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import type * as React from 'react';
import type { HasAlign, HasRef, HTMLAttributesWithRootRef } from '../../types';
import type { ScrollArrowProps } from '../ScrollArrow/ScrollArrow';
import type { CustomTouchEvent, CustomTouchEventHandler } from '../Touch/Touch';
import { type BulletsTestIds } from './Bullets';
import { type ScrollArrowsTestIds } from './ScrollArrows';

export interface GallerySlidesState {
coordX: number;
Expand All @@ -28,7 +30,9 @@ export interface LayoutState {
export interface BaseGalleryProps
extends Omit<HTMLAttributesWithRootRef<HTMLDivElement>, 'onChange' | 'onDragStart' | 'onDragEnd'>,
HasAlign,
HasRef<HTMLElement> {
HasRef<HTMLElement>,
BulletsTestIds,
ScrollArrowsTestIds {
slideWidth?: string | number;
slideIndex?: number;
onDragStart?: CustomTouchEventHandler;
Expand Down Expand Up @@ -62,4 +66,8 @@ export interface BaseGalleryProps
* Текст для кнопки-стрелки вправо (вперед). Делает ее доступной для ассистивных технологий
*/
arrowNextLabel?: string;
/**
* Передает атрибут `data-testid` для слайда
*/
slideTestId?: (index: number) => string;
}
Loading

0 comments on commit c52e9bf

Please sign in to comment.