Refactor useDropdownV2 (#12875)

# What this PR does

This PR introduces what’s needed to progressively refactor the
useDropdown hooks we have.

It removes useDropdownV2 and renames useDropdown used for multi-page
dropdowns to useDropdownContextStateManagement

## The 3 useDropdown hooks

There are currently 3 useDropdown hooks : 

One is used for managing states in some dropdowns that have multiple
inner pages with contexts and without recoil. It is limited and would
need a separate refactoring, thus I just renamed it.

Then we have useDropdown and useDropdownV2, which followed our multiple
versions of recoil state management wrappers.

Now that the state management has been stabilized, we can merge
everything on the last version.

## What this refactor will allow next

This refactor will allow to refactor the legacy recoil state management,
because useDropdown is depending on legacy recoil states with scopeId.

Because there are only a dozen of those legacy states left, and the
dropdown’s ones are the harder to refactor, because swapping them as-is
causes a big QA, and if we have a big QA to do on all dropdowns, better
refactor the whole dropdown management and have everything clean.

After this refactor, we will be able to delete the legacy dropdown
states, and proceed with the other legacy states, then removing all the
legacy recoil state mangament.

## How do we allow progressive refactoring ?

There are many places where useDropdown is used.

To have an easier refactoring process, we want to merge multiple small
pull requests so that it is easier to QA and review.

For this we will maintain both legacy component state and component
state V2 in parallel for isDropdownOpen, so that the new hooks
`useOpenDropdown` , `useCloseDropdown` are doing the same thing than
`useDropdown` and `useDropdownV2` .

Thus for the moment, whether we use the legacy hooks or the new ones,
the effects are the same.

And we can have dropdowns operating on the old states and the new states
living side by side in the app.

## QA

Component | Status | Comments
-- | -- | --
CommandMenuActionMenuDropdown | Ok |  
RecordIndexActionMenuDropdown | Ok |  
RecordShowRightDrawerOpenRecordButton | Ok |  
useCloseActionMenu | Ok |  
CommandMenuContextChipGroups | Ok |  
useCommandMenuCloseAnimationCompleteCleanup | Ok |  
ObjectOptionsDropdown | Ok |  
ObjectOptionsDropdownContent | Ok |  
ObjectOptionsDropdownFieldsContent | Ok |  
ObjectOptionsDropdownHiddenFieldsContent | Ok |  
ObjectOptionsDropdownHiddenRecordGroupsContent | Ok |  
ObjectOptionsDropdownLayoutContent | Ok |  
ObjectOptionsDropdownLayoutOpenInContent | Ok |  
ObjectOptionsDropdownMenuContent | Ok |  
ObjectOptionsDropdownRecordGroupFieldsContent | Ok |  
ObjectOptionsDropdownRecordGroupsContent | Ok |  
ObjectOptionsDropdownRecordGroupSortContent | Ok |  
RecordBoardColumnHeaderAggregateDropdown | Ok |  
AggregateDropdownContent | Ok |  
RecordBoardColumnHeaderAggregateDropdownFieldsContent | Ok |  
RecordBoardColumnHeaderAggregateDropdownMenuContent | Ok |  
RecordBoardColumnHeaderAggregateDropdownMenuContent | Ok |  
RecordBoardColumnHeaderAggregateDropdownOptionsContent | Ok |  
RecordBoard | Ok | Used closeAnyOpenDropdown instead for a better UX
RecordBoardCard | Ok |  
useRecordBoardSelection | Ok |  
RecordTableColumnAggregateFooterDropdownContent | Ok |  
RecordTableColumnFooterWithDropdown | Ok |  
useOpenRecordFilterChipFromTableHeader | Ok |  
useCloseAnyOpenDropdown | Ok |  
useCloseDropdownFromOutside | Removed | Removed because unused
useDropdownV2 | Removed | Removed because all calls have been removed
useOpenDropdownFromOutside | Removed | Removed because unused
useCloseAndResetViewPicker | Ok |  
WorkflowVariablesDropdown | Ok |
This commit is contained in:
Lucas Bordeau
2025-06-25 17:16:56 +02:00
committed by GitHub
parent 74f53cc759
commit 22b4595b0b
55 changed files with 399 additions and 267 deletions
@@ -8,7 +8,7 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope'; import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2'; import { useToggleDropdown } from '@/ui/layout/dropdown/hooks/useToggleDropdown';
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement'; import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
@@ -30,12 +30,14 @@ export const CommandMenuActionMenuDropdown = () => {
const dropdownId = const dropdownId =
getRightDrawerActionMenuDropdownIdFromActionMenuId(actionMenuId); getRightDrawerActionMenuDropdownIdFromActionMenuId(actionMenuId);
const { toggleDropdown } = useDropdownV2(); const { toggleDropdown } = useToggleDropdown();
const hotkeysConfig = { const hotkeysConfig = {
keys: ['ctrl+o', 'meta+o'], keys: ['ctrl+o', 'meta+o'],
callback: () => { callback: () => {
toggleDropdown(dropdownId); toggleDropdown({
dropdownComponentInstanceIdFromProps: dropdownId,
});
}, },
scope: AppHotkeyScope.CommandMenuOpen, scope: AppHotkeyScope.CommandMenuOpen,
dependencies: [toggleDropdown], dependencies: [toggleDropdown],
@@ -11,7 +11,7 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope'; import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2'; import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
@@ -48,7 +48,7 @@ export const RecordIndexActionMenuDropdown = () => {
); );
const dropdownId = getActionMenuDropdownIdFromActionMenuId(actionMenuId); const dropdownId = getActionMenuDropdownIdFromActionMenuId(actionMenuId);
const { closeDropdown } = useDropdownV2(); const { closeDropdown } = useCloseDropdown();
const actionMenuDropdownPosition = useRecoilValue( const actionMenuDropdownPosition = useRecoilValue(
extractComponentState( extractComponentState(
@@ -9,7 +9,7 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { ObjectRecord } from '@/object-record/types/ObjectRecord'; import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { AppPath } from '@/types/AppPath'; import { AppPath } from '@/types/AppPath';
import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2'; import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { getShowPageTabListComponentId } from '@/ui/layout/show-page/utils/getShowPageTabListComponentId'; import { getShowPageTabListComponentId } from '@/ui/layout/show-page/utils/getShowPageTabListComponentId';
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement'; import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
@@ -74,7 +74,7 @@ export const RecordShowRightDrawerOpenRecordButton = ({
ActionMenuComponentInstanceContext, ActionMenuComponentInstanceContext,
); );
const { closeDropdown } = useDropdownV2(); const { closeDropdown } = useCloseDropdown();
const handleOpenRecord = useRecoilCallback( const handleOpenRecord = useRecoilCallback(
({ snapshot, reset }) => ({ snapshot, reset }) =>
@@ -3,7 +3,7 @@ import { ActionMenuComponentInstanceContext } from '@/action-menu/states/context
import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId'; import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId';
import { getRightDrawerActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getRightDrawerActionMenuDropdownIdFromActionMenuId'; import { getRightDrawerActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getRightDrawerActionMenuDropdownIdFromActionMenuId';
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2'; import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { useContext } from 'react'; import { useContext } from 'react';
import { isDefined } from 'twenty-shared/utils'; import { isDefined } from 'twenty-shared/utils';
@@ -19,7 +19,7 @@ export const useCloseActionMenu = ({
const { closeCommandMenu } = useCommandMenu(); const { closeCommandMenu } = useCommandMenu();
const { closeDropdown } = useDropdownV2(); const { closeDropdown } = useCloseDropdown();
const actionMenuId = useAvailableComponentInstanceIdOrThrow( const actionMenuId = useAvailableComponentInstanceIdOrThrow(
ActionMenuComponentInstanceContext, ActionMenuComponentInstanceContext,
@@ -2,7 +2,7 @@ import { COMMAND_MENU_CONTEXT_CHIP_GROUPS_DROPDOWN_ID } from '@/command-menu/con
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2'; import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { isDefined } from 'twenty-shared/utils'; import { isDefined } from 'twenty-shared/utils';
import { MenuItem } from 'twenty-ui/navigation'; import { MenuItem } from 'twenty-ui/navigation';
import { import {
@@ -15,7 +15,7 @@ export const CommandMenuContextChipGroups = ({
}: { }: {
contextChips: CommandMenuContextChipProps[]; contextChips: CommandMenuContextChipProps[];
}) => { }) => {
const { closeDropdown } = useDropdownV2(); const { closeDropdown } = useCloseDropdown();
if (contextChips.length === 0) { if (contextChips.length === 0) {
return null; return null;
@@ -25,8 +25,8 @@ const mockResetContextStoreStates = jest.fn();
const mockResetSelectedItem = jest.fn(); const mockResetSelectedItem = jest.fn();
const mockEmitSidePanelCloseEvent = jest.fn(); const mockEmitSidePanelCloseEvent = jest.fn();
jest.mock('@/ui/layout/dropdown/hooks/useDropdownV2', () => ({ jest.mock('@/ui/layout/dropdown/hooks/useCloseDropdown', () => ({
useDropdownV2: () => ({ useCloseDropdown: () => ({
closeDropdown: mockCloseDropdown, closeDropdown: mockCloseDropdown,
}), }),
})); }));
@@ -13,7 +13,7 @@ import { isCommandMenuClosingState } from '@/command-menu/states/isCommandMenuCl
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState'; import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages';
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState'; import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2'; import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { emitSidePanelCloseEvent } from '@/ui/layout/right-drawer/utils/emitSidePanelCloseEvent'; import { emitSidePanelCloseEvent } from '@/ui/layout/right-drawer/utils/emitSidePanelCloseEvent';
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
import { getShowPageTabListComponentId } from '@/ui/layout/show-page/utils/getShowPageTabListComponentId'; import { getShowPageTabListComponentId } from '@/ui/layout/show-page/utils/getShowPageTabListComponentId';
@@ -27,7 +27,7 @@ export const useCommandMenuCloseAnimationCompleteCleanup = () => {
const { resetContextStoreStates } = useResetContextStoreStates(); const { resetContextStoreStates } = useResetContextStoreStates();
const { closeDropdown } = useDropdownV2(); const { closeDropdown } = useCloseDropdown();
const commandMenuCloseAnimationCompleteCleanup = useRecoilCallback( const commandMenuCloseAnimationCompleteCleanup = useRecoilCallback(
({ snapshot, set }) => ({ snapshot, set }) =>
@@ -1,6 +1,6 @@
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
export const useCurrentContentId = <T>() => { export const useDropdownContextCurrentContentId = <T>() => {
const [currentContentId, setCurrentContentId] = useState<T | null>(null); const [currentContentId, setCurrentContentId] = useState<T | null>(null);
const [previousContentId, setPreviousContentId] = useState<T | null>(null); const [previousContentId, setPreviousContentId] = useState<T | null>(null);
@@ -4,7 +4,13 @@ import { RecordTableColumnAggregateFooterDropdownContextValue } from '@/object-r
import { useDropdown as useDropdownUi } from '@/ui/layout/dropdown/hooks/useDropdown'; import { useDropdown as useDropdownUi } from '@/ui/layout/dropdown/hooks/useDropdown';
import { Context, useCallback, useContext } from 'react'; import { Context, useCallback, useContext } from 'react';
export const useDropdown = < /**
*
* @deprecated This hook is deprecated because it uses context instead of recoil and synchronous hooks like we do in the application
*
* TODO: refactor this generic way to handle multiple pages in a dropdown with state management and specific code paths in a dedicated module, instead of using context with generic union types.
*/
export const useDropdownContextStateManagement = <
T extends T extends
| RecordBoardColumnHeaderAggregateDropdownContextValue | RecordBoardColumnHeaderAggregateDropdownContextValue
| RecordTableColumnAggregateFooterDropdownContextValue | RecordTableColumnAggregateFooterDropdownContextValue
@@ -18,7 +24,7 @@ export const useDropdown = <
if (!dropdownContext) { if (!dropdownContext) {
throw new Error( throw new Error(
`useDropdown must be used within a context provider (${context.Provider.name})`, `useDropdownContextStateManagement must be used within a context provider (${context.Provider.name})`,
); );
} }
const dropdownId = dropdownContext.dropdownId; const dropdownId = dropdownContext.dropdownId;
@@ -1,5 +1,4 @@
import { DROPDOWN_OFFSET_Y } from '@/dropdown/constants/DropdownOffsetY'; import { useDropdownContextCurrentContentId } from '@/dropdown-context-state-management/hooks/useDropdownContextCurrentContentId';
import { useCurrentContentId } from '@/dropdown/hooks/useCurrentContentId';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { ObjectOptionsDropdownContent } from '@/object-record/object-options-dropdown/components/ObjectOptionsDropdownContent'; import { ObjectOptionsDropdownContent } from '@/object-record/object-options-dropdown/components/ObjectOptionsDropdownContent';
import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId'; import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId';
@@ -9,6 +8,7 @@ import { RecordGroupReorderConfirmationModal } from '@/object-record/record-grou
import { useRecordGroupReorderConfirmationModal } from '@/object-record/record-group/hooks/useRecordGroupReorderConfirmationModal'; import { useRecordGroupReorderConfirmationModal } from '@/object-record/record-group/hooks/useRecordGroupReorderConfirmationModal';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton'; import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton';
import { DROPDOWN_OFFSET_Y } from '@/ui/layout/dropdown/constants/DropdownOffsetY';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { ViewType } from '@/views/types/ViewType'; import { ViewType } from '@/views/types/ViewType';
import { Trans } from '@lingui/react/macro'; import { Trans } from '@lingui/react/macro';
@@ -25,7 +25,7 @@ export const ObjectOptionsDropdown = ({
viewType, viewType,
}: ObjectOptionsDropdownProps) => { }: ObjectOptionsDropdownProps) => {
const { currentContentId, handleContentChange, handleResetContent } = const { currentContentId, handleContentChange, handleResetContent } =
useCurrentContentId<ObjectOptionsContentId>(); useDropdownContextCurrentContentId<ObjectOptionsContentId>();
const { isDropdownOpen } = useDropdown(OBJECT_OPTIONS_DROPDOWN_ID); const { isDropdownOpen } = useDropdown(OBJECT_OPTIONS_DROPDOWN_ID);
const { const {
@@ -7,10 +7,10 @@ import { ObjectOptionsDropdownMenuContent } from '@/object-record/object-options
import { ObjectOptionsDropdownRecordGroupFieldsContent } from '@/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupFieldsContent'; import { ObjectOptionsDropdownRecordGroupFieldsContent } from '@/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupFieldsContent';
import { ObjectOptionsDropdownRecordGroupsContent } from '@/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupsContent'; import { ObjectOptionsDropdownRecordGroupsContent } from '@/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupsContent';
import { ObjectOptionsDropdownRecordGroupSortContent } from '@/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupSortContent'; import { ObjectOptionsDropdownRecordGroupSortContent } from '@/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupSortContent';
import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown'; import { useObjectOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsDropdown';
export const ObjectOptionsDropdownContent = () => { export const ObjectOptionsDropdownContent = () => {
const { currentContentId } = useOptionsDropdown(); const { currentContentId } = useObjectOptionsDropdown();
switch (currentContentId) { switch (currentContentId) {
case 'layout': case 'layout':
@@ -1,6 +1,6 @@
import { useObjectOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsDropdown';
import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard'; import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard';
import { useObjectOptionsForTable } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForTable'; import { useObjectOptionsForTable } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForTable';
import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader'; import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent'; import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent';
@@ -20,7 +20,7 @@ export const ObjectOptionsDropdownFieldsContent = () => {
objectMetadataItem, objectMetadataItem,
onContentChange, onContentChange,
resetContent, resetContent,
} = useOptionsDropdown(); } = useObjectOptionsDropdown();
const { const {
handleColumnVisibilityChange, handleColumnVisibilityChange,
@@ -3,9 +3,9 @@ import { useSetRecoilState } from 'recoil';
import { useObjectNamePluralFromSingular } from '@/object-metadata/hooks/useObjectNamePluralFromSingular'; import { useObjectNamePluralFromSingular } from '@/object-metadata/hooks/useObjectNamePluralFromSingular';
import { useObjectOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsDropdown';
import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard'; import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard';
import { useObjectOptionsForTable } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForTable'; import { useObjectOptionsForTable } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForTable';
import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown';
import { SettingsPath } from '@/types/SettingsPath'; import { SettingsPath } from '@/types/SettingsPath';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader'; import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
@@ -28,7 +28,7 @@ export const ObjectOptionsDropdownHiddenFieldsContent = () => {
objectMetadataItem, objectMetadataItem,
onContentChange, onContentChange,
closeDropdown, closeDropdown,
} = useOptionsDropdown(); } = useObjectOptionsDropdown();
const { objectNamePlural } = useObjectNamePluralFromSingular({ const { objectNamePlural } = useObjectNamePluralFromSingular({
objectNameSingular: objectMetadataItem.nameSingular, objectNameSingular: objectMetadataItem.nameSingular,
@@ -2,7 +2,7 @@ import { useEffect } from 'react';
import { useObjectNamePluralFromSingular } from '@/object-metadata/hooks/useObjectNamePluralFromSingular'; import { useObjectNamePluralFromSingular } from '@/object-metadata/hooks/useObjectNamePluralFromSingular';
import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown'; import { useObjectOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsDropdown';
import { RecordGroupsVisibilityDropdownSection } from '@/object-record/record-group/components/RecordGroupsVisibilityDropdownSection'; import { RecordGroupsVisibilityDropdownSection } from '@/object-record/record-group/components/RecordGroupsVisibilityDropdownSection';
import { useRecordGroupVisibility } from '@/object-record/record-group/hooks/useRecordGroupVisibility'; import { useRecordGroupVisibility } from '@/object-record/record-group/hooks/useRecordGroupVisibility';
import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState'; import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
@@ -30,7 +30,7 @@ export const ObjectOptionsDropdownHiddenRecordGroupsContent = () => {
objectMetadataItem, objectMetadataItem,
onContentChange, onContentChange,
closeDropdown, closeDropdown,
} = useOptionsDropdown(); } = useObjectOptionsDropdown();
const recordGroupFieldMetadata = useRecoilComponentValueV2( const recordGroupFieldMetadata = useRecoilComponentValueV2(
recordGroupFieldMetadataComponentState, recordGroupFieldMetadataComponentState,
@@ -1,6 +1,6 @@
import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId'; import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId';
import { useObjectOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsDropdown';
import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard'; import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard';
import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown';
import { useSetViewTypeFromLayoutOptionsMenu } from '@/object-record/object-options-dropdown/hooks/useSetViewTypeFromLayoutOptionsMenu'; import { useSetViewTypeFromLayoutOptionsMenu } from '@/object-record/object-options-dropdown/hooks/useSetViewTypeFromLayoutOptionsMenu';
import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState'; import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState'; import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState';
@@ -43,7 +43,7 @@ export const ObjectOptionsDropdownLayoutContent = () => {
resetContent, resetContent,
onContentChange, onContentChange,
dropdownId, dropdownId,
} = useOptionsDropdown(); } = useObjectOptionsDropdown();
const { isCompactModeActive, setAndPersistIsCompactModeActive } = const { isCompactModeActive, setAndPersistIsCompactModeActive } =
useObjectOptionsForBoard({ useObjectOptionsForBoard({
@@ -1,5 +1,5 @@
import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId'; import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId';
import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown'; import { useObjectOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsDropdown';
import { useUpdateObjectViewOptions } from '@/object-record/object-options-dropdown/hooks/useUpdateObjectViewOptions'; import { useUpdateObjectViewOptions } from '@/object-record/object-options-dropdown/hooks/useUpdateObjectViewOptions';
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext'; import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState'; import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState';
@@ -25,7 +25,7 @@ import {
import { MenuItemSelect } from 'twenty-ui/navigation'; import { MenuItemSelect } from 'twenty-ui/navigation';
export const ObjectOptionsDropdownLayoutOpenInContent = () => { export const ObjectOptionsDropdownLayoutOpenInContent = () => {
const { onContentChange } = useOptionsDropdown(); const { onContentChange } = useObjectOptionsDropdown();
const recordIndexOpenRecordIn = useRecoilValue(recordIndexOpenRecordInState); const recordIndexOpenRecordIn = useRecoilValue(recordIndexOpenRecordInState);
const { currentView } = useGetCurrentViewOnly(); const { currentView } = useGetCurrentViewOnly();
const { setAndPersistOpenRecordIn } = useUpdateObjectViewOptions(); const { setAndPersistOpenRecordIn } = useUpdateObjectViewOptions();
@@ -1,7 +1,7 @@
import { ObjectOptionsDropdownMenuViewName } from '@/object-record/object-options-dropdown/components/ObjectOptionsDropdownMenuViewName'; import { ObjectOptionsDropdownMenuViewName } from '@/object-record/object-options-dropdown/components/ObjectOptionsDropdownMenuViewName';
import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId'; import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId';
import { useObjectOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsDropdown';
import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard'; import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard';
import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown';
import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState'; import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
@@ -33,7 +33,7 @@ import { MenuItem } from 'twenty-ui/navigation';
export const ObjectOptionsDropdownMenuContent = () => { export const ObjectOptionsDropdownMenuContent = () => {
const { t } = useLingui(); const { t } = useLingui();
const { recordIndexId, objectMetadataItem, onContentChange, closeDropdown } = const { recordIndexId, objectMetadataItem, onContentChange, closeDropdown } =
useOptionsDropdown(); useObjectOptionsDropdown();
const { currentView } = useGetCurrentViewOnly(); const { currentView } = useGetCurrentViewOnly();
@@ -3,7 +3,7 @@ import { useEffect } from 'react';
import { useObjectNamePluralFromSingular } from '@/object-metadata/hooks/useObjectNamePluralFromSingular'; import { useObjectNamePluralFromSingular } from '@/object-metadata/hooks/useObjectNamePluralFromSingular';
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown'; import { useObjectOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsDropdown';
import { useSearchRecordGroupField } from '@/object-record/object-options-dropdown/hooks/useSearchRecordGroupField'; import { useSearchRecordGroupField } from '@/object-record/object-options-dropdown/hooks/useSearchRecordGroupField';
import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState'; import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
import { hiddenRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/hiddenRecordGroupIdsComponentSelector'; import { hiddenRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/hiddenRecordGroupIdsComponentSelector';
@@ -42,7 +42,7 @@ export const ObjectOptionsDropdownRecordGroupFieldsContent = () => {
onContentChange, onContentChange,
resetContent, resetContent,
closeDropdown, closeDropdown,
} = useOptionsDropdown(); } = useObjectOptionsDropdown();
const { objectNamePlural } = useObjectNamePluralFromSingular({ const { objectNamePlural } = useObjectNamePluralFromSingular({
objectNameSingular: objectMetadataItem.nameSingular, objectNameSingular: objectMetadataItem.nameSingular,
@@ -1,7 +1,7 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId'; import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId';
import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown'; import { useObjectOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsDropdown';
import { hiddenRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/hiddenRecordGroupIdsComponentSelector'; import { hiddenRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/hiddenRecordGroupIdsComponentSelector';
import { RecordGroupSort } from '@/object-record/record-group/types/RecordGroupSort'; import { RecordGroupSort } from '@/object-record/record-group/types/RecordGroupSort';
import { recordIndexRecordGroupSortComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupSortComponentState'; import { recordIndexRecordGroupSortComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupSortComponentState';
@@ -24,7 +24,7 @@ import {
import { MenuItemSelect } from 'twenty-ui/navigation'; import { MenuItemSelect } from 'twenty-ui/navigation';
export const ObjectOptionsDropdownRecordGroupSortContent = () => { export const ObjectOptionsDropdownRecordGroupSortContent = () => {
const { currentContentId, onContentChange } = useOptionsDropdown(); const { currentContentId, onContentChange } = useObjectOptionsDropdown();
const hiddenRecordGroupIds = useRecoilComponentValueV2( const hiddenRecordGroupIds = useRecoilComponentValueV2(
hiddenRecordGroupIdsComponentSelector, hiddenRecordGroupIdsComponentSelector,
@@ -1,7 +1,7 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId'; import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId';
import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown'; import { useObjectOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsDropdown';
import { RecordGroupsVisibilityDropdownSection } from '@/object-record/record-group/components/RecordGroupsVisibilityDropdownSection'; import { RecordGroupsVisibilityDropdownSection } from '@/object-record/record-group/components/RecordGroupsVisibilityDropdownSection';
import { useRecordGroupVisibility } from '@/object-record/record-group/hooks/useRecordGroupVisibility'; import { useRecordGroupVisibility } from '@/object-record/record-group/hooks/useRecordGroupVisibility';
import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState'; import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
@@ -43,7 +43,7 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => {
onContentChange, onContentChange,
resetContent, resetContent,
handleRecordGroupOrderChangeWithModal, handleRecordGroupOrderChangeWithModal,
} = useOptionsDropdown(); } = useObjectOptionsDropdown();
const { currentView } = useGetCurrentViewOnly(); const { currentView } = useGetCurrentViewOnly();
@@ -3,7 +3,7 @@ import { act } from 'react';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId'; import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId';
import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown'; import { useObjectOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsDropdown';
import { ObjectOptionsDropdownContext } from '@/object-record/object-options-dropdown/states/contexts/ObjectOptionsDropdownContext'; import { ObjectOptionsDropdownContext } from '@/object-record/object-options-dropdown/states/contexts/ObjectOptionsDropdownContext';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { ViewType } from '@/views/types/ViewType'; import { ViewType } from '@/views/types/ViewType';
@@ -61,7 +61,7 @@ describe('useOptionsDropdown', () => {
{children} {children}
</ObjectOptionsDropdownContext.Provider> </ObjectOptionsDropdownContext.Provider>
); );
return renderHook(() => useOptionsDropdown(), { wrapper }); return renderHook(() => useObjectOptionsDropdown(), { wrapper });
}; };
it('provides closeDropdown functionality from the context', () => { it('provides closeDropdown functionality from the context', () => {
@@ -1,15 +1,15 @@
import { useDropdown } from '@/dropdown/hooks/useDropdown'; import { useDropdownContextStateManagement } from '@/dropdown-context-state-management/hooks/useDropdownContextStateManagement';
import { ObjectOptionsDropdownContext } from '@/object-record/object-options-dropdown/states/contexts/ObjectOptionsDropdownContext'; import { ObjectOptionsDropdownContext } from '@/object-record/object-options-dropdown/states/contexts/ObjectOptionsDropdownContext';
import { useContext } from 'react'; import { useContext } from 'react';
export const useOptionsDropdown = () => { export const useObjectOptionsDropdown = () => {
const context = useContext(ObjectOptionsDropdownContext); const context = useContext(ObjectOptionsDropdownContext);
if (!context) { if (!context) {
throw new Error('useOptionsDropdown must be used within a context'); throw new Error('useObjectOptionsDropdown must be used within a context');
} }
const { closeDropdown } = useDropdown({ const { closeDropdown } = useDropdownContextStateManagement({
context: ObjectOptionsDropdownContext, context: ObjectOptionsDropdownContext,
}); });
@@ -4,7 +4,6 @@ import { useContext, useRef } from 'react';
import { useRecoilCallback } from 'recoil'; import { useRecoilCallback } from 'recoil';
import { ACTION_MENU_DROPDOWN_CLICK_OUTSIDE_ID } from '@/action-menu/constants/ActionMenuDropdownClickOutsideId'; import { ACTION_MENU_DROPDOWN_CLICK_OUTSIDE_ID } from '@/action-menu/constants/ActionMenuDropdownClickOutsideId';
import { getActionMenuIdFromRecordIndexId } from '@/action-menu/utils/getActionMenuIdFromRecordIndexId';
import { COMMAND_MENU_CLICK_OUTSIDE_ID } from '@/command-menu/constants/CommandMenuClickOutsideId'; import { COMMAND_MENU_CLICK_OUTSIDE_ID } from '@/command-menu/constants/CommandMenuClickOutsideId';
import { RecordBoardHeader } from '@/object-record/record-board/components/RecordBoardHeader'; import { RecordBoardHeader } from '@/object-record/record-board/components/RecordBoardHeader';
import { RecordBoardScrollToFocusedCardEffect } from '@/object-record/record-board/components/RecordBoardScrollToFocusedCardEffect'; import { RecordBoardScrollToFocusedCardEffect } from '@/object-record/record-board/components/RecordBoardScrollToFocusedCardEffect';
@@ -26,7 +25,7 @@ import { RECORD_INDEX_REMOVE_SORTING_MODAL_ID } from '@/object-record/record-ind
import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState'; import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState';
import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState'; import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2'; import { useCloseAnyOpenDropdown } from '@/ui/layout/dropdown/hooks/useCloseAnyOpenDropdown';
import { MODAL_BACKDROP_CLICK_OUTSIDE_ID } from '@/ui/layout/modal/constants/ModalBackdropClickOutsideId'; import { MODAL_BACKDROP_CLICK_OUTSIDE_ID } from '@/ui/layout/modal/constants/ModalBackdropClickOutsideId';
import { useModal } from '@/ui/layout/modal/hooks/useModal'; import { useModal } from '@/ui/layout/modal/hooks/useModal';
import { PAGE_ACTION_CONTAINER_CLICK_OUTSIDE_ID } from '@/ui/layout/page/constants/PageActionContainerClickOutsideId'; import { PAGE_ACTION_CONTAINER_CLICK_OUTSIDE_ID } from '@/ui/layout/page/constants/PageActionContainerClickOutsideId';
@@ -81,15 +80,13 @@ export const RecordBoard = () => {
RECORD_BOARD_CLICK_OUTSIDE_LISTENER_ID, RECORD_BOARD_CLICK_OUTSIDE_LISTENER_ID,
); );
const actionMenuId = getActionMenuIdFromRecordIndexId(recordBoardId); const { closeAnyOpenDropdown } = useCloseAnyOpenDropdown();
const { closeDropdown } = useDropdownV2();
const { deactivateBoardCard } = useActiveRecordBoardCard(recordBoardId); const { deactivateBoardCard } = useActiveRecordBoardCard(recordBoardId);
const { unfocusBoardCard } = useFocusedRecordBoardCard(recordBoardId); const { unfocusBoardCard } = useFocusedRecordBoardCard(recordBoardId);
const handleDragSelectionStart = () => { const handleDragSelectionStart = () => {
closeDropdown(actionMenuId); closeAnyOpenDropdown();
toggleClickOutside(false); toggleClickOutside(false);
}; };
@@ -5,7 +5,7 @@ import { getActionMenuIdFromRecordIndexId } from '@/action-menu/utils/getActionM
import { RecordBoardComponentInstanceContext } from '@/object-record/record-board/states/contexts/RecordBoardComponentInstanceContext'; import { RecordBoardComponentInstanceContext } from '@/object-record/record-board/states/contexts/RecordBoardComponentInstanceContext';
import { isRecordBoardCardSelectedComponentFamilyState } from '@/object-record/record-board/states/isRecordBoardCardSelectedComponentFamilyState'; import { isRecordBoardCardSelectedComponentFamilyState } from '@/object-record/record-board/states/isRecordBoardCardSelectedComponentFamilyState';
import { recordBoardSelectedRecordIdsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardSelectedRecordIdsComponentSelector'; import { recordBoardSelectedRecordIdsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardSelectedRecordIdsComponentSelector';
import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2'; import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue';
@@ -28,7 +28,7 @@ export const useRecordBoardSelection = (recordBoardId?: string) => {
recordBoardId, recordBoardId,
); );
const { closeDropdown } = useDropdownV2(); const { closeDropdown } = useCloseDropdown();
const dropdownId = getActionMenuDropdownIdFromActionMenuId( const dropdownId = getActionMenuDropdownIdFromActionMenuId(
getActionMenuIdFromRecordIndexId(instanceIdFromProps), getActionMenuIdFromRecordIndexId(instanceIdFromProps),
@@ -15,7 +15,7 @@ import { RecordBoardCardBody } from '@/object-record/record-board/record-board-c
import { RecordBoardCardHeader } from '@/object-record/record-board/record-board-card/components/RecordBoardCardHeader'; import { RecordBoardCardHeader } from '@/object-record/record-board/record-board-card/components/RecordBoardCardHeader';
import { RECORD_BOARD_CARD_CLICK_OUTSIDE_ID } from '@/object-record/record-board/record-board-card/constants/RecordBoardCardClickOutsideId'; import { RECORD_BOARD_CARD_CLICK_OUTSIDE_ID } from '@/object-record/record-board/record-board-card/constants/RecordBoardCardClickOutsideId';
import { useOpenRecordFromIndexView } from '@/object-record/record-index/hooks/useOpenRecordFromIndexView'; import { useOpenRecordFromIndexView } from '@/object-record/record-index/hooks/useOpenRecordFromIndexView';
import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2'; import { useOpenDropdown } from '@/ui/layout/dropdown/hooks/useOpenDropdown';
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId'; import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
import { useScrollWrapperElement } from '@/ui/utilities/scroll/hooks/useScrollWrapperElement'; import { useScrollWrapperElement } from '@/ui/utilities/scroll/hooks/useScrollWrapperElement';
import { useRecoilComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyStateV2'; import { useRecoilComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyStateV2';
@@ -140,22 +140,25 @@ export const RecordBoardCard = () => {
), ),
); );
const { openDropdown } = useDropdownV2(); const { openDropdown } = useOpenDropdown();
const { openRecordFromIndexView } = useOpenRecordFromIndexView(); const { openRecordFromIndexView } = useOpenRecordFromIndexView();
const { activateBoardCard } = useActiveRecordBoardCard(recordBoardId); const { activateBoardCard } = useActiveRecordBoardCard(recordBoardId);
const { unfocusBoardCard } = useFocusedRecordBoardCard(recordBoardId); const { unfocusBoardCard } = useFocusedRecordBoardCard(recordBoardId);
const handleActionMenuDropdown = (event: React.MouseEvent) => { const handleContextMenuOpen = (event: React.MouseEvent) => {
event.preventDefault(); event.preventDefault();
setIsCurrentCardSelected(true); setIsCurrentCardSelected(true);
setActionMenuDropdownPosition({ setActionMenuDropdownPosition({
x: event.clientX, x: event.clientX,
y: event.clientY, y: event.clientY,
}); });
openDropdown(actionMenuDropdownId, { openDropdown({
enableGlobalHotkeysWithModifiers: true, dropdownComponentInstanceIdFromProps: actionMenuDropdownId,
enableGlobalHotkeysConflictingWithKeyboard: false, globalHotkeysConfig: {
enableGlobalHotkeysWithModifiers: true,
enableGlobalHotkeysConflictingWithKeyboard: false,
},
}); });
}; };
@@ -185,7 +188,7 @@ export const RecordBoardCard = () => {
return ( return (
<StyledBoardCardWrapper <StyledBoardCardWrapper
data-click-outside-id={RECORD_BOARD_CARD_CLICK_OUTSIDE_ID} data-click-outside-id={RECORD_BOARD_CARD_CLICK_OUTSIDE_ID}
onContextMenu={handleActionMenuDropdown} onContextMenu={handleContextMenuOpen}
> >
<InView> <InView>
<StyledBoardCard <StyledBoardCard
@@ -1,5 +1,4 @@
import { DROPDOWN_OFFSET_Y } from '@/dropdown/constants/DropdownOffsetY'; import { useDropdownContextCurrentContentId } from '@/dropdown-context-state-management/hooks/useDropdownContextCurrentContentId';
import { useCurrentContentId } from '@/dropdown/hooks/useCurrentContentId';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { RecordBoardColumnHeaderAggregateDropdownComponentInstanceContext } from '@/object-record/record-board/contexts/RecordBoardColumnHeaderAggregateDropdownComponentInstanceContext'; import { RecordBoardColumnHeaderAggregateDropdownComponentInstanceContext } from '@/object-record/record-board/contexts/RecordBoardColumnHeaderAggregateDropdownComponentInstanceContext';
import { RecordBoardColumnHeaderAggregateDropdownButton } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownButton'; import { RecordBoardColumnHeaderAggregateDropdownButton } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownButton';
@@ -7,6 +6,7 @@ import { AggregateDropdownContent } from '@/object-record/record-board/record-bo
import { RecordBoardColumnHeaderAggregateDropdownContext } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownContext'; import { RecordBoardColumnHeaderAggregateDropdownContext } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownContext';
import { RecordBoardColumnHeaderAggregateContentId } from '@/object-record/record-board/types/RecordBoardColumnHeaderAggregateContentId'; import { RecordBoardColumnHeaderAggregateContentId } from '@/object-record/record-board/types/RecordBoardColumnHeaderAggregateContentId';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DROPDOWN_OFFSET_Y } from '@/ui/layout/dropdown/constants/DropdownOffsetY';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
type RecordBoardColumnHeaderAggregateDropdownProps = { type RecordBoardColumnHeaderAggregateDropdownProps = {
@@ -31,7 +31,8 @@ export const RecordBoardColumnHeaderAggregateDropdown = ({
handleContentChange, handleContentChange,
handleResetContent, handleResetContent,
previousContentId, previousContentId,
} = useCurrentContentId<RecordBoardColumnHeaderAggregateContentId>(); } =
useDropdownContextCurrentContentId<RecordBoardColumnHeaderAggregateContentId>();
return ( return (
<RecordBoardColumnHeaderAggregateDropdownComponentInstanceContext.Provider <RecordBoardColumnHeaderAggregateDropdownComponentInstanceContext.Provider
@@ -1,4 +1,4 @@
import { useDropdown } from '@/dropdown/hooks/useDropdown'; import { useDropdownContextStateManagement } from '@/dropdown-context-state-management/hooks/useDropdownContextStateManagement';
import { RecordBoardColumnHeaderAggregateDropdownContext } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownContext'; import { RecordBoardColumnHeaderAggregateDropdownContext } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownContext';
import { RecordBoardColumnHeaderAggregateDropdownFieldsContent } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownFieldsContent'; import { RecordBoardColumnHeaderAggregateDropdownFieldsContent } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownFieldsContent';
import { RecordBoardColumnHeaderAggregateDropdownMenuContent } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownMenuContent'; import { RecordBoardColumnHeaderAggregateDropdownMenuContent } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownMenuContent';
@@ -12,9 +12,10 @@ import { getAvailableFieldsIdsForAggregationFromObjectFields } from '@/object-re
import { t } from '@lingui/core/macro'; import { t } from '@lingui/core/macro';
export const AggregateDropdownContent = () => { export const AggregateDropdownContent = () => {
const { currentContentId, objectMetadataItem } = useDropdown({ const { currentContentId, objectMetadataItem } =
context: RecordBoardColumnHeaderAggregateDropdownContext, useDropdownContextStateManagement({
}); context: RecordBoardColumnHeaderAggregateDropdownContext,
});
switch (currentContentId) { switch (currentContentId) {
case 'countAggregateOperationsOptions': { case 'countAggregateOperationsOptions': {
@@ -1,4 +1,4 @@
import { useDropdown } from '@/dropdown/hooks/useDropdown'; import { useDropdownContextStateManagement } from '@/dropdown-context-state-management/hooks/useDropdownContextStateManagement';
import { RecordBoardColumnHeaderAggregateDropdownContext } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownContext'; import { RecordBoardColumnHeaderAggregateDropdownContext } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdownContext';
import { aggregateOperationComponentState } from '@/object-record/record-board/record-board-column/states/aggregateOperationComponentState'; import { aggregateOperationComponentState } from '@/object-record/record-board/record-board-column/states/aggregateOperationComponentState';
import { availableFieldIdsForAggregateOperationComponentState } from '@/object-record/record-board/record-board-column/states/availableFieldIdsForAggregateOperationComponentState'; import { availableFieldIdsForAggregateOperationComponentState } from '@/object-record/record-board/record-board-column/states/availableFieldIdsForAggregateOperationComponentState';
@@ -27,7 +27,7 @@ export const RecordBoardColumnHeaderAggregateDropdownFieldsContent = () => {
onContentChange, onContentChange,
resetContent, resetContent,
previousContentId, previousContentId,
} = useDropdown({ } = useDropdownContextStateManagement({
context: RecordBoardColumnHeaderAggregateDropdownContext, context: RecordBoardColumnHeaderAggregateDropdownContext,
}); });
@@ -1,6 +1,6 @@
import { Key } from 'ts-key-enum'; import { Key } from 'ts-key-enum';
import { useDropdown } from '@/dropdown/hooks/useDropdown'; import { useDropdownContextStateManagement } from '@/dropdown-context-state-management/hooks/useDropdownContextStateManagement';
import { import {
RecordBoardColumnHeaderAggregateDropdownContext, RecordBoardColumnHeaderAggregateDropdownContext,
RecordBoardColumnHeaderAggregateDropdownContextValue, RecordBoardColumnHeaderAggregateDropdownContextValue,
@@ -17,9 +17,11 @@ export const RecordBoardColumnHeaderAggregateDropdownMenuContent = () => {
const { t } = useLingui(); const { t } = useLingui();
const { onContentChange, closeDropdown } = const { onContentChange, closeDropdown } =
useDropdown<RecordBoardColumnHeaderAggregateDropdownContextValue>({ useDropdownContextStateManagement<RecordBoardColumnHeaderAggregateDropdownContextValue>(
context: RecordBoardColumnHeaderAggregateDropdownContext, {
}); context: RecordBoardColumnHeaderAggregateDropdownContext,
},
);
useScopedHotkeys( useScopedHotkeys(
[Key.Escape], [Key.Escape],
@@ -1,4 +1,4 @@
import { useDropdown } from '@/dropdown/hooks/useDropdown'; import { useDropdownContextStateManagement } from '@/dropdown-context-state-management/hooks/useDropdownContextStateManagement';
import { import {
RecordBoardColumnHeaderAggregateDropdownContext, RecordBoardColumnHeaderAggregateDropdownContext,
RecordBoardColumnHeaderAggregateDropdownContextValue, RecordBoardColumnHeaderAggregateDropdownContextValue,
@@ -32,9 +32,11 @@ export const RecordBoardColumnHeaderAggregateDropdownOptionsContent = ({
title: string; title: string;
}) => { }) => {
const { onContentChange, closeDropdown, resetContent } = const { onContentChange, closeDropdown, resetContent } =
useDropdown<RecordBoardColumnHeaderAggregateDropdownContextValue>({ useDropdownContextStateManagement<RecordBoardColumnHeaderAggregateDropdownContextValue>(
context: RecordBoardColumnHeaderAggregateDropdownContext, {
}); context: RecordBoardColumnHeaderAggregateDropdownContext,
},
);
useScopedHotkeys( useScopedHotkeys(
[Key.Escape], [Key.Escape],
@@ -1,4 +1,4 @@
import { useDropdown } from '@/dropdown/hooks/useDropdown'; import { useDropdownContextStateManagement } from '@/dropdown-context-state-management/hooks/useDropdownContextStateManagement';
import { AggregateOperations } from '@/object-record/record-table/constants/AggregateOperations'; import { AggregateOperations } from '@/object-record/record-table/constants/AggregateOperations';
import { DateAggregateOperations } from '@/object-record/record-table/constants/DateAggregateOperations'; import { DateAggregateOperations } from '@/object-record/record-table/constants/DateAggregateOperations';
import { RecordTableColumnAggregateFooterDropdownSubmenuContent } from '@/object-record/record-table/record-table-footer/components/RecordTableColumnAggregateDropdownSubmenuContent'; import { RecordTableColumnAggregateFooterDropdownSubmenuContent } from '@/object-record/record-table/record-table-footer/components/RecordTableColumnAggregateDropdownSubmenuContent';
@@ -14,9 +14,10 @@ import { useLingui } from '@lingui/react/macro';
export const RecordTableColumnAggregateFooterDropdownContent = () => { export const RecordTableColumnAggregateFooterDropdownContent = () => {
const { t } = useLingui(); const { t } = useLingui();
const { currentContentId, fieldMetadataType } = useDropdown({ const { currentContentId, fieldMetadataType } =
context: RecordTableColumnAggregateFooterDropdownContext, useDropdownContextStateManagement({
}); context: RecordTableColumnAggregateFooterDropdownContext,
});
const availableAggregateOperations = const availableAggregateOperations =
getAvailableAggregateOperationsForFieldMetadataType({ getAvailableAggregateOperationsForFieldMetadataType({
@@ -1,4 +1,4 @@
import { useCurrentContentId } from '@/dropdown/hooks/useCurrentContentId'; import { useDropdownContextCurrentContentId } from '@/dropdown-context-state-management/hooks/useDropdownContextCurrentContentId';
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext'; import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
import { RecordTableColumnAggregateFooterCellContext } from '@/object-record/record-table/record-table-footer/components/RecordTableColumnAggregateFooterCellContext'; import { RecordTableColumnAggregateFooterCellContext } from '@/object-record/record-table/record-table-footer/components/RecordTableColumnAggregateFooterCellContext';
import { RecordTableColumnAggregateFooterDropdownContent } from '@/object-record/record-table/record-table-footer/components/RecordTableColumnAggregateFooterDropdownContent'; import { RecordTableColumnAggregateFooterDropdownContent } from '@/object-record/record-table/record-table-footer/components/RecordTableColumnAggregateFooterDropdownContent';
@@ -19,7 +19,7 @@ export const RecordTableColumnFooterWithDropdown = ({
isFirstCell, isFirstCell,
}: RecordTableColumnFooterWithDropdownProps) => { }: RecordTableColumnFooterWithDropdownProps) => {
const { currentContentId, handleContentChange, handleResetContent } = const { currentContentId, handleContentChange, handleResetContent } =
useCurrentContentId<RecordTableFooterAggregateContentId>(); useDropdownContextCurrentContentId<RecordTableFooterAggregateContentId>();
const { fieldMetadataId } = useContext( const { fieldMetadataId } = useContext(
RecordTableColumnAggregateFooterCellContext, RecordTableColumnAggregateFooterCellContext,
@@ -2,7 +2,7 @@ import { useCreateEmptyRecordFilterFromFieldMetadataItem } from '@/object-record
import { useFilterableFieldMetadataItemsInRecordIndexContext } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItemsInRecordIndexContext'; import { useFilterableFieldMetadataItemsInRecordIndexContext } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItemsInRecordIndexContext';
import { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUpsertRecordFilter'; import { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUpsertRecordFilter';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2'; import { useOpenDropdown } from '@/ui/layout/dropdown/hooks/useOpenDropdown';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useSetEditableFilterChipDropdownStates } from '@/views/hooks/useSetEditableFilterChipDropdownStates'; import { useSetEditableFilterChipDropdownStates } from '@/views/hooks/useSetEditableFilterChipDropdownStates';
import { isDefined } from 'twenty-shared/utils'; import { isDefined } from 'twenty-shared/utils';
@@ -20,7 +20,7 @@ export const useOpenRecordFilterChipFromTableHeader = () => {
const { upsertRecordFilter } = useUpsertRecordFilter(); const { upsertRecordFilter } = useUpsertRecordFilter();
const { openDropdown } = useDropdownV2(); const { openDropdown } = useOpenDropdown();
const { setEditableFilterChipDropdownStates } = const { setEditableFilterChipDropdownStates } =
useSetEditableFilterChipDropdownStates(); useSetEditableFilterChipDropdownStates();
@@ -45,7 +45,10 @@ export const useOpenRecordFilterChipFromTableHeader = () => {
if (isDefined(existingNonAdvancedRecordFilter)) { if (isDefined(existingNonAdvancedRecordFilter)) {
setEditableFilterChipDropdownStates(existingNonAdvancedRecordFilter); setEditableFilterChipDropdownStates(existingNonAdvancedRecordFilter);
openDropdown(existingNonAdvancedRecordFilter.id); openDropdown({
dropdownComponentInstanceIdFromProps:
existingNonAdvancedRecordFilter.id,
});
return; return;
} }
@@ -56,7 +59,7 @@ export const useOpenRecordFilterChipFromTableHeader = () => {
upsertRecordFilter(newRecordFilter); upsertRecordFilter(newRecordFilter);
setEditableFilterChipDropdownStates(newRecordFilter); setEditableFilterChipDropdownStates(newRecordFilter);
openDropdown(newRecordFilter.id); openDropdown({ dropdownComponentInstanceIdFromProps: newRecordFilter.id });
}; };
return { openRecordFilterChipFromTableHeader }; return { openRecordFilterChipFromTableHeader };
@@ -2,7 +2,7 @@ import { DropdownOnToggleEffect } from '@/ui/layout/dropdown/components/Dropdown
import { DropdownInternalContainer } from '@/ui/layout/dropdown/components/internal/DropdownInternalContainer'; import { DropdownInternalContainer } from '@/ui/layout/dropdown/components/internal/DropdownInternalContainer';
import { DROPDOWN_RESIZE_MIN_HEIGHT } from '@/ui/layout/dropdown/constants/DropdownResizeMinHeight'; import { DROPDOWN_RESIZE_MIN_HEIGHT } from '@/ui/layout/dropdown/constants/DropdownResizeMinHeight';
import { DROPDOWN_RESIZE_MIN_WIDTH } from '@/ui/layout/dropdown/constants/DropdownResizeMinWidth'; import { DROPDOWN_RESIZE_MIN_WIDTH } from '@/ui/layout/dropdown/constants/DropdownResizeMinWidth';
import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponeInstanceContext'; import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext';
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope'; import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
import { dropdownMaxHeightComponentState } from '@/ui/layout/dropdown/states/internal/dropdownMaxHeightComponentState'; import { dropdownMaxHeightComponentState } from '@/ui/layout/dropdown/states/internal/dropdownMaxHeightComponentState';
import { dropdownMaxWidthComponentState } from '@/ui/layout/dropdown/states/internal/dropdownMaxWidthComponentState'; import { dropdownMaxWidthComponentState } from '@/ui/layout/dropdown/states/internal/dropdownMaxWidthComponentState';
@@ -3,20 +3,36 @@ import { renderHook } from '@testing-library/react';
import { act } from 'react'; import { act } from 'react';
import { RecoilRoot } from 'recoil'; import { RecoilRoot } from 'recoil';
import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext';
import { useCloseAnyOpenDropdown } from '@/ui/layout/dropdown/hooks/useCloseAnyOpenDropdown'; import { useCloseAnyOpenDropdown } from '@/ui/layout/dropdown/hooks/useCloseAnyOpenDropdown';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { useOpenDropdown } from '@/ui/layout/dropdown/hooks/useOpenDropdown';
import { isDropdownOpenComponentStateV2 } from '@/ui/layout/dropdown/states/isDropdownOpenComponentStateV2';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
const dropdownId = 'test-dropdown-id'; const dropdownId = 'test-dropdown-id';
const Wrapper = ({ children }: { children: React.ReactNode }) => { const Wrapper = ({ children }: { children: React.ReactNode }) => {
return <RecoilRoot>{children}</RecoilRoot>; return (
<RecoilRoot>
<DropdownComponentInstanceContext.Provider
value={{ instanceId: dropdownId }}
>
{children}
</DropdownComponentInstanceContext.Provider>
</RecoilRoot>
);
}; };
describe('useCloseAnyOpenDropdown', () => { describe('useCloseAnyOpenDropdown', () => {
it('should open dropdown and then close it with closeAnyOpenDropdown', async () => { it('should open dropdown and then close it with closeAnyOpenDropdown', async () => {
const { result } = renderHook( const { result } = renderHook(
() => { () => {
const { openDropdown, isDropdownOpen } = useDropdown(dropdownId); const isDropdownOpen = useRecoilComponentValueV2(
isDropdownOpenComponentStateV2,
dropdownId,
);
const { openDropdown } = useOpenDropdown();
const { closeAnyOpenDropdown } = useCloseAnyOpenDropdown(); const { closeAnyOpenDropdown } = useCloseAnyOpenDropdown();
@@ -1,43 +0,0 @@
import { expect } from '@storybook/test';
import { renderHook } from '@testing-library/react';
import { act } from 'react';
import { RecoilRoot } from 'recoil';
import { useCloseDropdownFromOutside } from '@/ui/layout/dropdown/hooks/useCloseDropdownFromOutside';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
const dropdownId = 'test-dropdown-id';
const Wrapper = ({ children }: { children: React.ReactNode }) => {
return <RecoilRoot>{children}</RecoilRoot>;
};
describe('useCloseDropdownFromOutside', () => {
it('should close open dropdown', async () => {
const { result } = renderHook(
() => {
const { isDropdownOpen, openDropdown } = useDropdown(dropdownId);
const { closeDropdownFromOutside } = useCloseDropdownFromOutside();
return { closeDropdownFromOutside, isDropdownOpen, openDropdown };
},
{
wrapper: Wrapper,
},
);
expect(result.current.isDropdownOpen).toBe(false);
act(() => {
result.current.openDropdown();
});
expect(result.current.isDropdownOpen).toBe(true);
act(() => {
result.current.closeDropdownFromOutside(dropdownId);
});
expect(result.current.isDropdownOpen).toBe(false);
});
});
@@ -1,4 +1,4 @@
import { useCloseDropdownFromOutside } from '@/ui/layout/dropdown/hooks/useCloseDropdownFromOutside'; import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { activeDropdownFocusIdState } from '@/ui/layout/dropdown/states/activeDropdownFocusIdState'; import { activeDropdownFocusIdState } from '@/ui/layout/dropdown/states/activeDropdownFocusIdState';
import { previousDropdownFocusIdState } from '@/ui/layout/dropdown/states/previousDropdownFocusIdState'; import { previousDropdownFocusIdState } from '@/ui/layout/dropdown/states/previousDropdownFocusIdState';
import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById'; import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById';
@@ -6,7 +6,7 @@ import { useRecoilCallback } from 'recoil';
import { isDefined } from 'twenty-shared/utils'; import { isDefined } from 'twenty-shared/utils';
export const useCloseAnyOpenDropdown = () => { export const useCloseAnyOpenDropdown = () => {
const { closeDropdownFromOutside } = useCloseDropdownFromOutside(); const { closeDropdown } = useCloseDropdown();
const { removeFocusItemFromFocusStackById } = const { removeFocusItemFromFocusStackById } =
useRemoveFocusItemFromFocusStackById(); useRemoveFocusItemFromFocusStackById();
@@ -33,14 +33,14 @@ export const useCloseAnyOpenDropdown = () => {
const thereIsOneNestedDropdownOpen = isDefined(previousDropdownFocusId); const thereIsOneNestedDropdownOpen = isDefined(previousDropdownFocusId);
if (isDefined(activeDropdownFocusId)) { if (isDefined(activeDropdownFocusId)) {
closeDropdownFromOutside(activeDropdownFocusId); closeDropdown(activeDropdownFocusId);
removeFocusItemFromFocusStackById({ removeFocusItemFromFocusStackById({
focusId: activeDropdownFocusId, focusId: activeDropdownFocusId,
}); });
} }
if (thereIsOneNestedDropdownOpen) { if (thereIsOneNestedDropdownOpen) {
closeDropdownFromOutside(previousDropdownFocusId); closeDropdown(previousDropdownFocusId);
removeFocusItemFromFocusStackById({ removeFocusItemFromFocusStackById({
focusId: previousDropdownFocusId, focusId: previousDropdownFocusId,
}); });
@@ -49,7 +49,7 @@ export const useCloseAnyOpenDropdown = () => {
set(previousDropdownFocusIdState, null); set(previousDropdownFocusIdState, null);
set(activeDropdownFocusIdState, null); set(activeDropdownFocusIdState, null);
}, },
[closeDropdownFromOutside, removeFocusItemFromFocusStackById], [closeDropdown, removeFocusItemFromFocusStackById],
); );
return { closeAnyOpenDropdown }; return { closeAnyOpenDropdown };
@@ -0,0 +1,79 @@
import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext';
import { useGoBackToPreviousDropdownFocusId } from '@/ui/layout/dropdown/hooks/useGoBackToPreviousDropdownFocusId';
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
import { isDropdownOpenComponentStateV2 } from '@/ui/layout/dropdown/states/isDropdownOpenComponentStateV2';
import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById';
import { useAvailableComponentInstanceId } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceId';
import { useRecoilCallback } from 'recoil';
import { isDefined } from 'twenty-shared/utils';
export const useCloseDropdown = () => {
const { goBackToPreviousDropdownFocusId } =
useGoBackToPreviousDropdownFocusId();
const { removeFocusItemFromFocusStackById } =
useRemoveFocusItemFromFocusStackById();
const dropdownComponentInstanceIdFromContext =
useAvailableComponentInstanceId(DropdownComponentInstanceContext);
const closeDropdown = useRecoilCallback(
({ set, snapshot }) =>
(dropdownComponentInstanceIdFromProps?: string) => {
const dropdownComponentInstanceId =
dropdownComponentInstanceIdFromProps ??
dropdownComponentInstanceIdFromContext;
if (!isDefined(dropdownComponentInstanceId)) {
throw new Error('Dropdown component instance ID is not defined');
}
const isDropdownOpen = snapshot
.getLoadable(
isDropdownOpenComponentStateV2.atomFamily({
instanceId: dropdownComponentInstanceId,
}),
)
.getValue();
const isDropdownOpenLegacy = snapshot
.getLoadable(
isDropdownOpenComponentState({
scopeId: dropdownComponentInstanceId,
}),
)
.getValue();
if (isDropdownOpen || isDropdownOpenLegacy) {
removeFocusItemFromFocusStackById({
focusId: dropdownComponentInstanceId,
});
goBackToPreviousDropdownFocusId();
set(
isDropdownOpenComponentStateV2.atomFamily({
instanceId: dropdownComponentInstanceId,
}),
false,
);
set(
isDropdownOpenComponentState({
scopeId: dropdownComponentInstanceId,
}),
false,
);
}
},
[
removeFocusItemFromFocusStackById,
goBackToPreviousDropdownFocusId,
dropdownComponentInstanceIdFromContext,
],
);
return {
closeDropdown,
};
};
@@ -1,14 +0,0 @@
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
import { useRecoilCallback } from 'recoil';
export const useCloseDropdownFromOutside = () => {
const closeDropdownFromOutside = useRecoilCallback(
({ set }) =>
(dropdownId: string) => {
set(isDropdownOpenComponentState({ scopeId: dropdownId }), false);
},
[],
);
return { closeDropdownFromOutside };
};
@@ -10,6 +10,13 @@ import { GlobalHotkeysConfig } from '@/ui/utilities/hotkey/types/GlobalHotkeysCo
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { useCallback } from 'react'; import { useCallback } from 'react';
/**
*
* @deprecated This hook is deprecated, use a specific hook instead :
* - `useOpenDropdown`
* - `useCloseDropdown`
* - `useToggleDropdown`
*/
export const useDropdown = (dropdownId?: string) => { export const useDropdown = (dropdownId?: string) => {
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack(); const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
const { removeFocusItemFromFocusStackById } = const { removeFocusItemFromFocusStackById } =
@@ -1,106 +0,0 @@
import { useRecoilCallback } from 'recoil';
import { useGoBackToPreviousDropdownFocusId } from '@/ui/layout/dropdown/hooks/useGoBackToPreviousDropdownFocusId';
import { useSetActiveDropdownFocusIdAndMemorizePrevious } from '@/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious';
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById';
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
import { GlobalHotkeysConfig } from '@/ui/utilities/hotkey/types/GlobalHotkeysConfig';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
export const useDropdownV2 = () => {
const { goBackToPreviousDropdownFocusId } =
useGoBackToPreviousDropdownFocusId();
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
const { removeFocusItemFromFocusStackById } =
useRemoveFocusItemFromFocusStackById();
const { setActiveDropdownFocusIdAndMemorizePrevious } =
useSetActiveDropdownFocusIdAndMemorizePrevious();
const closeDropdown = useRecoilCallback(
({ set, snapshot }) =>
(specificComponentId: string) => {
const scopeId = specificComponentId;
const isDropdownOpen = snapshot
.getLoadable(isDropdownOpenComponentState({ scopeId }))
.getValue();
if (isDropdownOpen) {
removeFocusItemFromFocusStackById({
focusId: scopeId,
});
goBackToPreviousDropdownFocusId();
set(
isDropdownOpenComponentState({
scopeId,
}),
false,
);
}
},
[removeFocusItemFromFocusStackById, goBackToPreviousDropdownFocusId],
);
const openDropdown = useRecoilCallback(
({ set }) =>
(
specificComponentId: string,
globalHotkeysConfig?: Partial<GlobalHotkeysConfig>,
) => {
const scopeId = specificComponentId;
set(
isDropdownOpenComponentState({
scopeId,
}),
true,
);
setActiveDropdownFocusIdAndMemorizePrevious(specificComponentId);
pushFocusItemToFocusStack({
focusId: scopeId,
component: {
type: FocusComponentType.DROPDOWN,
instanceId: scopeId,
},
globalHotkeysConfig,
// TODO: Remove this once we've fully migrated away from hotkey scopes
hotkeyScope: { scope: 'dropdown' } as HotkeyScope,
memoizeKey: 'global',
});
},
[pushFocusItemToFocusStack, setActiveDropdownFocusIdAndMemorizePrevious],
);
const toggleDropdown = useRecoilCallback(
({ snapshot }) =>
(
specificComponentId: string,
globalHotkeysConfig?: Partial<GlobalHotkeysConfig>,
) => {
const scopeId = specificComponentId;
const isDropdownOpen = snapshot
.getLoadable(isDropdownOpenComponentState({ scopeId }))
.getValue();
if (isDropdownOpen) {
closeDropdown(specificComponentId);
} else {
openDropdown(specificComponentId, globalHotkeysConfig);
}
},
[closeDropdown, openDropdown],
);
return {
closeDropdown,
openDropdown,
toggleDropdown,
};
};
@@ -0,0 +1,77 @@
import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext';
import { useSetActiveDropdownFocusIdAndMemorizePrevious } from '@/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious';
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
import { isDropdownOpenComponentStateV2 } from '@/ui/layout/dropdown/states/isDropdownOpenComponentStateV2';
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
import { GlobalHotkeysConfig } from '@/ui/utilities/hotkey/types/GlobalHotkeysConfig';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { useAvailableComponentInstanceId } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceId';
import { useRecoilCallback } from 'recoil';
import { isDefined } from 'twenty-shared/utils';
type OpenDropdownArgs = {
dropdownComponentInstanceIdFromProps?: string;
globalHotkeysConfig?: Partial<GlobalHotkeysConfig>;
};
export const useOpenDropdown = () => {
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
const { setActiveDropdownFocusIdAndMemorizePrevious } =
useSetActiveDropdownFocusIdAndMemorizePrevious();
const dropdownComponentInstanceIdFromContext =
useAvailableComponentInstanceId(DropdownComponentInstanceContext);
const openDropdown = useRecoilCallback(
({ set }) =>
(args?: OpenDropdownArgs | null | undefined) => {
const dropdownComponentInstanceId =
args?.dropdownComponentInstanceIdFromProps ??
dropdownComponentInstanceIdFromContext;
if (!isDefined(dropdownComponentInstanceId)) {
throw new Error('Dropdown component instance ID is not defined');
}
set(
isDropdownOpenComponentState({
scopeId: dropdownComponentInstanceId,
}),
true,
);
set(
isDropdownOpenComponentStateV2.atomFamily({
instanceId: dropdownComponentInstanceId,
}),
true,
);
setActiveDropdownFocusIdAndMemorizePrevious(
dropdownComponentInstanceId,
);
pushFocusItemToFocusStack({
focusId: dropdownComponentInstanceId,
component: {
type: FocusComponentType.DROPDOWN,
instanceId: dropdownComponentInstanceId,
},
globalHotkeysConfig: args?.globalHotkeysConfig ?? undefined,
// TODO: Remove this once we've fully migrated away from hotkey scopes
hotkeyScope: { scope: 'dropdown' } as HotkeyScope,
memoizeKey: 'global',
});
},
[
pushFocusItemToFocusStack,
setActiveDropdownFocusIdAndMemorizePrevious,
dropdownComponentInstanceIdFromContext,
],
);
return {
openDropdown,
};
};
@@ -0,0 +1,66 @@
import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext';
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { useOpenDropdown } from '@/ui/layout/dropdown/hooks/useOpenDropdown';
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
import { isDropdownOpenComponentStateV2 } from '@/ui/layout/dropdown/states/isDropdownOpenComponentStateV2';
import { GlobalHotkeysConfig } from '@/ui/utilities/hotkey/types/GlobalHotkeysConfig';
import { useAvailableComponentInstanceId } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceId';
import { useRecoilCallback } from 'recoil';
import { isDefined } from 'twenty-shared/utils';
export const useToggleDropdown = () => {
const dropdownComponentInstanceIdFromContext =
useAvailableComponentInstanceId(DropdownComponentInstanceContext);
const { openDropdown } = useOpenDropdown();
const { closeDropdown } = useCloseDropdown();
const toggleDropdown = useRecoilCallback(
({ snapshot }) =>
({
dropdownComponentInstanceIdFromProps,
globalHotkeysConfig,
}: {
dropdownComponentInstanceIdFromProps?: string;
globalHotkeysConfig?: Partial<GlobalHotkeysConfig>;
}) => {
const dropdownComponentInstanceId =
dropdownComponentInstanceIdFromProps ??
dropdownComponentInstanceIdFromContext;
if (!isDefined(dropdownComponentInstanceId)) {
throw new Error('Dropdown component instance ID is not defined');
}
const isDropdownOpen = snapshot
.getLoadable(
isDropdownOpenComponentStateV2.atomFamily({
instanceId: dropdownComponentInstanceId,
}),
)
.getValue();
const isDropdownOpenLegacy = snapshot
.getLoadable(
isDropdownOpenComponentState({
scopeId: dropdownComponentInstanceId,
}),
)
.getValue();
if (isDropdownOpen || isDropdownOpenLegacy) {
closeDropdown(dropdownComponentInstanceId);
} else {
openDropdown({
dropdownComponentInstanceIdFromProps: dropdownComponentInstanceId,
globalHotkeysConfig,
});
}
},
[closeDropdown, openDropdown, dropdownComponentInstanceIdFromContext],
);
return {
toggleDropdown,
};
};
@@ -1,4 +1,5 @@
import { createState } from 'twenty-ui/utilities'; import { createState } from 'twenty-ui/utilities';
export const activeDropdownFocusIdState = createState<string | null>({ export const activeDropdownFocusIdState = createState<string | null>({
key: 'activeDropdownFocusIdState', key: 'activeDropdownFocusIdState',
defaultValue: null, defaultValue: null,
@@ -1,4 +1,4 @@
import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponeInstanceContext'; import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext';
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
export const dropdownMaxHeightComponentState = createComponentStateV2< export const dropdownMaxHeightComponentState = createComponentStateV2<
@@ -1,4 +1,4 @@
import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponeInstanceContext'; import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext';
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
export const dropdownMaxWidthComponentState = createComponentStateV2< export const dropdownMaxWidthComponentState = createComponentStateV2<
@@ -1,5 +1,8 @@
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
/**
* @deprecated Use `isDropdownOpenComponentStateV2` instead.
*/
export const isDropdownOpenComponentState = createComponentState<boolean>({ export const isDropdownOpenComponentState = createComponentState<boolean>({
key: 'isDropdownOpenComponentState', key: 'isDropdownOpenComponentState',
defaultValue: false, defaultValue: false,
@@ -0,0 +1,8 @@
import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext';
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
export const isDropdownOpenComponentStateV2 = createComponentStateV2<boolean>({
key: 'isDropdownOpenComponentStateV2',
defaultValue: false,
componentInstanceContext: DropdownComponentInstanceContext,
});
@@ -1,4 +1,5 @@
import { createState } from 'twenty-ui/utilities'; import { createState } from 'twenty-ui/utilities';
export const previousDropdownFocusIdState = createState<string | null>({ export const previousDropdownFocusIdState = createState<string | null>({
key: 'previousDropdownFocusIdState', key: 'previousDropdownFocusIdState',
defaultValue: null, defaultValue: null,
@@ -0,0 +1,19 @@
import { useComponentInstanceStateContext } from '@/ui/utilities/state/component-state/hooks/useComponentInstanceStateContext';
import { ComponentInstanceStateContext } from '@/ui/utilities/state/component-state/types/ComponentInstanceStateContext';
import { isNonEmptyString } from '@sniptt/guards';
export const useAvailableComponentInstanceId = <
T extends { instanceId: string },
>(
Context: ComponentInstanceStateContext<T>,
): string | null => {
const instanceStateContext = useComponentInstanceStateContext(Context);
const instanceIdFromContext = instanceStateContext?.instanceId;
if (isNonEmptyString(instanceIdFromContext)) {
return instanceIdFromContext;
} else {
return null;
}
};
@@ -1,6 +1,6 @@
import { useCallback } from 'react'; import { useCallback } from 'react';
import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2'; import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { VIEW_PICKER_DROPDOWN_ID } from '@/views/view-picker/constants/ViewPickerDropdownId'; import { VIEW_PICKER_DROPDOWN_ID } from '@/views/view-picker/constants/ViewPickerDropdownId';
import { VIEW_PICKER_KANBAN_FIELD_DROPDOWN_ID } from '@/views/view-picker/constants/ViewPickerKanbanFieldDropdownId'; import { VIEW_PICKER_KANBAN_FIELD_DROPDOWN_ID } from '@/views/view-picker/constants/ViewPickerKanbanFieldDropdownId';
@@ -17,7 +17,7 @@ export const useCloseAndResetViewPicker = () => {
viewPickerIsPersistingComponentState, viewPickerIsPersistingComponentState,
); );
const { closeDropdown } = useDropdownV2(); const { closeDropdown } = useCloseDropdown();
const closeAndResetViewPicker = useCallback(() => { const closeAndResetViewPicker = useCallback(() => {
setViewPickerIsPersisting(false); setViewPickerIsPersisting(false);
@@ -1,6 +1,6 @@
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { StyledDropdownButtonContainer } from '@/ui/layout/dropdown/components/StyledDropdownButtonContainer'; import { StyledDropdownButtonContainer } from '@/ui/layout/dropdown/components/StyledDropdownButtonContainer';
import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2'; import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState'; import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState'; import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState';
import { WorkflowVariablesDropdownFieldItems } from '@/workflow/workflow-variables/components/WorkflowVariablesDropdownFieldItems'; import { WorkflowVariablesDropdownFieldItems } from '@/workflow/workflow-variables/components/WorkflowVariablesDropdownFieldItems';
@@ -51,7 +51,7 @@ export const WorkflowVariablesDropdown = ({
const isDropdownOpen = useRecoilValue( const isDropdownOpen = useRecoilValue(
extractComponentState(isDropdownOpenComponentState, dropdownId), extractComponentState(isDropdownOpenComponentState, dropdownId),
); );
const { closeDropdown } = useDropdownV2(); const { closeDropdown } = useCloseDropdown();
const availableVariablesInWorkflowStep = useAvailableVariablesInWorkflowStep({ const availableVariablesInWorkflowStep = useAvailableVariablesInWorkflowStep({
objectNameSingularToSelect, objectNameSingularToSelect,
}); });