@neo4j-nvl/interaction-handlers 1.2.2 → 2.0.0

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.
@@ -17,303 +17,489 @@ describe('ClickInteraction', () => {
17
17
  clickInteraction.destroy();
18
18
  myNVL.destroy();
19
19
  callbackMock.mockReset();
20
+ jest.restoreAllMocks();
20
21
  });
21
- test('clicking outside of nodes and relationships should not select anything', () => {
22
- clickInteraction.updateCallback('onCanvasClick', callbackMock);
23
- expect(myNVL.getSelectedNodes()).toHaveLength(0);
24
- let mouseEvent = new MouseEvent('mousedown', {
25
- clientX: 10,
26
- clientY: 10
27
- });
28
- myNVL.getContainer().dispatchEvent(mouseEvent);
29
- mouseEvent = new MouseEvent('click', {
30
- clientX: 10,
31
- clientY: 10
32
- });
33
- myNVL.getContainer().dispatchEvent(mouseEvent);
34
- return new Promise((resolve) => {
35
- expect(callbackMock).toHaveBeenCalledWith(mouseEvent);
36
- expect(callbackMock).toHaveBeenCalledTimes(1);
22
+ describe('clicks on empty canvas', () => {
23
+ test('clicking outside of nodes and relationships should not select anything', () => {
24
+ clickInteraction.updateCallback('onCanvasClick', callbackMock);
37
25
  expect(myNVL.getSelectedNodes()).toHaveLength(0);
38
- resolve();
26
+ let mouseEvent = new MouseEvent('mousedown', {
27
+ clientX: 10,
28
+ clientY: 10
29
+ });
30
+ myNVL.getContainer().dispatchEvent(mouseEvent);
31
+ mouseEvent = new MouseEvent('click', {
32
+ clientX: 10,
33
+ clientY: 10
34
+ });
35
+ myNVL.getContainer().dispatchEvent(mouseEvent);
36
+ return new Promise((resolve) => {
37
+ expect(callbackMock).toHaveBeenCalledWith(mouseEvent);
38
+ expect(callbackMock).toHaveBeenCalledTimes(1);
39
+ expect(myNVL.getSelectedNodes()).toHaveLength(0);
40
+ resolve();
41
+ });
39
42
  });
40
- });
41
- test('clicking outside of nodes and relationships should de-select everything', () => {
42
- clickInteraction.updateCallback('onCanvasClick', callbackMock);
43
- myNVL.updateElementsInGraph([
44
- { id: '0', selected: true },
45
- { id: '1', selected: true }
46
- ], []);
47
- expect(myNVL.getSelectedNodes()).toHaveLength(2);
48
- let mouseEvent = new MouseEvent('mousedown', {
49
- clientX: 10,
50
- clientY: 10
43
+ test('clicking outside of nodes and relationships should de-select everything', () => {
44
+ clickInteraction.updateCallback('onCanvasClick', callbackMock);
45
+ myNVL.updateElementsInGraph([
46
+ { id: '0', selected: true },
47
+ { id: '1', selected: true }
48
+ ], []);
49
+ expect(myNVL.getSelectedNodes()).toHaveLength(2);
50
+ let mouseEvent = new MouseEvent('mousedown', {
51
+ clientX: 10,
52
+ clientY: 10
53
+ });
54
+ myNVL.getContainer().dispatchEvent(mouseEvent);
55
+ mouseEvent = new MouseEvent('click', {
56
+ clientX: 10,
57
+ clientY: 10
58
+ });
59
+ myNVL.getContainer().dispatchEvent(mouseEvent);
60
+ return new Promise((resolve) => {
61
+ expect(callbackMock).toHaveBeenCalledWith(mouseEvent);
62
+ expect(callbackMock).toHaveBeenCalledTimes(1);
63
+ expect(myNVL.getSelectedNodes()).toHaveLength(0);
64
+ resolve();
65
+ });
51
66
  });
52
- myNVL.getContainer().dispatchEvent(mouseEvent);
53
- mouseEvent = new MouseEvent('click', {
54
- clientX: 10,
55
- clientY: 10
67
+ test(`clicking outside of nodes and relationships should not de-select anything
68
+ when selection mode is turned off but still trigger callback`, () => {
69
+ clickInteraction.destroy();
70
+ clickInteraction = new ClickInteraction(myNVL, { selectOnClick: false });
71
+ clickInteraction.updateCallback('onCanvasClick', callbackMock);
72
+ myNVL.updateElementsInGraph([
73
+ { id: '0', selected: true },
74
+ { id: '1', selected: true }
75
+ ], []);
76
+ expect(myNVL.getSelectedNodes()).toHaveLength(2);
77
+ let mouseEvent = new MouseEvent('mousedown', {
78
+ clientX: 10,
79
+ clientY: 10
80
+ });
81
+ myNVL.getContainer().dispatchEvent(mouseEvent);
82
+ mouseEvent = new MouseEvent('click', {
83
+ clientX: 10,
84
+ clientY: 10
85
+ });
86
+ myNVL.getContainer().dispatchEvent(mouseEvent);
87
+ return new Promise((resolve) => {
88
+ expect(callbackMock).toHaveBeenCalledWith(mouseEvent);
89
+ expect(callbackMock).toHaveBeenCalledTimes(1);
90
+ expect(myNVL.getSelectedNodes()).toHaveLength(2);
91
+ resolve();
92
+ });
56
93
  });
57
- myNVL.getContainer().dispatchEvent(mouseEvent);
58
- return new Promise((resolve) => {
59
- expect(callbackMock).toHaveBeenCalledWith(mouseEvent);
60
- expect(callbackMock).toHaveBeenCalledTimes(1);
61
- expect(myNVL.getSelectedNodes()).toHaveLength(0);
62
- resolve();
94
+ test('context menu event on canvas should trigger canvas right-click callback', () => {
95
+ clickInteraction.updateCallback('onCanvasRightClick', callbackMock);
96
+ const mouseEvent = new MouseEvent('contextmenu', {
97
+ clientX: 50,
98
+ clientY: 50
99
+ });
100
+ myNVL.getContainer().dispatchEvent(mouseEvent);
101
+ return new Promise((resolve) => {
102
+ expect(callbackMock).toHaveBeenCalledWith(mouseEvent);
103
+ expect(callbackMock).toHaveBeenCalledTimes(1);
104
+ resolve();
105
+ });
106
+ });
107
+ test('releasing right mouse button should not select anything and trigger no callbacks', () => {
108
+ clickInteraction.updateCallback('onNodeClick', callbackMock);
109
+ const mouseEvent = new MouseEvent('mouseup', {
110
+ clientX: 10,
111
+ clientY: 10,
112
+ button: 2
113
+ });
114
+ myNVL.getContainer().dispatchEvent(mouseEvent);
115
+ return new Promise((resolve) => {
116
+ expect(callbackMock).toHaveBeenCalledTimes(0);
117
+ resolve();
118
+ });
63
119
  });
64
120
  });
65
- test(`clicking outside of nodes and relationships should not de-select anything
66
- when selection mode is turned off but still trigger callback`, () => {
67
- clickInteraction.destroy();
68
- clickInteraction = new ClickInteraction(myNVL, { selectOnClick: false });
69
- clickInteraction.updateCallback('onCanvasClick', callbackMock);
70
- myNVL.updateElementsInGraph([
71
- { id: '0', selected: true },
72
- { id: '1', selected: true }
73
- ], []);
74
- expect(myNVL.getSelectedNodes()).toHaveLength(2);
75
- let mouseEvent = new MouseEvent('mousedown', {
76
- clientX: 10,
77
- clientY: 10
121
+ describe('clicks on nodes', () => {
122
+ test('clicking on a node should select it and trigger callback', () => {
123
+ clickInteraction.updateCallback('onNodeClick', callbackMock);
124
+ myNVL.getContainer().dispatchEvent(new MouseEvent('mousedown', {
125
+ clientX: 100,
126
+ clientY: 100
127
+ }));
128
+ const mouseEvent = new MouseEvent('click', {
129
+ clientX: 100,
130
+ clientY: 100
131
+ });
132
+ myNVL.getContainer().dispatchEvent(mouseEvent);
133
+ return new Promise((resolve) => {
134
+ expect(callbackMock).toHaveBeenCalledWith({ id: '0', selected: true, x: 100, y: 100 }, {
135
+ nodes: [
136
+ {
137
+ data: { id: '0', selected: true, x: 100, y: 100 },
138
+ distance: 0,
139
+ distanceVector: { x: 0, y: 0 },
140
+ insideNode: true,
141
+ pointerCoordinates: { x: 100, y: 100 },
142
+ targetCoordinates: { x: 100, y: 100 }
143
+ }
144
+ ],
145
+ relationships: [
146
+ {
147
+ data: { id: '10', from: '0', to: '1' },
148
+ distance: 0,
149
+ pointerCoordinates: { x: 100, y: 100 },
150
+ fromTargetCoordinates: { x: 100, y: 100 },
151
+ toTargetCoordinates: { x: 200, y: 200 }
152
+ }
153
+ ],
154
+ clusters: []
155
+ }, mouseEvent);
156
+ expect(callbackMock).toHaveBeenCalledTimes(1);
157
+ resolve();
158
+ });
78
159
  });
79
- myNVL.getContainer().dispatchEvent(mouseEvent);
80
- mouseEvent = new MouseEvent('click', {
81
- clientX: 10,
82
- clientY: 10
160
+ test('clicking on a node should not select it if selection mode is off but still trigger callback', () => {
161
+ clickInteraction.destroy();
162
+ clickInteraction = new ClickInteraction(myNVL, { selectOnClick: false });
163
+ clickInteraction.updateCallback('onNodeClick', callbackMock);
164
+ myNVL.getContainer().dispatchEvent(new MouseEvent('mousedown', {
165
+ clientX: 100,
166
+ clientY: 100
167
+ }));
168
+ const mouseEvent = new MouseEvent('click', {
169
+ clientX: 100,
170
+ clientY: 100
171
+ });
172
+ myNVL.getContainer().dispatchEvent(mouseEvent);
173
+ return new Promise((resolve) => {
174
+ expect(callbackMock).toHaveBeenCalledWith({ id: '0', x: 100, y: 100 }, {
175
+ nodes: [
176
+ {
177
+ data: { id: '0', x: 100, y: 100 },
178
+ distance: 0,
179
+ distanceVector: { x: 0, y: 0 },
180
+ insideNode: true,
181
+ pointerCoordinates: { x: 100, y: 100 },
182
+ targetCoordinates: { x: 100, y: 100 }
183
+ }
184
+ ],
185
+ relationships: [
186
+ {
187
+ data: { id: '10', from: '0', to: '1' },
188
+ distance: 0,
189
+ pointerCoordinates: { x: 100, y: 100 },
190
+ fromTargetCoordinates: { x: 100, y: 100 },
191
+ toTargetCoordinates: { x: 200, y: 200 }
192
+ }
193
+ ],
194
+ clusters: []
195
+ }, mouseEvent);
196
+ expect(callbackMock).toHaveBeenCalledTimes(1);
197
+ resolve();
198
+ });
83
199
  });
84
- myNVL.getContainer().dispatchEvent(mouseEvent);
85
- return new Promise((resolve) => {
86
- expect(callbackMock).toHaveBeenCalledWith(mouseEvent);
87
- expect(callbackMock).toHaveBeenCalledTimes(1);
88
- expect(myNVL.getSelectedNodes()).toHaveLength(2);
89
- resolve();
200
+ test('clicking on a node should de-select other selected nodes and trigger callback', () => {
201
+ clickInteraction.updateCallback('onNodeClick', callbackMock);
202
+ myNVL.updateElementsInGraph([{ id: '0' }, { id: '1', selected: true }], []);
203
+ expect(myNVL.getSelectedNodes()).toEqual([{ id: '1', selected: true, x: 200, y: 200 }]);
204
+ myNVL.getContainer().dispatchEvent(new MouseEvent('mousedown', {
205
+ clientX: 100,
206
+ clientY: 100
207
+ }));
208
+ const mouseEvent = new MouseEvent('click', {
209
+ clientX: 100,
210
+ clientY: 100
211
+ });
212
+ myNVL.getContainer().dispatchEvent(mouseEvent);
213
+ return new Promise((resolve) => {
214
+ expect(callbackMock).toHaveBeenCalledWith({ id: '0', selected: true, x: 100, y: 100 }, {
215
+ nodes: [
216
+ {
217
+ data: { id: '0', selected: true, x: 100, y: 100 },
218
+ distance: 0,
219
+ distanceVector: { x: 0, y: 0 },
220
+ insideNode: true,
221
+ pointerCoordinates: { x: 100, y: 100 },
222
+ targetCoordinates: { x: 100, y: 100 }
223
+ }
224
+ ],
225
+ relationships: [
226
+ {
227
+ data: { id: '10', from: '0', to: '1' },
228
+ distance: 0,
229
+ pointerCoordinates: { x: 100, y: 100 },
230
+ fromTargetCoordinates: { x: 100, y: 100 },
231
+ toTargetCoordinates: { x: 200, y: 200 }
232
+ }
233
+ ],
234
+ clusters: []
235
+ }, mouseEvent);
236
+ expect(callbackMock).toHaveBeenCalledTimes(1);
237
+ expect(myNVL.getSelectedNodes()).toEqual([{ id: '0', selected: true, x: 100, y: 100 }]);
238
+ resolve();
239
+ });
90
240
  });
91
- });
92
- test('clicking on a node should select it and trigger callback', () => {
93
- clickInteraction.updateCallback('onNodeClick', callbackMock);
94
- myNVL.getContainer().dispatchEvent(new MouseEvent('mousedown', {
95
- clientX: 100,
96
- clientY: 100
97
- }));
98
- const mouseEvent = new MouseEvent('click', {
99
- clientX: 100,
100
- clientY: 100
241
+ test('clicking an already selected node should de-select it', () => {
242
+ clickInteraction.updateCallback('onNodeClick', callbackMock);
243
+ myNVL.updateElementsInGraph([{ id: '0', selected: true }], []);
244
+ expect(myNVL.getSelectedNodes().map((node) => node.id)).toEqual(['0']);
245
+ myNVL.getContainer().dispatchEvent(new MouseEvent('mousedown', { clientX: 100, clientY: 100 }));
246
+ myNVL.getContainer().dispatchEvent(new MouseEvent('click', { clientX: 100, clientY: 100 }));
247
+ expect(myNVL.getSelectedNodes()).toHaveLength(0);
101
248
  });
102
- myNVL.getContainer().dispatchEvent(mouseEvent);
103
- return new Promise((resolve) => {
104
- expect(callbackMock).toHaveBeenCalledWith({ id: '0', selected: true, x: 100, y: 100 }, {
105
- nodes: [
106
- {
107
- data: { id: '0', selected: true, x: 100, y: 100 },
108
- distance: 0,
109
- distanceVector: { x: 0, y: 0 },
110
- insideNode: true,
111
- pointerCoordinates: { x: 100, y: 100 },
112
- targetCoordinates: { x: 100, y: 100 }
113
- }
114
- ],
115
- relationships: [
116
- {
117
- data: { id: '10', from: '0', to: '1' },
118
- distance: 0,
119
- pointerCoordinates: { x: 100, y: 100 },
120
- fromTargetCoordinates: { x: 100, y: 100 },
121
- toTargetCoordinates: { x: 200, y: 200 }
122
- }
123
- ]
124
- }, mouseEvent);
125
- expect(callbackMock).toHaveBeenCalledTimes(1);
126
- resolve();
249
+ test('context menu event on a node should trigger node right-click callback', () => {
250
+ clickInteraction.updateCallback('onNodeRightClick', callbackMock);
251
+ const mouseEvent = new MouseEvent('contextmenu', {
252
+ clientX: 100,
253
+ clientY: 100
254
+ });
255
+ myNVL.getContainer().dispatchEvent(mouseEvent);
256
+ return new Promise((resolve) => {
257
+ expect(callbackMock).toHaveBeenCalledWith({ id: '0', x: 100, y: 100 }, {
258
+ nodes: [
259
+ {
260
+ data: { id: '0', x: 100, y: 100 },
261
+ distance: 0,
262
+ distanceVector: { x: 0, y: 0 },
263
+ insideNode: true,
264
+ pointerCoordinates: { x: 100, y: 100 },
265
+ targetCoordinates: { x: 100, y: 100 }
266
+ }
267
+ ],
268
+ relationships: [
269
+ {
270
+ data: { id: '10', from: '0', to: '1' },
271
+ distance: 0,
272
+ pointerCoordinates: { x: 100, y: 100 },
273
+ fromTargetCoordinates: { x: 100, y: 100 },
274
+ toTargetCoordinates: { x: 200, y: 200 }
275
+ }
276
+ ],
277
+ clusters: []
278
+ }, mouseEvent);
279
+ expect(callbackMock).toHaveBeenCalledTimes(1);
280
+ resolve();
281
+ });
127
282
  });
128
283
  });
129
- test('clicking on a relationship should select it and trigger callback', () => {
130
- clickInteraction.updateCallback('onRelationshipClick', callbackMock);
131
- myNVL.getContainer().dispatchEvent(new MouseEvent('mousedown', {
132
- clientX: 150,
133
- clientY: 150
134
- }));
135
- const mouseEvent = new MouseEvent('click', {
136
- clientX: 150,
137
- clientY: 150
284
+ describe('clicks on relationships', () => {
285
+ test('clicking on a relationship should select it and trigger callback', () => {
286
+ clickInteraction.updateCallback('onRelationshipClick', callbackMock);
287
+ myNVL.getContainer().dispatchEvent(new MouseEvent('mousedown', {
288
+ clientX: 150,
289
+ clientY: 150
290
+ }));
291
+ const mouseEvent = new MouseEvent('click', {
292
+ clientX: 150,
293
+ clientY: 150
294
+ });
295
+ myNVL.getContainer().dispatchEvent(mouseEvent);
296
+ return new Promise((resolve) => {
297
+ expect(callbackMock).toHaveBeenCalledWith({ id: '10', selected: true, from: '0', to: '1' }, {
298
+ nodes: [],
299
+ relationships: [
300
+ {
301
+ data: { id: '10', selected: true, from: '0', to: '1' },
302
+ distance: 0,
303
+ pointerCoordinates: { x: 150, y: 150 },
304
+ fromTargetCoordinates: { x: 100, y: 100 },
305
+ toTargetCoordinates: { x: 200, y: 200 }
306
+ }
307
+ ],
308
+ clusters: []
309
+ }, mouseEvent);
310
+ expect(callbackMock).toHaveBeenCalledTimes(1);
311
+ resolve();
312
+ });
138
313
  });
139
- myNVL.getContainer().dispatchEvent(mouseEvent);
140
- return new Promise((resolve) => {
141
- expect(callbackMock).toHaveBeenCalledWith({ id: '10', selected: true, from: '0', to: '1' }, {
142
- nodes: [],
143
- relationships: [
144
- {
145
- data: { id: '10', selected: true, from: '0', to: '1' },
146
- distance: 0,
147
- pointerCoordinates: { x: 150, y: 150 },
148
- fromTargetCoordinates: { x: 100, y: 100 },
149
- toTargetCoordinates: { x: 200, y: 200 }
150
- }
151
- ]
152
- }, mouseEvent);
153
- expect(callbackMock).toHaveBeenCalledTimes(1);
154
- resolve();
314
+ test('clicking an already selected relationship should de-select it', () => {
315
+ clickInteraction.updateCallback('onRelationshipClick', callbackMock);
316
+ myNVL.updateElementsInGraph([], [{ id: '10', from: '0', to: '1', selected: true }]);
317
+ expect(myNVL.getSelectedRelationships().map((relationship) => relationship.id)).toEqual(['10']);
318
+ myNVL.getContainer().dispatchEvent(new MouseEvent('mousedown', { clientX: 150, clientY: 150 }));
319
+ myNVL.getContainer().dispatchEvent(new MouseEvent('click', { clientX: 150, clientY: 150 }));
320
+ expect(myNVL.getSelectedRelationships()).toHaveLength(0);
155
321
  });
156
- });
157
- test('clicking on a node should not select it if selection mode is off but still trigger callback', () => {
158
- clickInteraction.destroy();
159
- clickInteraction = new ClickInteraction(myNVL, { selectOnClick: false });
160
- clickInteraction.updateCallback('onNodeClick', callbackMock);
161
- myNVL.getContainer().dispatchEvent(new MouseEvent('mousedown', {
162
- clientX: 100,
163
- clientY: 100
164
- }));
165
- const mouseEvent = new MouseEvent('click', {
166
- clientX: 100,
167
- clientY: 100
322
+ test('context menu event on a relationship should trigger relationship right-click callback', () => {
323
+ clickInteraction.updateCallback('onRelationshipRightClick', callbackMock);
324
+ const mouseEvent = new MouseEvent('contextmenu', {
325
+ clientX: 150,
326
+ clientY: 150
327
+ });
328
+ myNVL.getContainer().dispatchEvent(mouseEvent);
329
+ return new Promise((resolve) => {
330
+ expect(callbackMock).toHaveBeenCalledWith({ id: '10', from: '0', to: '1' }, {
331
+ nodes: [],
332
+ relationships: [
333
+ {
334
+ data: { id: '10', from: '0', to: '1' },
335
+ distance: 0,
336
+ pointerCoordinates: { x: 150, y: 150 },
337
+ fromTargetCoordinates: { x: 100, y: 100 },
338
+ toTargetCoordinates: { x: 200, y: 200 }
339
+ }
340
+ ],
341
+ clusters: []
342
+ }, mouseEvent);
343
+ expect(callbackMock).toHaveBeenCalledTimes(1);
344
+ resolve();
345
+ });
168
346
  });
169
- myNVL.getContainer().dispatchEvent(mouseEvent);
170
- return new Promise((resolve) => {
171
- expect(callbackMock).toHaveBeenCalledWith({ id: '0', x: 100, y: 100 }, {
172
- nodes: [
173
- {
174
- data: { id: '0', x: 100, y: 100 },
175
- distance: 0,
176
- distanceVector: { x: 0, y: 0 },
177
- insideNode: true,
178
- pointerCoordinates: { x: 100, y: 100 },
179
- targetCoordinates: { x: 100, y: 100 }
180
- }
181
- ],
182
- relationships: [
347
+ });
348
+ describe('clicks on cluster hulls', () => {
349
+ test('clicking a cluster hull should trigger onClusterClick and select member nodes', () => {
350
+ clickInteraction.updateCallback('onClusterClick', callbackMock);
351
+ const cluster = { id: 'group-a', nodeIds: ['0', '1'] };
352
+ const nvlTargets = {
353
+ nodes: [],
354
+ relationships: [],
355
+ clusters: [
183
356
  {
184
- data: { id: '10', from: '0', to: '1' },
185
- distance: 0,
186
- pointerCoordinates: { x: 100, y: 100 },
187
- fromTargetCoordinates: { x: 100, y: 100 },
188
- toTargetCoordinates: { x: 200, y: 200 }
357
+ data: cluster,
358
+ pointerCoordinates: { x: 50, y: 50 },
359
+ hull: [
360
+ { x: 0, y: 0 },
361
+ { x: 100, y: 0 },
362
+ { x: 100, y: 100 },
363
+ { x: 0, y: 100 }
364
+ ]
189
365
  }
190
366
  ]
191
- }, mouseEvent);
192
- expect(callbackMock).toHaveBeenCalledTimes(1);
193
- resolve();
194
- });
195
- });
196
- test('clicking on a node should de-select other selected nodes and trigger callback', () => {
197
- clickInteraction.updateCallback('onNodeClick', callbackMock);
198
- myNVL.updateElementsInGraph([{ id: '0' }, { id: '1', selected: true }], []);
199
- expect(myNVL.getSelectedNodes()).toEqual([{ id: '1', selected: true, x: 200, y: 200 }]);
200
- myNVL.getContainer().dispatchEvent(new MouseEvent('mousedown', {
201
- clientX: 100,
202
- clientY: 100
203
- }));
204
- const mouseEvent = new MouseEvent('click', {
205
- clientX: 100,
206
- clientY: 100
367
+ };
368
+ jest.spyOn(myNVL, 'getHits').mockImplementation((event) => Object.assign(event, { nvlTargets }));
369
+ myNVL.getContainer().dispatchEvent(new MouseEvent('mousedown', {
370
+ clientX: 50,
371
+ clientY: 50
372
+ }));
373
+ const mouseEvent = new MouseEvent('click', {
374
+ clientX: 50,
375
+ clientY: 50
376
+ });
377
+ myNVL.getContainer().dispatchEvent(mouseEvent);
378
+ expect(callbackMock).toHaveBeenCalledWith(cluster, nvlTargets, mouseEvent);
379
+ expect(myNVL
380
+ .getSelectedNodes()
381
+ .map((n) => n.id)
382
+ .sort()).toEqual(['0', '1']);
207
383
  });
208
- myNVL.getContainer().dispatchEvent(mouseEvent);
209
- return new Promise((resolve) => {
210
- expect(callbackMock).toHaveBeenCalledWith({ id: '0', selected: true, x: 100, y: 100 }, {
211
- nodes: [
212
- {
213
- data: { id: '0', selected: true, x: 100, y: 100 },
214
- distance: 0,
215
- distanceVector: { x: 0, y: 0 },
216
- insideNode: true,
217
- pointerCoordinates: { x: 100, y: 100 },
218
- targetCoordinates: { x: 100, y: 100 }
219
- }
220
- ],
221
- relationships: [
384
+ test('clicking a cluster hull whose members are all selected should de-select them', () => {
385
+ clickInteraction.updateCallback('onClusterClick', callbackMock);
386
+ const cluster = { id: 'group-a', nodeIds: ['0', '1'] };
387
+ const nvlTargets = {
388
+ nodes: [],
389
+ relationships: [],
390
+ clusters: [
222
391
  {
223
- data: { id: '10', from: '0', to: '1' },
224
- distance: 0,
225
- pointerCoordinates: { x: 100, y: 100 },
226
- fromTargetCoordinates: { x: 100, y: 100 },
227
- toTargetCoordinates: { x: 200, y: 200 }
392
+ data: cluster,
393
+ pointerCoordinates: { x: 50, y: 50 },
394
+ hull: [
395
+ { x: 0, y: 0 },
396
+ { x: 100, y: 0 },
397
+ { x: 100, y: 100 },
398
+ { x: 0, y: 100 }
399
+ ]
228
400
  }
229
401
  ]
230
- }, mouseEvent);
231
- expect(callbackMock).toHaveBeenCalledTimes(1);
232
- expect(myNVL.getSelectedNodes()).toEqual([{ id: '0', selected: true, x: 100, y: 100 }]);
233
- resolve();
234
- });
235
- });
236
- test('context menu event on canvas should trigger canvas right-click callback', () => {
237
- clickInteraction.updateCallback('onCanvasRightClick', callbackMock);
238
- const mouseEvent = new MouseEvent('contextmenu', {
239
- clientX: 50,
240
- clientY: 50
241
- });
242
- myNVL.getContainer().dispatchEvent(mouseEvent);
243
- return new Promise((resolve) => {
244
- expect(callbackMock).toHaveBeenCalledWith(mouseEvent);
245
- expect(callbackMock).toHaveBeenCalledTimes(1);
246
- resolve();
247
- });
248
- });
249
- test('context menu event on a node should trigger node right-click callback', () => {
250
- clickInteraction.updateCallback('onNodeRightClick', callbackMock);
251
- const mouseEvent = new MouseEvent('contextmenu', {
252
- clientX: 100,
253
- clientY: 100
402
+ };
403
+ jest.spyOn(myNVL, 'getHits').mockImplementation((event) => Object.assign(event, { nvlTargets }));
404
+ myNVL.updateElementsInGraph([
405
+ { id: '0', selected: true },
406
+ { id: '1', selected: true }
407
+ ], []);
408
+ myNVL.getContainer().dispatchEvent(new MouseEvent('mousedown', { clientX: 50, clientY: 50 }));
409
+ myNVL.getContainer().dispatchEvent(new MouseEvent('click', { clientX: 50, clientY: 50 }));
410
+ expect(myNVL.getSelectedNodes()).toHaveLength(0);
254
411
  });
255
- myNVL.getContainer().dispatchEvent(mouseEvent);
256
- return new Promise((resolve) => {
257
- expect(callbackMock).toHaveBeenCalledWith({ id: '0', x: 100, y: 100 }, {
258
- nodes: [
259
- {
260
- data: { id: '0', x: 100, y: 100 },
261
- distance: 0,
262
- distanceVector: { x: 0, y: 0 },
263
- insideNode: true,
264
- pointerCoordinates: { x: 100, y: 100 },
265
- targetCoordinates: { x: 100, y: 100 }
266
- }
267
- ],
268
- relationships: [
412
+ test('clicking a cluster hull with only some members selected should select all of them', () => {
413
+ clickInteraction.updateCallback('onClusterClick', callbackMock);
414
+ const cluster = { id: 'group-a', nodeIds: ['0', '1'] };
415
+ const nvlTargets = {
416
+ nodes: [],
417
+ relationships: [],
418
+ clusters: [
269
419
  {
270
- data: { id: '10', from: '0', to: '1' },
271
- distance: 0,
272
- pointerCoordinates: { x: 100, y: 100 },
273
- fromTargetCoordinates: { x: 100, y: 100 },
274
- toTargetCoordinates: { x: 200, y: 200 }
420
+ data: cluster,
421
+ pointerCoordinates: { x: 50, y: 50 },
422
+ hull: [
423
+ { x: 0, y: 0 },
424
+ { x: 100, y: 0 },
425
+ { x: 100, y: 100 },
426
+ { x: 0, y: 100 }
427
+ ]
275
428
  }
276
429
  ]
277
- }, mouseEvent);
278
- expect(callbackMock).toHaveBeenCalledTimes(1);
279
- resolve();
280
- });
281
- });
282
- test('context menu event on a relationship should trigger relationship right-click callback', () => {
283
- clickInteraction.updateCallback('onRelationshipRightClick', callbackMock);
284
- const mouseEvent = new MouseEvent('contextmenu', {
285
- clientX: 150,
286
- clientY: 150
430
+ };
431
+ jest.spyOn(myNVL, 'getHits').mockImplementation((event) => Object.assign(event, { nvlTargets }));
432
+ myNVL.updateElementsInGraph([{ id: '0', selected: true }], []);
433
+ myNVL.getContainer().dispatchEvent(new MouseEvent('mousedown', { clientX: 50, clientY: 50 }));
434
+ myNVL.getContainer().dispatchEvent(new MouseEvent('click', { clientX: 50, clientY: 50 }));
435
+ expect(myNVL
436
+ .getSelectedNodes()
437
+ .map((node) => node.id)
438
+ .sort()).toEqual(['0', '1']);
287
439
  });
288
- myNVL.getContainer().dispatchEvent(mouseEvent);
289
- return new Promise((resolve) => {
290
- expect(callbackMock).toHaveBeenCalledWith({ id: '10', from: '0', to: '1' }, {
440
+ test.each([
441
+ ['onClusterDoubleClick', 'dblclick'],
442
+ ['onClusterRightClick', 'contextmenu']
443
+ ])('%s is triggered for a cluster hull', (callbackName, eventName) => {
444
+ clickInteraction.updateCallback(callbackName, callbackMock);
445
+ const cluster = { id: 'group-a', nodeIds: ['0', '1'] };
446
+ const nvlTargets = {
291
447
  nodes: [],
292
- relationships: [
448
+ relationships: [],
449
+ clusters: [
293
450
  {
294
- data: { id: '10', from: '0', to: '1' },
295
- distance: 0,
296
- pointerCoordinates: { x: 150, y: 150 },
297
- fromTargetCoordinates: { x: 100, y: 100 },
298
- toTargetCoordinates: { x: 200, y: 200 }
451
+ data: cluster,
452
+ pointerCoordinates: { x: 50, y: 50 },
453
+ hull: [
454
+ { x: 0, y: 0 },
455
+ { x: 100, y: 0 },
456
+ { x: 100, y: 100 },
457
+ { x: 0, y: 100 }
458
+ ]
299
459
  }
300
460
  ]
301
- }, mouseEvent);
302
- expect(callbackMock).toHaveBeenCalledTimes(1);
303
- resolve();
461
+ };
462
+ jest.spyOn(myNVL, 'getHits').mockImplementation((event) => Object.assign(event, { nvlTargets }));
463
+ const mouseEvent = new MouseEvent(eventName, { clientX: 50, clientY: 50 });
464
+ myNVL.getContainer().dispatchEvent(mouseEvent);
465
+ expect(callbackMock).toHaveBeenCalledWith(cluster, nvlTargets, mouseEvent);
304
466
  });
305
- });
306
- test('releasing right mouse button should not select anything and trigger no callbacks', () => {
307
- clickInteraction.updateCallback('onNodeClick', callbackMock);
308
- const mouseEvent = new MouseEvent('mouseup', {
309
- clientX: 10,
310
- clientY: 10,
311
- button: 2
312
- });
313
- myNVL.getContainer().dispatchEvent(mouseEvent);
314
- return new Promise((resolve) => {
315
- expect(callbackMock).toHaveBeenCalledTimes(0);
316
- resolve();
467
+ test('node hits take priority over cluster hits', () => {
468
+ clickInteraction.updateCallback('onNodeClick', callbackMock);
469
+ clickInteraction.updateCallback('onClusterClick', jest.fn());
470
+ const clusterCallback = jest.fn();
471
+ clickInteraction.updateCallback('onClusterClick', clusterCallback);
472
+ jest.spyOn(myNVL, 'getHits').mockImplementation((event) => Object.assign(event, {
473
+ nvlTargets: {
474
+ nodes: [
475
+ {
476
+ data: { id: '0', x: 100, y: 100 },
477
+ distance: 0,
478
+ distanceVector: { x: 0, y: 0 },
479
+ insideNode: true,
480
+ pointerCoordinates: { x: 100, y: 100 },
481
+ targetCoordinates: { x: 100, y: 100 }
482
+ }
483
+ ],
484
+ relationships: [],
485
+ clusters: [
486
+ {
487
+ data: { id: 'group-a', nodeIds: ['0', '1'] },
488
+ pointerCoordinates: { x: 100, y: 100 },
489
+ hull: [
490
+ { x: 0, y: 0 },
491
+ { x: 200, y: 0 },
492
+ { x: 200, y: 200 },
493
+ { x: 0, y: 200 }
494
+ ]
495
+ }
496
+ ]
497
+ }
498
+ }));
499
+ myNVL.getContainer().dispatchEvent(new MouseEvent('mousedown', { clientX: 100, clientY: 100 }));
500
+ myNVL.getContainer().dispatchEvent(new MouseEvent('click', { clientX: 100, clientY: 100 }));
501
+ expect(callbackMock).toHaveBeenCalledTimes(1);
502
+ expect(clusterCallback).not.toHaveBeenCalled();
317
503
  });
318
504
  });
319
505
  });