Compare commits

...
Author SHA1 Message Date
Sonarly Claude Code 0f7ec69cdf fix(record-table): guard against undefined recordField during column resize
https://sonarly.com/issue/5904?type=bug

When resizing a table column header on the messageThreads page, `recordField` can be undefined because `visibleRecordFields` changes mid-resize, causing an unhandled throw that crashes the page.

Fix: Replaced `throwIfNotDefined(recordField, 'recordField')` with graceful `isDefined` early returns in both `handleResizeHandlerMove` and `handleResizeHandlerEnd` callbacks in `useResizeTableHeader.ts`.

**Changes:**

1. **Import swap**: Replaced `throwIfNotDefined` with `isDefined` from `twenty-shared/utils` — the same utility already used extensively in sibling files in this directory.

2. **`handleResizeHandlerMove` (line 96)**: Changed from `throwIfNotDefined(recordField, 'recordField')` to `if (!isDefined(recordField)) return;`. When `visibleRecordFields` changes mid-resize and the resized field is no longer in the array, the move handler now silently skips the frame instead of throwing on every mousemove event.

3. **`handleResizeHandlerEnd` (line 164-172)**: Changed from `throwIfNotDefined` to an `isDefined` check that also **resets all resize state** (offset, pointer position, field metadata ID, drag selection) before returning. This prevents the user from getting stuck in a broken resize state when the field disappears mid-resize — the next interaction will start clean.

The `isDefined` utility is a proper TypeScript type guard (`value is NonNullable<T>`), so type narrowing after the check is preserved — the code below the guard can still access `recordField.size`, `recordField.id`, etc. without type errors.
2026-04-07 16:50:30 +00:00
@@ -28,7 +28,7 @@ import { useCallback, useState } from 'react';
import {
findById,
findByProperty,
throwIfNotDefined,
isDefined,
} from 'twenty-shared/utils';
export const useResizeTableHeader = () => {
@@ -93,7 +93,7 @@ export const useResizeTableHeader = () => {
({ x }) => {
if (!initialPointerPositionX) return;
throwIfNotDefined(recordField, 'recordField');
if (!isDefined(recordField)) return;
const newResizeOffset = x - initialPointerPositionX;
@@ -162,9 +162,14 @@ export const useResizeTableHeader = () => {
const store = useStore();
const handleResizeHandlerEnd = useCallback(async () => {
throwIfNotDefined(recordField, 'recordField');
if (!isDefined(recordField) || !resizedFieldMetadataId) {
store.set(resizeFieldOffset, 0);
setInitialPointerPositionX(null);
setResizedFieldMetadataId(null);
setDragSelectionStartEnabled(true);
if (!resizedFieldMetadataId) return;
return;
}
const currentResizeFieldOffset = store.get(resizeFieldOffset);