Skip to content

Commit 0b58b1b

Browse files
committed
fix(tables): keep column rename migration atomic
1 parent 3b12976 commit 0b58b1b

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
@@ -100,12 +100,6 @@ function findButton(label: string): HTMLButtonElement | undefined {
100100
)
101101
}
102102

103-
function setInputValue(input: HTMLInputElement, value: string): void {
104-
const valueSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set
105-
valueSetter?.call(input, value)
106-
input.dispatchEvent(new Event('input', { bubbles: true }))
107-
}
108-
109103
beforeEach(() => {
110104
globalThis.IS_REACT_ACT_ENVIRONMENT = true
111105
container = document.createElement('div')
@@ -186,8 +180,7 @@ describe('ColumnConfigSidebar', () => {
186180
expect(mockUpdateColumn).not.toHaveBeenCalled()
187181
})
188182

189-
it('edits a Reference column name and target table together', async () => {
190-
const onColumnRename = vi.fn()
183+
it('edits Reference configuration without exposing column renaming', async () => {
191184
await act(async () => {
192185
root.render(
193186
<ColumnConfigSidebar
@@ -201,26 +194,20 @@ describe('ColumnConfigSidebar', () => {
201194
}}
202195
workspaceId='workspace-1'
203196
tableId='table-current'
204-
onColumnRename={onColumnRename}
205197
/>
206198
)
207199
})
208200

209-
const nameInput = container.querySelector<HTMLInputElement>('#column-sidebar-name')
210-
expect(nameInput?.value).toBe('Related row')
201+
expect(container).not.toHaveTextContent('Column name')
202+
expect(container.querySelector('#column-sidebar-name')).toBeNull()
211203

212-
act(() => setInputValue(nameInput!, 'Renamed relation'))
213204
act(() => findCombobox('Select table')?.onChange?.('table-customers'))
214205
await act(async () => findButton('Save')?.click())
215206

216207
expect(mockUpdateColumn).toHaveBeenCalledWith({
217208
columnName: 'col-reference',
218-
updates: {
219-
name: 'Renamed relation',
220-
referenceTableId: 'table-customers',
221-
},
209+
updates: { referenceTableId: 'table-customers' },
222210
})
223-
expect(onColumnRename).toHaveBeenCalledWith('col-reference', 'Renamed relation')
224211
})
225212

226213
it('keeps Select options in the edit sidebar', 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
@@ -57,9 +57,6 @@ interface ColumnConfigSidebarProps {
5757
tableRowTtlEnabled: boolean
5858
workspaceId: string
5959
tableId: string
60-
/** Notify parent of a rename so it can rewrite local `columnOrder` /
61-
* `columnWidths` keys that reference the old name. */
62-
onColumnRename?: (oldName: string, newName: string) => void
6360
}
6461

6562
/**
@@ -109,7 +106,6 @@ function ColumnConfigBody({
109106
tableRowTtlEnabled,
110107
workspaceId,
111108
tableId,
112-
onColumnRename,
113109
}: ColumnConfigBodyProps) {
114110
const updateColumn = useUpdateColumn({ workspaceId, tableId })
115111
const addColumn = useAddTableColumn({ workspaceId, tableId })
@@ -170,7 +166,7 @@ function ColumnConfigBody({
170166
}
171167

172168
async function handleSave() {
173-
if (!trimmedName) {
169+
if (config.mode === 'create' && !trimmedName) {
174170
setShowValidation(true)
175171
return
176172
}
@@ -202,7 +198,6 @@ function ColumnConfigBody({
202198
return
203199
}
204200

205-
const renamed = trimmedName !== (existingColumn?.name ?? config.columnName)
206201
const typeChanged = !!existingColumn && existingColumn.type !== typeInput
207202
const uniqueChanged =
208203
supportsUnique && !!existingColumn && !!existingColumn.unique !== uniqueInput
@@ -216,15 +211,13 @@ function ColumnConfigBody({
216211
wantsReference && existingColumn?.referenceTableId !== referenceTableInput
217212

218213
const updates: {
219-
name?: string
220214
type?: ColumnDefinition['type']
221215
unique?: boolean
222216
options?: SelectOption[]
223217
multiple?: boolean
224218
currencyCode?: string
225219
referenceTableId?: string
226220
} = {
227-
...(renamed ? { name: trimmedName } : {}),
228221
...(typeChanged ? { type: typeInput } : {}),
229222
...(uniqueChanged ? { unique: uniqueInput } : {}),
230223
...(uniqueCleared ? { unique: false } : {}),
@@ -243,8 +236,7 @@ function ColumnConfigBody({
243236
}
244237

245238
await updateColumn.mutateAsync({ columnName: config.columnName, updates })
246-
if (renamed) onColumnRename?.(config.columnName, trimmedName)
247-
toast.success(`Saved "${trimmedName}"`)
239+
toast.success(`Saved "${existingColumn?.name ?? config.columnName}"`)
248240
onClose()
249241
} catch (err) {
250242
if (isValidationError(err)) {
@@ -277,23 +269,25 @@ function ColumnConfigBody({
277269
</div>
278270

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

298292
{config.mode === 'edit' && (
299293
<>

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

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

315315
/**
316-
* Sink populated by the grid: invoked from sidebar `onColumnRename` so the
317-
* grid can rewrite its local `columnWidths` / `columnOrder` keys after a
318-
* rename. The grid's render assigns to `current`; the wrapper forwards calls.
316+
* Sink populated by the grid: invoked from the workflow sidebar after a
317+
* rename so the grid can rewrite its local `columnWidths` / `columnOrder`
318+
* keys. The grid's render assigns to `current`; the wrapper forwards calls.
319319
*/
320320
const columnRenameSinkRef = useRef<((oldName: string, newName: string) => void) | null>(null)
321321
const onColumnRename = (oldName: string, newName: string) => {
@@ -1659,7 +1659,6 @@ export function Table({
16591659
}
16601660
workspaceId={workspaceId}
16611661
tableId={tableId}
1662-
onColumnRename={onColumnRename}
16631662
/>
16641663
<EnrichmentsSidebar
16651664
open={slideout.kind === 'enrichments'}

0 commit comments

Comments
 (0)