@reltio/graph 1.4.1585 → 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,121 @@
1
+ import React, {useState, useCallback, useEffect} from 'react';
2
+ import {useDispatch, useSelector} from 'react-redux';
3
+ import i18n from 'ui-i18n';
4
+
5
+ import Typography from '@mui/material/Typography';
6
+ import Button from '@mui/material/Button';
7
+ import Dialog from '@mui/material/Dialog';
8
+ import DialogActions from '@mui/material/DialogActions';
9
+ import DialogContent from '@mui/material/DialogContent';
10
+ import DialogTitle from '@mui/material/DialogTitle';
11
+ import {ConnectionEditor, useEditableConnection, withContext} from '@reltio/components';
12
+ import mdmModule, {profile} from '@reltio/mdm-module';
13
+ import {Directions, EditingConnection, Entity, validateConnectionRelation} from '@reltio/mdm-sdk';
14
+ import {GraphStateContext} from '../../../../contexts/GraphStateContext';
15
+ import {GraphState} from '../../../../types/graphDataTypes';
16
+ import {createNewConnection} from '../../../../helpers/relationsHelpers';
17
+ import {useCreatableInOutRelationTypes} from './useCreatableInOutRelationTypes';
18
+
19
+ import {useStyles} from './styles';
20
+
21
+ type Props = {
22
+ selectedEntity?: Entity;
23
+ open: boolean;
24
+ onClose: () => void;
25
+ onAddRelation?: GraphState['onAddRelation'];
26
+ };
27
+
28
+ const GraphAddRelationDialog = ({selectedEntity, open, onClose, onAddRelation}: Props) => {
29
+ const styles = useStyles();
30
+ const metadata = useSelector(mdmModule.selectors.getMetadata);
31
+
32
+ const [connection, setConnection] = useState<EditingConnection>({});
33
+ const {relation, entity} = connection;
34
+ const {type: relationTypeUri, direction} = relation || {};
35
+
36
+ const [loading, setLoading] = useState(false);
37
+ const dispatch = useDispatch();
38
+
39
+ useEffect(() => {
40
+ if (open) {
41
+ setConnection(createNewConnection());
42
+ }
43
+ }, [open]);
44
+
45
+ const {inRelationTypes, outRelationTypes} = useCreatableInOutRelationTypes(selectedEntity?.type);
46
+
47
+ const validate = useCallback(() => {
48
+ const errors = validateConnectionRelation(metadata, {relation: {object: relation}});
49
+ dispatch(profile.errors.actions.errorsSet(errors));
50
+ return errors.length === 0;
51
+ }, [relation, metadata, dispatch]);
52
+
53
+ const handleAdd = () => {
54
+ const {type: relationType, attributes = {}, startDate, endDate} = relation;
55
+ const startEntityUri = direction === Directions.OUT ? selectedEntity.uri : entity.uri;
56
+ const endEntityUri = direction === Directions.OUT ? entity.uri : selectedEntity.uri;
57
+
58
+ if (validate()) {
59
+ setLoading(true);
60
+ onAddRelation({
61
+ startEntityUri,
62
+ endEntityUri,
63
+ relationType,
64
+ attributes,
65
+ startDate,
66
+ endDate
67
+ })
68
+ .then(() => {
69
+ onClose();
70
+ })
71
+ .catch((e) => {
72
+ console.error(e);
73
+ })
74
+ .finally(() => {
75
+ setLoading(false);
76
+ });
77
+ }
78
+ };
79
+
80
+ const {onAddAttributes, onChangeAttribute, onRemoveAttribute, onChangeEntity, onChangeRelationType} =
81
+ useEditableConnection({connection, onConnectionChange: setConnection, relatedEntity: selectedEntity});
82
+
83
+ return (
84
+ <Dialog open={open} fullWidth maxWidth="sm" onClose={onClose} classes={{paper: styles.dialogPaper}}>
85
+ <DialogTitle classes={{root: styles.dialogTitle}}>
86
+ <Typography variant="subtitle1" classes={{root: styles.titleText}}>
87
+ {i18n.text('Add relationship')}
88
+ </Typography>
89
+ </DialogTitle>
90
+ <DialogContent classes={{root: styles.content}}>
91
+ <ConnectionEditor
92
+ connection={connection}
93
+ onAddAttributes={onAddAttributes}
94
+ onChangeAttribute={onChangeAttribute}
95
+ onRemoveAttribute={onRemoveAttribute}
96
+ relatedEntity={selectedEntity}
97
+ inRelationTypes={inRelationTypes}
98
+ outRelationTypes={outRelationTypes}
99
+ onChangeRelationType={onChangeRelationType}
100
+ onChangeEntity={onChangeEntity}
101
+ />
102
+ </DialogContent>
103
+ <DialogActions>
104
+ <Button onClick={onClose}>{i18n.text('Cancel')}</Button>
105
+ <Button
106
+ disabled={!connection?.entity || !relationTypeUri || loading}
107
+ color="primary"
108
+ onClick={handleAdd}
109
+ >
110
+ {i18n.text('Add')}
111
+ </Button>
112
+ </DialogActions>
113
+ </Dialog>
114
+ );
115
+ };
116
+
117
+ export default withContext(
118
+ GraphStateContext,
119
+ ({selectedEntity, onAddRelation}) => ({selectedEntity, onAddRelation}),
120
+ GraphAddRelationDialog
121
+ );
@@ -0,0 +1,80 @@
1
+ import React from 'react';
2
+ import {mount} from 'enzyme';
3
+ import {assoc, clone} from 'ramda';
4
+ import * as reactRedux from 'react-redux';
5
+ import AddIcon from '@mui/icons-material/Add';
6
+ import {ActionButton, ActionButtonMode} from '@reltio/components';
7
+
8
+ import GraphAddRelationDialog from '../GraphAddRelationDialog';
9
+ import GraphAddRelationButton from '../GraphAddRelationButton';
10
+ import {Entity} from '@reltio/mdm-sdk';
11
+ import {GraphState} from '../../../../../types/graphDataTypes';
12
+ import {GraphStateContext} from '../../../../../contexts/GraphStateContext';
13
+ import {store as mockStore, tenant} from './mockdata';
14
+
15
+ jest.mock('../GraphAddRelationDialog', () => () => null);
16
+
17
+ const setUp = ({selectedEntity}: {selectedEntity: Entity}) =>
18
+ mount(
19
+ <GraphStateContext.Provider value={{selectedEntity} as GraphState}>
20
+ <GraphAddRelationButton />
21
+ </GraphStateContext.Provider>
22
+ );
23
+
24
+ describe('GraphAddRelationButton tests', () => {
25
+ let store;
26
+ beforeEach(() => {
27
+ store = mockStore;
28
+ });
29
+ beforeAll(() => {
30
+ jest.spyOn(reactRedux, 'useSelector').mockImplementation((selector) => selector(store));
31
+ });
32
+
33
+ const selectedEntity = {
34
+ uri: 'entities/uri1',
35
+ type: 'configuration/entityTypes/HCP'
36
+ };
37
+
38
+ it('should render nothing if there are no available relation types', () => {
39
+ const component = setUp({
40
+ selectedEntity: {
41
+ uri: 'entities/uri1',
42
+ type: 'configuration/entityTypes/HCA'
43
+ }
44
+ });
45
+ expect(component.find(ActionButton)).toHaveLength(0);
46
+ expect(component.find(GraphAddRelationDialog)).toHaveLength(0);
47
+ });
48
+
49
+ it('should render nothing if there are no creatable relation types', () => {
50
+ store = clone(mockStore);
51
+ store.metadata[tenant].relationTypes = store.metadata[tenant].relationTypes.map(
52
+ assoc('access', ['READ', 'UPDATE'])
53
+ );
54
+ const component = setUp({selectedEntity});
55
+ expect(component.find(ActionButton)).toHaveLength(0);
56
+ expect(component.find(GraphAddRelationDialog)).toHaveLength(0);
57
+ });
58
+
59
+ it('should render ActionButton and GraphAddRelationDialog', () => {
60
+ const component = setUp({selectedEntity});
61
+ expect(component.find(ActionButton)).toHaveLength(1);
62
+ expect(component.find(ActionButton).prop('mode')).toBe(ActionButtonMode.button);
63
+ expect(component.find(ActionButton).prop('icon')).toEqual(AddIcon);
64
+ expect(component.find(ActionButton).prop('label')).toBe('Add relationship');
65
+ expect(component.find(GraphAddRelationDialog)).toHaveLength(1);
66
+ expect(component.find(GraphAddRelationDialog).prop('open')).toBe(false);
67
+ });
68
+
69
+ it('should switch dialog to open and to close state', () => {
70
+ const component = setUp({selectedEntity});
71
+ expect(component.find(ActionButton)).toHaveLength(1);
72
+ expect(component.find(GraphAddRelationDialog)).toHaveLength(1);
73
+ expect(component.find(GraphAddRelationDialog).prop('open')).toBe(false);
74
+ component.find(ActionButton).simulate('click');
75
+ expect(component.find(GraphAddRelationDialog).prop('open')).toBe(true);
76
+ component.find(GraphAddRelationDialog).prop('onClose')();
77
+ component.update();
78
+ expect(component.find(GraphAddRelationDialog).prop('open')).toBe(false);
79
+ });
80
+ });
@@ -0,0 +1,262 @@
1
+ import React from 'react';
2
+ import {mount} from 'enzyme';
3
+ import {act} from 'react-dom/test-utils';
4
+ import * as reactRedux from 'react-redux';
5
+ import {profile} from '@reltio/mdm-module';
6
+
7
+ import DialogActions from '@mui/material/DialogActions';
8
+ import Button from '@mui/material/Button';
9
+ import Typography from '@mui/material/Typography';
10
+ import GraphAddRelationDialog from '../GraphAddRelationDialog';
11
+
12
+ import {validateConnectionRelation, Entity, AttributeError} from '@reltio/mdm-sdk';
13
+ import {GraphStateContext} from '../../../../../contexts/GraphStateContext';
14
+ import {GraphState} from '../../../../../../src/types/graphDataTypes';
15
+ import {ConnectionEditor} from '@reltio/components';
16
+ import {store, inRelationTypes, outRelationTypes} from './mockdata';
17
+
18
+ const findButtonByLabel = (component, label) => component.find(Button).filterWhere((button) => button.text() === label);
19
+
20
+ const findCancelButton = (component) => findButtonByLabel(component, 'Cancel');
21
+ const findAddButton = (component) => findButtonByLabel(component, 'Add');
22
+
23
+ const newRelationUri = 'relations/uri$$123';
24
+
25
+ jest.mock('@reltio/mdm-sdk', () => ({
26
+ ...jest.requireActual<Record<string, unknown>>('@reltio/mdm-sdk'),
27
+ createRelation: jest.fn(),
28
+ validateConnectionRelation: jest.fn().mockReturnValue([]),
29
+ generateNewRelationUri: jest.fn().mockReturnValue(newRelationUri)
30
+ }));
31
+
32
+ jest.mock('@reltio/components', () => ({
33
+ ...jest.requireActual<Record<string, unknown>>('@reltio/components'),
34
+ ConnectionEditor: () => null
35
+ }));
36
+
37
+ const setUp = ({
38
+ selectedEntity,
39
+ handleClose,
40
+ onAddRelation
41
+ }: {
42
+ selectedEntity: Entity;
43
+ handleClose: () => void;
44
+ onAddRelation?: GraphState['onAddRelation'];
45
+ }) =>
46
+ mount(
47
+ <GraphStateContext.Provider value={{selectedEntity, onAddRelation} as GraphState}>
48
+ <GraphAddRelationDialog open={true} onClose={handleClose} />
49
+ </GraphStateContext.Provider>
50
+ );
51
+
52
+ describe('GraphAddRelationDialog tests', () => {
53
+ const handleClose = jest.fn();
54
+ const selectedEntity = {
55
+ uri: 'entities/uri1',
56
+ type: 'configuration/entityTypes/HCP'
57
+ };
58
+ const dispatch = jest.fn();
59
+
60
+ beforeAll(() => {
61
+ jest.spyOn(reactRedux, 'useSelector').mockImplementation((selector) => selector(store));
62
+ jest.spyOn(reactRedux, 'useDispatch').mockReturnValue(dispatch);
63
+ });
64
+
65
+ afterEach(() => {
66
+ jest.clearAllMocks();
67
+ });
68
+
69
+ it('should render GraphAddRelationDialog for selectedEntity from context', () => {
70
+ const component = setUp({selectedEntity, handleClose});
71
+ expect(component.find(Typography).text()).toBe('Add relationship');
72
+
73
+ const dialogActions = component.find(DialogActions);
74
+ expect(dialogActions).toHaveLength(1);
75
+ expect(dialogActions.find(Button)).toHaveLength(2);
76
+
77
+ expect(findCancelButton(component)).toHaveLength(1);
78
+ expect(findAddButton(component)).toHaveLength(1);
79
+ });
80
+
81
+ it('should render ConnectionEditor with definite params', () => {
82
+ const component = setUp({selectedEntity, handleClose});
83
+ expect(component.find(ConnectionEditor).props()).toMatchObject({
84
+ inRelationTypes: inRelationTypes,
85
+ outRelationTypes: outRelationTypes,
86
+ relatedEntity: selectedEntity,
87
+ connection: {relation: {uri: newRelationUri}}
88
+ });
89
+ });
90
+
91
+ it('should call onClose after click on Close button', () => {
92
+ const component = setUp({selectedEntity, handleClose});
93
+ const cancelButton = findCancelButton(component);
94
+ cancelButton.simulate('click');
95
+ expect(handleClose).toHaveBeenCalled();
96
+ });
97
+
98
+ it('should call onAddRelation and onClose after click on Add button', async () => {
99
+ const onAddRelation = jest.fn().mockImplementation(() => Promise.resolve());
100
+ const component = setUp({selectedEntity, handleClose, onAddRelation});
101
+
102
+ act(() =>
103
+ component.find(ConnectionEditor).prop('onChangeRelationType')({
104
+ type: 'configuration/relationTypes/IsInCommittee',
105
+ direction: 'IN'
106
+ })
107
+ );
108
+ component.update();
109
+ act(() =>
110
+ component.find(ConnectionEditor).prop('onChangeEntity')({
111
+ uri: 'entities/entityCommittee',
112
+ type: 'configuration/entityTypes/Committee'
113
+ })
114
+ );
115
+ component.update();
116
+ const addButton = findAddButton(component);
117
+ await act(async () => {
118
+ addButton.simulate('click');
119
+ });
120
+ component.update();
121
+ expect(dispatch).toHaveBeenCalledWith(profile.errors.actions.errorsSet([]));
122
+ expect(onAddRelation).toHaveBeenCalledWith({
123
+ attributes: {},
124
+ endDate: undefined,
125
+ endEntityUri: 'entities/uri1',
126
+ relationType: 'configuration/relationTypes/IsInCommittee',
127
+ startDate: undefined,
128
+ startEntityUri: 'entities/entityCommittee'
129
+ });
130
+ expect(handleClose).toHaveBeenCalled();
131
+ });
132
+
133
+ it('should disable Add button if relationType is empty', () => {
134
+ const onAddRelation = jest.fn().mockImplementation(() => Promise.resolve());
135
+ const component = setUp({selectedEntity, handleClose, onAddRelation});
136
+ expect(findAddButton(component).prop('disabled')).toBe(true);
137
+
138
+ act(() =>
139
+ component.find(ConnectionEditor).prop('onChangeRelationType')({
140
+ type: 'configuration/relationTypes/IsInCommittee',
141
+ direction: 'IN'
142
+ })
143
+ );
144
+ component.update();
145
+ expect(findAddButton(component).prop('disabled')).toBe(true);
146
+ act(() =>
147
+ component.find(ConnectionEditor).prop('onChangeEntity')({
148
+ uri: 'entities/entityCommittee',
149
+ type: 'configuration/entityTypes/Committee'
150
+ })
151
+ );
152
+ component.update();
153
+ expect(findAddButton(component).prop('disabled')).toBe(false);
154
+
155
+ act(() =>
156
+ component.find(ConnectionEditor).prop('onChangeRelationType')({type: undefined, direction: undefined})
157
+ );
158
+ component.update();
159
+ expect(findAddButton(component).prop('disabled')).toBe(true);
160
+ });
161
+
162
+ it('should disable Add button if entity is empty', () => {
163
+ const onAddRelation = jest.fn().mockImplementation(() => Promise.resolve());
164
+ const component = setUp({selectedEntity, handleClose, onAddRelation});
165
+ expect(findAddButton(component).prop('disabled')).toBe(true);
166
+ act(() =>
167
+ component.find(ConnectionEditor).prop('onChangeRelationType')({
168
+ type: 'configuration/relationTypes/IsInCommittee',
169
+ direction: 'IN'
170
+ })
171
+ );
172
+ component.update();
173
+ expect(findAddButton(component).prop('disabled')).toBe(true);
174
+ act(() =>
175
+ component.find(ConnectionEditor).prop('onChangeEntity')({
176
+ uri: 'entities/entityCommittee',
177
+ type: 'configuration/entityTypes/Committee'
178
+ })
179
+ );
180
+ component.update();
181
+ expect(findAddButton(component).prop('disabled')).toBe(false);
182
+
183
+ act(() => component.find(ConnectionEditor).prop('onChangeEntity')(undefined));
184
+ component.update();
185
+ expect(findAddButton(component).prop('disabled')).toBe(true);
186
+ });
187
+
188
+ it('should fire error if onAddRelation is failed, form should stay open', async () => {
189
+ const onAddRelation = jest.fn().mockImplementation(() => Promise.reject('Bad relation'));
190
+
191
+ const component = setUp({selectedEntity, handleClose, onAddRelation});
192
+
193
+ act(() =>
194
+ component.find(ConnectionEditor).prop('onChangeRelationType')({
195
+ type: 'configuration/relationTypes/IsInCommittee',
196
+ direction: 'IN'
197
+ })
198
+ );
199
+ component.update();
200
+ act(() =>
201
+ component.find(ConnectionEditor).prop('onChangeEntity')({
202
+ uri: 'entities/entityCommittee',
203
+ type: 'configuration/entityTypes/Committee'
204
+ })
205
+ );
206
+ component.update();
207
+
208
+ await act(async () => {
209
+ findAddButton(component).simulate('click');
210
+ });
211
+ component.update();
212
+
213
+ expect(dispatch).toHaveBeenCalledWith(profile.errors.actions.errorsSet([]));
214
+ expect(handleClose).not.toHaveBeenCalled();
215
+ });
216
+
217
+ it('should not call onAddRelation and onClose on Add button click if relation has validation errors', async () => {
218
+ const onAddRelation = jest.fn().mockImplementation(() => Promise.resolve());
219
+ const errorsSet = [
220
+ {
221
+ type: 'missedRefEntity'
222
+ }
223
+ ] as AttributeError[];
224
+ (validateConnectionRelation as jest.Mock).mockReturnValue(errorsSet);
225
+ const component = setUp({selectedEntity, handleClose, onAddRelation});
226
+
227
+ // set connection
228
+ act(() =>
229
+ component.find(ConnectionEditor).prop('onChangeRelationType')({
230
+ type: 'configuration/relationTypes/IsInCommittee',
231
+ direction: ''
232
+ })
233
+ );
234
+ component.update();
235
+ act(() =>
236
+ component.find(ConnectionEditor).prop('onChangeEntity')({
237
+ uri: 'entities/entity',
238
+ type: 'configuration/entityTypes/C'
239
+ })
240
+ );
241
+ component.update();
242
+
243
+ await act(async () => {
244
+ findAddButton(component).simulate('click');
245
+ });
246
+
247
+ expect(validateConnectionRelation).toHaveBeenCalledWith(store.metadata.alenat, {
248
+ relation: {
249
+ object: {
250
+ uri: newRelationUri,
251
+ type: 'configuration/relationTypes/IsInCommittee',
252
+ direction: '',
253
+ attributes: {}
254
+ }
255
+ }
256
+ });
257
+
258
+ expect(dispatch).toHaveBeenCalledWith(profile.errors.actions.errorsSet(errorsSet));
259
+ expect(onAddRelation).not.toHaveBeenCalled();
260
+ expect(handleClose).not.toHaveBeenCalled();
261
+ });
262
+ });
@@ -0,0 +1,75 @@
1
+ const tenant = 'alenat';
2
+
3
+ const store = {
4
+ metadata: {
5
+ [tenant]: {
6
+ entityTypes: [
7
+ {
8
+ uri: 'configuration/entityTypes/HCP'
9
+ },
10
+ {
11
+ uri: 'configuration/entityTypes/HCO'
12
+ },
13
+ {
14
+ uri: 'configuration/entityTypes/Committee'
15
+ },
16
+ {
17
+ uri: 'configuration/entityTypes/Location'
18
+ }
19
+ ],
20
+ relationTypes: [
21
+ {
22
+ uri: 'configuration/relationTypes/HasAddress',
23
+ label: 'has address',
24
+ startObject: {
25
+ objectTypeURI: 'configuration/entityTypes/HCP',
26
+ uri: 'configuration/relationTypes/HasAddress/startObject'
27
+ },
28
+ endObject: {
29
+ objectTypeURI: 'configuration/entityTypes/Location',
30
+ uri: 'configuration/relationTypes/HasAddress/endObject'
31
+ }
32
+ },
33
+ {
34
+ uri: 'configuration/relationTypes/HeadNursing',
35
+ label: 'Head of Nursing',
36
+ startObject: {
37
+ objectTypeURI: 'configuration/entityTypes/HCP',
38
+ uri: 'configuration/relationTypes/HeadNursing/startObject'
39
+ },
40
+ endObject: {
41
+ objectTypeURI: 'configuration/entityTypes/HCO',
42
+ uri: 'configuration/relationTypes/HeadNursing/endObject'
43
+ }
44
+ },
45
+ {
46
+ uri: 'configuration/relationTypes/IsInCommittee',
47
+ label: 'is in committee',
48
+ startObject: {
49
+ objectTypeURI: 'configuration/entityTypes/Committee',
50
+ uri: 'configuration/relationTypes/IsInCommittee/startObject'
51
+ },
52
+ endObject: {
53
+ objectTypeURI: 'configuration/entityTypes/HCP',
54
+ uri: 'configuration/relationTypes/IsInCommittee/endObject'
55
+ }
56
+ }
57
+ ]
58
+ }
59
+ },
60
+ search: {
61
+ searchOptions: {
62
+ searchByOv: false,
63
+ ovOnly: false
64
+ }
65
+ },
66
+ tenant: {
67
+ id: tenant
68
+ }
69
+ };
70
+
71
+ const inRelationTypes = store.metadata[tenant].relationTypes.slice(2, 3);
72
+
73
+ const outRelationTypes = store.metadata[tenant].relationTypes.slice(0, 2);
74
+
75
+ export {tenant, store, inRelationTypes, outRelationTypes};
@@ -0,0 +1,25 @@
1
+ import {makeStyles} from '@mui/styles';
2
+
3
+ export const useStyles = makeStyles((theme) => ({
4
+ dialogPaper: {
5
+ height: '600px',
6
+ maxWidth: '600px'
7
+ },
8
+ dialogTitle: {
9
+ padding: '12px 16px'
10
+ },
11
+ titleText: {
12
+ color: theme.palette.text.primary,
13
+ fontSize: '18px',
14
+ letterSpacing: 0,
15
+ lineHeight: '21px',
16
+ fontWeight: 'bold'
17
+ },
18
+ content: {
19
+ padding: '8px 16px 0px',
20
+ borderBottom: '1px solid rgba(0, 0, 0, 0.12)'
21
+ },
22
+ attributeList: {
23
+ paddingTop: '22px'
24
+ }
25
+ }));
@@ -0,0 +1,13 @@
1
+ import {useMemo} from 'react';
2
+ import {checkMetadataForCreate, getInOutRelationTypesForEntityTypeUri, Mode} from '@reltio/mdm-sdk';
3
+ import {useSelector} from 'react-redux';
4
+ import mdmModule from '@reltio/mdm-module';
5
+
6
+ export const useCreatableInOutRelationTypes = (entityTypeUri: string) => {
7
+ const metadata = useSelector(mdmModule.selectors.getMetadata);
8
+ const {inRelations: inRelationTypes = [], outRelations: outRelationTypes = []} = useMemo(
9
+ () => getInOutRelationTypesForEntityTypeUri(metadata, entityTypeUri, checkMetadataForCreate(Mode.Editing)),
10
+ [metadata, entityTypeUri]
11
+ );
12
+ return {inRelationTypes, outRelationTypes};
13
+ };