Replaced useDropdown calls by useCloseDropdown, useOpenDropdown and useToggleDropdown (#12958)

This PR replaces the many calls of useDropdown by the new standalone
hooks : useCloseDropdown, useOpenDropdown and useToggleDropdown.

This will allow to remove useDropdown and then the dropdown recoil
component state v1.

A big round of QA has been made, with some bugs caught along the way.

Closes https://github.com/twentyhq/core-team-issues/issues/1155
Closes https://github.com/twentyhq/core-team-issues/issues/618

## QA

Component|Status|Comment
|---|---|---|
CurrentWorkspaceMemberFavorites|Ok|
FavoriteFolderPickerFooter|Ok|
AdvancedFilterAddFilterRuleSelect|Ok|
AdvancedFilterRecordFilterGroupOptionsDropdown|Ok|
AdvancedFilterRecordFilterOperandSelectContent|Ok|
AdvancedFilterRecordFilterOptionsDropdown|Ok|
useAdvancedFilterFieldSelectDropdown|Ok|
ObjectFilterDropdownBooleanSelect|Ok|
ObjectFilterDropdownOptionSelect|Ok|
ObjectOptionsDropdown|Ok|
ObjectOptionsDropdownLayoutContent|Ok|
ObjectSortDropdownButton|Ok|
useCloseSortDropdown|Ok|
FormDateTimeFieldInput|Ok|Bug detected, cannot select a month or a year,
see issue https://github.com/twentyhq/twenty/issues/12922
FormSingleRecordPicker|Ok|
MultiItemFieldMenuItem|Ok|
RecordDetailRelationRecordsListItem|Ok|
RecordDetailRelationSection|Ok|
RecordDetailRelationSectionDropdownToMany|Ok|
RecordDetailRelationSectionDropdownToOne|Ok|
RecordTableColumnAggregateFooterDropdownSubmenuContent|Ok|
RecordTableColumnAggregateFooterAggregateOperationMenuItems|Ok|
RecordTableColumnAggregateFooterMenuContent|Ok|
RecordTableColumnAggregateFooterValueCell|Ok|
RecordTableColumnHeadDropdownMenu|Ok|
RecordTableHeaderPlusButtonContent|Ok|
useTriggerActionMenuDropdown|Ok|
MultipleSelectDropdown|Ok|
RecordBoardColumnHeaderAggregateDropdownButton|Ok|
SettingsDataModelFieldSelectFormOptionRow|Ok|
SettingsDataModelNewFieldBreadcrumbDropDown|Ok|
SettingsObjectFieldActiveActionDropdown|Ok|
SettingsObjectFieldInactiveActionDropdown|Ok|
SettingsObjectInactiveMenuDropDown|Ok|
SettingsSecurityApprovedAccessDomainRowDropdownMenu|Couldn’t test|
SettingsSecuritySSORowDropdownMenu|Couldn’t test|
SettingsAccountsRowDropdownMenu|Ok|
SettingsRoleAssignment|Ok|
SettingsServerlessFunctionTabEnvironmentVariableTableRow|Couldn’t test|
MatchColumnToFieldSelect|Ok|
SubMatchingSelectDropdownButton|Ok|Removed conflicting duplicate open
dropdown
SubMatchingSelectRowRightDropdown|Ok|
CurrencyPickerDropdownButton|Ok|
IconPicker|Ok|
DateTimePicker|Ok|
PhoneCountryPickerDropdownButton|OK|
Select|Ok|
Dropdown|Ok|Not QAing all dropdowns in the app because the ones of this
QA are enough to show up that Dropdown is behaving correctly on a lot of
use cases
DropdownMenuInnerSelect|Ok|
TabList|Ok|Removed onClickOutside called in dropdown clickable
component, validated with Raph who recently worked on this
DateInput|Ok|
MultiWorkspaceDropdownDefaultComponents|Ok|
AdvancedFilterChip|Ok|
EditableFilterDropdownButton|Ok|
UpdateViewButtonGroup|Ok|
ViewBarDetailsAddFilterButton|Ok|
ViewBarFilterButton|Ok|
ViewBarFilterDropdown|Ok|
ViewBarFilterDropdownAdvancedFilterButton|Ok|
ViewPickerDropdown|Ok|
ViewPickerListContent|Ok|
ViewPickerOptionDropdown|Ok|
WorkflowEditTriggerDatabaseEventForm|Ok|
WorkflowVariablesDropdownWorkflowStepItems|Ok|
AttachmentDropdown|Ok|
SupportDropdown|Ok|

Co-authored-by: Charles Bochet <charlesBochet@users.noreply.github.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Lucas Bordeau
2025-07-02 18:38:45 +02:00
committed by GitHub
parent a19a73a977
commit a7b9a0710e
70 changed files with 352 additions and 426 deletions

View File

@ -5,7 +5,6 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { arrayToChunks } from '~/utils/array/arrayToChunks';
@ -13,6 +12,8 @@ import { arrayToChunks } from '~/utils/array/arrayToChunks';
import { ICON_PICKER_DROPDOWN_CONTENT_WIDTH } from '@/ui/input/components/constants/IconPickerDropdownContentWidth';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { DropdownOffset } from '@/ui/layout/dropdown/types/DropdownOffset';
import { useSelectableListListenToEnterHotkeyOnItem } from '@/ui/layout/selectable-list/hooks/useSelectableListListenToEnterHotkeyOnItem';
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
@ -25,7 +26,6 @@ import {
LightIconButton,
} from 'twenty-ui/input';
import { IconPickerHotkeyScope } from '../types/IconPickerHotkeyScope';
import { DropdownOffset } from '@/ui/layout/dropdown/types/DropdownOffset';
export type IconPickerProps = {
disabled?: boolean;
@ -137,7 +137,7 @@ export const IconPicker = ({
}
};
const { closeDropdown } = useDropdown(dropdownId);
const { closeDropdown } = useCloseDropdown();
const { getIcons, getIcon } = useIcons();
const icons = getIcons();
@ -242,7 +242,7 @@ export const IconPicker = ({
iconKey={iconKey}
onClick={() => {
onChange({ iconKey, Icon: getIcon(iconKey) });
closeDropdown();
closeDropdown(dropdownId);
}}
selectedIconKey={selectedIconKey}
Icon={getIcon(iconKey)}

View File

@ -5,13 +5,13 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { SelectValue } from '@/ui/input/components/internal/select/types';
import { SelectControl } from '@/ui/input/components/SelectControl';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { DropdownOffset } from '@/ui/layout/dropdown/types/DropdownOffset';
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
@ -116,7 +116,7 @@ export const Select = <Value extends SelectValue>({
!isDefined(callToActionButton) &&
(!isDefined(emptyOption) || selectedOption !== emptyOption));
const { closeDropdown } = useDropdown(dropdownId);
const { closeDropdown } = useCloseDropdown();
const dropDownMenuWidth =
dropdownWidthAuto && selectContainerRef.current?.clientWidth
@ -195,7 +195,7 @@ export const Select = <Value extends SelectValue>({
onEnter={() => {
onChange?.(option.value);
onBlur?.();
closeDropdown();
closeDropdown(dropdownId);
}}
>
<MenuItemSelect
@ -207,7 +207,7 @@ export const Select = <Value extends SelectValue>({
onClick={() => {
onChange?.(option.value);
onBlur?.();
closeDropdown();
closeDropdown(dropdownId);
}}
/>
</SelectableListItem>

View File

@ -3,12 +3,12 @@ import styled from '@emotion/styled';
import { CurrencyCode } from '@/object-record/record-field/types/CurrencyCode';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { CurrencyPickerHotkeyScope } from '../types/CurrencyPickerHotkeyScope';
import { CURRENCIES } from '@/settings/data-model/constants/Currencies';
import { Currency } from '@/ui/input/components/internal/types/Currency';
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { IconChevronDown } from 'twenty-ui/display';
import { CurrencyPickerDropdownSelect } from './CurrencyPickerDropdownSelect';
@ -52,13 +52,13 @@ export const CurrencyPickerDropdownButton = ({
}) => {
const theme = useTheme();
const { closeDropdown } = useDropdown(
CurrencyPickerHotkeyScope.CurrencyPicker,
);
const dropdownId = CurrencyPickerHotkeyScope.CurrencyPicker;
const { closeDropdown } = useCloseDropdown();
const handleChange = (currency: Currency) => {
onChange(currency);
closeDropdown();
closeDropdown(dropdownId);
};
const currency = CURRENCIES.find(

View File

@ -3,13 +3,12 @@ import { DateTime } from 'luxon';
import { lazy, Suspense, useContext } from 'react';
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { SKELETON_LOADER_HEIGHT_SIZES } from '@/activities/components/SkeletonLoader';
import { AbsoluteDatePickerHeader } from '@/ui/input/components/internal/date/components/AbsoluteDatePickerHeader';
import { DateTimeInput } from '@/ui/input/components/internal/date/components/DateTimeInput';
import { RelativeDatePickerHeader } from '@/ui/input/components/internal/date/components/RelativeDatePickerHeader';
import { getHighlightedDates } from '@/ui/input/components/internal/date/utils/getHighlightedDates';
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { UserContext } from '@/users/contexts/UserContext';
import {
VariableDateViewFilterValueDirection,
@ -342,12 +341,8 @@ export const DateTimePicker = ({
const { timeZone } = useContext(UserContext);
const theme = useTheme();
const { closeDropdown: closeDropdownMonthSelect } = useDropdown(
MONTH_AND_YEAR_DROPDOWN_MONTH_SELECT_ID,
);
const { closeDropdown: closeDropdownYearSelect } = useDropdown(
MONTH_AND_YEAR_DROPDOWN_YEAR_SELECT_ID,
);
const { closeDropdown: closeDropdownMonthSelect } = useCloseDropdown();
const { closeDropdown: closeDropdownYearSelect } = useCloseDropdown();
const handleClear = () => {
closeDropdowns();
@ -355,8 +350,8 @@ export const DateTimePicker = ({
};
const closeDropdowns = () => {
closeDropdownYearSelect();
closeDropdownMonthSelect();
closeDropdownYearSelect(MONTH_AND_YEAR_DROPDOWN_YEAR_SELECT_ID);
closeDropdownMonthSelect(MONTH_AND_YEAR_DROPDOWN_MONTH_SELECT_ID);
};
const handleClose = (newDate: Date) => {

View File

@ -1,13 +1,15 @@
import { useCountries } from '@/ui/input/components/internal/hooks/useCountries';
import { Country } from '@/ui/input/components/internal/types/Country';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useEffect, useState } from 'react';
import { PhoneCountryPickerDropdownSelect } from './PhoneCountryPickerDropdownSelect';
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { isDropdownOpenComponentStateV2 } from '@/ui/layout/dropdown/states/isDropdownOpenComponentStateV2';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import 'react-phone-number-input/style.css';
import { isDefined } from 'twenty-shared/utils';
import { IconChevronDown, IconWorld } from 'twenty-ui/display';
@ -75,12 +77,17 @@ export const PhoneCountryPickerDropdownButton = ({
const [selectedCountry, setSelectedCountry] = useState<Country>();
const { isDropdownOpen, closeDropdown } = useDropdown(
'country-picker-dropdown-id',
const dropdownId = 'country-picker-dropdown-id';
const isDropdownOpen = useRecoilComponentValueV2(
isDropdownOpenComponentStateV2,
dropdownId,
);
const { closeDropdown } = useCloseDropdown();
const handleChange = (countryCode: string) => {
closeDropdown();
closeDropdown(dropdownId);
onChange(countryCode);
};