adobe / react-spectrum

A collection of libraries and tools that help you build adaptive, accessible, and robust user experiences.
https://react-spectrum.adobe.com
Apache License 2.0
13.03k stars 1.13k forks source link

`isVirtualPointerEvent` classifies playwright `click` as drag start #5073

Open hipstersmoothie opened 1 year ago

hipstersmoothie commented 1 year ago

Provide a general summary of the issue here

I'm writing tests for a draggable collection and my tests for clicking on an item in the collection are breaking because isVirtualPointerEvent returns true for playwright click events

๐Ÿค” Expected Behavior?

Click doesn't trigger drag start in playwright

๐Ÿ˜ฏ Current Behavior

Click does trigger drag start and freezes the app

๐Ÿ’ Possible Solution

I can write my tests to work around it but would be nice to fix

๐Ÿ”ฆ Context

No response

๐Ÿ–ฅ๏ธ Steps to Reproduce

If you want I can try to make a small example but clicking on an item in any draggable collaection through playwright should work

Version

@react-aria/dnd 3.3.0

What browsers are you seeing the problem on?

Chrome

If other, please specify.

No response

What operating system are you using?

Mac

๐Ÿงข Your Company/Team

No response

๐Ÿ•ท Tracking Issue

No response

snowystinger commented 1 year ago

Can you provide all the events that Playwright triggers for a click along with the event properties? something along these lines https://github.com/adobe/react-spectrum/issues/4753#issuecomment-1629761032 ?

hipstersmoothie commented 1 year ago

onPointerDown

{
 "_reactName": "onPointerDown",
 "_targetInst": null,
 "type": "pointerdown",
 "nativeEvent": {
  "isTrusted": true
 },
 "target": "Node",
 "currentTarget": null,
 "eventPhase": 3,
 "bubbles": true,
 "cancelable": true,
 "timeStamp": 2879.800000011921,
 "defaultPrevented": false,
 "isTrusted": true,
 "view": "Window",
 "detail": 0,
 "screenX": 59,
 "screenY": 59,
 "clientX": 59,
 "clientY": 59,
 "pageX": 59,
 "pageY": 59,
 "ctrlKey": false,
 "shiftKey": false,
 "altKey": false,
 "metaKey": false,
 "button": 0,
 "buttons": 1,
 "relatedTarget": null,
 "movementX": 0,
 "movementY": 0,
 "pointerId": 1,
 "width": 1,
 "height": 1,
 "pressure": 0,
 "tangentialPressure": 0,
 "tiltX": 0,
 "tiltY": 0,
 "twist": 0,
 "pointerType": "mouse",
 "isPrimary": true
}

onMouseDown

{
 "_reactName": "onMouseDown",
 "_targetInst": null,
 "type": "mousedown",
 "nativeEvent": {
  "isTrusted": true
 },
 "target": "Node",
 "currentTarget": null,
 "eventPhase": 3,
 "bubbles": true,
 "cancelable": true,
 "timeStamp": 2879.800000011921,
 "defaultPrevented": false,
 "isTrusted": true,
 "view": "Window",
 "detail": 1,
 "screenX": 59,
 "screenY": 59,
 "clientX": 59,
 "clientY": 59,
 "pageX": 59,
 "pageY": 59,
 "ctrlKey": false,
 "shiftKey": false,
 "altKey": false,
 "metaKey": false,
 "button": 0,
 "buttons": 1,
 "relatedTarget": null,
 "movementX": 0,
 "movementY": 0
}

onPointerUp

{
 "_reactName": "onPointerUp",
 "_targetInst": null,
 "type": "pointerup",
 "nativeEvent": {
  "isTrusted": true
 },
 "target": "Node",
 "currentTarget": null,
 "eventPhase": 3,
 "bubbles": true,
 "cancelable": true,
 "timeStamp": 2879.900000035763,
 "defaultPrevented": false,
 "isTrusted": true,
 "view": "Window",
 "detail": 0,
 "screenX": 59,
 "screenY": 59,
 "clientX": 59,
 "clientY": 59,
 "pageX": 59,
 "pageY": 59,
 "ctrlKey": false,
 "shiftKey": false,
 "altKey": false,
 "metaKey": false,
 "button": 0,
 "buttons": 0,
 "relatedTarget": null,
 "movementX": 0,
 "movementY": 0,
 "pointerId": 1,
 "width": 1,
 "height": 1,
 "pressure": 0,
 "tangentialPressure": 0,
 "tiltX": 0,
 "tiltY": 0,
 "twist": 0,
 "pointerType": "mouse",
 "isPrimary": true
}

onMouseUp

{
 "_reactName": "onMouseUp",
 "_targetInst": null,
 "type": "mouseup",
 "nativeEvent": {
  "isTrusted": true
 },
 "target": "Node",
 "currentTarget": null,
 "eventPhase": 3,
 "bubbles": true,
 "cancelable": true,
 "timeStamp": 2879.900000035763,
 "defaultPrevented": false,
 "isTrusted": true,
 "view": "Window",
 "detail": 1,
 "screenX": 59,
 "screenY": 59,
 "clientX": 59,
 "clientY": 59,
 "pageX": 59,
 "pageY": 59,
 "ctrlKey": false,
 "shiftKey": false,
 "altKey": false,
 "metaKey": false,
 "button": 0,
 "buttons": 0,
 "relatedTarget": null,
 "movementX": 0,
 "movementY": 0
}

onClick

{
 "_reactName": "onClick",
 "_targetInst": null,
 "type": "click",
 "nativeEvent": {
  "isTrusted": true
 },
 "target": "Node",
 "currentTarget": null,
 "eventPhase": 3,
 "bubbles": true,
 "cancelable": true,
 "timeStamp": 2879.900000035763,
 "defaultPrevented": false,
 "isTrusted": true,
 "view": "Window",
 "detail": 1,
 "screenX": 59,
 "screenY": 59,
 "clientX": 59,
 "clientY": 59,
 "pageX": 59,
 "pageY": 59,
 "ctrlKey": false,
 "shiftKey": false,
 "altKey": false,
 "metaKey": false,
 "button": 0,
 "buttons": 0,
 "relatedTarget": null,
 "movementX": 0,
 "movementY": 0
}
snowystinger commented 1 year ago

If you used my codesandbox for that, could you try it on this one as well? https://codesandbox.io/s/determined-bassi-dnmlmw?file=/src/App.js

I ask because I suspect Playwright of firing compatibility events that it shouldn't be. This was an issue in user event as well. In this codesandbox we should only see pointer events and the click, no mouse events.

If you didn't use the codesandbox for the event data you posted above, it'd still be useful to know. Thank you!

hipstersmoothie commented 1 year ago

For future people here is a drag to that fires the right events for me. It also supports steps

export async function dragTo(
    page: Page,
    source: Locator,
    target: Locator,
    {
        steps,
        sourcePosition,
        targetPosition,
        shouldMouseUp = true,
    }: {
        steps?: number;
        /**
         * Position to drag from in source element
         * @default center of source element
         */
        sourcePosition?: { x: number; y: number };
        /**
         * Position to drag to in target element
         * @default center of target element
         */
        targetPosition?: { x: number; y: number };
        shouldMouseUp?: boolean;
    },
) {
    await test.step(`Dragging ${source} to ${target}`, async () => {
        const sourceBox = (await source.boundingBox())!;
        const targetBox = (await target.boundingBox())!;

        const sourceX = sourcePosition ? sourcePosition.x : sourceBox.width / 2;
        const sourceY = sourcePosition ? sourcePosition.y : sourceBox.height / 2;

        const startX = sourceBox.x + sourceX;
        const startY = sourceBox.y + sourceY;

        await test.step(`Start drag interaction`, async () => {
            await page.mouse.move(startX, startY);
            await page.mouse.down();
            await source.dispatchEvent('dragstart', {
                clientX: startX,
                clientY: startY,
            });
        });

        const dataTransfer = await page.evaluateHandle(() => new DataTransfer());
        const targetBaseX = targetPosition ? targetPosition.x : targetBox.width / 2;
        const targetBaseY = targetPosition ? targetPosition.y : targetBox.height / 2;
        const targetPoint = {
            clientX: targetBox.x + targetBaseX,
            clientY: targetBox.y + targetBaseY,
        };
        const dummyEvent = {
            clientX: targetPoint.clientX,
            clientY: targetPoint.clientY,
            dataTransfer,
        };

        await test.step(`Execute drag interaction`, async () => {
            await source.dispatchEvent('drag', dummyEvent);
            await target.dispatchEvent('dragover', dummyEvent);
            await page.mouse.move(targetPoint.clientX, targetPoint.clientY, {
                steps,
            });
        });

        await test.step(`End drag interaction`, async () => {
            if (shouldMouseUp) {
                await page.mouse.up();
                await target.dispatchEvent('drop', dummyEvent);
            }
        });
    });
}
ritz078 commented 8 months ago

I am facing similar issue. Even the above code is not working for me.

@snowystinger Here are the events playwright fires for your codesandbox:

Screenshot 2024-02-19 at 4 02 44โ€ฏAM Screenshot 2024-02-19 at 4 03 03โ€ฏAM Screenshot 2024-02-19 at 4 03 22โ€ฏAM
Georgegriff commented 1 week ago

Ah it seems i've just come across a similar issue with long press, documented in here https://github.com/adobe/react-spectrum/issues/7306

Had to look up that isVirtualPointerEvent again to see how i could effectively trick this library into working

 await button.dispatchEvent("pointerdown", {
        pressure: 1,
        width: 5,
        height: 5,
        pointerType: "mouse",
      });

      await page.waitForTimeout(1000);
      await button.dispatchEvent("pointerup", {
        pressure: 1,
        width: 5,
        height: 5,
        pointerType: "mouse",
      });

Horrible workaround