Files
twenty/packages/twenty-front/src/modules/page-layout/components/PageLayoutSingleTabRenderer.tsx
T
Félix Malfait b05d2406ec fix(twenty-front): stop field widget layout dropdown from crashing th… (#23784)
…e record page

RecordTableWidgetViewDraftInitEffect read the page layout edit mode and
the page layout instance id from context, but the widget settings side
panel renders outside the page layout tree. Opening the Layout picker on
a relation field widget displayed as a table threw
"PageLayoutEditModeContext Context not found" and took down the whole
record page.

Both values are now passed in by the caller.

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/23784?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-08-05 10:21:28 +02:00

123 lines
4.8 KiB
TypeScript

import { SummaryCard } from '@/object-record/record-show/components/SummaryCard';
import { PageLayoutWidgetDndProvider } from '@/page-layout/components/dnd/PageLayoutWidgetDndProvider';
import { PageLayoutContent } from '@/page-layout/components/PageLayoutContent';
import { PageLayoutEditModeProvider } from '@/page-layout/components/PageLayoutEditModeProvider';
import { PageLayoutInitializationQueryEffect } from '@/page-layout/components/PageLayoutInitializationQueryEffect';
import { PageLayoutRecordPageCustomizationSessionRegistrationEffect } from '@/page-layout/components/PageLayoutRecordPageCustomizationSessionRegistrationEffect';
import { PageLayoutContentProvider } from '@/page-layout/contexts/PageLayoutContentContext';
import { useCurrentPageLayoutOrThrow } from '@/page-layout/hooks/useCurrentPageLayoutOrThrow';
import { useIsPageLayoutInEditMode } from '@/page-layout/hooks/useIsPageLayoutInEditMode';
import { usePageLayoutTabWithVisibleWidgetsOrThrow } from '@/page-layout/hooks/usePageLayoutTabWithVisibleWidgetsOrThrow';
import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext';
import { pageLayoutIsInitializedComponentState } from '@/page-layout/states/pageLayoutIsInitializedComponentState';
import { RecordTableWidgetViewDraftsInitializationEffect } from '@/page-layout/widgets/record-table/components/RecordTableWidgetViewDraftsInitializationEffect';
import { getTabLayoutMode } from '@/page-layout/utils/getTabLayoutMode';
import { getTabListInstanceIdFromPageLayoutAndRecord } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutAndRecord';
import { getTabPresentation } from '@/page-layout/utils/getTabPresentation';
import { sortTabsByPosition } from '@/page-layout/utils/sortTabsByPosition';
import { useLayoutRenderingContext } from '@/ui/layout/contexts/LayoutRenderingContext';
import { useTargetRecord } from '@/ui/layout/contexts/useTargetRecord';
import { TabListComponentInstanceContext } from '@/ui/layout/tab-list/states/contexts/TabListComponentInstanceContext';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
type PageLayoutSingleTabRendererProps = {
pageLayoutId: string;
};
const PageLayoutSingleTabRendererContent = () => {
const pageLayoutIsInitialized = useAtomComponentStateValue(
pageLayoutIsInitializedComponentState,
);
if (!pageLayoutIsInitialized) {
return null;
}
return <PageLayoutSingleTabRendererInner />;
};
const PageLayoutSingleTabRendererInner = () => {
const { currentPageLayout } = useCurrentPageLayoutOrThrow();
const targetRecordIdentifier = useTargetRecord();
const { isInSidePanel } = useLayoutRenderingContext();
const isPageLayoutInEditMode = useIsPageLayoutInEditMode();
const sortedActiveTabs = sortTabsByPosition(
currentPageLayout.tabs.filter((tab) => tab.isActive),
);
const firstTab = sortedActiveTabs[0];
const firstTabWithVisibleWidgets = usePageLayoutTabWithVisibleWidgetsOrThrow(
firstTab.id,
);
const layoutMode = getTabLayoutMode({
tab: firstTabWithVisibleWidgets,
pageLayoutType: currentPageLayout.type,
});
const presentation = getTabPresentation({
widgets: firstTabWithVisibleWidgets.widgets,
layoutMode,
isInEditMode: isPageLayoutInEditMode,
});
return (
<>
<SummaryCard
objectNameSingular={targetRecordIdentifier.targetObjectNameSingular}
objectRecordId={targetRecordIdentifier.id}
isInSidePanel={isInSidePanel}
/>
<PageLayoutContentProvider
value={{
tabId: firstTab.id,
layoutMode,
presentation,
}}
>
<PageLayoutWidgetDndProvider>
<PageLayoutContent />
</PageLayoutWidgetDndProvider>
</PageLayoutContentProvider>
</>
);
};
export const PageLayoutSingleTabRenderer = ({
pageLayoutId,
}: PageLayoutSingleTabRendererProps) => {
const { targetRecordIdentifier, layoutType } = useLayoutRenderingContext();
const tabListInstanceId = getTabListInstanceIdFromPageLayoutAndRecord({
pageLayoutId,
layoutType,
targetRecordIdentifier,
});
return (
<PageLayoutComponentInstanceContext.Provider
value={{
instanceId: pageLayoutId,
}}
>
<TabListComponentInstanceContext.Provider
value={{
instanceId: tabListInstanceId,
}}
>
<PageLayoutEditModeProvider
layoutType={layoutType}
pageLayoutId={pageLayoutId}
>
<PageLayoutInitializationQueryEffect pageLayoutId={pageLayoutId} />
<PageLayoutRecordPageCustomizationSessionRegistrationEffect />
<RecordTableWidgetViewDraftsInitializationEffect />
<PageLayoutSingleTabRendererContent />
</PageLayoutEditModeProvider>
</TabListComponentInstanceContext.Provider>
</PageLayoutComponentInstanceContext.Provider>
);
};