Skip to content

Commit 3a51850

Browse files
authored
fix(workflow): preserve canvas and deploy modal behavior (#7392)
1 parent 65a58a8 commit 3a51850

8 files changed

Lines changed: 56 additions & 6 deletions

File tree

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/components/output-select/output-select.test.tsx

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,14 @@ vi.mock('@sim/emcn', () => ({
6464
groups,
6565
multiSelectValues,
6666
onMultiSelectChange,
67+
disablePortal,
6768
}: {
6869
groups: Array<{ section?: string; items: Array<{ label: string; value: string }> }>
6970
multiSelectValues?: string[]
7071
onMultiSelectChange?: (values: string[]) => void
72+
disablePortal?: boolean
7173
}) => (
72-
<div data-chip-combobox>
74+
<div data-chip-combobox data-disable-portal={disablePortal || undefined}>
7375
{groups.flatMap((group) =>
7476
group.items.map((option) => (
7577
<button
@@ -201,14 +203,23 @@ function outputSelect(
201203
function renderOutputSelect(
202204
selectedOutputs: string[],
203205
onOutputSelect = vi.fn(),
204-
valueMode: 'id' | 'label' | 'public' = 'id'
206+
valueMode: 'id' | 'label' | 'public' = 'id',
207+
props: { size?: 'sm' | 'md'; disablePortal?: boolean } = {}
205208
) {
206209
;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true
207210
container = document.createElement('div')
208211
document.body.appendChild(container)
209212
root = createRoot(container)
210213
act(() => {
211-
root?.render(outputSelect('root', selectedOutputs, onOutputSelect, valueMode))
214+
root?.render(
215+
<OutputSelect
216+
workflowId='root'
217+
selectedOutputs={selectedOutputs}
218+
onOutputSelect={onOutputSelect}
219+
valueMode={valueMode}
220+
{...props}
221+
/>
222+
)
212223
})
213224
return onOutputSelect
214225
}
@@ -255,6 +266,15 @@ describe('OutputSelect nested workflow menu', () => {
255266
expect(document.body.textContent).not.toContain('Summarizer')
256267
})
257268

269+
it('forwards inline dropdown rendering to the chip combobox', () => {
270+
renderOutputSelect([], vi.fn(), 'id', { size: 'md', disablePortal: true })
271+
272+
expect(container.querySelector('[data-chip-combobox]')).toHaveAttribute(
273+
'data-disable-portal',
274+
'true'
275+
)
276+
})
277+
258278
it('keeps workflow-scoped values when toggling nested outputs', () => {
259279
const onOutputSelect = renderOutputSelect([])
260280
clickOption('Outputs')

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/components/output-select/output-select.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ interface OutputSelectProps {
4848
align?: 'start' | 'end' | 'center'
4949
/** Maximum height of the dropdown content in pixels */
5050
maxHeight?: number
51+
disablePortal?: boolean
5152
/**
5253
* Trigger chrome. `'sm'` is the compact pill used in inline toolbars;
5354
* `'md'` is the 30px chip field, for stacking with `ChipInput` in a form.
@@ -68,6 +69,7 @@ interface OutputSelectMenuProps {
6869
valueMode: 'id' | 'label' | 'public'
6970
align: 'start' | 'end' | 'center'
7071
maxHeight: number
72+
disablePortal: boolean
7173
size: 'sm' | 'md'
7274
className?: string
7375
}
@@ -125,6 +127,7 @@ function OutputSelectContent({
125127
valueMode = 'id',
126128
align = 'start',
127129
maxHeight = 200,
130+
disablePortal = false,
128131
size = 'sm',
129132
className,
130133
}: OutputSelectProps) {
@@ -228,6 +231,7 @@ function OutputSelectContent({
228231
valueMode={valueMode}
229232
align={align}
230233
maxHeight={maxHeight}
234+
disablePortal={disablePortal}
231235
size={size}
232236
className={className}
233237
/>
@@ -244,6 +248,7 @@ function OutputSelectMenu({
244248
valueMode,
245249
align,
246250
maxHeight,
251+
disablePortal,
247252
size,
248253
className,
249254
}: OutputSelectMenuProps) {
@@ -337,6 +342,7 @@ function OutputSelectMenu({
337342
align={align}
338343
maxHeight={maxHeight}
339344
dropdownWidth={180}
345+
disablePortal={disablePortal}
340346
/>
341347
)
342348
}

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/chat/chat.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,7 @@ export function ChatDeploy({
390390
disabled={chatSubmitting}
391391
size='md'
392392
className='w-full'
393+
disablePortal
393394
/>
394395
{errors.outputBlocks && (
395396
<p className='mt-[6.5px] text-[var(--text-error)] text-caption'>

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3265,6 +3265,8 @@ const WorkflowContent = React.memo(
32653265
const workflowChanges = changes.filter(
32663266
(change) => !('id' in change) || change.id !== CONNECTION_BLOCK_SELECTOR_NODE_ID
32673267
)
3268+
if (workflowChanges.length === 0) return
3269+
32683270
const hasSelectionChange = workflowChanges.some((c) => c.type === 'select')
32693271
setDisplayNodes((currentNodes) => {
32703272
// Filter out cross-context selection changes before applying so that
@@ -4855,8 +4857,8 @@ const WorkflowContent = React.memo(
48554857
pendingConnect,
48564858
onClose: closeConnectionBlockSelector,
48574859
},
4858-
width: CONNECTION_BLOCK_SELECTOR_DIMENSIONS.width,
4859-
height: CONNECTION_BLOCK_SELECTOR_DIMENSIONS.height,
4860+
initialWidth: CONNECTION_BLOCK_SELECTOR_DIMENSIONS.width,
4861+
initialHeight: CONNECTION_BLOCK_SELECTOR_DIMENSIONS.height,
48604862
zIndex: CONNECTION_PICKER_Z,
48614863
dragHandle: '.workflow-drag-handle',
48624864
draggable: true,

packages/emcn/src/components/chip-modal/chip-modal.test.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,16 @@ describe('ChipConfirmModal pending', () => {
270270
})
271271
})
272272

273+
describe('ChipModalBody', () => {
274+
it('scrolls vertically without exposing incidental horizontal overflow', () => {
275+
mount(<ChipModalBody data-testid='modal-body'>Content</ChipModalBody>)
276+
277+
const body = document.querySelector<HTMLElement>('[data-testid="modal-body"]')
278+
expect(body?.className).toContain('overflow-x-hidden')
279+
expect(body?.className).toContain('overflow-y-auto')
280+
})
281+
})
282+
273283
describe('ChipModal default actions', () => {
274284
beforeEach(makeElementsVisible)
275285

packages/emcn/src/components/chip-modal/chip-modal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ const ChipModalBody = React.forwardRef<HTMLDivElement, ChipModalBodyProps>(
414414
ref={ref}
415415
className={cn(
416416
'flex min-h-0 flex-1 flex-col',
417-
fullBleed ? 'overflow-hidden' : 'gap-4 overflow-y-auto px-2 pt-4 pb-4.5',
417+
fullBleed ? 'overflow-hidden' : 'gap-4 overflow-y-auto overflow-x-hidden px-2 pt-4 pb-4.5',
418418
className
419419
)}
420420
{...props}

packages/emcn/src/components/combobox/combobox.dom.test.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,14 @@ afterEach(() => {
6464
})
6565

6666
describe('Combobox onOpenChange', () => {
67+
it('renders the dropdown inside the component subtree when portals are disabled', () => {
68+
render(<Combobox options={OPTIONS} disablePortal />)
69+
70+
click(trigger())
71+
72+
expect(container?.querySelector('[role="listbox"]')).not.toBeNull()
73+
})
74+
6775
it('uses the overlay label for the interactive overflow layer', () => {
6876
render(
6977
<Combobox

packages/emcn/src/components/combobox/combobox.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ export interface ComboboxProps
146146
align?: 'start' | 'center' | 'end'
147147
/** Dropdown width - 'trigger' matches trigger width, or provide a pixel value */
148148
dropdownWidth?: 'trigger' | number
149+
disablePortal?: boolean
149150
/** Show an "All" option at the top that clears selection (multi-select only) */
150151
showAllOption?: boolean
151152
/** Custom label for the "All" option (default: "All") */
@@ -208,6 +209,7 @@ const Combobox = memo(
208209
searchPlaceholder = 'Search...',
209210
align = 'start',
210211
dropdownWidth = 'trigger',
212+
disablePortal = false,
211213
showAllOption = false,
212214
allOptionLabel = 'All',
213215
groups,
@@ -879,6 +881,7 @@ const Combobox = memo(
879881
</PopoverAnchor>
880882

881883
<PopoverContent
884+
disablePortal={disablePortal}
882885
side='bottom'
883886
align={align}
884887
sideOffset={4}

0 commit comments

Comments
 (0)