Skip to content

Commit

Permalink
feat(Cell): add auto scroll for draggable mode (#5833)
Browse files Browse the repository at this point in the history
* tech: extend e2e test infra

- Импортиурем файл с утилитами
- Добавляем запуск для конткретных движков

* feat(Cell): add auto scroll for draggable mode

* fix(review): reword checkIfElementIsInsideYEdgesOfViewport args

* fix(review): refactor useDraggable

* chore: add tests

* chore: add comments with references

* refactor: revert now() moving to lib/date

* review: mv condition for icon to var

* review: extend dom.test.ts
  • Loading branch information
inomdzhon authored Nov 20, 2023
1 parent fd7cb8d commit 43e5c5c
Show file tree
Hide file tree
Showing 30 changed files with 1,293 additions and 277 deletions.
44 changes: 44 additions & 0 deletions packages/vkui/jest.setup.js
Original file line number Diff line number Diff line change
@@ -1 +1,45 @@
require('@testing-library/jest-dom');

// Не реализован в JSDOM.
// https://jestjs.io/docs/manual-mocks
global.DOMRect = class DOMRect {
top = 0;
right = 0;
bottom = 0;
left = 0;
width = 0;
height = 0;
constructor(x = 0, y = 0, width = 0, height = 0) {
this.x = x;
this.y = y;
this.top = y;
this.right = x + width;
this.bottom = y + height;
this.left = x;
this.width = width;
this.height = height;
}
static fromRect(other) {
return new DOMRect(other?.x, other?.y, other?.width, other?.height);
}
toJSON() {
const { x, y, top, right, bottom, left, width, height } = this;
return JSON.stringify({ x, y, top, right, bottom, left, width, height });
}
};

// Не реализован в JSDOM.
// Объявление скопировано из документации https://jestjs.io/docs/manual-mocks
Object.defineProperty(global, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation((query) => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // устарело
removeListener: jest.fn(), // устарело
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
10 changes: 0 additions & 10 deletions packages/vkui/src/components/Cell/Cell.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,9 @@
position: relative;
}

/**
* CMP:
* List
*/
:global(.vkuiInternalList--dragging) .Cell:not(.Cell--dragging) {
transition: transform 0.3s ease;
pointer-events: none;
}

.Cell--dragging {
background-color: var(--vkui--color_background_secondary);
box-shadow: var(--vkui--elevation3);
z-index: var(--vkui_internal--z_index_cell_dragging);
}

.Cell--selectable.Cell--disabled {
Expand Down
30 changes: 0 additions & 30 deletions packages/vkui/src/components/Cell/Cell.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,44 +5,14 @@ import { Platform } from '../../lib/platform';
import { baselineComponent } from '../../testing/utils';
import { ConfigProvider } from '../ConfigProvider/ConfigProvider';
import { List } from '../List/List';
import { ListContext } from '../List/ListContext';
import { Cell } from './Cell';

const label = 'Перенести ячейку';
const dragger = () => screen.getByLabelText(label);

describe('Cell', () => {
baselineComponent((props) => <Cell {...props}>Cell</Cell>);

describe('Controls dragging', () => {
it('on mouse up/down', () => {
const toggleDrag = jest.fn();
render(
<ListContext.Provider value={{ toggleDrag }}>
<Cell draggable draggerLabel={label} />
</ListContext.Provider>,
);

fireEvent.mouseDown(dragger());
expect(toggleDrag).toHaveBeenLastCalledWith(true);

fireEvent.mouseUp(dragger());
expect(toggleDrag).toHaveBeenLastCalledWith(false);
});

it('stops drag on unmount', () => {
const toggleDrag = jest.fn();
const { rerender } = render(
<ListContext.Provider value={{ toggleDrag }}>
<Cell draggable draggerLabel={label} />
</ListContext.Provider>,
);

fireEvent.mouseDown(dragger());
rerender(<ListContext.Provider value={{ toggleDrag }} />);
expect(toggleDrag).toHaveBeenLastCalledWith(false);
});

it('does not reorder dragged item on click', () => {
const initialList = ['eugpoloz', 'arthurstam', 'xyz'];
let updatedList = [...initialList];
Expand Down
55 changes: 21 additions & 34 deletions packages/vkui/src/components/Cell/Cell.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import * as React from 'react';
import { classNames, noop } from '@vkontakte/vkjs';
import type { SwappedItemRange } from '../../hooks/useDraggableWithDomApi';
import { useExternRef } from '../../hooks/useExternRef';
import { usePlatform } from '../../hooks/usePlatform';
import { Platform } from '../../lib/platform';
import { HasRootRef } from '../../types';
import { ListContext } from '../List/ListContext';
import { Removable, RemovableProps } from '../Removable/Removable';
import { SimpleCell, SimpleCellProps } from '../SimpleCell/SimpleCell';
import { CellCheckbox, CellCheckboxProps } from './CellCheckbox/CellCheckbox';
import type { HasRootRef } from '../../types';
import { Removable, type RemovableProps } from '../Removable/Removable';
import { SimpleCell, type SimpleCellProps } from '../SimpleCell/SimpleCell';
import { CellCheckbox, type CellCheckboxProps } from './CellCheckbox/CellCheckbox';
import { CellDragger } from './CellDragger/CellDragger';
import { useDraggable } from './useDraggable';
import { DEFAULT_DRAGGABLE_LABEL } from './constants';
import styles from './Cell.module.css';

export interface CellProps
Expand Down Expand Up @@ -39,7 +39,7 @@ export interface CellProps
* Эти числа нужны для того, чтобы разработчик понимал, с какого индекса на какой произошел переход. В песочнице
* есть рабочий пример с обработкой этих чисел и перерисовкой списка.
*/
onDragFinish?: ({ from, to }: { from: number; to: number }) => void;
onDragFinish?(swappedItemRange: SwappedItemRange): void;
/**
* aria-label для кнопки перетаскивания ячейки
*/
Expand All @@ -65,11 +65,12 @@ export const Cell = ({
checked,
defaultChecked,
getRootRef,
draggerLabel = 'Перенести ячейку',
draggerLabel = DEFAULT_DRAGGABLE_LABEL,
className,
style,
...restProps
}: CellProps) => {
const [dragging, setDragging] = React.useState(false);
const selectable = mode === 'selectable';
const removable = mode === 'removable';
const Component = selectable ? 'label' : ComponentProps;
Expand All @@ -78,40 +79,26 @@ export const Cell = ({

const rootElRef = useExternRef(getRootRef);

const { dragging, ...draggableProps } = useDraggable({
rootElRef,
onDragFinish,
});

const { toggleDrag } = React.useContext(ListContext);
React.useEffect(() => {
if (dragging) {
toggleDrag(true);
return () => toggleDrag(false);
}
return undefined;
}, [dragging, toggleDrag]);

let dragger;
if (draggable) {
dragger = (
<CellDragger
className={styles['Cell__dragger']}
aria-label={draggerLabel}
{...draggableProps}
/>
);
}
const dragger = draggable ? (
<CellDragger
elRef={rootElRef}
className={styles['Cell__dragger']}
aria-label={draggerLabel}
disabled={disabled}
onDragStateChange={setDragging}
onDragFinish={onDragFinish}
/>
) : null;

let checkbox;
if (selectable) {
const checkboxProps: CellCheckboxProps = {
name,
value,
onChange,
defaultChecked,
checked,
disabled,
onChange,
};
checkbox = <CellCheckbox className={styles['Cell__checkbox']} {...checkboxProps} />;
}
Expand All @@ -122,8 +109,8 @@ export const Cell = ({

const cellClasses = classNames(
styles['Cell'],
platform === Platform.IOS && styles['Cell--ios'],
dragging && styles['Cell--dragging'],
platform === Platform.IOS && styles['Cell--ios'],
removable && styles['Cell--removable'],
Component === 'label' && styles['Cell--selectable'],
disabled && styles['Cell--disabled'],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
/* stylelint-disable @project-tools/stylelint-atomic, selector-max-universal */
.CellDragger {
cursor: ns-resize;
color: var(--vkui--color_icon_secondary);
user-select: none;
touch-action: manipulation;
}

.CellDragger__icon {
pointer-events: none;
}
47 changes: 30 additions & 17 deletions packages/vkui/src/components/Cell/CellDragger/CellDragger.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,56 @@
import * as React from 'react';
import { Icon24Reorder, Icon24ReorderIos } from '@vkontakte/icons';
import { classNames } from '@vkontakte/vkjs';
import {
type DraggableProps,
UseDraggableProps,
useDraggableWithDomApi,
} from '../../../hooks/useDraggableWithDomApi';
import { usePlatform } from '../../../hooks/usePlatform';
import { Platform } from '../../../lib/platform';
import { useIsomorphicLayoutEffect } from '../../../lib/useIsomorphicLayoutEffect';
import { HTMLAttributesWithRootRef } from '../../../types';
import { Touch } from '../../Touch/Touch';
import { DraggableProps } from '../useDraggable';
import styles from './CellDragger.module.css';

type CellDraggerProps = DraggableProps &
Omit<HTMLAttributesWithRootRef<HTMLElement>, keyof DraggableProps>;
interface CellDraggerProps
extends UseDraggableProps,
Omit<HTMLAttributesWithRootRef<HTMLElement>, keyof DraggableProps> {
disabled?: boolean;
onDragStateChange?(dragging: boolean): void;
}

export const CellDragger = ({
onDragStart,
onDragMove,
onDragEnd,
onClick,
elRef,
disabled,
className,
onDragStateChange,
onDragFinish,
...restProps
}: CellDraggerProps) => {
const platform = usePlatform();
const Icon = platform === Platform.IOS ? Icon24ReorderIos : Icon24Reorder;

const handleClick = (event: React.MouseEvent<HTMLElement>) => {
event.preventDefault();
if (onClick) {
onClick(event);
const { dragging, onDragStart, onDragMove, onDragEnd } = useDraggableWithDomApi({
elRef,
onDragFinish,
});

useIsomorphicLayoutEffect(() => {
if (onDragStateChange) {
onDragStateChange(dragging);
}
};
}, [dragging, onDragStateChange]);

return (
<Touch
className={classNames(styles['CellDragger'], className)}
onStart={onDragStart}
onMoveY={onDragMove}
onEnd={onDragEnd}
onClick={handleClick}
onStart={disabled ? undefined : onDragStart}
onMoveY={disabled ? undefined : onDragMove}
onEnd={disabled ? undefined : onDragEnd}
{...restProps}
>
{platform === Platform.IOS ? <Icon24ReorderIos /> : <Icon24Reorder />}
<Icon className={styles['CellDragger__icon']} />
</Touch>
);
};
1 change: 1 addition & 0 deletions packages/vkui/src/components/Cell/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const DEFAULT_DRAGGABLE_LABEL = 'Перенести ячейку';
Loading

0 comments on commit 43e5c5c

Please sign in to comment.