@neo4j-nvl/react 0.3.9-bdb232e9 → 0.3.9-ce347313

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.
@@ -36,4 +36,34 @@ describe('BasicNvlWrapper', () => {
36
36
  expect(NVL).toHaveBeenCalledTimes(2);
37
37
  expect(destroy).toHaveBeenCalledTimes(1);
38
38
  });
39
+ test('calls setZoom when zoom property is provided', () => {
40
+ const setZoom = jest.fn();
41
+ jest.spyOn(NVL.prototype, 'setZoom').mockImplementation(setZoom);
42
+ render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 1.5 }) }));
43
+ expect(setZoom).toHaveBeenCalledWith(1.5);
44
+ });
45
+ test('calls setPan when pan property is provided', () => {
46
+ const setPan = jest.fn();
47
+ jest.spyOn(NVL.prototype, 'setPan').mockImplementation(setPan);
48
+ render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], pan: { x: 10, y: 20 } }) }));
49
+ expect(setPan).toHaveBeenCalledWith(10, 20);
50
+ });
51
+ test('calls setZoomAndPan when both zoom and pan properties are provided', () => {
52
+ const setZoomAndPan = jest.fn();
53
+ jest.spyOn(NVL.prototype, 'setZoomAndPan').mockImplementation(setZoomAndPan);
54
+ render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [], zoom: 1.5, pan: { x: 10, y: 20 } }) }));
55
+ expect(setZoomAndPan).toHaveBeenCalledWith(1.5, 10, 20);
56
+ });
57
+ test('does not call zoom/pan methods when properties are undefined', () => {
58
+ const setZoom = jest.fn();
59
+ jest.spyOn(NVL.prototype, 'setZoom').mockImplementation(setZoom);
60
+ const setPan = jest.fn();
61
+ jest.spyOn(NVL.prototype, 'setPan').mockImplementation(setPan);
62
+ const setZoomAndPan = jest.fn();
63
+ jest.spyOn(NVL.prototype, 'setZoomAndPan').mockImplementation(setZoomAndPan);
64
+ render(_jsx("div", { children: _jsx(BasicNvlWrapper, { nodes: [], rels: [] }) }));
65
+ expect(setZoom).not.toHaveBeenCalled();
66
+ expect(setPan).not.toHaveBeenCalled();
67
+ expect(setZoomAndPan).not.toHaveBeenCalled();
68
+ });
39
69
  });
@@ -2,12 +2,13 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import NVL, { HierarchicalLayoutType } from '@neo4j-nvl/base';
3
3
  import { BoxSelectInteraction, ClickInteraction, DragNodeInteraction, DrawInteraction, HoverInteraction, LassoInteraction, PanInteraction, ZoomInteraction } from '@neo4j-nvl/interaction-handlers';
4
4
  import '@testing-library/jest-dom';
5
- import { act, render } from '@testing-library/react';
6
- import React, { createRef } from 'react';
5
+ import { render } from '@testing-library/react';
6
+ import React, { act, createRef } from 'react';
7
7
  import { InteractiveNvlWrapper } from '../interactive-nvl-wrapper/InteractiveNvlWrapper';
8
8
  jest.mock('@neo4j-nvl/base');
9
9
  jest.mock('@neo4j-nvl/layout-workers');
10
10
  jest.mock('@neo4j-nvl/interaction-handlers');
11
+ const MockedNVL = NVL;
11
12
  let mockOnInitialization;
12
13
  HoverInteraction.prototype.callbackMap = new Map();
13
14
  PanInteraction.prototype.callbackMap = new Map();
@@ -17,10 +18,7 @@ let destroyPanInteraction;
17
18
  describe('InteractiveNvlWrapper', () => {
18
19
  beforeEach(() => {
19
20
  jest.spyOn(NVL.prototype, 'getContainer').mockImplementation(() => document.createElement('div'));
20
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
21
- // @ts-expect-error
22
- // eslint-disable-next-line @typescript-eslint/no-unsafe-call
23
- NVL.mockImplementation((_container, _nodes, _rels, _options, callbacks) => {
21
+ MockedNVL.mockImplementation((_container, _nodes, _rels, _options, callbacks) => {
24
22
  mockOnInitialization = callbacks.onInitialization;
25
23
  return {
26
24
  getContainer: () => document.createElement('div'),
@@ -42,7 +40,7 @@ describe('InteractiveNvlWrapper', () => {
42
40
  });
43
41
  test('initialises NVL expectedly with an empty graph object and no other properties', () => {
44
42
  render(_jsx("div", { children: _jsx(InteractiveNvlWrapper, { nodes: [], rels: [] }) }));
45
- expect(NVL).toHaveBeenCalledWith(expect.any(HTMLDivElement), [], [], {}, { onInitialization: expect.any(Function) });
43
+ expect(NVL).toHaveBeenCalledWith(expect.any(HTMLDivElement), [], [], {}, { onInitialization: mockOnInitialization });
46
44
  });
47
45
  test('initialises NVL expectedly with a graph object and properties', () => {
48
46
  const mockLayoutDoneFunction = jest.fn();
@@ -52,7 +50,7 @@ describe('InteractiveNvlWrapper', () => {
52
50
  });
53
51
  expect(NVL).toHaveBeenCalledWith(expect.any(HTMLDivElement), [{ id: '1' }, { id: '2' }], [{ id: '12', from: '1', to: '2' }], { renderer: 'canvas', layout: HierarchicalLayoutType, layoutOptions: { enableCytoscape: true } }, {
54
52
  onLayoutDone: mockLayoutDoneFunction,
55
- onInitialization: expect.any(Function)
53
+ onInitialization: mockOnInitialization
56
54
  });
57
55
  });
58
56
  test('initialises expected interaction handlers with correct options', () => {
@@ -132,4 +130,24 @@ describe('InteractiveNvlWrapper', () => {
132
130
  });
133
131
  expect(customOnInitializationCallback).toHaveBeenCalledTimes(1);
134
132
  });
133
+ test('passes zoom and pan properties to BasicNvlWrapper', () => {
134
+ const setZoom = jest.fn();
135
+ const setPan = jest.fn();
136
+ const setZoomAndPan = jest.fn();
137
+ MockedNVL.mockImplementation(() => ({
138
+ getContainer: () => document.createElement('div'),
139
+ destroy: jest.fn(),
140
+ setLayout: jest.fn(),
141
+ setLayoutOptions: jest.fn(),
142
+ setNodePositions: jest.fn(),
143
+ setZoom,
144
+ setPan,
145
+ setZoomAndPan
146
+ }));
147
+ render(_jsx(InteractiveNvlWrapper, { nodes: [], rels: [], zoom: 1.5, pan: { x: 10, y: 20 } }));
148
+ act(() => {
149
+ mockOnInitialization?.();
150
+ });
151
+ expect(setZoomAndPan).toHaveBeenCalledWith(1.5, 10, 20);
152
+ });
135
153
  });
@@ -1,12 +1,6 @@
1
1
  import type { ExternalCallbacks, Layout, LayoutOptions, Node, NvlOptions, Relationship } from '@neo4j-nvl/base';
2
2
  import NVL from '@neo4j-nvl/base';
3
3
  import { type HTMLProps } from 'react';
4
- /**
5
- * @hidden
6
- */
7
- type IncludeMethods<T> = Pick<T, {
8
- [K in keyof T]: T[K] extends (_: unknown) => unknown ? K : never;
9
- }[keyof T]>;
10
4
  /**
11
5
  * The properties that can be passed to the {@link BasicNvlWrapper} component.
12
6
  */
@@ -23,18 +17,32 @@ export interface BasicReactWrapperProps {
23
17
  nvlCallbacks?: ExternalCallbacks;
24
18
  /** An object containing options for the Nvl instance */
25
19
  nvlOptions?: NvlOptions;
26
- /** Sets the positions of the nodes in the graph using the {@link NVL.setNodePositions} method. */
20
+ /** Sets the positions of the nodes in the graph using the setNodePositions method. */
27
21
  positions?: Node[];
22
+ /**
23
+ * Sets the zoom level of the viewport using the setZoom method.
24
+ * @remarks If both zoom and pan are provided, the setZoomAndPan method will be used.
25
+ */
26
+ zoom?: number;
27
+ /**
28
+ * Sets the pan coordinates of the viewport using the setPan method.
29
+ * @remarks If both zoom and pan are provided, the setZoomAndPan method will be used.
30
+ */
31
+ pan?: {
32
+ /** The x coordinate of the pan. */
33
+ x: number;
34
+ /** The y coordinate of the pan. */
35
+ y: number;
36
+ };
28
37
  /** A callback to handle any errors that happen during NVL initialization */
29
38
  onInitializationError?: (error: unknown) => void;
30
39
  }
31
40
  /**
32
41
  *
33
- * A basic React wrapper that wraps the base {@link NVL} library within a React component.
42
+ * A basic React wrapper that wraps the NVL base library within a React component.
34
43
  * It takes the class' arguments as properties, which are passed to the NVL constructor.
35
44
  * Any changes in properties will be reflected in the NVL instance by calling the corresponding methods.
36
45
  *
37
46
  * For examples, head to the {@link https://neo4j.com/docs/nvl/current/react-wrappers/#_basic_react_wrapper Basic React wrapper documentation page}.
38
47
  */
39
- export declare const BasicNvlWrapper: import("react").MemoExoticComponent<import("react").ForwardRefExoticComponent<Omit<BasicReactWrapperProps & HTMLProps<HTMLDivElement>, "ref"> & import("react").RefAttributes<IncludeMethods<NVL>>>>;
40
- export {};
48
+ export declare const BasicNvlWrapper: import("react").MemoExoticComponent<import("react").ForwardRefExoticComponent<Omit<BasicReactWrapperProps & HTMLProps<HTMLDivElement>, "ref"> & import("react").RefAttributes<Partial<Pick<NVL, "performance" | "restart" | "destroy" | "addAndUpdateElementsInGraph" | "getSelectedNodes" | "getSelectedRelationships" | "removeNodesWithIds" | "removeRelationshipsWithIds" | "getNodes" | "getRelationships" | "getNodeById" | "getRelationshipById" | "getPositionById" | "getCurrentOptions" | "deselectAll" | "fit" | "resetZoom" | "setUseWebGLRenderer" | "setRenderer" | "setDisableWebGL" | "pinNode" | "unPinNode" | "setLayout" | "setLayoutOptions" | "getNodesOnScreen" | "getNodePositions" | "setNodePositions" | "isLayoutMoving" | "saveToFile" | "getImageDataUrl" | "saveFullGraphToLargeFile" | "setZoom" | "getScale" | "getPan" | "getHits" | "getContainer">>>>>;
@@ -6,16 +6,15 @@ import { getMapDifferences, getNodeAttributeDifferences } from '../utils/graph-c
6
6
  import { useDeepCompareEffect } from '../utils/hooks';
7
7
  /**
8
8
  *
9
- * A basic React wrapper that wraps the base {@link NVL} library within a React component.
9
+ * A basic React wrapper that wraps the NVL base library within a React component.
10
10
  * It takes the class' arguments as properties, which are passed to the NVL constructor.
11
11
  * Any changes in properties will be reflected in the NVL instance by calling the corresponding methods.
12
12
  *
13
13
  * For examples, head to the {@link https://neo4j.com/docs/nvl/current/react-wrappers/#_basic_react_wrapper Basic React wrapper documentation page}.
14
14
  */
15
- export const BasicNvlWrapper = memo(forwardRef(({ nodes, rels, layout, layoutOptions, nvlCallbacks = {}, nvlOptions = {}, positions = [], onInitializationError, ...nvlEvents }, ref) => {
15
+ export const BasicNvlWrapper = memo(forwardRef(({ nodes, rels, layout, layoutOptions, nvlCallbacks = {}, nvlOptions = {}, positions = [], zoom, pan, onInitializationError, ...nvlEvents }, ref) => {
16
16
  const nvlRef = useRef(null);
17
17
  useImperativeHandle(ref, () => {
18
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
19
18
  const nvlMethods = Object.getOwnPropertyNames(NVL.prototype);
20
19
  return nvlMethods.reduce((current, method) => ({
21
20
  ...current,
@@ -23,13 +22,10 @@ export const BasicNvlWrapper = memo(forwardRef(({ nodes, rels, layout, layoutOpt
23
22
  if (nvlRef.current === null) {
24
23
  return null;
25
24
  }
26
- // @ts-ignore suppress the type casting error on spreading
25
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
27
26
  return nvlRef.current[method](...args);
28
27
  }
29
- }),
30
- // eslint-disable-next-line max-len
31
- // eslint-disable-next-line @typescript-eslint/prefer-reduce-type-parameter, @typescript-eslint/consistent-type-assertions
32
- {});
28
+ }), {});
33
29
  });
34
30
  const containerRef = useRef(null);
35
31
  const [currentNodes, setCurrentNodes] = useState(nodes);
@@ -128,5 +124,19 @@ export const BasicNvlWrapper = memo(forwardRef(({ nodes, rels, layout, layoutOpt
128
124
  }
129
125
  nvlRef.current.setNodePositions(positions);
130
126
  }, [positions]);
127
+ useEffect(() => {
128
+ if (nvlRef.current === null) {
129
+ return;
130
+ }
131
+ if (zoom !== undefined && pan !== undefined) {
132
+ nvlRef.current.setZoomAndPan(zoom, pan.x, pan.y);
133
+ }
134
+ else if (zoom !== undefined) {
135
+ nvlRef.current.setZoom(zoom);
136
+ }
137
+ else if (pan !== undefined) {
138
+ nvlRef.current.setPan(pan.x, pan.y);
139
+ }
140
+ }, [zoom, pan]);
131
141
  return _jsx("div", { id: BASIC_WRAPPER_ID, ref: containerRef, style: { height: '100%', outline: '0' }, ...nvlEvents });
132
142
  }));
@@ -1,15 +1,15 @@
1
1
  import { isEqual } from 'lodash';
2
2
  import { useEffect, useRef } from 'react';
3
- function deepCompareEquals(a, b) {
3
+ const deepCompareEquals = (a, b) => {
4
4
  return isEqual(a, b);
5
- }
6
- function useDeepCompareMemoize(value) {
5
+ };
6
+ const useDeepCompareMemoize = (value) => {
7
7
  const ref = useRef();
8
8
  if (!deepCompareEquals(value, ref.current)) {
9
9
  ref.current = value;
10
10
  }
11
11
  return ref.current;
12
- }
12
+ };
13
13
  export const useDeepCompareEffect = (callback, dependencies) => {
14
14
  // eslint-disable-next-line react-hooks/exhaustive-deps
15
15
  useEffect(callback, dependencies.map(useDeepCompareMemoize));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neo4j-nvl/react",
3
- "version": "0.3.9-bdb232e9",
3
+ "version": "0.3.9-ce347313",
4
4
  "main": "lib/index.js",
5
5
  "homepage": "https://neo4j.com/docs/nvl/current/",
6
6
  "license": "SEE LICENSE IN 'LICENSE.txt'",
@@ -36,8 +36,8 @@
36
36
  "react-dom": "^19.1.1"
37
37
  },
38
38
  "dependencies": {
39
- "@neo4j-nvl/base": "0.3.9-bdb232e9",
40
- "@neo4j-nvl/interaction-handlers": "0.3.9-bdb232e9",
39
+ "@neo4j-nvl/base": "0.3.9-ce347313",
40
+ "@neo4j-nvl/interaction-handlers": "0.3.9-ce347313",
41
41
  "lodash": "4.17.21"
42
42
  },
43
43
  "peerDependencies": {