diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx index 07e40430b56..385b81ecb7f 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx @@ -30,6 +30,7 @@ import { isSubBlockFeatureEnabled, isSubBlockHidden, isSubBlockVisibleForMode, + isTriggerModeSubBlock, resolveDependencyValue, } from '@/lib/workflows/subblocks/visibility' import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider' @@ -539,14 +540,14 @@ export const WorkflowBlock = memo(function WorkflowBlock({ if (effectiveTrigger) { const isValidTriggerSubblock = isPureTriggerBlock - ? block.mode === 'trigger' || !block.mode - : block.mode === 'trigger' + ? isTriggerModeSubBlock(block) || !block.mode + : isTriggerModeSubBlock(block) if (!isValidTriggerSubblock) { return false } } else { - if (block.mode === 'trigger') { + if (isTriggerModeSubBlock(block)) { return false } } diff --git a/apps/sim/lib/workflows/autolayout/utils.test.ts b/apps/sim/lib/workflows/autolayout/utils.test.ts index b8cb51abe74..c4e84e22db9 100644 --- a/apps/sim/lib/workflows/autolayout/utils.test.ts +++ b/apps/sim/lib/workflows/autolayout/utils.test.ts @@ -1,13 +1,18 @@ /** * @vitest-environment node */ -import { describe, expect, it, vi } from 'vitest' +import { beforeEach, describe, expect, it, vi } from 'vitest' import { DEFAULT_VERTICAL_SPACING } from '@/lib/workflows/autolayout/constants' -import { resolveNoteOverlaps } from '@/lib/workflows/autolayout/utils' +import { getBlockMetrics, resolveNoteOverlaps } from '@/lib/workflows/autolayout/utils' +import type { getBlock } from '@/blocks' import type { BlockState } from '@/stores/workflows/workflow/types' +const { mockGetBlock } = vi.hoisted(() => ({ + mockGetBlock: vi.fn(), +})) + vi.mock('@/blocks', () => ({ - getBlock: () => null, + getBlock: mockGetBlock, })) function createBlock( @@ -29,6 +34,10 @@ function createBlock( } as BlockState } +beforeEach(() => { + mockGetBlock.mockReturnValue(null) +}) + describe('resolveNoteOverlaps', () => { it('relocates a note that overlaps a laid-out block', () => { const blocks: Record = { @@ -236,3 +245,73 @@ describe('resolveNoteOverlaps', () => { }) }) }) + +describe('getBlockMetrics preview row estimation', () => { + /** + * Mirrors a block that spreads a trigger's subBlocks after its own, + * producing duplicate canonical pair entries with trigger/trigger-advanced + * modes (e.g. the Table block spreading the table_new_row trigger). + */ + const tableLikeConfig = { + category: 'blocks', + subBlocks: [ + { id: 'operation', title: 'Operation', type: 'dropdown' }, + { + id: 'tableSelector', + title: 'Table', + type: 'table-selector', + mode: 'basic', + canonicalParamId: 'tableId', + }, + { + id: 'manualTableId', + title: 'Table ID', + type: 'short-input', + mode: 'advanced', + canonicalParamId: 'tableId', + }, + { id: 'data', title: 'Row Data (JSON)', type: 'code' }, + { + id: 'tableSelector', + title: 'Table', + type: 'table-selector', + mode: 'trigger', + canonicalParamId: 'tableId', + }, + { + id: 'manualTableId', + title: 'Table ID', + type: 'short-input', + mode: 'trigger-advanced', + canonicalParamId: 'tableId', + }, + { id: 'eventType', title: 'Event', type: 'dropdown', mode: 'trigger' }, + ], + } as unknown as ReturnType + + function createTableBlock(canonicalMode: 'basic' | 'advanced'): BlockState { + return { + id: 'table-1', + type: 'table', + name: 'Table 1', + position: { x: 0, y: 0 }, + subBlocks: { + operation: { id: 'operation', type: 'dropdown', value: 'insert_row' }, + tableSelector: { id: 'tableSelector', type: 'table-selector', value: 'tbl_1' }, + manualTableId: { id: 'manualTableId', type: 'short-input', value: 'tbl_1' }, + }, + outputs: {}, + enabled: true, + data: { canonicalModes: { tableId: canonicalMode } }, + } as unknown as BlockState + } + + it('renders one row per canonical pair regardless of basic/advanced mode', () => { + mockGetBlock.mockReturnValue(tableLikeConfig) + + const basic = getBlockMetrics(createTableBlock('basic')) + const advanced = getBlockMetrics(createTableBlock('advanced')) + + expect(advanced.height).toBe(basic.height) + }) +}) diff --git a/apps/sim/lib/workflows/autolayout/utils.ts b/apps/sim/lib/workflows/autolayout/utils.ts index f25c1086745..bd77abeb980 100644 --- a/apps/sim/lib/workflows/autolayout/utils.ts +++ b/apps/sim/lib/workflows/autolayout/utils.ts @@ -20,6 +20,7 @@ import { isSubBlockFeatureEnabled, isSubBlockHidden, isSubBlockVisibleForMode, + isTriggerModeSubBlock, } from '@/lib/workflows/subblocks/visibility' import { getBlock } from '@/blocks' import type { BlockState } from '@/stores/workflows/workflow/types' @@ -175,13 +176,13 @@ function getVisiblePreviewSubBlockCount(block: BlockState): number { if (effectiveTrigger) { const isValidTriggerSubblock = isPureTriggerBlock - ? subBlock.mode === 'trigger' || !subBlock.mode - : subBlock.mode === 'trigger' + ? isTriggerModeSubBlock(subBlock) || !subBlock.mode + : isTriggerModeSubBlock(subBlock) if (!isValidTriggerSubblock) { return false } - } else if (subBlock.mode === 'trigger') { + } else if (isTriggerModeSubBlock(subBlock)) { return false }