@reltio/sources 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 (94) hide show
  1. package/index.ts +1 -0
  2. package/package.json +38 -21
  3. package/public/bundle.js +205 -0
  4. package/public/bundle.js.LICENSE.txt +79 -0
  5. package/public/package.json +22 -0
  6. package/scripts/build/index.js +20 -0
  7. package/src/components/CrosswalkTreeView/CrosswalkTreeView.tsx +47 -0
  8. package/src/components/CrosswalkTreeView/__tests__/CrosswalkTreeView.spec.tsx +72 -0
  9. package/src/components/CrosswalkTreeView/__tests__/helpers.spec.ts +961 -0
  10. package/src/components/CrosswalkTreeView/helpers.ts +91 -0
  11. package/src/components/CrosswalkTreeView/styles.ts +10 -0
  12. package/src/components/EmptyState/EmptyState.tsx +20 -0
  13. package/src/components/EmptyState/__tests__/EmptyState.test.tsx +13 -0
  14. package/src/components/EmptyState/icons/EmptySources.svg +43 -0
  15. package/src/components/EmptyState/styles.ts +19 -0
  16. package/src/components/LegendTable/LegendTable.tsx +124 -0
  17. package/src/components/LegendTable/LegendTableRow/ContributorRow/ContributorRow.tsx +46 -0
  18. package/src/components/LegendTable/LegendTableRow/ContributorRow/__tests__/ContributorRow.test.tsx +103 -0
  19. package/src/components/LegendTable/LegendTableRow/ContributorRow/styles.ts +31 -0
  20. package/src/components/LegendTable/LegendTableRow/LegendTableRow.tsx +179 -0
  21. package/src/components/LegendTable/LegendTableRow/__tests__/LegendTableRow.test.tsx +371 -0
  22. package/src/components/LegendTable/LegendTableRow/styles.ts +31 -0
  23. package/src/components/LegendTable/__tests__/LegendTable.test.tsx +273 -0
  24. package/src/components/LegendTable/__tests__/helpers.test.ts +541 -0
  25. package/src/components/LegendTable/helpers.ts +64 -0
  26. package/src/components/LegendTable/styles.ts +17 -0
  27. package/src/components/LegendView/LegendView.tsx +201 -0
  28. package/src/components/LegendView/__tests__/LegendView.test.tsx +307 -0
  29. package/src/components/LegendView/styles.ts +51 -0
  30. package/src/components/RightSidePanel/RightSidePanel.tsx +25 -0
  31. package/src/components/RightSidePanel/__tests__/RightSidePanel.test.tsx +28 -0
  32. package/src/components/RightSidePanel/styles.ts +14 -0
  33. package/src/components/RulesestsSelector/RulesestsSelector.tsx +31 -0
  34. package/src/components/RulesestsSelector/RulesetOption/RulesetOption.tsx +25 -0
  35. package/src/components/RulesestsSelector/RulesetOption/__tests__/RulesetOption.test.tsx +25 -0
  36. package/src/components/RulesestsSelector/RulesetOption/styles.ts +27 -0
  37. package/src/components/RulesestsSelector/RulesetsMenuList/RulesetsMenuList.tsx +14 -0
  38. package/src/components/RulesestsSelector/__tests__/RulesetsSelector.test.tsx +91 -0
  39. package/src/components/RulesestsSelector/styles.ts +7 -0
  40. package/src/components/SankeyChart/Link.tsx +31 -0
  41. package/src/components/SankeyChart/MergeTooltip.tsx +37 -0
  42. package/src/components/SankeyChart/Node.tsx +137 -0
  43. package/src/components/SankeyChart/SankeyChart.tsx +103 -0
  44. package/src/components/SankeyChart/__tests__/Link.spec.tsx +127 -0
  45. package/src/components/SankeyChart/__tests__/MergeTooltip.spec.tsx +22 -0
  46. package/src/components/SankeyChart/__tests__/Node.spec.tsx +248 -0
  47. package/src/components/SankeyChart/__tests__/SankeyChart.spec.tsx +161 -0
  48. package/src/components/SankeyChart/__tests__/__snapshots__/SankeyChart.spec.tsx.snap +183 -0
  49. package/src/components/SankeyChart/sankey.ts +322 -0
  50. package/src/components/SankeyChart/styles.ts +41 -0
  51. package/src/components/SankeyChart/useSankey.tsx +44 -0
  52. package/src/components/SelectableTree/SelectableTree.tsx +46 -0
  53. package/src/components/SelectableTree/__tests__/SelectableTree.test.tsx +28 -0
  54. package/src/components/SelectableTree/__tests__/__snapshots__/SelectableTree.test.tsx.snap +59 -0
  55. package/src/components/SelectableTree/styles.ts +42 -0
  56. package/src/components/SourcesView/SourcesView.tsx +318 -0
  57. package/src/components/SourcesView/__tests__/IntegrationSourcesView.spec.tsx +199 -0
  58. package/src/components/SourcesView/__tests__/SourcesView.spec.tsx +903 -0
  59. package/src/components/SourcesView/__tests__/data/metadata.data.ts +27 -0
  60. package/src/components/SourcesView/__tests__/data/store.data.ts +65 -0
  61. package/src/components/SourcesView/icons/LegendIcon.svg +8 -0
  62. package/src/components/SourcesView/index.tsx +57 -0
  63. package/src/components/SourcesView/styles.ts +44 -0
  64. package/src/components/TreeActionSnackbar/TreeActionSnackbar.tsx +38 -0
  65. package/src/components/TreeActionSnackbar/__tests__/TreeActionSnackbar.test.tsx +38 -0
  66. package/src/components/TreeActionSnackbar/styles.ts +11 -0
  67. package/src/components/UnmergeDialog/UnmergeDialog.tsx +78 -0
  68. package/src/components/UnmergeDialog/__tests__/UnmergeDialog.test.tsx +124 -0
  69. package/src/components/UnmergeDialog/__tests__/testData.data.ts +271 -0
  70. package/src/components/UnmergeDialog/styles.ts +34 -0
  71. package/src/components/UnsubscribeDialog/UnsubscribeDialog.tsx +64 -0
  72. package/src/components/UnsubscribeDialog/__tests__/UnsubscribeDialog.test.tsx +59 -0
  73. package/src/components/UnsubscribeDialog/styles.ts +31 -0
  74. package/src/hooks/__tests__/useCrosswalkTree.specs.tsx +68 -0
  75. package/src/hooks/__tests__/useCrosswalkTreeActions.specs.ts +110 -0
  76. package/src/hooks/__tests__/useCrosswalksDisplay.specs.tsx +213 -0
  77. package/src/hooks/__tests__/useEntityWithSurvivorshipGroups.specs.ts +160 -0
  78. package/src/hooks/useCrosswalkTree.ts +38 -0
  79. package/src/hooks/useCrosswalkTreeActions.ts +83 -0
  80. package/src/hooks/useCrosswalksDisplay.ts +110 -0
  81. package/src/hooks/useEntityWithSurvivorshipGroups.tsx +51 -0
  82. package/src/icons/notMatch.svg +14 -0
  83. package/src/index.ts +1 -0
  84. package/src/services/errors.ts +11 -0
  85. package/src/types/LegendMode.ts +5 -0
  86. package/src/types/SankeyChartData.ts +31 -0
  87. package/src/types/SourcesSavedState.ts +3 -0
  88. package/src/types/UnmergeContributorEvent.ts +7 -0
  89. package/src/types/UnsubscribeContributorEvent.ts +6 -0
  90. package/src/types/index.ts +4 -0
  91. package/tsconfig.json +4 -0
  92. package/webpack.config.js +10 -0
  93. package/bundle.js +0 -2
  94. package/bundle.js.LICENSE.txt +0 -42
@@ -0,0 +1,322 @@
1
+ import {sum, map, pipe, groupBy, toPairs, sort, nth, prop, reduce, last} from 'ramda';
2
+ import {SankeyLink, SankeyData} from '../../types/SankeyChartData';
3
+
4
+ const sumValues = (links: SankeyLink[]): number => pipe(map(prop('value')), sum)(links);
5
+ const interpolateNumber = (a: number, b: number) => ((a = +a), (b = +b), (t) => a * (1 - t) + b * t);
6
+
7
+ export default function Sankey() {
8
+ const lastLinksSize = 22;
9
+
10
+ let nodeWidth = 24,
11
+ smallestNodeMaxHeight = 15,
12
+ smallestNodeMinHeight = 10,
13
+ nodePadding = 8,
14
+ size = [1, 1] as [number, number];
15
+
16
+ function sankey({nodes, links}: SankeyData) {
17
+ const graph = {nodes, links};
18
+ computeNodeLinks(graph);
19
+ computeNodeValues(graph);
20
+ computeNodeBreadths(graph);
21
+ computeNodeDepths(graph);
22
+ computeLinkDepths(graph);
23
+ correctNodesDepth(graph);
24
+ return graph;
25
+ }
26
+
27
+ sankey.nodeWidth = () => nodeWidth;
28
+ sankey.setNodeWidth = (width: number) => ((nodeWidth = +width), sankey);
29
+
30
+ sankey.nodePadding = () => nodePadding;
31
+ sankey.setNodePadding = (padding: number) => ((nodePadding = +padding), sankey);
32
+
33
+ sankey.smallestNodeMaxHeight = () => smallestNodeMaxHeight;
34
+ sankey.setSmallestNodeMaxHeight = (height: number) => ((smallestNodeMaxHeight = +height), sankey);
35
+
36
+ sankey.smallestNodeMinHeight = () => smallestNodeMinHeight;
37
+ sankey.setSmallestNodeMinHeight = (height: number) => ((smallestNodeMinHeight = +height), sankey);
38
+
39
+ sankey.size = () => size;
40
+ sankey.setSize = (newSize: [number, number]) => ((size = newSize), sankey);
41
+
42
+ sankey.update = (graph) => {
43
+ computeLinkDepths(graph);
44
+ return graph;
45
+ };
46
+
47
+ sankey.link = () => {
48
+ let curvature = 0.5;
49
+
50
+ function link(d) {
51
+ const lastLine = !d.target.sourceLinks || d.target.sourceLinks.length == 0;
52
+ const addPixels = 0.13;
53
+ const x0 = d.source.x + d.source.dx,
54
+ x1 = d.target.x,
55
+ xi = interpolateNumber(x0, x1),
56
+ x2 = xi(curvature),
57
+ x3 = xi(1 - curvature),
58
+ y0 = d.source.y + d.sy - addPixels,
59
+ y1 = d.target.y + d.ty - addPixels;
60
+ const appendix = !lastLine
61
+ ? `L${x1 + nodeWidth},${y1} L${x1 + nodeWidth},${y1 + d.dy + 2 * addPixels}`
62
+ : '';
63
+
64
+ return y0 === y1
65
+ ? `M${x0},${y0}
66
+ L${x1},${y1}${appendix}
67
+ L${x1},${y1 + d.dy + 2 * addPixels}
68
+ L${x0},${y0 + d.dy + 2 * addPixels} Z`
69
+ : `M${x0},${y0}
70
+ C${x2},${y0} ${x3},${y1} ${x1},${y1}${appendix}
71
+ L${x1},${y1 + d.dy + 2 * addPixels}
72
+ C${x3},${y1 + d.dy + 2 * addPixels} ${x2},${y0 + d.dy + 2 * addPixels}
73
+ ${x0},${y0 + d.dy + 2 * addPixels} Z`;
74
+ }
75
+
76
+ link.curvature = function (_) {
77
+ if (!arguments.length) return curvature;
78
+ curvature = +_;
79
+ return link;
80
+ };
81
+
82
+ return link;
83
+ };
84
+
85
+ // Populate the sourceLinks and targetLinks for each node.
86
+ function computeNodeLinks({nodes, links}: SankeyData) {
87
+ nodes.forEach((node) => {
88
+ node.sourceLinks = [];
89
+ node.targetLinks = [];
90
+ });
91
+ links.forEach((link) => {
92
+ link.source.sourceLinks.push(link);
93
+ link.target.targetLinks.push(link);
94
+ });
95
+ }
96
+
97
+ // Compute the value (size) of each node by summing the associated links.
98
+ function computeNodeValues({nodes}: SankeyData) {
99
+ nodes.forEach((node) => {
100
+ node.value = Math.max(sumValues(node.sourceLinks), sumValues(node.targetLinks));
101
+ });
102
+ }
103
+
104
+ // Iteratively assign the breadth (x-position) for each node.
105
+ // Nodes are assigned the maximum breadth of incoming neighbors plus one;
106
+ // nodes with no incoming links are assigned breadth zero, while
107
+ // nodes with no outgoing links are assigned the maximum breadth.
108
+ function computeNodeBreadths({nodes}: SankeyData) {
109
+ if (!nodes.length) return;
110
+ let remainingNodes = [nodes[0]],
111
+ nextNodes,
112
+ x = 0;
113
+
114
+ while (remainingNodes.length) {
115
+ nextNodes = [];
116
+ remainingNodes.forEach((node) => {
117
+ node.x = x;
118
+ node.dx = nodeWidth;
119
+ node.targetLinks.forEach((link) => {
120
+ if (nextNodes.indexOf(link.source) < 0) {
121
+ nextNodes.push(link.source);
122
+ }
123
+ });
124
+ });
125
+ remainingNodes = nextNodes;
126
+ ++x;
127
+ }
128
+
129
+ nodes.forEach((node) => {
130
+ node.x = x - 1 - node.x;
131
+ });
132
+
133
+ if (x > 2) {
134
+ const w = (size[0] - 2 * nodeWidth - lastLinksSize) / (x - 2);
135
+ nodes.forEach((node) => {
136
+ if (!node.sourceLinks || node.sourceLinks.length == 0) node.x = size[0] - nodeWidth;
137
+ else node.x *= w;
138
+ });
139
+ } else {
140
+ nodes[0].x = size[0] - nodeWidth;
141
+ nodes[1].x = nodes[0].x - lastLinksSize - nodeWidth;
142
+ }
143
+ }
144
+
145
+ function computeNodeDepths({nodes, links}: SankeyData) {
146
+ const nodesByBreadth = pipe(
147
+ groupBy(prop('x')),
148
+ toPairs,
149
+ sort((a, b) => a[0] - b[0]),
150
+ map(nth(1))
151
+ )(nodes);
152
+
153
+ //
154
+ initializeNodeDepth();
155
+ resolveCollisions();
156
+
157
+ const max = pipe(
158
+ map((nodes) => (nodes.length - 1) * nodePadding + sum(map((node) => node.dy, nodes))),
159
+ reduce(Math.max, 0)
160
+ )(nodesByBreadth);
161
+
162
+ const y1 = (size[1] - max) / 2;
163
+
164
+ nodesByBreadth.forEach((nodes) => {
165
+ const {y, dy} = last(nodes);
166
+ const y0 = size[1] - (y + dy);
167
+ nodes.forEach((node) => {
168
+ node.y += y0;
169
+ if (y1 > 0) node.y -= y1;
170
+ });
171
+ });
172
+
173
+ function initializeNodeDepth() {
174
+ let ky = pipe(
175
+ map((nodes) => (size[1] - (nodes.length - 1) * nodePadding) / sumValues(nodes)),
176
+ reduce(Math.min, Infinity)
177
+ )(nodesByBreadth);
178
+
179
+ const min = pipe(
180
+ map((node) => node.value / (node.targetLinks?.length || 1)),
181
+ reduce(Math.min, Infinity)
182
+ )(nodes);
183
+
184
+ ky = Math.max(Math.min(ky, smallestNodeMaxHeight / min), smallestNodeMinHeight / min);
185
+
186
+ nodesByBreadth.forEach((nodes) => {
187
+ nodes.forEach((node, i) => {
188
+ node.y = i;
189
+ node.dy = node.value * ky;
190
+ });
191
+ });
192
+
193
+ links.forEach((link) => {
194
+ link.dy = link.value * ky;
195
+ });
196
+ }
197
+
198
+ function resolveCollisions() {
199
+ nodesByBreadth.forEach((nodes) => {
200
+ let node,
201
+ dy,
202
+ y0 = 0,
203
+ i;
204
+ const n = nodes.length;
205
+
206
+ // Push any overlapping nodes down.
207
+ nodes.sort((a, b) => a.y - b.y);
208
+ for (i = 0; i < n; ++i) {
209
+ node = nodes[i];
210
+ dy = y0 - node.y;
211
+ if (dy > 0) node.y += dy;
212
+ y0 = node.y + node.dy + nodePadding;
213
+ }
214
+
215
+ // If the bottommost node goes outside the bounds, push it back up.
216
+ dy = y0 - nodePadding - size[1];
217
+ if (dy > 0) {
218
+ y0 = node.y -= dy;
219
+
220
+ // Push any overlapping nodes back up.
221
+ for (i = n - 2; i >= 0; --i) {
222
+ node = nodes[i];
223
+ dy = node.y + node.dy + nodePadding - y0;
224
+ if (dy > 0) node.y -= dy;
225
+ y0 = node.y;
226
+ }
227
+ }
228
+ });
229
+ }
230
+ }
231
+
232
+ function computeLinkDepths({nodes}: SankeyData) {
233
+ const nodesByBreadth = pipe(
234
+ groupBy(prop('x')),
235
+ toPairs,
236
+ sort((a: number, b: number) => a[0] - b[0]),
237
+ map(nth(1))
238
+ )(nodes);
239
+
240
+ nodesByBreadth.forEach((nodes) => {
241
+ nodes.forEach((node) => {
242
+ if (node.targetLinks?.length) {
243
+ const result = [];
244
+ const linksByUuid = node.targetLinks.map((l) => l.crosswalk.uuid);
245
+ const sourceNodes = [];
246
+ node.targetLinks.forEach(function (l) {
247
+ if (this.indexOf(l.source) == -1) this.push(l.source);
248
+ }, sourceNodes);
249
+ for (let i = 0; i < sourceNodes.length; i++)
250
+ for (let j = 0; j < sourceNodes[i].sourceLinks.length; j++) {
251
+ const index = linksByUuid.indexOf(sourceNodes[i].sourceLinks[j].crosswalk.uuid);
252
+ if (index != -1) result.push(node.targetLinks[index]);
253
+ }
254
+ node.targetLinks = result;
255
+ }
256
+
257
+ if (node.sourceLinks?.length) {
258
+ const result = [];
259
+ if (node.targetLinks?.length) {
260
+ const linksByUuid = node.sourceLinks.map((l) => l.crosswalk.uuid);
261
+ for (let i = 0; i < node.targetLinks.length; i++) {
262
+ const index = linksByUuid.indexOf(node.targetLinks[i].crosswalk.uuid);
263
+ if (index != -1) result.push(node.sourceLinks[index]);
264
+ }
265
+ }
266
+
267
+ const resultByUuid = result.map((l) => l.crosswalk.uuid);
268
+ const newSources = node.sourceLinks.filter(function (l) {
269
+ return this.indexOf(l.crosswalk.uuid) == -1;
270
+ }, resultByUuid);
271
+
272
+ node.sourceLinks = newSources.concat(result);
273
+ if (result.length > 0) {
274
+ let sizeForNewSources = 0;
275
+ for (let i = 0; i < newSources.length; i++) sizeForNewSources += newSources[i].dy;
276
+ node.sizeForNewSources = sizeForNewSources;
277
+ }
278
+ }
279
+ });
280
+ });
281
+
282
+ nodes.forEach((node) => {
283
+ let sy = 0,
284
+ ty = node.sizeForNewSources || 0;
285
+ delete node.sizeForNewSources;
286
+ node.sourceLinks.forEach((link) => {
287
+ link.sy = sy;
288
+ sy += link.dy;
289
+ });
290
+ node.targetLinks.forEach((link) => {
291
+ link.ty = ty;
292
+ ty += link.dy;
293
+ });
294
+ });
295
+ }
296
+
297
+ function correctNodesDepth({nodes}: SankeyData) {
298
+ const nodesByBreadth = pipe(
299
+ groupBy(prop('x')),
300
+ toPairs,
301
+ sort((a: number, b: number) => b[0] - a[0]),
302
+ map(nth(1))
303
+ )(nodes);
304
+
305
+ nodesByBreadth.forEach((nodes) => {
306
+ nodes = nodes.concat().sort((a, b) => a.y - b.y);
307
+ nodes.forEach((node, i) => {
308
+ if (node.sourceLinks?.length) {
309
+ const {target, ty, dy} = last(node.sourceLinks);
310
+ let y0 = target.y + ty + dy - node.dy;
311
+ if (nodes[i - 1]?.x == node.x && y0 < nodes[i - 1]?.y + nodes[i - 1]?.dy + nodePadding)
312
+ y0 = nodes[i - 1].y + nodes[i - 1].dy + nodePadding;
313
+ if (node.y > y0) {
314
+ node.y = y0;
315
+ }
316
+ }
317
+ });
318
+ });
319
+ }
320
+
321
+ return sankey;
322
+ }
@@ -0,0 +1,41 @@
1
+ import {makeStyles} from '@mui/styles';
2
+
3
+ export const useStyles = makeStyles({
4
+ tooltipTitle: {
5
+ fontWeight: 500,
6
+ fontSize: '16px',
7
+ lineHeight: '24px',
8
+ letterSpacing: '0.15px',
9
+ marginBottom: '8px',
10
+ userSelect: 'none'
11
+ },
12
+ tooltipRow: {
13
+ display: 'flex',
14
+ alignItems: 'center',
15
+ fontSize: '13px',
16
+ lineHeight: '15px',
17
+ fontWeight: 'normal',
18
+ '&:not(:last-child)': {
19
+ marginBottom: '4px'
20
+ }
21
+ },
22
+ tooltipIcon: {
23
+ fontSize: '18px',
24
+ color: 'rgba(0, 0, 0, 0.54)',
25
+ marginRight: '8px'
26
+ },
27
+ node: {
28
+ stroke: '#000000',
29
+ strokeWidth: 1,
30
+ strokeOpacity: 0.38
31
+ },
32
+ rootNode: {
33
+ transform: 'rotate(90deg)',
34
+ userSelect: 'none',
35
+ fontSize: '13px'
36
+ },
37
+ popupContainer: {
38
+ padding: 5,
39
+ boxSizing: 'border-box'
40
+ }
41
+ });
@@ -0,0 +1,44 @@
1
+ import {useMemo} from 'react';
2
+ import {SankeyData, SankeyNode} from '../../types/SankeyChartData';
3
+ import sankey from './sankey';
4
+
5
+ type Props = {
6
+ width: number;
7
+ height: number;
8
+ data: SankeyData;
9
+ };
10
+
11
+ const useSankey = ({data, width, height}: Props) =>
12
+ useMemo(() => {
13
+ if (!width || !height) return {nodes: [], links: []};
14
+
15
+ const shallowCopy = (data: SankeyData): SankeyData => {
16
+ const safeNodes = data.nodes.map((node) => ({...node}));
17
+ const getSafeNode = (originalNode: SankeyNode) => {
18
+ const index = data.nodes.indexOf(originalNode);
19
+ return safeNodes[index];
20
+ };
21
+
22
+ const safeLinks = data.links.map((link) => ({
23
+ ...link,
24
+ source: getSafeNode(link.source),
25
+ target: getSafeNode(link.target)
26
+ }));
27
+ return {nodes: safeNodes, links: safeLinks};
28
+ };
29
+
30
+ const sankeyLayout = sankey()
31
+ .setNodeWidth(14)
32
+ .setNodePadding(20)
33
+ .setSize([height - 2, width - 2]);
34
+
35
+ const nodeWidth = sankeyLayout.nodeWidth();
36
+ const path = sankeyLayout.link();
37
+
38
+ const safeData = shallowCopy(data);
39
+ const {nodes, links}: SankeyData = sankeyLayout(safeData);
40
+
41
+ return {nodes, links, nodeWidth, path};
42
+ }, [data, height, width]);
43
+
44
+ export default useSankey;
@@ -0,0 +1,46 @@
1
+ import React, {useCallback} from 'react';
2
+ import CheckCircleIcon from '@mui/icons-material/CheckCircle';
3
+ import InfoIcon from '@mui/icons-material/Info';
4
+ import {CrosswalksDisplayProvider, noop} from '@reltio/components';
5
+ import {
6
+ CrosswalkBase,
7
+ CrosswalkTree,
8
+ getCrosswalkColor as getUniqueCrosswalkColor,
9
+ getCrosswalkId
10
+ } from '@reltio/mdm-sdk';
11
+ import classnames from 'classnames';
12
+ import CrosswalkTreeView from '../CrosswalkTreeView/CrosswalkTreeView';
13
+ import {useStyles} from './styles';
14
+
15
+ type Props = {
16
+ coloredCrosswalks: string[];
17
+ crosswalkTree: CrosswalkTree;
18
+ selected: boolean;
19
+ title: string;
20
+ onSelect?: () => void;
21
+ };
22
+
23
+ export const SelectableTree = ({coloredCrosswalks, crosswalkTree, selected, title, onSelect = noop}: Props) => {
24
+ const styles = useStyles();
25
+ const getCrosswalkColor = useCallback(
26
+ (crosswalk: CrosswalkBase) => {
27
+ return coloredCrosswalks.includes(crosswalk.uri)
28
+ ? getUniqueCrosswalkColor(getCrosswalkId(crosswalk))
29
+ : '#CACCD1';
30
+ },
31
+ [coloredCrosswalks]
32
+ );
33
+ return (
34
+ <CrosswalksDisplayProvider getCrosswalkColor={getCrosswalkColor}>
35
+ <div className={styles.container} onClick={onSelect}>
36
+ <span className={styles.title}>
37
+ {title} <InfoIcon className={styles.hintIcon} />
38
+ </span>
39
+ <div className={classnames(styles.graph, {[`${styles.selectedTree}`]: selected})}>
40
+ {selected && <CheckCircleIcon className={styles.checkedIcon} />}
41
+ <CrosswalkTreeView crosswalkTree={crosswalkTree} disableUnmerge={true} />
42
+ </div>
43
+ </div>
44
+ </CrosswalksDisplayProvider>
45
+ );
46
+ };
@@ -0,0 +1,28 @@
1
+ import React from 'react';
2
+ import {render} from '@testing-library/react';
3
+ import {SelectableTree} from '../SelectableTree';
4
+ import {crosswalkTree} from '../../UnmergeDialog/__tests__/testData.data';
5
+
6
+ describe('SelectableTree', () => {
7
+ const props = {
8
+ onSelect: jest.fn(),
9
+ title: 'the name',
10
+ coloredCrosswalks: [],
11
+ crosswalkTree
12
+ };
13
+ it('should render component correctly', () => {
14
+ const {asFragment} = render(<SelectableTree {...props} selected={true} />);
15
+ expect(asFragment()).toMatchSnapshot();
16
+ });
17
+
18
+ it('should render CheckCircleIcon if selected === true', () => {
19
+ const {container} = render(<SelectableTree {...props} selected={true} />);
20
+ expect(container.querySelector('.checkedIcon')).toBeInTheDocument();
21
+ expect(container.querySelector('.title').textContent).toBe('the name ');
22
+ });
23
+
24
+ it('should not render CheckCircleIcon if selected === false', () => {
25
+ const {container} = render(<SelectableTree {...props} selected={false} />);
26
+ expect(container.querySelector('.checkedIcon')).not.toBeInTheDocument();
27
+ });
28
+ });
@@ -0,0 +1,59 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`SelectableTree should render component correctly 1`] = `
4
+ <DocumentFragment>
5
+ <div
6
+ class="container"
7
+ >
8
+ <span
9
+ class="title"
10
+ >
11
+ the name
12
+ <svg
13
+ aria-hidden="true"
14
+ class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium hintIcon css-i4bv87-MuiSvgIcon-root"
15
+ data-testid="InfoIcon"
16
+ focusable="false"
17
+ viewBox="0 0 24 24"
18
+ >
19
+ <path
20
+ d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"
21
+ />
22
+ </svg>
23
+ </span>
24
+ <div
25
+ class="graph selectedTree"
26
+ >
27
+ <svg
28
+ aria-hidden="true"
29
+ class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium checkedIcon css-i4bv87-MuiSvgIcon-root"
30
+ data-testid="CheckCircleIcon"
31
+ focusable="false"
32
+ viewBox="0 0 24 24"
33
+ >
34
+ <path
35
+ d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"
36
+ />
37
+ </svg>
38
+ <div
39
+ class="graph"
40
+ data-reltio-id="reltio-crosswalk-tree"
41
+ >
42
+ <svg
43
+ cursor="auto"
44
+ height="200"
45
+ width="0"
46
+ >
47
+ <g
48
+ pointer-events="auto"
49
+ transform="translate(0,199) rotate(-90)"
50
+ >
51
+ <g />
52
+ <g />
53
+ </g>
54
+ </svg>
55
+ </div>
56
+ </div>
57
+ </div>
58
+ </DocumentFragment>
59
+ `;
@@ -0,0 +1,42 @@
1
+ import {makeStyles} from '@mui/styles';
2
+
3
+ export const useStyles = makeStyles((theme) => ({
4
+ container: {
5
+ display: 'flex',
6
+ flexDirection: 'column',
7
+ '& + &': {
8
+ marginLeft: '16px'
9
+ }
10
+ },
11
+ title: {
12
+ fontSize: '13px',
13
+ lineHeight: '15px',
14
+ color: '#666666',
15
+ marginBottom: '7px'
16
+ },
17
+ hintIcon: {
18
+ width: '16px',
19
+ height: '16px',
20
+ verticalAlign: 'middle'
21
+ },
22
+ graph: {
23
+ width: '400px',
24
+ height: '300px',
25
+ borderRadius: '8px',
26
+ background: 'rgba(0, 0, 0, 0.03)',
27
+ border: '1px solid rgba(0, 0, 0, 0.03)',
28
+ position: 'relative'
29
+ },
30
+ checkedIcon: {
31
+ position: 'absolute',
32
+ height: '20px',
33
+ width: '20px',
34
+ right: '10px',
35
+ top: '10px',
36
+ color: theme.palette.primary.main
37
+ },
38
+ selectedTree: {
39
+ border: `1px solid ${theme.palette.primary.main}`,
40
+ background: 'rgba(0, 114, 206, 0.05)'
41
+ }
42
+ }));