From df7a04db2a3d8c131e0f955071c9bf276937b17e Mon Sep 17 00:00:00 2001 From: Brendan Forster Date: Fri, 13 Dec 2024 00:01:58 -0800 Subject: [PATCH 01/10] draggable --- src/internal/components/Draggable.test.tsx | 93 ++++++++++++++++++++++ src/internal/components/Draggable.tsx | 93 ++++++++++++++++++++++ 2 files changed, 186 insertions(+) create mode 100644 src/internal/components/Draggable.test.tsx create mode 100644 src/internal/components/Draggable.tsx diff --git a/src/internal/components/Draggable.test.tsx b/src/internal/components/Draggable.test.tsx new file mode 100644 index 0000000000..3e847f4873 --- /dev/null +++ b/src/internal/components/Draggable.test.tsx @@ -0,0 +1,93 @@ +import { useCallback, useEffect, useState } from 'react'; +import { cn } from '../../styles/theme'; + +type DraggableProps = { + children: React.ReactNode; + gridSize?: number; + startingPosition?: { x: number; y: number }; // TODO [BOE-886]: make this based on the parent component's position +}; + +export default function Draggable({ + children, + gridSize = 1, + startingPosition = { x: 20, y: 20 }, +}: DraggableProps) { + const [position, setPosition] = useState(startingPosition); + const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 }); + const [isDragging, setIsDragging] = useState(false); + + const snapToGrid = useCallback( + (positionValue: number) => { + return Math.round(positionValue / gridSize) * gridSize; + }, + [gridSize], + ); + + const handleDragStart = (e: React.MouseEvent | React.TouchEvent) => { + setIsDragging(true); + + const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX; + const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY; + + setDragOffset({ + x: clientX - position.x, + y: clientY - position.y, + }); + }; + + useEffect(() => { + if (!isDragging) { + return; + } + + const handleGlobalMove = (e: MouseEvent | TouchEvent) => { + const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX; + const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY; + + setPosition({ + x: clientX - dragOffset.x, + y: clientY - dragOffset.y, + }); + }; + + const handleGlobalEnd = () => { + setPosition((prev) => ({ + x: snapToGrid(prev.x), + y: snapToGrid(prev.y), + })); + setIsDragging(false); + }; + + document.addEventListener('mousemove', handleGlobalMove); + document.addEventListener('touchmove', handleGlobalMove); + document.addEventListener('mouseup', handleGlobalEnd); + document.addEventListener('touchend', handleGlobalEnd); + + return () => { + document.removeEventListener('mousemove', handleGlobalMove); + document.removeEventListener('touchmove', handleGlobalMove); + document.removeEventListener('mouseup', handleGlobalEnd); + document.removeEventListener('touchend', handleGlobalEnd); + }; + }, [isDragging, dragOffset, snapToGrid]); + + return ( +
+ {children} +
+ ); +} diff --git a/src/internal/components/Draggable.tsx b/src/internal/components/Draggable.tsx new file mode 100644 index 0000000000..3e847f4873 --- /dev/null +++ b/src/internal/components/Draggable.tsx @@ -0,0 +1,93 @@ +import { useCallback, useEffect, useState } from 'react'; +import { cn } from '../../styles/theme'; + +type DraggableProps = { + children: React.ReactNode; + gridSize?: number; + startingPosition?: { x: number; y: number }; // TODO [BOE-886]: make this based on the parent component's position +}; + +export default function Draggable({ + children, + gridSize = 1, + startingPosition = { x: 20, y: 20 }, +}: DraggableProps) { + const [position, setPosition] = useState(startingPosition); + const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 }); + const [isDragging, setIsDragging] = useState(false); + + const snapToGrid = useCallback( + (positionValue: number) => { + return Math.round(positionValue / gridSize) * gridSize; + }, + [gridSize], + ); + + const handleDragStart = (e: React.MouseEvent | React.TouchEvent) => { + setIsDragging(true); + + const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX; + const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY; + + setDragOffset({ + x: clientX - position.x, + y: clientY - position.y, + }); + }; + + useEffect(() => { + if (!isDragging) { + return; + } + + const handleGlobalMove = (e: MouseEvent | TouchEvent) => { + const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX; + const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY; + + setPosition({ + x: clientX - dragOffset.x, + y: clientY - dragOffset.y, + }); + }; + + const handleGlobalEnd = () => { + setPosition((prev) => ({ + x: snapToGrid(prev.x), + y: snapToGrid(prev.y), + })); + setIsDragging(false); + }; + + document.addEventListener('mousemove', handleGlobalMove); + document.addEventListener('touchmove', handleGlobalMove); + document.addEventListener('mouseup', handleGlobalEnd); + document.addEventListener('touchend', handleGlobalEnd); + + return () => { + document.removeEventListener('mousemove', handleGlobalMove); + document.removeEventListener('touchmove', handleGlobalMove); + document.removeEventListener('mouseup', handleGlobalEnd); + document.removeEventListener('touchend', handleGlobalEnd); + }; + }, [isDragging, dragOffset, snapToGrid]); + + return ( +
+ {children} +
+ ); +} From df0b18dfaa783dedd05815e42a809c97da46bd91 Mon Sep 17 00:00:00 2001 From: Brendan Forster Date: Fri, 13 Dec 2024 00:07:14 -0800 Subject: [PATCH 02/10] fix: tests --- src/internal/components/Draggable.test.tsx | 233 +++++++++++++-------- 1 file changed, 141 insertions(+), 92 deletions(-) diff --git a/src/internal/components/Draggable.test.tsx b/src/internal/components/Draggable.test.tsx index 3e847f4873..3f701bd48f 100644 --- a/src/internal/components/Draggable.test.tsx +++ b/src/internal/components/Draggable.test.tsx @@ -1,93 +1,142 @@ -import { useCallback, useEffect, useState } from 'react'; -import { cn } from '../../styles/theme'; - -type DraggableProps = { - children: React.ReactNode; - gridSize?: number; - startingPosition?: { x: number; y: number }; // TODO [BOE-886]: make this based on the parent component's position -}; - -export default function Draggable({ - children, - gridSize = 1, - startingPosition = { x: 20, y: 20 }, -}: DraggableProps) { - const [position, setPosition] = useState(startingPosition); - const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 }); - const [isDragging, setIsDragging] = useState(false); - - const snapToGrid = useCallback( - (positionValue: number) => { - return Math.round(positionValue / gridSize) * gridSize; - }, - [gridSize], - ); - - const handleDragStart = (e: React.MouseEvent | React.TouchEvent) => { - setIsDragging(true); - - const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX; - const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY; - - setDragOffset({ - x: clientX - position.x, - y: clientY - position.y, +import { fireEvent, render, screen } from '@testing-library/react'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import Draggable from './Draggable'; + +describe('Draggable', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('renders children correctly', () => { + render( + +
Drag me
+
, + ); + expect(screen.getByTestId('ockDraggable')).toBeInTheDocument(); + }); + + it('starts at the default position if no starting position is provided', () => { + render( + +
Drag me
+
, + ); + const draggable = screen.getByTestId('ockDraggable'); + expect(draggable).toHaveStyle({ left: '20px', top: '20px' }); + }); + + it('starts at the specified position if starting position is provided', () => { + render( + +
Drag me
+
, + ); + const draggable = screen.getByTestId('ockDraggable'); + expect(draggable).toHaveStyle({ left: '100px', top: '100px' }); + }); + + it('changes cursor style when dragging', () => { + render( + +
Drag me
+
, + ); + const draggable = screen.getByTestId('ockDraggable'); + expect(draggable).toHaveClass('cursor-grab'); + + fireEvent.mouseDown(draggable); + expect(draggable).toHaveClass('cursor-grabbing'); + + fireEvent.mouseUp(draggable); + expect(draggable).toHaveClass('cursor-grab'); + }); + + it('snaps to grid when dragging ends', () => { + render( + +
Drag me
+
, + ); + + const draggable = screen.getByTestId('ockDraggable'); + fireEvent.mouseDown(draggable, { clientX: 0, clientY: 0 }); + fireEvent.mouseMove(document, { clientX: 14, clientY: 16 }); + fireEvent.mouseUp(document); + expect(draggable).toHaveStyle({ left: '10px', top: '20px' }); + }); + + it('handles touch events', () => { + render( + +
Drag me
+
, + ); + const draggable = screen.getByTestId('ockDraggable'); + + fireEvent.touchStart(draggable, { + touches: [{ clientX: 0, clientY: 0 }], + }); + expect(draggable).toHaveClass('cursor-grabbing'); + + fireEvent.touchMove(document, { + touches: [{ clientX: 50, clientY: 50 }], }); - }; - - useEffect(() => { - if (!isDragging) { - return; - } - - const handleGlobalMove = (e: MouseEvent | TouchEvent) => { - const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX; - const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY; - - setPosition({ - x: clientX - dragOffset.x, - y: clientY - dragOffset.y, - }); - }; - - const handleGlobalEnd = () => { - setPosition((prev) => ({ - x: snapToGrid(prev.x), - y: snapToGrid(prev.y), - })); - setIsDragging(false); - }; - - document.addEventListener('mousemove', handleGlobalMove); - document.addEventListener('touchmove', handleGlobalMove); - document.addEventListener('mouseup', handleGlobalEnd); - document.addEventListener('touchend', handleGlobalEnd); - - return () => { - document.removeEventListener('mousemove', handleGlobalMove); - document.removeEventListener('touchmove', handleGlobalMove); - document.removeEventListener('mouseup', handleGlobalEnd); - document.removeEventListener('touchend', handleGlobalEnd); - }; - }, [isDragging, dragOffset, snapToGrid]); - - return ( -
- {children} -
- ); -} + + fireEvent.touchEnd(document); + expect(draggable).toHaveClass('cursor-grab'); + }); + + it('calculates drag offset correctly', () => { + render( + +
Drag me
+
, + ); + const draggable = screen.getByTestId('ockDraggable'); + + fireEvent.mouseDown(draggable, { clientX: 60, clientY: 70 }); + expect(draggable).toHaveStyle({ left: '50px', top: '50px' }); + + // Move with the calculated offset + fireEvent.mouseMove(document, { clientX: 80, clientY: 90 }); + expect(draggable).toHaveStyle({ left: '70px', top: '70px' }); + }); + + it('updates position during drag movement', () => { + render( + +
Drag me
+
, + ); + const draggable = screen.getByTestId('ockDraggable'); + + fireEvent.mouseDown(draggable, { clientX: 0, clientY: 0 }); + + // Multiple move events + fireEvent.mouseMove(document, { clientX: 50, clientY: 50 }); + expect(draggable).toHaveStyle({ left: '50px', top: '50px' }); + + fireEvent.mouseMove(document, { clientX: 100, clientY: 75 }); + expect(draggable).toHaveStyle({ left: '100px', top: '75px' }); + }); + + it('cleans up event listeners when unmounted during drag', () => { + const { unmount } = render( + +
Drag me
+
, + ); + const draggable = screen.getByTestId('ockDraggable'); + + // Start dragging + fireEvent.mouseDown(draggable, { clientX: 0, clientY: 0 }); + + // Unmount while dragging + unmount(); + + // Verify no errors when moving/ending after unmount + fireEvent.mouseMove(document, { clientX: 50, clientY: 50 }); + fireEvent.mouseUp(document); + }); +}); From d7b435cde29c65e78fca39c727d3fc8bb664b628 Mon Sep 17 00:00:00 2001 From: Brendan Forster Date: Sun, 15 Dec 2024 12:04:33 +0900 Subject: [PATCH 03/10] pointer events, optional snapToGrid, update tests --- src/internal/components/Draggable.test.tsx | 91 +++++++++++++++------- src/internal/components/Draggable.tsx | 43 ++++------ 2 files changed, 78 insertions(+), 56 deletions(-) diff --git a/src/internal/components/Draggable.test.tsx b/src/internal/components/Draggable.test.tsx index 3f701bd48f..35b958f4f8 100644 --- a/src/internal/components/Draggable.test.tsx +++ b/src/internal/components/Draggable.test.tsx @@ -1,4 +1,5 @@ import { fireEvent, render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import Draggable from './Draggable'; @@ -45,27 +46,51 @@ describe('Draggable', () => { const draggable = screen.getByTestId('ockDraggable'); expect(draggable).toHaveClass('cursor-grab'); - fireEvent.mouseDown(draggable); + fireEvent.pointerDown(draggable); expect(draggable).toHaveClass('cursor-grabbing'); - fireEvent.mouseUp(draggable); + fireEvent.pointerUp(draggable); expect(draggable).toHaveClass('cursor-grab'); }); - it('snaps to grid when dragging ends', () => { - render( + it('snaps to grid when dragging ends if enableSnapToGrid is true', async () => { + const user = userEvent.setup(); + const { rerender } = render(
Drag me
, ); const draggable = screen.getByTestId('ockDraggable'); - fireEvent.mouseDown(draggable, { clientX: 0, clientY: 0 }); - fireEvent.mouseMove(document, { clientX: 14, clientY: 16 }); - fireEvent.mouseUp(document); + await user.pointer([ + { keys: '[MouseLeft>]', target: draggable }, + { coords: { x: 14, y: 16 } }, + { keys: '[/MouseLeft]' }, + ]); expect(draggable).toHaveStyle({ left: '10px', top: '20px' }); }); + it('does not snap to grid when dragging ends if enableSnapToGrid is false', async () => { + const user = userEvent.setup(); + render( + +
Drag me
+
, + ); + + const draggable = screen.getByTestId('ockDraggable'); + await user.pointer([ + { keys: '[MouseLeft>]', target: draggable }, + { coords: { x: 14, y: 16 } }, + { keys: '[/MouseLeft]' }, + ]); + expect(draggable).toHaveStyle({ left: '14px', top: '16px' }); + }); + it('handles touch events', () => { render( @@ -74,20 +99,23 @@ describe('Draggable', () => { ); const draggable = screen.getByTestId('ockDraggable'); - fireEvent.touchStart(draggable, { - touches: [{ clientX: 0, clientY: 0 }], + fireEvent.pointerDown(draggable, { + clientX: 0, + clientY: 0, }); expect(draggable).toHaveClass('cursor-grabbing'); - fireEvent.touchMove(document, { - touches: [{ clientX: 50, clientY: 50 }], + fireEvent.pointerMove(document, { + clientX: 50, + clientY: 50, }); - fireEvent.touchEnd(document); + fireEvent.pointerUp(document); expect(draggable).toHaveClass('cursor-grab'); }); - it('calculates drag offset correctly', () => { + it('calculates drag offset correctly', async () => { + const user = userEvent.setup(); render(
Drag me
@@ -95,15 +123,16 @@ describe('Draggable', () => { ); const draggable = screen.getByTestId('ockDraggable'); - fireEvent.mouseDown(draggable, { clientX: 60, clientY: 70 }); - expect(draggable).toHaveStyle({ left: '50px', top: '50px' }); - - // Move with the calculated offset - fireEvent.mouseMove(document, { clientX: 80, clientY: 90 }); + await user.pointer([ + { keys: '[MouseLeft>]', target: draggable, coords: { x: 60, y: 70 } }, + { coords: { x: 80, y: 90 } }, + { keys: '[/MouseLeft]' }, + ]); expect(draggable).toHaveStyle({ left: '70px', top: '70px' }); }); - it('updates position during drag movement', () => { + it('updates position during drag movement', async () => { + const user = userEvent.setup(); render(
Drag me
@@ -111,13 +140,16 @@ describe('Draggable', () => { ); const draggable = screen.getByTestId('ockDraggable'); - fireEvent.mouseDown(draggable, { clientX: 0, clientY: 0 }); - - // Multiple move events - fireEvent.mouseMove(document, { clientX: 50, clientY: 50 }); + await user.pointer([ + { keys: '[MouseLeft>]', target: draggable, coords: { x: 0, y: 0 } }, + { coords: { x: 50, y: 50 } }, + ]); expect(draggable).toHaveStyle({ left: '50px', top: '50px' }); - fireEvent.mouseMove(document, { clientX: 100, clientY: 75 }); + await user.pointer([ + { coords: { x: 100, y: 75 } }, + ]) + expect(draggable).toHaveStyle({ left: '100px', top: '75px' }); }); @@ -129,14 +161,13 @@ describe('Draggable', () => { ); const draggable = screen.getByTestId('ockDraggable'); - // Start dragging - fireEvent.mouseDown(draggable, { clientX: 0, clientY: 0 }); + const removeSpy = vi.spyOn(document, 'removeEventListener'); + + fireEvent.pointerDown(draggable, { clientX: 0, clientY: 0 }); - // Unmount while dragging unmount(); - // Verify no errors when moving/ending after unmount - fireEvent.mouseMove(document, { clientX: 50, clientY: 50 }); - fireEvent.mouseUp(document); + expect(removeSpy).toHaveBeenCalledWith('pointermove', expect.any(Function)); + expect(removeSpy).toHaveBeenCalledWith('pointerup', expect.any(Function)); }); }); diff --git a/src/internal/components/Draggable.tsx b/src/internal/components/Draggable.tsx index 3e847f4873..fe999b0336 100644 --- a/src/internal/components/Draggable.tsx +++ b/src/internal/components/Draggable.tsx @@ -4,13 +4,15 @@ import { cn } from '../../styles/theme'; type DraggableProps = { children: React.ReactNode; gridSize?: number; - startingPosition?: { x: number; y: number }; // TODO [BOE-886]: make this based on the parent component's position + startingPosition?: { x: number; y: number }; + enableSnapToGrid?: boolean; }; export default function Draggable({ children, gridSize = 1, startingPosition = { x: 20, y: 20 }, + enableSnapToGrid = true, }: DraggableProps) { const [position, setPosition] = useState(startingPosition); const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 }); @@ -23,15 +25,12 @@ export default function Draggable({ [gridSize], ); - const handleDragStart = (e: React.MouseEvent | React.TouchEvent) => { + const handleDragStart = (e: React.PointerEvent) => { setIsDragging(true); - const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX; - const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY; - setDragOffset({ - x: clientX - position.x, - y: clientY - position.y, + x: e.clientX - position.x, + y: e.clientY - position.y, }); }; @@ -40,36 +39,29 @@ export default function Draggable({ return; } - const handleGlobalMove = (e: MouseEvent | TouchEvent) => { - const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX; - const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY; - + const handleGlobalMove = (e: PointerEvent) => { setPosition({ - x: clientX - dragOffset.x, - y: clientY - dragOffset.y, + x: e.clientX - dragOffset.x, + y: e.clientY - dragOffset.y, }); }; const handleGlobalEnd = () => { setPosition((prev) => ({ - x: snapToGrid(prev.x), - y: snapToGrid(prev.y), + x: enableSnapToGrid ? snapToGrid(prev.x) : prev.x, + y: enableSnapToGrid ? snapToGrid(prev.y) : prev.y, })); setIsDragging(false); }; - document.addEventListener('mousemove', handleGlobalMove); - document.addEventListener('touchmove', handleGlobalMove); - document.addEventListener('mouseup', handleGlobalEnd); - document.addEventListener('touchend', handleGlobalEnd); + document.addEventListener('pointermove', handleGlobalMove); + document.addEventListener('pointerup', handleGlobalEnd); return () => { - document.removeEventListener('mousemove', handleGlobalMove); - document.removeEventListener('touchmove', handleGlobalMove); - document.removeEventListener('mouseup', handleGlobalEnd); - document.removeEventListener('touchend', handleGlobalEnd); + document.removeEventListener('pointermove', handleGlobalMove); + document.removeEventListener('pointerup', handleGlobalEnd); }; - }, [isDragging, dragOffset, snapToGrid]); + }, [isDragging, dragOffset, snapToGrid, enableSnapToGrid]); return (
{children}
From 8dc8e184011c43bf5dcd9db5e791070c946ec168 Mon Sep 17 00:00:00 2001 From: Brendan Forster Date: Sun, 15 Dec 2024 12:05:32 +0900 Subject: [PATCH 04/10] fix: inters --- src/internal/components/Draggable.test.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/internal/components/Draggable.test.tsx b/src/internal/components/Draggable.test.tsx index 35b958f4f8..098ffc8ec6 100644 --- a/src/internal/components/Draggable.test.tsx +++ b/src/internal/components/Draggable.test.tsx @@ -55,7 +55,7 @@ describe('Draggable', () => { it('snaps to grid when dragging ends if enableSnapToGrid is true', async () => { const user = userEvent.setup(); - const { rerender } = render( + render(
Drag me
, @@ -146,9 +146,7 @@ describe('Draggable', () => { ]); expect(draggable).toHaveStyle({ left: '50px', top: '50px' }); - await user.pointer([ - { coords: { x: 100, y: 75 } }, - ]) + await user.pointer([{ coords: { x: 100, y: 75 } }]); expect(draggable).toHaveStyle({ left: '100px', top: '75px' }); }); From aebae8cea53e91c89044e05432aa7eb4e74be10a Mon Sep 17 00:00:00 2001 From: Brendan Forster Date: Tue, 17 Dec 2024 20:56:16 +0900 Subject: [PATCH 05/10] zindex levels --- src/internal/components/Draggable.tsx | 5 +++-- src/styles/constants.ts | 8 ++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 src/styles/constants.ts diff --git a/src/internal/components/Draggable.tsx b/src/internal/components/Draggable.tsx index fe999b0336..fb82c827e4 100644 --- a/src/internal/components/Draggable.tsx +++ b/src/internal/components/Draggable.tsx @@ -1,5 +1,6 @@ import { useCallback, useEffect, useState } from 'react'; -import { cn } from '../../styles/theme'; +import { zIndex } from '@/styles/constants'; +import { cn } from '@/styles/theme'; type DraggableProps = { children: React.ReactNode; @@ -68,12 +69,12 @@ export default function Draggable({ data-testid="ockDraggable" className={cn( 'fixed select-none', + zIndex.modal, isDragging ? 'cursor-grabbing' : 'cursor-grab', )} style={{ left: `${position.x}px`, top: `${position.y}px`, - zIndex: 1000, touchAction: 'none', }} onPointerDown={handleDragStart} diff --git a/src/styles/constants.ts b/src/styles/constants.ts new file mode 100644 index 0000000000..951f0528d7 --- /dev/null +++ b/src/styles/constants.ts @@ -0,0 +1,8 @@ +export const zIndex = { + base: 0, + navigation: 1, + dropdown: 10, + tooltip: 20, + modal: 40, + notification: 50 +} From bc1387109a7d67141b349cdae644d31a80974a87 Mon Sep 17 00:00:00 2001 From: Brendan Forster Date: Tue, 17 Dec 2024 20:56:55 +0900 Subject: [PATCH 06/10] fix: linters --- src/internal/components/Draggable.tsx | 2 +- src/styles/constants.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/internal/components/Draggable.tsx b/src/internal/components/Draggable.tsx index fb82c827e4..be98ecf16b 100644 --- a/src/internal/components/Draggable.tsx +++ b/src/internal/components/Draggable.tsx @@ -1,6 +1,6 @@ -import { useCallback, useEffect, useState } from 'react'; import { zIndex } from '@/styles/constants'; import { cn } from '@/styles/theme'; +import { useCallback, useEffect, useState } from 'react'; type DraggableProps = { children: React.ReactNode; diff --git a/src/styles/constants.ts b/src/styles/constants.ts index 951f0528d7..a7d2e93854 100644 --- a/src/styles/constants.ts +++ b/src/styles/constants.ts @@ -4,5 +4,5 @@ export const zIndex = { dropdown: 10, tooltip: 20, modal: 40, - notification: 50 -} + notification: 50, +}; From dcfaf2394a6fa41c3f936e221cc1df847f5a037a Mon Sep 17 00:00:00 2001 From: Brendan Forster Date: Tue, 17 Dec 2024 21:07:48 +0900 Subject: [PATCH 07/10] fix: imports --- src/internal/components/Draggable.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/internal/components/Draggable.tsx b/src/internal/components/Draggable.tsx index be98ecf16b..02b836f364 100644 --- a/src/internal/components/Draggable.tsx +++ b/src/internal/components/Draggable.tsx @@ -1,6 +1,6 @@ -import { zIndex } from '@/styles/constants'; -import { cn } from '@/styles/theme'; import { useCallback, useEffect, useState } from 'react'; +import { zIndex } from '../../styles/constants'; +import { cn } from '../../styles/theme'; type DraggableProps = { children: React.ReactNode; From 4a582a3f5362a0ac6e1a075a88a6e08561fd8326 Mon Sep 17 00:00:00 2001 From: Brendan Forster Date: Tue, 17 Dec 2024 23:34:37 +0900 Subject: [PATCH 08/10] enableSnapToGrid defaulted to false --- src/internal/components/Draggable.test.tsx | 2 +- src/internal/components/Draggable.tsx | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/internal/components/Draggable.test.tsx b/src/internal/components/Draggable.test.tsx index 098ffc8ec6..833e1d7bd6 100644 --- a/src/internal/components/Draggable.test.tsx +++ b/src/internal/components/Draggable.test.tsx @@ -56,7 +56,7 @@ describe('Draggable', () => { it('snaps to grid when dragging ends if enableSnapToGrid is true', async () => { const user = userEvent.setup(); render( - +
Drag me
, ); diff --git a/src/internal/components/Draggable.tsx b/src/internal/components/Draggable.tsx index 02b836f364..c3dda2e3c8 100644 --- a/src/internal/components/Draggable.tsx +++ b/src/internal/components/Draggable.tsx @@ -13,7 +13,7 @@ export default function Draggable({ children, gridSize = 1, startingPosition = { x: 20, y: 20 }, - enableSnapToGrid = true, + enableSnapToGrid = false, }: DraggableProps) { const [position, setPosition] = useState(startingPosition); const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 }); @@ -68,14 +68,13 @@ export default function Draggable({
From a95a8b718f778794763a302c60b6b21389343c39 Mon Sep 17 00:00:00 2001 From: Brendan Forster Date: Tue, 17 Dec 2024 23:40:08 +0900 Subject: [PATCH 09/10] fix: linters --- src/internal/components/Draggable.test.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/internal/components/Draggable.test.tsx b/src/internal/components/Draggable.test.tsx index 833e1d7bd6..fdc50c4d36 100644 --- a/src/internal/components/Draggable.test.tsx +++ b/src/internal/components/Draggable.test.tsx @@ -56,7 +56,11 @@ describe('Draggable', () => { it('snaps to grid when dragging ends if enableSnapToGrid is true', async () => { const user = userEvent.setup(); render( - +
Drag me
, ); From 91761c49189c6a9ab576f4f441f223f97ff03099 Mon Sep 17 00:00:00 2001 From: Brendan Forster Date: Tue, 17 Dec 2024 23:56:08 +0900 Subject: [PATCH 10/10] updated names --- src/internal/components/Draggable.test.tsx | 4 ++-- src/internal/components/Draggable.tsx | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/internal/components/Draggable.test.tsx b/src/internal/components/Draggable.test.tsx index fdc50c4d36..97ffa9ab07 100644 --- a/src/internal/components/Draggable.test.tsx +++ b/src/internal/components/Draggable.test.tsx @@ -59,7 +59,7 @@ describe('Draggable', () => {
Drag me
, @@ -80,7 +80,7 @@ describe('Draggable', () => {
Drag me
, diff --git a/src/internal/components/Draggable.tsx b/src/internal/components/Draggable.tsx index c3dda2e3c8..5f6c61711a 100644 --- a/src/internal/components/Draggable.tsx +++ b/src/internal/components/Draggable.tsx @@ -6,20 +6,20 @@ type DraggableProps = { children: React.ReactNode; gridSize?: number; startingPosition?: { x: number; y: number }; - enableSnapToGrid?: boolean; + snapToGrid?: boolean; }; export default function Draggable({ children, gridSize = 1, startingPosition = { x: 20, y: 20 }, - enableSnapToGrid = false, + snapToGrid = false, }: DraggableProps) { const [position, setPosition] = useState(startingPosition); const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 }); const [isDragging, setIsDragging] = useState(false); - const snapToGrid = useCallback( + const calculateSnapToGrid = useCallback( (positionValue: number) => { return Math.round(positionValue / gridSize) * gridSize; }, @@ -49,8 +49,8 @@ export default function Draggable({ const handleGlobalEnd = () => { setPosition((prev) => ({ - x: enableSnapToGrid ? snapToGrid(prev.x) : prev.x, - y: enableSnapToGrid ? snapToGrid(prev.y) : prev.y, + x: snapToGrid ? calculateSnapToGrid(prev.x) : prev.x, + y: snapToGrid ? calculateSnapToGrid(prev.y) : prev.y, })); setIsDragging(false); }; @@ -62,7 +62,7 @@ export default function Draggable({ document.removeEventListener('pointermove', handleGlobalMove); document.removeEventListener('pointerup', handleGlobalEnd); }; - }, [isDragging, dragOffset, snapToGrid, enableSnapToGrid]); + }, [isDragging, dragOffset, snapToGrid, calculateSnapToGrid]); return (