diff --git a/packages/twenty-front/src/modules/page-layout/components/PageLayoutRendererContent.tsx b/packages/twenty-front/src/modules/page-layout/components/PageLayoutRendererContent.tsx
index 25b7538167..cd632779f4 100644
--- a/packages/twenty-front/src/modules/page-layout/components/PageLayoutRendererContent.tsx
+++ b/packages/twenty-front/src/modules/page-layout/components/PageLayoutRendererContent.tsx
@@ -116,6 +116,9 @@ export const PageLayoutRendererContent = () => {
{(sortedTabs.length > 1 || isPageLayoutInEditMode) && (
& {
tabs: PageLayoutTab[];
+ defaultTabIdToFocusOnMobileAndSidePanel?: string;
};
export const PageLayoutTabListEffect = ({
tabs,
onChangeTab,
componentInstanceId,
+ defaultTabIdToFocusOnMobileAndSidePanel,
}: PageLayoutTabListEffectProps) => {
const [activeTabId, setActiveTabId] = useRecoilComponentState(
activeTabIdComponentState,
componentInstanceId,
);
- const activeTabExists = tabs.some((tab) => tab.id === activeTabId);
- const initialActiveTabId = activeTabExists ? activeTabId : tabs[0]?.id;
+ const isMobile = useIsMobile();
+ const { isInRightDrawer } = useLayoutRenderingContext();
+
+ const initialActiveTabId = getPageLayoutTabListInitialActiveTabId({
+ activeTabId,
+ tabs,
+ defaultTabIdToFocusOnMobileAndSidePanel,
+ isMobile,
+ isInRightDrawer,
+ });
useEffect(() => {
setActiveTabId(initialActiveTabId);
diff --git a/packages/twenty-front/src/modules/page-layout/constants/DefaultWorkflowRunPageLayout.ts b/packages/twenty-front/src/modules/page-layout/constants/DefaultWorkflowRunPageLayout.ts
index c882e6c11c..d19be39f4f 100644
--- a/packages/twenty-front/src/modules/page-layout/constants/DefaultWorkflowRunPageLayout.ts
+++ b/packages/twenty-front/src/modules/page-layout/constants/DefaultWorkflowRunPageLayout.ts
@@ -15,6 +15,7 @@ export const DEFAULT_WORKFLOW_RUN_PAGE_LAYOUT: PageLayout = {
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
+ defaultTabIdToFocusOnMobileAndSidePanel: 'workflow-run-tab-flow',
tabs: [
// Fields tab (position 100)
{
diff --git a/packages/twenty-front/src/modules/page-layout/constants/DefaultWorkflowVersionPageLayout.ts b/packages/twenty-front/src/modules/page-layout/constants/DefaultWorkflowVersionPageLayout.ts
index d65c28bd51..b2538e98b9 100644
--- a/packages/twenty-front/src/modules/page-layout/constants/DefaultWorkflowVersionPageLayout.ts
+++ b/packages/twenty-front/src/modules/page-layout/constants/DefaultWorkflowVersionPageLayout.ts
@@ -15,6 +15,7 @@ export const DEFAULT_WORKFLOW_VERSION_PAGE_LAYOUT: PageLayout = {
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
+ defaultTabIdToFocusOnMobileAndSidePanel: 'workflow-version-tab-flow',
tabs: [
// Fields tab (position 100)
{
diff --git a/packages/twenty-front/src/modules/page-layout/types/PageLayout.ts b/packages/twenty-front/src/modules/page-layout/types/PageLayout.ts
index 017aacf957..a06ca3d6a5 100644
--- a/packages/twenty-front/src/modules/page-layout/types/PageLayout.ts
+++ b/packages/twenty-front/src/modules/page-layout/types/PageLayout.ts
@@ -3,4 +3,5 @@ import { type PageLayout as PageLayoutGenerated } from '~/generated/graphql';
export type PageLayout = Omit & {
tabs: PageLayoutTab[];
+ defaultTabIdToFocusOnMobileAndSidePanel?: string;
};
diff --git a/packages/twenty-front/src/modules/page-layout/utils/__tests__/getPageLayoutTabListInitialActiveTabId.test.ts b/packages/twenty-front/src/modules/page-layout/utils/__tests__/getPageLayoutTabListInitialActiveTabId.test.ts
new file mode 100644
index 0000000000..457ce48477
--- /dev/null
+++ b/packages/twenty-front/src/modules/page-layout/utils/__tests__/getPageLayoutTabListInitialActiveTabId.test.ts
@@ -0,0 +1,226 @@
+import { type PageLayoutTab } from '@/page-layout/types/PageLayoutTab';
+import { getPageLayoutTabListInitialActiveTabId } from '../getPageLayoutTabListInitialActiveTabId';
+
+describe('getPageLayoutTabListInitialActiveTabId', () => {
+ const createMockTab = (id: string): PageLayoutTab => ({
+ id,
+ pageLayoutId: 'page-layout-1',
+ title: `Tab ${id}`,
+ position: 0,
+ widgets: [],
+ createdAt: '2024-01-01T00:00:00.000Z',
+ updatedAt: '2024-01-01T00:00:00.000Z',
+ });
+
+ const mockTabs: PageLayoutTab[] = [
+ createMockTab('tab-1'),
+ createMockTab('tab-2'),
+ createMockTab('tab-3'),
+ ];
+
+ describe('when activeTabId exists in tabs', () => {
+ it('should return activeTabId regardless of context', () => {
+ const result = getPageLayoutTabListInitialActiveTabId({
+ activeTabId: 'tab-2',
+ tabs: mockTabs,
+ defaultTabIdToFocusOnMobileAndSidePanel: 'tab-3',
+ isMobile: true,
+ isInRightDrawer: true,
+ });
+
+ expect(result).toBe('tab-2');
+ });
+
+ it('should return activeTabId even when not on mobile or in drawer', () => {
+ const result = getPageLayoutTabListInitialActiveTabId({
+ activeTabId: 'tab-1',
+ tabs: mockTabs,
+ defaultTabIdToFocusOnMobileAndSidePanel: 'tab-3',
+ isMobile: false,
+ isInRightDrawer: false,
+ });
+
+ expect(result).toBe('tab-1');
+ });
+ });
+
+ describe('when activeTabId does not exist in tabs', () => {
+ describe('on mobile or in right drawer', () => {
+ it('should return defaultTabIdToFocusOnMobileAndSidePanel when on mobile and default exists', () => {
+ const result = getPageLayoutTabListInitialActiveTabId({
+ activeTabId: 'non-existent-tab',
+ tabs: mockTabs,
+ defaultTabIdToFocusOnMobileAndSidePanel: 'tab-3',
+ isMobile: true,
+ isInRightDrawer: false,
+ });
+
+ expect(result).toBe('tab-3');
+ });
+
+ it('should return defaultTabIdToFocusOnMobileAndSidePanel when in right drawer and default exists', () => {
+ const result = getPageLayoutTabListInitialActiveTabId({
+ activeTabId: null,
+ tabs: mockTabs,
+ defaultTabIdToFocusOnMobileAndSidePanel: 'tab-2',
+ isMobile: false,
+ isInRightDrawer: true,
+ });
+
+ expect(result).toBe('tab-2');
+ });
+
+ it('should return defaultTabIdToFocusOnMobileAndSidePanel when on mobile and in right drawer', () => {
+ const result = getPageLayoutTabListInitialActiveTabId({
+ activeTabId: null,
+ tabs: mockTabs,
+ defaultTabIdToFocusOnMobileAndSidePanel: 'tab-1',
+ isMobile: true,
+ isInRightDrawer: true,
+ });
+
+ expect(result).toBe('tab-1');
+ });
+
+ it('should fallback to first tab when defaultTabIdToFocusOnMobileAndSidePanel does not exist', () => {
+ const result = getPageLayoutTabListInitialActiveTabId({
+ activeTabId: null,
+ tabs: mockTabs,
+ defaultTabIdToFocusOnMobileAndSidePanel: 'non-existent-tab',
+ isMobile: true,
+ isInRightDrawer: false,
+ });
+
+ expect(result).toBe('tab-1');
+ });
+
+ it('should fallback to first tab when defaultTabIdToFocusOnMobileAndSidePanel is undefined', () => {
+ const result = getPageLayoutTabListInitialActiveTabId({
+ activeTabId: null,
+ tabs: mockTabs,
+ defaultTabIdToFocusOnMobileAndSidePanel: undefined,
+ isMobile: true,
+ isInRightDrawer: false,
+ });
+
+ expect(result).toBe('tab-1');
+ });
+ });
+
+ describe('not on mobile and not in right drawer', () => {
+ it('should return first tab when default is provided', () => {
+ const result = getPageLayoutTabListInitialActiveTabId({
+ activeTabId: null,
+ tabs: mockTabs,
+ defaultTabIdToFocusOnMobileAndSidePanel: 'tab-3',
+ isMobile: false,
+ isInRightDrawer: false,
+ });
+
+ expect(result).toBe('tab-1');
+ });
+
+ it('should return first tab when default is not provided', () => {
+ const result = getPageLayoutTabListInitialActiveTabId({
+ activeTabId: null,
+ tabs: mockTabs,
+ defaultTabIdToFocusOnMobileAndSidePanel: undefined,
+ isMobile: false,
+ isInRightDrawer: false,
+ });
+
+ expect(result).toBe('tab-1');
+ });
+ });
+ });
+
+ describe('edge cases', () => {
+ it('should return null when tabs array is empty', () => {
+ const result = getPageLayoutTabListInitialActiveTabId({
+ activeTabId: null,
+ tabs: [],
+ defaultTabIdToFocusOnMobileAndSidePanel: 'tab-1',
+ isMobile: false,
+ isInRightDrawer: false,
+ });
+
+ expect(result).toBe(null);
+ });
+
+ it('should return null when tabs array is empty even with default on mobile', () => {
+ const result = getPageLayoutTabListInitialActiveTabId({
+ activeTabId: null,
+ tabs: [],
+ defaultTabIdToFocusOnMobileAndSidePanel: 'tab-1',
+ isMobile: true,
+ isInRightDrawer: false,
+ });
+
+ expect(result).toBe(null);
+ });
+
+ it('should handle activeTabId being null', () => {
+ const result = getPageLayoutTabListInitialActiveTabId({
+ activeTabId: null,
+ tabs: mockTabs,
+ defaultTabIdToFocusOnMobileAndSidePanel: undefined,
+ isMobile: false,
+ isInRightDrawer: false,
+ });
+
+ expect(result).toBe('tab-1');
+ });
+
+ it('should handle single tab array', () => {
+ const singleTab = [createMockTab('only-tab')];
+
+ const result = getPageLayoutTabListInitialActiveTabId({
+ activeTabId: null,
+ tabs: singleTab,
+ defaultTabIdToFocusOnMobileAndSidePanel: undefined,
+ isMobile: false,
+ isInRightDrawer: false,
+ });
+
+ expect(result).toBe('only-tab');
+ });
+ });
+
+ describe('priority order', () => {
+ it('should prioritize activeTabId over default and context', () => {
+ const result = getPageLayoutTabListInitialActiveTabId({
+ activeTabId: 'tab-1',
+ tabs: mockTabs,
+ defaultTabIdToFocusOnMobileAndSidePanel: 'tab-2',
+ isMobile: true,
+ isInRightDrawer: true,
+ });
+
+ expect(result).toBe('tab-1');
+ });
+
+ it('should prioritize valid default over first tab when on mobile', () => {
+ const result = getPageLayoutTabListInitialActiveTabId({
+ activeTabId: 'non-existent',
+ tabs: mockTabs,
+ defaultTabIdToFocusOnMobileAndSidePanel: 'tab-3',
+ isMobile: true,
+ isInRightDrawer: false,
+ });
+
+ expect(result).toBe('tab-3');
+ });
+
+ it('should fallback to first tab when default is invalid on mobile', () => {
+ const result = getPageLayoutTabListInitialActiveTabId({
+ activeTabId: 'non-existent',
+ tabs: mockTabs,
+ defaultTabIdToFocusOnMobileAndSidePanel: 'invalid-tab',
+ isMobile: true,
+ isInRightDrawer: false,
+ });
+
+ expect(result).toBe('tab-1');
+ });
+ });
+});
diff --git a/packages/twenty-front/src/modules/page-layout/utils/getPageLayoutTabListInitialActiveTabId.ts b/packages/twenty-front/src/modules/page-layout/utils/getPageLayoutTabListInitialActiveTabId.ts
new file mode 100644
index 0000000000..e4b580fc92
--- /dev/null
+++ b/packages/twenty-front/src/modules/page-layout/utils/getPageLayoutTabListInitialActiveTabId.ts
@@ -0,0 +1,41 @@
+import { type PageLayoutTab } from '@/page-layout/types/PageLayoutTab';
+import { isDefined } from 'twenty-shared/utils';
+
+type GetPageLayoutTabListInitialActiveTabIdParams = {
+ activeTabId: string | null;
+ tabs: PageLayoutTab[];
+ defaultTabIdToFocusOnMobileAndSidePanel?: string;
+ isMobile: boolean;
+ isInRightDrawer: boolean;
+};
+
+export const getPageLayoutTabListInitialActiveTabId = ({
+ activeTabId,
+ tabs,
+ defaultTabIdToFocusOnMobileAndSidePanel,
+ isMobile,
+ isInRightDrawer,
+}: GetPageLayoutTabListInitialActiveTabIdParams): string | null => {
+ const activeTabExists = tabs.some((tab) => tab.id === activeTabId);
+
+ if (activeTabExists) {
+ return activeTabId;
+ }
+
+ const isOnMobileOrSidePanel = isMobile || isInRightDrawer;
+
+ if (
+ isOnMobileOrSidePanel &&
+ isDefined(defaultTabIdToFocusOnMobileAndSidePanel)
+ ) {
+ const defaultTabExists = tabs.some(
+ (tab) => tab.id === defaultTabIdToFocusOnMobileAndSidePanel,
+ );
+
+ if (defaultTabExists) {
+ return defaultTabIdToFocusOnMobileAndSidePanel;
+ }
+ }
+
+ return tabs[0]?.id ?? null;
+};