Compare commits

...
Author SHA1 Message Date
Sonarly Claude Code b1d1cbc47e Board view hidden field dropdown shows fields not available in column definitions lookup
https://sonarly.com/issue/7305?type=bug

When a user tries to show a hidden field (like Attachments or Timeline Activities) on a Kanban board view, the handler crashes because the dropdown and the visibility handler use different, inconsistent sets of field definitions.

Fix: The fix ensures the hidden fields dropdown in Kanban/board views only displays fields that the board field visibility handler can actually process.

**What changed:** In `ViewFieldsHiddenDropdownSection.tsx`, the `hiddenBoardFields` array is now destructured from `useObjectOptionsForBoard` (which was already being called). When the view type is `Kanban`, the filter for `availableFieldMetadataItemsToShow` now includes an additional check: only fields whose `id` matches a `fieldMetadataId` in `hiddenBoardFields` are shown. This ensures fields excluded by `TABLE_COLUMNS_DENY_LIST` (e.g., `attachments`, `timelineActivities`) and `isLabelIdentifier` fields — which are absent from `availableColumnDefinitions` — are not displayed as clickable hidden fields.

**Why this works:** `hiddenBoardFields` is computed from `availableColumnDefinitions` (lines 75-93 of `useObjectOptionsForBoard.ts`), which is the **same** source that `handleBoardFieldVisibilityChange` searches at line 178 when processing the click. This restores the consistency guarantee that existed in the old `ObjectOptionsDropdownHiddenFieldsContent` component before it was replaced in commit `c342ece2ee`.

**What is NOT changed:** The table view path (`changeRecordFieldVisibility`) is unaffected because the `viewType === ViewType.Kanban` guard only applies the board-specific filter. No shared utilities or the `useObjectOptionsForBoard` hook were modified.
2026-03-06 15:04:13 +00:00
Sonarly Claude Code 84319479ee Unchecked findIndex(-1) in board field visibility toggle causes TypeError
https://sonarly.com/issue/7338?type=bug

When toggling field visibility on a Kanban board, `Array.findIndex()` returns -1 because the field exists in `currentRecordFields` but not in `recordIndexFieldDefinitions`. Accessing `array[-1]` yields `undefined`, and setting `.isVisible` on it throws the TypeError.

Fix: Added bounds checks for `findIndex()` returning `-1` in two places in `useObjectOptionsForBoard.ts`:

1. **`handleReorderBoardFields`** (line 117): Guards against `findIndex` returning `-1` when reordering board fields. If the field isn't found in `recordIndexFieldDefinitions`, the `produce` callback returns early (immer returns the original array unchanged).

2. **`handleBoardFieldVisibilityChange`** (line 229): Guards against `findIndex` returning `-1` when toggling field visibility. Same early-return pattern.

Both `produce` callbacks now safely no-op when the field exists in `currentRecordFields` but not in `recordIndexFieldDefinitions`. This is correct because:
- The primary state updates (`updateRecordField`, `saveViewFields`) have already executed by this point
- `recordIndexFieldDefinitions` is explicitly transitional state (marked with `// TODO: remove after refactor`)
- Skipping the `recordIndexFieldDefinitions` update when the field isn't found is strictly better than crashing
2026-03-06 15:02:07 +00:00
@@ -18,11 +18,12 @@ export const ViewFieldsHiddenDropdownSection = () => {
const { changeRecordFieldVisibility } =
useChangeRecordFieldVisibility(recordIndexId);
const { handleBoardFieldVisibilityChange } = useObjectOptionsForBoard({
objectNameSingular: objectMetadataItem.nameSingular,
recordBoardId: recordIndexId,
viewBarId: recordIndexId,
});
const { handleBoardFieldVisibilityChange, hiddenBoardFields } =
useObjectOptionsForBoard({
objectNameSingular: objectMetadataItem.nameSingular,
recordBoardId: recordIndexId,
viewBarId: recordIndexId,
});
const handleChangeFieldVisibility =
viewType === ViewType.Kanban
@@ -42,10 +43,24 @@ export const ViewFieldsHiddenDropdownSection = () => {
});
const availableFieldMetadataItemsToShow = activeFieldMetadataItems.filter(
(fieldMetadataItemToFilter) =>
!visibleRecordFields
(fieldMetadataItemToFilter) => {
const isAlreadyVisible = visibleRecordFields
.map((recordField) => recordField.fieldMetadataItemId)
.includes(fieldMetadataItemToFilter.id),
.includes(fieldMetadataItemToFilter.id);
if (isAlreadyVisible) {
return false;
}
if (viewType === ViewType.Kanban) {
return hiddenBoardFields.some(
(boardField) =>
boardField.fieldMetadataId === fieldMetadataItemToFilter.id,
);
}
return true;
},
);
const { getIcon } = useIcons();