@reltio/graph 1.4.1584 → 1.4.1586-mui5

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 (164) hide show
  1. package/index.ts +1 -0
  2. package/package.json +26 -9
  3. package/public/bundle.js +205 -0
  4. package/public/bundle.js.LICENSE.txt +88 -0
  5. package/public/package.json +29 -0
  6. package/scripts/build/index.js +20 -0
  7. package/src/__tests__/GraphView.test.tsx +25 -0
  8. package/src/components/CircleLayout/CircleLayout.tsx +76 -0
  9. package/src/components/CircleLayout/__tests__/CircleLayout.test.tsx +194 -0
  10. package/src/components/GotoButton/GotoButton.tsx +23 -0
  11. package/src/components/GotoButton/__tests__/GotoButton.test.tsx +33 -0
  12. package/src/components/GotoButton/styles.ts +15 -0
  13. package/src/components/GraphArea/GraphArea.tsx +113 -0
  14. package/src/components/GraphArea/__tests__/GraphArea.test.tsx +202 -0
  15. package/src/components/GraphArea/styles.ts +30 -0
  16. package/src/components/GraphLayoutSelector/GraphLayoutSelector.tsx +43 -0
  17. package/src/components/GraphLayoutSelector/__tests__/GraphLayoutSelector.spec.tsx +44 -0
  18. package/src/components/GraphLayoutSelector/styles.ts +15 -0
  19. package/src/components/GraphPerspectiveView/GraphPerspectiveView.tsx +58 -0
  20. package/src/components/GraphPerspectiveView/__tests__/GraphPerspectiveView.test.tsx +115 -0
  21. package/src/components/GraphPerspectiveView/__tests__/useGraphLoader.test.tsx +1519 -0
  22. package/src/components/GraphPerspectiveView/__tests__/useRelationshipTable.test.tsx +1003 -0
  23. package/src/components/GraphPerspectiveView/__tests__/useSelectedEntity.test.tsx +373 -0
  24. package/src/components/GraphPerspectiveView/styles.ts +34 -0
  25. package/src/components/GraphRightSidePanel/GraphRightSidePanel.tsx +27 -0
  26. package/src/components/GraphRightSidePanel/__tests__/GraphRightSidePanel.test.tsx +18 -0
  27. package/src/components/GraphRightSidePanel/icons/relationship-icon.svg +3 -0
  28. package/src/components/GraphRightSidePanel/styles.ts +8 -0
  29. package/src/components/GraphTypeSelector/GraphTypeSelector.tsx +56 -0
  30. package/src/components/GraphTypeSelector/__tests__/GraphTypeSelector.test.tsx +109 -0
  31. package/src/components/GraphTypeSelector/styles.ts +17 -0
  32. package/src/components/HierarchyLayout/HierarchyLayout.ts +102 -0
  33. package/src/components/HierarchyLayout/__tests__/HierarchyLayout.spec.tsx +241 -0
  34. package/src/components/HierarchyLayout/__tests__/buildHierarchy.spec.ts +130 -0
  35. package/src/components/HierarchyLayout/__tests__/calculations.spec.ts +230 -0
  36. package/src/components/HierarchyLayout/buildHierarchy.ts +109 -0
  37. package/src/components/HierarchyLayout/calculations.ts +86 -0
  38. package/src/components/RightPanelEntityDetails/RightPanelEntityDetails.tsx +33 -0
  39. package/src/components/RightPanelEntityDetails/__tests__/RightPanelEntityDetails.test.tsx +39 -0
  40. package/src/components/RightPanelEntityDetails/styles.ts +12 -0
  41. package/src/components/RightPanelProfileBand/RightPanelProfileBand.tsx +33 -0
  42. package/src/components/RightPanelProfileBand/__tests__/RightPanelProfileBand.test.tsx +67 -0
  43. package/src/components/RightPanelProfileBand/styles.ts +8 -0
  44. package/src/components/RightPanelRelationship/RightPanelRelationship.tsx +25 -0
  45. package/src/components/RightPanelRelationship/__tests__/RightPanelRelationship.test.tsx +18 -0
  46. package/src/components/RightPanelRelationship/components/CollapsibleTable/CollapsibleTable.tsx +100 -0
  47. package/src/components/RightPanelRelationship/components/CollapsibleTable/TableHead.tsx +38 -0
  48. package/src/components/RightPanelRelationship/components/CollapsibleTable/__tests__/CollapsibleTable.test.tsx +169 -0
  49. package/src/components/RightPanelRelationship/components/CollapsibleTable/__tests__/TableHead.test.tsx +107 -0
  50. package/src/components/RightPanelRelationship/components/CollapsibleTable/styles.ts +38 -0
  51. package/src/components/RightPanelRelationship/components/CollapsibleTable/types.ts +20 -0
  52. package/src/components/RightPanelRelationship/components/DeleteRelationDialog/DeleteRelationDialog.tsx +34 -0
  53. package/src/components/RightPanelRelationship/components/DeleteRelationDialog/__tests__/DeleteRelationDialog.test.tsx +46 -0
  54. package/src/components/RightPanelRelationship/components/GraphAddRelationDialog/GraphAddRelationButton.tsx +43 -0
  55. package/src/components/RightPanelRelationship/components/GraphAddRelationDialog/GraphAddRelationDialog.tsx +121 -0
  56. package/src/components/RightPanelRelationship/components/GraphAddRelationDialog/__tests__/GraphAddRelationButton.test.tsx +80 -0
  57. package/src/components/RightPanelRelationship/components/GraphAddRelationDialog/__tests__/GraphAddRelationDialog.test.tsx +262 -0
  58. package/src/components/RightPanelRelationship/components/GraphAddRelationDialog/__tests__/mockdata.ts +75 -0
  59. package/src/components/RightPanelRelationship/components/GraphAddRelationDialog/styles.ts +25 -0
  60. package/src/components/RightPanelRelationship/components/GraphAddRelationDialog/useCreatableInOutRelationTypes.ts +13 -0
  61. package/src/components/RightPanelRelationship/components/RelationshipTable/RelationshipTable.tsx +126 -0
  62. package/src/components/RightPanelRelationship/components/RelationshipTable/__tests__/RelationshipTable.test.tsx +176 -0
  63. package/src/components/RightPanelRelationship/components/RelationshipTable/cell-renderers/ControlsCellRenderer.tsx +59 -0
  64. package/src/components/RightPanelRelationship/components/RelationshipTable/cell-renderers/DefaultCellRenderer.tsx +29 -0
  65. package/src/components/RightPanelRelationship/components/RelationshipTable/cell-renderers/ExpandedRowRenderer.tsx +94 -0
  66. package/src/components/RightPanelRelationship/components/RelationshipTable/cell-renderers/HeadCellRenderer.tsx +34 -0
  67. package/src/components/RightPanelRelationship/components/RelationshipTable/cell-renderers/ProfileCellRenderer.tsx +41 -0
  68. package/src/components/RightPanelRelationship/components/RelationshipTable/cell-renderers/__tests__/ControlsCellRenderer.test.tsx +80 -0
  69. package/src/components/RightPanelRelationship/components/RelationshipTable/cell-renderers/__tests__/DefaultCellRenderer.test.tsx +47 -0
  70. package/src/components/RightPanelRelationship/components/RelationshipTable/cell-renderers/__tests__/ExpandedRowRenderer.test.tsx +166 -0
  71. package/src/components/RightPanelRelationship/components/RelationshipTable/cell-renderers/__tests__/HeadCellRenderer.test.tsx +23 -0
  72. package/src/components/RightPanelRelationship/components/RelationshipTable/cell-renderers/__tests__/ProfileCellRenderer.test.tsx +47 -0
  73. package/src/components/RightPanelRelationship/components/RelationshipTable/cell-renderers/components/DeleteRelationButton.tsx +32 -0
  74. package/src/components/RightPanelRelationship/components/RelationshipTable/cell-renderers/components/__tests__/DeleteRelationButton.test.tsx +28 -0
  75. package/src/components/RightPanelRelationship/components/RelationshipTable/cell-renderers/styles.ts +113 -0
  76. package/src/components/RightPanelRelationship/components/RelationshipTable/styles.ts +36 -0
  77. package/src/components/RightPanelRelationship/components/RelationshipsFilters/RelationshipsFilters.tsx +41 -0
  78. package/src/components/RightPanelRelationship/components/RelationshipsFilters/RelationshipsFiltersDialog/RelationshipsFiltersDialog.tsx +91 -0
  79. package/src/components/RightPanelRelationship/components/RelationshipsFilters/RelationshipsFiltersDialog/__tests__/RelationshipsFilterDialog.test.tsx +192 -0
  80. package/src/components/RightPanelRelationship/components/RelationshipsFilters/RelationshipsFiltersDialog/styles.ts +34 -0
  81. package/src/components/RightPanelRelationship/components/RelationshipsFilters/RelationshipsSearchTextField/RelationshipsSearchTextField.tsx +31 -0
  82. package/src/components/RightPanelRelationship/components/RelationshipsFilters/RelationshipsSearchTextField/__tests__/RelationshipsSearchTextField.test.tsx +43 -0
  83. package/src/components/RightPanelRelationship/components/RelationshipsFilters/RelationshipsSearchTextField/styles.ts +17 -0
  84. package/src/components/RightPanelRelationship/components/RelationshipsFilters/__tests__/RelationshipsFilters.test.tsx +76 -0
  85. package/src/components/RightPanelRelationship/components/RelationshipsFilters/styles.ts +19 -0
  86. package/src/components/RightPanelRelationship/styles.ts +20 -0
  87. package/src/components/SigmaCustomRenderers/SigmaCustomRenderersContainer.tsx +16 -0
  88. package/src/components/SigmaGraphContainer/SigmaGraphContainer.tsx +55 -0
  89. package/src/components/SigmaGraphContainer/__tests__/SigmaGraphContainer.test.tsx +25 -0
  90. package/src/components/SigmaGraphResizer/SigmaGraphResizer.tsx +13 -0
  91. package/src/components/SigmaGraphResizer/__tests__/SigmaGraphResizer.test.tsx +28 -0
  92. package/src/components/TreeLayout/TreeLayout.tsx +33 -0
  93. package/src/components/TreeLayout/__tests__/TreeLayout.test.tsx +42 -0
  94. package/src/components/TreeLayout/styles.ts +10 -0
  95. package/src/components/ZoomSlider/ZoomSlider.tsx +90 -0
  96. package/src/components/ZoomSlider/__tests__/ZoomSlider.test.tsx +84 -0
  97. package/src/components/ZoomSlider/styles.ts +57 -0
  98. package/src/constants/index.ts +1 -0
  99. package/src/contexts/GraphStateContext.tsx +19 -0
  100. package/src/contexts/SigmaCustomRenderersContext.ts +20 -0
  101. package/src/helpers/relationsHelpers.ts +9 -0
  102. package/src/hooks/__tests__/useGraphLayout.test.tsx +825 -0
  103. package/src/hooks/__tests__/useGraphLayoutSelector.test.tsx +19 -0
  104. package/src/hooks/__tests__/useSelfRelationLoopsRenderer.test.ts +198 -0
  105. package/src/hooks/__tests__/useSigmaCustomRenderers.test.ts +273 -0
  106. package/src/hooks/__tests__/useTooltipRenderer.test.ts +289 -0
  107. package/src/hooks/useGraphLayout.ts +308 -0
  108. package/src/hooks/useGraphLayoutSelector.ts +19 -0
  109. package/src/hooks/useGraphLoader.ts +279 -0
  110. package/src/hooks/useGraphRightSidePanelElements.tsx +36 -0
  111. package/src/hooks/useGraphState.ts +40 -0
  112. package/src/hooks/useGraphType.ts +32 -0
  113. package/src/hooks/useRelationshipTable.ts +212 -0
  114. package/src/hooks/useRelationshipTableFilters.ts +43 -0
  115. package/src/hooks/useSelectedEntity.ts +89 -0
  116. package/src/hooks/useSelfRelationLoopsRenderer.ts +52 -0
  117. package/src/hooks/useSigmaCustomRenderers.ts +112 -0
  118. package/src/hooks/useTooltipRenderer.ts +80 -0
  119. package/src/index.tsx +37 -0
  120. package/src/rendering/canvas/__tests__/hover.spec.ts +31 -0
  121. package/src/rendering/canvas/__tests__/label.spec.ts +64 -0
  122. package/src/rendering/canvas/__tests__/nodeBackground.spec.ts +99 -0
  123. package/src/rendering/canvas/__tests__/selfRelationLoop.spec.ts +88 -0
  124. package/src/rendering/canvas/__tests__/tooltip.spec.ts +33 -0
  125. package/src/rendering/canvas/hover.ts +13 -0
  126. package/src/rendering/canvas/label.ts +33 -0
  127. package/src/rendering/canvas/nodeBackground.ts +61 -0
  128. package/src/rendering/canvas/selfRelationLoop.ts +108 -0
  129. package/src/rendering/canvas/tooltip.ts +38 -0
  130. package/src/rendering/icons/collapseIcon.inline.svg.ts +6 -0
  131. package/src/rendering/icons/expandIcon.inline.svg.ts +5 -0
  132. package/src/rendering/icons/no_photo.inline.svg.ts +13 -0
  133. package/src/rendering/webgl/edge.arrowHead.ts +87 -0
  134. package/src/rendering/webgl/edge.clamped.ts +246 -0
  135. package/src/rendering/webgl/edge.reversedArrowHead.ts +14 -0
  136. package/src/rendering/webgl/helpers/__tests__/imageHelper.spec.ts +36 -0
  137. package/src/rendering/webgl/helpers/imageHelper.ts +13 -0
  138. package/src/rendering/webgl/image.ts +115 -0
  139. package/src/rendering/webgl/node.border.ts +78 -0
  140. package/src/rendering/webgl/node.buttons.ts +241 -0
  141. package/src/rendering/webgl/node.image.ts +169 -0
  142. package/src/rendering/webgl/shaders/edge.clamped.vert.glsl.ts +55 -0
  143. package/src/rendering/webgl/shaders/node.border.frag.glsl.ts +30 -0
  144. package/src/rendering/webgl/shaders/node.border.vert.glsl.ts +37 -0
  145. package/src/rendering/webgl/shaders/node.buttons.frag.glsl.ts +32 -0
  146. package/src/rendering/webgl/shaders/node.buttons.vert.glsl.ts +49 -0
  147. package/src/rendering/webgl/shaders/node.image.frag.glsl.ts +46 -0
  148. package/src/rendering/webgl/shaders/node.image.vert.glsl.ts +42 -0
  149. package/src/types/graphDataTypes.ts +143 -0
  150. package/src/types/sigmaCustomRenderersTypes.ts +13 -0
  151. package/src/utils/__tests__/graph.spec.ts +152 -0
  152. package/src/utils/__tests__/graphLayout.spec.ts +125 -0
  153. package/src/utils/__tests__/hopsData.spec.ts +456 -0
  154. package/src/utils/__tests__/processResponceToGraph.spec.ts +165 -0
  155. package/src/utils/graph.ts +83 -0
  156. package/src/utils/graphLayout.ts +70 -0
  157. package/src/utils/hopsData.ts +198 -0
  158. package/src/utils/processResponseToGraph.ts +32 -0
  159. package/tsconfig.json +4 -0
  160. package/webpack.config.js +10 -0
  161. package/bundle.js +0 -2
  162. package/bundle.js.LICENSE.txt +0 -54
  163. /package/{5271a9e7b6651c852e93.png → public/5271a9e7b6651c852e93.png} +0 -0
  164. /package/{main.css → public/main.css} +0 -0
@@ -0,0 +1,825 @@
1
+ import {renderHook, act} from '@testing-library/react-hooks';
2
+ import {MultiGraph} from 'graphology';
3
+ import {useSigma, useSetSettings, useLoadGraph, useRegisterEvents} from '@react-sigma/core';
4
+ import * as reactRedux from 'react-redux';
5
+ import {animateNodes} from 'sigma/utils/animate';
6
+ import {HopsResponse, RelationTypeDirection} from '@reltio/mdm-sdk';
7
+ import {useGraphLayout} from '../useGraphLayout';
8
+ import {fillGraphWithHopsResponse} from '../../utils/graph';
9
+ import {useTooltipRenderer} from '../useTooltipRenderer';
10
+ import {GraphologyLayout, GraphologyWorkerLayoutConstructor, GraphPathType} from '../../types/graphDataTypes';
11
+
12
+ jest.mock('sigma/utils/animate', () => ({
13
+ animateNodes: jest.fn()
14
+ }));
15
+
16
+ jest.mock('@react-sigma/core', () => {
17
+ const sigma = jest.requireActual('@react-sigma/core');
18
+ return {
19
+ ...sigma,
20
+ useSigma: jest.fn().mockReturnValue({
21
+ getCamera: () => ({
22
+ setState: () => {}
23
+ }),
24
+ getGraph: () => ({
25
+ neighbors: () => {},
26
+ hasExtremity: () => {},
27
+ findEdge: () => {}
28
+ })
29
+ }),
30
+ useLoadGraph: jest.fn().mockReturnValue(jest.fn()),
31
+ useRegisterEvents: jest.fn().mockReturnValue(jest.fn()),
32
+ useSetSettings: jest.fn().mockReturnValue(jest.fn())
33
+ };
34
+ });
35
+
36
+ jest.mock('../useSelfRelationLoopsRenderer', () => ({
37
+ ...jest.requireActual<Record<string, unknown>>('../useSelfRelationLoopsRenderer'),
38
+ useSelfRelationLoopsRenderer: jest.fn().mockReturnValue({
39
+ selfRelationsPathsMap: {},
40
+ selfRelationLoopsCanvas: null
41
+ })
42
+ }));
43
+
44
+ jest.mock('../../utils/graphLayout', () => ({
45
+ ...jest.requireActual<Record<string, unknown>>('../../utils/graphLayout'),
46
+ getMousePos: jest.fn()
47
+ }));
48
+
49
+ jest.mock('../useTooltipRenderer');
50
+
51
+ const store = {
52
+ profile: {
53
+ entity: {
54
+ uri: 'node1',
55
+ type: 'hcp'
56
+ }
57
+ },
58
+ tenant: {id: 't1'},
59
+ metadata: {
60
+ t1: {
61
+ entityTypes: [
62
+ {
63
+ uri: 'hcp',
64
+ typeImage: 'images/defaultImage/typeImage_gender_neutral.png'
65
+ }
66
+ ],
67
+ relationTypes: [
68
+ {
69
+ uri: 'relType2',
70
+ typeColor: '#663344',
71
+ label: 'Affilation'
72
+ },
73
+ {
74
+ uri: 'relType3'
75
+ },
76
+ {
77
+ uri: 'relType4',
78
+ extendsTypeURI: 'relType2'
79
+ }
80
+ ]
81
+ }
82
+ },
83
+ ui: {
84
+ paths: {
85
+ imagePath: 'https://s3.amazonaws.com/reltio.images/api'
86
+ }
87
+ }
88
+ };
89
+
90
+ const data: HopsResponse = {
91
+ entities: [
92
+ {uri: 'node1', type: 'hcp', traversedRelations: 2, untraversedRelations: 0},
93
+ {uri: 'node2', type: 'hcp', traversedRelations: 3, untraversedRelations: 2},
94
+ {uri: 'node3', type: 'hca', traversedRelations: 1, untraversedRelations: 0},
95
+ {uri: 'node4', type: 'hco', traversedRelations: 1, untraversedRelations: 1},
96
+ {uri: 'node5', type: 'hcp', traversedRelations: 1, untraversedRelations: 0}
97
+ ],
98
+ relations: [
99
+ {
100
+ uri: 'r1',
101
+ type: 'relType1',
102
+ startObject: {
103
+ objectURI: 'node1'
104
+ },
105
+ endObject: {
106
+ objectURI: 'node2'
107
+ },
108
+ direction: RelationTypeDirection.directed
109
+ },
110
+ {
111
+ uri: 'r2',
112
+ type: 'relType2',
113
+ startObject: {
114
+ objectURI: 'node3'
115
+ },
116
+ endObject: {
117
+ objectURI: 'node2'
118
+ },
119
+ direction: RelationTypeDirection.directed
120
+ },
121
+ {
122
+ uri: 'r3',
123
+ type: 'relType3',
124
+ startObject: {
125
+ objectURI: 'node2'
126
+ },
127
+ endObject: {
128
+ objectURI: 'node4'
129
+ },
130
+ direction: RelationTypeDirection.undirected
131
+ },
132
+ {
133
+ uri: 'r4',
134
+ type: 'relType4',
135
+ startObject: {
136
+ objectURI: 'node1'
137
+ },
138
+ endObject: {
139
+ objectURI: 'node5'
140
+ },
141
+ direction: RelationTypeDirection.bidirectional
142
+ }
143
+ ]
144
+ };
145
+
146
+ type StartHookProps = {
147
+ selectedNode?: string;
148
+ graph?: MultiGraph;
149
+ onNodeClick?: (node: string) => void;
150
+ layoutSettings?: Record<string, unknown>;
151
+ syncLayout?: GraphologyLayout<Record<string, unknown>>;
152
+ workerLayout?: GraphologyWorkerLayoutConstructor<Record<string, unknown>>;
153
+ isDirected?: boolean;
154
+ onNodeCollapse?: (node: string, path: GraphPathType) => void;
155
+ onNodeExpand?: (node: string) => void;
156
+ };
157
+
158
+ const startHook = ({
159
+ selectedNode = 'node1',
160
+ graph = new MultiGraph(),
161
+ onNodeClick = jest.fn(),
162
+ onNodeCollapse = jest.fn(),
163
+ onNodeExpand = jest.fn(),
164
+ layoutSettings = {},
165
+ syncLayout,
166
+ workerLayout,
167
+ isDirected
168
+ }: StartHookProps = {}) => {
169
+ return renderHook(useGraphLayout, {
170
+ initialProps: {
171
+ data,
172
+ selectedNode,
173
+ graph,
174
+ onNodeClick,
175
+ onNodeCollapse,
176
+ onNodeExpand,
177
+ layoutSettings,
178
+ syncLayout,
179
+ workerLayout,
180
+ isDirected
181
+ }
182
+ });
183
+ };
184
+
185
+ const createGraph = () => {
186
+ const graph = new MultiGraph();
187
+ fillGraphWithHopsResponse(graph, data);
188
+ return graph;
189
+ };
190
+
191
+ describe('useGraphLayout tests', () => {
192
+ beforeAll(() => {
193
+ jest.spyOn(reactRedux, 'useSelector').mockImplementation((selector) => selector(store));
194
+ });
195
+
196
+ afterEach(() => {
197
+ jest.clearAllMocks();
198
+ });
199
+
200
+ it('should set ratio to 1.2', () => {
201
+ const graph = createGraph();
202
+ const setCameraState = jest.fn();
203
+
204
+ (useSigma as jest.Mock).mockReturnValue({
205
+ getCamera: () => ({
206
+ setState: setCameraState
207
+ }),
208
+ getGraph: () => graph
209
+ });
210
+
211
+ startHook();
212
+
213
+ expect(setCameraState).toHaveBeenCalledWith({ratio: 1.2});
214
+ });
215
+
216
+ it('onNodeClick should work correctly', () => {
217
+ const registerEvents = jest.fn();
218
+ const onNodeClick = jest.fn();
219
+ (useRegisterEvents as jest.Mock).mockReturnValue(registerEvents);
220
+
221
+ const graph = createGraph();
222
+ startHook({graph, onNodeClick});
223
+
224
+ const clickNode = registerEvents.mock.calls[registerEvents.mock.calls.length - 1][0].clickNode;
225
+
226
+ act(() => {
227
+ clickNode({node: 'node2'});
228
+ });
229
+
230
+ expect(onNodeClick).toHaveBeenCalledWith('node2');
231
+ });
232
+
233
+ describe('onCollapseNode behavior', () => {
234
+ it('should call onNodeCollapse and onNodeClick if click on collapse button', () => {
235
+ const registerEvents = jest.fn();
236
+ const onNodeCollapse = jest.fn();
237
+ const onNodeClick = jest.fn();
238
+ (useRegisterEvents as jest.Mock).mockReturnValue(registerEvents);
239
+
240
+ const graph = createGraph();
241
+
242
+ (useSigma as jest.Mock).mockReturnValue({
243
+ getCamera: () => ({
244
+ setState: () => {}
245
+ }),
246
+ getGraph: () => graph,
247
+ viewportToGraph: jest.fn().mockReturnValue({x: 5, y: 10}),
248
+ framedGraphToViewport: jest.fn().mockReturnValue({x: 80, y: 140}),
249
+ getNodeDisplayData: jest.fn().mockReturnValue({
250
+ x: 4,
251
+ y: 7,
252
+ buttonOffset: [19, 19],
253
+ buttonSize: 8,
254
+ showCollapse: true
255
+ }),
256
+ scaleSize: jest.fn().mockImplementation((value) => value)
257
+ });
258
+
259
+ startHook({graph, onNodeCollapse, onNodeClick});
260
+
261
+ const click = registerEvents.mock.calls[registerEvents.mock.calls.length - 1][0].click;
262
+
263
+ act(() => {
264
+ click({x: 100, y: 160});
265
+ });
266
+
267
+ expect(onNodeCollapse).toHaveBeenCalled();
268
+ expect(onNodeClick).toHaveBeenCalled();
269
+ });
270
+
271
+ it('should not call onNodeCollapse and onNodeClick when click outside of collapse button', () => {
272
+ const registerEvents = jest.fn();
273
+ const onNodeCollapse = jest.fn();
274
+ const onNodeClick = jest.fn();
275
+ (useRegisterEvents as jest.Mock).mockReturnValue(registerEvents);
276
+
277
+ const graph = createGraph();
278
+
279
+ (useSigma as jest.Mock).mockReturnValue({
280
+ getCamera: () => ({
281
+ setState: () => {}
282
+ }),
283
+ getGraph: () => graph,
284
+ viewportToGraph: jest.fn().mockReturnValue({x: 5, y: 10}),
285
+ framedGraphToViewport: jest.fn().mockReturnValue({x: 80, y: 140}),
286
+ getNodeDisplayData: jest.fn().mockReturnValue({
287
+ x: 4,
288
+ y: 7,
289
+ buttonOffset: [19, 19],
290
+ buttonSize: 8,
291
+ showCollapse: true
292
+ }),
293
+ scaleSize: jest.fn().mockImplementation((value) => value)
294
+ });
295
+
296
+ startHook({graph, onNodeCollapse, onNodeClick});
297
+
298
+ const click = registerEvents.mock.calls[registerEvents.mock.calls.length - 1][0].click;
299
+
300
+ act(() => {
301
+ click({x: 130, y: 200});
302
+ });
303
+
304
+ expect(onNodeCollapse).not.toHaveBeenCalled();
305
+ expect(onNodeClick).not.toHaveBeenCalled();
306
+ });
307
+
308
+ it('should not call onNodeCollapse and onNodeClick if showCollapse is false', () => {
309
+ const registerEvents = jest.fn();
310
+ const onNodeCollapse = jest.fn();
311
+ const onNodeClick = jest.fn();
312
+ (useRegisterEvents as jest.Mock).mockReturnValue(registerEvents);
313
+
314
+ const graph = createGraph();
315
+
316
+ (useSigma as jest.Mock).mockReturnValue({
317
+ getCamera: () => ({
318
+ setState: () => {}
319
+ }),
320
+ getGraph: () => graph,
321
+ viewportToGraph: jest.fn().mockReturnValue({x: 5, y: 10}),
322
+ framedGraphToViewport: jest.fn().mockReturnValue({x: 80, y: 140}),
323
+ getNodeDisplayData: jest.fn().mockReturnValue({
324
+ x: 4,
325
+ y: 7,
326
+ buttonOffset: [19, 19],
327
+ buttonSize: 8,
328
+ showCollapse: false
329
+ }),
330
+ scaleSize: jest.fn().mockImplementation((value) => value)
331
+ });
332
+
333
+ startHook({graph, onNodeCollapse, onNodeClick});
334
+
335
+ const click = registerEvents.mock.calls[registerEvents.mock.calls.length - 1][0].click;
336
+
337
+ act(() => {
338
+ click({x: 100, y: 160});
339
+ });
340
+
341
+ expect(onNodeCollapse).not.toHaveBeenCalled();
342
+ expect(onNodeClick).not.toHaveBeenCalled();
343
+ });
344
+ });
345
+
346
+ describe('onExpandeNode behavior', () => {
347
+ it('should call onNodeExpand and onNodeClick if click on expand button', () => {
348
+ const registerEvents = jest.fn();
349
+ const onNodeExpand = jest.fn();
350
+ const onNodeClick = jest.fn();
351
+ (useRegisterEvents as jest.Mock).mockReturnValue(registerEvents);
352
+
353
+ const graph = createGraph();
354
+
355
+ (useSigma as jest.Mock).mockReturnValue({
356
+ getCamera: () => ({
357
+ setState: () => {}
358
+ }),
359
+ getGraph: () => graph,
360
+ viewportToGraph: jest.fn().mockReturnValue({x: 5, y: 10}),
361
+ framedGraphToViewport: jest.fn().mockReturnValue({x: 80, y: 140}),
362
+ getNodeDisplayData: jest.fn().mockReturnValue({
363
+ x: 4,
364
+ y: 7,
365
+ buttonOffset: [19, 19],
366
+ buttonSize: 8,
367
+ showExpand: true
368
+ }),
369
+ scaleSize: jest.fn().mockImplementation((value) => value)
370
+ });
371
+
372
+ startHook({graph, onNodeExpand, onNodeClick});
373
+
374
+ const click = registerEvents.mock.calls[registerEvents.mock.calls.length - 1][0].click;
375
+
376
+ act(() => {
377
+ click({x: 100, y: 120});
378
+ });
379
+
380
+ expect(onNodeExpand).toHaveBeenCalled();
381
+ expect(onNodeClick).toHaveBeenCalled();
382
+ });
383
+
384
+ it('should not call onNodeExpand and onNodeClick when click outside of expand button', () => {
385
+ const registerEvents = jest.fn();
386
+ const onNodeExpand = jest.fn();
387
+ const onNodeClick = jest.fn();
388
+ (useRegisterEvents as jest.Mock).mockReturnValue(registerEvents);
389
+
390
+ const graph = createGraph();
391
+
392
+ (useSigma as jest.Mock).mockReturnValue({
393
+ getCamera: () => ({
394
+ setState: () => {}
395
+ }),
396
+ getGraph: () => graph,
397
+ viewportToGraph: jest.fn().mockReturnValue({x: 5, y: 10}),
398
+ framedGraphToViewport: jest.fn().mockReturnValue({x: 80, y: 140}),
399
+ getNodeDisplayData: jest.fn().mockReturnValue({
400
+ x: 4,
401
+ y: 7,
402
+ buttonOffset: [19, 19],
403
+ buttonSize: 8,
404
+ showExpand: true
405
+ }),
406
+ scaleSize: jest.fn().mockImplementation((value) => value)
407
+ });
408
+
409
+ startHook({graph, onNodeExpand, onNodeClick});
410
+
411
+ const click = registerEvents.mock.calls[registerEvents.mock.calls.length - 1][0].click;
412
+
413
+ act(() => {
414
+ click({x: 130, y: 200});
415
+ });
416
+
417
+ expect(onNodeExpand).not.toHaveBeenCalled();
418
+ expect(onNodeClick).not.toHaveBeenCalled();
419
+ });
420
+
421
+ it('should not call onNodeExpand and onNodeClick if showExpand is false', () => {
422
+ const registerEvents = jest.fn();
423
+ const onNodeExpand = jest.fn();
424
+ const onNodeClick = jest.fn();
425
+ (useRegisterEvents as jest.Mock).mockReturnValue(registerEvents);
426
+
427
+ const graph = createGraph();
428
+
429
+ (useSigma as jest.Mock).mockReturnValue({
430
+ getCamera: () => ({
431
+ setState: () => {}
432
+ }),
433
+ getGraph: () => graph,
434
+ viewportToGraph: jest.fn().mockReturnValue({x: 5, y: 10}),
435
+ framedGraphToViewport: jest.fn().mockReturnValue({x: 80, y: 140}),
436
+ getNodeDisplayData: jest.fn().mockReturnValue({
437
+ x: 4,
438
+ y: 7,
439
+ buttonOffset: [19, 19],
440
+ buttonSize: 8,
441
+ showExpand: false
442
+ }),
443
+ scaleSize: jest.fn().mockImplementation((value) => value)
444
+ });
445
+
446
+ startHook({graph, onNodeExpand, onNodeClick});
447
+
448
+ const click = registerEvents.mock.calls[registerEvents.mock.calls.length - 1][0].click;
449
+
450
+ act(() => {
451
+ click({x: 100, y: 120});
452
+ });
453
+
454
+ expect(onNodeExpand).not.toHaveBeenCalled();
455
+ expect(onNodeClick).not.toHaveBeenCalled();
456
+ });
457
+ });
458
+
459
+ describe('check sync and worker layout working', () => {
460
+ it('sync layout', () => {
461
+ const graph = createGraph();
462
+ const layoutSettings = {};
463
+ const positions = {
464
+ node1: {x: 5, y: 10},
465
+ node2: {x: 6, y: 11},
466
+ node3: {x: 7, y: 12},
467
+ node4: {x: 8, y: 13}
468
+ };
469
+ const loadGraph = jest.fn();
470
+
471
+ (useLoadGraph as jest.Mock).mockReturnValue(loadGraph);
472
+ const syncLayout = jest.fn().mockReturnValue(positions);
473
+
474
+ startHook({graph, layoutSettings, syncLayout});
475
+
476
+ expect(loadGraph).toHaveBeenCalledWith(graph);
477
+ expect(syncLayout).toHaveBeenCalledWith(graph, layoutSettings);
478
+ expect(animateNodes).toHaveBeenCalledWith(graph, positions, {duration: 1000});
479
+ });
480
+
481
+ it('worker layout', () => {
482
+ jest.useFakeTimers();
483
+ const graph = createGraph();
484
+ const loadGraph = jest.fn();
485
+ const layoutSettings = {};
486
+
487
+ (useLoadGraph as jest.Mock).mockReturnValue(loadGraph);
488
+
489
+ const stopWorker = jest.fn();
490
+ const startWorker = jest.fn();
491
+ const killWorker = jest.fn();
492
+ const workerLayout = jest.fn().mockReturnValue({
493
+ stop: stopWorker,
494
+ start: startWorker,
495
+ kill: killWorker
496
+ });
497
+
498
+ const {unmount} = startHook({graph, layoutSettings, workerLayout});
499
+
500
+ expect(loadGraph).toHaveBeenCalledWith(graph);
501
+ expect(workerLayout).toHaveBeenCalledWith(graph, layoutSettings);
502
+ expect(startWorker).toHaveBeenCalled();
503
+ expect(stopWorker).not.toHaveBeenCalled();
504
+ jest.runAllTimers();
505
+ expect(stopWorker).toHaveBeenCalled();
506
+ jest.useRealTimers();
507
+ unmount();
508
+ expect(killWorker).toHaveBeenCalled();
509
+ });
510
+ });
511
+
512
+ describe('node reducer data', () => {
513
+ const setSettings = jest.fn();
514
+ const registerEvents = jest.fn();
515
+ beforeAll(() => {
516
+ (useSetSettings as jest.Mock).mockReturnValue(setSettings);
517
+ (useRegisterEvents as jest.Mock).mockReturnValue(registerEvents);
518
+ });
519
+
520
+ const getNodeReducer = () => setSettings.mock.calls[setSettings.mock.calls.length - 1][0].nodeReducer;
521
+ const getEnterNode = () => registerEvents.mock.calls[registerEvents.mock.calls.length - 1][0].enterNode;
522
+
523
+ it('should return correctly image path', () => {
524
+ const graph = createGraph();
525
+ startHook({graph});
526
+ const nodeReducer = getNodeReducer();
527
+ expect(nodeReducer('node1', graph.getNodeAttributes('node1')).image).toBe(
528
+ 'https://s3.amazonaws.com/reltio.images/api/images/defaultImage/typeImage_gender_neutral.png'
529
+ );
530
+ });
531
+
532
+ it('should return selected node props if node is selected node', () => {
533
+ const graph = createGraph();
534
+ startHook({graph, selectedNode: 'node2'});
535
+ const nodeReducer = getNodeReducer();
536
+ expect(nodeReducer('node2', graph.getNodeAttributes('node2'))).toMatchObject({
537
+ size: 24,
538
+ buttonSize: 8,
539
+ buttonOffset: [19, 19]
540
+ });
541
+ });
542
+
543
+ it('should not fail if selected node is not in graph', () => {
544
+ const graph = createGraph();
545
+ const {result} = startHook({graph, selectedNode: 'non existing node'});
546
+ expect(result.error).toBeUndefined();
547
+ });
548
+
549
+ it('should return default node props if node is not selected node', () => {
550
+ const graph = createGraph();
551
+ startHook({graph, selectedNode: 'node2'});
552
+ const nodeReducer = getNodeReducer();
553
+ expect(nodeReducer('node1', graph.getNodeAttributes('node1'))).toMatchObject({
554
+ size: 12,
555
+ buttonSize: 6,
556
+ buttonOffset: [8, 13]
557
+ });
558
+ });
559
+
560
+ it('should return correctly inPath prop if node is selected node', () => {
561
+ const graph = createGraph();
562
+ startHook({graph, selectedNode: 'node1'});
563
+ const nodeReducer = getNodeReducer();
564
+ expect(nodeReducer('node1', graph.getNodeAttributes('node1')).inPath).toBe(true);
565
+ });
566
+
567
+ it('should return correctly inPath prop if node in path', async () => {
568
+ const graph = createGraph();
569
+
570
+ (useSigma as jest.Mock).mockReturnValue({
571
+ getCamera: () => ({
572
+ setState: () => {}
573
+ }),
574
+ getGraph: () => graph
575
+ });
576
+
577
+ const {rerender} = startHook({graph, selectedNode: 'node3'});
578
+ rerender();
579
+ const nodeReducer = getNodeReducer();
580
+ expect(nodeReducer('node2', graph.getNodeAttributes('node2')).inPath).toBe(true);
581
+ });
582
+
583
+ it('should return correct props for main node', async () => {
584
+ const graph = createGraph();
585
+
586
+ (useSigma as jest.Mock).mockReturnValue({
587
+ getCamera: () => ({
588
+ setState: () => {}
589
+ }),
590
+ getGraph: () => graph
591
+ });
592
+
593
+ const {rerender} = startHook({graph, selectedNode: 'node3'});
594
+ rerender();
595
+ const nodeReducer = getNodeReducer();
596
+ expect(nodeReducer(store.profile.entity.uri, graph.getNodeAttributes('node2'))).toMatchObject({
597
+ color: '#00FFFF',
598
+ inPath: true
599
+ });
600
+ });
601
+
602
+ it('should return correctly inactive prop if node is inactive', () => {
603
+ const graph = createGraph();
604
+ startHook({graph, selectedNode: 'node2'});
605
+ const nodeReducer = getNodeReducer();
606
+ expect(nodeReducer('node5', graph.getNodeAttributes('node5')).inactive).toBe(true);
607
+ });
608
+
609
+ it('should show empty label if node is inactive and node is not hovered', () => {
610
+ const graph = createGraph();
611
+ startHook({graph, selectedNode: 'node2'});
612
+ const nodeReducer = getNodeReducer();
613
+ expect(nodeReducer('node5', graph.getNodeAttributes('node5')).label).toBe('');
614
+ });
615
+
616
+ it('should highlight node if node is hovered', () => {
617
+ const graph = createGraph();
618
+
619
+ startHook({graph, selectedNode: 'node1'});
620
+ const enterNode = getEnterNode();
621
+ act(() => {
622
+ enterNode({node: 'node2'});
623
+ });
624
+ const nodeReducer = getNodeReducer();
625
+ expect(nodeReducer('node2', graph.getNodeAttributes('node2')).highlighted).toBe(true);
626
+ });
627
+
628
+ it('should set showExpand/showCollapse props', () => {
629
+ const graph = createGraph();
630
+ (useSigma as jest.Mock).mockReturnValue({
631
+ getCamera: () => ({
632
+ setState: () => {}
633
+ }),
634
+ getGraph: () => graph
635
+ });
636
+
637
+ startHook({graph, selectedNode: 'node2'});
638
+ const nodeReducer = getNodeReducer();
639
+ expect(nodeReducer('node1', graph.getNodeAttributes('node1'))).toMatchObject({
640
+ showCollapse: true,
641
+ showExpand: false
642
+ });
643
+ expect(nodeReducer('node2', graph.getNodeAttributes('node2'))).toMatchObject({
644
+ showCollapse: true,
645
+ showExpand: true
646
+ });
647
+ expect(nodeReducer('node3', graph.getNodeAttributes('node3'))).toMatchObject({
648
+ showCollapse: false,
649
+ showExpand: false
650
+ });
651
+ expect(nodeReducer('node4', graph.getNodeAttributes('node4'))).toMatchObject({
652
+ showCollapse: false,
653
+ showExpand: true
654
+ });
655
+ expect(nodeReducer('node5', graph.getNodeAttributes('node5'))).toMatchObject({
656
+ showCollapse: false,
657
+ showExpand: false
658
+ });
659
+ });
660
+ });
661
+
662
+ describe('edge reducer data', () => {
663
+ const setSettings = jest.fn();
664
+ const registerEvents = jest.fn();
665
+ beforeAll(() => {
666
+ (useSetSettings as jest.Mock).mockReturnValue(setSettings);
667
+ (useRegisterEvents as jest.Mock).mockReturnValue(registerEvents);
668
+ });
669
+
670
+ const getEdgeReducer = () => setSettings.mock.calls[setSettings.mock.calls.length - 1][0].edgeReducer;
671
+
672
+ it('should render correctly properties for basic case', () => {
673
+ const graph = createGraph();
674
+ startHook({graph, selectedNode: 'node1'});
675
+ const edgeReducer = getEdgeReducer();
676
+ expect(edgeReducer('r1', graph.getEdgeAttributes('r1'))).toMatchObject({
677
+ hidden: false,
678
+ size: 1,
679
+ color: 'rgba(0, 0, 0, 0.2)'
680
+ });
681
+ });
682
+
683
+ it('should set hovered correctly for edge', () => {
684
+ (useTooltipRenderer as jest.Mock).mockReturnValue('r1');
685
+
686
+ const graph = createGraph();
687
+ startHook({graph});
688
+
689
+ const edgeReducer = getEdgeReducer();
690
+
691
+ expect(edgeReducer('r1', graph.getEdgeAttributes('r1')).hovered).toBe(true);
692
+ expect(edgeReducer('r2', graph.getEdgeAttributes('r2')).hovered).toBe(undefined);
693
+ });
694
+
695
+ it('should not apply correctly arrowSizeRatio if graph is not directed', () => {
696
+ const graph = createGraph();
697
+ startHook({graph, selectedNode: 'node1', isDirected: false});
698
+ const edgeReducer = getEdgeReducer();
699
+ expect(edgeReducer('r1', graph.getEdgeAttributes('r1')).arrowSizeRatio).toBeUndefined();
700
+ });
701
+
702
+ it('should apply correctly arrowSizeRatio if graph is directed', () => {
703
+ const graph = createGraph();
704
+ startHook({graph, selectedNode: 'node1', isDirected: true});
705
+ const edgeReducer = getEdgeReducer();
706
+ expect(edgeReducer('r1', graph.getEdgeAttributes('r1')).arrowSizeRatio).toBe(4);
707
+ });
708
+
709
+ it('should hide edge if has selected node that not equals rootNode and has not extremity and edge not in path', () => {
710
+ const graph = createGraph();
711
+ const {rerender} = startHook({graph, selectedNode: 'node4', isDirected: true});
712
+ rerender();
713
+ const edgeReducer = getEdgeReducer();
714
+ expect(edgeReducer('r2', graph.getEdgeAttributes('r2')).hidden).toBe(true);
715
+ });
716
+
717
+ it('should highlight and increase size of edge if edge in path', () => {
718
+ const graph = createGraph();
719
+ const {rerender} = startHook({graph, selectedNode: 'node2', isDirected: true});
720
+ rerender();
721
+ const edgeReducer = getEdgeReducer();
722
+ expect(edgeReducer('r1', graph.getEdgeAttributes('r1'))).toMatchObject({
723
+ color: '#0072CE',
724
+ size: 4,
725
+ arrowSizeRatio: 1.4
726
+ });
727
+ });
728
+
729
+ it('should set edge type as "arrow" for directed relation if isDirected is true', () => {
730
+ const graph = createGraph();
731
+ const {rerender} = startHook({graph, selectedNode: 'node4', isDirected: true});
732
+ rerender();
733
+ const edgeReducer = getEdgeReducer();
734
+ expect(edgeReducer('r2', graph.getEdgeAttributes('r2')).type).toBe('arrow');
735
+ });
736
+
737
+ it('should set edge type as "line" for directed relation if isDirected is false', () => {
738
+ const graph = createGraph();
739
+ const {rerender} = startHook({graph, selectedNode: 'node4', isDirected: false});
740
+ rerender();
741
+ const edgeReducer = getEdgeReducer();
742
+ expect(edgeReducer('r2', graph.getEdgeAttributes('r2')).type).toBe('line');
743
+ });
744
+
745
+ it('should set edge type as "line" for undirected relation if isDirected is true', () => {
746
+ const graph = createGraph();
747
+ const {rerender} = startHook({graph, selectedNode: 'node4', isDirected: true});
748
+ rerender();
749
+ const edgeReducer = getEdgeReducer();
750
+ expect(edgeReducer('r2', graph.getEdgeAttributes('r3')).type).toBe('line');
751
+ });
752
+
753
+ it('should set edge type as "line" for undirected relation if isDirected is false', () => {
754
+ const graph = createGraph();
755
+ const {rerender} = startHook({graph, selectedNode: 'node4', isDirected: false});
756
+ rerender();
757
+ const edgeReducer = getEdgeReducer();
758
+ expect(edgeReducer('r2', graph.getEdgeAttributes('r3')).type).toBe('line');
759
+ });
760
+
761
+ it('should set edge type as "doubleArrow" for bidirectional relation if isDirected is true', () => {
762
+ const graph = createGraph();
763
+ const {rerender} = startHook({graph, selectedNode: 'node4', isDirected: true});
764
+ rerender();
765
+ const edgeReducer = getEdgeReducer();
766
+ expect(edgeReducer('r4', graph.getEdgeAttributes('r4')).type).toBe('doubleArrow');
767
+ });
768
+
769
+ it('should set edge type as "line" for bidirectional relation if isDirected is false', () => {
770
+ const graph = createGraph();
771
+ const {rerender} = startHook({graph, selectedNode: 'node4', isDirected: false});
772
+ rerender();
773
+ const edgeReducer = getEdgeReducer();
774
+ expect(edgeReducer('r4', graph.getEdgeAttributes('r4')).type).toBe('line');
775
+ });
776
+
777
+ it('should use edge color from relationType if relationType has typeColor', () => {
778
+ const graph = createGraph();
779
+ const {rerender} = startHook({graph});
780
+ rerender();
781
+ const edgeReducer = getEdgeReducer();
782
+ expect(edgeReducer('r2', graph.getEdgeAttributes('r2')).color).toBe('#663344');
783
+ });
784
+
785
+ it('should use edge color from inherited relationType if relationType has not typeColor', () => {
786
+ const graph = createGraph();
787
+ const {rerender} = startHook({graph});
788
+ rerender();
789
+ const edgeReducer = getEdgeReducer();
790
+ expect(edgeReducer('r4', graph.getEdgeAttributes('r4')).color).toBe('#663344');
791
+ });
792
+
793
+ it('should use default color if relationType has not typeColor', () => {
794
+ const graph = createGraph();
795
+ const {rerender} = startHook({graph});
796
+ rerender();
797
+ const edgeReducer = getEdgeReducer();
798
+ expect(edgeReducer('r3', graph.getEdgeAttributes('r3')).color).toBe('rgba(0, 0, 0, 0.2)');
799
+ });
800
+
801
+ it('should use default color if relationType not found in metadata', () => {
802
+ const graph = createGraph();
803
+ const {rerender} = startHook({graph});
804
+ rerender();
805
+ const edgeReducer = getEdgeReducer();
806
+ expect(edgeReducer('r1', graph.getEdgeAttributes('r1')).color).toBe('rgba(0, 0, 0, 0.2)');
807
+ });
808
+
809
+ it('should set edge label from relationType if relationType has label', () => {
810
+ const graph = createGraph();
811
+ const {rerender} = startHook({graph});
812
+ rerender();
813
+ const edgeReducer = getEdgeReducer();
814
+ expect(edgeReducer('r2', graph.getEdgeAttributes('r2')).relationLabel).toBe('Affilation');
815
+ });
816
+
817
+ it('should set default edge label if relationType has not label', () => {
818
+ const graph = createGraph();
819
+ const {rerender} = startHook({graph});
820
+ rerender();
821
+ const edgeReducer = getEdgeReducer();
822
+ expect(edgeReducer('r3', graph.getEdgeAttributes('r3')).relationLabel).toBe('<No label>');
823
+ });
824
+ });
825
+ });