Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simulate drag and drop #440

Closed
ipap360 opened this issue Sep 1, 2020 · 11 comments
Closed

Simulate drag and drop #440

ipap360 opened this issue Sep 1, 2020 · 11 comments
Labels
new event New event to be included in the API

Comments

@ipap360
Copy link

ipap360 commented Sep 1, 2020

We have some a custom drag-n-drop multi select form control in our project and we've added a small helper to simulate drag and drop events (we found the code posted somewhere and we tweaked it a bit). Our project is using React DnD but I believe that the helper would apply to any generic html5 DnD implementation.

I wonder whether this is something that would be useful to add and maintain here.

Relevant code or config

const fireMouseEvent = function (
    type: string,
    elem: EventTarget,
    centerX: number,
    centerY: number
) {
    const evt = document.createEvent('MouseEvents');
    evt.initMouseEvent(
        type,
        true,
        true,
        window,
        1,
        1,
        1,
        centerX,
        centerY,
        false,
        false,
        false,
        false,
        0,
        elem
    );
    return elem.dispatchEvent(evt);
};

export const dragAndDrop = (elemDrag: HTMLElement, elemDrop: HTMLElement) => {
    act(() => {
        // calculate positions
        let pos = elemDrag.getBoundingClientRect();
        const center1X = Math.floor((pos.left + pos.right) / 2);
        const center1Y = Math.floor((pos.top + pos.bottom) / 2);

        pos = elemDrop.getBoundingClientRect();
        const center2X = Math.floor((pos.left + pos.right) / 2);
        const center2Y = Math.floor((pos.top + pos.bottom) / 2);

        // mouse over dragged element and mousedown
        fireMouseEvent('mousemove', elemDrag, center1X, center1Y);
        fireMouseEvent('mouseenter', elemDrag, center1X, center1Y);
        fireMouseEvent('mouseover', elemDrag, center1X, center1Y);
        fireMouseEvent('mousedown', elemDrag, center1X, center1Y);

        // start dragging process over to drop target
        const dragStarted = fireMouseEvent(
            'dragstart',
            elemDrag,
            center1X,
            center1Y
        );
        if (!dragStarted) {
            return;
        }

        fireMouseEvent('drag', elemDrag, center1X, center1Y);
        fireMouseEvent('mousemove', elemDrag, center1X, center1Y);
        fireMouseEvent('drag', elemDrag, center2X, center2Y);
        fireMouseEvent('mousemove', elemDrop, center2X, center2Y);

        // trigger dragging process on top of drop target
        fireMouseEvent('mouseenter', elemDrop, center2X, center2Y);
        fireMouseEvent('dragenter', elemDrop, center2X, center2Y);
        fireMouseEvent('mouseover', elemDrop, center2X, center2Y);
        fireMouseEvent('dragover', elemDrop, center2X, center2Y);

        // release dragged element on top of drop target
        fireMouseEvent('drop', elemDrop, center2X, center2Y);
        fireMouseEvent('dragend', elemDrag, center2X, center2Y);
        fireMouseEvent('mouseup', elemDrag, center2X, center2Y);
    });
};

// example usage
dragAndDrop(screen.getByText('text of item to drag'), screen.getByLabelText('label of area to drop'));
@lourenci
Copy link
Collaborator

lourenci commented Sep 1, 2020

I'm all for it, though you should wait for a member position on this before to spend time on the implementation.

Are those all the events triggered by a drag event? Cypress can help us with that, I make do with it for some drag test I did in the past.

@nickserv nickserv added the new event New event to be included in the API label Sep 1, 2020
@kentcdodds
Copy link
Member

Yeah, for this kind of thing I think that Cypress is the place to go. Simulating drag-and-drop in an environment which does not support layout like jsdom (which is where most people use user-event) would be very tricky to get legit confidence from.

@ipap360
Copy link
Author

ipap360 commented Sep 1, 2020

Thank you for your answers! I didn't know about this jsdom limitation until now. We're not using cypress at the moment but perhaps it is time we do.

@ipap360 ipap360 closed this as completed Sep 1, 2020
@brapifra
Copy link

Do you still feel strongly about this @kentcdodds ? I have written quite a few tests with jest and jsdom to make sure logic related to dnd actions works as expected and I'm happy with the confidence they give me.
e.g.

it('sorts the list once I drop an item');
it('doesn't sort the list if I drop an invalid item');

@kentcdodds
Copy link
Member

If you're happy with it then fell free to continue doing it that way. I personally wouldn't want components that really heavily on layout to have that important aspect of their behavior mocked.

@brapifra
Copy link

So I assume that's a No, I don't want an userEvent.drag/drop event` 😅 ?

@kentcdodds
Copy link
Member

That's a: "I don't think I would use it, but some people like yourself and others who use user-event in a real browser might. This means I'll accept a pull request for it, but I won't maintain it." 😅

@brapifra
Copy link

haha that sounds good to me! Thanks

@ph-fritsche
Copy link
Member

That is a great addition. One can use this to test that drag&drop implementations work in principle.
E.g. providing a handler in a library that is later added on some concrete component.

We should just alert everyone in the documentation that the coordinates received for the PointerEvents are not reliable and that the test does not verify if a drag operation can be actually performed by the user because the source or target element might be hidden.

It would be great if the first argument of userEvent.drag could be either a draggable element, a Selection or an instance of DataTransfer.

@praveen4463
Copy link

@ipap360 Thanks for the example code!

@lucasfelixc
Copy link

Hi everyone, I would like to know how I would test just the drag, when I drag an input file it changes the style and I wanted to test this, without executing the drop

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
new event New event to be included in the API
Projects
None yet
Development

No branches or pull requests

8 participants