Skip to content

Commit 3fa2422

Browse files
authored
fix/COMPASS-9935 fix callbacks (#168)
1 parent c42d569 commit 3fa2422

17 files changed

+396
-161
lines changed

src/components/canvas/canvas.test.tsx

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
import userEvent from '@testing-library/user-event';
2+
13
import { Canvas } from '@/components/canvas/canvas';
24
import { render, screen } from '@/mocks/testing-utils';
35
import { EMPLOYEES_NODE, ORDERS_NODE } from '@/mocks/datasets/nodes';
46
import { EMPLOYEES_TO_ORDERS_EDGE } from '@/mocks/datasets/edges';
57

8+
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
9+
610
describe('canvas', () => {
711
it('Should have elements on the canvas', () => {
812
render(
@@ -15,4 +19,90 @@ describe('canvas', () => {
1519
expect(screen.getByText('orders')).toBeInTheDocument();
1620
expect(screen.getByText('employees')).toBeInTheDocument();
1721
});
22+
23+
describe('callbacks', () => {
24+
it('Should call onNodeContextMenu when a node is right-clicked', async () => {
25+
const onNodeContextMenu = vi.fn();
26+
render(
27+
<Canvas
28+
title={'MongoDB Diagram'}
29+
nodes={[ORDERS_NODE, EMPLOYEES_NODE]}
30+
edges={[EMPLOYEES_TO_ORDERS_EDGE]}
31+
onNodeContextMenu={onNodeContextMenu}
32+
/>,
33+
);
34+
const orders = screen.getByText('orders');
35+
await userEvent.pointer({ target: orders, keys: '[MouseRight]' });
36+
expect(onNodeContextMenu).toHaveBeenCalledTimes(1);
37+
expect(onNodeContextMenu.mock.calls[0][1]).toMatchObject(ORDERS_NODE);
38+
});
39+
40+
// Disabled because the drag simulation is not working. Leaving this here in case someone figures it out.
41+
// From Compass e2e tests we know that react flow requires the drag to take a certain time
42+
// (with webdriverio we were able to add not only pauses but also set the duration of the drag itself).
43+
// Test onSelectionDragStop should be added as well.
44+
it.skip('Should call the drag callbacks when a node is being dragged', async () => {
45+
const onNodeDrag = vi.fn();
46+
const onNodeDragStop = vi.fn();
47+
render(
48+
<Canvas
49+
title={'MongoDB Diagram'}
50+
nodes={[ORDERS_NODE, EMPLOYEES_NODE]}
51+
edges={[EMPLOYEES_TO_ORDERS_EDGE]}
52+
onNodeDrag={onNodeDrag}
53+
onNodeDragStop={onNodeDragStop}
54+
/>,
55+
);
56+
const orders = screen.getByText('orders');
57+
const rect = orders.getBoundingClientRect();
58+
await userEvent.pointer({ target: orders, keys: '[MouseLeft>]' });
59+
await sleep(100);
60+
await userEvent.pointer({ coords: { x: rect.x + 120, y: rect.y + 120 } });
61+
await sleep(100);
62+
await userEvent.pointer({ coords: { x: rect.x + 130, y: rect.y + 130 } });
63+
await sleep(100);
64+
await userEvent.pointer({ keys: '[/MouseLeft]' });
65+
const { position: _position, ...nodeWithoutPosition } = ORDERS_NODE;
66+
67+
expect(onNodeDrag).toHaveBeenCalled();
68+
expect(onNodeDrag.mock.calls[0][1]).toMatchObject(nodeWithoutPosition);
69+
70+
expect(onNodeDragStop).toHaveBeenCalledTimes(1);
71+
expect(onNodeDragStop.mock.calls[0][1]).toMatchObject(nodeWithoutPosition);
72+
});
73+
74+
it('Should call onNodeClick when a node is clicked', async () => {
75+
const onNodeClick = vi.fn();
76+
render(
77+
<Canvas
78+
title={'MongoDB Diagram'}
79+
nodes={[ORDERS_NODE, EMPLOYEES_NODE]}
80+
edges={[EMPLOYEES_TO_ORDERS_EDGE]}
81+
onNodeClick={onNodeClick}
82+
/>,
83+
);
84+
const orders = screen.getByText('orders');
85+
orders.click();
86+
expect(onNodeClick).toHaveBeenCalledTimes(1);
87+
expect(onNodeClick.mock.calls[0][1]).toMatchObject(ORDERS_NODE);
88+
});
89+
90+
it('Should call onSelectionChange when different nodes are clicked', async () => {
91+
const onSelectionChange = vi.fn();
92+
render(
93+
<Canvas
94+
title={'MongoDB Diagram'}
95+
nodes={[ORDERS_NODE, EMPLOYEES_NODE]}
96+
edges={[EMPLOYEES_TO_ORDERS_EDGE]}
97+
onSelectionChange={onSelectionChange}
98+
/>,
99+
);
100+
screen.getByText('orders').click();
101+
screen.getByText('employees').click();
102+
screen.getByText('employees').click(); // for unknown reason there needs to be an extra click in the test
103+
expect(onSelectionChange.mock.calls[0][0]).toMatchObject({ nodes: [] });
104+
expect(onSelectionChange.mock.calls[1][0]).toMatchObject({ nodes: [ORDERS_NODE] });
105+
expect(onSelectionChange.mock.calls[2][0]).toMatchObject({ nodes: [EMPLOYEES_NODE] });
106+
});
107+
});
18108
});

src/components/canvas/canvas.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { SelfReferencingEdge } from '@/components/edge/self-referencing-edge';
2020
import { FieldEdge } from '@/components/edge/field-edge';
2121
import { MarkerList } from '@/components/markers/marker-list';
2222
import { ConnectionLine } from '@/components/line/connection-line';
23-
import { getExternalNode, convertToInternalNodes } from '@/utilities/convert-nodes';
23+
import { convertToExternalNode, convertToExternalNodes, convertToInternalNodes } from '@/utilities/convert-nodes';
2424
import { convertToExternalEdge, convertToExternalEdges, convertToInternalEdges } from '@/utilities/convert-edges';
2525
import { EditableDiagramInteractionsProvider } from '@/hooks/use-editable-diagram-interactions';
2626

@@ -95,28 +95,28 @@ export const Canvas = ({
9595

9696
const _onNodeContextMenu = useCallback(
9797
(event: MouseEvent, node: InternalNode) => {
98-
onNodeContextMenu?.(event, getExternalNode(node));
98+
onNodeContextMenu?.(event, convertToExternalNode(node));
9999
},
100100
[onNodeContextMenu],
101101
);
102102

103103
const _onNodeDrag = useCallback(
104104
(event: MouseEvent, node: InternalNode, nodes: InternalNode[]) => {
105-
onNodeDrag?.(event, getExternalNode(node), nodes.map(getExternalNode));
105+
onNodeDrag?.(event, convertToExternalNode(node), convertToExternalNodes(nodes));
106106
},
107107
[onNodeDrag],
108108
);
109109

110110
const _onNodeDragStop = useCallback(
111111
(event: MouseEvent, node: InternalNode, nodes: InternalNode[]) => {
112-
onNodeDragStop?.(event, getExternalNode(node), nodes.map(getExternalNode));
112+
onNodeDragStop?.(event, convertToExternalNode(node), convertToExternalNodes(nodes));
113113
},
114114
[onNodeDragStop],
115115
);
116116

117117
const _onSelectionDragStop = useCallback(
118118
(event: MouseEvent, nodes: InternalNode[]) => {
119-
onSelectionDragStop?.(event, nodes.map(getExternalNode));
119+
onSelectionDragStop?.(event, convertToExternalNodes(nodes));
120120
},
121121
[onSelectionDragStop],
122122
);
@@ -130,21 +130,21 @@ export const Canvas = ({
130130

131131
const _onNodeClick = useCallback(
132132
(event: MouseEvent, node: InternalNode) => {
133-
onNodeClick?.(event, getExternalNode(node));
133+
onNodeClick?.(event, convertToExternalNode(node));
134134
},
135135
[onNodeClick],
136136
);
137137

138138
const _onSelectionContextMenu = useCallback(
139139
(event: MouseEvent, nodes: InternalNode[]) => {
140-
onSelectionContextMenu?.(event, nodes.map(getExternalNode));
140+
onSelectionContextMenu?.(event, convertToExternalNodes(nodes));
141141
},
142142
[onSelectionContextMenu],
143143
);
144144

145145
const _onSelectionChange = useCallback(
146146
({ nodes, edges }: { nodes: InternalNode[]; edges: InternalEdge[] }) => {
147-
onSelectionChange?.({ nodes: nodes.map(getExternalNode), edges: convertToExternalEdges(edges) });
147+
onSelectionChange?.({ nodes: convertToExternalNodes(nodes), edges: convertToExternalEdges(edges) });
148148
},
149149
[onSelectionChange],
150150
);

src/components/edge/field-edge.test.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,14 @@ describe('field-edge', () => {
2525
...ORDERS_NODE,
2626
data: {
2727
title: ORDERS_NODE.title,
28-
visibleFields: ORDERS_NODE.fields.map(field => ({ ...field, hasChildren: false })),
29-
externalNode: ORDERS_NODE,
28+
fields: ORDERS_NODE.fields.map(field => ({ ...field, hasChildren: false, isVisible: true })),
3029
},
3130
},
3231
{
3332
...EMPLOYEES_NODE,
3433
data: {
3534
title: EMPLOYEES_NODE.title,
36-
visibleFields: EMPLOYEES_NODE.fields.map(field => ({ ...field, hasChildren: false })),
37-
externalNode: EMPLOYEES_NODE,
35+
fields: EMPLOYEES_NODE.fields.map(field => ({ ...field, hasChildren: false, isVisible: true })),
3836
},
3937
},
4038
];

src/components/edge/floating-edge.test.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,14 @@ describe('floating-edge', () => {
2626
...ORDERS_NODE,
2727
data: {
2828
title: ORDERS_NODE.title,
29-
visibleFields: ORDERS_NODE.fields.map(field => ({ ...field, hasChildren: false })),
30-
externalNode: ORDERS_NODE,
29+
fields: ORDERS_NODE.fields.map(field => ({ ...field, hasChildren: false, isVisible: true })),
3130
},
3231
},
3332
{
3433
...EMPLOYEES_NODE,
3534
data: {
3635
title: EMPLOYEES_NODE.title,
37-
visibleFields: EMPLOYEES_NODE.fields.map(field => ({ ...field, hasChildren: false })),
38-
externalNode: EMPLOYEES_NODE,
36+
fields: EMPLOYEES_NODE.fields.map(field => ({ ...field, hasChildren: false, isVisible: true })),
3937
},
4038
},
4139
];

src/components/edge/self-referencing-edge.test.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@ describe('self-referencing-edge', () => {
2727
...EMPLOYEES_NODE,
2828
data: {
2929
title: EMPLOYEES_NODE.title,
30-
visibleFields: EMPLOYEES_NODE.fields.map(field => ({ ...field, hasChildren: false })),
31-
externalNode: EMPLOYEES_NODE,
30+
fields: EMPLOYEES_NODE.fields.map(field => ({ ...field, hasChildren: false, isVisible: true })),
3231
},
3332
},
3433
];

src/components/field/field-list.test.tsx

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,14 @@ describe('field-list', () => {
3535
<FieldWithEditableInteractions
3636
onFieldClick={onFieldClick}
3737
fields={[
38-
{ name: 'field-with-just-name', selectable: true, hasChildren: false },
39-
{ id: ['field', 'with', 'id'], name: 'and-custom-name', selectable: true, hasChildren: false },
38+
{ name: 'field-with-just-name', selectable: true, hasChildren: false, isVisible: true },
39+
{
40+
id: ['field', 'with', 'id'],
41+
name: 'and-custom-name',
42+
selectable: true,
43+
hasChildren: false,
44+
isVisible: true,
45+
},
4046
]}
4147
/>,
4248
);
@@ -64,10 +70,10 @@ describe('field-list', () => {
6470
<FieldWithEditableInteractions
6571
onFieldExpandToggle={onFieldExpandToggle}
6672
fields={[
67-
{ id: ['other'], name: 'other', hasChildren: false },
68-
{ id: ['parent'], name: 'parent', expanded: true, hasChildren: true },
69-
{ id: ['parent', 'child1'], name: 'child1', depth: 1, hasChildren: false },
70-
{ id: ['parent', 'child2'], name: 'child2', depth: 1, hasChildren: false },
73+
{ id: ['other'], name: 'other', hasChildren: false, isVisible: true },
74+
{ id: ['parent'], name: 'parent', expanded: true, hasChildren: true, isVisible: true },
75+
{ id: ['parent', 'child1'], name: 'child1', depth: 1, hasChildren: false, isVisible: true },
76+
{ id: ['parent', 'child2'], name: 'child2', depth: 1, hasChildren: false, isVisible: true },
7177
]}
7278
/>,
7379
);
@@ -76,4 +82,21 @@ describe('field-list', () => {
7682
expect(screen.queryByTestId('field-expand-toggle-coll-parent-child1')).not.toBeInTheDocument();
7783
expect(screen.queryByTestId('field-expand-toggle-coll-parent-child2')).not.toBeInTheDocument();
7884
});
85+
86+
it('should only display visible fields', () => {
87+
render(
88+
<FieldWithEditableInteractions
89+
fields={[
90+
{ id: ['other'], name: 'other', hasChildren: false, isVisible: true },
91+
{ id: ['parent'], name: 'parent', expanded: false, hasChildren: true, isVisible: true },
92+
{ id: ['parent', 'child1'], name: 'child1', depth: 1, hasChildren: false, isVisible: false },
93+
{ id: ['parent', 'child2'], name: 'child2', depth: 1, hasChildren: false, isVisible: false },
94+
]}
95+
/>,
96+
);
97+
expect(screen.getByText('other')).toBeInTheDocument();
98+
expect(screen.getByText('parent')).toBeInTheDocument();
99+
expect(screen.queryByText('child1')).not.toBeInTheDocument();
100+
expect(screen.queryByText('child2')).not.toBeInTheDocument();
101+
});
79102
});

src/components/field/field-list.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,14 @@ interface Props {
2121
fields: InternalNodeField[];
2222
}
2323

24-
export const FieldList = ({ fields, nodeId, nodeType, isHovering }: Props) => {
24+
export const FieldList = ({ fields: _fields, nodeId, nodeType, isHovering }: Props) => {
2525
const { onClickField } = useEditableDiagramInteractions();
2626
const isFieldSelectionEnabled = !!onClickField;
2727

28+
const fields = useMemo(() => {
29+
return _fields.filter(field => field.isVisible);
30+
}, [_fields]);
31+
2832
const spacing = Math.max(0, ...fields.map(field => field.glyphs?.length || 0));
2933
const previewGroupArea = useMemo(() => getPreviewGroupArea(fields), [fields]);
3034
const selectedGroupHeight = useMemo(() => {

0 commit comments

Comments
 (0)