@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,25 @@
1
+ import React from 'react';
2
+ import {shallow} from 'enzyme';
3
+ import MenuItem from '@mui/material/MenuItem';
4
+ import {RulesetOption} from '../RulesetOption';
5
+
6
+ describe('RulesetOption tests', () => {
7
+ it('should render all parts correctly', () => {
8
+ const innerRef = React.createRef<HTMLLIElement>();
9
+ const innerProps = {onClick: jest.fn()};
10
+ const data = {
11
+ uri: 'uri1',
12
+ mapping: [],
13
+ label: 'some label',
14
+ description: 'some description'
15
+ };
16
+ const component = shallow(<RulesetOption innerRef={innerRef} innerProps={innerProps} data={data} />);
17
+ const menuItemComponent = component.find(MenuItem);
18
+ expect(menuItemComponent.props()).toMatchObject({
19
+ className: 'root',
20
+ onClick: innerProps.onClick
21
+ });
22
+ expect(menuItemComponent.find('.label').text()).toBe(data.label);
23
+ expect(menuItemComponent.find('.description').text()).toBe(data.description);
24
+ });
25
+ });
@@ -0,0 +1,27 @@
1
+ import {makeStyles} from '@mui/styles';
2
+
3
+ export const useStyles = makeStyles((theme) => ({
4
+ root: {
5
+ display: 'flex',
6
+ flexDirection: 'column',
7
+ alignItems: 'flex-start',
8
+ paddingTop: '14px',
9
+ paddingBottom: '13px',
10
+ borderBottom: '1px solid rgba(0, 0, 0, 0.05)',
11
+ whiteSpace: 'normal',
12
+ '&:last-child': {
13
+ borderBottom: 'none'
14
+ }
15
+ },
16
+ label: {
17
+ marginBottom: '2px',
18
+ fontSize: '16px',
19
+ lineHeight: '24px',
20
+ color: theme.palette.text.primary
21
+ },
22
+ description: {
23
+ fontSize: '13px',
24
+ lineHeight: '15px',
25
+ color: theme.palette.text.secondary
26
+ }
27
+ }));
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+ import {MenuListComponentProps} from 'react-select';
3
+
4
+ export const RulesetsMenuList = <OptionType, IsMulti extends boolean>(
5
+ props: MenuListComponentProps<OptionType, IsMulti>
6
+ ) => {
7
+ const {innerRef, getStyles, children} = props;
8
+ const styles = {...getStyles('menuList', props), maxHeight: '500px', padding: 0};
9
+ return (
10
+ <div ref={innerRef} style={styles}>
11
+ {children}
12
+ </div>
13
+ );
14
+ };
@@ -0,0 +1,91 @@
1
+ import React from 'react';
2
+ import {act} from 'react-dom/test-utils';
3
+ import {shallow} from 'enzyme';
4
+ import {DropDownSelector} from '@reltio/components';
5
+ import {RulesetsSelector} from '../RulesestsSelector';
6
+ import {RulesetOption} from '../RulesetOption/RulesetOption';
7
+ import {RulesetsMenuList} from '../RulesetsMenuList/RulesetsMenuList';
8
+
9
+ describe('RulesetsSelector tests', () => {
10
+ const survivorshipGroups = [
11
+ {
12
+ uri: 'uri1',
13
+ mapping: [],
14
+ label: 'label 1',
15
+ description: 'description 1'
16
+ },
17
+ {
18
+ uri: 'uri2',
19
+ mapping: [],
20
+ label: 'label 2'
21
+ },
22
+ {
23
+ uri: 'uri3',
24
+ mapping: []
25
+ }
26
+ ];
27
+
28
+ it('should render RulesetsSelector with correct props', () => {
29
+ const survivorshipGroupUri = 'uri3';
30
+ const onChange = jest.fn();
31
+
32
+ const component = shallow(
33
+ <RulesetsSelector
34
+ survivorshipGroups={survivorshipGroups}
35
+ currentSurvivorshipGroupUri={survivorshipGroupUri}
36
+ onChange={onChange}
37
+ />
38
+ );
39
+
40
+ expect(component.find(DropDownSelector).props()).toEqual({
41
+ height: 40,
42
+ className: 'root',
43
+ components: {Option: RulesetOption, MenuList: RulesetsMenuList},
44
+ label: 'Rulesets',
45
+ options: [
46
+ {
47
+ description: 'description 1',
48
+ label: 'label 1',
49
+ mapping: [],
50
+ uri: 'uri1'
51
+ },
52
+ {
53
+ label: 'label 2',
54
+ mapping: [],
55
+ uri: 'uri2'
56
+ },
57
+ {
58
+ label: '<No label>',
59
+ mapping: [],
60
+ uri: 'uri3'
61
+ }
62
+ ],
63
+ value: {
64
+ label: '<No label>',
65
+ mapping: [],
66
+ uri: 'uri3'
67
+ },
68
+ onChange: expect.any(Function)
69
+ });
70
+ });
71
+
72
+ it('onChange handler should work correctly', () => {
73
+ const survivorshipGroupUri = 'uri1';
74
+ const onChange = jest.fn();
75
+
76
+ const component = shallow(
77
+ <RulesetsSelector
78
+ survivorshipGroups={survivorshipGroups}
79
+ currentSurvivorshipGroupUri={survivorshipGroupUri}
80
+ onChange={onChange}
81
+ />
82
+ );
83
+
84
+ act(() => {
85
+ const survivorshipGroup = {label: 'label 2', mapping: [], uri: 'uri2'};
86
+ component.find(DropDownSelector).prop('onChange')(survivorshipGroup);
87
+ });
88
+
89
+ expect(onChange).toHaveBeenCalledWith('uri2');
90
+ });
91
+ });
@@ -0,0 +1,7 @@
1
+ import {makeStyles} from '@mui/styles';
2
+
3
+ export const useStyles = makeStyles({
4
+ root: {
5
+ width: '280px'
6
+ }
7
+ });
@@ -0,0 +1,31 @@
1
+ import React from 'react';
2
+ import {useCrosswalkColor, useCrosswalkHighlight} from '@reltio/components';
3
+ import {SankeyLink} from '../../types/SankeyChartData';
4
+
5
+ type Props = {
6
+ link: SankeyLink;
7
+ path: string;
8
+ highlightEnabled: boolean;
9
+ isColored: boolean;
10
+ };
11
+
12
+ export const Link = ({link, path, highlightEnabled, isColored}: Props) => {
13
+ const {crosswalk} = link;
14
+ const color = useCrosswalkColor(crosswalk);
15
+ const {isDimmed, highlightCrosswalk, removeHighlight} = useCrosswalkHighlight(crosswalk);
16
+ return (
17
+ <path
18
+ d={path}
19
+ fill={isColored ? (isDimmed ? 'rgba(0, 0, 0, 0.12)' : color) : '#F0F0F0'}
20
+ opacity={highlightEnabled && isDimmed ? 0.19 : 1}
21
+ onMouseEnter={() => {
22
+ if (highlightEnabled) highlightCrosswalk();
23
+ }}
24
+ onMouseLeave={() => {
25
+ if (highlightEnabled) removeHighlight();
26
+ }}
27
+ />
28
+ );
29
+ };
30
+
31
+ export default Link;
@@ -0,0 +1,37 @@
1
+ import React from 'react';
2
+ import i18n from 'ui-i18n';
3
+ import {LightArrowTooltip} from '@reltio/components';
4
+ import CalendarTodayIcon from '@mui/icons-material/CalendarToday';
5
+ import PersonIcon from '@mui/icons-material/Person';
6
+ import {useStyles} from './styles';
7
+
8
+ type Props = {
9
+ user: string;
10
+ time: number;
11
+ children: React.ReactElement;
12
+ };
13
+
14
+ export const MergeTooltip = ({user, time, children}: Props) => {
15
+ const styles = useStyles();
16
+ const tooltipTitle = (
17
+ <div>
18
+ <span className={styles.tooltipTitle}>{i18n.text('Merge details')}</span>
19
+ <div className={styles.tooltipRow}>
20
+ <CalendarTodayIcon className={styles.tooltipIcon} />
21
+ {i18n.date(time, 'L LTS')}
22
+ </div>
23
+ <div className={styles.tooltipRow}>
24
+ <PersonIcon className={styles.tooltipIcon} />
25
+ {user}
26
+ </div>
27
+ </div>
28
+ );
29
+
30
+ return (
31
+ <LightArrowTooltip title={tooltipTitle} placement={'top'}>
32
+ {children}
33
+ </LightArrowTooltip>
34
+ );
35
+ };
36
+
37
+ export default MergeTooltip;
@@ -0,0 +1,137 @@
1
+ import React, {MouseEvent, useEffect, useRef, useState} from 'react';
2
+ import i18n from 'ui-i18n';
3
+ import Popover from '@mui/material/Popover';
4
+ import MergeTooltip from './MergeTooltip';
5
+ import {SankeyNode} from '../../types/SankeyChartData';
6
+ import {SmallIconButtonWithTooltip, noop, useCrosswalkColor, useCrosswalkHighlight} from '@reltio/components';
7
+ import {groupBy, prop, pipe, map, head, last, max, min} from 'ramda';
8
+ import UnmergeIcon from '../../icons/notMatch.svg';
9
+ import {useStyles} from './styles';
10
+
11
+ type Props = {
12
+ nodeWidth: number;
13
+ node: SankeyNode;
14
+ highlightEnabled: boolean;
15
+ isColored: boolean;
16
+ disableUnmerge: boolean;
17
+ onUnmerge: (contributorUri: string) => void;
18
+ isDragging: boolean;
19
+ };
20
+
21
+ export const Node = ({node, nodeWidth, highlightEnabled, isColored, disableUnmerge, onUnmerge, isDragging}: Props) => {
22
+ const styles = useStyles();
23
+ const [labelX, setLabelX] = useState(0);
24
+ const [anchorEl, setAnchorEl] = useState(null);
25
+ const textRef = useRef(null);
26
+ const {
27
+ x,
28
+ y,
29
+ name,
30
+ label,
31
+ targetLinks,
32
+ sourceLinks,
33
+ entity: {crosswalks = [], merges}
34
+ } = node;
35
+ const [firstCrosswalk] = crosswalks;
36
+ const {isDimmed, highlightCrosswalk, removeHighlight} = useCrosswalkHighlight(firstCrosswalk);
37
+ const color = useCrosswalkColor(firstCrosswalk);
38
+ const startNodeHeight = crosswalks?.length
39
+ ? sourceLinks[crosswalks.length - 1].sy + sourceLinks[crosswalks.length - 1].dy
40
+ : 0;
41
+
42
+ const renderMergeNodes = () => {
43
+ const timeMap = pipe(map(prop('source')), groupBy(prop('time')))(targetLinks);
44
+
45
+ return merges?.map(({time, user}, index) => {
46
+ const sources = timeMap[time];
47
+ if (!sources) return;
48
+
49
+ const [start, end] = sources.reduce(
50
+ (acc, {sourceLinks}) => [
51
+ min(head(sourceLinks).ty, acc[0]),
52
+ max(last(sourceLinks).ty + last(sourceLinks).dy, acc[1])
53
+ ],
54
+ [Infinity, 0]
55
+ );
56
+
57
+ return (
58
+ <MergeTooltip user={user} time={time} key={'node-' + index}>
59
+ <rect
60
+ transform={`translate(0,${start})`}
61
+ height={end - start}
62
+ width={nodeWidth}
63
+ className={styles.node}
64
+ fillOpacity={0.54}
65
+ fill={'#F0F0F0'}
66
+ opacity={highlightEnabled && isDimmed ? 0.38 : 1}
67
+ />
68
+ </MergeTooltip>
69
+ );
70
+ });
71
+ };
72
+
73
+ const openPopup = (e: MouseEvent<SVGRectElement>) => setAnchorEl(e.currentTarget);
74
+ const closePopup = () => setAnchorEl(null);
75
+ const handleUnmerge = () => {
76
+ onUnmerge(name);
77
+ closePopup();
78
+ };
79
+
80
+ useEffect(() => {
81
+ const nodeHeight = node.dy;
82
+ const textWidth = textRef?.current?.clientWidth || 0;
83
+ if (nodeHeight < textWidth) setLabelX(nodeHeight / 2 - textWidth / 2);
84
+ }, []);
85
+
86
+ useEffect(() => {
87
+ if (isDragging && !!anchorEl) closePopup();
88
+ }, [isDragging]);
89
+
90
+ return (
91
+ <g transform={`translate(${x},${y})`}>
92
+ {name === 'ROOT' ? (
93
+ <text dy={-5} dx={labelX} ref={textRef} className={styles.rootNode}>
94
+ {label || ''}
95
+ </text>
96
+ ) : (
97
+ <>
98
+ <rect
99
+ height={startNodeHeight}
100
+ width={14}
101
+ className={styles.node}
102
+ fill={isColored ? (isDimmed ? 'rgba(0, 0, 0, 0.12)' : color) : '#F0F0F0'}
103
+ key={'node-main'}
104
+ opacity={highlightEnabled && isDimmed ? 0.19 : 1}
105
+ onMouseEnter={() => {
106
+ if (highlightEnabled) highlightCrosswalk();
107
+ }}
108
+ onMouseLeave={() => {
109
+ if (highlightEnabled) removeHighlight();
110
+ }}
111
+ onClick={disableUnmerge ? noop : openPopup}
112
+ cursor={disableUnmerge ? 'default' : 'pointer'}
113
+ data-reltio-id="reltio-sankey-chart-node"
114
+ />
115
+ <Popover
116
+ open={!!anchorEl}
117
+ anchorEl={anchorEl}
118
+ anchorOrigin={{vertical: 'top', horizontal: 'right'}}
119
+ onClose={closePopup}
120
+ classes={{paper: styles.popupContainer}}
121
+ data-reltio-id="reltio-unmerge-popup"
122
+ >
123
+ <SmallIconButtonWithTooltip
124
+ size="XS"
125
+ tooltipTitle={i18n.text('Unmerge')}
126
+ icon={UnmergeIcon}
127
+ onClick={handleUnmerge}
128
+ />
129
+ </Popover>
130
+ {!!targetLinks?.length && renderMergeNodes()}
131
+ </>
132
+ )}
133
+ </g>
134
+ );
135
+ };
136
+
137
+ export default Node;
@@ -0,0 +1,103 @@
1
+ import React, {useMemo, useState, useEffect} from 'react';
2
+ import useSankey from './useSankey';
3
+ import Node from './Node';
4
+ import Link from './Link';
5
+ import {SankeyData} from '../../types/SankeyChartData';
6
+
7
+ type Props = {
8
+ width: number;
9
+ height: number;
10
+ data: SankeyData;
11
+ accentContributors?: boolean;
12
+ disableUnmerge: boolean;
13
+ onUnmerge: (contributorUri: string) => void;
14
+ };
15
+
16
+ const SankeyChart = ({width, height, data, accentContributors, onUnmerge, disableUnmerge}: Props) => {
17
+ const [isDragging, setDragging] = useState(false);
18
+ const [isMouseDown, setMouseDown] = useState(false);
19
+ const [previousPageX, setPreviousPageX] = useState(0);
20
+ const [offsetX, setOffsetX] = useState(0);
21
+ const {links, nodes, nodeWidth, path} = useSankey({data, width, height});
22
+
23
+ useEffect(() => {
24
+ setOffsetX(0);
25
+ setPreviousPageX(0);
26
+ }, [width, height]);
27
+
28
+ const sankeyNodes = useMemo(
29
+ () =>
30
+ nodes.map((node, index) => (
31
+ <Node
32
+ highlightEnabled={accentContributors}
33
+ isColored={accentContributors}
34
+ node={node}
35
+ nodeWidth={nodeWidth}
36
+ key={`SankeyNode-${index}`}
37
+ disableUnmerge={disableUnmerge}
38
+ onUnmerge={onUnmerge}
39
+ isDragging={isDragging}
40
+ />
41
+ )),
42
+ [nodes, accentContributors, nodeWidth, width, height, isDragging]
43
+ );
44
+
45
+ const sankeyLinks = useMemo(
46
+ () =>
47
+ links.map((link, index) => (
48
+ <Link
49
+ highlightEnabled={!accentContributors}
50
+ isColored={!accentContributors}
51
+ link={link}
52
+ path={path(link)}
53
+ key={`SankeyLink-${index}`}
54
+ />
55
+ )),
56
+ [accentContributors, links, path]
57
+ );
58
+
59
+ const extractPositionDelta = (event: React.MouseEvent) => {
60
+ const left = event.pageX;
61
+ const offsetX = left - previousPageX;
62
+ setPreviousPageX(left);
63
+ return offsetX;
64
+ };
65
+
66
+ const onDown = (event: React.MouseEvent) => {
67
+ event.preventDefault();
68
+ setMouseDown(true);
69
+ extractPositionDelta(event);
70
+ };
71
+ const onMove = (event: React.MouseEvent) => {
72
+ if (!isMouseDown) return;
73
+ setDragging(true);
74
+ const left = extractPositionDelta(event);
75
+ setOffsetX(offsetX + left);
76
+ };
77
+ const onUp = () => {
78
+ setMouseDown(false);
79
+ setDragging(false);
80
+ };
81
+
82
+ const pointerEvents = isDragging ? 'none' : 'auto';
83
+ const cursor = isDragging ? 'move' : 'auto';
84
+
85
+ return (
86
+ <svg
87
+ width={width}
88
+ height={height}
89
+ cursor={cursor}
90
+ onMouseDown={onDown}
91
+ onMouseMove={onMove}
92
+ onMouseUp={onUp}
93
+ onMouseLeave={onUp}
94
+ >
95
+ <g transform={`translate(${offsetX},${height - 1}) rotate(-90)`} pointerEvents={pointerEvents}>
96
+ <g>{sankeyLinks}</g>
97
+ <g>{sankeyNodes}</g>
98
+ </g>
99
+ </svg>
100
+ );
101
+ };
102
+
103
+ export default SankeyChart;
@@ -0,0 +1,127 @@
1
+ import React from 'react';
2
+ import userEvent from '@testing-library/user-event';
3
+ import {render} from '@testing-library/react';
4
+ import {Link} from '../Link';
5
+ import * as reltioComponents from '@reltio/components';
6
+
7
+ jest.mock('@reltio/components', () => ({
8
+ ...jest.requireActual<Record<string, unknown>>('@reltio/components'),
9
+ useCrosswalkColor: jest.fn(),
10
+ useCrosswalkHighlight: jest.fn()
11
+ }));
12
+
13
+ describe('Sankey link', () => {
14
+ const highlightCrosswalk = jest.fn();
15
+ const removeHighlight = jest.fn();
16
+ let user;
17
+
18
+ beforeAll(() => {
19
+ jest.spyOn(reltioComponents, 'useCrosswalkColor').mockReturnValue('#FF0000');
20
+ jest.spyOn(reltioComponents, 'useCrosswalkHighlight').mockReturnValue({
21
+ isHighlighted: true,
22
+ isDimmed: false,
23
+ highlightCrosswalk,
24
+ removeHighlight
25
+ });
26
+ });
27
+
28
+ beforeEach(() => {
29
+ user = userEvent.setup({delay: null});
30
+ });
31
+
32
+ const props = {
33
+ link: {
34
+ value: 10,
35
+ source: {
36
+ entity: {uri: 'testUri'},
37
+ name: 'test',
38
+ time: 1234
39
+ },
40
+ target: {
41
+ entity: {uri: 'test1Uri'},
42
+ name: 'test1',
43
+ time: 1234
44
+ },
45
+ crosswalk: {type: 'testType', uri: 'crosswalkUri', value: '20'}
46
+ },
47
+ path: 'M10 10 H 90 V 90 H 10 L 10 10',
48
+ highlightEnabled: false,
49
+ isColored: false
50
+ };
51
+
52
+ it('should render Link correctly', () => {
53
+ const {asFragment} = render(<Link {...props} />);
54
+ expect(asFragment()).toMatchInlineSnapshot(`
55
+ <DocumentFragment>
56
+ <path
57
+ d="M10 10 H 90 V 90 H 10 L 10 10"
58
+ fill="#F0F0F0"
59
+ opacity="1"
60
+ />
61
+ </DocumentFragment>
62
+ `);
63
+ });
64
+
65
+ it('should not work highlighting if highlightEnabled = false', async () => {
66
+ const {container} = render(<Link {...props} />);
67
+ const path = container.querySelector('path');
68
+ await user.hover(path);
69
+ expect(highlightCrosswalk).toBeCalledTimes(0);
70
+ await user.unhover(path);
71
+ expect(removeHighlight).toBeCalledTimes(0);
72
+ });
73
+
74
+ it('should work highlighting if highlightEnabled = true', async () => {
75
+ const {container} = render(<Link {...props} highlightEnabled={true} />);
76
+ const path = container.querySelector('path');
77
+ await user.hover(path);
78
+ expect(highlightCrosswalk).toBeCalledTimes(1);
79
+ await user.unhover(path);
80
+ expect(removeHighlight).toBeCalledTimes(1);
81
+ });
82
+
83
+ it('should not color link if isColored = false', () => {
84
+ const {asFragment} = render(<Link {...props} isColored={false} />);
85
+ expect(asFragment()).toMatchInlineSnapshot(`
86
+ <DocumentFragment>
87
+ <path
88
+ d="M10 10 H 90 V 90 H 10 L 10 10"
89
+ fill="#F0F0F0"
90
+ opacity="1"
91
+ />
92
+ </DocumentFragment>
93
+ `);
94
+ });
95
+
96
+ it('should not color link if isColored = true', () => {
97
+ const {asFragment} = render(<Link {...props} isColored={true} />);
98
+ expect(asFragment()).toMatchInlineSnapshot(`
99
+ <DocumentFragment>
100
+ <path
101
+ d="M10 10 H 90 V 90 H 10 L 10 10"
102
+ fill="#FF0000"
103
+ opacity="1"
104
+ />
105
+ </DocumentFragment>
106
+ `);
107
+ });
108
+
109
+ it('should be dimmed if isDimmed and highlightCrosswalk = true', () => {
110
+ jest.spyOn(reltioComponents, 'useCrosswalkHighlight').mockReturnValue({
111
+ isHighlighted: true,
112
+ isDimmed: true,
113
+ highlightCrosswalk,
114
+ removeHighlight
115
+ });
116
+ const {asFragment} = render(<Link {...props} highlightEnabled={true} isColored={true} />);
117
+ expect(asFragment()).toMatchInlineSnapshot(`
118
+ <DocumentFragment>
119
+ <path
120
+ d="M10 10 H 90 V 90 H 10 L 10 10"
121
+ fill="rgba(0, 0, 0, 0.12)"
122
+ opacity="0.19"
123
+ />
124
+ </DocumentFragment>
125
+ `);
126
+ });
127
+ });
@@ -0,0 +1,22 @@
1
+ import React from 'react';
2
+ import {shallow} from 'enzyme';
3
+ import {LightArrowTooltip} from '@reltio/components';
4
+ import {MergeTooltip} from '../MergeTooltip';
5
+
6
+ describe('MergeTooltip', () => {
7
+ it('should render MergeTooltip', () => {
8
+ const wrapper = shallow(
9
+ <MergeTooltip user={'TestUser'} time={1620711408828}>
10
+ <span>Text</span>
11
+ </MergeTooltip>
12
+ );
13
+
14
+ expect(wrapper.find('span').text()).toBe('Text');
15
+
16
+ const title = wrapper.find(LightArrowTooltip).prop('title') as React.ReactElement;
17
+ const titleWrapper = shallow(title);
18
+
19
+ const rows = titleWrapper.find('.tooltipRow');
20
+ expect(rows.at(1).text()).toBe('TestUser');
21
+ });
22
+ });