Compare commits

...
Author SHA1 Message Date
Sonarly Claude Code 7728d61983 Filter dropdown throws instead of handling undefined view gracefully
https://sonarly.com/issue/5512?type=bug

The ViewBarFilterDropdownAdvancedFilterButton component throws an unrecoverable error when currentView is undefined, instead of gracefully returning null during transient states where the view data is unavailable.

Fix: The fix splits `ViewBarFilterDropdownAdvancedFilterButton` into two components to comply with React's Rules of Hooks while gracefully handling the case where `currentView` is `undefined`.

**Before:** The exported component called all hooks unconditionally, then threw an unrecoverable error mid-render when `currentView?.objectMetadataId` was falsy:

```typescript file=packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownAdvancedFilterButton.tsx lines=54-58
const objectMetadataId = currentView?.objectMetadataId;

if (!objectMetadataId) {
  throw new Error('Object metadata id is missing from current view');
}
```

Simply changing `throw` to `return null` would violate the Rules of Hooks, because multiple hooks (`useObjectMetadataItemById`, `useAtomFamilySelectorValue`, etc.) are called **after** that guard.

**After:** The exported component is a lightweight shell that only calls `useGetCurrentViewOnly()`, checks for `objectMetadataId`, and returns `null` if it is undefined. All remaining hooks are moved into a new inner component `ViewBarFilterDropdownAdvancedFilterButtonContent` that receives `objectMetadataId` as a guaranteed `string` prop:

```typescript file=packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownAdvancedFilterButton.tsx lines=141-155
export const ViewBarFilterDropdownAdvancedFilterButton = () => {
  const { currentView } = useGetCurrentViewOnly();

  const objectMetadataId = currentView?.objectMetadataId;

  if (!isDefined(objectMetadataId)) {
    return null;
  }

  return (
    <ViewBarFilterDropdownAdvancedFilterButtonContent
      objectMetadataId={objectMetadataId}
    />
  );
};
```

This matches the approach from the reverted commit `0df4821e41` and correctly handles the transient state where the view ID from the URL hasn't been resolved in `coreViewsState` yet — the button simply doesn't render until the view is available.
2026-03-04 12:31:51 +00:00
@@ -31,7 +31,11 @@ const StyledPill = styled(Pill)`
color: ${themeCssVariables.color.blue};
`;
export const ViewBarFilterDropdownAdvancedFilterButton = () => {
const ViewBarFilterDropdownAdvancedFilterButtonContent = ({
objectMetadataId,
}: {
objectMetadataId: string;
}) => {
const advancedFilterQuerySubFilterCount = 0; // TODO
const { t } = useLingui();
@@ -51,14 +55,8 @@ export const ViewBarFilterDropdownAdvancedFilterButton = () => {
const { upsertRecordFilter } = useUpsertRecordFilter();
const objectMetadataId = currentView?.objectMetadataId;
if (!objectMetadataId) {
throw new Error('Object metadata id is missing from current view');
}
const { objectMetadataItem } = useObjectMetadataItemById({
objectId: objectMetadataId ?? null,
objectId: objectMetadataId,
});
const availableFieldMetadataItemsForFilter = useAtomFamilySelectorValue(
@@ -139,3 +137,19 @@ export const ViewBarFilterDropdownAdvancedFilterButton = () => {
</SelectableListItem>
);
};
export const ViewBarFilterDropdownAdvancedFilterButton = () => {
const { currentView } = useGetCurrentViewOnly();
const objectMetadataId = currentView?.objectMetadataId;
if (!isDefined(objectMetadataId)) {
return null;
}
return (
<ViewBarFilterDropdownAdvancedFilterButtonContent
objectMetadataId={objectMetadataId}
/>
);
};