@neo4j-nvl/react 1.2.1 → 2.0.0-1e695738

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 (31) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/README.md +6 -6
  3. package/lib/basic-wrapper/BasicNvlWrapper.d.ts +30 -41
  4. package/lib/basic-wrapper/BasicNvlWrapper.js +116 -149
  5. package/lib/index.d.ts +5 -8
  6. package/lib/index.js +3 -3
  7. package/lib/interactive-nvl-wrapper/InteractionHandlers.d.ts +8 -8
  8. package/lib/interactive-nvl-wrapper/InteractionHandlers.js +62 -57
  9. package/lib/interactive-nvl-wrapper/InteractiveNvlWrapper.d.ts +5 -5
  10. package/lib/interactive-nvl-wrapper/InteractiveNvlWrapper.js +54 -42
  11. package/lib/interactive-nvl-wrapper/hooks.d.ts +4 -4
  12. package/lib/interactive-nvl-wrapper/hooks.js +24 -27
  13. package/lib/interactive-nvl-wrapper/types.d.ts +9 -9
  14. package/lib/interactive-nvl-wrapper/types.js +0 -1
  15. package/lib/static-picture-wrapper/StaticPictureWrapper.d.ts +15 -15
  16. package/lib/static-picture-wrapper/StaticPictureWrapper.js +71 -55
  17. package/lib/utils/constants.d.ts +1 -1
  18. package/lib/utils/constants.js +4 -2
  19. package/lib/utils/graph-comparison.d.ts +5 -5
  20. package/lib/utils/graph-comparison.js +53 -68
  21. package/lib/utils/hooks.d.ts +2 -2
  22. package/lib/utils/hooks.js +10 -11
  23. package/package.json +26 -11
  24. package/lib/__tests__/BasicNvlWrapper.test.d.ts +0 -1
  25. package/lib/__tests__/BasicNvlWrapper.test.js +0 -135
  26. package/lib/__tests__/InteractiveNvlWrapper.test.d.ts +0 -1
  27. package/lib/__tests__/InteractiveNvlWrapper.test.js +0 -392
  28. package/lib/__tests__/StaticPictureWrapper.test.d.ts +0 -1
  29. package/lib/__tests__/StaticPictureWrapper.test.js +0 -85
  30. package/lib/__tests__/index.test.d.ts +0 -1
  31. package/lib/__tests__/index.test.js +0 -8
@@ -1,72 +1,57 @@
1
- import { isEqual, isNil, keyBy, keys, sortBy, transform } from 'lodash';
1
+ import { isEqual, isNil, keyBy, keys, sortBy, transform } from "lodash-es";
2
+ // src/utils/graph-comparison.ts
2
3
  const getMapDifferences = (prevGraphElements, newGraphElements) => {
3
- const prevMap = keyBy(prevGraphElements, 'id');
4
- const currentMap = keyBy(newGraphElements, 'id');
5
- const prevIds = sortBy(keys(prevMap));
6
- const currentIds = sortBy(keys(currentMap));
7
- const added = [];
8
- const removed = [];
9
- const updated = [];
10
- let i = 0;
11
- let j = 0;
12
- while (i < prevIds.length && j < currentIds.length) {
13
- const prevId = prevIds[i];
14
- const currId = currentIds[j];
15
- if (prevId === undefined || currId === undefined) {
16
- continue;
17
- }
18
- if (prevId === currId) {
19
- if (!isEqual(prevMap[prevId], currentMap[currId])) {
20
- updated.push(currId);
21
- }
22
- i += 1;
23
- j += 1;
24
- }
25
- else if (prevId < currId) {
26
- removed.push(prevId);
27
- i += 1;
28
- }
29
- else {
30
- added.push(currId);
31
- j += 1;
32
- }
33
- }
34
- while (i < prevIds.length) {
35
- const prevId = prevIds[i];
36
- if (prevId === undefined) {
37
- continue;
38
- }
39
- removed.push(prevId);
40
- i += 1;
41
- }
42
- while (j < currentIds.length) {
43
- const currId = currentIds[j];
44
- if (currId === undefined) {
45
- continue;
46
- }
47
- added.push(currId);
48
- j += 1;
49
- }
50
- return {
51
- added: added.map((id) => currentMap[id]).filter((n) => !isNil(n)),
52
- removed: removed.map((id) => prevMap[id]).filter((n) => !isNil(n)),
53
- updated: updated.map((id) => currentMap[id]).filter((n) => !isNil(n))
54
- };
4
+ const prevMap = keyBy(prevGraphElements, "id");
5
+ const currentMap = keyBy(newGraphElements, "id");
6
+ const prevIds = sortBy(keys(prevMap));
7
+ const currentIds = sortBy(keys(currentMap));
8
+ const added = [];
9
+ const removed = [];
10
+ const updated = [];
11
+ let i = 0;
12
+ let j = 0;
13
+ while (i < prevIds.length && j < currentIds.length) {
14
+ const prevId = prevIds[i];
15
+ const currId = currentIds[j];
16
+ if (prevId === void 0 || currId === void 0) continue;
17
+ if (prevId === currId) {
18
+ if (!isEqual(prevMap[prevId], currentMap[currId])) updated.push(currId);
19
+ i += 1;
20
+ j += 1;
21
+ } else if (prevId < currId) {
22
+ removed.push(prevId);
23
+ i += 1;
24
+ } else {
25
+ added.push(currId);
26
+ j += 1;
27
+ }
28
+ }
29
+ while (i < prevIds.length) {
30
+ const prevId = prevIds[i];
31
+ if (prevId === void 0) continue;
32
+ removed.push(prevId);
33
+ i += 1;
34
+ }
35
+ while (j < currentIds.length) {
36
+ const currId = currentIds[j];
37
+ if (currId === void 0) continue;
38
+ added.push(currId);
39
+ j += 1;
40
+ }
41
+ return {
42
+ added: added.map((id) => currentMap[id]).filter((n) => !isNil(n)),
43
+ removed: removed.map((id) => prevMap[id]).filter((n) => !isNil(n)),
44
+ updated: updated.map((id) => currentMap[id]).filter((n) => !isNil(n))
45
+ };
55
46
  };
56
47
  const getNodeAttributeDifferences = (prevNodes, newNodes) => {
57
- const prevNodeMap = keyBy(prevNodes, 'id');
58
- return newNodes
59
- .map((nodeToUpdate) => {
60
- const previousNode = prevNodeMap[nodeToUpdate.id];
61
- if (previousNode === undefined) {
62
- return null;
63
- }
64
- return transform(nodeToUpdate, (result, value, key) => {
65
- if (key === 'id' || value !== previousNode[key]) {
66
- Object.assign(result, { [key]: value });
67
- }
68
- });
69
- })
70
- .filter((n) => n !== null && Object.keys(n).length > 1);
48
+ const prevNodeMap = keyBy(prevNodes, "id");
49
+ return newNodes.map((nodeToUpdate) => {
50
+ const previousNode = prevNodeMap[nodeToUpdate.id];
51
+ if (previousNode === void 0) return null;
52
+ return transform(nodeToUpdate, (result, value, key) => {
53
+ if (key === "id" || value !== previousNode[key]) Object.assign(result, { [key]: value });
54
+ });
55
+ }).filter((n) => n !== null && Object.keys(n).length > 1);
71
56
  };
72
- export { getNodeAttributeDifferences, getMapDifferences };
57
+ export { getMapDifferences, getNodeAttributeDifferences };
@@ -1,2 +1,2 @@
1
- import type { EffectCallback } from 'react';
2
- export declare const useDeepCompareEffect: (callback: EffectCallback, dependencies: unknown[]) => void;
1
+ import { EffectCallback } from "react";
2
+ export declare const useDeepCompareEffect: (callback: EffectCallback, dependencies: unknown[]) => void;
@@ -1,16 +1,15 @@
1
- import { isEqual } from 'lodash';
2
- import { useEffect, useRef } from 'react';
1
+ import { useEffect, useRef } from "react";
2
+ import { isEqual } from "lodash-es";
3
+ // src/utils/hooks.ts
3
4
  const deepCompareEquals = (a, b) => {
4
- return isEqual(a, b);
5
+ return isEqual(a, b);
5
6
  };
6
7
  const useDeepCompareMemoize = (value) => {
7
- const ref = useRef();
8
- if (!deepCompareEquals(value, ref.current)) {
9
- ref.current = value;
10
- }
11
- return ref.current;
8
+ const ref = useRef();
9
+ if (!deepCompareEquals(value, ref.current)) ref.current = value;
10
+ return ref.current;
12
11
  };
13
- export const useDeepCompareEffect = (callback, dependencies) => {
14
- // eslint-disable-next-line react-hooks/exhaustive-deps
15
- useEffect(callback, dependencies.map(useDeepCompareMemoize));
12
+ const useDeepCompareEffect = (callback, dependencies) => {
13
+ useEffect(callback, dependencies.map(useDeepCompareMemoize));
16
14
  };
15
+ export { useDeepCompareEffect };
package/package.json CHANGED
@@ -1,7 +1,17 @@
1
1
  {
2
2
  "name": "@neo4j-nvl/react",
3
- "version": "1.2.1",
3
+ "version": "2.0.0-1e695738",
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": "yarn global:tsc -w",
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,27 @@
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.21",
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": "1.2.1",
40
- "@neo4j-nvl/interaction-handlers": "1.2.1",
41
- "lodash": "4.18.1"
53
+ "@neo4j-nvl/base": "2.0.0-1e695738",
54
+ "@neo4j-nvl/interaction-handlers": "2.0.0-1e695738",
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"
46
- }
58
+ "react": "^18.0.0 || ^19.0.0",
59
+ "react-dom": "^18.0.0 || ^19.0.0"
60
+ },
61
+ "stableVersion": "2.0.0"
47
62
  }
@@ -1 +0,0 @@
1
- import '@testing-library/jest-dom';
@@ -1,135 +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' }], layout: HierarchicalLayoutType, layoutOptions: { enableCytoscape: true }, nvlOptions: { renderer: 'canvas' }, nvlCallbacks: { onLayoutDone: mockLayoutDoneFunction } }) }));
31
- expect(NVL).toHaveBeenCalledWith(expect.any(HTMLDivElement), [{ id: '1' }, { id: '2' }], [{ id: '12', from: '1', to: '2' }], { renderer: 'canvas', layout: HierarchicalLayoutType, layoutOptions: { enableCytoscape: true } }, {
32
- onLayoutDone: mockLayoutDoneFunction
33
- });
34
- });
35
- test('destroys NVL on unmount', () => {
36
- const destroy = jest.fn();
37
- jest.spyOn(NVL.prototype, 'destroy').mockImplementation(destroy);
38
- const { unmount } = render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [] }) }));
39
- unmount();
40
- expect(NVL).toHaveBeenCalledTimes(1);
41
- expect(destroy).toHaveBeenCalledTimes(1);
42
- });
43
- test('successfully re-initialises NVL when using React StrictMode', () => {
44
- const destroy = jest.fn();
45
- jest.spyOn(NVL.prototype, 'destroy').mockImplementation(destroy);
46
- render(_jsx(StrictMode, { children: _jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [] }) }) }));
47
- expect(NVL).toHaveBeenCalledTimes(2);
48
- expect(destroy).toHaveBeenCalledTimes(1);
49
- });
50
- test('reapplies zoom and pan when re-initialising NVL with React StrictMode', () => {
51
- const destroy = jest.fn();
52
- jest.spyOn(NVL.prototype, 'destroy').mockImplementation(destroy);
53
- render(_jsx(StrictMode, { children: _jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 1.5, pan: { x: 10, y: 20 } }) }) }));
54
- expect(NVL).toHaveBeenCalledTimes(2);
55
- expect(destroy).toHaveBeenCalledTimes(1);
56
- expect(setZoomAndPan).toHaveBeenCalledTimes(2);
57
- expect(setZoomAndPan).toHaveBeenNthCalledWith(1, 1.5, 10, 20);
58
- expect(setZoomAndPan).toHaveBeenNthCalledWith(2, 1.5, 10, 20);
59
- });
60
- test('calls setZoom when zoom property is provided', () => {
61
- render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 1.5 }) }));
62
- expect(setZoom).toHaveBeenCalledWith(1.5);
63
- expect(setZoom).toHaveBeenCalledTimes(1);
64
- });
65
- test('calls setPan when pan property is provided', () => {
66
- render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], pan: { x: 10, y: 20 } }) }));
67
- expect(setPan).toHaveBeenCalledWith(10, 20);
68
- expect(setPan).toHaveBeenCalledTimes(1);
69
- });
70
- test('calls setZoomAndPan when both zoom and pan properties are provided', () => {
71
- render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 1.5, pan: { x: 10, y: 20 } }) }));
72
- expect(setZoomAndPan).toHaveBeenCalledWith(1.5, 10, 20);
73
- expect(setZoomAndPan).toHaveBeenCalledTimes(1);
74
- });
75
- test('does not call zoom/pan methods when properties are undefined', () => {
76
- render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [] }) }));
77
- expect(setZoom).not.toHaveBeenCalled();
78
- expect(setPan).not.toHaveBeenCalled();
79
- expect(setZoomAndPan).not.toHaveBeenCalled();
80
- });
81
- test('only calls setZoom when zoom changes', () => {
82
- const { rerender } = render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 1.5 }) }));
83
- expect(setZoom).toHaveBeenCalledTimes(1);
84
- expect(setZoom).toHaveBeenCalledWith(1.5);
85
- rerender(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 1.5 }) }));
86
- expect(setZoom).toHaveBeenCalledTimes(1);
87
- rerender(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 2.0 }) }));
88
- expect(setZoom).toHaveBeenCalledTimes(2);
89
- expect(setZoom).toHaveBeenCalledWith(2.0);
90
- expect(setPan).not.toHaveBeenCalled();
91
- expect(setZoomAndPan).not.toHaveBeenCalled();
92
- });
93
- test('only calls setPan when pan changes', () => {
94
- const { rerender } = render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], pan: { x: 10, y: 20 } }) }));
95
- expect(setPan).toHaveBeenCalledTimes(1);
96
- expect(setPan).toHaveBeenCalledWith(10, 20);
97
- rerender(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], pan: { x: 10, y: 20 } }) }));
98
- expect(setPan).toHaveBeenCalledTimes(1);
99
- rerender(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], pan: { x: 30, y: 40 } }) }));
100
- expect(setPan).toHaveBeenCalledTimes(2);
101
- expect(setPan).toHaveBeenCalledWith(30, 40);
102
- expect(setZoom).not.toHaveBeenCalled();
103
- expect(setZoomAndPan).not.toHaveBeenCalled();
104
- });
105
- test('calls setZoomAndPan when both zoom and pan change', () => {
106
- const { rerender } = render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 1.5, pan: { x: 10, y: 20 } }) }));
107
- expect(setZoomAndPan).toHaveBeenCalledTimes(1);
108
- expect(setZoomAndPan).toHaveBeenCalledWith(1.5, 10, 20);
109
- rerender(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 1.5, pan: { x: 10, y: 20 } }) }));
110
- expect(setZoomAndPan).toHaveBeenCalledTimes(1);
111
- rerender(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 2.0, pan: { x: 30, y: 40 } }) }));
112
- expect(setZoomAndPan).toHaveBeenCalledTimes(2);
113
- expect(setZoomAndPan).toHaveBeenCalledWith(2.0, 30, 40);
114
- expect(setZoom).not.toHaveBeenCalled();
115
- expect(setPan).not.toHaveBeenCalled();
116
- });
117
- test('calls appropriate methods when only zoom changes while both are set', () => {
118
- const { rerender } = render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 1.5, pan: { x: 10, y: 20 } }) }));
119
- expect(setZoomAndPan).toHaveBeenCalledTimes(1);
120
- rerender(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 2.0, pan: { x: 10, y: 20 } }) }));
121
- expect(setZoom).toHaveBeenCalledTimes(1);
122
- expect(setZoom).toHaveBeenCalledWith(2.0);
123
- expect(setZoomAndPan).toHaveBeenCalledTimes(1);
124
- expect(setPan).not.toHaveBeenCalled();
125
- });
126
- test('calls appropriate methods when only pan changes while both are set', () => {
127
- const { rerender } = render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 1.5, pan: { x: 10, y: 20 } }) }));
128
- expect(setZoomAndPan).toHaveBeenCalledTimes(1);
129
- rerender(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 1.5, pan: { x: 30, y: 40 } }) }));
130
- expect(setPan).toHaveBeenCalledTimes(1);
131
- expect(setPan).toHaveBeenCalledWith(30, 40);
132
- expect(setZoomAndPan).toHaveBeenCalledTimes(1);
133
- expect(setZoom).not.toHaveBeenCalled();
134
- });
135
- });
@@ -1 +0,0 @@
1
- import '@testing-library/jest-dom';