Migrate right drawer record page to the command menu (#9459)

Closes #9423



https://github.com/user-attachments/assets/0d93f170-8c4f-43ff-a0ca-3d2874d44820
This commit is contained in:
Raphaël Bosi
2025-01-09 09:58:14 +01:00
committed by GitHub
parent e0e436a51d
commit a2f2f4148a
37 changed files with 400 additions and 232 deletions

View File

@ -1,5 +1,5 @@
import { renderHook } from '@testing-library/react';
import { act } from 'react-dom/test-utils';
import { act } from 'react';
import { MemoryRouter } from 'react-router-dom';
import { RecoilRoot, useRecoilValue } from 'recoil';
@ -69,20 +69,4 @@ describe('useCommandMenu', () => {
expect(result.current.isCommandMenuOpened).toBe(false);
});
it('onItemClick', () => {
const { result } = renderHooks();
const onClickMock = jest.fn();
act(() => {
result.current.commandMenu.onItemClick({
shouldCloseCommandMenuOnClick: true,
onClick: onClickMock,
to: '/test',
});
});
expect(result.current.isCommandMenuOpened).toBe(true);
expect(onClickMock).toHaveBeenCalledTimes(1);
});
});

View File

@ -0,0 +1,54 @@
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
import { renderHook } from '@testing-library/react';
import { act } from 'react';
import { MemoryRouter } from 'react-router-dom';
import { RecoilRoot, useRecoilValue } from 'recoil';
import { useCommandMenuOnItemClick } from '../useCommandMenuOnItemClick';
const Wrapper = ({ children }: { children: React.ReactNode }) => (
<RecoilRoot>
<MemoryRouter
initialEntries={['/one', '/two', { pathname: '/three' }]}
initialIndex={1}
>
{children}
</MemoryRouter>
</RecoilRoot>
);
const renderHooks = () => {
const { result } = renderHook(
() => {
const { onItemClick } = useCommandMenuOnItemClick();
const isCommandMenuOpened = useRecoilValue(isCommandMenuOpenedState);
return {
onItemClick,
isCommandMenuOpened,
};
},
{
wrapper: Wrapper,
},
);
return { result };
};
describe('useCommandMenuOnItemClick', () => {
it('onItemClick', () => {
const { result } = renderHooks();
const onClickMock = jest.fn();
act(() => {
result.current.onItemClick({
shouldCloseCommandMenuOnClick: true,
onClick: onClickMock,
to: '/test',
});
});
expect(result.current.isCommandMenuOpened).toBe(true);
expect(onClickMock).toHaveBeenCalledTimes(1);
});
});

View File

@ -1,6 +1,3 @@
import { isNonEmptyString } from '@sniptt/guards';
import { useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import { useRecoilCallback, useRecoilValue, useSetRecoilState } from 'recoil';
import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState';
@ -10,6 +7,8 @@ import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
import { isDefined } from '~/utils/isDefined';
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
import { CommandMenuPages } from '@/command-menu/components/CommandMenuPages';
import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState';
import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
@ -17,10 +16,10 @@ import { contextStoreFiltersComponentState } from '@/context-store/states/contex
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { mainContextStoreComponentInstanceIdState } from '@/context-store/states/mainContextStoreComponentInstanceId';
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
import { isCommandMenuOpenedState } from '../states/isCommandMenuOpenedState';
export const useCommandMenu = () => {
const navigate = useNavigate();
const setIsCommandMenuOpened = useSetRecoilState(isCommandMenuOpenedState);
const { resetSelectedItem } = useSelectableList('command-menu-list');
const {
@ -212,6 +211,8 @@ export const useCommandMenu = () => {
);
if (isCommandMenuOpened) {
set(viewableRecordIdState, null);
set(commandMenuPageState, CommandMenuPages.Root);
setIsCommandMenuOpened(false);
resetSelectedItem();
goBackToPreviousHotkeyScope();
@ -238,39 +239,21 @@ export const useCommandMenu = () => {
[closeCommandMenu, openCommandMenu],
);
const onItemClick = useCallback(
({
shouldCloseCommandMenuOnClick,
onClick,
to,
}: {
shouldCloseCommandMenuOnClick?: boolean;
onClick?: () => void;
to?: string;
}) => {
if (
isDefined(shouldCloseCommandMenuOnClick) &&
shouldCloseCommandMenuOnClick
) {
toggleCommandMenu();
}
if (isDefined(onClick)) {
onClick();
return;
}
if (isNonEmptyString(to)) {
navigate(to);
return;
}
const openRecordInCommandMenu = useRecoilCallback(
({ set }) => {
return (recordId: string) => {
openCommandMenu();
set(commandMenuPageState, CommandMenuPages.ViewRecord);
set(viewableRecordIdState, recordId);
};
},
[navigate, toggleCommandMenu],
[openCommandMenu],
);
return {
openCommandMenu,
closeCommandMenu,
openRecordInCommandMenu,
toggleCommandMenu,
onItemClick,
};
};

View File

@ -2,6 +2,7 @@ import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { useKeyboardShortcutMenu } from '@/keyboard-shortcut-menu/hooks/useKeyboardShortcutMenu';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
@ -10,7 +11,7 @@ import { useRecoilValue } from 'recoil';
import { Key } from 'ts-key-enum';
export const useCommandMenuHotKeys = () => {
const { closeCommandMenu } = useCommandMenu();
const { closeCommandMenu, toggleCommandMenu } = useCommandMenu();
const commandMenuSearch = useRecoilValue(commandMenuSearchState);
@ -24,6 +25,18 @@ export const useCommandMenuHotKeys = () => {
'command-menu',
);
const { closeKeyboardShortcutMenu } = useKeyboardShortcutMenu();
useScopedHotkeys(
'ctrl+k,meta+k',
() => {
closeKeyboardShortcutMenu();
toggleCommandMenu();
},
AppHotkeyScope.CommandMenu,
[toggleCommandMenu],
);
useScopedHotkeys(
[Key.Escape],
() => {

View File

@ -0,0 +1,41 @@
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
import { isNonEmptyString } from '@sniptt/guards';
import { useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import { isDefined } from 'twenty-ui';
export const useCommandMenuOnItemClick = () => {
const { toggleCommandMenu } = useCommandMenu();
const navigate = useNavigate();
const onItemClick = useCallback(
({
shouldCloseCommandMenuOnClick,
onClick,
to,
}: {
shouldCloseCommandMenuOnClick?: boolean;
onClick?: () => void;
to?: string;
}) => {
if (
isDefined(shouldCloseCommandMenuOnClick) &&
shouldCloseCommandMenuOnClick
) {
toggleCommandMenu();
}
if (isDefined(onClick)) {
onClick();
return;
}
if (isNonEmptyString(to)) {
navigate(to);
return;
}
},
[navigate, toggleCommandMenu],
);
return { onItemClick };
};