Skip to content

Commit 32bb7bb

Browse files
feat(apollo-react): some fixes
1 parent 8942487 commit 32bb7bb

13 files changed

Lines changed: 699 additions & 63 deletions

File tree

packages/apollo-react/src/canvas/components/AddNodePanel/AddNodeManager.test.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,33 @@ describe('AddNodeManager', () => {
195195
);
196196
});
197197

198+
it('preserves parented preview placement when materializing the selected node', () => {
199+
mockNodes = [
200+
existingNode,
201+
{
202+
...previewNode,
203+
parentId: 'loop-1',
204+
extent: 'parent',
205+
},
206+
];
207+
mockPreviewNodeReturn.mockReturnValue({
208+
previewNode: mockNodes[1],
209+
previewNodeConnectionInfo: connectionInfo,
210+
});
211+
212+
render(<AddNodeManager customPanel={TestPanel} />);
213+
214+
screen.getByTestId('select-node').click();
215+
216+
const addedNode = mockNodes.find((n) => n.id === 'test-node-1234567890');
217+
expect(addedNode).toMatchObject({
218+
id: 'test-node-1234567890',
219+
parentId: 'loop-1',
220+
extent: 'parent',
221+
position: { x: 100, y: 200 },
222+
});
223+
});
224+
198225
it('applies onBeforeNodeAdded transforms when node is added as target', () => {
199226
// Override shared setup for this test: existing node is the source
200227
mockEdges = [

packages/apollo-react/src/canvas/components/ButtonHandle/ButtonHandle.styles.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,17 +167,25 @@ export const StyledNotch = styled.div<{
167167

168168
export const StyledHandle = (
169169
props: HandleProps & {
170+
$visualPosition?: Position;
170171
$positionPercent: number;
171172
$total: number;
172173
$visible: boolean;
173174
$customPositionAndOffsets?: HandleConfigurationSpecificPosition;
174175
}
175176
) => {
176-
const { $total, $visible, $positionPercent, $customPositionAndOffsets, ...handleProps } = props;
177-
const { position } = handleProps;
177+
const {
178+
$total,
179+
$visible,
180+
$positionPercent,
181+
$customPositionAndOffsets,
182+
$visualPosition,
183+
...handleProps
184+
} = props;
185+
const visualPosition = $visualPosition ?? handleProps.position;
178186

179187
const { width, height, top, bottom, left, right, transform } = useButtonHandleSizeAndPosition({
180-
position,
188+
position: visualPosition,
181189
positionPercent: $positionPercent,
182190
numHandles: $total,
183191
customPositionAndOffsets: $customPositionAndOffsets,

packages/apollo-react/src/canvas/components/ButtonHandle/ButtonHandle.test.tsx

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,51 @@ describe('ButtonHandles', () => {
118118
expect.objectContaining({
119119
handleId: 'handle1',
120120
nodeId: 'test-node',
121+
position: Position.Right,
122+
})
123+
);
124+
});
125+
126+
it('uses visual positioning for layout and connectionPosition for emitted flow direction', async () => {
127+
const user = userEvent.setup();
128+
const handleClick = vi.fn();
129+
const handles: ButtonHandleConfig[] = [
130+
{
131+
id: 'handle1',
132+
type: 'source',
133+
handleType: 'output',
134+
showButton: true,
135+
onAction: handleClick,
136+
},
137+
];
138+
139+
render(
140+
<ButtonHandles
141+
handles={handles}
142+
nodeId="test-node"
143+
position={Position.Left}
144+
connectionPosition={Position.Right}
145+
selected={true}
146+
visible={true}
147+
/>
148+
);
149+
150+
const handle = screen.getByTestId('handle');
151+
expect(handle).toHaveAttribute('position', Position.Right);
152+
expect(handle).toHaveStyle({ left: '0' });
153+
154+
const animatedButton = handle.querySelector('.nodrag.nopan > *');
155+
if (!animatedButton) {
156+
throw new Error('Animated button not found');
157+
}
158+
159+
await user.click(animatedButton as Element);
160+
161+
expect(handleClick).toHaveBeenCalledWith(
162+
expect.objectContaining({
163+
handleId: 'handle1',
164+
nodeId: 'test-node',
165+
position: Position.Right,
121166
})
122167
);
123168
});

packages/apollo-react/src/canvas/components/ButtonHandle/ButtonHandle.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ type ButtonHandleProps = {
5858
nodeId: string;
5959
type: 'source' | 'target';
6060
position: Position;
61+
connectionPosition?: Position;
6162
handleType: 'artifact' | 'input' | 'output';
6263
label?: string;
6364
labelIcon?: React.ReactNode;
@@ -80,6 +81,7 @@ const ButtonHandleBase = ({
8081
nodeId,
8182
type,
8283
position,
84+
connectionPosition = position,
8385
handleType,
8486
label,
8587
labelIcon,
@@ -124,7 +126,7 @@ const ButtonHandleBase = ({
124126
handleId: id,
125127
nodeId,
126128
handleType,
127-
position,
129+
position: connectionPosition,
128130
originalEvent: event,
129131
};
130132

@@ -136,11 +138,11 @@ const ButtonHandleBase = ({
136138
handleId: id,
137139
nodeId,
138140
handleType,
139-
position,
141+
position: connectionPosition,
140142
// timestamp: Date.now(), // Optional - uncomment if you need timing info
141143
});
142144
},
143-
[id, nodeId, handleType, position, onAction]
145+
[connectionPosition, id, nodeId, handleType, onAction]
144146
);
145147

146148
const markAsHovered = useCallback(() => setIsHovered(true), []);
@@ -153,8 +155,9 @@ const ButtonHandleBase = ({
153155
return (
154156
<StyledHandle
155157
type={type}
156-
position={position}
158+
position={connectionPosition}
157159
id={id}
160+
$visualPosition={position}
158161
$positionPercent={positionPercent}
159162
$total={total}
160163
$visible={visible}
@@ -237,6 +240,7 @@ const ButtonHandlesBase = ({
237240
nodeId,
238241
handles,
239242
position,
243+
connectionPosition = position,
240244
selected = false,
241245
visible = true,
242246
showAddButton = true,
@@ -249,6 +253,7 @@ const ButtonHandlesBase = ({
249253
nodeId: string;
250254
handles: ButtonHandleConfig[];
251255
position: Position;
256+
connectionPosition?: Position;
252257
selected?: boolean;
253258
visible?: boolean;
254259
showAddButton?: boolean;
@@ -288,6 +293,7 @@ const ButtonHandlesBase = ({
288293
nodeId={nodeId}
289294
type={handle.type}
290295
position={position}
296+
connectionPosition={connectionPosition}
291297
handleType={handle.handleType}
292298
label={handle.label}
293299
labelIcon={handle.labelIcon}

packages/apollo-react/src/canvas/components/Edges/SequenceEdge.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { type EdgeProps, Position } from '@uipath/apollo-react/canvas/xyflow/react';
22
import { memo, useRef, useState } from 'react';
3-
import { PREVIEW_EDGE_ID } from '../../constants';
3+
import { PREVIEW_EDGE_ID, PREVIEW_NODE_ID } from '../../constants';
44
import { useEdgeExecutionState, useEdgePath, useElementValidationStatus } from '../../hooks';
55
import type { NodeExecutionStateWithDebug } from '../../types/execution';
66
import { useBaseCanvasMode } from '../BaseCanvas/BaseCanvasModeProvider';
@@ -71,7 +71,7 @@ export const SequenceEdge = memo(function SequenceEdge({
7171

7272
const { mode } = useBaseCanvasMode();
7373
const isReadOnly = mode === 'readonly';
74-
const isPreviewEdge = id === PREVIEW_EDGE_ID;
74+
const isPreviewEdge = id === PREVIEW_EDGE_ID || source === PREVIEW_NODE_ID || target === PREVIEW_NODE_ID;
7575

7676
const executionStatus = useEdgeExecutionState(id, target);
7777
const { validationStatus } = useElementValidationStatus(id) ?? { validationStatus: undefined };

packages/apollo-react/src/canvas/components/LoopNode/LoopNode.helpers.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,21 @@ describe('LoopNode.helpers', () => {
1010
it('builds groups for populated walls and insets inner groups', () => {
1111
const groups = buildWallHandleGroups(
1212
{
13-
left: [{ id: 'start', type: 'target', handleType: 'input' }],
14-
right: [{ id: 'continue', type: 'source', handleType: 'output' }],
13+
left: [{ id: 'start', type: 'source', handleType: 'output' }],
14+
right: [{ id: 'continue', type: 'target', handleType: 'input' }],
1515
},
1616
'inner'
1717
);
1818

1919
expect(groups).toEqual([
2020
{
2121
position: 'left',
22-
handles: [{ id: 'start', type: 'target', handleType: 'input' }],
22+
handles: [{ id: 'start', type: 'source', handleType: 'output' }],
2323
customPositionAndOffsets: { left: 16 },
2424
},
2525
{
2626
position: 'right',
27-
handles: [{ id: 'continue', type: 'source', handleType: 'output' }],
27+
handles: [{ id: 'continue', type: 'target', handleType: 'input' }],
2828
customPositionAndOffsets: { right: 16 },
2929
},
3030
]);

0 commit comments

Comments
 (0)