From db4c863b7b582bd260817daee3fdf748889001b8 Mon Sep 17 00:00:00 2001 From: gabriellsh Date: Mon, 26 May 2025 18:42:39 -0300 Subject: [PATCH 1/2] Implement tests --- .../useDraggable.stories.tsx | 191 +++++++++++++++--- 1 file changed, 168 insertions(+), 23 deletions(-) diff --git a/packages/ui-voip/src/components/VoipPopupDraggable/useDraggable.stories.tsx b/packages/ui-voip/src/components/VoipPopupDraggable/useDraggable.stories.tsx index 0f33027048071..d1f3534a791c1 100644 --- a/packages/ui-voip/src/components/VoipPopupDraggable/useDraggable.stories.tsx +++ b/packages/ui-voip/src/components/VoipPopupDraggable/useDraggable.stories.tsx @@ -1,9 +1,10 @@ import { Box, Button } from '@rocket.chat/fuselage'; import type { Meta, StoryObj } from '@storybook/react'; -import { within, fireEvent, waitFor, expect } from '@storybook/test'; -import { useEffect, useState, type Ref } from 'react'; +import { within, fireEvent, waitFor, expect, userEvent } from '@storybook/test'; +import { useEffect, useLayoutEffect, useState, type Ref } from 'react'; import { useDraggable, DEFAULT_BOUNDING_ELEMENT_OPTIONS } from './DraggableCore'; +import VoipPopupPortal from '../VoipPopupPortal'; class BoundingBoxResizeEvent extends Event { constructor( @@ -27,6 +28,48 @@ const isBoundingBoxResizeEvent = (event: unknown): event is BoundingBoxResizeEve const BOUNDING_SIZE = 1000; +const DraggableElement = ({ + draggableRef, + handleRef, + onClick, + onClickResize, + backgroundColor, + height, + width, +}: { + draggableRef: Ref; + handleRef: Ref; + onClick?: () => void; + onClickResize?: () => void; + backgroundColor?: string; + height?: number; + width?: number; +}) => { + return ( + + + {!!onClick && ( + + )} + {!!onClickResize && ( + + )} + + ); +}; + const DraggableBase = ({ boundingRef, draggableRef, @@ -75,27 +118,15 @@ const DraggableBase = ({ style={{ overflow: 'hidden' }} data-testid='bounding-box' > - - - {!!onClick && ( - - )} - {!!onClickResize && ( - - )} - + ); }; @@ -166,6 +197,7 @@ type Story = StoryObj; export const Default: Story = {}; +// TODO: use `userEvent` for all tests const moveHelper = async (handle: HTMLElement, offset: { x: number; y: number }) => { const handleRect = handle.getBoundingClientRect(); // Grab the middle of the handle @@ -348,3 +380,116 @@ export const ElementUpdates: Story = { }); }, }; + +const getLorem = (size: number) => { + return Array.from({ length: size }, (_, i) => ( + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad + minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in + reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in + culpa qui officia deserunt mollit anim id est laborum. + + )); +}; + +// Mocked page with scrollable content and text +const MockedPage = () => { + const [draggableRef, boundingRef, handleRef] = useDraggable(); + + useLayoutEffect(() => { + boundingRef(document.body); + + // Storybook root grows to wrap the content + // This is needed in order to mock a page with a scrollbar + // There might be a better solution but it + const root = document.getElementById('storybook-root'); + if (root) { + root.style.width = '100%'; + root.style.height = '100%'; + } + }, [boundingRef]); + + const overflowBoxProps = { + display: 'flex', + h: 'full', + flexGrow: 1, + flexShrink: 0, + flexBasis: '33%', + overflowY: 'scroll', + overflowX: 'hidden', + } as const; + + return ( + <> + + + + {getLorem(100)} + + + + + {getLorem(100)} + + + + + {getLorem(100)} + + + + + + + + ); +}; + +export const ScrollablePage: Story = { + render: () => , + play: async ({ canvasElement, step }) => { + const canvas = within(canvasElement.parentElement || canvasElement); + + await step('should not allow selecting text when dragging an element', async () => { + const user = userEvent.setup({ delay: 0 }); + const handle = await canvas.findByTestId('drag-handle'); + const draggable = await canvas.findByTestId('draggable-box'); + + await expect(handle).toBeInTheDocument(); + await expect(draggable).toBeInTheDocument(); + + const handleRect = handle.getBoundingClientRect(); + // Grab the middle of the handle + const startX = handleRect.left + handleRect.width / 2; + const startY = handleRect.top + handleRect.height / 2; + + await user.pointer([ + { + target: handle, + keys: '[MouseLeft>]', + coords: { + x: startX, + y: startY, + }, + }, + { + // we need to set offset and target to trigger text selection + // if target or offset is not set, the issue doesn't happen + // I believe this is due to events being simulated and not exact mouse interaction + target: document.documentElement, + offset: 0, + pointerName: 'mouse', + coords: { + x: startX, + y: -10, + }, + }, + { + keys: '[/MouseLeft]', + }, + ]); + + expect(document.getSelection()?.toString()).toBe(''); + }); + }, +}; From e30b579c315e2dcaf7d07c8d2069147f8047f04f Mon Sep 17 00:00:00 2001 From: gabriellsh Date: Mon, 26 May 2025 18:42:56 -0300 Subject: [PATCH 2/2] prevent text selection when dragging --- .../ui-voip/src/components/VoipPopupDraggable/DraggableCore.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/ui-voip/src/components/VoipPopupDraggable/DraggableCore.ts b/packages/ui-voip/src/components/VoipPopupDraggable/DraggableCore.ts index 08dff670d3015..ec64041f40464 100644 --- a/packages/ui-voip/src/components/VoipPopupDraggable/DraggableCore.ts +++ b/packages/ui-voip/src/components/VoipPopupDraggable/DraggableCore.ts @@ -353,6 +353,7 @@ class HandleDomElement if (!element) { return; } + event.preventDefault(); this.emit('grab', [getPointerEventCoordinates(event), element.getBoundingClientRect()]); };