diff --git a/packages/apollo-react/src/canvas/components/AddNodePanel/AddNodeManager.helpers.test.ts b/packages/apollo-react/src/canvas/components/AddNodePanel/AddNodeManager.helpers.test.ts index a92f827ca..07d0ad4f2 100644 --- a/packages/apollo-react/src/canvas/components/AddNodePanel/AddNodeManager.helpers.test.ts +++ b/packages/apollo-react/src/canvas/components/AddNodePanel/AddNodeManager.helpers.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it } from 'vitest'; import { PREVIEW_NODE_ID } from '../../constants'; import type { NodeLayout } from '../../hooks/useCanvasNodeLayout'; import type { NodeShape } from '../../schema/node-definition'; -import { getExpandedSize } from '../../utils/collapse'; +import { getExpandedSize } from '../../utils/node-size'; import { getContainerFitGeometry, getNodeDimensions } from '../../utils/container'; import { placeAddedNode } from './AddNodeManager.helpers'; diff --git a/packages/apollo-react/src/canvas/components/AddNodePanel/AddNodeManager.helpers.ts b/packages/apollo-react/src/canvas/components/AddNodePanel/AddNodeManager.helpers.ts index bdab36b8b..4ca4e6abb 100644 --- a/packages/apollo-react/src/canvas/components/AddNodePanel/AddNodeManager.helpers.ts +++ b/packages/apollo-react/src/canvas/components/AddNodePanel/AddNodeManager.helpers.ts @@ -5,7 +5,7 @@ import type { NodeLayout } from '../../hooks/useCanvasNodeLayout'; import type { PreviewNodeConnectionInfo } from '../../hooks/usePreviewNode'; import type { NodeManifest } from '../../schema'; import { resolveCollisions } from '../../utils'; -import { getExpandedSize } from '../../utils/collapse'; +import { getExpandedSize } from '../../utils/node-size'; import { CONTAINER_SEQUENCE_GAP_PX, collectLinearDownstreamSiblings, diff --git a/packages/apollo-react/src/canvas/components/BaseNode/BaseNode.stories.tsx b/packages/apollo-react/src/canvas/components/BaseNode/BaseNode.stories.tsx index 28a76c7c4..2e0552406 100644 --- a/packages/apollo-react/src/canvas/components/BaseNode/BaseNode.stories.tsx +++ b/packages/apollo-react/src/canvas/components/BaseNode/BaseNode.stories.tsx @@ -1967,6 +1967,30 @@ const stackedManifest: { nodes: NodeManifest[]; categories: CategoryManifest[] } }, ], }, + { + // A collapsed rectangle keeps its shape: it stays a wide pill AND shows the + // stacked layer, rather than shrinking to a square (collapse never squares). + nodeType: 'uipath.agent.rectangle', + version: '1.0.0', + category: 'agents', + tags: ['agent'], + sortOrder: 1, + display: { + label: 'Rectangle Agent', + icon: 'agent', + shape: 'rectangle', + }, + handleConfiguration: [ + { + position: 'left', + handles: [{ id: 'input', type: 'target', handleType: 'input' }], + }, + { + position: 'right', + handles: [{ id: 'output', type: 'source', handleType: 'output' }], + }, + ], + }, ], }; @@ -2004,6 +2028,17 @@ function StackedTreatmentStory() { isCollapsed: true, }, }), + createNode({ + id: 'collapsed-rectangle', + type: 'uipath.agent.rectangle', + position: { x: 768, y: 200 }, + data: { + nodeType: 'uipath.agent.rectangle', + version: '1.0.0', + display: { label: 'Collapsed Rectangle', subLabel: 'stays a pill', shape: 'rectangle' }, + isCollapsed: true, + }, + }), ], [] ); @@ -2016,7 +2051,7 @@ function StackedTreatmentStory() { ); diff --git a/packages/apollo-react/src/canvas/hooks/useCanvasNodeLayout.ts b/packages/apollo-react/src/canvas/hooks/useCanvasNodeLayout.ts index a5208f262..c31e6ff1f 100644 --- a/packages/apollo-react/src/canvas/hooks/useCanvasNodeLayout.ts +++ b/packages/apollo-react/src/canvas/hooks/useCanvasNodeLayout.ts @@ -2,7 +2,7 @@ import type { Node } from '@uipath/apollo-react/canvas/xyflow/react'; import { useCallback, useMemo } from 'react'; import { useOptionalNodeTypeRegistry } from '../core'; import type { NodeManifest } from '../schema/node-definition'; -import { getExpandedSize } from '../utils/collapse'; +import { getExpandedSize } from '../utils/node-size'; import { type ContainerFitGeometry, getContainerFitGeometry as getDefaultContainerFitGeometry, diff --git a/packages/apollo-react/src/canvas/utils/collapse.ts b/packages/apollo-react/src/canvas/utils/collapse.ts deleted file mode 100644 index faf759193..000000000 --- a/packages/apollo-react/src/canvas/utils/collapse.ts +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Collapse/Expand Transformation Helpers - * - * Utilities for transforming node dimensions and shapes between collapsed and expanded states. - * These helpers ensure consistent behavior across the codebase. - */ - -import type { NodeShape } from '../schema/node-definition'; -import { DEFAULT_CONTAINER_HEIGHT, DEFAULT_CONTAINER_WIDTH } from './container'; - -/** - * Default dimension for collapsed nodes when no measured dimensions are available. - */ -export const COLLAPSED_NODE_SIZE = 96; - -/** - * Default width for expanded rectangle nodes. - */ -export const EXPANDED_RECTANGLE_WIDTH = 288; - -/** - * Transform a shape to its collapsed form. - * Rectangle nodes collapse to square, other shapes remain unchanged. - */ -export const getCollapsedShape = (originalShape?: NodeShape): NodeShape | undefined => { - return originalShape === 'rectangle' ? 'square' : originalShape; -}; - -/** - * Transform a collapsed shape back to its expanded form. - * Square nodes expand to rectangle (since rectangle → square when collapsing). - * Other shapes remain unchanged. - */ -export const getExpandedShape = (collapsedShape?: NodeShape): NodeShape | undefined => { - return collapsedShape === 'square' ? 'rectangle' : (collapsedShape ?? undefined); -}; - -/** - * Calculate the collapsed size (square) from original dimensions. - * Uses height as the basis for the square dimension. - */ -export const getCollapsedSize = (): { width: number; height: number } => { - return { - width: COLLAPSED_NODE_SIZE, - height: COLLAPSED_NODE_SIZE, - }; -}; - -/** - * Calculate the expanded size from original dimensions stored in ui.size. - * This is the inverse of getCollapsedSize - it restores the original dimensions - * when expanding a collapsed node. - */ -export const getExpandedSize = (shape?: NodeShape): { width: number; height: number } => { - if (shape === 'container') { - return { - width: DEFAULT_CONTAINER_WIDTH, - height: DEFAULT_CONTAINER_HEIGHT, - }; - } - - return { - width: shape === 'rectangle' ? EXPANDED_RECTANGLE_WIDTH : COLLAPSED_NODE_SIZE, - height: COLLAPSED_NODE_SIZE, - }; -}; diff --git a/packages/apollo-react/src/canvas/utils/index.ts b/packages/apollo-react/src/canvas/utils/index.ts index 978251d7f..b565838e7 100644 --- a/packages/apollo-react/src/canvas/utils/index.ts +++ b/packages/apollo-react/src/canvas/utils/index.ts @@ -4,7 +4,7 @@ export * from './CanvasEventBus'; export * from './CssUtil'; export * from './coded-agents/d3-layout'; export * from './coded-agents/mermaid-parser'; -export * from './collapse'; +export * from './node-size'; export * from './constraint-validator'; export * from './container'; export * from './createPreviewGraph'; diff --git a/packages/apollo-react/src/canvas/utils/manifest-resolver.test.ts b/packages/apollo-react/src/canvas/utils/manifest-resolver.test.ts index 9ccb6dfa6..b9ca59f81 100644 --- a/packages/apollo-react/src/canvas/utils/manifest-resolver.test.ts +++ b/packages/apollo-react/src/canvas/utils/manifest-resolver.test.ts @@ -43,15 +43,15 @@ describe('resolveDisplay', () => { expect(result.shape).toBe('square'); }); - it('collapses rectangle to square when isCollapsed is true', () => { + it('keeps a rectangle shape when collapsed (collapse never squares the node)', () => { const result = resolveDisplay( { label: 'Agent', icon: 'bot', shape: 'rectangle' }, { isCollapsed: true } ); - expect(result.shape).toBe('square'); + expect(result.shape).toBe('rectangle'); }); - it('preserves non-rectangle shapes when collapsed', () => { + it('keeps non-rectangle shapes when collapsed', () => { const result = resolveDisplay( { label: 'Node', icon: 'box', shape: 'circle' }, { isCollapsed: true } @@ -59,6 +59,14 @@ describe('resolveDisplay', () => { expect(result.shape).toBe('circle'); }); + it('honors an instance display.shape override', () => { + const result = resolveDisplay( + { label: 'Node', icon: 'box', shape: 'square' }, + { display: { shape: 'circle' } } + ); + expect(result.shape).toBe('circle'); + }); + it('uses manifest canvasLabel for canvas label when no instance override', () => { const result = resolveDisplay({ label: 'Send Outlook Email', diff --git a/packages/apollo-react/src/canvas/utils/manifest-resolver.ts b/packages/apollo-react/src/canvas/utils/manifest-resolver.ts index 7c19ceab0..784995e33 100644 --- a/packages/apollo-react/src/canvas/utils/manifest-resolver.ts +++ b/packages/apollo-react/src/canvas/utils/manifest-resolver.ts @@ -14,7 +14,6 @@ import type { HandleActionEvent, HandleMouseEvent } from '../components/ButtonHa import type { HandleGroupManifest, HandleManifest } from '../schema/node-definition/handle'; import type { NodeDisplayManifest } from '../schema/node-definition/node-manifest'; import type { InstanceDisplayConfig } from '../schema/node-instance'; -import { getCollapsedShape } from './collapse'; /** * Context object passed to resolution functions @@ -110,12 +109,11 @@ export function resolveDisplay( } as ResolvedDisplay; } - const isCollapsed = context?.isCollapsed ?? false; + // Shape comes from the instance override (if any) or the manifest. Collapsing + // never changes it: a collapsed node hides its artifacts and shows the stacked + // affordance but keeps its footprint. const shape = context?.display?.shape ?? manifestDisplay.shape; - const collapsedShape = getCollapsedShape(shape); - const expandedShape = manifestDisplay.shape; - // Resolve the canvas chip label. // // Order: instance.canvasLabel > instance.label > manifest.canvasLabel > manifest.label. @@ -140,7 +138,7 @@ export function resolveDisplay( label: resolvedLabel, canvasLabel: context?.display?.canvasLabel ?? manifestDisplay.canvasLabel, icon: context?.display?.icon ?? manifestDisplay.icon ?? '', - shape: isCollapsed ? collapsedShape : expandedShape, + shape, }; } diff --git a/packages/apollo-react/src/canvas/utils/node-size.ts b/packages/apollo-react/src/canvas/utils/node-size.ts new file mode 100644 index 000000000..a691c621a --- /dev/null +++ b/packages/apollo-react/src/canvas/utils/node-size.ts @@ -0,0 +1,36 @@ +/** + * Node sizing helpers. + * + * Default pixel dimensions for a node based on its shape, used when a node has + * no measured size yet (creation, layout). + */ + +import type { NodeShape } from '../schema/node-definition'; +import { DEFAULT_CONTAINER_HEIGHT, DEFAULT_CONTAINER_WIDTH } from './container'; + +/** + * Default square dimension for a node with no explicit size. + */ +export const COLLAPSED_NODE_SIZE = 96; + +/** + * Default width for a rectangle (pill) node. + */ +export const EXPANDED_RECTANGLE_WIDTH = 288; + +/** + * Default size for a node of the given shape. + */ +export const getExpandedSize = (shape?: NodeShape): { width: number; height: number } => { + if (shape === 'container') { + return { + width: DEFAULT_CONTAINER_WIDTH, + height: DEFAULT_CONTAINER_HEIGHT, + }; + } + + return { + width: shape === 'rectangle' ? EXPANDED_RECTANGLE_WIDTH : COLLAPSED_NODE_SIZE, + height: COLLAPSED_NODE_SIZE, + }; +};