Skip to content

Commit ac24c87

Browse files
authored
fix(ui): align editor subblocks and combobox styling (#8049)
* fix(ui): align fallback model subblock styling * fix(ui): keep editor pickers on shared combobox styling
1 parent a854709 commit ac24c87

10 files changed

Lines changed: 76 additions & 63 deletions

File tree

‎apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/knowledge-base-selector/knowledge-base-selector.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client'
22

33
import { useCallback, useMemo } from 'react'
4-
import { ChipCombobox, ChipTag, type ComboboxOption } from '@sim/emcn'
4+
import { ChipTag, Combobox, type ComboboxOption } from '@sim/emcn'
55
import { X } from '@sim/emcn/icons'
66
import { useQueries } from '@tanstack/react-query'
77
import { useParams } from 'next/navigation'
@@ -211,7 +211,7 @@ export function KnowledgeBaseSelector({
211211
</div>
212212
)}
213213

214-
<ChipCombobox
214+
<Combobox
215215
options={options}
216216
value={isMultiSelect ? undefined : (selectedIds[0] ?? '')}
217217
multiSelect={isMultiSelect}

‎apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/mcp-server-modal/mcp-server-selector.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client'
22

3-
import { ChipCombobox } from '@sim/emcn'
3+
import { Combobox } from '@sim/emcn'
44
import { useParams } from 'next/navigation'
55
import { McpIcon } from '@/components/icons'
66
import { getMcpTargetOptions } from '@/components/mcp/target-options'
@@ -65,7 +65,7 @@ export function McpServerSelector({
6565
})
6666

6767
return (
68-
<ChipCombobox
68+
<Combobox
6969
options={comboboxOptions}
7070
value={inputValue}
7171
selectedValue={selectedServerId}

‎apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/mcp-server-modal/mcp-tool-selector.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client'
22

33
import { useMemo } from 'react'
4-
import { ChipCombobox } from '@sim/emcn'
4+
import { Combobox } from '@sim/emcn'
55
import { useParams } from 'next/navigation'
66
import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/formatted-text'
77
import { getWorkflowSearchLabelHighlight } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/workflow-search-highlight'
@@ -86,7 +86,7 @@ export function McpToolSelector({
8686
})
8787

8888
return (
89-
<ChipCombobox
89+
<Combobox
9090
options={comboboxOptions}
9191
value={inputValue}
9292
selectedValue={selectedToolId}

‎apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/model-fallback-list/model-fallback-list.test.tsx‎

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ vi.mock('next/navigation', () => ({
3030
}))
3131

3232
vi.mock('@sim/emcn', () => ({
33+
Button: ({ children, ...props }: React.ButtonHTMLAttributes<HTMLButtonElement>) => (
34+
<button {...props}>{children}</button>
35+
),
3336
Chip: ({
3437
children,
3538
disabled,
@@ -43,7 +46,7 @@ vi.mock('@sim/emcn', () => ({
4346
{children}
4447
</button>
4548
),
46-
ChipCombobox: ({
49+
Combobox: ({
4750
options,
4851
value,
4952
placeholder,
@@ -60,21 +63,6 @@ vi.mock('@sim/emcn', () => ({
6063
))}
6164
</div>
6265
),
63-
ChipDropdown: ({
64-
options,
65-
value,
66-
placeholder,
67-
}: {
68-
options: Array<{ value: string; label: string }>
69-
value?: string
70-
placeholder?: string
71-
}) => (
72-
<div data-combobox={placeholder} data-value={value}>
73-
{options.map((option) => (
74-
<span key={option.value}>{option.label}</span>
75-
))}
76-
</div>
77-
),
7866
Label: ({ children }: { children?: React.ReactNode }) => <span>{children}</span>,
7967
Tooltip: {
8068
Root: ({ children }: { children?: React.ReactNode }) => <>{children}</>,

‎apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/model-fallback-list/model-fallback-list.tsx‎

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client'
22

33
import { memo, useCallback, useEffect, useMemo, useRef } from 'react'
4-
import { Chip, ChipCombobox, ChipDropdown, type ComboboxOption, Label, Tooltip } from '@sim/emcn'
4+
import { Button, Chip, Combobox, type ComboboxOption, Label, Tooltip } from '@sim/emcn'
55
import { ChevronDown, ChevronUp, Plus, Trash } from '@sim/emcn/icons'
66
import { generateShortId } from '@sim/utils/id'
77
import { useParams } from 'next/navigation'
@@ -123,55 +123,64 @@ const FallbackRow = memo(function FallbackRow({
123123
const apiKeyValue = isWholeEnvVarReference(row.apiKey) ? row.apiKey : ''
124124

125125
return (
126-
<div
127-
data-fallback-row-id={row.id}
128-
className='space-y-3 rounded-lg border border-[var(--border)] p-3'
129-
>
130-
<div className='flex items-center justify-between'>
131-
<span className='text-[var(--text-muted)] text-small'>{ordinalChoiceLabel(index)}</span>
132-
<div className='flex items-center'>
126+
<div data-fallback-row-id={row.id} className='min-w-0 rounded-sm border border-[var(--border)]'>
127+
<div className='flex items-center justify-between gap-2 rounded-t-[4px] bg-[var(--surface-4)] px-2 py-1'>
128+
<span className='text-[var(--text-secondary)] text-small'>{ordinalChoiceLabel(index)}</span>
129+
<div className='flex shrink-0 items-center gap-1'>
133130
{canMove && (
134131
<>
135132
<Tooltip.Root>
136133
<Tooltip.Trigger asChild>
137-
<Chip
138-
leftIcon={ChevronUp}
134+
<Button
135+
type='button'
136+
variant='ghost'
137+
size='icon'
139138
onClick={() => onMove(row.id, -1)}
140139
disabled={readOnly || index === 0}
141140
aria-label='Move up'
142-
/>
141+
>
142+
<ChevronUp className='size-[14px]' />
143+
</Button>
143144
</Tooltip.Trigger>
144145
<Tooltip.Content>Move up</Tooltip.Content>
145146
</Tooltip.Root>
146147
<Tooltip.Root>
147148
<Tooltip.Trigger asChild>
148-
<Chip
149-
leftIcon={ChevronDown}
149+
<Button
150+
type='button'
151+
variant='ghost'
152+
size='icon'
150153
onClick={() => onMove(row.id, 1)}
151154
disabled={readOnly || isLast}
152155
aria-label='Move down'
153-
/>
156+
>
157+
<ChevronDown className='size-[14px]' />
158+
</Button>
154159
</Tooltip.Trigger>
155160
<Tooltip.Content>Move down</Tooltip.Content>
156161
</Tooltip.Root>
157162
</>
158163
)}
159164
<Tooltip.Root>
160165
<Tooltip.Trigger asChild>
161-
<Chip
162-
leftIcon={Trash}
166+
<Button
167+
type='button'
168+
variant='ghost'
169+
size='icon'
163170
onClick={() => onRemove(row.id)}
164171
disabled={readOnly}
165172
aria-label='Remove fallback model'
166-
/>
173+
>
174+
<Trash className='size-[14px]' />
175+
</Button>
167176
</Tooltip.Trigger>
168177
<Tooltip.Content>Remove</Tooltip.Content>
169178
</Tooltip.Root>
170179
</div>
171180
</div>
172181

173-
<div className='flex flex-col gap-3'>
174-
<ChipCombobox
182+
<div className='flex flex-col gap-2.5 rounded-b-[4px] border-[var(--border)] border-t bg-[var(--surface-2)] p-2'>
183+
<Combobox
175184
options={modelOptions}
176185
value={row.model}
177186
onChange={(model) => onChangeModel(row.id, model)}
@@ -184,9 +193,9 @@ const FallbackRow = memo(function FallbackRow({
184193
emptyMessage='No models available'
185194
/>
186195
{needsApiKey && (
187-
<div className='flex flex-col gap-1.5'>
188-
<Label>API key</Label>
189-
<ChipCombobox
196+
<div className='flex flex-col gap-2.5'>
197+
<Label className='pl-0.5'>API key</Label>
198+
<Combobox
190199
options={envVarOptions}
191200
value={apiKeyValue}
192201
onChange={(apiKey) => onChangeApiKey(row.id, apiKey)}
@@ -201,9 +210,9 @@ const FallbackRow = memo(function FallbackRow({
201210
</div>
202211
)}
203212
{tuningFields.map(({ knob, options }) => (
204-
<div key={knob} className='flex flex-col gap-1.5'>
205-
<Label>{FALLBACK_TUNING_LABELS[knob]}</Label>
206-
<ChipDropdown
213+
<div key={knob} className='flex flex-col gap-2.5'>
214+
<Label className='pl-0.5'>{FALLBACK_TUNING_LABELS[knob]}</Label>
215+
<Combobox
207216
options={options}
208217
value={row[knob] ?? options[0]?.value ?? ''}
209218
onChange={(value) => onChangeTuning(row.id, knob, value)}
@@ -387,7 +396,7 @@ export function ModelFallbackList({
387396
)
388397

389398
return (
390-
<div className='space-y-2'>
399+
<div className='flex w-full min-w-0 flex-col gap-2.5'>
391400
{rows.map((row, index) => (
392401
<FallbackRow
393402
key={row.id}
@@ -410,9 +419,8 @@ export function ModelFallbackList({
410419
))}
411420
{!readOnly && (
412421
<Chip
413-
variant='outline'
414422
leftIcon={Plus}
415-
fullWidth
423+
className='self-start'
416424
onClick={handleAdd}
417425
disabled={rows.length >= MAX_FALLBACK_MODELS}
418426
>

‎apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/workspace-folder-selector/workspace-folder-selector.test.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ vi.mock('next/navigation', () => ({
2020
}))
2121

2222
vi.mock('@sim/emcn', () => ({
23-
ChipCombobox: ({
23+
Combobox: ({
2424
options,
2525
disabled,
2626
isLoading,

‎apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/workspace-folder-selector/workspace-folder-selector.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client'
22

33
import { useMemo } from 'react'
4-
import { ChipCombobox } from '@sim/emcn'
4+
import { Combobox } from '@sim/emcn'
55
import { useParams } from 'next/navigation'
66
import type { FolderResourceType } from '@/lib/api/contracts/folders'
77
import { parseFolderPath } from '@/lib/folders/paths'
@@ -58,7 +58,7 @@ export function WorkspaceFolderSelector({
5858
}, [folders, isPlaceholderData, required, subBlock.multiSelect, subBlock.placeholder])
5959

6060
return (
61-
<ChipCombobox
61+
<Combobox
6262
options={options}
6363
value={subBlock.multiSelect ? undefined : selected}
6464
onChange={(value) => {

‎apps/sim/components/mcp/mcp-selectors.test.tsx‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
/** @vitest-environment node */
22
import type { ComponentProps } from 'react'
3-
import type { ChipCombobox } from '@sim/emcn'
3+
import type { Combobox } from '@sim/emcn'
44
import { renderToStaticMarkup } from 'react-dom/server'
55
import { beforeEach, describe, expect, it, vi } from 'vitest'
66

77
const mocks = vi.hoisted(() => ({
8-
combobox: vi.fn<(props: ComponentProps<typeof ChipCombobox>) => null>(() => null),
8+
combobox: vi.fn<(props: ComponentProps<typeof Combobox>) => null>(() => null),
99
setValue: vi.fn(),
1010
longInput: vi.fn(() => null),
1111
shortInput: vi.fn(() => null),
@@ -38,7 +38,7 @@ vi.mock('@/stores/workflow-diff/store', () => ({
3838
},
3939
}),
4040
}))
41-
vi.mock('@sim/emcn', () => ({ ChipCombobox: mocks.combobox, Label: () => null }))
41+
vi.mock('@sim/emcn', () => ({ Combobox: mocks.combobox, Label: () => null }))
4242
vi.mock(
4343
'@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/long-input/long-input',
4444
() => ({ LongInput: mocks.longInput })

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,22 @@ afterEach(() => {
7676
})
7777

7878
describe('Combobox onOpenChange', () => {
79+
it('preserves trigger styling when an editable input adds padding for a clear button', () => {
80+
render(
81+
<Combobox
82+
options={OPTIONS}
83+
editable
84+
className='min-w-0'
85+
inputProps={{ className: 'pr-[60px]' }}
86+
/>
87+
)
88+
89+
const control = trigger()
90+
expect(control.classList.contains('min-w-0')).toBe(true)
91+
expect(control.classList.contains('pr-[60px]')).toBe(true)
92+
expect(control.classList.contains('pr-10')).toBe(false)
93+
})
94+
7995
it.each([false, true])(
8096
'puts the field name and validation ARIA on the interactive control (editable=%s)',
8197
(editable) => {

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

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -790,13 +790,6 @@ const Combobox = memo(
790790
<div className='group relative'>
791791
<Input
792792
ref={inputRef}
793-
className={cn(
794-
'w-full pr-10 transition-colors',
795-
(overlayContent || SelectedIcon) && 'text-transparent caret-foreground',
796-
SelectedIcon && !overlayContent && 'pl-7',
797-
open && 'focus-visible:border-[var(--border-1)]',
798-
className
799-
)}
800793
placeholder={placeholder}
801794
value={value ?? ''}
802795
onChange={handleInputChange}
@@ -810,6 +803,14 @@ const Combobox = memo(
810803
aria-required={ariaRequired}
811804
aria-invalid={ariaInvalid}
812805
{...inputProps}
806+
className={cn(
807+
'w-full pr-10 transition-colors',
808+
(overlayContent || SelectedIcon) && 'text-transparent caret-foreground',
809+
SelectedIcon && !overlayContent && 'pl-7',
810+
open && 'focus-visible:border-[var(--border-1)]',
811+
className,
812+
inputProps?.className
813+
)}
813814
role='combobox'
814815
aria-expanded={open}
815816
aria-haspopup='listbox'

0 commit comments

Comments
 (0)