@headless-tree/core 0.0.10 → 0.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/lib/cjs/core/build-proxified-instance.d.ts +2 -0
  3. package/lib/cjs/core/build-proxified-instance.js +58 -0
  4. package/lib/cjs/core/build-static-instance.d.ts +2 -0
  5. package/lib/cjs/core/build-static-instance.js +26 -0
  6. package/lib/cjs/core/create-tree.js +62 -40
  7. package/lib/cjs/features/async-data-loader/feature.d.ts +1 -4
  8. package/lib/cjs/features/async-data-loader/feature.js +35 -23
  9. package/lib/cjs/features/async-data-loader/types.d.ts +4 -6
  10. package/lib/cjs/features/drag-and-drop/feature.d.ts +2 -3
  11. package/lib/cjs/features/drag-and-drop/feature.js +79 -44
  12. package/lib/cjs/features/drag-and-drop/types.d.ts +15 -6
  13. package/lib/cjs/features/drag-and-drop/utils.d.ts +2 -3
  14. package/lib/cjs/features/drag-and-drop/utils.js +140 -37
  15. package/lib/cjs/features/expand-all/feature.d.ts +1 -5
  16. package/lib/cjs/features/expand-all/feature.js +12 -6
  17. package/lib/cjs/features/hotkeys-core/feature.d.ts +1 -3
  18. package/lib/cjs/features/main/types.d.ts +8 -2
  19. package/lib/cjs/features/prop-memoization/feature.d.ts +2 -0
  20. package/lib/cjs/features/prop-memoization/feature.js +48 -0
  21. package/lib/cjs/features/prop-memoization/types.d.ts +10 -0
  22. package/lib/cjs/features/prop-memoization/types.js +2 -0
  23. package/lib/cjs/features/renaming/feature.d.ts +1 -4
  24. package/lib/cjs/features/renaming/feature.js +36 -22
  25. package/lib/cjs/features/renaming/types.d.ts +2 -2
  26. package/lib/cjs/features/search/feature.d.ts +1 -4
  27. package/lib/cjs/features/search/feature.js +38 -24
  28. package/lib/cjs/features/search/types.d.ts +0 -1
  29. package/lib/cjs/features/selection/feature.d.ts +1 -4
  30. package/lib/cjs/features/selection/feature.js +54 -35
  31. package/lib/cjs/features/selection/types.d.ts +1 -1
  32. package/lib/cjs/features/sync-data-loader/feature.d.ts +1 -3
  33. package/lib/cjs/features/sync-data-loader/feature.js +7 -2
  34. package/lib/cjs/features/tree/feature.d.ts +1 -5
  35. package/lib/cjs/features/tree/feature.js +97 -92
  36. package/lib/cjs/features/tree/types.d.ts +5 -8
  37. package/lib/cjs/index.d.ts +5 -1
  38. package/lib/cjs/index.js +4 -1
  39. package/lib/cjs/mddocs-entry.d.ts +10 -0
  40. package/lib/cjs/test-utils/test-tree-do.d.ts +23 -0
  41. package/lib/cjs/test-utils/test-tree-do.js +99 -0
  42. package/lib/cjs/test-utils/test-tree-expect.d.ts +15 -0
  43. package/lib/cjs/test-utils/test-tree-expect.js +62 -0
  44. package/lib/cjs/test-utils/test-tree.d.ts +47 -0
  45. package/lib/cjs/test-utils/test-tree.js +203 -0
  46. package/lib/cjs/types/core.d.ts +39 -24
  47. package/lib/cjs/utilities/errors.d.ts +1 -0
  48. package/lib/cjs/utilities/errors.js +5 -0
  49. package/lib/cjs/utilities/insert-items-at-target.js +10 -3
  50. package/lib/cjs/utilities/remove-items-from-parents.js +14 -8
  51. package/lib/cjs/utils.d.ts +3 -3
  52. package/lib/cjs/utils.js +6 -6
  53. package/lib/esm/core/build-proxified-instance.d.ts +2 -0
  54. package/lib/esm/core/build-proxified-instance.js +54 -0
  55. package/lib/esm/core/build-static-instance.d.ts +2 -0
  56. package/lib/esm/core/build-static-instance.js +22 -0
  57. package/lib/esm/core/create-tree.js +62 -40
  58. package/lib/esm/features/async-data-loader/feature.d.ts +1 -4
  59. package/lib/esm/features/async-data-loader/feature.js +35 -23
  60. package/lib/esm/features/async-data-loader/types.d.ts +4 -6
  61. package/lib/esm/features/drag-and-drop/feature.d.ts +2 -3
  62. package/lib/esm/features/drag-and-drop/feature.js +79 -44
  63. package/lib/esm/features/drag-and-drop/types.d.ts +15 -6
  64. package/lib/esm/features/drag-and-drop/utils.d.ts +2 -3
  65. package/lib/esm/features/drag-and-drop/utils.js +138 -34
  66. package/lib/esm/features/expand-all/feature.d.ts +1 -5
  67. package/lib/esm/features/expand-all/feature.js +12 -6
  68. package/lib/esm/features/hotkeys-core/feature.d.ts +1 -3
  69. package/lib/esm/features/main/types.d.ts +8 -2
  70. package/lib/esm/features/prop-memoization/feature.d.ts +2 -0
  71. package/lib/esm/features/prop-memoization/feature.js +45 -0
  72. package/lib/esm/features/prop-memoization/types.d.ts +10 -0
  73. package/lib/esm/features/prop-memoization/types.js +1 -0
  74. package/lib/esm/features/renaming/feature.d.ts +1 -4
  75. package/lib/esm/features/renaming/feature.js +36 -22
  76. package/lib/esm/features/renaming/types.d.ts +2 -2
  77. package/lib/esm/features/search/feature.d.ts +1 -4
  78. package/lib/esm/features/search/feature.js +38 -24
  79. package/lib/esm/features/search/types.d.ts +0 -1
  80. package/lib/esm/features/selection/feature.d.ts +1 -4
  81. package/lib/esm/features/selection/feature.js +54 -35
  82. package/lib/esm/features/selection/types.d.ts +1 -1
  83. package/lib/esm/features/sync-data-loader/feature.d.ts +1 -3
  84. package/lib/esm/features/sync-data-loader/feature.js +7 -2
  85. package/lib/esm/features/tree/feature.d.ts +1 -5
  86. package/lib/esm/features/tree/feature.js +98 -93
  87. package/lib/esm/features/tree/types.d.ts +5 -8
  88. package/lib/esm/index.d.ts +5 -1
  89. package/lib/esm/index.js +4 -1
  90. package/lib/esm/mddocs-entry.d.ts +10 -0
  91. package/lib/esm/test-utils/test-tree-do.d.ts +23 -0
  92. package/lib/esm/test-utils/test-tree-do.js +95 -0
  93. package/lib/esm/test-utils/test-tree-expect.d.ts +15 -0
  94. package/lib/esm/test-utils/test-tree-expect.js +58 -0
  95. package/lib/esm/test-utils/test-tree.d.ts +47 -0
  96. package/lib/esm/test-utils/test-tree.js +199 -0
  97. package/lib/esm/types/core.d.ts +39 -24
  98. package/lib/esm/utilities/errors.d.ts +1 -0
  99. package/lib/esm/utilities/errors.js +1 -0
  100. package/lib/esm/utilities/insert-items-at-target.js +10 -3
  101. package/lib/esm/utilities/remove-items-from-parents.js +14 -8
  102. package/lib/esm/utils.d.ts +3 -3
  103. package/lib/esm/utils.js +3 -3
  104. package/package.json +7 -3
  105. package/src/core/build-proxified-instance.ts +117 -0
  106. package/src/core/build-static-instance.ts +27 -0
  107. package/src/core/core.spec.ts +210 -0
  108. package/src/core/create-tree.ts +73 -78
  109. package/src/features/async-data-loader/async-data-loader.spec.ts +124 -0
  110. package/src/features/async-data-loader/feature.ts +34 -44
  111. package/src/features/async-data-loader/types.ts +4 -6
  112. package/src/features/drag-and-drop/drag-and-drop.spec.ts +717 -0
  113. package/src/features/drag-and-drop/feature.ts +88 -63
  114. package/src/features/drag-and-drop/types.ts +24 -10
  115. package/src/features/drag-and-drop/utils.ts +197 -56
  116. package/src/features/expand-all/expand-all.spec.ts +56 -0
  117. package/src/features/expand-all/feature.ts +9 -24
  118. package/src/features/hotkeys-core/feature.ts +5 -14
  119. package/src/features/main/types.ts +14 -1
  120. package/src/features/prop-memoization/feature.ts +51 -0
  121. package/src/features/prop-memoization/prop-memoization.spec.ts +68 -0
  122. package/src/features/prop-memoization/types.ts +11 -0
  123. package/src/features/renaming/feature.ts +37 -45
  124. package/src/features/renaming/renaming.spec.ts +127 -0
  125. package/src/features/renaming/types.ts +2 -2
  126. package/src/features/search/feature.ts +36 -46
  127. package/src/features/search/search.spec.ts +117 -0
  128. package/src/features/search/types.ts +0 -1
  129. package/src/features/selection/feature.ts +50 -53
  130. package/src/features/selection/selection.spec.ts +219 -0
  131. package/src/features/selection/types.ts +0 -2
  132. package/src/features/sync-data-loader/feature.ts +9 -18
  133. package/src/features/tree/feature.ts +101 -144
  134. package/src/features/tree/tree.spec.ts +475 -0
  135. package/src/features/tree/types.ts +5 -9
  136. package/src/index.ts +6 -1
  137. package/src/mddocs-entry.ts +13 -0
  138. package/src/test-utils/test-tree-do.ts +136 -0
  139. package/src/test-utils/test-tree-expect.ts +86 -0
  140. package/src/test-utils/test-tree.ts +227 -0
  141. package/src/types/core.ts +76 -108
  142. package/src/utilities/errors.ts +2 -0
  143. package/src/utilities/insert-items-at-target.ts +10 -3
  144. package/src/utilities/remove-items-from-parents.ts +15 -10
  145. package/src/utils.spec.ts +89 -0
  146. package/src/utils.ts +6 -6
  147. package/tsconfig.json +1 -0
  148. package/vitest.config.ts +6 -0
@@ -0,0 +1,717 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { TestTree } from "../../test-utils/test-tree";
3
+ import { dragAndDropFeature } from "./feature";
4
+ import { selectionFeature } from "../selection/feature";
5
+ import { ItemInstance } from "../../types/core";
6
+ import { createOnDropHandler } from "../../utilities/create-on-drop-handler";
7
+ import { propMemoizationFeature } from "../prop-memoization/feature";
8
+
9
+ const isItem = (item: unknown): item is ItemInstance<any> =>
10
+ !!item && typeof item === "object" && "getId" in item;
11
+ const areItemsEqual = (a: ItemInstance<any>, b: ItemInstance<any>) => {
12
+ if (!isItem(a) || !isItem(b)) return undefined;
13
+ if (a.getId() === b.getId()) return true;
14
+ console.warn("Items are not equal:", a.getId(), b.getId());
15
+ return false;
16
+ };
17
+ expect.addEqualityTesters([areItemsEqual]);
18
+
19
+ const factory = TestTree.default({
20
+ initialState: {
21
+ expandedItems: ["x1", "x11", "x2", "x21"],
22
+ },
23
+ onDrop: vi.fn(),
24
+ }).withFeatures(selectionFeature, dragAndDropFeature, propMemoizationFeature);
25
+
26
+ describe("core-feature/drag-and-drop", () => {
27
+ factory.forSuits((tree) => {
28
+ describe("happy paths", () => {
29
+ it("drop on expanded folder with leafs", () => {
30
+ tree.do.ctrlSelectItem("x111");
31
+ tree.do.startDrag("x111");
32
+ tree.do.dragOverAndDrop("x21");
33
+ tree.expect.dropped(["x111"], {
34
+ dragLineIndex: null,
35
+ dragLineLevel: null,
36
+ childIndex: null,
37
+ insertionIndex: null,
38
+ item: tree.item("x21"),
39
+ });
40
+ });
41
+
42
+ it("drop on expanded folder with nested items", () => {
43
+ tree.do.ctrlSelectItem("x111");
44
+ tree.do.startDrag("x111");
45
+ tree.do.dragOverAndDrop("x2");
46
+ tree.expect.dropped(["x111"], {
47
+ dragLineIndex: null,
48
+ dragLineLevel: null,
49
+ childIndex: null,
50
+ insertionIndex: null,
51
+ item: tree.item("x2"),
52
+ });
53
+ });
54
+
55
+ it("drop on collapsed folder", () => {
56
+ tree.do.ctrlSelectItem("x111");
57
+ tree.do.startDrag("x111");
58
+ tree.do.dragOverAndDrop("x12");
59
+ tree.expect.dropped(["x111"], {
60
+ dragLineIndex: null,
61
+ dragLineLevel: null,
62
+ childIndex: null,
63
+ insertionIndex: null,
64
+ item: tree.item("x12"),
65
+ });
66
+ });
67
+
68
+ it("drop above item", () => {
69
+ tree.do.ctrlSelectItem("x111");
70
+ tree.do.startDrag("x111");
71
+ tree.setElementBoundingBox("x212");
72
+ const event = tree.createTopDragEvent();
73
+ tree.do.dragOverAndDrop("x212", event);
74
+ tree.expect.dropped(["x111"], {
75
+ dragLineIndex: 12,
76
+ dragLineLevel: 2,
77
+ childIndex: 1,
78
+ insertionIndex: 1,
79
+ item: tree.item("x21"),
80
+ });
81
+ });
82
+
83
+ it("drop below item", () => {
84
+ tree.do.ctrlSelectItem("x111");
85
+ tree.do.startDrag("x111");
86
+ const event = tree.createBottomDragEvent();
87
+ tree.do.dragOverAndDrop("x212", event);
88
+ tree.expect.dropped(["x111"], {
89
+ dragLineIndex: 13,
90
+ dragLineLevel: 2,
91
+ childIndex: 2,
92
+ insertionIndex: 2,
93
+ item: tree.item("x21"),
94
+ });
95
+ });
96
+
97
+ it("drop not reparented", () => {
98
+ tree.do.ctrlSelectItem("x111");
99
+ tree.do.startDrag("x111");
100
+ tree.setElementBoundingBox("x114");
101
+ const event = tree.createBottomDragEvent(2);
102
+ tree.do.dragOverAndDrop("x114", event);
103
+ tree.expect.dropped(["x111"], {
104
+ dragLineIndex: 6,
105
+ dragLineLevel: 2,
106
+ childIndex: 4,
107
+ insertionIndex: 3,
108
+ item: tree.item("x11"),
109
+ });
110
+ });
111
+
112
+ it("drop reparented one level", () => {
113
+ tree.do.ctrlSelectItem("x111");
114
+ tree.do.startDrag("x111");
115
+ tree.setElementBoundingBox("x114");
116
+ const event = tree.createBottomDragEvent(1);
117
+ tree.do.dragOverAndDrop("x114", event);
118
+ tree.expect.dropped(["x111"], {
119
+ dragLineIndex: 6,
120
+ dragLineLevel: 1,
121
+ childIndex: 1,
122
+ insertionIndex: 1,
123
+ item: tree.item("x1"),
124
+ });
125
+ });
126
+
127
+ it("drop reparented two levels", () => {
128
+ // TODO reparenting two levels should work at x144, but not at x114
129
+ tree.do.ctrlSelectItem("x111");
130
+ tree.do.startDrag("x111");
131
+ const event = tree.createBottomDragEvent(0);
132
+ tree.do.dragOverAndDrop("x114", event);
133
+ tree.expect.dropped(["x111"], {
134
+ dragLineIndex: 6,
135
+ dragLineLevel: 0,
136
+ childIndex: 1,
137
+ insertionIndex: 1,
138
+ item: tree.item("x"),
139
+ });
140
+ });
141
+
142
+ it("drags multiple in retained order (correct order)", () => {
143
+ tree.do.ctrlSelectItem("x111");
144
+ tree.do.ctrlSelectItem("x112");
145
+ tree.do.ctrlSelectItem("x113");
146
+ tree.do.ctrlSelectItem("x114");
147
+ tree.do.startDrag("x111");
148
+ tree.do.dragOverAndDrop("x21");
149
+ tree.expect.dropped(["x111", "x112", "x113", "x114"], {
150
+ dragLineIndex: null,
151
+ dragLineLevel: null,
152
+ childIndex: null,
153
+ insertionIndex: null,
154
+ item: tree.item("x21"),
155
+ });
156
+ });
157
+
158
+ it.skip("drags multiple in retained order (inverse order)", () => {
159
+ tree.do.ctrlSelectItem("x114");
160
+ tree.do.ctrlSelectItem("x113");
161
+ tree.do.ctrlSelectItem("x112");
162
+ tree.do.ctrlSelectItem("x111");
163
+ tree.do.startDrag("x111");
164
+ tree.do.dragOverAndDrop("x21");
165
+ tree.expect.dropped(["x111", "x112", "x113", "x114"], {
166
+ dragLineIndex: null,
167
+ dragLineLevel: null,
168
+ childIndex: null,
169
+ insertionIndex: null,
170
+ item: tree.item("x21"),
171
+ });
172
+ });
173
+
174
+ it.skip("drags multiple in retained order (scrambled order)", () => {
175
+ tree.do.ctrlSelectItem("x112");
176
+ tree.do.ctrlSelectItem("x113");
177
+ tree.do.ctrlSelectItem("x111");
178
+ tree.do.ctrlSelectItem("x114");
179
+ tree.do.startDrag("x111");
180
+ tree.do.dragOverAndDrop("x21");
181
+ tree.expect.dropped(["x111", "x112", "x113", "x114"], {
182
+ dragLineIndex: null,
183
+ dragLineLevel: null,
184
+ childIndex: null,
185
+ insertionIndex: null,
186
+ item: tree.item("x21"),
187
+ });
188
+ });
189
+
190
+ it("updates dnd state", () => {
191
+ const setDndState = tree.mockedHandler("setDndState");
192
+ tree.do.startDrag("x111");
193
+ expect(setDndState).toBeCalledWith({
194
+ draggedItems: [tree.item("x111")],
195
+ draggingOverItem: tree.item("x1"),
196
+ });
197
+ tree.do.dragOver("x21");
198
+ expect(setDndState).toBeCalledWith({
199
+ draggedItems: [tree.item("x111")],
200
+ draggingOverItem: tree.item("x21"),
201
+ dragTarget: {
202
+ childIndex: null,
203
+ dragLineIndex: null,
204
+ dragLineLevel: null,
205
+ insertionIndex: null,
206
+ item: tree.item("x21"),
207
+ },
208
+ });
209
+ tree.do.drop("x22");
210
+ expect(setDndState).toBeCalledWith(null);
211
+ });
212
+ });
213
+
214
+ describe.todo("insertion indices");
215
+
216
+ describe("drag lines for happy paths", () => {
217
+ it("drop on expanded folder", () => {
218
+ tree.do.ctrlSelectItem("x111");
219
+ tree.do.startDrag("x111");
220
+ tree.do.dragOver("x21");
221
+ expect(tree.instance.getDragLineData()).toEqual(null);
222
+ expect(tree.instance.getDragLineStyle()).toEqual({ display: "none" });
223
+ });
224
+
225
+ it("drop on collapsed folder", () => {
226
+ tree.do.ctrlSelectItem("x111");
227
+ tree.do.startDrag("x111");
228
+ tree.do.dragOver("x12");
229
+ expect(tree.instance.getDragLineData()).toEqual(null);
230
+ expect(tree.instance.getDragLineStyle(0, 0)).toEqual({
231
+ display: "none",
232
+ });
233
+ });
234
+
235
+ it("drop above item", () => {
236
+ tree.do.ctrlSelectItem("x111");
237
+ tree.do.startDrag("x111");
238
+ const event = tree.createTopDragEvent();
239
+ tree.setElementBoundingBox("x212");
240
+ tree.do.dragOver("x212", event);
241
+ tree.expect.defaultDragLineProps(2);
242
+ });
243
+
244
+ it("drop below item", () => {
245
+ tree.do.ctrlSelectItem("x111");
246
+ tree.do.startDrag("x111");
247
+ const event = tree.createBottomDragEvent();
248
+ tree.setElementBoundingBox("x213");
249
+ tree.do.dragOver("x212", event);
250
+ tree.expect.defaultDragLineProps(2);
251
+ });
252
+
253
+ it("drop not reparented", () => {
254
+ tree.do.ctrlSelectItem("x111");
255
+ tree.do.startDrag("x111");
256
+ const event = tree.createBottomDragEvent(2);
257
+ tree.setElementBoundingBox("x12");
258
+ tree.setElementBoundingBox("x114");
259
+ tree.do.dragOver("x114", event);
260
+ tree.expect.defaultDragLineProps(2);
261
+ });
262
+
263
+ it("drop reparented one level", () => {
264
+ tree.do.ctrlSelectItem("x111");
265
+ tree.do.startDrag("x111");
266
+ const event = tree.createBottomDragEvent(1);
267
+ tree.setElementBoundingBox("x12");
268
+ tree.setElementBoundingBox("x114");
269
+ tree.do.dragOver("x114", event);
270
+ tree.expect.defaultDragLineProps(1);
271
+ });
272
+
273
+ it("drop reparented two levels", () => {
274
+ tree.do.ctrlSelectItem("x111");
275
+ tree.do.startDrag("x111");
276
+ const event = tree.createBottomDragEvent(0);
277
+ tree.setElementBoundingBox("x12");
278
+ tree.setElementBoundingBox("x114");
279
+ tree.do.dragOver("x114", event);
280
+ tree.expect.defaultDragLineProps(0);
281
+ });
282
+ });
283
+
284
+ describe("foreign dnd", () => {
285
+ const data = Symbol("foreignObject");
286
+ const format = "application/json";
287
+
288
+ const createForeignDragObject = tree
289
+ .mockedHandler("createForeignDragObject")
290
+ .mockReturnValue({ data, format });
291
+ const onCompleteForeignDrop = tree.mockedHandler("onCompleteForeignDrop");
292
+
293
+ it("drags tree item outside to foreign object", () => {
294
+ tree.do.selectMultiple("x111", "x112");
295
+ const event = tree.do.startDrag("x111");
296
+ tree.do.dragEnd("x111");
297
+ expect(event.dataTransfer.setData).toHaveBeenCalledWith(format, data);
298
+ expect(createForeignDragObject).toHaveBeenCalledWith([
299
+ tree.item("x111"),
300
+ tree.item("x112"),
301
+ ]);
302
+ expect(onCompleteForeignDrop).toHaveBeenCalledWith([
303
+ tree.item("x111"),
304
+ tree.item("x112"),
305
+ ]);
306
+ });
307
+
308
+ it("drags foreign object inside tree, on folder", () => {
309
+ tree.mockedHandler("canDropForeignDragObject").mockReturnValue(true);
310
+ const onDropForeignDragObject = tree.mockedHandler(
311
+ "onDropForeignDragObject",
312
+ );
313
+ const event = TestTree.dragEvent();
314
+ tree.do.dragOver("x11", event);
315
+ tree.do.drop("x11", event);
316
+ expect(onDropForeignDragObject).toHaveBeenCalledWith(
317
+ event.dataTransfer,
318
+ {
319
+ childIndex: null,
320
+ dragLineIndex: null,
321
+ dragLineLevel: null,
322
+ insertionIndex: null,
323
+ item: tree.item("x11"),
324
+ },
325
+ );
326
+ });
327
+
328
+ it("drags foreign object inside tree, between items", () => {
329
+ tree
330
+ .mockedHandler("canDropForeignDragObject")
331
+ .mockImplementation((_, target) => target.item.isFolder());
332
+ const onDropForeignDragObject = tree.mockedHandler(
333
+ "onDropForeignDragObject",
334
+ );
335
+ const event = tree.createBottomDragEvent(2);
336
+ tree.setElementBoundingBox("x212");
337
+ tree.setElementBoundingBox("x213");
338
+ tree.do.dragOver("x112", event);
339
+ tree.do.drop("x112", event);
340
+ expect(onDropForeignDragObject).toHaveBeenCalledWith(
341
+ event.dataTransfer,
342
+ {
343
+ childIndex: 2,
344
+ dragLineIndex: 4,
345
+ dragLineLevel: 2,
346
+ insertionIndex: 2,
347
+ item: tree.item("x11"),
348
+ },
349
+ );
350
+ });
351
+
352
+ it("doesnt drag foreign object inside tree if not allowed", () => {
353
+ tree.mockedHandler("canDropForeignDragObject").mockReturnValue(false);
354
+ const onDropForeignDragObject = tree.mockedHandler(
355
+ "onDropForeignDragObject",
356
+ );
357
+ const event = TestTree.dragEvent();
358
+ tree.do.dragOverNotAllowed("x11", event);
359
+ tree.do.drop("x11", event);
360
+ expect(onDropForeignDragObject).not.toHaveBeenCalled();
361
+ });
362
+ });
363
+
364
+ describe("with insertion handlers", () => {
365
+ const changeChildren = vi.fn();
366
+ const suiteTree = tree.with({
367
+ onDrop: createOnDropHandler((item, newChildren) => {
368
+ changeChildren(item.getId(), newChildren);
369
+ }),
370
+ });
371
+
372
+ suiteTree.resetBeforeEach();
373
+
374
+ it("drags within same tree on folder", () => {
375
+ suiteTree.do.selectMultiple("x111", "x112");
376
+ suiteTree.do.startDrag("x111");
377
+ suiteTree.do.dragOverAndDrop("x21");
378
+ expect(changeChildren).toHaveBeenCalledWith("x11", ["x113", "x114"]);
379
+ expect(changeChildren).toHaveBeenCalledWith("x21", [
380
+ "x211",
381
+ "x212",
382
+ "x213",
383
+ "x214",
384
+ "x111",
385
+ "x112",
386
+ ]);
387
+ });
388
+
389
+ it("drags within same tree inside folder", () => {
390
+ suiteTree.do.selectMultiple("x111", "x112");
391
+ suiteTree.do.startDrag("x111");
392
+ suiteTree.do.dragOverAndDrop(
393
+ "x212",
394
+ suiteTree.createBottomDragEvent(2),
395
+ );
396
+ expect(changeChildren).toHaveBeenCalledWith("x11", ["x113", "x114"]);
397
+ expect(changeChildren).toHaveBeenCalledWith("x21", [
398
+ "x211",
399
+ "x212",
400
+ "x111",
401
+ "x112",
402
+ "x213",
403
+ "x214",
404
+ ]);
405
+ });
406
+
407
+ it("drags within one folder", () => {
408
+ if (!("invalidateItemData" in suiteTree.instance)) {
409
+ // since sync trees don't reflect changes in children, this test
410
+ // doesn't work for them
411
+ return;
412
+ }
413
+
414
+ suiteTree.do.selectMultiple("x111", "x112");
415
+ suiteTree.do.startDrag("x111");
416
+ suiteTree.do.dragOverAndDrop(
417
+ "x113",
418
+ suiteTree.createBottomDragEvent(2),
419
+ );
420
+ expect(changeChildren).toHaveBeenCalledWith("x11", [
421
+ "x113",
422
+ "x111",
423
+ "x112",
424
+ "x114",
425
+ ]);
426
+ });
427
+
428
+ it("drags outside", () => {
429
+ const createForeignDragObject = suiteTree
430
+ .mockedHandler("createForeignDragObject")
431
+ .mockReturnValue({ format: "format", data: "data" });
432
+ const onCompleteForeignDrop = suiteTree.mockedHandler(
433
+ "onCompleteForeignDrop",
434
+ );
435
+ suiteTree.do.selectMultiple("x111", "x112");
436
+ const e = suiteTree.do.startDrag("x111");
437
+ expect(e.dataTransfer.setData).toHaveBeenCalledWith("format", "data");
438
+ expect(createForeignDragObject).toHaveBeenCalledWith([
439
+ suiteTree.item("x111"),
440
+ suiteTree.item("x112"),
441
+ ]);
442
+ suiteTree.do.dragEnd("x111");
443
+ expect(onCompleteForeignDrop).toHaveBeenCalledWith([
444
+ suiteTree.item("x111"),
445
+ suiteTree.item("x112"),
446
+ ]);
447
+ });
448
+
449
+ it("drags inside if allowed", () => {
450
+ suiteTree
451
+ .mockedHandler("canDropForeignDragObject")
452
+ .mockReturnValue(true);
453
+ const onDropForeignDragObject = suiteTree.mockedHandler(
454
+ "onDropForeignDragObject",
455
+ );
456
+ const e = TestTree.dragEvent();
457
+ suiteTree.do.drop("x21", e);
458
+ expect(onDropForeignDragObject).toBeCalledWith(e.dataTransfer, {
459
+ childIndex: null,
460
+ dragLineIndex: null,
461
+ dragLineLevel: null,
462
+ insertionIndex: null,
463
+ item: suiteTree.item("x21"),
464
+ });
465
+ });
466
+
467
+ it("doesnt drag inside if not allowed", () => {
468
+ suiteTree
469
+ .mockedHandler("canDropForeignDragObject")
470
+ .mockReturnValue(false);
471
+ const onDropForeignDragObject = suiteTree.mockedHandler(
472
+ "onDropForeignDragObject",
473
+ );
474
+ const e = TestTree.dragEvent();
475
+ suiteTree.do.drop("x21", e);
476
+ expect(onDropForeignDragObject).not.toHaveBeenCalled();
477
+ });
478
+
479
+ it("drags multiple within in retained order (correct order)", () => {
480
+ suiteTree.do.selectMultiple("x111", "x112", "x113", "x114");
481
+ suiteTree.do.startDrag("x111");
482
+ suiteTree.do.dragOverAndDrop("x212", suiteTree.createBottomDragEvent());
483
+ expect(changeChildren).toHaveBeenCalledWith("x11", []);
484
+ expect(changeChildren).toHaveBeenCalledWith("x21", [
485
+ "x211",
486
+ "x212",
487
+ "x111",
488
+ "x112",
489
+ "x113",
490
+ "x114",
491
+ "x213",
492
+ "x214",
493
+ ]);
494
+ });
495
+
496
+ it("drags multiple within in retained order (inverse order)", () => {
497
+ suiteTree.do.selectMultiple("x114", "x113", "x112", "x111");
498
+ suiteTree.do.startDrag("x111");
499
+ suiteTree.do.dragOverAndDrop("x212", suiteTree.createBottomDragEvent());
500
+ expect(changeChildren).toHaveBeenCalledWith("x11", []);
501
+ expect(changeChildren).toHaveBeenCalledWith("x21", [
502
+ "x211",
503
+ "x212",
504
+ "x114",
505
+ "x113",
506
+ "x112",
507
+ "x111",
508
+ "x213",
509
+ "x214",
510
+ ]);
511
+ });
512
+
513
+ it("drags multiple within in retained order (scrambled order)", () => {
514
+ suiteTree.do.selectMultiple("x111", "x114", "x112", "x113");
515
+ suiteTree.do.startDrag("x111");
516
+ suiteTree.do.dragOverAndDrop("x212", suiteTree.createBottomDragEvent());
517
+ expect(changeChildren).toHaveBeenCalledWith("x11", []);
518
+ expect(changeChildren).toHaveBeenCalledWith("x21", [
519
+ "x211",
520
+ "x212",
521
+ "x111",
522
+ "x114",
523
+ "x112",
524
+ "x113",
525
+ "x213",
526
+ "x214",
527
+ ]);
528
+ });
529
+ });
530
+
531
+ describe("special cases", () => {
532
+ it.todo("drops at bottom of tree");
533
+ });
534
+
535
+ describe("drop redirection", () => {
536
+ it("redirects to parent folder without inbetween dropping", async () => {
537
+ const testTree = await tree
538
+ .with({ canReorder: false })
539
+ .createTestCaseTree();
540
+ testTree.do.startDrag("x111");
541
+ testTree.do.dragOverAndDrop("x212", testTree.createBottomDragEvent(2));
542
+ testTree.expect.dropped(["x111"], {
543
+ dragLineIndex: null,
544
+ dragLineLevel: null,
545
+ childIndex: null,
546
+ insertionIndex: null,
547
+ item: tree.item("x21"),
548
+ });
549
+ });
550
+
551
+ it("doesnt redirect to parent folder with inbetween dropping", async () => {
552
+ const testTree = await tree
553
+ .with({ canReorder: true })
554
+ .createTestCaseTree();
555
+ testTree.do.startDrag("x111");
556
+ testTree.do.dragOverAndDrop("x212", testTree.createBottomDragEvent(2));
557
+ testTree.expect.dropped(["x111"], {
558
+ childIndex: 2,
559
+ dragLineIndex: 13,
560
+ dragLineLevel: 2,
561
+ insertionIndex: 2,
562
+ item: tree.item("x21"),
563
+ });
564
+ });
565
+ });
566
+
567
+ describe("dnd restrictions", () => {
568
+ it.todo("cannot drop on self", () => {
569
+ tree.do.startDrag("x11");
570
+ tree.expect.dragOverNotAllowed("x112");
571
+ });
572
+
573
+ it.todo("cannot drop on self, nested additional layer", () => {
574
+ tree.do.startDrag("x1");
575
+ tree.expect.dragOverNotAllowed("x112");
576
+ });
577
+
578
+ it.todo("does not reparent into itself", () => {
579
+ tree.do.startDrag("x11");
580
+ tree.setElementBoundingBox("x114");
581
+ const event = tree.createBottomDragEvent(1);
582
+ tree.expect.dragOverNotAllowed("x114", event);
583
+ });
584
+
585
+ it.for([0, 1, 2])("does not reparent at level %i of a subtree", (i) => {
586
+ tree.do.ctrlSelectItem("x111");
587
+ tree.do.startDrag("x111");
588
+ tree.setElementBoundingBox("x112");
589
+ const event = tree.createBottomDragEvent(i);
590
+ tree.do.dragOverAndDrop("x112", event);
591
+ tree.expect.dropped(["x111"], {
592
+ dragLineIndex: 4,
593
+ dragLineLevel: 2,
594
+ childIndex: 2,
595
+ insertionIndex: 1,
596
+ item: tree.item("x11"),
597
+ });
598
+ });
599
+
600
+ it("cannot drop on item with canDrop=false", () => {
601
+ const canDrop = tree.mockedHandler("canDrop").mockReturnValue(false);
602
+ tree.do.startDrag("x111");
603
+ tree.expect.dragOverNotAllowed("x2");
604
+ expect(canDrop).toHaveBeenCalledWith([tree.item("x111")], {
605
+ item: tree.item("x2"),
606
+ childIndex: null,
607
+ dragLineIndex: null,
608
+ dragLineLevel: null,
609
+ insertionIndex: null,
610
+ });
611
+ });
612
+
613
+ it.todo("item with canDrag=false is not draggable", () => {
614
+ const canDrag = tree.mockedHandler("canDrag").mockReturnValue(false);
615
+ expect(tree.instance.getItemInstance("x111").getProps().draggable).toBe(
616
+ false,
617
+ );
618
+ expect(canDrag).toHaveBeenCalledWith([tree.item("x111")]);
619
+ });
620
+
621
+ it("item with canDrag=false does not invoke drag handler when dragged", () => {
622
+ const canDrag = tree.mockedHandler("canDrag").mockReturnValue(false);
623
+ const setDndState = tree.mockedHandler("setDndState");
624
+ const e = TestTree.dragEvent();
625
+ tree.instance.getItemInstance("x111").getProps().onDragStart(e);
626
+ expect(canDrag).toHaveBeenCalledWith([tree.item("x111")]);
627
+ expect(e.preventDefault).toBeCalled();
628
+ expect(setDndState).not.toBeCalled();
629
+ });
630
+
631
+ it.todo("cancels drag");
632
+
633
+ it("drags all selected if drag is started within selection", () => {
634
+ tree.do.ctrlSelectItem("x111");
635
+ tree.do.ctrlSelectItem("x112");
636
+ tree.do.ctrlSelectItem("x113");
637
+ tree.do.startDrag("x111");
638
+ tree.do.dragOverAndDrop("x21");
639
+ tree.expect.dropped(["x111", "x112", "x113"], {
640
+ dragLineIndex: null,
641
+ dragLineLevel: null,
642
+ childIndex: null,
643
+ insertionIndex: null,
644
+ item: tree.item("x21"),
645
+ });
646
+ });
647
+
648
+ it("drags all only new item if drag is started outside previous selection", () => {
649
+ tree.do.ctrlSelectItem("x111");
650
+ tree.do.ctrlSelectItem("x112");
651
+ tree.do.ctrlSelectItem("x113");
652
+ tree.do.startDrag("x114");
653
+ tree.do.dragOverAndDrop("x21");
654
+ tree.expect.dropped(["x114"], {
655
+ dragLineIndex: null,
656
+ dragLineLevel: null,
657
+ childIndex: null,
658
+ insertionIndex: null,
659
+ item: tree.item("x21"),
660
+ });
661
+ });
662
+ });
663
+
664
+ describe("item instance methods", () => {
665
+ it("returns isDropTarget() correct for folders", () => {
666
+ tree.do.startDrag("x111");
667
+ tree.do.dragOver("x21");
668
+ expect(tree.instance.getItemInstance("x21").isDropTarget()).toBe(true);
669
+ expect(tree.instance.getItemInstance("x211").isDropTarget()).toBe(
670
+ false,
671
+ );
672
+ });
673
+
674
+ it("returns isDropTarget() correct for items", () => {
675
+ tree.do.startDrag("x111");
676
+ tree.do.dragOver("x211");
677
+ expect(tree.instance.getItemInstance("x21").isDropTarget()).toBe(true);
678
+ expect(tree.instance.getItemInstance("x211").isDropTarget()).toBe(
679
+ false,
680
+ );
681
+ });
682
+
683
+ it("returns isDraggingOver() correct for folders", () => {
684
+ tree.do.startDrag("x111");
685
+ tree.do.dragOver("x21");
686
+ expect(tree.instance.getItemInstance("x21").isDraggingOver()).toBe(
687
+ true,
688
+ );
689
+ expect(tree.instance.getItemInstance("x211").isDraggingOver()).toBe(
690
+ false,
691
+ );
692
+ });
693
+
694
+ it("returns isDraggingOver() correct for items", () => {
695
+ tree.do.startDrag("x111");
696
+ tree.do.dragOver("x211");
697
+ expect(tree.instance.getItemInstance("x211").isDraggingOver()).toBe(
698
+ true,
699
+ );
700
+ expect(tree.instance.getItemInstance("x21").isDraggingOver()).toBe(
701
+ false,
702
+ );
703
+ });
704
+ });
705
+
706
+ describe("retains last drag state with dragcode", () => {
707
+ it("uses constant number of calls to canDrop", () => {
708
+ const canDrop = tree.mockedHandler("canDrop").mockReturnValue(true);
709
+ tree.do.startDrag("x111");
710
+ Array.from({ length: 30 }).forEach(() => {
711
+ tree.do.dragOver("x12");
712
+ });
713
+ expect(canDrop).toBeCalledTimes(3);
714
+ });
715
+ });
716
+ });
717
+ });