Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,14 @@ vi.mock('@sim/emcn', () => ({
groups,
multiSelectValues,
onMultiSelectChange,
disablePortal,
}: {
groups: Array<{ section?: string; items: Array<{ label: string; value: string }> }>
multiSelectValues?: string[]
onMultiSelectChange?: (values: string[]) => void
disablePortal?: boolean
}) => (
<div data-chip-combobox>
<div data-chip-combobox data-disable-portal={disablePortal || undefined}>
{groups.flatMap((group) =>
group.items.map((option) => (
<button
Expand Down Expand Up @@ -201,14 +203,23 @@ function outputSelect(
function renderOutputSelect(
selectedOutputs: string[],
onOutputSelect = vi.fn(),
valueMode: 'id' | 'label' | 'public' = 'id'
valueMode: 'id' | 'label' | 'public' = 'id',
props: { size?: 'sm' | 'md'; disablePortal?: boolean } = {}
) {
;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true
container = document.createElement('div')
document.body.appendChild(container)
root = createRoot(container)
act(() => {
root?.render(outputSelect('root', selectedOutputs, onOutputSelect, valueMode))
root?.render(
<OutputSelect
workflowId='root'
selectedOutputs={selectedOutputs}
onOutputSelect={onOutputSelect}
valueMode={valueMode}
{...props}
/>
)
})
return onOutputSelect
}
Expand Down Expand Up @@ -255,6 +266,15 @@ describe('OutputSelect nested workflow menu', () => {
expect(document.body.textContent).not.toContain('Summarizer')
})

it('forwards inline dropdown rendering to the chip combobox', () => {
renderOutputSelect([], vi.fn(), 'id', { size: 'md', disablePortal: true })

expect(container.querySelector('[data-chip-combobox]')).toHaveAttribute(
'data-disable-portal',
'true'
)
})

it('keeps workflow-scoped values when toggling nested outputs', () => {
const onOutputSelect = renderOutputSelect([])
clickOption('Outputs')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ interface OutputSelectProps {
align?: 'start' | 'end' | 'center'
/** Maximum height of the dropdown content in pixels */
maxHeight?: number
disablePortal?: boolean
/**
* Trigger chrome. `'sm'` is the compact pill used in inline toolbars;
* `'md'` is the 30px chip field, for stacking with `ChipInput` in a form.
Expand All @@ -68,6 +69,7 @@ interface OutputSelectMenuProps {
valueMode: 'id' | 'label' | 'public'
align: 'start' | 'end' | 'center'
maxHeight: number
disablePortal: boolean
size: 'sm' | 'md'
className?: string
}
Expand Down Expand Up @@ -125,6 +127,7 @@ function OutputSelectContent({
valueMode = 'id',
align = 'start',
maxHeight = 200,
disablePortal = false,
size = 'sm',
className,
}: OutputSelectProps) {
Expand Down Expand Up @@ -228,6 +231,7 @@ function OutputSelectContent({
valueMode={valueMode}
align={align}
maxHeight={maxHeight}
disablePortal={disablePortal}
size={size}
className={className}
/>
Expand All @@ -244,6 +248,7 @@ function OutputSelectMenu({
valueMode,
align,
maxHeight,
disablePortal,
size,
className,
}: OutputSelectMenuProps) {
Expand Down Expand Up @@ -337,6 +342,7 @@ function OutputSelectMenu({
align={align}
maxHeight={maxHeight}
dropdownWidth={180}
disablePortal={disablePortal}
/>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,7 @@ export function ChatDeploy({
disabled={chatSubmitting}
size='md'
className='w-full'
disablePortal
/>
{errors.outputBlocks && (
<p className='mt-[6.5px] text-[var(--text-error)] text-caption'>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3265,6 +3265,8 @@ const WorkflowContent = React.memo(
const workflowChanges = changes.filter(
(change) => !('id' in change) || change.id !== CONNECTION_BLOCK_SELECTOR_NODE_ID
)
if (workflowChanges.length === 0) return

const hasSelectionChange = workflowChanges.some((c) => c.type === 'select')
setDisplayNodes((currentNodes) => {
// Filter out cross-context selection changes before applying so that
Expand Down Expand Up @@ -4855,8 +4857,8 @@ const WorkflowContent = React.memo(
pendingConnect,
onClose: closeConnectionBlockSelector,
},
width: CONNECTION_BLOCK_SELECTOR_DIMENSIONS.width,
height: CONNECTION_BLOCK_SELECTOR_DIMENSIONS.height,
initialWidth: CONNECTION_BLOCK_SELECTOR_DIMENSIONS.width,
initialHeight: CONNECTION_BLOCK_SELECTOR_DIMENSIONS.height,
zIndex: CONNECTION_PICKER_Z,
dragHandle: '.workflow-drag-handle',
draggable: true,
Expand Down
10 changes: 10 additions & 0 deletions packages/emcn/src/components/chip-modal/chip-modal.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,16 @@ describe('ChipConfirmModal pending', () => {
})
})

describe('ChipModalBody', () => {
it('scrolls vertically without exposing incidental horizontal overflow', () => {
mount(<ChipModalBody data-testid='modal-body'>Content</ChipModalBody>)

const body = document.querySelector<HTMLElement>('[data-testid="modal-body"]')
expect(body?.className).toContain('overflow-x-hidden')
expect(body?.className).toContain('overflow-y-auto')
})
})

describe('ChipModal default actions', () => {
beforeEach(makeElementsVisible)

Expand Down
2 changes: 1 addition & 1 deletion packages/emcn/src/components/chip-modal/chip-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ const ChipModalBody = React.forwardRef<HTMLDivElement, ChipModalBodyProps>(
ref={ref}
className={cn(
'flex min-h-0 flex-1 flex-col',
fullBleed ? 'overflow-hidden' : 'gap-4 overflow-y-auto px-2 pt-4 pb-4.5',
fullBleed ? 'overflow-hidden' : 'gap-4 overflow-y-auto overflow-x-hidden px-2 pt-4 pb-4.5',
className
)}
{...props}
Expand Down
8 changes: 8 additions & 0 deletions packages/emcn/src/components/combobox/combobox.dom.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ afterEach(() => {
})

describe('Combobox onOpenChange', () => {
it('renders the dropdown inside the component subtree when portals are disabled', () => {
render(<Combobox options={OPTIONS} disablePortal />)

click(trigger())

expect(container?.querySelector('[role="listbox"]')).not.toBeNull()
})

it('uses the overlay label for the interactive overflow layer', () => {
render(
<Combobox
Expand Down
3 changes: 3 additions & 0 deletions packages/emcn/src/components/combobox/combobox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ export interface ComboboxProps
align?: 'start' | 'center' | 'end'
/** Dropdown width - 'trigger' matches trigger width, or provide a pixel value */
dropdownWidth?: 'trigger' | number
disablePortal?: boolean
/** Show an "All" option at the top that clears selection (multi-select only) */
showAllOption?: boolean
/** Custom label for the "All" option (default: "All") */
Expand Down Expand Up @@ -208,6 +209,7 @@ const Combobox = memo(
searchPlaceholder = 'Search...',
align = 'start',
dropdownWidth = 'trigger',
disablePortal = false,
showAllOption = false,
allOptionLabel = 'All',
groups,
Expand Down Expand Up @@ -879,6 +881,7 @@ const Combobox = memo(
</PopoverAnchor>

<PopoverContent
disablePortal={disablePortal}
side='bottom'
align={align}
sideOffset={4}
Expand Down
Loading