@neo4j-nvl/react 0.3.1-4e3f4948 → 0.3.1-61fc472d

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/README.md CHANGED
@@ -14,15 +14,10 @@ npm install @neo4j-nvl/react
14
14
 
15
15
  ### Using the library
16
16
 
17
- This is an example on how to use the BasicReactWrapper component:
17
+ This is an example on how to use the BasicReactWrapper component:
18
18
 
19
19
  ```tsx
20
- <BasicNvlWrapper
21
- nodes={nodes}
22
- rels={relationships}
23
- nvlOptions={options}
24
- nvlCallbacks={callbacks}
25
- />
20
+ <BasicNvlWrapper nodes={nodes} rels={relationships} nvlOptions={options} nvlCallbacks={callbacks} />
26
21
  ```
27
22
 
28
23
  When nodes and/or relationships are updated in the React wrapper, the NVL instance will be updated accordingly:
@@ -81,5 +76,9 @@ const [multiSelect, setMultiSelect] = useState(false)
81
76
  </>
82
77
  ```
83
78
 
79
+ You can also find more instructions and examples on how to use the NVL React wrappers in the [docs](https://neo4j.com/docs/nvl/current/react-wrappers/).
84
80
 
85
- You can also find more instructions and examples on how to use the NVL React wrappers in the [docs](https://neo4j.com/docs/nvl/current/react-wrappers/).
81
+ ### Product analytics
82
+
83
+ In order to improve the library we are collecting some information about the usage of NVL. If you prefer to disable this behavior set the parameter `disableTelemetry=true` as a parameter in `nvlOptions`.
84
+ For example: `<BasicNvlWrapper nvlOptions={{ disableTelemetry: true }} nodes=[0,1] rels=[0,1]/>` or `<InteractiveNvlWrapper nvlOptions={{ disableTelemetry: true }} nodes=[0,1] rels=[0,1]/>`
@@ -0,0 +1 @@
1
+ import '@testing-library/jest-dom';
@@ -0,0 +1,35 @@
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 React 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
+ afterEach(() => {
11
+ jest.clearAllMocks();
12
+ });
13
+ test('initialises NVL expectedly with an empty graph object and no other properties', () => {
14
+ render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [] }) }));
15
+ expect(NVL).toHaveBeenCalledWith(expect.any(HTMLDivElement), [], [], {}, {});
16
+ });
17
+ test('initialises NVL expectedly with a graph object and properties', () => {
18
+ const mockLayoutDoneFunction = jest.fn();
19
+ 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 } }) }));
20
+ expect(NVL).toHaveBeenCalledWith(expect.any(HTMLDivElement), [{ id: '1' }, { id: '2' }], [{ id: '12', from: '1', to: '2' }], { renderer: 'canvas', layout: HierarchicalLayoutType, layoutOptions: { enableCytoscape: true } }, {
21
+ onLayoutDone: mockLayoutDoneFunction
22
+ });
23
+ });
24
+ test('destroys NVL on unmount', () => {
25
+ const destroy = jest.fn();
26
+ jest.spyOn(NVL.prototype, 'destroy').mockImplementation(destroy);
27
+ const { unmount } = render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [] }) }));
28
+ unmount();
29
+ expect(destroy).toHaveBeenCalled();
30
+ });
31
+ test('successfully re-initialises NVL when using React StrictMode', () => {
32
+ render(_jsx(React.StrictMode, { children: _jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [] }) }) }));
33
+ expect(NVL).toHaveBeenCalledTimes(2);
34
+ });
35
+ });
@@ -0,0 +1 @@
1
+ import '@testing-library/jest-dom';
@@ -0,0 +1,90 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import NVL, { HierarchicalLayoutType } from '@neo4j-nvl/base';
3
+ import { BoxSelectInteraction, ClickInteraction, DragNodeInteraction, DrawInteraction, HoverInteraction, LassoInteraction, PanInteraction, ZoomInteraction } from '@neo4j-nvl/interaction-handlers';
4
+ import '@testing-library/jest-dom';
5
+ import { render } from '@testing-library/react';
6
+ import React, { 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
+ HoverInteraction.prototype.callbackMap = new Map();
12
+ PanInteraction.prototype.callbackMap = new Map();
13
+ describe('InteractiveNvlWrapper', () => {
14
+ afterEach(() => {
15
+ jest.clearAllMocks();
16
+ });
17
+ test('initialises NVL expectedly with an empty graph object and no other properties', () => {
18
+ render(_jsx("div", { children: _jsx(InteractiveNvlWrapper, { nodes: [], rels: [] }) }));
19
+ expect(NVL).toHaveBeenCalledWith(expect.any(HTMLDivElement), [], [], {}, {});
20
+ });
21
+ test('initialises NVL expectedly with a graph object and properties', () => {
22
+ const mockLayoutDoneFunction = jest.fn();
23
+ render(_jsx(InteractiveNvlWrapper, { nodes: [{ id: '1' }, { id: '2' }], rels: [{ id: '12', from: '1', to: '2' }], layout: HierarchicalLayoutType, layoutOptions: { enableCytoscape: true }, nvlOptions: { renderer: 'canvas' }, nvlCallbacks: { onLayoutDone: mockLayoutDoneFunction } }));
24
+ expect(NVL).toHaveBeenCalledWith(expect.any(HTMLDivElement), [{ id: '1' }, { id: '2' }], [{ id: '12', from: '1', to: '2' }], { renderer: 'canvas', layout: HierarchicalLayoutType, layoutOptions: { enableCytoscape: true } }, {
25
+ onLayoutDone: mockLayoutDoneFunction
26
+ });
27
+ });
28
+ test('initialises expected interaction handlers with correct options', () => {
29
+ const myNvlRef = createRef();
30
+ jest.spyOn(NVL.prototype, 'getContainer').mockImplementation(() => document.createElement('div'));
31
+ render(_jsx(InteractiveNvlWrapper, { nodes: [], rels: [], ref: myNvlRef, mouseEventCallbacks: {
32
+ onHover: true,
33
+ onPan: true
34
+ }, interactionOptions: {
35
+ drawShadowOnHover: true
36
+ } }));
37
+ expect(HoverInteraction).toHaveBeenCalledWith(myNvlRef.current, { drawShadowOnHover: true });
38
+ expect(PanInteraction).toHaveBeenCalledWith(myNvlRef.current, { drawShadowOnHover: true });
39
+ expect(HoverInteraction).toHaveBeenCalledTimes(1);
40
+ expect(PanInteraction).toHaveBeenCalledTimes(1);
41
+ expect(ClickInteraction).not.toHaveBeenCalled();
42
+ expect(DragNodeInteraction).not.toHaveBeenCalled();
43
+ expect(DrawInteraction).not.toHaveBeenCalled();
44
+ expect(ZoomInteraction).not.toHaveBeenCalled();
45
+ expect(BoxSelectInteraction).not.toHaveBeenCalled();
46
+ expect(LassoInteraction).not.toHaveBeenCalled();
47
+ });
48
+ test('destroys NVL and active interaction handlers on unmount', () => {
49
+ const destroy = jest.fn();
50
+ jest.spyOn(NVL.prototype, 'destroy').mockImplementation(destroy);
51
+ const destroyHoverInteraction = jest.fn();
52
+ jest.spyOn(HoverInteraction.prototype, 'destroy').mockImplementation(destroyHoverInteraction);
53
+ const destroyPanInteraction = jest.fn();
54
+ jest.spyOn(PanInteraction.prototype, 'destroy').mockImplementation(destroyPanInteraction);
55
+ jest.spyOn(NVL.prototype, 'getContainer').mockImplementation(() => document.createElement('div'));
56
+ const { unmount } = render(_jsx(InteractiveNvlWrapper, { nodes: [], rels: [], mouseEventCallbacks: {
57
+ onHover: true
58
+ } }));
59
+ unmount();
60
+ expect(destroy).toHaveBeenCalledTimes(1);
61
+ expect(destroyHoverInteraction).toHaveBeenCalledTimes(1);
62
+ expect(destroyPanInteraction).not.toHaveBeenCalled();
63
+ });
64
+ test('successfully re-initialises NVL and active interaction handlers when using React StrictMode', () => {
65
+ const destroy = jest.fn();
66
+ jest.spyOn(NVL.prototype, 'destroy').mockImplementation(destroy);
67
+ const destroyHoverInteraction = jest.fn();
68
+ jest.spyOn(HoverInteraction.prototype, 'destroy').mockImplementation(destroyHoverInteraction);
69
+ const destroyPanInteraction = jest.fn();
70
+ jest.spyOn(PanInteraction.prototype, 'destroy').mockImplementation(destroyPanInteraction);
71
+ jest.spyOn(NVL.prototype, 'getContainer').mockImplementation(() => document.createElement('div'));
72
+ const myNvlRef = createRef();
73
+ render(_jsx(React.StrictMode, { children: _jsx(InteractiveNvlWrapper, { nodes: [], rels: [], ref: myNvlRef, mouseEventCallbacks: {
74
+ onHover: true
75
+ }, interactionOptions: {
76
+ drawShadowOnHover: true
77
+ } }) }));
78
+ expect(NVL).toHaveBeenCalledTimes(2);
79
+ expect(HoverInteraction).toHaveBeenCalledTimes(2);
80
+ expect(HoverInteraction).toHaveBeenCalledWith(myNvlRef.current, { drawShadowOnHover: true });
81
+ expect(destroy).toHaveBeenCalledTimes(1);
82
+ expect(destroyHoverInteraction).toHaveBeenCalledTimes(1);
83
+ expect(destroyPanInteraction).not.toHaveBeenCalled();
84
+ expect(DragNodeInteraction).not.toHaveBeenCalled();
85
+ expect(DrawInteraction).not.toHaveBeenCalled();
86
+ expect(ZoomInteraction).not.toHaveBeenCalled();
87
+ expect(BoxSelectInteraction).not.toHaveBeenCalled();
88
+ expect(LassoInteraction).not.toHaveBeenCalled();
89
+ });
90
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ import * as exportedModules from '../index';
2
+ jest.mock('@neo4j-nvl/layout-workers');
3
+ describe('index', () => {
4
+ test('provides the expected components', () => {
5
+ const modules = Object.keys(exportedModules);
6
+ expect(modules).toContain('BasicNvlWrapper');
7
+ });
8
+ });
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import NVL from '@neo4j-nvl/base';
3
3
  import { forwardRef, memo, useEffect, useImperativeHandle, useRef, useState } from 'react';
4
+ import { BASIC_WRAPPER_ID } from '../utils/constants';
4
5
  import { getMapDifferences, getNodeAttributeDifferences } from '../utils/graph-comparison';
5
6
  import { useDeepCompareEffect } from '../utils/hooks';
6
7
  /**
@@ -192,5 +193,5 @@ export const BasicNvlWrapper = memo(forwardRef(({ nodes, rels, layout, layoutOpt
192
193
  }
193
194
  nvlRef.current.setDisableWebGL(nvlOptions.disableWebGL);
194
195
  }, [nvlOptions.disableWebGL]);
195
- return _jsx("div", { ref: containerRef, style: { height: '100%', outline: '0' }, ...nvlEvents });
196
+ return _jsx("div", { id: BASIC_WRAPPER_ID, ref: containerRef, style: { height: '100%', outline: '0' }, ...nvlEvents });
196
197
  }));
@@ -2,6 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { BoxSelectInteraction, ClickInteraction, DragNodeInteraction, DrawInteraction, HoverInteraction, LassoInteraction, PanInteraction, ZoomInteraction } from '@neo4j-nvl/interaction-handlers';
3
3
  import { forwardRef, memo, useEffect, useRef } from 'react';
4
4
  import { BasicNvlWrapper } from '../basic-wrapper/BasicNvlWrapper';
5
+ import { INTERACTIVE_WRAPPER_ID } from '../utils/constants';
5
6
  import { destroyInteraction, useInteraction } from './hooks';
6
7
  const options = {
7
8
  selectOnClick: false,
@@ -40,18 +41,18 @@ const options = {
40
41
  * }
41
42
  * ```
42
43
  */
43
- export const InteractiveNvlWrapper = memo(forwardRef(({ nodes, rels, layout, layoutOptions, mouseEventCallbacks = {}, nvlCallbacks = {}, nvlOptions = {}, interactionOptions = options, onInitializationError = null, ...nvlEvents }, nvlRef) => {
44
+ export const InteractiveNvlWrapper = memo(forwardRef(({ nodes, rels, layout, layoutOptions, onInitializationError, mouseEventCallbacks = {}, nvlCallbacks = {}, nvlOptions = {}, interactionOptions = options, ...nvlEvents }, nvlRef) => {
44
45
  const newNvlRef = useRef(null);
45
46
  const myNvlRef = nvlRef ?? newNvlRef;
46
47
  const { onHover, onNodeClick, onNodeDoubleClick, onNodeRightClick, onRelationshipClick, onRelationshipDoubleClick, onRelationshipRightClick, onCanvasClick, onCanvasRightClick, onPan, onZoom, onDrag, onDragStart, onDragEnd, onDrawEnd, onHoverNodeMargin, onBoxStarted, onBoxSelect, onLassoStarted, onLassoSelect } = mouseEventCallbacks;
47
- const hoverInteraction = useRef();
48
- const clickInteraction = useRef();
49
- const panInteraction = useRef();
50
- const zoomInteraction = useRef();
51
- const dragNodeInteraction = useRef();
52
- const drawInteraction = useRef();
53
- const multiSelectInteraction = useRef();
54
- const lassoInteraction = useRef();
48
+ const hoverInteraction = useRef(null);
49
+ const clickInteraction = useRef(null);
50
+ const panInteraction = useRef(null);
51
+ const zoomInteraction = useRef(null);
52
+ const dragNodeInteraction = useRef(null);
53
+ const drawInteraction = useRef(null);
54
+ const multiSelectInteraction = useRef(null);
55
+ const lassoInteraction = useRef(null);
55
56
  useInteraction(HoverInteraction, hoverInteraction, onHover, 'onHover', myNvlRef, interactionOptions);
56
57
  useInteraction(ClickInteraction, clickInteraction, onNodeClick, 'onNodeClick', myNvlRef, interactionOptions);
57
58
  useInteraction(ClickInteraction, clickInteraction, onNodeDoubleClick, 'onNodeDoubleClick', myNvlRef, interactionOptions);
@@ -82,5 +83,5 @@ export const InteractiveNvlWrapper = memo(forwardRef(({ nodes, rels, layout, lay
82
83
  destroyInteraction(multiSelectInteraction);
83
84
  destroyInteraction(lassoInteraction);
84
85
  }, []);
85
- return (_jsx(BasicNvlWrapper, { ref: myNvlRef, nodes: nodes, rels: rels, nvlOptions: nvlOptions, nvlCallbacks: nvlCallbacks, layout: layout, layoutOptions: layoutOptions, onInitializationError: onInitializationError, ...nvlEvents }));
86
+ return (_jsx(BasicNvlWrapper, { ref: myNvlRef, nodes: nodes, id: INTERACTIVE_WRAPPER_ID, rels: rels, nvlOptions: nvlOptions, nvlCallbacks: nvlCallbacks, layout: layout, layoutOptions: layoutOptions, onInitializationError: onInitializationError, ...nvlEvents }));
86
87
  }));
@@ -2,4 +2,4 @@ import type NVL from '@neo4j-nvl/base';
2
2
  import type { MutableRefObject } from 'react';
3
3
  import type { InteractionOptions, MouseEvent, MouseInteraction, MouseInteractionModule } from './types';
4
4
  export declare const destroyInteraction: (interactionRef: MutableRefObject<MouseInteraction | null>) => void;
5
- export declare const useInteraction: (Interaction: MouseInteractionModule, interactionRef: MutableRefObject<MouseInteraction>, callback: ((...args: unknown[]) => void) | boolean | undefined, eventName: MouseEvent, nvlRef: MutableRefObject<NVL | null>, interactionOptions: InteractionOptions) => void;
5
+ export declare const useInteraction: (Interaction: MouseInteractionModule, interactionRef: MutableRefObject<MouseInteraction | null>, callback: ((...args: unknown[]) => void) | boolean | undefined, eventName: MouseEvent, nvlRef: MutableRefObject<NVL | null>, interactionOptions: InteractionOptions) => void;
@@ -0,0 +1,2 @@
1
+ export const BASIC_WRAPPER_ID: "NVL_basic-wrapper";
2
+ export const INTERACTIVE_WRAPPER_ID: "NVL_interactive-wrapper";
@@ -0,0 +1,2 @@
1
+ export const BASIC_WRAPPER_ID = 'NVL_basic-wrapper';
2
+ export const INTERACTIVE_WRAPPER_ID = 'NVL_interactive-wrapper';
@@ -11,5 +11,6 @@ function useDeepCompareMemoize(value) {
11
11
  return ref.current;
12
12
  }
13
13
  export const useDeepCompareEffect = (callback, dependencies) => {
14
+ // eslint-disable-next-line react-hooks/exhaustive-deps
14
15
  useEffect(callback, dependencies.map(useDeepCompareMemoize));
15
16
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neo4j-nvl/react",
3
- "version": "0.3.1-4e3f4948",
3
+ "version": "0.3.1-61fc472d",
4
4
  "main": "lib/index.js",
5
5
  "homepage": "https://neo4j.com/docs/nvl/current/",
6
6
  "license": "SEE LICENSE IN 'LICENSE.txt'",
@@ -19,7 +19,8 @@
19
19
  "watch": "tsc -w",
20
20
  "test": "jest",
21
21
  "prepack": "cp ../../LICENSE.txt ./",
22
- "postpack": "rm LICENSE.txt"
22
+ "postpack": "rm LICENSE.txt",
23
+ "eslint": "eslint ./src/"
23
24
  },
24
25
  "files": [
25
26
  "LICENSE.txt",
@@ -37,17 +38,19 @@
37
38
  "@types/lodash": "4.14.202",
38
39
  "@types/react": "18.2.58",
39
40
  "babel-eslint": "^10.1.0",
41
+ "eslint": "8.38.0",
40
42
  "jest": "^29.7.0",
41
43
  "typescript": "^5.4.5"
42
44
  },
43
45
  "dependencies": {
44
- "@neo4j-nvl/base": "^0.3.1-4e3f4948",
45
- "@neo4j-nvl/interaction-handlers": "^0.3.1-4e3f4948",
46
+ "@neo4j-nvl/base": "^0.3.1-61fc472d",
47
+ "@neo4j-nvl/interaction-handlers": "^0.3.1-61fc472d",
46
48
  "lodash": "4.17.21",
47
49
  "react": "^18.2.0",
48
50
  "react-dom": "^18.2.0"
49
51
  },
50
52
  "peerDependencies": {
53
+ "eslint": "*",
51
54
  "jest": "*",
52
55
  "typescript": "*"
53
56
  },