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:
committed by
GitHub
parent
d90d2e3151
commit
be89ef30cd
+12
-3
@@ -1,9 +1,9 @@
|
||||
import { useExitLayoutCustomizationMode } from '@/layout-customization/hooks/useExitLayoutCustomizationMode';
|
||||
import { activeCustomizationPageLayoutIdsState } from '@/layout-customization/states/activeCustomizationPageLayoutIdsState';
|
||||
import { useSaveNavigationMenuItemsDraft } from '@/navigation-menu-item/edit/hooks/useSaveNavigationMenuItemsDraft';
|
||||
import { navigationMenuItemsDraftState } from '@/navigation-menu-item/common/states/navigationMenuItemsDraftState';
|
||||
import { navigationMenuItemsSelector } from '@/navigation-menu-item/common/states/navigationMenuItemsSelector';
|
||||
import { filterWorkspaceNavigationMenuItems } from '@/navigation-menu-item/common/utils/filterWorkspaceNavigationMenuItems';
|
||||
import { useSaveNavigationMenuItemsDraft } from '@/navigation-menu-item/edit/hooks/useSaveNavigationMenuItemsDraft';
|
||||
import { useSaveFieldsWidgetGroups } from '@/page-layout/hooks/useSaveFieldsWidgetGroups';
|
||||
import { useUpdatePageLayoutWithTabsAndWidgets } from '@/page-layout/hooks/useUpdatePageLayoutWithTabsAndWidgets';
|
||||
import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState';
|
||||
@@ -16,11 +16,12 @@ import { convertPageLayoutToTabLayouts } from '@/page-layout/utils/convertPageLa
|
||||
import { reInjectDynamicRelationWidgetsFromDraft } from '@/page-layout/utils/reInjectDynamicRelationWidgetsFromDraft';
|
||||
import { transformPageLayout } from '@/page-layout/utils/transformPageLayout';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { PageLayoutType } from '~/generated-metadata/graphql';
|
||||
import { FeatureFlagKey, PageLayoutType } from '~/generated-metadata/graphql';
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
import { logError } from '~/utils/logError';
|
||||
|
||||
@@ -36,6 +37,10 @@ export const useSaveLayoutCustomization = () => {
|
||||
const { exitLayoutCustomizationMode } = useExitLayoutCustomizationMode();
|
||||
const { saveFieldsWidgetGroups } = useSaveFieldsWidgetGroups();
|
||||
|
||||
const featureFlags = useFeatureFlagsMap();
|
||||
const isRecordPageLayoutEditingEnabled =
|
||||
featureFlags[FeatureFlagKey.IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED];
|
||||
|
||||
const save = useCallback(async () => {
|
||||
setIsSaving(true);
|
||||
try {
|
||||
@@ -92,7 +97,10 @@ export const useSaveLayoutCustomization = () => {
|
||||
);
|
||||
|
||||
if (isPageLayoutStructureDirty) {
|
||||
const updateInput = convertPageLayoutDraftToUpdateInput(draft);
|
||||
const updateInput = convertPageLayoutDraftToUpdateInput(draft, {
|
||||
shouldFilterDynamicRelationWidgets:
|
||||
!isRecordPageLayoutEditingEnabled,
|
||||
});
|
||||
const result = await updatePageLayoutWithTabsAndWidgets(
|
||||
pageLayoutId,
|
||||
updateInput,
|
||||
@@ -107,6 +115,7 @@ export const useSaveLayoutCustomization = () => {
|
||||
transformPageLayout(updatedPageLayout);
|
||||
|
||||
const pageLayoutToPersist =
|
||||
!isRecordPageLayoutEditingEnabled &&
|
||||
persistedLayout.type === PageLayoutType.RECORD_PAGE
|
||||
? reInjectDynamicRelationWidgetsFromDraft(
|
||||
persistedLayout,
|
||||
|
||||
+12
-3
@@ -10,10 +10,11 @@ import { convertPageLayoutToTabLayouts } from '@/page-layout/utils/convertPageLa
|
||||
import { isPageLayoutEmpty } from '@/page-layout/utils/isPageLayoutEmpty';
|
||||
import { useAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentState';
|
||||
import { useAtomComponentStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateCallbackState';
|
||||
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { PageLayoutType } from '~/generated-metadata/graphql';
|
||||
import { FeatureFlagKey, PageLayoutType } from '~/generated-metadata/graphql';
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
|
||||
type PageLayoutInitializationQueryEffectProps = {
|
||||
@@ -26,9 +27,17 @@ export const PageLayoutInitializationQueryEffect = ({
|
||||
const [pageLayoutIsInitialized, setPageLayoutIsInitialized] =
|
||||
useAtomComponentState(pageLayoutIsInitializedComponentState);
|
||||
|
||||
const basePageLayout = useBasePageLayout(pageLayoutId);
|
||||
const featureFlags = useFeatureFlagsMap();
|
||||
const isRecordPageLayoutEditingEnabled =
|
||||
featureFlags[FeatureFlagKey.IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED];
|
||||
|
||||
const pageLayout = usePageLayoutWithRelationWidgets(basePageLayout);
|
||||
const basePageLayout = useBasePageLayout(pageLayoutId);
|
||||
const pageLayoutWithRelationWidgets =
|
||||
usePageLayoutWithRelationWidgets(basePageLayout);
|
||||
|
||||
const pageLayout = isRecordPageLayoutEditingEnabled
|
||||
? basePageLayout
|
||||
: pageLayoutWithRelationWidgets;
|
||||
|
||||
const { setIsPageLayoutInEditMode } =
|
||||
useSetIsPageLayoutInEditMode(pageLayoutId);
|
||||
|
||||
@@ -7,8 +7,10 @@ import { PageLayoutComponentInstanceContext } from '@/page-layout/states/context
|
||||
import { getTabListInstanceIdFromPageLayoutAndRecord } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutAndRecord';
|
||||
import { useLayoutRenderingContext } from '@/ui/layout/contexts/LayoutRenderingContext';
|
||||
import { TabListComponentInstanceContext } from '@/ui/layout/tab-list/states/contexts/TabListComponentInstanceContext';
|
||||
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
|
||||
import 'react-grid-layout/css/styles.css';
|
||||
import 'react-resizable/css/styles.css';
|
||||
import { FeatureFlagKey } from '~/generated-metadata/graphql';
|
||||
|
||||
type PageLayoutRendererProps = {
|
||||
pageLayoutId: string;
|
||||
@@ -19,6 +21,10 @@ export const PageLayoutRenderer = ({
|
||||
}: PageLayoutRendererProps) => {
|
||||
const { targetRecordIdentifier, layoutType } = useLayoutRenderingContext();
|
||||
|
||||
const featureFlags = useFeatureFlagsMap();
|
||||
const isRecordPageLayoutEditingEnabled =
|
||||
featureFlags[FeatureFlagKey.IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED];
|
||||
|
||||
const tabListInstanceId = getTabListInstanceIdFromPageLayoutAndRecord({
|
||||
pageLayoutId,
|
||||
layoutType,
|
||||
@@ -42,7 +48,9 @@ export const PageLayoutRenderer = ({
|
||||
>
|
||||
<PageLayoutInitializationQueryEffect pageLayoutId={pageLayoutId} />
|
||||
<PageLayoutRecordPageCustomizationSessionRegistrationEffect />
|
||||
<PageLayoutRelationWidgetsSyncEffect pageLayoutId={pageLayoutId} />
|
||||
{!isRecordPageLayoutEditingEnabled && (
|
||||
<PageLayoutRelationWidgetsSyncEffect pageLayoutId={pageLayoutId} />
|
||||
)}
|
||||
<PageLayoutRendererContent />
|
||||
</PageLayoutEditModeProvider>
|
||||
</TabListComponentInstanceContext.Provider>
|
||||
|
||||
+2
@@ -161,6 +161,8 @@ export const PAGE_LAYOUT_WIDGET_FRAGMENT = gql`
|
||||
}
|
||||
... on FieldConfiguration {
|
||||
configurationType
|
||||
fieldDisplayMode
|
||||
fieldMetadataId
|
||||
}
|
||||
... on FieldRichTextConfiguration {
|
||||
configurationType
|
||||
|
||||
@@ -10,10 +10,11 @@ import { reInjectDynamicRelationWidgetsFromDraft } from '@/page-layout/utils/reI
|
||||
import { transformPageLayout } from '@/page-layout/utils/transformPageLayout';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useAtomComponentStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateCallbackState';
|
||||
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { PageLayoutType } from '~/generated-metadata/graphql';
|
||||
import { FeatureFlagKey, PageLayoutType } from '~/generated-metadata/graphql';
|
||||
|
||||
export const useSavePageLayout = (pageLayoutIdFromProps: string) => {
|
||||
const pageLayoutId = useAvailableComponentInstanceIdOrThrow(
|
||||
@@ -40,11 +41,17 @@ export const useSavePageLayout = (pageLayoutIdFromProps: string) => {
|
||||
const { updatePageLayoutWithTabsAndWidgets } =
|
||||
useUpdatePageLayoutWithTabsAndWidgets();
|
||||
|
||||
const featureFlags = useFeatureFlagsMap();
|
||||
const isRecordPageLayoutEditingEnabled =
|
||||
featureFlags[FeatureFlagKey.IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED];
|
||||
|
||||
const store = useStore();
|
||||
|
||||
const savePageLayout = useCallback(async () => {
|
||||
const pageLayoutDraft = store.get(pageLayoutDraftCallbackState);
|
||||
const updateInput = convertPageLayoutDraftToUpdateInput(pageLayoutDraft);
|
||||
const updateInput = convertPageLayoutDraftToUpdateInput(pageLayoutDraft, {
|
||||
shouldFilterDynamicRelationWidgets: !isRecordPageLayoutEditingEnabled,
|
||||
});
|
||||
|
||||
const result = await updatePageLayoutWithTabsAndWidgets(
|
||||
pageLayoutId,
|
||||
@@ -60,6 +67,7 @@ export const useSavePageLayout = (pageLayoutIdFromProps: string) => {
|
||||
transformPageLayout(updatedPageLayout);
|
||||
|
||||
const pageLayoutToPersist =
|
||||
!isRecordPageLayoutEditingEnabled &&
|
||||
persistedLayout.type === PageLayoutType.RECORD_PAGE
|
||||
? reInjectDynamicRelationWidgetsFromDraft(
|
||||
persistedLayout,
|
||||
@@ -77,6 +85,7 @@ export const useSavePageLayout = (pageLayoutIdFromProps: string) => {
|
||||
|
||||
return result;
|
||||
}, [
|
||||
isRecordPageLayoutEditingEnabled,
|
||||
pageLayoutCurrentLayoutsCallbackState,
|
||||
pageLayoutDraftCallbackState,
|
||||
pageLayoutId,
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { type WidgetConfigurationType } from '~/generated-metadata/graphql';
|
||||
import {
|
||||
type FieldDisplayMode,
|
||||
type WidgetConfigurationType,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
export type FieldConfiguration = {
|
||||
__typename: 'FieldConfiguration';
|
||||
configurationType: WidgetConfigurationType.FIELD;
|
||||
fieldMetadataId: string;
|
||||
layout: 'FIELD' | 'CARD' | 'VIEW';
|
||||
fieldDisplayMode: FieldDisplayMode;
|
||||
};
|
||||
|
||||
+92
-14
@@ -60,19 +60,19 @@ describe('convertPageLayoutDraftToUpdateInput', () => {
|
||||
expect(result.tabs[0].widgets[0].id).toBe('w1');
|
||||
});
|
||||
|
||||
it('should filter out dynamic relation widgets', () => {
|
||||
const regularWidget = makeWidget({ id: 'w1' });
|
||||
const dynamicWidget = makeWidget({
|
||||
id: `${DYNAMIC_RELATION_WIDGET_ID_PREFIX}relation-1`,
|
||||
type: WidgetType.VIEW,
|
||||
it('should handle multiple widget types', () => {
|
||||
const fieldsWidget = makeWidget({ id: 'w1', type: WidgetType.FIELDS });
|
||||
const timelineWidget = makeWidget({
|
||||
id: 'w2',
|
||||
type: WidgetType.TIMELINE,
|
||||
});
|
||||
|
||||
const draft = makeDraft([makeTab('tab-1', [regularWidget, dynamicWidget])]);
|
||||
const draft = makeDraft([makeTab('tab-1', [fieldsWidget, timelineWidget])]);
|
||||
|
||||
const result = convertPageLayoutDraftToUpdateInput(draft);
|
||||
|
||||
expect(result.tabs[0].widgets).toHaveLength(1);
|
||||
expect(result.tabs[0].widgets[0].id).toBe('w1');
|
||||
expect(result.tabs[0].widgets).toHaveLength(2);
|
||||
expect(result.tabs[0].widgets.map((w) => w.id)).toEqual(['w1', 'w2']);
|
||||
});
|
||||
|
||||
it('should map gridPosition correctly', () => {
|
||||
@@ -121,24 +121,25 @@ describe('convertPageLayoutDraftToUpdateInput', () => {
|
||||
expect(result.tabs[1].id).toBe('tab-2');
|
||||
});
|
||||
|
||||
it('should keep all non-dynamic widgets when multiple widget types exist', () => {
|
||||
it('should keep all widgets when multiple widget types exist', () => {
|
||||
const fieldsWidget = makeWidget({ id: 'w1', type: WidgetType.FIELDS });
|
||||
const timelineWidget = makeWidget({
|
||||
id: 'w2',
|
||||
type: WidgetType.TIMELINE,
|
||||
});
|
||||
const dynamicWidget = makeWidget({
|
||||
id: `${DYNAMIC_RELATION_WIDGET_ID_PREFIX}rel`,
|
||||
const fieldWidget = makeWidget({
|
||||
id: 'w3',
|
||||
type: WidgetType.FIELD,
|
||||
});
|
||||
|
||||
const draft = makeDraft([
|
||||
makeTab('tab-1', [fieldsWidget, dynamicWidget, timelineWidget]),
|
||||
makeTab('tab-1', [fieldsWidget, fieldWidget, timelineWidget]),
|
||||
]);
|
||||
|
||||
const result = convertPageLayoutDraftToUpdateInput(draft);
|
||||
|
||||
expect(result.tabs[0].widgets).toHaveLength(2);
|
||||
expect(result.tabs[0].widgets.map((w) => w.id)).toEqual(['w1', 'w2']);
|
||||
expect(result.tabs[0].widgets).toHaveLength(3);
|
||||
expect(result.tabs[0].widgets.map((w) => w.id)).toEqual(['w1', 'w3', 'w2']);
|
||||
});
|
||||
|
||||
it('should produce VERTICAL_LIST position with index from widget.position when tab is VERTICAL_LIST', () => {
|
||||
@@ -326,4 +327,81 @@ describe('convertPageLayoutDraftToUpdateInput', () => {
|
||||
columnSpan: 6,
|
||||
});
|
||||
});
|
||||
|
||||
describe('shouldFilterDynamicRelationWidgets', () => {
|
||||
it('should filter out dynamic relation widgets when shouldFilterDynamicRelationWidgets is true', () => {
|
||||
const regularWidget = makeWidget({ id: 'w1' });
|
||||
const dynamicWidget = makeWidget({
|
||||
id: `${DYNAMIC_RELATION_WIDGET_ID_PREFIX}relation-1`,
|
||||
type: WidgetType.VIEW,
|
||||
});
|
||||
|
||||
const draft = makeDraft([
|
||||
makeTab('tab-1', [regularWidget, dynamicWidget]),
|
||||
]);
|
||||
|
||||
const result = convertPageLayoutDraftToUpdateInput(draft, {
|
||||
shouldFilterDynamicRelationWidgets: true,
|
||||
});
|
||||
|
||||
expect(result.tabs[0].widgets).toHaveLength(1);
|
||||
expect(result.tabs[0].widgets[0].id).toBe('w1');
|
||||
});
|
||||
|
||||
it('should keep all non-dynamic widgets when shouldFilterDynamicRelationWidgets is true and multiple widget types exist', () => {
|
||||
const fieldsWidget = makeWidget({ id: 'w1', type: WidgetType.FIELDS });
|
||||
const timelineWidget = makeWidget({
|
||||
id: 'w2',
|
||||
type: WidgetType.TIMELINE,
|
||||
});
|
||||
const dynamicWidget = makeWidget({
|
||||
id: `${DYNAMIC_RELATION_WIDGET_ID_PREFIX}rel`,
|
||||
});
|
||||
|
||||
const draft = makeDraft([
|
||||
makeTab('tab-1', [fieldsWidget, dynamicWidget, timelineWidget]),
|
||||
]);
|
||||
|
||||
const result = convertPageLayoutDraftToUpdateInput(draft, {
|
||||
shouldFilterDynamicRelationWidgets: true,
|
||||
});
|
||||
|
||||
expect(result.tabs[0].widgets).toHaveLength(2);
|
||||
expect(result.tabs[0].widgets.map((w) => w.id)).toEqual(['w1', 'w2']);
|
||||
});
|
||||
|
||||
it('should not filter dynamic relation widgets when shouldFilterDynamicRelationWidgets is false', () => {
|
||||
const regularWidget = makeWidget({ id: 'w1' });
|
||||
const dynamicWidget = makeWidget({
|
||||
id: `${DYNAMIC_RELATION_WIDGET_ID_PREFIX}relation-1`,
|
||||
type: WidgetType.VIEW,
|
||||
});
|
||||
|
||||
const draft = makeDraft([
|
||||
makeTab('tab-1', [regularWidget, dynamicWidget]),
|
||||
]);
|
||||
|
||||
const result = convertPageLayoutDraftToUpdateInput(draft, {
|
||||
shouldFilterDynamicRelationWidgets: false,
|
||||
});
|
||||
|
||||
expect(result.tabs[0].widgets).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('should not filter dynamic relation widgets by default', () => {
|
||||
const regularWidget = makeWidget({ id: 'w1' });
|
||||
const dynamicWidget = makeWidget({
|
||||
id: `${DYNAMIC_RELATION_WIDGET_ID_PREFIX}relation-1`,
|
||||
type: WidgetType.VIEW,
|
||||
});
|
||||
|
||||
const draft = makeDraft([
|
||||
makeTab('tab-1', [regularWidget, dynamicWidget]),
|
||||
]);
|
||||
|
||||
const result = convertPageLayoutDraftToUpdateInput(draft);
|
||||
|
||||
expect(result.tabs[0].widgets).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+2
-1
@@ -4,6 +4,7 @@ import { type PageLayoutTab } from '@/page-layout/types/PageLayoutTab';
|
||||
import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget';
|
||||
import { injectRelationWidgetsIntoLayout } from '@/page-layout/utils/injectRelationWidgetsIntoLayout';
|
||||
import {
|
||||
FieldDisplayMode,
|
||||
WidgetConfigurationType,
|
||||
WidgetType,
|
||||
} from '~/generated-metadata/graphql';
|
||||
@@ -195,7 +196,7 @@ describe('injectRelationWidgetsIntoLayout', () => {
|
||||
expect(injectedWidget.configuration).toEqual(
|
||||
expect.objectContaining({
|
||||
fieldMetadataId: 'f1',
|
||||
layout: 'CARD',
|
||||
fieldDisplayMode: FieldDisplayMode.CARD,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
+16
-9
@@ -40,19 +40,25 @@ const buildWidgetPosition = (
|
||||
|
||||
export const convertPageLayoutDraftToUpdateInput = (
|
||||
pageLayoutDraft: DraftPageLayout,
|
||||
options?: { shouldFilterDynamicRelationWidgets?: boolean },
|
||||
): UpdatePageLayoutWithTabsInput => {
|
||||
const shouldFilter = options?.shouldFilterDynamicRelationWidgets ?? false;
|
||||
|
||||
return {
|
||||
name: pageLayoutDraft.name,
|
||||
type: pageLayoutDraft.type,
|
||||
objectMetadataId: pageLayoutDraft.objectMetadataId ?? null,
|
||||
tabs: pageLayoutDraft.tabs.map((tab) => ({
|
||||
id: tab.id,
|
||||
title: tab.title,
|
||||
position: tab.position,
|
||||
layoutMode: tab.layoutMode,
|
||||
widgets: tab.widgets
|
||||
.filter((widget) => !isDynamicRelationWidget(widget))
|
||||
.map((widget, widgetIndex) => ({
|
||||
tabs: pageLayoutDraft.tabs.map((tab) => {
|
||||
const widgets = shouldFilter
|
||||
? tab.widgets.filter((widget) => !isDynamicRelationWidget(widget))
|
||||
: tab.widgets;
|
||||
|
||||
return {
|
||||
id: tab.id,
|
||||
title: tab.title,
|
||||
position: tab.position,
|
||||
layoutMode: tab.layoutMode,
|
||||
widgets: widgets.map((widget, widgetIndex) => ({
|
||||
id: widget.id,
|
||||
pageLayoutTabId: widget.pageLayoutTabId,
|
||||
title: widget.title,
|
||||
@@ -71,6 +77,7 @@ export const convertPageLayoutDraftToUpdateInput = (
|
||||
),
|
||||
configuration: widget.configuration ?? null,
|
||||
})),
|
||||
})),
|
||||
};
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget';
|
||||
import {
|
||||
FieldDisplayMode,
|
||||
PageLayoutTabLayoutMode,
|
||||
WidgetConfigurationType,
|
||||
WidgetType,
|
||||
@@ -28,7 +29,7 @@ export const createDefaultFieldWidget = ({
|
||||
__typename: 'FieldConfiguration',
|
||||
configurationType: WidgetConfigurationType.FIELD,
|
||||
fieldMetadataId,
|
||||
layout: 'CARD',
|
||||
fieldDisplayMode: FieldDisplayMode.CARD,
|
||||
},
|
||||
gridPosition: {
|
||||
__typename: 'GridPosition',
|
||||
|
||||
+2
-1
@@ -4,6 +4,7 @@ import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget';
|
||||
import { DYNAMIC_RELATION_WIDGET_ID_PREFIX } from '@/page-layout/utils/isDynamicRelationWidget';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
FieldDisplayMode,
|
||||
PageLayoutTabLayoutMode,
|
||||
WidgetConfigurationType,
|
||||
WidgetType,
|
||||
@@ -38,7 +39,7 @@ const getRelationFieldWidgetToInsert = (
|
||||
__typename: 'FieldConfiguration' as const,
|
||||
configurationType: WidgetConfigurationType.FIELD,
|
||||
fieldMetadataId: field.id,
|
||||
layout: 'CARD' as const,
|
||||
fieldDisplayMode: FieldDisplayMode.CARD,
|
||||
},
|
||||
isOverridden: false,
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
|
||||
+10
-9
@@ -7,14 +7,14 @@ import { expect, within } from 'storybook/test';
|
||||
import { CatalogDecorator, type CatalogStory } from 'twenty-ui/testing';
|
||||
|
||||
import { currentUserWorkspaceState } from '@/auth/states/currentUserWorkspaceState';
|
||||
import { ApolloCoreClientContext } from '@/object-metadata/contexts/ApolloCoreClientContext';
|
||||
import { isMinimalMetadataReadyState } from '@/metadata-store/states/isMinimalMetadataReadyState';
|
||||
import { setTestObjectMetadataItemsInMetadataStore } from '~/testing/utils/setTestObjectMetadataItemsInMetadataStore';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
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';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { CoreObjectNameSingular } from 'twenty-shared/types';
|
||||
import { setTestObjectMetadataItemsInMetadataStore } from '~/testing/utils/setTestObjectMetadataItemsInMetadataStore';
|
||||
|
||||
import { pageLayoutPersistedComponentState } from '@/page-layout/states/pageLayoutPersistedComponentState';
|
||||
import { WidgetRenderer } from '@/page-layout/widgets/components/WidgetRenderer';
|
||||
@@ -35,14 +35,15 @@ import { widgetCardHoveredComponentFamilyState } from '@/page-layout/widgets/sta
|
||||
import { type WidgetCardVariant } from '@/page-layout/widgets/types/WidgetCardVariant';
|
||||
import { LayoutRenderingProvider } from '@/ui/layout/contexts/LayoutRenderingContext';
|
||||
import {
|
||||
GraphOrderBy,
|
||||
WidgetType,
|
||||
AggregateOperations,
|
||||
AxisNameDisplay,
|
||||
BarChartLayout,
|
||||
FieldDisplayMode,
|
||||
GraphOrderBy,
|
||||
PageLayoutTabLayoutMode,
|
||||
PageLayoutType,
|
||||
WidgetConfigurationType,
|
||||
WidgetType,
|
||||
} from '~/generated-metadata/graphql';
|
||||
import { ChipGeneratorsDecorator } from '~/testing/decorators/ChipGeneratorsDecorator';
|
||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||
@@ -1023,7 +1024,7 @@ export const WithManyToOneRelationFieldWidget: Story = {
|
||||
__typename: 'FieldConfiguration',
|
||||
configurationType: WidgetConfigurationType.FIELD,
|
||||
fieldMetadataId: accountOwnerField.id,
|
||||
layout: 'FIELD',
|
||||
fieldDisplayMode: FieldDisplayMode.FIELD,
|
||||
},
|
||||
isOverridden: false,
|
||||
createdAt: '2024-01-01T00:00:00Z',
|
||||
@@ -1142,7 +1143,7 @@ export const WithOneToManyRelationFieldWidget: Story = {
|
||||
__typename: 'FieldConfiguration',
|
||||
configurationType: WidgetConfigurationType.FIELD,
|
||||
fieldMetadataId: companyPeopleField.id,
|
||||
layout: 'FIELD',
|
||||
fieldDisplayMode: FieldDisplayMode.FIELD,
|
||||
},
|
||||
isOverridden: false,
|
||||
createdAt: '2024-01-01T00:00:00Z',
|
||||
@@ -1253,7 +1254,7 @@ export const OneToManyRelationFieldWidgetWithSeeAllButton: Story = {
|
||||
__typename: 'FieldConfiguration',
|
||||
configurationType: WidgetConfigurationType.FIELD,
|
||||
fieldMetadataId: companyPeopleField.id,
|
||||
layout: 'FIELD',
|
||||
fieldDisplayMode: FieldDisplayMode.FIELD,
|
||||
},
|
||||
isOverridden: false,
|
||||
createdAt: '2024-01-01T00:00:00Z',
|
||||
|
||||
+5
-4
@@ -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}
|
||||
|
||||
+21
-20
@@ -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',
|
||||
|
||||
+2
-1
@@ -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;
|
||||
|
||||
|
||||
+3
-2
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user