Differentiate edit mode behavior for record pages vs dashboards (#17550)
Temporary disable tabs edition for record page layouts https://github.com/user-attachments/assets/c1f5d7fd-f125-4fb0-bf9c-96fadec14cd2 --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Devessier <29370468+Devessier@users.noreply.github.com> Co-authored-by: Baptiste Devessier <baptiste@devessier.fr>
This commit is contained in:
+21
-13
@@ -14,6 +14,7 @@ import { getScrollWrapperInstanceIdFromPageLayoutId } from '@/page-layout/utils/
|
||||
import { getTabListInstanceIdFromPageLayoutId } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutId';
|
||||
import { getTabsByDisplayMode } from '@/page-layout/utils/getTabsByDisplayMode';
|
||||
import { getTabsWithVisibleWidgets } from '@/page-layout/utils/getTabsWithVisibleWidgets';
|
||||
import { shouldEnableTabEditingFeatures } from '@/page-layout/utils/shouldEnableTabEditingFeatures';
|
||||
import { sortTabsByPosition } from '@/page-layout/utils/sortTabsByPosition';
|
||||
import { useLayoutRenderingContext } from '@/ui/layout/contexts/LayoutRenderingContext';
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||
@@ -66,23 +67,29 @@ export const PageLayoutRendererContent = () => {
|
||||
);
|
||||
const { navigatePageLayoutCommandMenu } = useNavigatePageLayoutCommandMenu();
|
||||
|
||||
const handleAddTab = isPageLayoutInEditMode
|
||||
? () => {
|
||||
const newTabId = createPageLayoutTab(t`Untitled`);
|
||||
setTabSettingsOpenTabId(newTabId);
|
||||
navigatePageLayoutCommandMenu({
|
||||
commandMenuPage: CommandMenuPages.PageLayoutTabSettings,
|
||||
focusTitleInput: true,
|
||||
});
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
if (!isDefined(currentPageLayout)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleAddTab =
|
||||
isPageLayoutInEditMode &&
|
||||
shouldEnableTabEditingFeatures(currentPageLayout.type)
|
||||
? () => {
|
||||
const newTabId = createPageLayoutTab(t`Untitled`);
|
||||
setTabSettingsOpenTabId(newTabId);
|
||||
navigatePageLayoutCommandMenu({
|
||||
commandMenuPage: CommandMenuPages.PageLayoutTabSettings,
|
||||
focusTitleInput: true,
|
||||
});
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const canEnableTabEditing =
|
||||
isPageLayoutInEditMode &&
|
||||
shouldEnableTabEditingFeatures(currentPageLayout.type);
|
||||
|
||||
const tabsWithVisibleWidgets = getTabsWithVisibleWidgets({
|
||||
tabs: currentPageLayout.tabs,
|
||||
isMobile,
|
||||
@@ -123,8 +130,9 @@ export const PageLayoutRendererContent = () => {
|
||||
behaveAsLinks={!isInRightDrawer && !isPageLayoutInEditMode}
|
||||
componentInstanceId={tabListInstanceId}
|
||||
onAddTab={handleAddTab}
|
||||
isReorderEnabled={isPageLayoutInEditMode}
|
||||
onReorder={isPageLayoutInEditMode ? reorderTabs : undefined}
|
||||
isReorderEnabled={canEnableTabEditing}
|
||||
onReorder={canEnableTabEditing ? reorderTabs : undefined}
|
||||
pageLayoutType={currentPageLayout.type}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
@@ -37,11 +37,13 @@ import { isPageLayoutInEditModeComponentState } from '@/page-layout/states/isPag
|
||||
import { pageLayoutTabListCurrentDragDroppableIdComponentState } from '@/page-layout/states/pageLayoutTabListCurrentDragDroppableIdComponentState';
|
||||
import { pageLayoutTabSettingsOpenTabIdComponentState } from '@/page-layout/states/pageLayoutTabSettingsOpenTabIdComponentState';
|
||||
import { type PageLayoutTab } from '@/page-layout/types/PageLayoutTab';
|
||||
import { shouldEnableTabEditingFeatures } from '@/page-layout/utils/shouldEnableTabEditingFeatures';
|
||||
import { TabListFromUrlOptionalEffect } from '@/ui/layout/tab-list/components/TabListFromUrlOptionalEffect';
|
||||
import { type SingleTabProps } from '@/ui/layout/tab-list/types/SingleTabProps';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { type PageLayoutType } from '~/generated/graphql';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
box-sizing: border-box;
|
||||
@@ -75,6 +77,7 @@ type PageLayoutTabListProps = Omit<TabListProps, 'tabs'> & {
|
||||
onAddTab?: () => void;
|
||||
onReorder?: (result: DropResult, provided: ResponderProvided) => boolean;
|
||||
behaveAsLinks: boolean;
|
||||
pageLayoutType: PageLayoutType;
|
||||
};
|
||||
|
||||
export const PageLayoutTabList = ({
|
||||
@@ -88,6 +91,7 @@ export const PageLayoutTabList = ({
|
||||
onAddTab,
|
||||
isReorderEnabled,
|
||||
onReorder,
|
||||
pageLayoutType,
|
||||
}: PageLayoutTabListProps) => {
|
||||
const { getIcon } = useIcons();
|
||||
|
||||
@@ -229,17 +233,24 @@ export const PageLayoutTabList = ({
|
||||
|
||||
const handleSelectTab = useCallback(
|
||||
(tabId: string) => {
|
||||
if (isPageLayoutInEditMode && activeTabId === tabId) {
|
||||
const shouldOpenSettings =
|
||||
isPageLayoutInEditMode &&
|
||||
shouldEnableTabEditingFeatures(pageLayoutType);
|
||||
|
||||
if (shouldOpenSettings && activeTabId === tabId) {
|
||||
openTabSettings(tabId);
|
||||
return;
|
||||
}
|
||||
if (isPageLayoutInEditMode && isTabSettingsOpen) {
|
||||
|
||||
if (shouldOpenSettings && isTabSettingsOpen) {
|
||||
openTabSettings(tabId);
|
||||
}
|
||||
|
||||
selectTab(tabId);
|
||||
},
|
||||
[
|
||||
isPageLayoutInEditMode,
|
||||
pageLayoutType,
|
||||
activeTabId,
|
||||
isTabSettingsOpen,
|
||||
openTabSettings,
|
||||
@@ -249,18 +260,25 @@ export const PageLayoutTabList = ({
|
||||
|
||||
const handleSelectTabFromDropdown = useCallback(
|
||||
(tabId: string) => {
|
||||
if (isPageLayoutInEditMode && activeTabId === tabId) {
|
||||
const shouldOpenSettings =
|
||||
isPageLayoutInEditMode &&
|
||||
shouldEnableTabEditingFeatures(pageLayoutType);
|
||||
|
||||
if (shouldOpenSettings && activeTabId === tabId) {
|
||||
openTabSettings(tabId);
|
||||
closeOverflowDropdown();
|
||||
return;
|
||||
}
|
||||
if (isPageLayoutInEditMode && isTabSettingsOpen) {
|
||||
|
||||
if (shouldOpenSettings && isTabSettingsOpen) {
|
||||
openTabSettings(tabId);
|
||||
}
|
||||
|
||||
selectTabFromDropdown(tabId);
|
||||
},
|
||||
[
|
||||
isPageLayoutInEditMode,
|
||||
pageLayoutType,
|
||||
activeTabId,
|
||||
isTabSettingsOpen,
|
||||
openTabSettings,
|
||||
@@ -336,6 +354,7 @@ export const PageLayoutTabList = ({
|
||||
onSelect={handleSelectTabFromDropdown}
|
||||
visibleTabCount={visibleTabCount}
|
||||
onClose={closeOverflowDropdown}
|
||||
pageLayoutType={pageLayoutType}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
+8
-1
@@ -18,6 +18,7 @@ import { PageLayoutComponentInstanceContext } from '@/page-layout/states/context
|
||||
import { isPageLayoutInEditModeComponentState } from '@/page-layout/states/isPageLayoutInEditModeComponentState';
|
||||
import { isPageLayoutTabDraggingComponentState } from '@/page-layout/states/isPageLayoutTabDraggingComponentState';
|
||||
import { pageLayoutTabSettingsOpenTabIdComponentState } from '@/page-layout/states/pageLayoutTabSettingsOpenTabIdComponentState';
|
||||
import { shouldEnableTabEditingFeatures } from '@/page-layout/utils/shouldEnableTabEditingFeatures';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
@@ -28,6 +29,7 @@ import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/com
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
|
||||
import { useContext } from 'react';
|
||||
import { type PageLayoutType } from '~/generated/graphql';
|
||||
|
||||
const StyledOverflowDropdownListDraggableWrapper = styled.div`
|
||||
display: flex;
|
||||
@@ -48,6 +50,7 @@ type PageLayoutTabListReorderableOverflowDropdownProps = {
|
||||
onSelect: (tabId: string) => void;
|
||||
visibleTabCount: number;
|
||||
onClose: () => void;
|
||||
pageLayoutType: PageLayoutType;
|
||||
};
|
||||
|
||||
export const PageLayoutTabListReorderableOverflowDropdown = ({
|
||||
@@ -60,6 +63,7 @@ export const PageLayoutTabListReorderableOverflowDropdown = ({
|
||||
onSelect,
|
||||
visibleTabCount,
|
||||
onClose,
|
||||
pageLayoutType,
|
||||
}: PageLayoutTabListReorderableOverflowDropdownProps) => {
|
||||
const theme = useTheme();
|
||||
const context = useContext(TabListComponentInstanceContext);
|
||||
@@ -74,6 +78,9 @@ export const PageLayoutTabListReorderableOverflowDropdown = ({
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const shouldShowEditButton =
|
||||
isPageLayoutInEditMode && shouldEnableTabEditingFeatures(pageLayoutType);
|
||||
|
||||
const isTabDragging = useRecoilComponentValue(
|
||||
isPageLayoutTabDraggingComponentState,
|
||||
instanceId,
|
||||
@@ -201,7 +208,7 @@ export const PageLayoutTabListReorderableOverflowDropdown = ({
|
||||
: () => handleTabSelect(tab.id)
|
||||
}
|
||||
disabled={disabled}
|
||||
showEditButton={isPageLayoutInEditMode}
|
||||
showEditButton={shouldShowEditButton}
|
||||
onEditClick={handleEditClick}
|
||||
/>
|
||||
</div>
|
||||
|
||||
+2
@@ -11,6 +11,7 @@ import { PageLayoutTabListEffect } from '@/page-layout/components/PageLayoutTabL
|
||||
import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext';
|
||||
import { type PageLayoutTab } from '@/page-layout/types/PageLayoutTab';
|
||||
import { calculateNewPosition } from '@/ui/layout/draggable-list/utils/calculateNewPosition';
|
||||
import { PageLayoutType } from '~/generated/graphql';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
border: 1px solid ${({ theme }) => theme.border.color.strong};
|
||||
@@ -163,6 +164,7 @@ const PageLayoutTabListPlayground = ({
|
||||
onAddTab={isReorderEnabled ? handleAddTab : undefined}
|
||||
isReorderEnabled={isReorderEnabled}
|
||||
onReorder={isReorderEnabled ? handleReorder : undefined}
|
||||
pageLayoutType={PageLayoutType.DASHBOARD}
|
||||
/>
|
||||
</StyledContainer>
|
||||
);
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
import { shouldEnableTabEditingFeatures } from '@/page-layout/utils/shouldEnableTabEditingFeatures';
|
||||
import { PageLayoutType } from '~/generated/graphql';
|
||||
|
||||
describe('shouldEnableTabEditingFeatures', () => {
|
||||
it('should return true for DASHBOARD layout type', () => {
|
||||
const result = shouldEnableTabEditingFeatures(PageLayoutType.DASHBOARD);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for RECORD_PAGE layout type', () => {
|
||||
const result = shouldEnableTabEditingFeatures(PageLayoutType.RECORD_PAGE);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for RECORD_INDEX layout type', () => {
|
||||
const result = shouldEnableTabEditingFeatures(PageLayoutType.RECORD_INDEX);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
describe('behavior validation', () => {
|
||||
it('should enable tab editing features only for dashboards', () => {
|
||||
// Dashboards should allow adding tabs and opening settings on click
|
||||
expect(shouldEnableTabEditingFeatures(PageLayoutType.DASHBOARD)).toBe(
|
||||
true,
|
||||
);
|
||||
|
||||
// Record pages should NOT allow adding tabs or opening settings on click
|
||||
expect(shouldEnableTabEditingFeatures(PageLayoutType.RECORD_PAGE)).toBe(
|
||||
false,
|
||||
);
|
||||
|
||||
// Record index pages should NOT allow adding tabs or opening settings on click
|
||||
expect(shouldEnableTabEditingFeatures(PageLayoutType.RECORD_INDEX)).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
import { PageLayoutType } from '~/generated/graphql';
|
||||
|
||||
export const shouldEnableTabEditingFeatures = (
|
||||
pageLayoutType: PageLayoutType,
|
||||
): boolean => {
|
||||
return pageLayoutType === PageLayoutType.DASHBOARD;
|
||||
};
|
||||
Reference in New Issue
Block a user