Migrate field widgets to backend (#18808)

- Renamed FieldConfiguration's layout field to fieldDisplayMode as it
caused issues with the layout field of BarChartConfiguration
- Create relation Field widgets for standard objects

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Baptiste Devessier
2026-03-23 01:26:00 +01:00
committed by GitHub
parent d90d2e3151
commit be89ef30cd
38 changed files with 1388 additions and 790 deletions
@@ -15,9 +15,9 @@ import { assertFieldWidgetOrThrow } from '@/page-layout/widgets/field/utils/asse
import { useLayoutRenderingContext } from '@/ui/layout/contexts/LayoutRenderingContext';
import { useTargetRecord } from '@/ui/layout/contexts/useTargetRecord';
import { SidePanelProvider } from '@/ui/layout/side-panel/contexts/SidePanelContext';
import { useAtomFamilySelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilySelectorValue';
import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
import { useAtomFamilySelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilySelectorValue';
import { isDefined } from 'twenty-shared/utils';
import {
AnimatedPlaceholder,
@@ -27,6 +27,7 @@ import {
AnimatedPlaceholderEmptyTitle,
EMPTY_PLACEHOLDER_TRANSITION_PROPS,
} from 'twenty-ui/layout';
import { FieldDisplayMode } from '~/generated-metadata/graphql';
const StyledContainer = styled.div`
box-sizing: border-box;
@@ -92,10 +93,10 @@ export const FieldWidget = ({ widget }: FieldWidgetProps) => {
labelWidth: 90,
});
const layout = widget.configuration.layout;
const fieldDisplayMode = widget.configuration.fieldDisplayMode;
if (isFieldMorphRelation(fieldDefinition)) {
if (layout === 'CARD') {
if (fieldDisplayMode === FieldDisplayMode.CARD) {
return (
<FieldWidgetMorphRelationCard
fieldDefinition={fieldDefinition}
@@ -115,7 +116,7 @@ export const FieldWidget = ({ widget }: FieldWidgetProps) => {
}
if (isFieldRelation(fieldDefinition)) {
if (layout === 'CARD') {
if (fieldDisplayMode === FieldDisplayMode.CARD) {
return (
<FieldWidgetRelationCard
fieldDefinition={fieldDefinition}
@@ -4,10 +4,8 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
import { MemoryRouter } from 'react-router-dom';
import { expect, userEvent, waitFor, within } from 'storybook/test';
import { ApolloCoreClientContext } from '@/object-metadata/contexts/ApolloCoreClientContext';
import { isMinimalMetadataReadyState } from '@/metadata-store/states/isMinimalMetadataReadyState';
import { setTestObjectMetadataItemsInMetadataStore } from '~/testing/utils/setTestObjectMetadataItemsInMetadataStore';
import { CoreObjectNameSingular } from 'twenty-shared/types';
import { ApolloCoreClientContext } from '@/object-metadata/contexts/ApolloCoreClientContext';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
import { PageLayoutContentProvider } from '@/page-layout/contexts/PageLayoutContentContext';
@@ -23,8 +21,10 @@ import { FieldWidget } from '@/page-layout/widgets/field/components/FieldWidget'
import { WidgetComponentInstanceContext } from '@/page-layout/widgets/states/contexts/WidgetComponentInstanceContext';
import { LayoutRenderingProvider } from '@/ui/layout/contexts/LayoutRenderingContext';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { CoreObjectNameSingular } from 'twenty-shared/types';
import { ComponentDecorator } from 'twenty-ui/testing';
import {
FieldDisplayMode,
PageLayoutTabLayoutMode,
PageLayoutType,
WidgetConfigurationType,
@@ -36,6 +36,7 @@ import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMeta
import { getTestEnrichedObjectMetadataItemsMock } from '~/testing/utils/getTestEnrichedObjectMetadataItemsMock';
import { getMockFieldMetadataItemOrThrow } from '~/testing/utils/getMockFieldMetadataItemOrThrow';
import { getMockObjectMetadataItemOrThrow } from '~/testing/utils/getMockObjectMetadataItemOrThrow';
import { setTestObjectMetadataItemsInMetadataStore } from '~/testing/utils/setTestObjectMetadataItemsInMetadataStore';
const companyObjectMetadataItem = getMockObjectMetadataItemOrThrow(
CoreObjectNameSingular.Company,
@@ -348,7 +349,7 @@ export const TextFieldWidget: Story = {
__typename: 'FieldConfiguration',
configurationType: WidgetConfigurationType.FIELD,
fieldMetadataId: nameField.id,
layout: 'FIELD',
fieldDisplayMode: FieldDisplayMode.FIELD,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
@@ -442,7 +443,7 @@ export const AddressFieldWidget: Story = {
__typename: 'FieldConfiguration',
configurationType: WidgetConfigurationType.FIELD,
fieldMetadataId: addressField.id,
layout: 'FIELD',
fieldDisplayMode: FieldDisplayMode.FIELD,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
@@ -539,7 +540,7 @@ export const NumberFieldWidget: Story = {
__typename: 'FieldConfiguration',
configurationType: WidgetConfigurationType.FIELD,
fieldMetadataId: employeesField.id,
layout: 'FIELD',
fieldDisplayMode: FieldDisplayMode.FIELD,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
@@ -633,7 +634,7 @@ export const LinkFieldWidget: Story = {
__typename: 'FieldConfiguration',
configurationType: WidgetConfigurationType.FIELD,
fieldMetadataId: linkedinField.id,
layout: 'FIELD',
fieldDisplayMode: FieldDisplayMode.FIELD,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
@@ -727,7 +728,7 @@ export const ManyToOneRelationFieldWidget: Story = {
__typename: 'FieldConfiguration',
configurationType: WidgetConfigurationType.FIELD,
fieldMetadataId: accountOwnerField.id,
layout: 'FIELD',
fieldDisplayMode: FieldDisplayMode.FIELD,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
@@ -831,7 +832,7 @@ export const OneToManyRelationFieldWidget: Story = {
__typename: 'FieldConfiguration',
configurationType: WidgetConfigurationType.FIELD,
fieldMetadataId: companyPeopleField.id,
layout: 'FIELD',
fieldDisplayMode: FieldDisplayMode.FIELD,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
@@ -927,7 +928,7 @@ export const BooleanFieldWidget: Story = {
__typename: 'FieldConfiguration',
configurationType: WidgetConfigurationType.FIELD,
fieldMetadataId: idealCustomerProfileField.id,
layout: 'FIELD',
fieldDisplayMode: FieldDisplayMode.FIELD,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
@@ -1020,7 +1021,7 @@ export const CurrencyFieldWidget: Story = {
__typename: 'FieldConfiguration',
configurationType: WidgetConfigurationType.FIELD,
fieldMetadataId: annualRecurringRevenueField.id,
layout: 'FIELD',
fieldDisplayMode: FieldDisplayMode.FIELD,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
@@ -1113,7 +1114,7 @@ export const EmailsFieldWidget: Story = {
__typename: 'FieldConfiguration',
configurationType: WidgetConfigurationType.FIELD,
fieldMetadataId: personEmailsField.id,
layout: 'FIELD',
fieldDisplayMode: FieldDisplayMode.FIELD,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
@@ -1207,7 +1208,7 @@ export const PhonesFieldWidget: Story = {
__typename: 'FieldConfiguration',
configurationType: WidgetConfigurationType.FIELD,
fieldMetadataId: personPhonesField.id,
layout: 'FIELD',
fieldDisplayMode: FieldDisplayMode.FIELD,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
@@ -1301,7 +1302,7 @@ export const SelectFieldWidget: Story = {
__typename: 'FieldConfiguration',
configurationType: WidgetConfigurationType.FIELD,
fieldMetadataId: opportunityStageField.id,
layout: 'FIELD',
fieldDisplayMode: FieldDisplayMode.FIELD,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
@@ -1396,7 +1397,7 @@ export const MultiSelectFieldWidget: Story = {
__typename: 'FieldConfiguration',
configurationType: WidgetConfigurationType.FIELD,
fieldMetadataId: companyWorkPolicyField.id,
layout: 'FIELD',
fieldDisplayMode: FieldDisplayMode.FIELD,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
@@ -1494,7 +1495,7 @@ export const TimelineActivityRelationFieldWidget: Story = {
__typename: 'FieldConfiguration',
configurationType: WidgetConfigurationType.FIELD,
fieldMetadataId: timelineActivityWorkspaceMemberField.id,
layout: 'FIELD',
fieldDisplayMode: FieldDisplayMode.FIELD,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
@@ -1593,7 +1594,7 @@ export const ManyToOneRelationCardWidget: Story = {
__typename: 'FieldConfiguration',
configurationType: WidgetConfigurationType.FIELD,
fieldMetadataId: accountOwnerField.id,
layout: 'CARD',
fieldDisplayMode: FieldDisplayMode.CARD,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
@@ -1705,7 +1706,7 @@ export const OneToManyRelationCardWidget: Story = {
__typename: 'FieldConfiguration',
configurationType: WidgetConfigurationType.FIELD,
fieldMetadataId: companyPeopleField.id,
layout: 'CARD',
fieldDisplayMode: FieldDisplayMode.CARD,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
@@ -1800,7 +1801,7 @@ export const TimelineActivityRelationCardWidget: Story = {
__typename: 'FieldConfiguration',
configurationType: WidgetConfigurationType.FIELD,
fieldMetadataId: timelineActivityWorkspaceMemberField.id,
layout: 'CARD',
fieldDisplayMode: FieldDisplayMode.CARD,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
@@ -1961,7 +1962,7 @@ export const OneToManyRelationCardWidgetWithProgressiveLoading: Story = {
__typename: 'FieldConfiguration',
configurationType: WidgetConfigurationType.FIELD,
fieldMetadataId: companyPeopleField.id,
layout: 'CARD',
fieldDisplayMode: FieldDisplayMode.CARD,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
@@ -1,6 +1,7 @@
import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget';
import { assertFieldWidgetOrThrow } from '@/page-layout/widgets/field/utils/assertFieldWidgetOrThrow';
import {
FieldDisplayMode,
WidgetConfigurationType,
WidgetType,
} from '~/generated-metadata/graphql';
@@ -14,7 +15,7 @@ describe('assertFieldWidgetOrThrow', () => {
__typename: 'FieldConfiguration',
configurationType: WidgetConfigurationType.FIELD,
fieldMetadataId: 'fm-1',
layout: 'FIELD',
fieldDisplayMode: FieldDisplayMode.FIELD,
},
} as PageLayoutWidget;
@@ -1,6 +1,7 @@
import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget';
import { isFieldWidget } from '@/page-layout/widgets/field/utils/isFieldWidget';
import {
FieldDisplayMode,
WidgetConfigurationType,
WidgetType,
} from '~/generated-metadata/graphql';
@@ -14,7 +15,7 @@ describe('isFieldWidget', () => {
__typename: 'FieldConfiguration',
configurationType: WidgetConfigurationType.FIELD,
fieldMetadataId: 'fm-1',
layout: 'FIELD',
fieldDisplayMode: FieldDisplayMode.FIELD,
},
} as PageLayoutWidget;
@@ -29,7 +30,7 @@ describe('isFieldWidget', () => {
__typename: 'FieldConfiguration',
configurationType: WidgetConfigurationType.FIELD,
fieldMetadataId: 'fm-1',
layout: 'FIELD',
fieldDisplayMode: FieldDisplayMode.FIELD,
},
} as PageLayoutWidget;