From 5c24cf4084db880f658d51ce14525c0e81ca2bd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Bosi?= <71827178+bosiraphael@users.noreply.github.com> Date: Wed, 5 Feb 2025 14:25:29 +0100 Subject: [PATCH] 320 new command menu navigation bar buttons (#10018) Closes https://github.com/twentyhq/core-team-issues/issues/320 https://github.com/user-attachments/assets/8082e986-07fd-46fb-9652-ad006aa9dac8 --- ...cordIndexActionMenuBarAllActionsButton.tsx | 11 +++- .../components/CommandMenuContainer.tsx | 6 +- .../components/CommandMenuContextChip.tsx | 31 ++++++++-- .../components/CommandMenuTopBar.tsx | 57 +++++++++++++++---- .../__stories__/CommandMenu.stories.tsx | 52 ++++++++++++++++- .../hooks/__tests__/useCommandMenu.test.tsx | 4 +- .../command-menu/hooks/useCommandMenu.ts | 49 +++++++++------- .../PageHeaderOpenCommandMenuButton.tsx | 15 ++++- 8 files changed, 183 insertions(+), 42 deletions(-) diff --git a/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuBarAllActionsButton.tsx b/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuBarAllActionsButton.tsx index 9805b6375..6a0d8067c 100644 --- a/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuBarAllActionsButton.tsx +++ b/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuBarAllActionsButton.tsx @@ -1,4 +1,5 @@ import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; +import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { IconLayoutSidebarRightExpand, getOsControlSymbol } from 'twenty-ui'; @@ -38,11 +39,17 @@ const StyledSeparator = styled.div<{ size: 'sm' | 'md' }>` export const RecordIndexActionMenuBarAllActionsButton = () => { const theme = useTheme(); - const { openCommandMenu } = useCommandMenu(); + const { navigateCommandMenu } = useCommandMenu(); return ( <> - openCommandMenu()}> + + navigateCommandMenu({ + page: CommandMenuPages.Root, + }) + } + > All Actions diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuContainer.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuContainer.tsx index b5e2f027d..255010704 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuContainer.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuContainer.tsx @@ -1,4 +1,5 @@ import { RecordActionMenuEntriesSetter } from '@/action-menu/actions/record-actions/components/RecordActionMenuEntriesSetter'; +import { NoSelectionRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKey'; import { RecordAgnosticActionMenuEntriesSetter } from '@/action-menu/actions/record-agnostic-actions/components/RecordAgnosticActionMenuEntriesSetter'; import { RunWorkflowRecordAgnosticActionMenuEntriesSetter } from '@/action-menu/actions/record-agnostic-actions/components/RunWorkflowRecordAgnosticActionMenuEntriesSetter'; import { RecordAgnosticActionsKey } from '@/action-menu/actions/record-agnostic-actions/types/RecordAgnosticActionsKey'; @@ -91,7 +92,10 @@ export const CommandMenuContainer = ({ value={{ isInRightDrawer: false, onActionExecutedCallback: ({ key }) => { - if (key !== RecordAgnosticActionsKey.SEARCH_RECORDS) { + if ( + key !== RecordAgnosticActionsKey.SEARCH_RECORDS && + key !== NoSelectionRecordActionKeys.CREATE_NEW_RECORD + ) { toggleCommandMenu(); } }, diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuContextChip.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuContextChip.tsx index 895ca9356..02fe9fa61 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuContextChip.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuContextChip.tsx @@ -1,7 +1,12 @@ import styled from '@emotion/styled'; +import { isNonEmptyString } from '@sniptt/guards'; import { Fragment } from 'react/jsx-runtime'; +import { isDefined } from 'twenty-shared'; -const StyledChip = styled.div` +const StyledChip = styled.button<{ + withText: boolean; + onClick?: () => void; +}>` align-items: center; background: ${({ theme }) => theme.background.transparent.light}; border: 1px solid ${({ theme }) => theme.border.color.medium}; @@ -10,11 +15,21 @@ const StyledChip = styled.div` display: flex; gap: ${({ theme }) => theme.spacing(1)}; height: ${({ theme }) => theme.spacing(6)}; - padding: 0 ${({ theme }) => theme.spacing(2)}; + /* If the chip has text, we add extra padding to have a more balanced design */ + padding: 0 + ${({ theme, withText }) => (withText ? theme.spacing(2) : theme.spacing(1))}; font-size: ${({ theme }) => theme.font.size.sm}; font-weight: ${({ theme }) => theme.font.weight.medium}; line-height: ${({ theme }) => theme.text.lineHeight.lg}; color: ${({ theme }) => theme.font.color.primary}; + cursor: ${({ onClick }) => (isDefined(onClick) ? 'pointer' : 'default')}; + + &:hover { + background: ${({ onClick, theme }) => + isDefined(onClick) + ? theme.background.transparent.medium + : theme.background.transparent.light}; + } `; const StyledIconsContainer = styled.div` @@ -24,18 +39,26 @@ const StyledIconsContainer = styled.div` export const CommandMenuContextChip = ({ Icons, text, + onClick, + testId, }: { Icons: React.ReactNode[]; text?: string; + onClick?: () => void; + testId?: string; }) => { return ( - + {Icons.map((Icon, index) => ( {Icon} ))} - {text} + {text && {text}} ); }; diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuTopBar.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuTopBar.tsx index 220b9a15a..77832b49a 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuTopBar.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuTopBar.tsx @@ -9,12 +9,21 @@ import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchS import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { useLingui } from '@lingui/react/macro'; import { useRecoilState, useRecoilValue } from 'recoil'; import { isDefined } from 'twenty-shared'; -import { IconX, LightIconButton, useIsMobile } from 'twenty-ui'; +import { + Button, + IconChevronLeft, + IconX, + LightIconButton, + getOsControlSymbol, + useIsMobile, +} from 'twenty-ui'; +import { FeatureFlagKey } from '~/generated-metadata/graphql'; const StyledInputContainer = styled.div` align-items: center; @@ -81,7 +90,7 @@ export const CommandMenuTopBar = () => { const isMobile = useIsMobile(); - const { closeCommandMenu } = useCommandMenu(); + const { closeCommandMenu, goBackFromCommandMenu } = useCommandMenu(); const contextStoreCurrentObjectMetadataId = useRecoilComponentValueV2( contextStoreCurrentObjectMetadataIdComponentState, @@ -93,9 +102,22 @@ export const CommandMenuTopBar = () => { const theme = useTheme(); + const isCommandMenuV2Enabled = useIsFeatureEnabled( + FeatureFlagKey.IsCommandMenuV2Enabled, + ); + return ( + {isCommandMenuV2Enabled && ( + ]} + onClick={() => { + goBackFromCommandMenu(); + }} + testId="command-menu-go-back-button" + /> + )} {commandMenuPage !== CommandMenuPages.SearchRecords && isDefined(contextStoreCurrentObjectMetadataId) && ( { )} {!isMobile && ( - - - + <> + {isCommandMenuV2Enabled ? ( +