@neo4j-nvl/react 2.0.0 → 2.1.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.
- package/CHANGELOG.md +25 -1
- package/README.md +8 -0
- package/lib/basic-wrapper/BasicNvlWrapper.d.ts +58 -32
- package/lib/basic-wrapper/BasicNvlWrapper.js +129 -143
- package/lib/index.d.ts +5 -8
- package/lib/index.js +3 -3
- package/lib/interactive-nvl-wrapper/InteractionHandlers.d.ts +8 -8
- package/lib/interactive-nvl-wrapper/InteractionHandlers.js +62 -59
- package/lib/interactive-nvl-wrapper/InteractiveNvlWrapper.d.ts +5 -4
- package/lib/interactive-nvl-wrapper/InteractiveNvlWrapper.js +55 -42
- package/lib/interactive-nvl-wrapper/hooks.d.ts +4 -4
- package/lib/interactive-nvl-wrapper/hooks.js +24 -27
- package/lib/interactive-nvl-wrapper/types.d.ts +24 -9
- package/lib/interactive-nvl-wrapper/types.js +0 -1
- package/lib/static-picture-wrapper/StaticPictureWrapper.d.ts +21 -15
- package/lib/static-picture-wrapper/StaticPictureWrapper.js +77 -55
- package/lib/utils/constants.d.ts +1 -1
- package/lib/utils/constants.js +4 -2
- package/lib/utils/graph-comparison.d.ts +5 -5
- package/lib/utils/graph-comparison.js +53 -68
- package/lib/utils/hooks.d.ts +2 -2
- package/lib/utils/hooks.js +10 -11
- package/package.json +24 -10
- package/lib/__tests__/BasicNvlWrapper.test.d.ts +0 -1
- package/lib/__tests__/BasicNvlWrapper.test.js +0 -159
- package/lib/__tests__/InteractiveNvlWrapper.test.d.ts +0 -1
- package/lib/__tests__/InteractiveNvlWrapper.test.js +0 -396
- package/lib/__tests__/StaticPictureWrapper.test.d.ts +0 -1
- package/lib/__tests__/StaticPictureWrapper.test.js +0 -85
- package/lib/__tests__/index.test.d.ts +0 -1
- package/lib/__tests__/index.test.js +0 -8
package/package.json
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neo4j-nvl/react",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
|
+
"type": "module",
|
|
4
5
|
"main": "lib/index.js",
|
|
6
|
+
"types": "lib/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./lib/index.d.ts",
|
|
10
|
+
"import": "./lib/index.js",
|
|
11
|
+
"default": "./lib/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./package.json": "./package.json"
|
|
14
|
+
},
|
|
5
15
|
"homepage": "https://neo4j.com/docs/nvl/current/",
|
|
6
16
|
"license": "SEE LICENSE IN 'LICENSE.txt'",
|
|
7
17
|
"description": "React wrappers for the Neo4j Visualization Library",
|
|
@@ -15,8 +25,8 @@
|
|
|
15
25
|
"url": "https://community.neo4j.com/c/neo4j-graph-platform/neo4j-bloom"
|
|
16
26
|
},
|
|
17
27
|
"scripts": {
|
|
18
|
-
"build": "yarn global:tsc",
|
|
19
|
-
"watch": "
|
|
28
|
+
"build": "yarn global:tsc --noEmit && tsdown",
|
|
29
|
+
"watch": "tsdown --watch",
|
|
20
30
|
"test": " yarn global:jest",
|
|
21
31
|
"prepack": "cp ../../LICENSE.txt ./ && cp ../../CHANGELOG.md ./",
|
|
22
32
|
"postpack": "rm LICENSE.txt && rm CHANGELOG.md",
|
|
@@ -26,22 +36,26 @@
|
|
|
26
36
|
"LICENSE.txt",
|
|
27
37
|
"lib"
|
|
28
38
|
],
|
|
39
|
+
"skills": [
|
|
40
|
+
"npm:@neo4j-nvl/base"
|
|
41
|
+
],
|
|
29
42
|
"devDependencies": {
|
|
30
43
|
"@testing-library/dom": "10.4.1",
|
|
31
44
|
"@testing-library/jest-dom": "5.17.0",
|
|
32
45
|
"@testing-library/react": "16.3.0",
|
|
33
|
-
"@types/lodash": "4.17.
|
|
46
|
+
"@types/lodash-es": "4.17.12",
|
|
34
47
|
"@types/react": "18.3.27",
|
|
35
48
|
"react": "19.2.1",
|
|
36
|
-
"react-dom": "19.2.1"
|
|
49
|
+
"react-dom": "19.2.1",
|
|
50
|
+
"tsdown": "0.23.1"
|
|
37
51
|
},
|
|
38
52
|
"dependencies": {
|
|
39
|
-
"@neo4j-nvl/base": "2.
|
|
40
|
-
"@neo4j-nvl/interaction-handlers": "2.
|
|
41
|
-
"lodash": "4.18.1"
|
|
53
|
+
"@neo4j-nvl/base": "2.1.0",
|
|
54
|
+
"@neo4j-nvl/interaction-handlers": "2.1.0",
|
|
55
|
+
"lodash-es": "4.18.1"
|
|
42
56
|
},
|
|
43
57
|
"peerDependencies": {
|
|
44
|
-
"react": "18.0.0 || ^19.0.0",
|
|
45
|
-
"react-dom": "18.0.0 || ^19.0.0"
|
|
58
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
59
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
46
60
|
}
|
|
47
61
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import '@testing-library/jest-dom';
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import NVL, { HierarchicalLayoutType } from '@neo4j-nvl/base';
|
|
3
|
-
import '@testing-library/jest-dom';
|
|
4
|
-
import { render } from '@testing-library/react';
|
|
5
|
-
import { StrictMode } from 'react';
|
|
6
|
-
import { BasicNvlWrapper } from '../basic-wrapper/BasicNvlWrapper';
|
|
7
|
-
jest.mock('@neo4j-nvl/base');
|
|
8
|
-
jest.mock('@neo4j-nvl/layout-workers');
|
|
9
|
-
describe('BasicNvlWrapper', () => {
|
|
10
|
-
let setZoom;
|
|
11
|
-
let setPan;
|
|
12
|
-
let setZoomAndPan;
|
|
13
|
-
beforeEach(() => {
|
|
14
|
-
setZoom = jest.fn();
|
|
15
|
-
setPan = jest.fn();
|
|
16
|
-
setZoomAndPan = jest.fn();
|
|
17
|
-
jest.spyOn(NVL.prototype, 'setZoom').mockImplementation(setZoom);
|
|
18
|
-
jest.spyOn(NVL.prototype, 'setPan').mockImplementation(setPan);
|
|
19
|
-
jest.spyOn(NVL.prototype, 'setZoomAndPan').mockImplementation(setZoomAndPan);
|
|
20
|
-
});
|
|
21
|
-
afterEach(() => {
|
|
22
|
-
jest.clearAllMocks();
|
|
23
|
-
});
|
|
24
|
-
test('initialises NVL expectedly with an empty graph object and no other properties', () => {
|
|
25
|
-
render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [] }) }));
|
|
26
|
-
expect(NVL).toHaveBeenCalledWith(expect.any(HTMLDivElement), [], [], {}, {});
|
|
27
|
-
});
|
|
28
|
-
test('initialises NVL expectedly with a graph object and properties', () => {
|
|
29
|
-
const mockLayoutDoneFunction = jest.fn();
|
|
30
|
-
render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [{ id: '1' }, { id: '2' }], rels: [{ id: '12', from: '1', to: '2' }], nvlOptions: {
|
|
31
|
-
renderer: 'canvas',
|
|
32
|
-
layout: HierarchicalLayoutType,
|
|
33
|
-
layoutOptions: { direction: 'down' }
|
|
34
|
-
}, nvlCallbacks: { onLayoutDone: mockLayoutDoneFunction } }) }));
|
|
35
|
-
expect(NVL).toHaveBeenCalledWith(expect.any(HTMLDivElement), [{ id: '1' }, { id: '2' }], [{ id: '12', from: '1', to: '2' }], { renderer: 'canvas', layout: HierarchicalLayoutType, layoutOptions: { direction: 'down' } }, {
|
|
36
|
-
onLayoutDone: mockLayoutDoneFunction
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
test('destroys NVL on unmount', () => {
|
|
40
|
-
const destroy = jest.fn();
|
|
41
|
-
jest.spyOn(NVL.prototype, 'destroy').mockImplementation(destroy);
|
|
42
|
-
const { unmount } = render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [] }) }));
|
|
43
|
-
unmount();
|
|
44
|
-
expect(NVL).toHaveBeenCalledTimes(1);
|
|
45
|
-
expect(destroy).toHaveBeenCalledTimes(1);
|
|
46
|
-
});
|
|
47
|
-
test('successfully re-initialises NVL when using React StrictMode', () => {
|
|
48
|
-
const destroy = jest.fn();
|
|
49
|
-
jest.spyOn(NVL.prototype, 'destroy').mockImplementation(destroy);
|
|
50
|
-
render(_jsx(StrictMode, { children: _jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [] }) }) }));
|
|
51
|
-
expect(NVL).toHaveBeenCalledTimes(2);
|
|
52
|
-
expect(destroy).toHaveBeenCalledTimes(1);
|
|
53
|
-
});
|
|
54
|
-
test('reapplies zoom and pan when re-initialising NVL with React StrictMode', () => {
|
|
55
|
-
const destroy = jest.fn();
|
|
56
|
-
jest.spyOn(NVL.prototype, 'destroy').mockImplementation(destroy);
|
|
57
|
-
render(_jsx(StrictMode, { children: _jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 1.5, pan: { x: 10, y: 20 } }) }) }));
|
|
58
|
-
expect(NVL).toHaveBeenCalledTimes(2);
|
|
59
|
-
expect(destroy).toHaveBeenCalledTimes(1);
|
|
60
|
-
expect(setZoomAndPan).toHaveBeenCalledTimes(2);
|
|
61
|
-
expect(setZoomAndPan).toHaveBeenNthCalledWith(1, 1.5, 10, 20);
|
|
62
|
-
expect(setZoomAndPan).toHaveBeenNthCalledWith(2, 1.5, 10, 20);
|
|
63
|
-
});
|
|
64
|
-
test('calls setZoom when zoom property is provided', () => {
|
|
65
|
-
render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 1.5 }) }));
|
|
66
|
-
expect(setZoom).toHaveBeenCalledWith(1.5);
|
|
67
|
-
expect(setZoom).toHaveBeenCalledTimes(1);
|
|
68
|
-
});
|
|
69
|
-
test('calls setPan when pan property is provided', () => {
|
|
70
|
-
render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], pan: { x: 10, y: 20 } }) }));
|
|
71
|
-
expect(setPan).toHaveBeenCalledWith(10, 20);
|
|
72
|
-
expect(setPan).toHaveBeenCalledTimes(1);
|
|
73
|
-
});
|
|
74
|
-
test('calls setZoomAndPan when both zoom and pan properties are provided', () => {
|
|
75
|
-
render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 1.5, pan: { x: 10, y: 20 } }) }));
|
|
76
|
-
expect(setZoomAndPan).toHaveBeenCalledWith(1.5, 10, 20);
|
|
77
|
-
expect(setZoomAndPan).toHaveBeenCalledTimes(1);
|
|
78
|
-
});
|
|
79
|
-
test('does not call zoom/pan methods when properties are undefined', () => {
|
|
80
|
-
render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [] }) }));
|
|
81
|
-
expect(setZoom).not.toHaveBeenCalled();
|
|
82
|
-
expect(setPan).not.toHaveBeenCalled();
|
|
83
|
-
expect(setZoomAndPan).not.toHaveBeenCalled();
|
|
84
|
-
});
|
|
85
|
-
test('only calls setZoom when zoom changes', () => {
|
|
86
|
-
const { rerender } = render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 1.5 }) }));
|
|
87
|
-
expect(setZoom).toHaveBeenCalledTimes(1);
|
|
88
|
-
expect(setZoom).toHaveBeenCalledWith(1.5);
|
|
89
|
-
rerender(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 1.5 }) }));
|
|
90
|
-
expect(setZoom).toHaveBeenCalledTimes(1);
|
|
91
|
-
rerender(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 2.0 }) }));
|
|
92
|
-
expect(setZoom).toHaveBeenCalledTimes(2);
|
|
93
|
-
expect(setZoom).toHaveBeenCalledWith(2.0);
|
|
94
|
-
expect(setPan).not.toHaveBeenCalled();
|
|
95
|
-
expect(setZoomAndPan).not.toHaveBeenCalled();
|
|
96
|
-
});
|
|
97
|
-
test('only calls setPan when pan changes', () => {
|
|
98
|
-
const { rerender } = render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], pan: { x: 10, y: 20 } }) }));
|
|
99
|
-
expect(setPan).toHaveBeenCalledTimes(1);
|
|
100
|
-
expect(setPan).toHaveBeenCalledWith(10, 20);
|
|
101
|
-
rerender(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], pan: { x: 10, y: 20 } }) }));
|
|
102
|
-
expect(setPan).toHaveBeenCalledTimes(1);
|
|
103
|
-
rerender(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], pan: { x: 30, y: 40 } }) }));
|
|
104
|
-
expect(setPan).toHaveBeenCalledTimes(2);
|
|
105
|
-
expect(setPan).toHaveBeenCalledWith(30, 40);
|
|
106
|
-
expect(setZoom).not.toHaveBeenCalled();
|
|
107
|
-
expect(setZoomAndPan).not.toHaveBeenCalled();
|
|
108
|
-
});
|
|
109
|
-
test('calls setZoomAndPan when both zoom and pan change', () => {
|
|
110
|
-
const { rerender } = render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 1.5, pan: { x: 10, y: 20 } }) }));
|
|
111
|
-
expect(setZoomAndPan).toHaveBeenCalledTimes(1);
|
|
112
|
-
expect(setZoomAndPan).toHaveBeenCalledWith(1.5, 10, 20);
|
|
113
|
-
rerender(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 1.5, pan: { x: 10, y: 20 } }) }));
|
|
114
|
-
expect(setZoomAndPan).toHaveBeenCalledTimes(1);
|
|
115
|
-
rerender(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 2.0, pan: { x: 30, y: 40 } }) }));
|
|
116
|
-
expect(setZoomAndPan).toHaveBeenCalledTimes(2);
|
|
117
|
-
expect(setZoomAndPan).toHaveBeenCalledWith(2.0, 30, 40);
|
|
118
|
-
expect(setZoom).not.toHaveBeenCalled();
|
|
119
|
-
expect(setPan).not.toHaveBeenCalled();
|
|
120
|
-
});
|
|
121
|
-
test('calls appropriate methods when only zoom changes while both are set', () => {
|
|
122
|
-
const { rerender } = render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 1.5, pan: { x: 10, y: 20 } }) }));
|
|
123
|
-
expect(setZoomAndPan).toHaveBeenCalledTimes(1);
|
|
124
|
-
rerender(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 2.0, pan: { x: 10, y: 20 } }) }));
|
|
125
|
-
expect(setZoom).toHaveBeenCalledTimes(1);
|
|
126
|
-
expect(setZoom).toHaveBeenCalledWith(2.0);
|
|
127
|
-
expect(setZoomAndPan).toHaveBeenCalledTimes(1);
|
|
128
|
-
expect(setPan).not.toHaveBeenCalled();
|
|
129
|
-
});
|
|
130
|
-
test('calls appropriate methods when only pan changes while both are set', () => {
|
|
131
|
-
const { rerender } = render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 1.5, pan: { x: 10, y: 20 } }) }));
|
|
132
|
-
expect(setZoomAndPan).toHaveBeenCalledTimes(1);
|
|
133
|
-
rerender(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 1.5, pan: { x: 30, y: 40 } }) }));
|
|
134
|
-
expect(setPan).toHaveBeenCalledTimes(1);
|
|
135
|
-
expect(setPan).toHaveBeenCalledWith(30, 40);
|
|
136
|
-
expect(setZoomAndPan).toHaveBeenCalledTimes(1);
|
|
137
|
-
expect(setZoom).not.toHaveBeenCalled();
|
|
138
|
-
});
|
|
139
|
-
test('calls setLayout when nvlOptions.layout changes', () => {
|
|
140
|
-
const setLayout = jest.fn();
|
|
141
|
-
jest.spyOn(NVL.prototype, 'setLayout').mockImplementation(setLayout);
|
|
142
|
-
const { rerender } = render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], nvlOptions: { layout: HierarchicalLayoutType } }) }));
|
|
143
|
-
expect(setLayout).toHaveBeenCalledWith(HierarchicalLayoutType);
|
|
144
|
-
expect(setLayout).toHaveBeenCalledTimes(1);
|
|
145
|
-
rerender(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], nvlOptions: { layout: 'forceDirected' } }) }));
|
|
146
|
-
expect(setLayout).toHaveBeenCalledWith('forceDirected');
|
|
147
|
-
expect(setLayout).toHaveBeenCalledTimes(2);
|
|
148
|
-
});
|
|
149
|
-
test('calls setLayoutOptions when nvlOptions.layoutOptions changes', () => {
|
|
150
|
-
const setLayoutOptions = jest.fn();
|
|
151
|
-
jest.spyOn(NVL.prototype, 'setLayoutOptions').mockImplementation(setLayoutOptions);
|
|
152
|
-
const { rerender } = render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], nvlOptions: { layoutOptions: { direction: 'down' } } }) }));
|
|
153
|
-
expect(setLayoutOptions).toHaveBeenCalledWith({ direction: 'down' });
|
|
154
|
-
expect(setLayoutOptions).toHaveBeenCalledTimes(1);
|
|
155
|
-
rerender(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], nvlOptions: { layoutOptions: { direction: 'up' } } }) }));
|
|
156
|
-
expect(setLayoutOptions).toHaveBeenCalledWith({ direction: 'up' });
|
|
157
|
-
expect(setLayoutOptions).toHaveBeenCalledTimes(2);
|
|
158
|
-
});
|
|
159
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import '@testing-library/jest-dom';
|
|
@@ -1,396 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import NVL, { HierarchicalLayoutType } from '@neo4j-nvl/base';
|
|
3
|
-
import { BoxSelectInteraction, ClickInteraction, DragNodeInteraction, DrawInteraction, HoverInteraction, KeyboardInteraction, LassoInteraction, PanInteraction, ZoomInteraction } from '@neo4j-nvl/interaction-handlers';
|
|
4
|
-
import '@testing-library/jest-dom';
|
|
5
|
-
import { render } from '@testing-library/react';
|
|
6
|
-
import { StrictMode, act, createRef } from 'react';
|
|
7
|
-
import { InteractiveNvlWrapper } from '../interactive-nvl-wrapper/InteractiveNvlWrapper';
|
|
8
|
-
jest.mock('@neo4j-nvl/base');
|
|
9
|
-
jest.mock('@neo4j-nvl/layout-workers');
|
|
10
|
-
jest.mock('@neo4j-nvl/interaction-handlers');
|
|
11
|
-
const MockedNVL = NVL;
|
|
12
|
-
let mockOnInitialization;
|
|
13
|
-
HoverInteraction.prototype.callbackMap = new Map();
|
|
14
|
-
HoverInteraction.prototype.updateCallback = jest.fn();
|
|
15
|
-
HoverInteraction.prototype.removeCallback = jest.fn();
|
|
16
|
-
HoverInteraction.prototype.destroy = jest.fn();
|
|
17
|
-
PanInteraction.prototype.callbackMap = new Map();
|
|
18
|
-
PanInteraction.prototype.updateCallback = jest.fn();
|
|
19
|
-
PanInteraction.prototype.removeCallback = jest.fn();
|
|
20
|
-
PanInteraction.prototype.destroy = jest.fn();
|
|
21
|
-
ClickInteraction.prototype.callbackMap = new Map();
|
|
22
|
-
ClickInteraction.prototype.updateCallback = jest.fn();
|
|
23
|
-
ClickInteraction.prototype.removeCallback = jest.fn();
|
|
24
|
-
ClickInteraction.prototype.destroy = jest.fn();
|
|
25
|
-
ZoomInteraction.prototype.callbackMap = new Map();
|
|
26
|
-
ZoomInteraction.prototype.updateCallback = jest.fn();
|
|
27
|
-
ZoomInteraction.prototype.removeCallback = jest.fn();
|
|
28
|
-
ZoomInteraction.prototype.destroy = jest.fn();
|
|
29
|
-
DragNodeInteraction.prototype.callbackMap = new Map();
|
|
30
|
-
DragNodeInteraction.prototype.updateCallback = jest.fn();
|
|
31
|
-
DragNodeInteraction.prototype.removeCallback = jest.fn();
|
|
32
|
-
DragNodeInteraction.prototype.destroy = jest.fn();
|
|
33
|
-
DrawInteraction.prototype.callbackMap = new Map();
|
|
34
|
-
DrawInteraction.prototype.updateCallback = jest.fn();
|
|
35
|
-
DrawInteraction.prototype.removeCallback = jest.fn();
|
|
36
|
-
DrawInteraction.prototype.destroy = jest.fn();
|
|
37
|
-
BoxSelectInteraction.prototype.callbackMap = new Map();
|
|
38
|
-
BoxSelectInteraction.prototype.updateCallback = jest.fn();
|
|
39
|
-
BoxSelectInteraction.prototype.removeCallback = jest.fn();
|
|
40
|
-
BoxSelectInteraction.prototype.destroy = jest.fn();
|
|
41
|
-
LassoInteraction.prototype.callbackMap = new Map();
|
|
42
|
-
LassoInteraction.prototype.updateCallback = jest.fn();
|
|
43
|
-
LassoInteraction.prototype.removeCallback = jest.fn();
|
|
44
|
-
LassoInteraction.prototype.destroy = jest.fn();
|
|
45
|
-
KeyboardInteraction.prototype.callbackMap = new Map();
|
|
46
|
-
KeyboardInteraction.prototype.updateCallback = jest.fn();
|
|
47
|
-
KeyboardInteraction.prototype.removeCallback = jest.fn();
|
|
48
|
-
KeyboardInteraction.prototype.destroy = jest.fn();
|
|
49
|
-
let mockDestroy;
|
|
50
|
-
let destroyHoverInteraction;
|
|
51
|
-
let destroyPanInteraction;
|
|
52
|
-
describe('InteractiveNvlWrapper', () => {
|
|
53
|
-
beforeEach(() => {
|
|
54
|
-
jest.spyOn(NVL.prototype, 'getContainer').mockImplementation(() => document.createElement('div'));
|
|
55
|
-
MockedNVL.mockImplementation((_container, _nodes, _rels, _options, callbacks) => {
|
|
56
|
-
mockOnInitialization = callbacks.onInitialization;
|
|
57
|
-
return {
|
|
58
|
-
getContainer: () => document.createElement('div'),
|
|
59
|
-
destroy: mockDestroy,
|
|
60
|
-
setLayout: jest.fn(),
|
|
61
|
-
setLayoutOptions: jest.fn(),
|
|
62
|
-
setRenderer: jest.fn()
|
|
63
|
-
};
|
|
64
|
-
});
|
|
65
|
-
mockDestroy = jest.fn();
|
|
66
|
-
destroyHoverInteraction = jest.fn();
|
|
67
|
-
destroyPanInteraction = jest.fn();
|
|
68
|
-
jest.spyOn(NVL.prototype, 'destroy').mockImplementation(mockDestroy);
|
|
69
|
-
jest.spyOn(HoverInteraction.prototype, 'destroy').mockImplementation(destroyHoverInteraction);
|
|
70
|
-
jest.spyOn(PanInteraction.prototype, 'destroy').mockImplementation(destroyPanInteraction);
|
|
71
|
-
jest.spyOn(ClickInteraction.prototype, 'destroy').mockImplementation(jest.fn());
|
|
72
|
-
jest.spyOn(ZoomInteraction.prototype, 'destroy').mockImplementation(jest.fn());
|
|
73
|
-
jest.spyOn(DragNodeInteraction.prototype, 'destroy').mockImplementation(jest.fn());
|
|
74
|
-
jest.spyOn(DrawInteraction.prototype, 'destroy').mockImplementation(jest.fn());
|
|
75
|
-
jest.spyOn(BoxSelectInteraction.prototype, 'destroy').mockImplementation(jest.fn());
|
|
76
|
-
jest.spyOn(LassoInteraction.prototype, 'destroy').mockImplementation(jest.fn());
|
|
77
|
-
jest.spyOn(KeyboardInteraction.prototype, 'destroy').mockImplementation(jest.fn());
|
|
78
|
-
});
|
|
79
|
-
afterEach(() => {
|
|
80
|
-
jest.clearAllMocks();
|
|
81
|
-
});
|
|
82
|
-
test('initialises NVL expectedly with an empty graph object and no other properties', () => {
|
|
83
|
-
render(_jsx("div", { children: _jsx(InteractiveNvlWrapper, { nodes: [], rels: [] }) }));
|
|
84
|
-
expect(NVL).toHaveBeenCalledWith(expect.any(HTMLDivElement), [], [], {}, { onInitialization: mockOnInitialization });
|
|
85
|
-
});
|
|
86
|
-
test('initialises NVL expectedly with a graph object and properties', () => {
|
|
87
|
-
const mockLayoutDoneFunction = jest.fn();
|
|
88
|
-
render(_jsx(InteractiveNvlWrapper, { nodes: [{ id: '1' }, { id: '2' }], rels: [{ id: '12', from: '1', to: '2' }], nvlOptions: {
|
|
89
|
-
renderer: 'canvas',
|
|
90
|
-
layout: HierarchicalLayoutType,
|
|
91
|
-
layoutOptions: { direction: 'down' }
|
|
92
|
-
}, nvlCallbacks: { onLayoutDone: mockLayoutDoneFunction } }));
|
|
93
|
-
act(() => {
|
|
94
|
-
mockOnInitialization?.();
|
|
95
|
-
});
|
|
96
|
-
expect(NVL).toHaveBeenCalledWith(expect.any(HTMLDivElement), [{ id: '1' }, { id: '2' }], [{ id: '12', from: '1', to: '2' }], { renderer: 'canvas', layout: HierarchicalLayoutType, layoutOptions: { direction: 'down' } }, {
|
|
97
|
-
onLayoutDone: mockLayoutDoneFunction,
|
|
98
|
-
onInitialization: mockOnInitialization
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
test('initialises expected interaction handlers with correct options', () => {
|
|
102
|
-
const myNvlRef = createRef();
|
|
103
|
-
render(_jsx(InteractiveNvlWrapper, { nodes: [], rels: [], ref: myNvlRef, mouseEventCallbacks: {
|
|
104
|
-
onHover: true,
|
|
105
|
-
onPan: true
|
|
106
|
-
}, interactionOptions: {
|
|
107
|
-
drawShadowOnHover: true
|
|
108
|
-
} }));
|
|
109
|
-
act(() => {
|
|
110
|
-
mockOnInitialization?.();
|
|
111
|
-
});
|
|
112
|
-
expect(HoverInteraction).toHaveBeenCalledWith(myNvlRef.current, { drawShadowOnHover: true });
|
|
113
|
-
expect(PanInteraction).toHaveBeenCalledWith(myNvlRef.current, { drawShadowOnHover: true });
|
|
114
|
-
expect(HoverInteraction).toHaveBeenCalledTimes(1);
|
|
115
|
-
expect(PanInteraction).toHaveBeenCalledTimes(1);
|
|
116
|
-
expect(ClickInteraction).not.toHaveBeenCalled();
|
|
117
|
-
expect(DragNodeInteraction).not.toHaveBeenCalled();
|
|
118
|
-
expect(DrawInteraction).not.toHaveBeenCalled();
|
|
119
|
-
expect(ZoomInteraction).not.toHaveBeenCalled();
|
|
120
|
-
expect(BoxSelectInteraction).not.toHaveBeenCalled();
|
|
121
|
-
expect(LassoInteraction).not.toHaveBeenCalled();
|
|
122
|
-
expect(KeyboardInteraction).not.toHaveBeenCalled();
|
|
123
|
-
});
|
|
124
|
-
test.each([
|
|
125
|
-
{
|
|
126
|
-
name: 'HoverInteraction',
|
|
127
|
-
InteractionClass: HoverInteraction,
|
|
128
|
-
callback: { onHover: jest.fn() },
|
|
129
|
-
options: { drawShadowOnHover: true }
|
|
130
|
-
},
|
|
131
|
-
{
|
|
132
|
-
name: 'ClickInteraction with onCanvasClick',
|
|
133
|
-
InteractionClass: ClickInteraction,
|
|
134
|
-
callback: { onCanvasClick: jest.fn() },
|
|
135
|
-
options: { selectOnClick: false }
|
|
136
|
-
},
|
|
137
|
-
{
|
|
138
|
-
name: 'ClickInteraction with onCanvasDoubleClick',
|
|
139
|
-
InteractionClass: ClickInteraction,
|
|
140
|
-
callback: { onCanvasDoubleClick: jest.fn() },
|
|
141
|
-
options: { selectOnClick: false }
|
|
142
|
-
},
|
|
143
|
-
{
|
|
144
|
-
name: 'ClickInteraction with onCanvasRightClick',
|
|
145
|
-
InteractionClass: ClickInteraction,
|
|
146
|
-
callback: { onCanvasRightClick: jest.fn() },
|
|
147
|
-
options: { selectOnClick: false }
|
|
148
|
-
},
|
|
149
|
-
{
|
|
150
|
-
name: 'ClickInteraction with onNodeClick',
|
|
151
|
-
InteractionClass: ClickInteraction,
|
|
152
|
-
callback: { onNodeClick: jest.fn() },
|
|
153
|
-
options: { selectOnClick: true }
|
|
154
|
-
},
|
|
155
|
-
{
|
|
156
|
-
name: 'ClickInteraction with onNodeDoubleClick',
|
|
157
|
-
InteractionClass: ClickInteraction,
|
|
158
|
-
callback: { onNodeDoubleClick: jest.fn() },
|
|
159
|
-
options: { selectOnClick: false }
|
|
160
|
-
},
|
|
161
|
-
{
|
|
162
|
-
name: 'ClickInteraction with onNodeRightClick',
|
|
163
|
-
InteractionClass: ClickInteraction,
|
|
164
|
-
callback: { onNodeRightClick: jest.fn() },
|
|
165
|
-
options: { selectOnClick: false }
|
|
166
|
-
},
|
|
167
|
-
{
|
|
168
|
-
name: 'ClickInteraction with onRelationshipClick',
|
|
169
|
-
InteractionClass: ClickInteraction,
|
|
170
|
-
callback: { onRelationshipClick: jest.fn() },
|
|
171
|
-
options: { selectOnClick: false }
|
|
172
|
-
},
|
|
173
|
-
{
|
|
174
|
-
name: 'ClickInteraction with onRelationshipDoubleClick',
|
|
175
|
-
InteractionClass: ClickInteraction,
|
|
176
|
-
callback: { onRelationshipDoubleClick: jest.fn() },
|
|
177
|
-
options: { selectOnClick: false }
|
|
178
|
-
},
|
|
179
|
-
{
|
|
180
|
-
name: 'ClickInteraction with onRelationshipRightClick',
|
|
181
|
-
InteractionClass: ClickInteraction,
|
|
182
|
-
callback: { onRelationshipRightClick: jest.fn() },
|
|
183
|
-
options: { selectOnClick: false }
|
|
184
|
-
},
|
|
185
|
-
{
|
|
186
|
-
name: 'PanInteraction',
|
|
187
|
-
InteractionClass: PanInteraction,
|
|
188
|
-
callback: { onPan: jest.fn() },
|
|
189
|
-
options: {}
|
|
190
|
-
},
|
|
191
|
-
{
|
|
192
|
-
name: 'ZoomInteraction',
|
|
193
|
-
InteractionClass: ZoomInteraction,
|
|
194
|
-
callback: { onZoomAndPan: jest.fn() },
|
|
195
|
-
options: {}
|
|
196
|
-
},
|
|
197
|
-
{
|
|
198
|
-
name: 'DragNodeInteraction with onDrag',
|
|
199
|
-
InteractionClass: DragNodeInteraction,
|
|
200
|
-
callback: { onDrag: jest.fn() },
|
|
201
|
-
options: { selectOnRelease: false }
|
|
202
|
-
},
|
|
203
|
-
{
|
|
204
|
-
name: 'DragNodeInteraction with onDragStart',
|
|
205
|
-
InteractionClass: DragNodeInteraction,
|
|
206
|
-
callback: { onDragStart: jest.fn() },
|
|
207
|
-
options: {}
|
|
208
|
-
},
|
|
209
|
-
{
|
|
210
|
-
name: 'DragNodeInteraction with onDragEnd',
|
|
211
|
-
InteractionClass: DragNodeInteraction,
|
|
212
|
-
callback: { onDragEnd: jest.fn() },
|
|
213
|
-
options: {}
|
|
214
|
-
},
|
|
215
|
-
{
|
|
216
|
-
name: 'DrawInteraction with onHoverNodeMargin',
|
|
217
|
-
InteractionClass: DrawInteraction,
|
|
218
|
-
callback: { onHoverNodeMargin: jest.fn() },
|
|
219
|
-
options: { excludeNodeMargin: false }
|
|
220
|
-
},
|
|
221
|
-
{
|
|
222
|
-
name: 'DrawInteraction with onDrawStarted',
|
|
223
|
-
InteractionClass: DrawInteraction,
|
|
224
|
-
callback: { onDrawStarted: jest.fn() },
|
|
225
|
-
options: {}
|
|
226
|
-
},
|
|
227
|
-
{
|
|
228
|
-
name: 'DrawInteraction with onDrawEnded',
|
|
229
|
-
InteractionClass: DrawInteraction,
|
|
230
|
-
callback: { onDrawEnded: jest.fn() },
|
|
231
|
-
options: {}
|
|
232
|
-
},
|
|
233
|
-
{
|
|
234
|
-
name: 'BoxSelectInteraction with onBoxStarted',
|
|
235
|
-
InteractionClass: BoxSelectInteraction,
|
|
236
|
-
callback: { onBoxStarted: jest.fn() },
|
|
237
|
-
options: {}
|
|
238
|
-
},
|
|
239
|
-
{
|
|
240
|
-
name: 'BoxSelectInteraction with onBoxSelect',
|
|
241
|
-
InteractionClass: BoxSelectInteraction,
|
|
242
|
-
callback: { onBoxSelect: jest.fn() },
|
|
243
|
-
options: {}
|
|
244
|
-
},
|
|
245
|
-
{
|
|
246
|
-
name: 'LassoInteraction with onLassoStarted',
|
|
247
|
-
InteractionClass: LassoInteraction,
|
|
248
|
-
callback: { onLassoStarted: jest.fn() },
|
|
249
|
-
options: {}
|
|
250
|
-
},
|
|
251
|
-
{
|
|
252
|
-
name: 'LassoInteraction with onLassoSelect',
|
|
253
|
-
InteractionClass: LassoInteraction,
|
|
254
|
-
callback: { onLassoSelect: jest.fn() },
|
|
255
|
-
options: {}
|
|
256
|
-
}
|
|
257
|
-
])('initialises $name when its callback is provided', ({ InteractionClass, callback, options }) => {
|
|
258
|
-
const myNvlRef = createRef();
|
|
259
|
-
render(_jsx(InteractiveNvlWrapper, { nodes: [], rels: [], ref: myNvlRef, mouseEventCallbacks: callback, interactionOptions: options }));
|
|
260
|
-
act(() => {
|
|
261
|
-
mockOnInitialization?.();
|
|
262
|
-
});
|
|
263
|
-
expect(InteractionClass).toHaveBeenCalledWith(myNvlRef.current, options);
|
|
264
|
-
expect(InteractionClass).toHaveBeenCalledTimes(1);
|
|
265
|
-
});
|
|
266
|
-
test.each([
|
|
267
|
-
{
|
|
268
|
-
name: 'KeyboardInteraction with onKeyDown',
|
|
269
|
-
callback: { onKeyDown: jest.fn() }
|
|
270
|
-
},
|
|
271
|
-
{
|
|
272
|
-
name: 'KeyboardInteraction with onKeyUp',
|
|
273
|
-
callback: { onKeyUp: jest.fn() }
|
|
274
|
-
},
|
|
275
|
-
{
|
|
276
|
-
name: 'KeyboardInteraction with onNodeFocus',
|
|
277
|
-
callback: { onNodeFocus: jest.fn() }
|
|
278
|
-
},
|
|
279
|
-
{
|
|
280
|
-
name: 'KeyboardInteraction with onNodeBlur',
|
|
281
|
-
callback: { onNodeBlur: jest.fn() }
|
|
282
|
-
},
|
|
283
|
-
{
|
|
284
|
-
name: 'KeyboardInteraction with onRelationshipFocus',
|
|
285
|
-
callback: { onRelationshipFocus: jest.fn() }
|
|
286
|
-
},
|
|
287
|
-
{
|
|
288
|
-
name: 'KeyboardInteraction with onRelationshipBlur',
|
|
289
|
-
callback: { onRelationshipBlur: jest.fn() }
|
|
290
|
-
},
|
|
291
|
-
{
|
|
292
|
-
name: 'KeyboardInteraction with onCanvasFocus',
|
|
293
|
-
callback: { onCanvasFocus: jest.fn() }
|
|
294
|
-
},
|
|
295
|
-
{
|
|
296
|
-
name: 'KeyboardInteraction with onCanvasBlur',
|
|
297
|
-
callback: { onCanvasBlur: jest.fn() }
|
|
298
|
-
}
|
|
299
|
-
])('initialises $name when its callback is provided', ({ callback }) => {
|
|
300
|
-
const myNvlRef = createRef();
|
|
301
|
-
render(_jsx(InteractiveNvlWrapper, { nodes: [], rels: [], ref: myNvlRef, keyboardEventCallbacks: callback }));
|
|
302
|
-
act(() => {
|
|
303
|
-
mockOnInitialization?.();
|
|
304
|
-
});
|
|
305
|
-
expect(KeyboardInteraction).toHaveBeenCalledWith(myNvlRef.current, expect.objectContaining({}));
|
|
306
|
-
expect(KeyboardInteraction).toHaveBeenCalledTimes(1);
|
|
307
|
-
});
|
|
308
|
-
test('does not attempt to initialize interaction handlers if NVL container is not available', () => {
|
|
309
|
-
const myNvlRef = createRef();
|
|
310
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
311
|
-
// @ts-expect-error
|
|
312
|
-
jest.spyOn(NVL.prototype, 'getContainer').mockImplementation(() => null);
|
|
313
|
-
render(_jsx(InteractiveNvlWrapper, { nodes: [], rels: [], ref: myNvlRef, mouseEventCallbacks: {
|
|
314
|
-
onHover: true
|
|
315
|
-
}, interactionOptions: {
|
|
316
|
-
drawShadowOnHover: true
|
|
317
|
-
} }));
|
|
318
|
-
expect(HoverInteraction).not.toHaveBeenCalled();
|
|
319
|
-
expect(NVL).toHaveBeenCalledTimes(1);
|
|
320
|
-
});
|
|
321
|
-
test('destroys NVL and active interaction handlers on unmount', () => {
|
|
322
|
-
const { unmount } = render(_jsx(InteractiveNvlWrapper, { nodes: [], rels: [], mouseEventCallbacks: {
|
|
323
|
-
onHover: true
|
|
324
|
-
} }));
|
|
325
|
-
act(() => {
|
|
326
|
-
mockOnInitialization?.();
|
|
327
|
-
});
|
|
328
|
-
unmount();
|
|
329
|
-
expect(mockDestroy).toHaveBeenCalledTimes(1);
|
|
330
|
-
expect(destroyHoverInteraction).toHaveBeenCalledTimes(1);
|
|
331
|
-
expect(destroyPanInteraction).not.toHaveBeenCalled();
|
|
332
|
-
});
|
|
333
|
-
test('successfully re-initialises NVL and active interaction handlers when using React StrictMode', () => {
|
|
334
|
-
const myNvlRef = createRef();
|
|
335
|
-
render(_jsx(StrictMode, { children: _jsx(InteractiveNvlWrapper, { nodes: [], rels: [], ref: myNvlRef, mouseEventCallbacks: {
|
|
336
|
-
onHover: true
|
|
337
|
-
}, interactionOptions: {
|
|
338
|
-
drawShadowOnHover: true
|
|
339
|
-
} }) }));
|
|
340
|
-
act(() => {
|
|
341
|
-
mockOnInitialization?.();
|
|
342
|
-
});
|
|
343
|
-
expect(NVL).toHaveBeenCalledTimes(2);
|
|
344
|
-
expect(HoverInteraction).toHaveBeenCalledTimes(2);
|
|
345
|
-
expect(HoverInteraction).toHaveBeenCalledWith(myNvlRef.current, { drawShadowOnHover: true });
|
|
346
|
-
expect(mockDestroy).toHaveBeenCalledTimes(1);
|
|
347
|
-
expect(destroyHoverInteraction).toHaveBeenCalledTimes(1);
|
|
348
|
-
expect(destroyPanInteraction).not.toHaveBeenCalled();
|
|
349
|
-
expect(DragNodeInteraction).not.toHaveBeenCalled();
|
|
350
|
-
expect(DrawInteraction).not.toHaveBeenCalled();
|
|
351
|
-
expect(ZoomInteraction).not.toHaveBeenCalled();
|
|
352
|
-
expect(BoxSelectInteraction).not.toHaveBeenCalled();
|
|
353
|
-
expect(LassoInteraction).not.toHaveBeenCalled();
|
|
354
|
-
expect(KeyboardInteraction).not.toHaveBeenCalled();
|
|
355
|
-
});
|
|
356
|
-
test('destroys keyboard interaction on unmount', () => {
|
|
357
|
-
const destroyKeyboardInteraction = jest.fn();
|
|
358
|
-
jest.spyOn(KeyboardInteraction.prototype, 'destroy').mockImplementation(destroyKeyboardInteraction);
|
|
359
|
-
const { unmount } = render(_jsx(InteractiveNvlWrapper, { nodes: [], rels: [], keyboardEventCallbacks: {
|
|
360
|
-
onKeyDown: true
|
|
361
|
-
} }));
|
|
362
|
-
act(() => {
|
|
363
|
-
mockOnInitialization?.();
|
|
364
|
-
});
|
|
365
|
-
unmount();
|
|
366
|
-
expect(destroyKeyboardInteraction).toHaveBeenCalledTimes(1);
|
|
367
|
-
});
|
|
368
|
-
test('also calls custom callbacks on initialization', () => {
|
|
369
|
-
const customOnInitializationCallback = jest.fn();
|
|
370
|
-
render(_jsx(InteractiveNvlWrapper, { nodes: [], rels: [], nvlCallbacks: { onInitialization: customOnInitializationCallback } }));
|
|
371
|
-
act(() => {
|
|
372
|
-
mockOnInitialization?.();
|
|
373
|
-
});
|
|
374
|
-
expect(customOnInitializationCallback).toHaveBeenCalledTimes(1);
|
|
375
|
-
});
|
|
376
|
-
test('passes zoom and pan properties to BasicNvlWrapper', () => {
|
|
377
|
-
const setZoom = jest.fn();
|
|
378
|
-
const setPan = jest.fn();
|
|
379
|
-
const setZoomAndPan = jest.fn();
|
|
380
|
-
MockedNVL.mockImplementation(() => ({
|
|
381
|
-
getContainer: () => document.createElement('div'),
|
|
382
|
-
destroy: jest.fn(),
|
|
383
|
-
setLayout: jest.fn(),
|
|
384
|
-
setLayoutOptions: jest.fn(),
|
|
385
|
-
setNodePositions: jest.fn(),
|
|
386
|
-
setZoom,
|
|
387
|
-
setPan,
|
|
388
|
-
setZoomAndPan
|
|
389
|
-
}));
|
|
390
|
-
render(_jsx(InteractiveNvlWrapper, { nodes: [], rels: [], zoom: 1.5, pan: { x: 10, y: 20 } }));
|
|
391
|
-
act(() => {
|
|
392
|
-
mockOnInitialization?.();
|
|
393
|
-
});
|
|
394
|
-
expect(setZoomAndPan).toHaveBeenCalledWith(1.5, 10, 20);
|
|
395
|
-
});
|
|
396
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import '@testing-library/jest-dom';
|