From 088cd3025ace01a57d1016b77d4008111b54fad6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Bosi?= <71827178+bosiraphael@users.noreply.github.com> Date: Fri, 19 Dec 2025 14:31:57 +0100 Subject: [PATCH] Implement new version of the side panel sub header (#16683) ## Before CleanShot 2025-12-18 at 17 08 09@2x ## After CleanShot 2025-12-18 at 17 07 49@2x --- .../components/CommandMenuBackButton.tsx | 8 +- .../components/SidePanelHeader.tsx | 148 ------------------ .../components/SidePanelHeaderSyncEffect.tsx | 17 -- .../__stories__/SidePanelHeader.stories.tsx | 101 ------------ .../CommandMenuSubPageNavigationHeader.tsx | 39 ----- .../SidePanelSubPageNavigationHeader.tsx | 40 +++++ .../components/ChartFiltersSettings.tsx | 46 +++--- 7 files changed, 69 insertions(+), 330 deletions(-) delete mode 100644 packages/twenty-front/src/modules/command-menu/components/SidePanelHeader.tsx delete mode 100644 packages/twenty-front/src/modules/command-menu/components/SidePanelHeaderSyncEffect.tsx delete mode 100644 packages/twenty-front/src/modules/command-menu/components/__stories__/SidePanelHeader.stories.tsx delete mode 100644 packages/twenty-front/src/modules/command-menu/pages/common/components/CommandMenuSubPageNavigationHeader.tsx create mode 100644 packages/twenty-front/src/modules/command-menu/pages/common/components/SidePanelSubPageNavigationHeader.tsx diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuBackButton.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuBackButton.tsx index 70bd56c4dd..c5cc326503 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuBackButton.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuBackButton.tsx @@ -13,11 +13,13 @@ import { MenuItem } from 'twenty-ui/navigation'; const StyledNavigationIcon = styled.div` align-items: center; - color: ${({ theme }) => theme.font.color.secondary}; cursor: pointer; display: flex; justify-content: center; - margin-right: ${({ theme }) => theme.spacing(1)}; +`; + +const StyledIconChevronLeft = styled(IconChevronLeft)` + color: ${({ theme }) => theme.font.color.secondary}; `; export const CommandMenuBackButton = () => { @@ -50,7 +52,7 @@ export const CommandMenuBackButton = () => { clickableComponent={ theme.background.secondary}; - border-bottom: 1px solid ${({ theme }) => theme.border.color.medium}; - display: flex; - flex-direction: row; - padding: ${({ theme }) => theme.spacing(4)}; - gap: ${({ theme }) => theme.spacing(2)}; -`; - -const StyledHeaderInfo = styled.div` - display: flex; - flex-direction: column; - width: 100%; - gap: ${({ theme }) => theme.spacing(2)}; -`; - -const StyledHeaderTitle = styled.div` - color: ${({ theme }) => theme.font.color.primary}; - font-weight: ${({ theme }) => theme.font.weight.semiBold}; - font-size: ${({ theme }) => theme.font.size.xl}; - width: fit-content; - max-width: 420px; - & > input:disabled { - color: ${({ theme }) => theme.font.color.primary}; - } -`; - -const StyledHeaderType = styled.div` - color: ${({ theme }) => theme.font.color.tertiary}; - padding-left: ${({ theme }) => theme.spacing(1)}; -`; - -const StyledHeaderIconContainer = styled.div` - align-self: flex-start; - display: flex; - justify-content: center; - align-items: center; - background-color: ${({ theme }) => theme.background.transparent.light}; - border-radius: ${({ theme }) => theme.border.radius.sm}; - padding: ${({ theme }) => theme.spacing(2)}; -`; - -type SidePanelHeaderProps = { - Icon: IconComponent; - iconColor: string; - initialTitle: string; - headerType: string; - iconTooltip?: string; -} & ( - | { - disabled: true; - onTitleChange?: never; - } - | { - disabled?: boolean; - onTitleChange: (newTitle: string) => void; - } -); - -export const SidePanelHeader = ({ - Icon, - iconColor, - initialTitle, - headerType, - disabled, - onTitleChange, - iconTooltip, -}: SidePanelHeaderProps) => { - const [shouldFocusTitleInput, setShouldFocusTitleInput] = - useRecoilComponentState(commandMenuShouldFocusTitleInputComponentState); - - const theme = useTheme(); - - const [title, setTitle] = useState(initialTitle); - - const { updateCommandMenuPageInfo } = useUpdateCommandMenuPageInfo(); - - const handleChange = (newTitle: string) => { - setTitle(newTitle); - }; - - const saveTitle = () => { - onTitleChange?.(title); - updateCommandMenuPageInfo({ - pageTitle: title, - pageIcon: Icon, - }); - }; - - const tooltipId = `side-panel-icon-tooltip-${headerType.replace(/\s+/g, '-')}`; - - return ( - <> - - - - - - {iconTooltip && ( - - )} - - - { - setTitle(initialTitle); - }} - onClickOutside={saveTitle} - onTab={saveTitle} - onShiftTab={saveTitle} - shouldOpen={shouldFocusTitleInput} - onOpen={() => setShouldFocusTitleInput(false)} - /> - - {headerType} - - - - ); -}; diff --git a/packages/twenty-front/src/modules/command-menu/components/SidePanelHeaderSyncEffect.tsx b/packages/twenty-front/src/modules/command-menu/components/SidePanelHeaderSyncEffect.tsx deleted file mode 100644 index 0eb5df13ae..0000000000 --- a/packages/twenty-front/src/modules/command-menu/components/SidePanelHeaderSyncEffect.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { useEffect } from 'react'; - -type SidePanelHeaderTitleSyncEffectProps = { - initialTitle: string; - setTitle: (title: string) => void; -}; - -export const SidePanelHeaderTitleSyncEffect = ({ - initialTitle, - setTitle, -}: SidePanelHeaderTitleSyncEffectProps) => { - useEffect(() => { - setTitle(initialTitle); - }, [initialTitle, setTitle]); - - return null; -}; diff --git a/packages/twenty-front/src/modules/command-menu/components/__stories__/SidePanelHeader.stories.tsx b/packages/twenty-front/src/modules/command-menu/components/__stories__/SidePanelHeader.stories.tsx deleted file mode 100644 index e1963a8c67..0000000000 --- a/packages/twenty-front/src/modules/command-menu/components/__stories__/SidePanelHeader.stories.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import { type Meta, type StoryObj } from '@storybook/react'; -import { expect, fn, userEvent, waitFor, within } from '@storybook/test'; -import { IconPlus } from 'twenty-ui/display'; -import { ComponentDecorator } from 'twenty-ui/testing'; -import { THEME_LIGHT } from 'twenty-ui/theme'; -import { CommandMenuPageComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuPageComponentInstanceContext'; -import { SidePanelHeader } from '../SidePanelHeader'; - -const meta: Meta = { - title: 'Modules/CommandMenu/SidePanelHeader', - component: SidePanelHeader, - args: { - onTitleChange: fn(), - }, - argTypes: {}, - decorators: [ - ComponentDecorator, - (Story) => ( - - - - ), - ], - parameters: { - disableHotkeyInitialization: true, - }, -}; - -export default meta; - -type Story = StoryObj; - -export const Default: Story = { - args: { - headerType: 'Action', - iconColor: THEME_LIGHT.font.color.tertiary, - initialTitle: 'Create Record', - Icon: IconPlus, - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - - expect(await canvas.findByText('Create Record')).toBeVisible(); - expect(await canvas.findByText('Action')).toBeVisible(); - }, -}; - -export const EditableTitle: Story = { - args: { - headerType: 'Action', - iconColor: THEME_LIGHT.font.color.tertiary, - initialTitle: 'Create Record', - Icon: IconPlus, - onTitleChange: fn(), - }, - play: async ({ canvasElement, args }) => { - const canvas = within(canvasElement); - const titleText = await canvas.findByText('Create Record'); - await userEvent.click(titleText); - - const titleInput = await canvas.findByDisplayValue('Create Record'); - - const NEW_TITLE = 'New Title'; - - await userEvent.clear(titleInput); - await userEvent.type(titleInput, NEW_TITLE); - - await userEvent.keyboard('{Enter}'); - - await waitFor(() => { - expect(args.onTitleChange).toHaveBeenCalledWith(NEW_TITLE); - }); - }, -}; - -export const Disabled: Story = { - args: { - headerType: 'Action', - iconColor: THEME_LIGHT.font.color.tertiary, - initialTitle: 'Create Record', - Icon: IconPlus, - disabled: true, - onTitleChange: fn(), - }, - play: async ({ canvasElement, args }) => { - const canvas = within(canvasElement); - - const titleText = await canvas.findByText('Create Record'); - - expect(window.getComputedStyle(titleText).cursor).toBe('default'); - - await userEvent.click(titleText); - - const titleInput = canvas.queryByDisplayValue('Create Record'); - expect(titleInput).not.toBeInTheDocument(); - - expect(args.onTitleChange).not.toHaveBeenCalled(); - }, -}; diff --git a/packages/twenty-front/src/modules/command-menu/pages/common/components/CommandMenuSubPageNavigationHeader.tsx b/packages/twenty-front/src/modules/command-menu/pages/common/components/CommandMenuSubPageNavigationHeader.tsx deleted file mode 100644 index 00f9cdef08..0000000000 --- a/packages/twenty-front/src/modules/command-menu/pages/common/components/CommandMenuSubPageNavigationHeader.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import styled from '@emotion/styled'; -import { IconChevronLeft } from 'twenty-ui/display'; -import { IconButton } from 'twenty-ui/input'; - -const StyledContainer = styled.div` - align-items: center; - display: flex; - - flex-direction: row; -`; - -const StyledTextContainer = styled.div` - color: ${({ theme }) => theme.font.color.primary}; - font-size: ${({ theme }) => theme.font.size.md}; - - padding-left: ${({ theme }) => theme.spacing(1)}; - padding-bottom: ${({ theme }) => theme.spacing(0.2)}; -`; - -type CommandMenuSubPageNavigationHeaderProps = { - title: string; - onBackClick: () => void; -}; - -export const CommandMenuSubPageNavigationHeader = ({ - onBackClick, - title, -}: CommandMenuSubPageNavigationHeaderProps) => { - return ( - - - {title} - - ); -}; diff --git a/packages/twenty-front/src/modules/command-menu/pages/common/components/SidePanelSubPageNavigationHeader.tsx b/packages/twenty-front/src/modules/command-menu/pages/common/components/SidePanelSubPageNavigationHeader.tsx new file mode 100644 index 0000000000..181f59fb6b --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/common/components/SidePanelSubPageNavigationHeader.tsx @@ -0,0 +1,40 @@ +import styled from '@emotion/styled'; +import { IconChevronLeft } from 'twenty-ui/display'; +import { IconButton } from 'twenty-ui/input'; + +const StyledContainer = styled.div` + align-items: center; + border-bottom: 1px solid ${({ theme }) => theme.border.color.light}; + display: flex; + gap: ${({ theme }) => theme.spacing(1)}; + padding: 0 ${({ theme }) => theme.spacing(2)}; + height: 40px; +`; + +const StyledText = styled.span` + color: ${({ theme }) => theme.font.color.tertiary}; + font-size: ${({ theme }) => theme.font.size.md}; + font-weight: ${({ theme }) => theme.font.weight.medium}; +`; + +type SidePanelSubPageNavigationHeaderProps = { + title: string; + onBackClick: () => void; +}; + +export const SidePanelSubPageNavigationHeader = ({ + onBackClick, + title, +}: SidePanelSubPageNavigationHeaderProps) => { + return ( + + + {title} + + ); +}; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/ChartFiltersSettings.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/ChartFiltersSettings.tsx index 544cd2a064..9a83ee7132 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/ChartFiltersSettings.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/ChartFiltersSettings.tsx @@ -1,6 +1,6 @@ -import { CommandMenuSubPageNavigationHeader } from '@/command-menu/pages/common/components/CommandMenuSubPageNavigationHeader'; -import { ChartFiltersSettingsInitializeStateEffect } from '@/command-menu/pages/page-layout/components/ChartFiltersSettingsInitializeStateEffect'; import { useCommandMenuHistory } from '@/command-menu/hooks/useCommandMenuHistory'; +import { SidePanelSubPageNavigationHeader } from '@/command-menu/pages/common/components/SidePanelSubPageNavigationHeader'; +import { ChartFiltersSettingsInitializeStateEffect } from '@/command-menu/pages/page-layout/components/ChartFiltersSettingsInitializeStateEffect'; import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/command-menu/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord'; import { useUpdateCurrentWidgetConfig } from '@/command-menu/pages/page-layout/hooks/useUpdateCurrentWidgetConfig'; import { type ChartWidget } from '@/command-menu/pages/page-layout/types/ChartWidget'; @@ -95,30 +95,32 @@ export const ChartFiltersSettings = ({ ); return ( - - + -
- {t`Conditions`} - - +
+ {t`Conditions`} + - - - - -
- + + + + +
+
+
+ ); };