From 04b0a65e73d953ef390f112675c39d53ec478fb0 Mon Sep 17 00:00:00 2001 From: Abdul Rahman <81605929+abdulrahmancodes@users.noreply.github.com> Date: Fri, 21 Nov 2025 20:42:41 +0530 Subject: [PATCH] feat: fix Command Menu Side Panel Layout (#15883) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Figma Design](https://www.figma.com/design/xt8O9mFeLl46C5InWwoMrN/Twenty?node-id=81380-344641&t=FpjWNOK2gZuDQQfr-0) --- > [!NOTE] > Adds a side panel layout for the Command Menu, routes modals into a local container, updates the top bar and context chips, and standardizes small button sizes. > > - **Command Menu**: > - **Side Panel Layout**: Introduces `CommandMenuSidePanelLayout` with animated width, hosts `CommandMenuRouter`, and provides a modal container via `ModalContainerContext`. > - **Top Bar**: Redesign (`CommandMenuTopBar`) with back icon, optional AI sparkles action, compact height (`COMMAND_MENU_SEARCH_BAR_HEIGHT=40`), and updated placeholder. > - **Context Chips**: Adds `CommandMenuLastContextChip` and `CommandMenuRecordInfo`; extends `CommandMenuContextChip` with `page` prop; updates `CommandMenuContextChipGroups` to render last chip as record info when applicable. > - **Container Simplification**: `CommandMenuContainer` simplified to just provide contexts and `AgentChatProvider`. > - **Modal System**: > - Adds `ModalContainerContext` and updates `Modal` to portal into provided container; `Modal.Backdrop` supports `isInContainer`. > - Updates usages (e.g., `UserOrMetadataLoader`, `ActionModal`) to align with new modal behavior. > - **Page Integration**: > - Replaces `PageBody` with `CommandMenuSidePanelLayout` in `RecordShowPage` and `RecordIndexContainerGater`. > - Removes global `CommandMenuRouter` from `DefaultLayout` (keeps keyboard shortcuts). > - **UI/Styling**: > - Standardizes several buttons to `size="small"` (e.g., command actions, open record, options, reply, workflow footer). > - Adjusts `ShowPageSubContainer` styling when rendered inside command menu. > - Storybook tests updated for new placeholder text. > > Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 81fcaa145618a2fa1c3e11e2dc88fe832c51374c. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot). --------- Co-authored-by: Félix Malfait Co-authored-by: Lucas Bordeau Co-authored-by: Devessier Co-authored-by: Aman Raj <92664006+araj00@users.noreply.github.com> Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com> Co-authored-by: Paul Rastoin <45004772+prastoin@users.noreply.github.com> --- .../actions/components/ActionModal.tsx | 25 +- .../components/ActionOpenSidePanelPage.tsx | 6 +- .../DeleteMultipleRecordsAction.tsx | 10 +- .../components/DeleteSingleRecordAction.tsx | 12 +- .../components/CmdEnterActionButton.tsx | 2 +- .../CommandMenuActionMenuDropdown.tsx | 6 +- .../RecordShowRightDrawerOpenRecordButton.tsx | 2 +- .../ai/components/AIChatThreadGroup.tsx | 5 +- .../ai/hooks/useCreateNewAIChatThread.ts | 2 +- .../components/CommandMenuBackButton.tsx | 82 +++++++ .../components/CommandMenuContainer.tsx | 31 +-- .../components/CommandMenuContextChip.tsx | 5 + .../CommandMenuContextChipGroups.tsx | 89 ------- ...nuContextChipGroupsWithRecordSelection.tsx | 64 ----- .../components/CommandMenuPageInfo.tsx | 68 ++++++ .../components/CommandMenuPageLayoutInfo.tsx | 160 ++++++++++++ .../components/CommandMenuRecordInfo.tsx | 162 ++++++++++++ .../components/CommandMenuTopBar.tsx | 123 +++++----- .../CommandMenuTopBarRightCornerIcon.tsx | 63 +++++ .../CommandMenuWorkflowStepInfo.tsx | 230 ++++++++++++++++++ .../__stories__/CommandMenu.stories.tsx | 9 +- .../CommandMenuContextChipGroups.stories.tsx | 97 -------- .../hooks/usePageLayoutHeaderInfo.ts | 151 ++++++++++++ .../CommandMenuNavigationHistoryDropdownId.ts | 2 + .../constants/CommandMenuSearchBarHeight.ts | 2 +- .../CommandMenuSearchBarHeightMobile.ts | 1 + .../constants/CommandMenuSidePanelWidth.ts | 1 + .../hooks/__tests__/useCommandMenu.test.tsx | 43 ++++ ...ilterActionsWithCommandMenuSearch.test.tsx | 102 ++++++++ .../hooks/useCommandMenuHotKeys.ts | 2 +- .../hooks/useOpenAskAIPageInCommandMenu.ts | 17 +- .../useOpenRecordsSearchPageInCommandMenu.ts | 4 + .../hooks/useWorkflowCommandMenu.ts | 57 ++++- .../CommandMenuMessageThreadPage.tsx | 1 + .../CommandMenuPageLayoutGraphFilter.tsx | 16 -- .../CommandMenuPageLayoutGraphTypeSelect.tsx | 26 -- .../CommandMenuPageLayoutIframeSettings.tsx | 20 -- .../CommandMenuPageLayoutTabSettings.tsx | 18 -- .../hooks/useNavigatePageLayoutCommandMenu.ts | 3 + ...commandMenuWorkflowStepIdComponentState.ts | 10 + .../CommandMenuWorkflowCreateStepContent.tsx | 1 + ...CommandMenuWorkflowEditStepTypeContent.tsx | 1 + ...ndMenuWorkflowSelectTriggerTypeContent.tsx | 1 + .../MainNavigationDrawerFixedItems.tsx | 2 +- .../components/CommandMenuLayout.tsx | 151 ++++++++++++ .../components/CommandMenuPageLayout.tsx | 144 +++++++++++ .../components/RecordIndexContainerGater.tsx | 6 +- .../components/RecordTableWidthEffect.tsx | 45 ++-- .../states/tableWidthResizeIsActivedState.ts | 6 + .../components/RecordTitleCell.tsx | 2 +- .../components/RecordTitleCellFieldInput.tsx | 2 +- .../RecordTitleCellTextFieldInput.tsx | 2 +- .../components/RecordTitleDoubleTextInput.tsx | 2 +- .../RecordTitleFullNameFieldInput.tsx | 2 +- .../hooks/useEditPageLayoutWidget.ts | 2 + .../input/editor/components/BlockEditor.tsx | 2 +- .../ui/layout/modal/components/Modal.tsx | 31 ++- .../modal/contexts/ModalContainerContext.tsx | 13 + .../layout/page/components/DefaultLayout.tsx | 20 +- .../ui/layout/page/components/PageBody.tsx | 5 +- .../components/SelectableListItem.tsx | 20 +- .../components/ShowPageSubContainer.tsx | 24 +- .../workflow/utils/getAgentIdFromStep.ts | 39 +++ .../components/WorkflowDiagramCanvasBase.tsx | 98 +++----- .../WorkflowDiagramEmptyTriggerReadonly.tsx | 1 + .../WorkflowDiagramStepNodeEditable.tsx | 1 + .../WorkflowDiagramStepNodeReadonly.tsx | 1 + .../components/WorkflowRunStepInputDetail.tsx | 27 +- .../WorkflowRunStepOutputDetail.tsx | 44 +--- .../components/WorkflowStepFooter.tsx | 7 +- .../__tests__/useUpdateAgentLabel.test.tsx | 94 +++++++ .../hooks/useUpdateAgentLabel.ts | 34 +++ .../components/WorkflowEditActionAiAgent.tsx | 39 --- .../WorkflowEditActionServerlessFunction.tsx | 27 +- ...rkflowReadonlyActionServerlessFunction.tsx | 21 -- .../WorkflowEditActionCreateRecord.tsx | 30 --- .../WorkflowEditActionDeleteRecord.tsx | 27 -- .../WorkflowEditActionSendEmail.tsx | 32 +-- .../WorkflowEditActionUpdateRecord.tsx | 27 -- .../WorkflowEditActionUpsertRecord.tsx | 31 --- ...WorkflowEditActionCreateRecord.stories.tsx | 9 - ...WorkflowEditActionDeleteRecord.stories.tsx | 27 -- .../WorkflowEditActionFindRecords.stories.tsx | 9 - .../WorkflowEditActionSendEmail.stories.tsx | 3 - ...WorkflowEditActionUpdateRecord.stories.tsx | 27 -- .../components/WorkflowEditActionDelay.tsx | 29 --- .../components/WorkflowEditActionFilter.tsx | 30 --- .../WorkflowEditActionFindRecords.tsx | 27 -- .../WorkflowEditActionFormBuilder.tsx | 29 --- .../WorkflowEditActionFormFiller.tsx | 19 -- .../WorkflowEditActionFormBuilder.stories.tsx | 9 - .../useWorkflowActionHeader.test.tsx | 223 ----------------- .../hooks/useWorkflowActionHeader.ts | 38 --- .../WorkflowEditActionHttpRequest.tsx | 25 +- .../WorkflowEditActionHttpRequest.stories.tsx | 4 - .../components/WorkflowEditActionIterator.tsx | 29 --- .../WorkflowEditTriggerCronForm.tsx | 34 --- .../WorkflowEditTriggerDatabaseEventForm.tsx | 27 -- .../components/WorkflowEditTriggerManual.tsx | 29 --- .../WorkflowEditTriggerWebhookForm.tsx | 34 +-- .../hooks/useVariableDropdown.ts | 1 + .../pages/object-record/RecordShowPage.tsx | 6 +- 102 files changed, 1913 insertions(+), 1548 deletions(-) create mode 100644 packages/twenty-front/src/modules/command-menu/components/CommandMenuBackButton.tsx delete mode 100644 packages/twenty-front/src/modules/command-menu/components/CommandMenuContextChipGroups.tsx delete mode 100644 packages/twenty-front/src/modules/command-menu/components/CommandMenuContextChipGroupsWithRecordSelection.tsx create mode 100644 packages/twenty-front/src/modules/command-menu/components/CommandMenuPageInfo.tsx create mode 100644 packages/twenty-front/src/modules/command-menu/components/CommandMenuPageLayoutInfo.tsx create mode 100644 packages/twenty-front/src/modules/command-menu/components/CommandMenuRecordInfo.tsx create mode 100644 packages/twenty-front/src/modules/command-menu/components/CommandMenuTopBarRightCornerIcon.tsx create mode 100644 packages/twenty-front/src/modules/command-menu/components/CommandMenuWorkflowStepInfo.tsx delete mode 100644 packages/twenty-front/src/modules/command-menu/components/__stories__/CommandMenuContextChipGroups.stories.tsx create mode 100644 packages/twenty-front/src/modules/command-menu/components/hooks/usePageLayoutHeaderInfo.ts create mode 100644 packages/twenty-front/src/modules/command-menu/constants/CommandMenuNavigationHistoryDropdownId.ts create mode 100644 packages/twenty-front/src/modules/command-menu/constants/CommandMenuSearchBarHeightMobile.ts create mode 100644 packages/twenty-front/src/modules/command-menu/constants/CommandMenuSidePanelWidth.ts create mode 100644 packages/twenty-front/src/modules/command-menu/hooks/__tests__/useFilterActionsWithCommandMenuSearch.test.tsx create mode 100644 packages/twenty-front/src/modules/command-menu/pages/workflow/states/commandMenuWorkflowStepIdComponentState.ts create mode 100644 packages/twenty-front/src/modules/object-record/components/CommandMenuLayout.tsx create mode 100644 packages/twenty-front/src/modules/object-record/components/CommandMenuPageLayout.tsx create mode 100644 packages/twenty-front/src/modules/object-record/record-table/states/tableWidthResizeIsActivedState.ts create mode 100644 packages/twenty-front/src/modules/ui/layout/modal/contexts/ModalContainerContext.tsx create mode 100644 packages/twenty-front/src/modules/workflow/utils/getAgentIdFromStep.ts create mode 100644 packages/twenty-front/src/modules/workflow/workflow-steps/hooks/__tests__/useUpdateAgentLabel.test.tsx create mode 100644 packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useUpdateAgentLabel.ts delete mode 100644 packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/hooks/__tests__/useWorkflowActionHeader.test.tsx delete mode 100644 packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/hooks/useWorkflowActionHeader.ts diff --git a/packages/twenty-front/src/modules/action-menu/actions/components/ActionModal.tsx b/packages/twenty-front/src/modules/action-menu/actions/components/ActionModal.tsx index 2ba6e1e718..d42265c52e 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/components/ActionModal.tsx +++ b/packages/twenty-front/src/modules/action-menu/actions/components/ActionModal.tsx @@ -1,5 +1,4 @@ import { type ReactNode, useContext } from 'react'; -import { createPortal } from 'react-dom'; import { ActionDisplay } from '@/action-menu/actions/display/components/ActionDisplay'; import { ActionConfigContext } from '@/action-menu/contexts/ActionConfigContext'; @@ -63,19 +62,17 @@ export const ActionModal = ({ return ( <> - {isModalOpened && - createPortal( - , - document.body, - )} + {isModalOpened && ( + + )} ); }; diff --git a/packages/twenty-front/src/modules/action-menu/actions/components/ActionOpenSidePanelPage.tsx b/packages/twenty-front/src/modules/action-menu/actions/components/ActionOpenSidePanelPage.tsx index 6482d421f4..4a54d8b732 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/components/ActionOpenSidePanelPage.tsx +++ b/packages/twenty-front/src/modules/action-menu/actions/components/ActionOpenSidePanelPage.tsx @@ -2,11 +2,12 @@ import { ActionDisplay } from '@/action-menu/actions/display/components/ActionDi import { ActionConfigContext } from '@/action-menu/contexts/ActionConfigContext'; import { useNavigateCommandMenu } from '@/command-menu/hooks/useNavigateCommandMenu'; import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState'; +import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState'; import { type CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { type MessageDescriptor } from '@lingui/core'; import { t } from '@lingui/core/macro'; import { useContext } from 'react'; -import { useSetRecoilState } from 'recoil'; +import { useRecoilValue, useSetRecoilState } from 'recoil'; import { type IconComponent } from 'twenty-ui/display'; export const ActionOpenSidePanelPage = ({ @@ -28,6 +29,8 @@ export const ActionOpenSidePanelPage = ({ const setCommandMenuSearchState = useSetRecoilState(commandMenuSearchState); + const isCommandMenuOpened = useRecoilValue(isCommandMenuOpenedState); + if (!actionConfig) { return null; } @@ -39,6 +42,7 @@ export const ActionOpenSidePanelPage = ({ page, pageTitle: t(pageTitle), pageIcon, + resetNavigationStack: isCommandMenuOpened, }); if (shouldResetSearchState) { diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/multiple-records/components/DeleteMultipleRecordsAction.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/multiple-records/components/DeleteMultipleRecordsAction.tsx index 6400d11453..0f7deb15ac 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/multiple-records/components/DeleteMultipleRecordsAction.tsx +++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/multiple-records/components/DeleteMultipleRecordsAction.tsx @@ -1,4 +1,4 @@ -import { ActionModal } from '@/action-menu/actions/components/ActionModal'; +import { Action } from '@/action-menu/actions/components/Action'; import { ActionConfigContext } from '@/action-menu/contexts/ActionConfigContext'; import { computeProgressText } from '@/action-menu/utils/computeProgressText'; import { getActionLabel } from '@/action-menu/utils/getActionLabel'; @@ -14,7 +14,6 @@ import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/ import { useRecordIndexIdFromCurrentContextStore } from '@/object-record/record-index/hooks/useRecordIndexIdFromCurrentContextStore'; import { useResetTableRowSelection } from '@/object-record/record-table/hooks/internal/useResetTableRowSelection'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; -import { t } from '@lingui/core/macro'; import { useContext } from 'react'; import { isDefined } from 'twenty-shared/utils'; @@ -92,12 +91,7 @@ export const DeleteMultipleRecordsAction = () => { return ( - + ); }; diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/components/DeleteSingleRecordAction.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/components/DeleteSingleRecordAction.tsx index a29d602a73..99597b27bc 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/components/DeleteSingleRecordAction.tsx +++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/components/DeleteSingleRecordAction.tsx @@ -1,11 +1,10 @@ -import { ActionModal } from '@/action-menu/actions/components/ActionModal'; +import { Action } from '@/action-menu/actions/components/Action'; import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow'; import { useDeleteFavorite } from '@/favorites/hooks/useDeleteFavorite'; import { useFavorites } from '@/favorites/hooks/useFavorites'; import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord'; import { useRecordIndexIdFromCurrentContextStore } from '@/object-record/record-index/hooks/useRecordIndexIdFromCurrentContextStore'; import { useResetTableRowSelection } from '@/object-record/record-table/hooks/internal/useResetTableRowSelection'; -import { t } from '@lingui/core/macro'; import { isDefined } from 'twenty-shared/utils'; export const DeleteSingleRecordAction = () => { @@ -37,12 +36,5 @@ export const DeleteSingleRecordAction = () => { await deleteOneRecord(recordId); }; - return ( - - ); + return ; }; diff --git a/packages/twenty-front/src/modules/action-menu/components/CmdEnterActionButton.tsx b/packages/twenty-front/src/modules/action-menu/components/CmdEnterActionButton.tsx index 2be7c8699d..b4a8ddaddd 100644 --- a/packages/twenty-front/src/modules/action-menu/components/CmdEnterActionButton.tsx +++ b/packages/twenty-front/src/modules/action-menu/components/CmdEnterActionButton.tsx @@ -25,7 +25,7 @@ export const CmdEnterActionButton = ({ title={title} variant={disabled ? 'secondary' : 'primary'} accent="blue" - size="medium" + size="small" onClick={onClick} disabled={disabled} hotkeys={[getOsControlSymbol(), '⏎']} diff --git a/packages/twenty-front/src/modules/action-menu/components/CommandMenuActionMenuDropdown.tsx b/packages/twenty-front/src/modules/action-menu/components/CommandMenuActionMenuDropdown.tsx index d9ec84428a..90c6c2c5b9 100644 --- a/packages/twenty-front/src/modules/action-menu/components/CommandMenuActionMenuDropdown.tsx +++ b/packages/twenty-front/src/modules/action-menu/components/CommandMenuActionMenuDropdown.tsx @@ -66,7 +66,11 @@ export const CommandMenuActionMenuDropdown = () => { dropdownId={dropdownId} data-select-disable clickableComponent={ -