Skip to content

Commit f4b5334

Browse files
johannes-weberJohannes Weber
andauthored
chore: Add hideButtonsOnDrag and clickDragThreshold property to InternalDragHandle (#3545)
Co-authored-by: Johannes Weber <[email protected]>
1 parent 4efc6d5 commit f4b5334

File tree

5 files changed

+79
-18
lines changed

5 files changed

+79
-18
lines changed

src/internal/components/drag-handle-wrapper/__tests__/drag-handle-wrapper.test.tsx

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,15 @@ function expectDirectionButtonToBeVisible(direction: Direction) {
5353
).toBe(true);
5454
}
5555

56-
function renderDragHandle(props: Omit<DragHandleWrapperProps, 'children'>) {
56+
function renderDragHandle(props: Partial<Omit<DragHandleWrapperProps, 'children'>>) {
57+
const mergedProps: Omit<DragHandleWrapperProps, 'children'> = {
58+
directions: {},
59+
hideButtonsOnDrag: false,
60+
clickDragThreshold: 3,
61+
...props,
62+
};
5763
const { container } = render(
58-
<DragHandleWrapper {...props}>
64+
<DragHandleWrapper {...mergedProps}>
5965
<button type="button" id="drag-button">
6066
Drag
6167
</button>
@@ -343,7 +349,7 @@ test('shows direction buttons when dragged 2 pixels', () => {
343349
expect(getDirectionButton('block-start')).toBeVisible();
344350
});
345351

346-
test("doesn't show direction buttons when dragged more than 3 pixels", () => {
352+
test("doesn't show direction buttons when dragged more than 3 pixels (default threshold)", () => {
347353
const { dragHandle } = renderDragHandle({
348354
directions: { 'block-start': 'active' },
349355
tooltipText: 'Click me!',
@@ -355,6 +361,49 @@ test("doesn't show direction buttons when dragged more than 3 pixels", () => {
355361
expectDirectionButtonToBeHidden('block-start');
356362
});
357363

364+
test('shows direction buttons when dragged less than custom clickDragThreshold', () => {
365+
const { dragHandle } = renderDragHandle({
366+
directions: { 'block-start': 'active' },
367+
tooltipText: 'Click me!',
368+
clickDragThreshold: 10,
369+
});
370+
371+
fireEvent.pointerDown(dragHandle, { clientX: 50, clientY: 50 });
372+
fireEvent.pointerMove(dragHandle, { clientX: 55, clientY: 55 });
373+
fireEvent.pointerUp(dragHandle);
374+
expectDirectionButtonToBeVisible('block-start');
375+
});
376+
377+
describe('hideButtonsOnDrag property', () => {
378+
test('hides direction buttons when dragging with hideButtonsOnDrag=true', () => {
379+
const { dragHandle, showButtons } = renderDragHandle({
380+
directions: { 'block-start': 'active' },
381+
tooltipText: 'Click me!',
382+
hideButtonsOnDrag: true,
383+
});
384+
385+
showButtons();
386+
expectDirectionButtonToBeVisible('block-start');
387+
fireEvent.pointerDown(dragHandle, { clientX: 50, clientY: 50 });
388+
fireEvent.pointerMove(dragHandle, { clientX: 55, clientY: 55 });
389+
expectDirectionButtonToBeHidden('block-start');
390+
});
391+
392+
test('keeps direction buttons visible when dragging with hideButtonsOnDrag=false (default)', () => {
393+
const { dragHandle, showButtons } = renderDragHandle({
394+
directions: { 'block-start': 'active' },
395+
tooltipText: 'Click me!',
396+
hideButtonsOnDrag: false,
397+
});
398+
399+
showButtons();
400+
expectDirectionButtonToBeVisible('block-start');
401+
fireEvent.pointerDown(dragHandle, { clientX: 50, clientY: 50 });
402+
fireEvent.pointerMove(dragHandle, { clientX: 55, clientY: 55 }); // Move more than default threshold
403+
expectDirectionButtonToBeVisible('block-start');
404+
});
405+
});
406+
358407
test('hides direction buttons on Escape keypress', () => {
359408
const { dragHandle, showButtons } = renderDragHandle({
360409
directions: { 'block-start': 'active' },

src/internal/components/drag-handle-wrapper/index.tsx

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,15 @@ import PortalOverlay from './portal-overlay';
1414

1515
import styles from './styles.css.js';
1616

17-
// The amount of distance after pointer down that the cursor is allowed to
18-
// jitter for a subsequent mouseup to still register as a "press" instead of
19-
// a drag. A little allowance is needed for usability reasons, but this number
20-
// isn't set in stone.
21-
const PRESS_DELTA_MAX = 3;
22-
2317
export default function DragHandleWrapper({
2418
directions,
2519
tooltipText,
2620
children,
2721
onDirectionClick,
2822
triggerMode = 'focus',
2923
initialShowButtons = false,
24+
hideButtonsOnDrag,
25+
clickDragThreshold,
3026
}: DragHandleWrapperProps) {
3127
const wrapperRef = useRef<HTMLDivElement | null>(null);
3228
const dragHandleRef = useRef<HTMLDivElement | null>(null);
@@ -70,25 +66,26 @@ export default function DragHandleWrapper({
7066
useEffect(() => {
7167
const controller = new AbortController();
7268

73-
// See `PRESS_DELTA_MAX` above. We need to differentiate between a "click"
74-
// and a "drag" action. We can say a "click" happens when a "pointerdown"
75-
// is followed by a "pointerup" with no "pointermove" between the two.
69+
// We need to differentiate between a "click" and a "drag" action.
70+
// We can say a "click" happens when a "pointerdown" is followed by
71+
// a "pointerup" with no "pointermove" between the two.
7672
// However, it would be a poor usability experience if a "click" isn't
7773
// registered because, while pressing my mouse, I moved it by just one
7874
// pixel, making it a "drag" instead. So we allow the pointer to move by
79-
// `PRESS_DELTA_MAX` pixels before setting `didPointerDrag` to true.
75+
// `clickDragThreshold` pixels before setting `didPointerDrag` to true.
8076
document.addEventListener(
8177
'pointermove',
8278
event => {
8379
if (
8480
isPointerDown.current &&
8581
initialPointerPosition.current &&
86-
(event.clientX > initialPointerPosition.current.x + PRESS_DELTA_MAX ||
87-
event.clientX < initialPointerPosition.current.x - PRESS_DELTA_MAX ||
88-
event.clientY > initialPointerPosition.current.y + PRESS_DELTA_MAX ||
89-
event.clientY < initialPointerPosition.current.y - PRESS_DELTA_MAX)
82+
(event.clientX > initialPointerPosition.current.x + clickDragThreshold ||
83+
event.clientX < initialPointerPosition.current.x - clickDragThreshold ||
84+
event.clientY > initialPointerPosition.current.y + clickDragThreshold ||
85+
event.clientY < initialPointerPosition.current.y - clickDragThreshold)
9086
) {
9187
didPointerDrag.current = true;
88+
hideButtonsOnDrag && setShowButtons(false);
9289
}
9390
},
9491
{ signal: controller.signal }
@@ -123,7 +120,7 @@ export default function DragHandleWrapper({
123120
);
124121

125122
return () => controller.abort();
126-
}, []);
123+
}, [clickDragThreshold, hideButtonsOnDrag]);
127124

128125
const onHandlePointerDown: React.PointerEventHandler = event => {
129126
// Tooltip behavior: the tooltip should appear on hover, but disappear when

src/internal/components/drag-handle-wrapper/interfaces.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,6 @@ export interface DragHandleWrapperProps {
1212
children: React.ReactNode;
1313
triggerMode?: TriggerMode;
1414
initialShowButtons?: boolean;
15+
hideButtonsOnDrag: boolean;
16+
clickDragThreshold: number;
1517
}

src/internal/components/drag-handle/index.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ const InternalDragHandle = forwardRef(
2626
onDirectionClick,
2727
triggerMode,
2828
initialShowButtons,
29+
hideButtonsOnDrag = false,
30+
clickDragThreshold = 3,
2931
...rest
3032
}: DragHandleProps,
3133
ref: React.Ref<Element>
@@ -39,6 +41,8 @@ const InternalDragHandle = forwardRef(
3941
onDirectionClick={onDirectionClick}
4042
triggerMode={triggerMode}
4143
initialShowButtons={initialShowButtons}
44+
hideButtonsOnDrag={hideButtonsOnDrag}
45+
clickDragThreshold={clickDragThreshold}
4246
>
4347
<DragHandleButton
4448
ref={ref}

src/internal/components/drag-handle/interfaces.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ export interface DragHandleProps {
2424
onDirectionClick?: (direction: DragHandleProps.Direction) => void;
2525
triggerMode?: TriggerMode;
2626
initialShowButtons?: boolean;
27+
/**
28+
* Hide the UAP buttons when dragging is active.
29+
*/
30+
hideButtonsOnDrag?: boolean;
31+
/**
32+
* Max cursor movement (in pixels) that still counts as a press rather than
33+
* a drag. Small threshold needed for usability.
34+
*/
35+
clickDragThreshold?: number;
2736
}
2837

2938
export namespace DragHandleProps {

0 commit comments

Comments
 (0)