@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,91 @@
1
+ import {chain, concat, curry, evolve, filter, map, pipe, uniq} from 'ramda';
2
+ import {CrosswalkBase, CrosswalkTree, eqCrosswalks} from '@reltio/mdm-sdk';
3
+ import {SankeyLink, SankeyNode} from '../../types/SankeyChartData';
4
+
5
+ const isCrosswalkDisplayed = curry(
6
+ (displayedCrosswalks: CrosswalkBase[], crosswalk: CrosswalkBase) =>
7
+ !displayedCrosswalks || displayedCrosswalks.some(eqCrosswalks(crosswalk))
8
+ );
9
+
10
+ const filterDisplayedCrosswalks = curry((displayedCrosswalks: CrosswalkBase[], tree: CrosswalkTree) => {
11
+ return evolve(
12
+ {
13
+ crosswalks: filter(isCrosswalkDisplayed(displayedCrosswalks)),
14
+ merges: map(
15
+ evolve({
16
+ losers: map(filterDisplayedCrosswalks(displayedCrosswalks))
17
+ })
18
+ )
19
+ },
20
+ tree
21
+ );
22
+ });
23
+
24
+ const addCrosswalkUniqueId = (tree: CrosswalkTree) => {
25
+ let count = 1;
26
+ const addIdToSubtree = pipe(
27
+ evolve({crosswalks: map((crosswalk) => ({...crosswalk, uuid: count++}))}),
28
+ evolve({
29
+ merges: map(
30
+ evolve({
31
+ losers: map((loser) => addIdToSubtree(loser))
32
+ })
33
+ )
34
+ })
35
+ );
36
+ return addIdToSubtree(tree);
37
+ };
38
+
39
+ const processTree = (tree: CrosswalkTree & {label?: string}) => {
40
+ const nodes: SankeyNode[] = [];
41
+ const links: SankeyLink[] = [];
42
+
43
+ const processSubTree = (tree, parentIndex, time) => {
44
+ const {crosswalks = [], merges = [], uri = '', label} = tree;
45
+
46
+ nodes.push({entity: tree, name: uri, time, label});
47
+ const index = nodes.length - 1;
48
+
49
+ const mergesCrosswalks = pipe(
50
+ chain(({losers, time}) => {
51
+ return pipe(
52
+ filter((loser) => loser.crosswalks?.length || loser.merges?.length),
53
+ chain((loser) => processSubTree(loser, index, time))
54
+ )(losers);
55
+ }),
56
+ concat(crosswalks)
57
+ )(merges);
58
+
59
+ if (parentIndex != -1)
60
+ mergesCrosswalks.forEach((crosswalk) =>
61
+ links.push({
62
+ source: nodes[index],
63
+ target: nodes[parentIndex],
64
+ value: 10,
65
+ crosswalk
66
+ })
67
+ );
68
+
69
+ return mergesCrosswalks;
70
+ };
71
+ processSubTree(tree, -1, Date.now());
72
+
73
+ const linkedNodes = pipe(
74
+ chain(({source, target}) => [source, target]),
75
+ uniq
76
+ )(links);
77
+
78
+ return {nodes: nodes.filter((node) => linkedNodes.includes(node)), links};
79
+ };
80
+
81
+ export const getSankeyData = (
82
+ crosswalkTree: CrosswalkTree,
83
+ entityLabel = '',
84
+ displayedCrosswalks?: CrosswalkBase[]
85
+ ) => {
86
+ return pipe(
87
+ filterDisplayedCrosswalks(displayedCrosswalks),
88
+ addCrosswalkUniqueId,
89
+ processTree
90
+ )({uri: 'ROOT', label: entityLabel, crosswalks: [], merges: [{time: Date.now(), losers: [crosswalkTree]}]});
91
+ };
@@ -0,0 +1,10 @@
1
+ import {makeStyles} from '@mui/styles';
2
+
3
+ export const useStyles = makeStyles(() => ({
4
+ graph: {
5
+ padding: '8px 0px',
6
+ boxSizing: 'border-box',
7
+ height: '100%',
8
+ width: '100%'
9
+ }
10
+ }));
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+ import i18n from 'ui-i18n';
3
+ import EmptyIcon from './icons/EmptySources.svg';
4
+
5
+ import {useStyles} from './styles';
6
+
7
+ const EmptyState = () => {
8
+ const styles = useStyles();
9
+
10
+ return (
11
+ <div className={styles.root}>
12
+ <EmptyIcon />
13
+ <span className={styles.info}>
14
+ {i18n.text('Please persist your profile to be able to edit crosswalks')}
15
+ </span>
16
+ </div>
17
+ );
18
+ };
19
+
20
+ export default EmptyState;
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ import {shallow} from 'enzyme';
3
+
4
+ import EmptyIcon from '../icons/EmptySources.svg';
5
+ import EmptyState from '../EmptyState';
6
+
7
+ describe('EmptyState tests', () => {
8
+ it('should render correctly', () => {
9
+ const wrapper = shallow(<EmptyState />);
10
+ expect(wrapper.find(EmptyIcon)).toHaveLength(1);
11
+ expect(wrapper.find('.info').text()).toBe('Please persist your profile to be able to edit crosswalks');
12
+ });
13
+ });
@@ -0,0 +1,43 @@
1
+ <svg width="90" height="90" viewBox="0 0 90 90" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <circle cx="45" cy="45" r="45" fill="#0072CE" fill-opacity="0.1"/>
3
+ <mask id="mask0" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="90" height="90">
4
+ <circle cx="45" cy="45" r="45" fill="white"/>
5
+ </mask>
6
+ <g mask="url(#mask0)">
7
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M-5.625 3.16406H84.375V90H-5.625V3.16406Z" fill="url(#paint0_linear)"/>
8
+ <path opacity="0.049615" fill-rule="evenodd" clip-rule="evenodd" d="M66.0097 19.9917L86.6249 40.6068V92.9444H32.83L18.2812 79.1309V19.9917H66.0097Z" fill="url(#paint1_linear)"/>
9
+ <path opacity="0.06" fill-rule="evenodd" clip-rule="evenodd" d="M16.5234 20.3906H87.5391C88.8982 20.3906 90 21.4924 90 22.8516V78.0469C90 79.406 88.8982 80.5078 87.5391 80.5078H16.5234C15.1643 80.5078 14.0625 79.406 14.0625 78.0469V22.8516C14.0625 21.4924 15.1643 20.3906 16.5234 20.3906Z" fill="black"/>
10
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M17.5781 19.6875H88.2422C89.213 19.6875 90 20.4745 90 21.4453V76.6406C90 77.6114 89.213 78.3984 88.2422 78.3984H17.5781C16.6073 78.3984 15.8203 77.6114 15.8203 76.6406V21.4453C15.8203 20.4745 16.6073 19.6875 17.5781 19.6875Z" fill="white"/>
11
+ <path opacity="0.8" fill-rule="evenodd" clip-rule="evenodd" d="M37.0459 25.5356C37.0459 24.9532 37.5181 24.481 38.1006 24.481H52.7342C53.3167 24.481 53.7889 24.9532 53.7889 25.5356C53.7889 26.1181 53.3167 26.5903 52.7342 26.5903H38.1006C37.5181 26.5903 37.0459 26.1181 37.0459 25.5356Z" fill="#B6C7DB"/>
12
+ <path opacity="0.2" fill-rule="evenodd" clip-rule="evenodd" d="M36.9141 29.7544C36.9141 29.1719 37.3863 28.6997 37.9688 28.6997H60.5408C61.1233 28.6997 61.5955 29.1719 61.5955 29.7544C61.5955 30.3369 61.1233 30.8091 60.5408 30.8091H37.9687C37.3863 30.8091 36.9141 30.3369 36.9141 29.7544Z" fill="#979797"/>
13
+ <path opacity="0.8" fill-rule="evenodd" clip-rule="evenodd" d="M37.0459 57.3047C37.0459 56.7222 37.5181 56.25 38.1006 56.25H52.7342C53.3167 56.25 53.7889 56.7222 53.7889 57.3047C53.7889 57.8872 53.3167 58.3594 52.7342 58.3594H38.1006C37.5181 58.3594 37.0459 57.8872 37.0459 57.3047Z" fill="#B6C7DB"/>
14
+ <path opacity="0.2" fill-rule="evenodd" clip-rule="evenodd" d="M36.9141 61.5234C36.9141 60.9409 37.3863 60.4688 37.9688 60.4688H60.5408C61.1233 60.4688 61.5955 60.9409 61.5955 61.5234C61.5955 62.1059 61.1233 62.5781 60.5408 62.5781H37.9687C37.3863 62.5781 36.9141 62.1059 36.9141 61.5234Z" fill="#979797"/>
15
+ <path opacity="0.8" fill-rule="evenodd" clip-rule="evenodd" d="M37.0459 67.8516C37.0459 67.2691 37.5181 66.7969 38.1006 66.7969H66.7967C67.3792 66.7969 67.8514 67.2691 67.8514 67.8516C67.8514 68.4341 67.3792 68.9062 66.7967 68.9062H38.1006C37.5181 68.9062 37.0459 68.4341 37.0459 67.8516Z" fill="#B6C7DB"/>
16
+ <path opacity="0.2" fill-rule="evenodd" clip-rule="evenodd" d="M36.9141 72.0703C36.9141 71.4878 37.3863 71.0156 37.9688 71.0156H64.0564C64.6389 71.0156 65.1111 71.4878 65.1111 72.0703C65.1111 72.6528 64.6389 73.125 64.0564 73.125H37.9687C37.3863 73.125 36.9141 72.6528 36.9141 72.0703Z" fill="#979797"/>
17
+ <path opacity="0.8" fill-rule="evenodd" clip-rule="evenodd" d="M48.6475 36.2109C48.6475 35.6284 49.1197 35.1562 49.7021 35.1562H78.3982C78.9807 35.1562 79.4529 35.6284 79.4529 36.2109C79.4529 36.7934 78.9807 37.2656 78.3982 37.2656H49.7021C49.1197 37.2656 48.6475 36.7934 48.6475 36.2109Z" fill="#B6C7DB"/>
18
+ <path opacity="0.2" fill-rule="evenodd" clip-rule="evenodd" d="M48.5156 40.4297C48.5156 39.8472 48.9878 39.375 49.5703 39.375H72.1424C72.7249 39.375 73.1971 39.8472 73.1971 40.4297C73.1971 41.0122 72.7249 41.4844 72.1424 41.4844H49.5703C48.9878 41.4844 48.5156 41.0122 48.5156 40.4297Z" fill="#979797"/>
19
+ <circle cx="23.5547" cy="27.7734" r="3.16406" fill="#B6C7DB"/>
20
+ <circle cx="23.5547" cy="27.7734" r="3.16406" fill="#B6C7DB"/>
21
+ <circle cx="42.1875" cy="37.2656" r="2.10938" fill="#B6C7DB"/>
22
+ <circle cx="42.1875" cy="47.8125" r="2.10938" fill="#B6C7DB"/>
23
+ <circle cx="23.5547" cy="59.4141" r="3.16406" fill="#B6C7DB"/>
24
+ <circle cx="23.5547" cy="69.9609" r="3.16406" fill="#B6C7DB"/>
25
+ <path opacity="0.8" fill-rule="evenodd" clip-rule="evenodd" d="M48.6475 46.7578C48.6475 46.1753 49.1197 45.7031 49.7021 45.7031H78.3982C78.9807 45.7031 79.4529 46.1753 79.4529 46.7578C79.4529 47.3403 78.9807 47.8125 78.3982 47.8125H49.7021C49.1197 47.8125 48.6475 47.3403 48.6475 46.7578Z" fill="#B6C7DB"/>
26
+ <path opacity="0.2" fill-rule="evenodd" clip-rule="evenodd" d="M48.5156 50.9766C48.5156 50.3941 48.9878 49.9219 49.5703 49.9219H75.658C76.2405 49.9219 76.7127 50.3941 76.7127 50.9766C76.7127 51.5591 76.2405 52.0312 75.658 52.0312H49.5703C48.9878 52.0312 48.5156 51.5591 48.5156 50.9766Z" fill="#979797"/>
27
+ <path opacity="0.2" fill-rule="evenodd" clip-rule="evenodd" d="M32.5244 37.9688L32.5244 35.1562H31.8213V38.4487V38.6719V49.3471H35.7513V48.644L32.5244 48.6439L32.5244 38.6719H35.7513V37.9688H32.5244Z" fill="#2B98F0"/>
28
+ <path opacity="0.54" fill-rule="evenodd" clip-rule="evenodd" d="M33.4234 27.0703L32.1805 28.3133L30.9375 27.0703H33.4234Z" fill="black"/>
29
+ <path opacity="0.6" fill-rule="evenodd" clip-rule="evenodd" d="M17.5781 7.73438H82.9688C84.1337 7.73438 85.0781 8.67877 85.0781 9.84375V16.1719C85.0781 17.3369 84.1337 18.2812 82.9688 18.2812H17.5781C16.4131 18.2812 15.4688 17.3369 15.4688 16.1719V9.84375C15.4688 8.67877 16.4131 7.73438 17.5781 7.73438Z" fill="white"/>
30
+ <path opacity="0.2" fill-rule="evenodd" clip-rule="evenodd" d="M20.3906 13.0078C20.3906 11.8428 21.335 10.8984 22.5 10.8984H47.1094C48.2744 10.8984 49.2188 11.8428 49.2188 13.0078C49.2188 14.1728 48.2744 15.1172 47.1094 15.1172H22.5C21.335 15.1172 20.3906 14.1728 20.3906 13.0078Z" fill="#979797"/>
31
+ <path opacity="0.2" fill-rule="evenodd" clip-rule="evenodd" d="M52.0312 13.0078C52.0312 11.8428 52.9756 10.8984 54.1406 10.8984H57.6562C58.8212 10.8984 59.7656 11.8428 59.7656 13.0078C59.7656 14.1728 58.8212 15.1172 57.6562 15.1172H54.1406C52.9756 15.1172 52.0312 14.1728 52.0312 13.0078Z" fill="#979797"/>
32
+ </g>
33
+ <defs>
34
+ <linearGradient id="paint0_linear" x1="-5.625" y1="3.16406" x2="-5.625" y2="90" gradientUnits="userSpaceOnUse">
35
+ <stop stop-color="#FAFAFA" stop-opacity="0.01"/>
36
+ <stop offset="1" stop-color="#F4F4F4"/>
37
+ </linearGradient>
38
+ <linearGradient id="paint1_linear" x1="93.5568" y1="63.6719" x2="54.4507" y2="27.3098" gradientUnits="userSpaceOnUse">
39
+ <stop stop-opacity="0.01"/>
40
+ <stop offset="1"/>
41
+ </linearGradient>
42
+ </defs>
43
+ </svg>
@@ -0,0 +1,19 @@
1
+ import {makeStyles} from '@mui/styles';
2
+
3
+ export const useStyles = makeStyles((theme) => ({
4
+ root: {
5
+ height: '100%',
6
+ overflow: 'hidden',
7
+ background: theme.palette.background.default,
8
+ display: 'flex',
9
+ flexDirection: 'column',
10
+ alignItems: 'center',
11
+ paddingTop: '115px'
12
+ },
13
+ info: {
14
+ color: theme.palette.text.secondary,
15
+ fontSize: '16px',
16
+ lineHeight: '24px',
17
+ marginTop: '16px'
18
+ }
19
+ }));
@@ -0,0 +1,124 @@
1
+ import React, {memo, useCallback, useEffect, useMemo, useState} from 'react';
2
+ import {
3
+ AttributeType,
4
+ checkCanCreateCrosswalk,
5
+ checkCanDeleteCrosswalk,
6
+ checkCanEditCrosswalk,
7
+ Contributor,
8
+ CrosswalkBase,
9
+ Entity,
10
+ TEntityType
11
+ } from '@reltio/mdm-sdk';
12
+ import {AddCrosswalkEvent, DeleteCrosswalkEvent, EditCrosswalkEvent, noop} from '@reltio/components';
13
+ import {buildTableRowsData, collectVisibleCrosswalks} from './helpers';
14
+ import LegendTableRow from './LegendTableRow/LegendTableRow';
15
+ import {append, assoc, evolve, has, ifElse, includes, pipe, unless, without} from 'ramda';
16
+ import {UnsubscribeContributorEvent} from '../../types';
17
+
18
+ import {useStyles} from './styles';
19
+
20
+ type Props = {
21
+ entity: Entity;
22
+ entityType: TEntityType;
23
+ contributors: Contributor[];
24
+ accentContributors?: boolean;
25
+ selectedAttributeTypes?: AttributeType[];
26
+ onChangeVisibleCrosswalks: (visibleCrosswalks: CrosswalkBase[]) => void;
27
+ readOnly?: boolean;
28
+ canUnmerge: boolean;
29
+ disableUnmerge: boolean;
30
+ onAddCrosswalk?: (event: AddCrosswalkEvent) => void;
31
+ onEditCrosswalk?: (event: EditCrosswalkEvent) => void;
32
+ onDeleteCrosswalk?: (event: DeleteCrosswalkEvent) => void;
33
+ onUnmergeContributor?: (contributorUri: string) => void;
34
+ onUnsubscribeContributor?: (event: UnsubscribeContributorEvent & {contributorUri: string}) => void;
35
+ };
36
+
37
+ const LegendTable = ({
38
+ accentContributors,
39
+ entity,
40
+ entityType,
41
+ contributors,
42
+ selectedAttributeTypes,
43
+ onChangeVisibleCrosswalks,
44
+ readOnly,
45
+ canUnmerge,
46
+ disableUnmerge,
47
+ onAddCrosswalk = noop,
48
+ onEditCrosswalk = noop,
49
+ onDeleteCrosswalk = noop,
50
+ onUnmergeContributor = noop,
51
+ onUnsubscribeContributor = noop
52
+ }: Props) => {
53
+ const styles = useStyles();
54
+
55
+ const rowsData = useMemo(
56
+ () => buildTableRowsData(entity, contributors, selectedAttributeTypes),
57
+ [entity, contributors, selectedAttributeTypes]
58
+ );
59
+ const [expandedByContributor, setExpandedByContributor] = useState<{[contributorUri: string]: string[]}>({});
60
+ const visibleCrosswalks = useMemo(
61
+ () => collectVisibleCrosswalks(rowsData, expandedByContributor),
62
+ [rowsData, expandedByContributor]
63
+ );
64
+ useEffect(() => {
65
+ onChangeVisibleCrosswalks(visibleCrosswalks);
66
+ }, [visibleCrosswalks]);
67
+
68
+ const handleDelete = useCallback(
69
+ (crosswalkId: string) => {
70
+ onDeleteCrosswalk({entityUri: entity?.uri, crosswalkId});
71
+ },
72
+ [entity?.uri, onDeleteCrosswalk]
73
+ );
74
+
75
+ const handleEdit = useCallback(
76
+ (params: Omit<EditCrosswalkEvent, 'entityUri'>) => {
77
+ onEditCrosswalk({...params, entityUri: entity?.uri});
78
+ },
79
+ [entity?.uri, onEditCrosswalk]
80
+ );
81
+
82
+ return (
83
+ <div className={styles.table}>
84
+ {rowsData.map(({contributorUri, crosswalksBySources}) => {
85
+ return (
86
+ <LegendTableRow
87
+ key={contributorUri}
88
+ className={styles.tableRow}
89
+ canUnmerge={canUnmerge}
90
+ contributorUri={contributorUri}
91
+ accentContributor={accentContributors}
92
+ crosswalksBySources={crosswalksBySources}
93
+ disableAddCrosswalk={readOnly || !checkCanCreateCrosswalk(entityType)}
94
+ disableEditCrosswalk={readOnly || !checkCanEditCrosswalk(entityType)}
95
+ disableDeleteCrosswalk={readOnly || !checkCanDeleteCrosswalk(entityType)}
96
+ disableUnmergeContributor={disableUnmerge}
97
+ onAdd={(params) => onAddCrosswalk({...params, entity})}
98
+ onDelete={handleDelete}
99
+ onEdit={handleEdit}
100
+ onUnmerge={() => onUnmergeContributor(contributorUri)}
101
+ onUnsubscribeContributor={onUnsubscribeContributor}
102
+ expandedSources={expandedByContributor[contributorUri]}
103
+ onToggleSourceExpanded={(sourceUri: string) => {
104
+ setExpandedByContributor(
105
+ pipe(
106
+ unless(has(contributorUri), assoc(contributorUri, [])),
107
+ evolve({
108
+ [contributorUri]: ifElse(
109
+ includes(sourceUri),
110
+ without([sourceUri]),
111
+ append(sourceUri)
112
+ )
113
+ })
114
+ )
115
+ );
116
+ }}
117
+ />
118
+ );
119
+ })}
120
+ </div>
121
+ );
122
+ };
123
+
124
+ export default memo(LegendTable);
@@ -0,0 +1,46 @@
1
+ import React from 'react';
2
+ import i18n from 'ui-i18n';
3
+ import Typography from '@mui/material/Typography';
4
+ import VisibilityIcon from '@mui/icons-material/Visibility';
5
+ import {SmallIconButtonWithTooltip, Spacer} from '@reltio/components';
6
+ import {getLastUriPart} from '@reltio/mdm-sdk';
7
+
8
+ import {useStyles} from './styles';
9
+
10
+ type ContributorAction = React.ComponentPropsWithRef<typeof SmallIconButtonWithTooltip> & {
11
+ show: boolean;
12
+ };
13
+
14
+ type Props = {
15
+ contributorUri: string;
16
+ isFocused: boolean;
17
+ focusable: boolean;
18
+ onToggleFocus: () => void;
19
+ contributorActions: ContributorAction[];
20
+ };
21
+
22
+ const ContributorRow = ({contributorUri, focusable, contributorActions, onToggleFocus, isFocused}: Props) => {
23
+ const styles = useStyles({isFocused});
24
+ return (
25
+ <div className={styles.root}>
26
+ <SmallIconButtonWithTooltip
27
+ size="XXS"
28
+ onClick={() => onToggleFocus()}
29
+ className={styles.focusedIcon}
30
+ icon={focusable && VisibilityIcon}
31
+ disabled={!focusable}
32
+ tooltipTitle={focusable && i18n.text('Focus')}
33
+ />
34
+ <Typography variant={'body2'} className={styles.uri}>
35
+ <span className={styles.title}>{i18n.text('Entity ID:')}</span>
36
+ {getLastUriPart(contributorUri)}
37
+ </Typography>
38
+ <Spacer />
39
+ {contributorActions.map(({show, ...buttonProps}, i) => {
40
+ return show && <SmallIconButtonWithTooltip key={i} size="XS" showForDisabled {...buttonProps} />;
41
+ })}
42
+ </div>
43
+ );
44
+ };
45
+
46
+ export default ContributorRow;
@@ -0,0 +1,103 @@
1
+ import React from 'react';
2
+ import {shallow} from 'enzyme';
3
+ import {omit} from 'ramda';
4
+
5
+ import ContributorRow from '../ContributorRow';
6
+ import VisibilityIcon from '@mui/icons-material/Visibility';
7
+ import Typography from '@mui/material/Typography';
8
+ import {SmallIconButtonWithTooltip} from '@reltio/components';
9
+ import UnsubscribeIcon from '@mui/icons-material/Delete';
10
+ import AddIcon from '@mui/icons-material/Add';
11
+
12
+ describe('ContributorRow tests', () => {
13
+ const contributorActions = [
14
+ {
15
+ icon: AddIcon,
16
+ tooltipTitle: 'Add crosswalk',
17
+ onClick: jest.fn(),
18
+ disabled: false,
19
+ show: true
20
+ },
21
+ {
22
+ icon: UnsubscribeIcon,
23
+ tooltipTitle: 'Unmerge',
24
+ onClick: jest.fn(),
25
+ disabled: true,
26
+ show: true
27
+ },
28
+ {
29
+ icon: UnsubscribeIcon,
30
+ tooltipTitle: 'Unsubscribe',
31
+ onClick: jest.fn(),
32
+ disabled: false,
33
+ show: true
34
+ }
35
+ ];
36
+
37
+ const props = {
38
+ contributorUri: 'entities/qwer',
39
+ onToggleFocus: jest.fn(),
40
+ focusable: true,
41
+ isFocused: true,
42
+ contributorActions
43
+ };
44
+
45
+ it('should render correctly', () => {
46
+ const wrapper = shallow(<ContributorRow {...props} />);
47
+ const buttons = wrapper.find(SmallIconButtonWithTooltip);
48
+ expect(buttons).toHaveLength(contributorActions.length + 1);
49
+ expect(buttons.at(0).props()).toMatchObject({
50
+ icon: VisibilityIcon,
51
+ disabled: false,
52
+ tooltipTitle: 'Focus'
53
+ });
54
+ expect(buttons.at(0).prop('icon')).toEqual(VisibilityIcon);
55
+ expect(buttons.at(0).prop('disabled')).toEqual(false);
56
+ expect(wrapper.find(Typography).text()).toBe('Entity ID:qwer');
57
+ contributorActions.forEach((action, i) => {
58
+ expect(buttons.at(i + 1).props()).toMatchObject(omit(['show'], action));
59
+ });
60
+ });
61
+
62
+ it('should not render contributor action if show === false', () => {
63
+ const actions = [
64
+ {
65
+ icon: AddIcon,
66
+ tooltipTitle: 'Add crosswalk',
67
+ onClick: jest.fn(),
68
+ disabled: false,
69
+ show: false
70
+ },
71
+ {
72
+ icon: UnsubscribeIcon,
73
+ tooltipTitle: 'Unmerge',
74
+ onClick: jest.fn(),
75
+ disabled: true,
76
+ show: true
77
+ }
78
+ ];
79
+ const wrapper = shallow(<ContributorRow {...props} contributorActions={actions} />);
80
+ const buttons = wrapper.find(SmallIconButtonWithTooltip);
81
+ expect(buttons).toHaveLength(2);
82
+ expect(buttons.at(0).prop('icon')).toEqual(VisibilityIcon);
83
+ expect(buttons.at(1).props()).toMatchObject(omit(['show'], actions[1]));
84
+ });
85
+
86
+ it('should render disabled eye button without icon if focusable === false', () => {
87
+ const wrapper = shallow(<ContributorRow {...props} focusable={false} />);
88
+ const button = wrapper.find(SmallIconButtonWithTooltip).at(0);
89
+ expect(button.prop('disabled')).toBe(true);
90
+ expect(button.prop('icon')).toBeFalsy();
91
+ });
92
+
93
+ it('should call props.onToggleFocus on focus button click', () => {
94
+ const wrapper = shallow(<ContributorRow {...props} />);
95
+ wrapper.find(SmallIconButtonWithTooltip).at(0).simulate('click');
96
+ expect(props.onToggleFocus).toBeCalled();
97
+ });
98
+
99
+ it('should render disabled focus button when focusable=false', () => {
100
+ const wrapper = shallow(<ContributorRow {...props} focusable={false} />);
101
+ expect(wrapper.find(SmallIconButtonWithTooltip).at(0).prop('disabled')).toBe(true);
102
+ });
103
+ });
@@ -0,0 +1,31 @@
1
+ import {makeStyles} from '@mui/styles';
2
+
3
+ type StylesProps = {
4
+ isFocused?: boolean;
5
+ };
6
+
7
+ export const useStyles = makeStyles((theme) => ({
8
+ root: {
9
+ display: 'flex',
10
+ alignItems: 'center',
11
+ paddingRight: '8px',
12
+ '& button + button': {
13
+ marginLeft: '4px'
14
+ }
15
+ },
16
+ focusedIcon: ({isFocused}: StylesProps) => ({
17
+ width: 28,
18
+ height: 28,
19
+ color: isFocused ? '#000' : 'rgba(0, 0, 0, 0.54)',
20
+ borderRadius: 0
21
+ }),
22
+ title: {
23
+ color: theme.palette.text.secondary,
24
+ paddingLeft: '6px',
25
+ paddingRight: '2px'
26
+ },
27
+ uri: {
28
+ lineHeight: '15px',
29
+ color: theme.palette.text.primary
30
+ }
31
+ }));