Skip to content

Commit 1c3a779

Browse files
committed
fix(tables): keep column rename migration atomic
1 parent 4d5daea commit 1c3a779

3 files changed

Lines changed: 28 additions & 48 deletions

File tree

apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/column-config-sidebar/column-config-sidebar.test.tsx

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -102,12 +102,6 @@ function findButton(label: string): HTMLButtonElement | undefined {
102102
)
103103
}
104104

105-
function setInputValue(input: HTMLInputElement, value: string): void {
106-
const valueSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set
107-
valueSetter?.call(input, value)
108-
input.dispatchEvent(new Event('input', { bubbles: true }))
109-
}
110-
111105
beforeEach(() => {
112106
globalThis.IS_REACT_ACT_ENVIRONMENT = true
113107
container = document.createElement('div')
@@ -190,8 +184,7 @@ describe('ColumnConfigSidebar', () => {
190184
expect(mockUpdateColumn).not.toHaveBeenCalled()
191185
})
192186

193-
it('edits a Reference column name and target table together', async () => {
194-
const onColumnRename = vi.fn()
187+
it('edits Reference configuration without exposing column renaming', async () => {
195188
await act(async () => {
196189
root.render(
197190
<ColumnConfigSidebar
@@ -205,27 +198,21 @@ describe('ColumnConfigSidebar', () => {
205198
}}
206199
workspaceId='workspace-1'
207200
tableId='table-current'
208-
onColumnRename={onColumnRename}
209201
referenceColumnsEnabled
210202
/>
211203
)
212204
})
213205

214-
const nameInput = container.querySelector<HTMLInputElement>('#column-sidebar-name')
215-
expect(nameInput?.value).toBe('Related row')
206+
expect(container).not.toHaveTextContent('Column name')
207+
expect(container.querySelector('#column-sidebar-name')).toBeNull()
216208

217-
act(() => setInputValue(nameInput!, 'Renamed relation'))
218209
act(() => findCombobox('Select table')?.onChange?.('table-customers'))
219210
await act(async () => findButton('Save')?.click())
220211

221212
expect(mockUpdateColumn).toHaveBeenCalledWith({
222213
columnName: 'col-reference',
223-
updates: {
224-
name: 'Renamed relation',
225-
referenceTableId: 'table-customers',
226-
},
214+
updates: { referenceTableId: 'table-customers' },
227215
})
228-
expect(onColumnRename).toHaveBeenCalledWith('col-reference', 'Renamed relation')
229216
})
230217

231218
it('keeps an existing Reference column visible but not retargetable when disabled', async () => {

apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/column-config-sidebar/column-config-sidebar.tsx

Lines changed: 21 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,6 @@ interface ColumnConfigSidebarProps {
5858
referenceColumnsEnabled: boolean
5959
workspaceId: string
6060
tableId: string
61-
/** Notify parent of a rename so it can rewrite local `columnOrder` /
62-
* `columnWidths` keys that reference the old name. */
63-
onColumnRename?: (oldName: string, newName: string) => void
6461
}
6562

6663
/**
@@ -111,7 +108,6 @@ function ColumnConfigBody({
111108
referenceColumnsEnabled,
112109
workspaceId,
113110
tableId,
114-
onColumnRename,
115111
}: ColumnConfigBodyProps) {
116112
const updateColumn = useUpdateColumn({ workspaceId, tableId })
117113
const addColumn = useAddTableColumn({ workspaceId, tableId })
@@ -178,7 +174,7 @@ function ColumnConfigBody({
178174
}
179175

180176
async function handleSave() {
181-
if (!trimmedName) {
177+
if (config.mode === 'create' && !trimmedName) {
182178
setShowValidation(true)
183179
return
184180
}
@@ -210,7 +206,6 @@ function ColumnConfigBody({
210206
return
211207
}
212208

213-
const renamed = trimmedName !== (existingColumn?.name ?? config.columnName)
214209
const typeChanged = !!existingColumn && existingColumn.type !== typeInput
215210
const uniqueChanged =
216211
supportsUnique && !!existingColumn && !!existingColumn.unique !== uniqueInput
@@ -224,15 +219,13 @@ function ColumnConfigBody({
224219
wantsReference && existingColumn?.referenceTableId !== referenceTableInput
225220

226221
const updates: {
227-
name?: string
228222
type?: ColumnDefinition['type']
229223
unique?: boolean
230224
options?: SelectOption[]
231225
multiple?: boolean
232226
currencyCode?: string
233227
referenceTableId?: string
234228
} = {
235-
...(renamed ? { name: trimmedName } : {}),
236229
...(typeChanged ? { type: typeInput } : {}),
237230
...(uniqueChanged ? { unique: uniqueInput } : {}),
238231
...(uniqueCleared ? { unique: false } : {}),
@@ -251,8 +244,7 @@ function ColumnConfigBody({
251244
}
252245

253246
await updateColumn.mutateAsync({ columnName: config.columnName, updates })
254-
if (renamed) onColumnRename?.(config.columnName, trimmedName)
255-
toast.success(`Saved "${trimmedName}"`)
247+
toast.success(`Saved "${existingColumn?.name ?? config.columnName}"`)
256248
onClose()
257249
} catch (err) {
258250
if (isValidationError(err)) {
@@ -285,23 +277,25 @@ function ColumnConfigBody({
285277
</div>
286278

287279
<div className='flex-1 overflow-y-auto overflow-x-hidden px-2 pt-3 pb-2 [overflow-anchor:none]'>
288-
<div className='flex flex-col gap-[9.5px]'>
289-
<RequiredLabel htmlFor='column-sidebar-name'>Column name</RequiredLabel>
290-
<ChipInput
291-
id='column-sidebar-name'
292-
value={nameInput}
293-
onChange={(e) => {
294-
setNameInput(e.target.value)
295-
if (nameError) setNameError(null)
296-
}}
297-
spellCheck={false}
298-
autoComplete='off'
299-
error={Boolean((showValidation && !trimmedName) || nameError)}
300-
aria-invalid={(showValidation && !trimmedName) || nameError ? true : undefined}
301-
/>
302-
{showValidation && !trimmedName && <FieldError message='Column name is required' />}
303-
{nameError && !(showValidation && !trimmedName) && <FieldError message={nameError} />}
304-
</div>
280+
{config.mode === 'create' && (
281+
<div className='flex flex-col gap-[9.5px]'>
282+
<RequiredLabel htmlFor='column-sidebar-name'>Column name</RequiredLabel>
283+
<ChipInput
284+
id='column-sidebar-name'
285+
value={nameInput}
286+
onChange={(e) => {
287+
setNameInput(e.target.value)
288+
if (nameError) setNameError(null)
289+
}}
290+
spellCheck={false}
291+
autoComplete='off'
292+
error={Boolean((showValidation && !trimmedName) || nameError)}
293+
aria-invalid={(showValidation && !trimmedName) || nameError ? true : undefined}
294+
/>
295+
{showValidation && !trimmedName && <FieldError message='Column name is required' />}
296+
{nameError && !(showValidation && !trimmedName) && <FieldError message={nameError} />}
297+
</div>
298+
)}
305299

306300
{config.mode === 'edit' && (
307301
<>

apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -316,9 +316,9 @@ export function Table({
316316
}, [])
317317

318318
/**
319-
* Sink populated by the grid: invoked from sidebar `onColumnRename` so the
320-
* grid can rewrite its local `columnWidths` / `columnOrder` keys after a
321-
* rename. The grid's render assigns to `current`; the wrapper forwards calls.
319+
* Sink populated by the grid: invoked from the workflow sidebar after a
320+
* rename so the grid can rewrite its local `columnWidths` / `columnOrder`
321+
* keys. The grid's render assigns to `current`; the wrapper forwards calls.
322322
*/
323323
const columnRenameSinkRef = useRef<((oldName: string, newName: string) => void) | null>(null)
324324
const onColumnRename = (oldName: string, newName: string) => {
@@ -1665,7 +1665,6 @@ export function Table({
16651665
}
16661666
workspaceId={workspaceId}
16671667
tableId={tableId}
1668-
onColumnRename={onColumnRename}
16691668
/>
16701669
<EnrichmentsSidebar
16711670
open={slideout.kind === 'enrichments'}

0 commit comments

Comments
 (0)