fix: add missing LayoutRenderingProvider in SettingsApplicationCustomTab (#19679)

## Summary

- `SettingsApplicationCustomTab` renders `FrontComponentRenderer` which
calls `useFrontComponentExecutionContext` →
`useLayoutRenderingContext()`, but the settings page never provided a
`LayoutRenderingProvider`
- Every other render site (side panel, command menu, record pages) wraps
`FrontComponentRenderer` with this provider — it was just missed here
- Opening the "Custom" tab in Settings → Applications crashes with:
`LayoutRenderingContext Context not found`
- Fix: wrap with `LayoutRenderingProvider` using `DASHBOARD` layout type
and no target record, matching the pattern used in
`SidePanelFrontComponentPage`

## Test plan

- [ ] Open Settings → Applications → any app with a custom settings tab
- [ ] Click the "Custom" tab — should render the front component without
crashing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Lazare Rossillon
2026-04-16 12:01:28 +02:00
committed by GitHub
parent 60701c2cf9
commit d54092b0e2
6 changed files with 29 additions and 62 deletions
@@ -17,9 +17,6 @@ const mockCloseSidePanelMenu = jest.fn();
const mockSetCommandMenuItemProgress = jest.fn();
let mockCurrentUser: { id: string } | null = { id: 'user-123' };
let mockTargetRecordIdentifier: { id: string } | undefined = {
id: 'record-456',
};
jest.mock('~/hooks/useNavigateApp', () => ({
useNavigateApp: () => mockNavigateApp,
@@ -86,12 +83,6 @@ jest.mock('@/ui/utilities/state/jotai/hooks/useSetAtomFamilyState', () => ({
useSetAtomFamilyState: () => mockSetCommandMenuItemProgress,
}));
jest.mock('@/ui/layout/contexts/LayoutRenderingContext', () => ({
useLayoutRenderingContext: () => ({
targetRecordIdentifier: mockTargetRecordIdentifier,
}),
}));
const FRONT_COMPONENT_ID = 'fc-test-id';
const COMMAND_MENU_ITEM_ID = 'cmd-item-1';
@@ -99,7 +90,6 @@ describe('useFrontComponentExecutionContext', () => {
beforeEach(() => {
jest.clearAllMocks();
mockCurrentUser = { id: 'user-123' };
mockTargetRecordIdentifier = { id: 'record-456' };
});
describe('executionContext', () => {
@@ -107,6 +97,7 @@ describe('useFrontComponentExecutionContext', () => {
const { result } = renderHook(() =>
useFrontComponentExecutionContext({
frontComponentId: FRONT_COMPONENT_ID,
recordId: 'record-456',
}),
);
@@ -129,9 +120,7 @@ describe('useFrontComponentExecutionContext', () => {
expect(result.current.executionContext.userId).toBeNull();
});
it('should return null recordId when no target record', () => {
mockTargetRecordIdentifier = undefined;
it('should return null recordId when no recordId provided', () => {
const { result } = renderHook(() =>
useFrontComponentExecutionContext({
frontComponentId: FRONT_COMPONENT_ID,
@@ -14,7 +14,6 @@ import { useNavigateSidePanel } from '@/side-panel/hooks/useNavigateSidePanel';
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
import { sidePanelSearchState } from '@/side-panel/states/sidePanelSearchState';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { useLayoutRenderingContext } from '@/ui/layout/contexts/LayoutRenderingContext';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { useSetAtomFamilyState } from '@/ui/utilities/state/jotai/hooks/useSetAtomFamilyState';
import { assertUnreachable, isDefined } from 'twenty-shared/utils';
@@ -24,9 +23,11 @@ import { useNavigateApp } from '~/hooks/useNavigateApp';
export const useFrontComponentExecutionContext = ({
frontComponentId,
commandMenuItemId,
recordId,
}: {
frontComponentId: string;
commandMenuItemId?: string;
recordId?: string;
}): {
executionContext: FrontComponentExecutionContext;
frontComponentHostCommunicationApi: FrontComponentHostCommunicationApi;
@@ -123,12 +124,10 @@ export const useFrontComponentExecutionContext = ({
}
};
const { targetRecordIdentifier } = useLayoutRenderingContext();
const executionContext: FrontComponentExecutionContext = {
frontComponentId,
userId: currentUser?.id ?? null,
recordId: targetRecordIdentifier?.id ?? null,
recordId: recordId ?? null,
};
const unmountFrontComponent: FrontComponentHostCommunicationApi['unmountFrontComponent'] =