Compare commits

...
Author SHA1 Message Date
Sonarly Claude Code 34ef01b9ed fix: restore chip count display for relation-to-many fields in unfocused table cells
https://sonarly.com/issue/15886?type=bug

Table performance optimization replaced dynamic focus state with static unfocused provider, causing the ExpandableList "+N" overflow count to never display in non-hovered cells. Overflowing relation chips are hidden with no visual indicator.

Fix: The table performance optimization in commit 159bb9d70a replaced the dynamic `FieldFocusContextProvider` (with `useState`) with `FieldFocusStaticUnfocusedProvider` (hardcoded `isFocused: false`). This broke `RelationFromManyFieldDisplay`, `MorphRelationOneToManyFieldDisplay`, and `MultiSelectFieldDisplay` which all passed `isChipCountDisplayed={isFocused}` to `ExpandableList`.

When `isChipCountDisplayed` is explicitly `false`, `ExpandableList` both:
1. Hides the "+N" overflow count badge (line 95: `canDisplayChipCount = false`)
2. Disables its internal mouse hover fallback (lines 130-138: `onMouseEnter`/`onMouseLeave` set to `undefined`)

The fix removes the `isChipCountDisplayed` prop from all affected `ExpandableList` usages. When the prop is `undefined`, `ExpandableList` falls back to its internal hover detection via `onMouseEnter`/`onMouseLeave`, which correctly shows the "+N" count on hover. This is the same pattern already used by `LinksDisplay`, `FilesDisplay`, and `ArrayDisplay`.

Additionally, `MultiSelectFieldDisplay` had a dead branch (`isFocused ? <ExpandableList> : <MultiSelectDisplay>`) where the `ExpandableList` path was never reached. The fix always renders `ExpandableList`, consistent with the other display components.

Removed unused imports: `useFieldFocus`, `styled` from `@linaria/react`, `themeCssVariables`, `StyledContainer` styled component, and `MultiSelectDisplay`.
2026-03-18 10:37:18 +00:00
3 changed files with 11 additions and 40 deletions
@@ -1,6 +1,5 @@
import { RecordChip } from '@/object-record/components/RecordChip';
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
import { useFieldFocus } from '@/object-record/record-field/ui/hooks/useFieldFocus';
import { useMorphRelationFromManyFieldDisplay } from '@/object-record/record-field/ui/meta-types/hooks/useMorphRelationFromManyFieldDisplay';
import { ExpandableList } from '@/ui/layout/expandable-list/components/ExpandableList';
@@ -11,7 +10,6 @@ import { isDefined } from 'twenty-shared/utils';
export const MorphRelationOneToManyFieldDisplay = () => {
const { morphValuesWithObjectNameSingular } =
useMorphRelationFromManyFieldDisplay();
const { isFocused } = useFieldFocus();
const { disableChipClick, triggerEvent } = useContext(FieldContext);
if (!isDefined(morphValuesWithObjectNameSingular)) {
@@ -39,7 +37,7 @@ export const MorphRelationOneToManyFieldDisplay = () => {
);
return (
<ExpandableList isChipCountDisplayed={isFocused}>
<ExpandableList>
{flattenMorphValuesWithObjectNameSingular
.filter(isDefined)
.map(({ objectNameSingular, record }) => {
@@ -1,6 +1,4 @@
import { useFieldFocus } from '@/object-record/record-field/ui/hooks/useFieldFocus';
import { useMultiSelectFieldDisplay } from '@/object-record/record-field/ui/meta-types/hooks/useMultiSelectFieldDisplay';
import { MultiSelectDisplay } from '@/ui/field/display/components/MultiSelectDisplay';
import { ExpandableList } from '@/ui/layout/expandable-list/components/ExpandableList';
import { Tag } from 'twenty-ui/components';
import { isDefined } from 'twenty-shared/utils';
@@ -8,8 +6,6 @@ import { isDefined } from 'twenty-shared/utils';
export const MultiSelectFieldDisplay = () => {
const { fieldValue, fieldDefinition } = useMultiSelectFieldDisplay();
const { isFocused } = useFieldFocus();
const selectedOptions = fieldValue
? fieldDefinition.metadata.options?.filter((option) =>
fieldValue.includes(option.value),
@@ -18,8 +14,8 @@ export const MultiSelectFieldDisplay = () => {
if (!isDefined(selectedOptions)) return null;
return isFocused ? (
<ExpandableList isChipCountDisplayed={isFocused}>
return (
<ExpandableList>
{selectedOptions.map((selectedOption, index) => (
<Tag
key={index}
@@ -28,10 +24,5 @@ export const MultiSelectFieldDisplay = () => {
/>
))}
</ExpandableList>
) : (
<MultiSelectDisplay
values={fieldValue}
options={fieldDefinition.metadata.options}
/>
);
};
@@ -8,7 +8,6 @@ import { CoreObjectNameSingular } from 'twenty-shared/types';
import { RecordChip } from '@/object-record/components/RecordChip';
import { isActivityTargetField } from '@/object-record/record-field-list/utils/categorizeRelationFields';
import { FieldContext } from '@/object-record/record-field/ui/contexts/FieldContext';
import { useFieldFocus } from '@/object-record/record-field/ui/hooks/useFieldFocus';
import { useRelationFromManyFieldDisplay } from '@/object-record/record-field/ui/meta-types/hooks/useRelationFromManyFieldDisplay';
import { ForbiddenFieldDisplay } from '@/object-record/record-field/ui/meta-types/display/components/ForbiddenFieldDisplay';
import { extractTargetRecordsFromJunction } from '@/object-record/record-field/ui/utils/junction/extractTargetRecordsFromJunction';
@@ -16,25 +15,12 @@ import { getJunctionConfig } from '@/object-record/record-field/ui/utils/junctio
import { hasJunctionConfig } from '@/object-record/record-field/ui/utils/junction/hasJunctionConfig';
import { ExpandableList } from '@/ui/layout/expandable-list/components/ExpandableList';
import { styled } from '@linaria/react';
import { isArray } from '@sniptt/guards';
import { isDefined } from 'twenty-shared/utils';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledContainer = styled.div`
align-items: center;
display: flex;
gap: ${themeCssVariables.spacing[1]};
justify-content: flex-start;
max-width: 100%;
overflow: hidden;
width: 100%;
`;
export const RelationFromManyFieldDisplay = () => {
const { fieldValue, fieldDefinition, generateRecordChipData } =
useRelationFromManyFieldDisplay();
const { isFocused } = useFieldFocus();
const { disableChipClick, triggerEvent } = useContext(FieldContext);
const { objectMetadataItems } = useObjectMetadataItems();
@@ -109,15 +95,11 @@ export const RelationFromManyFieldDisplay = () => {
})
.filter(isDefined);
if (isFocused) {
return (
<ExpandableList isChipCountDisplayed={isFocused}>
{chips}
</ExpandableList>
);
}
return <StyledContainer>{chips}</StyledContainer>;
return (
<ExpandableList>
{chips}
</ExpandableList>
);
}
if (isJunctionRelation && isDefined(junctionConfig)) {
@@ -151,7 +133,7 @@ export const RelationFromManyFieldDisplay = () => {
}
return (
<ExpandableList isChipCountDisplayed={isFocused}>
<ExpandableList>
{targetRecordsWithMetadata.map(({ record, objectMetadata }) => (
<RecordChip
key={record.id}
@@ -167,7 +149,7 @@ export const RelationFromManyFieldDisplay = () => {
if (isRelationFromActivityTargets) {
return (
<ExpandableList isChipCountDisplayed={isFocused}>
<ExpandableList>
{activityTargetObjectRecords.filter(isDefined).map((record) => (
<RecordChip
key={record.targetObject.id}
@@ -181,7 +163,7 @@ export const RelationFromManyFieldDisplay = () => {
}
return (
<ExpandableList isChipCountDisplayed={isFocused}>
<ExpandableList>
{fieldValue.filter(isDefined).map((record) => {
const recordChipData = generateRecordChipData(record);
return (