Skip to content

Commit c140976

Browse files
fix: keep React drag sorting active after remap
1 parent bf2618d commit c140976

5 files changed

Lines changed: 148 additions & 2 deletions

File tree

‎playwright/tests-frameworks/index.spec.ts‎

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,82 @@ test.describe("Vue wrappers working as expected", async () => {
127127
});
128128

129129
test.describe("React wrapper working as expected", async () => {
130+
test("useDragAndDrop() keeps sorting across multiple rows during one drag", async ({
131+
browserName,
132+
}) => {
133+
test.skip(
134+
browserName !== "chromium",
135+
"Synthetic native DragEvent sequence is only deterministic in Chromium."
136+
);
137+
138+
await page.evaluate(async () => {
139+
const originElement = document.getElementById(
140+
"react_use_drag_and_drop_continuous_depeche_mode"
141+
);
142+
const targetIds = [
143+
"react_use_drag_and_drop_continuous_duran_duran",
144+
"react_use_drag_and_drop_continuous_pet_shop_boys",
145+
"react_use_drag_and_drop_continuous_kraftwerk",
146+
];
147+
148+
if (!originElement) throw new Error("Missing origin element");
149+
150+
const dataTransfer = new DataTransfer();
151+
152+
const getEventProps = (element: Element) => {
153+
const rect = element.getBoundingClientRect();
154+
const x = rect.x + rect.width / 2;
155+
const y = rect.y + rect.height / 2;
156+
157+
return {
158+
bubbles: true,
159+
cancelable: true,
160+
clientX: x,
161+
clientY: y,
162+
dataTransfer,
163+
screenX: x,
164+
screenY: y,
165+
};
166+
};
167+
168+
originElement.dispatchEvent(
169+
new DragEvent("dragstart", getEventProps(originElement))
170+
);
171+
172+
for (const id of targetIds) {
173+
await new Promise((resolve) => setTimeout(resolve, 100));
174+
175+
const targetElement = document.getElementById(id);
176+
177+
if (!targetElement) throw new Error(`Missing target element ${id}`);
178+
179+
targetElement.dispatchEvent(
180+
new DragEvent("dragover", getEventProps(targetElement))
181+
);
182+
183+
await new Promise((resolve) => setTimeout(resolve, 100));
184+
}
185+
186+
const dropElement = document.getElementById(targetIds[targetIds.length - 1]);
187+
188+
if (!dropElement) throw new Error("Missing drop element");
189+
190+
await new Promise((resolve) => setTimeout(resolve, 100));
191+
192+
dropElement.dispatchEvent(
193+
new DragEvent("drop", getEventProps(dropElement))
194+
);
195+
196+
await new Promise((resolve) => setTimeout(resolve, 200));
197+
});
198+
199+
await expect(
200+
page.locator("#react_use_drag_and_drop_continuous_values")
201+
).toHaveText(
202+
"duran_duran pet_shop_boys kraftwerk depeche_mode tears_for_fears spandau_ballet"
203+
);
204+
});
205+
130206
test("dragAndDrop() can enable sorting, accept new values, and update the parent config", async () => {
131207
// Check that the list items can be sorted
132208
await drag(page, {

‎src/index.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2591,9 +2591,9 @@ export function validateSort<T>(
25912591
state.draggedNodes.map((x) => x.el).includes(data.targetData.node.el)
25922592
) {
25932593
state.currentTargetValue = data.targetData.node.data.value;
2594-
}
25952594

2596-
return false;
2595+
return false;
2596+
}
25972597
}
25982598

25992599
if (state.preventEnter) return false;

‎src/react/index.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,14 @@ export function useDragAndDrop<E extends HTMLElement, T = unknown>(
9393

9494
useEffect(() => {
9595
dragAndDrop({ parent, state: [values, setValues], ...options });
96+
}, []);
97+
98+
useEffect(() => {
99+
if (parent.current)
100+
parentValues.set(parent.current, [
101+
values as Array<unknown>,
102+
setValues as Dispatch<SetStateAction<Array<unknown>>>,
103+
]);
96104
}, [values]);
97105

98106
useEffect(() => {

‎tests-frameworks/react/App.tsx‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from "react";
22
import Test1 from "./components/Test1";
33
import Test2 from "./components/Test2";
4+
import Test3 from "./components/Test3";
45
import Test4 from "./components/Test4";
56
import reactLogo from "/reactjs-icon.svg";
67

@@ -15,6 +16,11 @@ const App: React.FC = () => {
1516
<div className="divider"></div>
1617
<Test2 id="react_use_drag_and_drop" testDescription="useDragAndDrop" />
1718
<div className="divider"></div>
19+
<Test3
20+
id="react_use_drag_and_drop_continuous"
21+
testDescription="useDragAndDrop continuous sorting"
22+
/>
23+
<div className="divider"></div>
1824
<Test4 id="react_deferred_sort" testDescription="deferred renders" />
1925
</div>
2026
</>
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import React from "react";
2+
3+
import { useDragAndDrop } from "../../../src/react/index";
4+
5+
type Tape = {
6+
id: string;
7+
label: string;
8+
};
9+
10+
const tapes: Tape[] = [
11+
{ id: "depeche_mode", label: "Depeche Mode" },
12+
{ id: "duran_duran", label: "Duran Duran" },
13+
{ id: "pet_shop_boys", label: "Pet Shop Boys" },
14+
{ id: "kraftwerk", label: "Kraftwerk" },
15+
{ id: "tears_for_fears", label: "Tears for Fears" },
16+
{ id: "spandau_ballet", label: "Spandau Ballet" },
17+
];
18+
19+
function Test3(props: { id: string; testDescription: string }) {
20+
const [parent, values] = useDragAndDrop<HTMLUListElement, Tape>(tapes);
21+
22+
return (
23+
<>
24+
<h3>#{props.id}</h3>
25+
<h4>{props.testDescription}</h4>
26+
<ul
27+
id={props.id}
28+
ref={parent}
29+
style={{ display: "block", width: "220px" }}
30+
>
31+
{values.map((tape) => (
32+
<li
33+
className="item"
34+
key={tape.id}
35+
id={props.id + "_" + tape.id}
36+
style={{
37+
boxSizing: "border-box",
38+
display: "block",
39+
height: "56px",
40+
margin: "0 0 8px",
41+
padding: "16px",
42+
width: "220px",
43+
}}
44+
>
45+
{tape.label}
46+
</li>
47+
))}
48+
</ul>
49+
<span id={props.id + "_values"}>
50+
{values.map((x: Tape) => x.id).join(" ")}
51+
</span>
52+
</>
53+
);
54+
}
55+
56+
export default Test3;

0 commit comments

Comments
 (0)