From 3d90eb4eb9b5de4713b8639d1e92e999945e2731 Mon Sep 17 00:00:00 2001 From: Lucas Bordeau Date: Tue, 8 Apr 2025 11:03:10 +0200 Subject: [PATCH] Fix broken dropdown auto resize behavior (#11423) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR was originally about fixing advanced filter dropdown auto resize to avoid breaking the app main container, but the regression is not limited to advanced filter dropdown, so this PR fixes the regression for every dropdown in the app. This PR adds a max dropdown max width to allow resizing dropdowns horizontally also, which can happen easily for the advanced filter dropdown. In this PR we also start removing `fieldMetadataItemUsedInDropdown` in component `AdvancedFilterDropdownTextInput` because it has no impact outside of this component which is used only once. The autoresize behavior determines the right padding-bottom between mobile and PC. Mobile : Capture d’écran 2025-04-07 à 16 03 12 PC : Capture d’écran 2025-04-07 à 16 03 30 Fixes https://github.com/twentyhq/core-team-issues/issues/725 Fixes https://github.com/twentyhq/twenty/issues/11409 --------- Co-authored-by: Charles Bochet --- .../RecordIndexActionMenuDropdown.tsx | 2 +- .../files/components/AttachmentDropdown.tsx | 2 +- .../AdvancedFilterDropdownFilterInput.tsx | 23 ++------ .../AdvancedFilterDropdownTextInput.tsx | 51 ++++++------------ .../AdvancedFilterRootRecordFilterGroup.tsx | 54 ++++++++++--------- .../components/AdvancedFilterValueInput.tsx | 14 ++--- .../MultipleFiltersDropdownContent.tsx | 2 - ...ersDropdownFilterOnFilterChangedEffect.tsx | 36 ------------- .../components/ObjectOptionsDropdown.tsx | 2 +- .../__tests__/useOptionsDropdown.test.tsx | 2 - ...cordBoardColumnHeaderAggregateDropdown.tsx | 2 +- .../RecordIndexAddRecordInGroupDropdown.tsx | 2 +- .../SettingsAccountsRowDropdownMenu.tsx | 4 +- .../SettingsDataModelFieldPhonesForm.tsx | 7 ++- ...ettingsObjectFieldActiveActionDropdown.tsx | 2 +- ...tingsObjectFieldDisabledActionDropdown.tsx | 4 +- .../components/SettingsObjectSummaryCard.tsx | 2 +- .../SettingsObjectInactiveMenuDropDown.tsx | 2 +- .../SettingsSecuritySSORowDropdownMenu.tsx | 4 +- ...ityApprovedAccessDomainRowDropdownMenu.tsx | 6 +-- .../support/components/SupportDropdown.tsx | 2 +- .../ui/input/components/IconPicker.tsx | 4 +- .../modules/ui/input/components/Select.tsx | 9 ++-- .../date/components/InternalDatePicker.tsx | 2 + .../layout/dropdown/components/Dropdown.tsx | 40 +++++++++++--- .../dropdown/components/DropdownContent.tsx | 21 +++++--- .../dropdown/components/DropdownMenu.tsx | 9 +++- .../components/DropdownMenuItemsContainer.tsx | 13 ++++- .../hooks/__tests__/useDropdown.test.tsx | 14 ----- .../hooks/internal/useDropdownStates.ts | 5 -- .../ui/layout/dropdown/hooks/useDropdown.ts | 16 ++---- .../states/dropdownWidthComponentState.ts | 8 --- .../dropdownMaxHeightComponentState.ts} | 4 +- .../dropdownMaxWidthComponentState.ts | 10 ++++ .../AdvancedFilterDropdownButton.tsx | 2 +- .../components/ViewPickerDropdown.tsx | 1 - .../WorkflowEditTriggerDatabaseEventForm.tsx | 2 +- .../components/WorkflowVariablesDropdown.tsx | 2 +- 38 files changed, 174 insertions(+), 213 deletions(-) delete mode 100644 packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/MultipleFiltersDropdownFilterOnFilterChangedEffect.tsx delete mode 100644 packages/twenty-front/src/modules/ui/layout/dropdown/states/dropdownWidthComponentState.ts rename packages/twenty-front/src/modules/ui/layout/dropdown/states/{dropdownMaxHeightComponentStateV2.ts => internal/dropdownMaxHeightComponentState.ts} (74%) create mode 100644 packages/twenty-front/src/modules/ui/layout/dropdown/states/internal/dropdownMaxWidthComponentState.ts diff --git a/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuDropdown.tsx b/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuDropdown.tsx index 10bcf2da3b..34ffed71f3 100644 --- a/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuDropdown.tsx +++ b/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuDropdown.tsx @@ -72,7 +72,7 @@ export const RecordIndexActionMenuDropdown = () => { scope: ActionMenuDropdownHotkeyScope.ActionMenuDropdown, }} data-select-disable - dropdownMenuWidth={width} + dropdownWidth={width} dropdownPlacement="bottom-start" dropdownStrategy="absolute" dropdownOffset={{ diff --git a/packages/twenty-front/src/modules/activities/files/components/AttachmentDropdown.tsx b/packages/twenty-front/src/modules/activities/files/components/AttachmentDropdown.tsx index c27dc6bed4..4a3bddc56c 100644 --- a/packages/twenty-front/src/modules/activities/files/components/AttachmentDropdown.tsx +++ b/packages/twenty-front/src/modules/activities/files/components/AttachmentDropdown.tsx @@ -48,7 +48,7 @@ export const AttachmentDropdown = ({ clickableComponent={ } - dropdownMenuWidth={160} + dropdownWidth={160} dropdownComponents={ { - const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2( - fieldMetadataItemUsedInDropdownComponentSelector, - filterDropdownId, - ); - const subFieldNameUsedInDropdown = useRecoilComponentValueV2( subFieldNameUsedInDropdownComponentState, filterDropdownId, ); - if (!isDefined(fieldMetadataItemUsedInDropdown)) { - return null; - } - - const filterType = getFilterTypeFromFieldType( - fieldMetadataItemUsedInDropdown.type, - ); + const filterType = recordFilter.type; const isActorSourceCompositeFilter = isFilterOnActorSourceSubField( subFieldNameUsedInDropdown, @@ -57,7 +44,7 @@ export const AdvancedFilterDropdownFilterInput = ({ <> - + )} {filterType === 'ACTOR' && diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterDropdownTextInput.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterDropdownTextInput.tsx index 37d03684e8..723bf617cd 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterDropdownTextInput.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterDropdownTextInput.tsx @@ -1,57 +1,36 @@ import { useState } from 'react'; -import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; -import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector'; -import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState'; -import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter'; +import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter'; import { TextInputV2 } from '@/ui/input/components/TextInputV2'; -import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; -import { v4 } from 'uuid'; -export const AdvancedFilterDropdownTextInput = () => { - const selectedOperandInDropdown = useRecoilComponentValueV2( - selectedOperandInDropdownComponentState, - ); - - const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2( - fieldMetadataItemUsedInDropdownComponentSelector, - ); - - const selectedFilter = useRecoilComponentValueV2( - selectedFilterComponentState, - ); +type AdvancedFilterDropdownTextInputProps = { + recordFilter: RecordFilter; +}; +export const AdvancedFilterDropdownTextInput = ({ + recordFilter, +}: AdvancedFilterDropdownTextInputProps) => { const { applyRecordFilter } = useApplyRecordFilter(); - const [inputValue, setInputValue] = useState( - () => selectedFilter?.value || '', - ); + const [inputValue, setInputValue] = useState(() => recordFilter?.value || ''); const handleChange = (newValue: string) => { - if (!fieldMetadataItemUsedInDropdown || !selectedOperandInDropdown) { - return; - } - setInputValue(newValue); applyRecordFilter({ - id: selectedFilter?.id ? selectedFilter.id : v4(), - fieldMetadataId: fieldMetadataItemUsedInDropdown?.id ?? '', + id: recordFilter.id, + fieldMetadataId: recordFilter?.fieldMetadataId ?? '', value: newValue, - operand: selectedOperandInDropdown, + operand: recordFilter.operand, displayValue: newValue, - type: getFilterTypeFromFieldType(fieldMetadataItemUsedInDropdown.type), - label: fieldMetadataItemUsedInDropdown.label, - recordFilterGroupId: selectedFilter?.recordFilterGroupId, - positionInRecordFilterGroup: selectedFilter?.positionInRecordFilterGroup, + type: recordFilter.type, + label: recordFilter.label, + recordFilterGroupId: recordFilter?.recordFilterGroupId, + positionInRecordFilterGroup: recordFilter?.positionInRecordFilterGroup, }); }; - if (!selectedOperandInDropdown || !fieldMetadataItemUsedInDropdown) { - return null; - } - return ( ` @@ -16,7 +18,7 @@ const StyledContainer = styled.div<{ isGrayBackground?: boolean }>` flex-direction: column; gap: ${({ theme }) => theme.spacing(2)}; padding: ${({ theme }) => theme.spacing(2)}; - overflow: hidden; + min-width: 650px; `; export const AdvancedFilterRootRecordFilterGroup = () => { @@ -34,28 +36,32 @@ export const AdvancedFilterRootRecordFilterGroup = () => { } return ( - - {childRecordFiltersAndRecordFilterGroups.map( - (recordFilterGroupChild, recordFilterGroupChildIndex) => - isRecordFilterGroupChildARecordFilterGroup(recordFilterGroupChild) ? ( - - ) : ( - - ), - )} - - + + + {childRecordFiltersAndRecordFilterGroups.map( + (recordFilterGroupChild, recordFilterGroupChildIndex) => + isRecordFilterGroupChildARecordFilterGroup( + recordFilterGroupChild, + ) ? ( + + ) : ( + + ), + )} + + + ); }; diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterValueInput.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterValueInput.tsx index b804e2055c..fce8ea9c25 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterValueInput.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterValueInput.tsx @@ -45,11 +45,15 @@ export const AdvancedFilterValueInput = ({ const operandHasNoInput = recordFilter && !configurableViewFilterOperands.has(recordFilter.operand); + if (!isDefined(recordFilter)) { + return null; + } + const handleFilterValueDropdownClose = () => { setObjectFilterDropdownSearchInput(''); }; - const filterType = recordFilter?.type; + const filterType = recordFilter.type; const dropdownContentOffset = filterType === 'DATE' || filterType === 'DATE_TIME' @@ -67,7 +71,7 @@ export const AdvancedFilterValueInput = ({ ) : isDefined(filterType) && (TEXT_FILTER_TYPES.includes(filterType) || NUMBER_FILTER_TYPES.includes(filterType)) ? ( - + ) : ( } dropdownComponents={ - + } dropdownHotkeyScope={{ scope: dropdownId }} dropdownOffset={dropdownContentOffset} dropdownPlacement="bottom-start" - dropdownMenuWidth={280} + dropdownWidth={280} onClose={handleFilterValueDropdownClose} /> )} diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/MultipleFiltersDropdownContent.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/MultipleFiltersDropdownContent.tsx index 78d2cbeb33..631c391b98 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/MultipleFiltersDropdownContent.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/MultipleFiltersDropdownContent.tsx @@ -3,7 +3,6 @@ import { ObjectFilterOperandSelectAndInput } from '@/object-record/object-filter import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFilterIsSelectedComponentState'; import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState'; import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; -import { MultipleFiltersDropdownFilterOnFilterChangedEffect } from './MultipleFiltersDropdownFilterOnFilterChangedEffect'; import { ObjectFilterDropdownFilterSelect } from './ObjectFilterDropdownFilterSelect'; type MultipleFiltersDropdownContentProps = { @@ -40,7 +39,6 @@ export const MultipleFiltersDropdownContent = ({ ) : ( )} - ); }; diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/MultipleFiltersDropdownFilterOnFilterChangedEffect.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/MultipleFiltersDropdownFilterOnFilterChangedEffect.tsx deleted file mode 100644 index 8c3c297253..0000000000 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/MultipleFiltersDropdownFilterOnFilterChangedEffect.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { useEffect } from 'react'; - -import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; -import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector'; -import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; -import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; -import { isDefined } from 'twenty-shared/utils'; - -export const MultipleFiltersDropdownFilterOnFilterChangedEffect = () => { - const { setDropdownWidth } = useDropdown(); - - const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2( - fieldMetadataItemUsedInDropdownComponentSelector, - ); - - useEffect(() => { - if (!isDefined(fieldMetadataItemUsedInDropdown)) { - return; - } - - const filterType = getFilterTypeFromFieldType( - fieldMetadataItemUsedInDropdown.type, - ); - - switch (filterType) { - case 'DATE': - case 'DATE_TIME': - setDropdownWidth(280); - break; - default: - setDropdownWidth(200); - } - }, [fieldMetadataItemUsedInDropdown, setDropdownWidth]); - - return null; -}; diff --git a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdown.tsx b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdown.tsx index 19b7e2c54e..df13e04eba 100644 --- a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdown.tsx +++ b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdown.tsx @@ -33,7 +33,7 @@ export const ObjectOptionsDropdown = ({ diff --git a/packages/twenty-front/src/modules/object-record/object-options-dropdown/hooks/__tests__/useOptionsDropdown.test.tsx b/packages/twenty-front/src/modules/object-record/object-options-dropdown/hooks/__tests__/useOptionsDropdown.test.tsx index edebe6b213..837e1bc695 100644 --- a/packages/twenty-front/src/modules/object-record/object-options-dropdown/hooks/__tests__/useOptionsDropdown.test.tsx +++ b/packages/twenty-front/src/modules/object-record/object-options-dropdown/hooks/__tests__/useOptionsDropdown.test.tsx @@ -26,8 +26,6 @@ describe('useOptionsDropdown', () => { closeDropdown: mockCloseDropdown, toggleDropdown: jest.fn(), openDropdown: jest.fn(), - dropdownWidth: undefined, - setDropdownWidth: jest.fn(), dropdownPlacement: null, setDropdownPlacement: jest.fn(), }); diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdown.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdown.tsx index f384739a62..bb8fdde83b 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdown.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdown.tsx @@ -46,7 +46,7 @@ export const RecordBoardColumnHeaderAggregateDropdown = ({ dropdownHotkeyScope={{ scope: RecordBoardColumnHotkeyScope.ColumnHeader, }} - dropdownMenuWidth={DROPDOWN_WIDTH} + dropdownWidth={DROPDOWN_WIDTH} dropdownOffset={{ y: DROPDOWN_OFFSET_Y }} clickableComponent={ } - dropdownMenuWidth={160} + dropdownWidth={160} dropdownComponents={ - dropdownWidth={'auto'} dropdownId="selectDefaultCountryCode" value={stripSimpleQuotesFromString( value?.primaryPhoneCountryCode, diff --git a/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectFieldActiveActionDropdown.tsx b/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectFieldActiveActionDropdown.tsx index 458dee809c..c987fddc0a 100644 --- a/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectFieldActiveActionDropdown.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectFieldActiveActionDropdown.tsx @@ -55,7 +55,7 @@ export const SettingsObjectFieldActiveActionDropdown = ({ accent="tertiary" /> } - dropdownMenuWidth={160} + dropdownWidth={160} dropdownComponents={ } - dropdownMenuWidth={160} + dropdownWidth={160} dropdownComponents={ } - dropdownMenuWidth={160} + dropdownWidth={160} dropdownComponents={ } - dropdownMenuWidth={160} + dropdownWidth={160} dropdownComponents={ [0]; @@ -72,7 +72,7 @@ export const SettingsSecuritySSORowDropdownMenu = ({ clickableComponent={ } - dropdownMenuWidth={160} + dropdownWidth={160} dropdownComponents={ [0]; @@ -61,7 +61,7 @@ export const SettingsSecurityApprovedAccessDomainRowDropdownMenu = ({ clickableComponent={ } - dropdownMenuWidth={160} + dropdownWidth={160} dropdownComponents={ { dropdownPlacement="top-start" dropdownOffset={{ x: 0, y: -28 }} clickableComponent={} - dropdownMenuWidth={160} + dropdownWidth={160} dropdownComponents={ } - dropdownMenuWidth={176} + dropdownWidth={176} dropdownComponents={ = { disabled?: boolean; selectSizeVariant?: SelectSizeVariant; dropdownId: string; - dropdownWidth?: `${string}px` | 'auto' | number; + dropdownWidth?: number; dropdownWidthAuto?: boolean; emptyOption?: SelectOption; fullWidth?: boolean; @@ -128,7 +128,7 @@ export const Select = ({ ) : ( ({ )} {!!filteredOptions.length && ( - + {filteredOptions.map((option) => ( ` + width: 280px; + & .react-datepicker { border-color: ${({ theme }) => theme.border.color.light}; background: transparent; diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/components/Dropdown.tsx b/packages/twenty-front/src/modules/ui/layout/dropdown/components/Dropdown.tsx index 4b838c8f7b..4e08747b52 100644 --- a/packages/twenty-front/src/modules/ui/layout/dropdown/components/Dropdown.tsx +++ b/packages/twenty-front/src/modules/ui/layout/dropdown/components/Dropdown.tsx @@ -3,9 +3,12 @@ import { DropdownOnToggleEffect } from '@/ui/layout/dropdown/components/Dropdown import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponeInstanceContext'; import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope'; import { dropdownHotkeyComponentState } from '@/ui/layout/dropdown/states/dropdownHotkeyComponentState'; +import { dropdownMaxHeightComponentState } from '@/ui/layout/dropdown/states/internal/dropdownMaxHeightComponentState'; +import { dropdownMaxWidthComponentState } from '@/ui/layout/dropdown/states/internal/dropdownMaxWidthComponentState'; import { DropdownOffset } from '@/ui/layout/dropdown/types/DropdownOffset'; import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import styled from '@emotion/styled'; import { Placement, @@ -20,6 +23,7 @@ import { flushSync } from 'react-dom'; import { Keys } from 'react-hotkeys-hook'; import { useRecoilCallback } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; +import { useIsMobile } from 'twenty-ui/utilities'; import { useDropdown } from '../hooks/useDropdown'; const StyledDropdownFallbackAnchor = styled.div` @@ -43,7 +47,7 @@ export type DropdownProps = { dropdownHotkeyScope: HotkeyScope; dropdownId: string; dropdownPlacement?: Placement; - dropdownMenuWidth?: `${string}px` | `${number}%` | 'auto' | number; + dropdownWidth?: `${string}px` | `${number}%` | 'auto' | number; dropdownOffset?: DropdownOffset; dropdownStrategy?: 'fixed' | 'absolute'; onClickOutside?: () => void; @@ -56,7 +60,7 @@ export const Dropdown = ({ className, clickableComponent, dropdownComponents, - dropdownMenuWidth, + dropdownWidth, hotkey, dropdownId, dropdownHotkeyScope, @@ -82,17 +86,39 @@ export const Dropdown = ({ ] : []; + const setDropdownMaxHeight = useSetRecoilComponentStateV2( + dropdownMaxHeightComponentState, + dropdownId, + ); + + const setDropdownMaxWidth = useSetRecoilComponentStateV2( + dropdownMaxWidthComponentState, + dropdownId, + ); + + const isMobile = useIsMobile(); + const bottomAutoresizePadding = isMobile ? 64 : 32; + const { refs, floatingStyles, placement } = useFloating({ placement: dropdownPlacement, middleware: [ ...offsetMiddleware, flip(), size({ - padding: 32, - apply: () => { + padding: { + right: 32, + bottom: bottomAutoresizePadding, + }, + /** + * DO NOT TOUCH THIS apply() MIDDLEWARE PLEASE + * THIS IS MANDATORY FOR KEEPING AUTORESIZING FOR ALL DROPDOWNS + * IT'S THE STANDARD WAY OF WORKING RECOMMENDED BY THE LIBRARY + * See https://floating-ui.com/docs/size#usage + */ + apply: ({ availableHeight, availableWidth }) => { flushSync(() => { - // TODO: I think this is not needed anymore let's remove it if not used for a few weeks - // setDropdownMaxHeight(availableHeight); + setDropdownMaxHeight(availableHeight); + setDropdownMaxWidth(availableWidth); }); }, boundary: document.querySelector('#root') ?? undefined, @@ -144,7 +170,7 @@ export const Dropdown = ({ void; - dropdownMenuWidth?: `${string}px` | `${number}%` | 'auto' | number; + dropdownWidth?: `${string}px` | `${number}%` | 'auto' | number; dropdownComponents: React.ReactNode; parentDropdownId?: string; avoidPortal?: boolean; @@ -56,17 +57,22 @@ export const DropdownContent = ({ floatingStyles, hotkey, onHotkeyTriggered, - dropdownMenuWidth, + dropdownWidth, dropdownComponents, avoidPortal, }: DropdownContentProps) => { - const { isDropdownOpen, closeDropdown, dropdownWidth, setDropdownPlacement } = + const { isDropdownOpen, closeDropdown, setDropdownPlacement } = useDropdown(dropdownId); const activeDropdownFocusId = useRecoilValue(activeDropdownFocusIdState); const dropdownMaxHeight = useRecoilComponentValueV2( - dropdownMaxHeightComponentStateV2, + dropdownMaxHeightComponentState, + dropdownId, + ); + + const dropdownMaxWidth = useRecoilComponentValueV2( + dropdownMaxWidthComponentState, dropdownId, ); @@ -112,6 +118,7 @@ export const DropdownContent = ({ const dropdownMenuStyles = { ...floatingStyles, maxHeight: dropdownMaxHeight, + maxWidth: dropdownMaxWidth, }; return ( @@ -129,7 +136,7 @@ export const DropdownContent = ({ {dropdownComponents} @@ -148,7 +155,7 @@ export const DropdownContent = ({ {dropdownComponents} diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenu.tsx b/packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenu.tsx index 5967fbd0a8..8a77b3a6c6 100644 --- a/packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenu.tsx +++ b/packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenu.tsx @@ -1,4 +1,5 @@ import styled from '@emotion/styled'; +import { isDefined } from 'twenty-shared/utils'; const StyledDropdownMenu = styled.div<{ width?: `${string}px` | `${number}%` | 'auto' | number; @@ -7,8 +8,12 @@ const StyledDropdownMenu = styled.div<{ flex-direction: column; height: 100%; - width: ${({ width = 200 }) => - typeof width === 'number' ? `${width}px` : width}; + width: ${({ width }) => + isDefined(width) + ? typeof width === 'number' + ? `${width}px` + : width + : 'auto'}; `; export const DropdownMenu = StyledDropdownMenu; diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenuItemsContainer.tsx b/packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenuItemsContainer.tsx index 679d61a0dd..9ed2897bc8 100644 --- a/packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenuItemsContainer.tsx +++ b/packages/twenty-front/src/modules/ui/layout/dropdown/components/DropdownMenuItemsContainer.tsx @@ -1,9 +1,12 @@ import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper'; +import { css } from '@emotion/react'; import styled from '@emotion/styled'; import { useId } from 'react'; +import { isDefined } from 'twenty-shared/utils'; const StyledDropdownMenuItemsExternalContainer = styled.div<{ hasMaxHeight?: boolean; + width: number; }>` --padding: ${({ theme }) => theme.spacing(1)}; @@ -15,7 +18,11 @@ const StyledDropdownMenuItemsExternalContainer = styled.div<{ padding: var(--padding); - width: calc(100% - 2 * var(--padding)); + ${({ width }) => + isDefined(width) && + css` + width: ${width}px; + `} `; const StyledDropdownMenuItemsInternalContainer = styled.div` @@ -38,12 +45,14 @@ export const DropdownMenuItemsContainer = ({ children, hasMaxHeight, className, + width = 200, scrollable = true, }: { children: React.ReactNode; hasMaxHeight?: boolean; className?: string; scrollable?: boolean; + width?: number; }) => { const id = useId(); @@ -52,6 +61,7 @@ export const DropdownMenuItemsContainer = ({ hasMaxHeight={hasMaxHeight} className={className} role="listbox" + width={width} > {hasMaxHeight ? ( {children} diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/__tests__/useDropdown.test.tsx b/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/__tests__/useDropdown.test.tsx index 68ea50959e..4cd9c1f83b 100644 --- a/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/__tests__/useDropdown.test.tsx +++ b/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/__tests__/useDropdown.test.tsx @@ -51,18 +51,4 @@ describe('useDropdown', () => { expect(result.current.isDropdownOpen).toBe(false); }); - - it('should change dropdownWidth', async () => { - const { result } = renderHook(() => useDropdown(dropdownId), { - wrapper: Wrapper, - }); - - expect(result.current.dropdownWidth).toBe(200); - - await act(async () => { - result.current.setDropdownWidth(220); - }); - - expect(result.current.dropdownWidth).toEqual(220); - }); }); diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/internal/useDropdownStates.ts b/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/internal/useDropdownStates.ts index feb437745f..5c81780039 100644 --- a/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/internal/useDropdownStates.ts +++ b/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/internal/useDropdownStates.ts @@ -1,7 +1,6 @@ import { DropdownScopeInternalContext } from '@/ui/layout/dropdown/scopes/scope-internal-context/DropdownScopeInternalContext'; import { dropdownHotkeyComponentState } from '@/ui/layout/dropdown/states/dropdownHotkeyComponentState'; import { dropdownPlacementComponentState } from '@/ui/layout/dropdown/states/dropdownPlacementComponentState'; -import { dropdownWidthComponentState } from '@/ui/layout/dropdown/states/dropdownWidthComponentState'; import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState'; import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId'; import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState'; @@ -28,10 +27,6 @@ export const useDropdownStates = ({ dropdownHotkeyComponentState, scopeId, ), - dropdownWidthState: extractComponentState( - dropdownWidthComponentState, - scopeId, - ), isDropdownOpenState: extractComponentState( isDropdownOpenComponentState, scopeId, diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useDropdown.ts b/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useDropdown.ts index 512b542778..1f1cc83d3d 100644 --- a/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useDropdown.ts +++ b/packages/twenty-front/src/modules/ui/layout/dropdown/hooks/useDropdown.ts @@ -12,14 +12,10 @@ import { useCallback } from 'react'; import { isDefined } from 'twenty-shared/utils'; export const useDropdown = (dropdownId?: string) => { - const { - scopeId, - dropdownWidthState, - isDropdownOpenState, - dropdownPlacementState, - } = useDropdownStates({ - dropdownScopeId: getScopeIdOrUndefinedFromComponentId(dropdownId), - }); + const { scopeId, isDropdownOpenState, dropdownPlacementState } = + useDropdownStates({ + dropdownScopeId: getScopeIdOrUndefinedFromComponentId(dropdownId), + }); const { setActiveDropdownFocusIdAndMemorizePrevious } = useSetActiveDropdownFocusIdAndMemorizePrevious(); @@ -32,8 +28,6 @@ export const useDropdown = (dropdownId?: string) => { goBackToPreviousHotkeyScope, } = usePreviousHotkeyScope(); - const [dropdownWidth, setDropdownWidth] = useRecoilState(dropdownWidthState); - const [dropdownPlacement, setDropdownPlacement] = useRecoilState( dropdownPlacementState, ); @@ -103,8 +97,6 @@ export const useDropdown = (dropdownId?: string) => { closeDropdown, toggleDropdown, openDropdown, - dropdownWidth, - setDropdownWidth, dropdownPlacement, setDropdownPlacement, }; diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/states/dropdownWidthComponentState.ts b/packages/twenty-front/src/modules/ui/layout/dropdown/states/dropdownWidthComponentState.ts deleted file mode 100644 index bec6cb37b6..0000000000 --- a/packages/twenty-front/src/modules/ui/layout/dropdown/states/dropdownWidthComponentState.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; - -export const dropdownWidthComponentState = createComponentState< - number | undefined ->({ - key: 'dropdownWidthComponentState', - defaultValue: 200, -}); diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/states/dropdownMaxHeightComponentStateV2.ts b/packages/twenty-front/src/modules/ui/layout/dropdown/states/internal/dropdownMaxHeightComponentState.ts similarity index 74% rename from packages/twenty-front/src/modules/ui/layout/dropdown/states/dropdownMaxHeightComponentStateV2.ts rename to packages/twenty-front/src/modules/ui/layout/dropdown/states/internal/dropdownMaxHeightComponentState.ts index 9cd8185c09..f375c8b87f 100644 --- a/packages/twenty-front/src/modules/ui/layout/dropdown/states/dropdownMaxHeightComponentStateV2.ts +++ b/packages/twenty-front/src/modules/ui/layout/dropdown/states/internal/dropdownMaxHeightComponentState.ts @@ -1,10 +1,10 @@ import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponeInstanceContext'; import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; -export const dropdownMaxHeightComponentStateV2 = createComponentStateV2< +export const dropdownMaxHeightComponentState = createComponentStateV2< number | undefined >({ - key: 'dropdownMaxHeightComponentStateV2', + key: 'dropdownMaxHeightComponentState', componentInstanceContext: DropdownComponentInstanceContext, defaultValue: undefined, }); diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/states/internal/dropdownMaxWidthComponentState.ts b/packages/twenty-front/src/modules/ui/layout/dropdown/states/internal/dropdownMaxWidthComponentState.ts new file mode 100644 index 0000000000..4b7345b849 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/layout/dropdown/states/internal/dropdownMaxWidthComponentState.ts @@ -0,0 +1,10 @@ +import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponeInstanceContext'; +import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; + +export const dropdownMaxWidthComponentState = createComponentStateV2< + number | undefined +>({ + key: 'dropdownMaxWidthComponentState', + componentInstanceContext: DropdownComponentInstanceContext, + defaultValue: undefined, +}); diff --git a/packages/twenty-front/src/modules/views/components/AdvancedFilterDropdownButton.tsx b/packages/twenty-front/src/modules/views/components/AdvancedFilterDropdownButton.tsx index 983f1d017c..da51bc55c7 100644 --- a/packages/twenty-front/src/modules/views/components/AdvancedFilterDropdownButton.tsx +++ b/packages/twenty-front/src/modules/views/components/AdvancedFilterDropdownButton.tsx @@ -24,7 +24,7 @@ export const AdvancedFilterDropdownButton = () => { dropdownHotkeyScope={{ scope: ADVANCED_FILTER_DROPDOWN_ID }} dropdownOffset={{ y: 8, x: 0 }} dropdownPlacement="bottom-start" - dropdownMenuWidth={650} + dropdownWidth="100%" /> ); }; diff --git a/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerDropdown.tsx b/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerDropdown.tsx index 4fa7f059c2..709ab982b0 100644 --- a/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerDropdown.tsx +++ b/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerDropdown.tsx @@ -74,7 +74,6 @@ export const ViewPickerDropdown = () => { dropdownHotkeyScope={{ scope: ViewsHotkeyScope.ListDropdown }} dropdownOffset={{ x: 0, y: 8 }} dropdownPlacement="bottom-start" - dropdownMenuWidth={200} onClickOutside={handleClickOutside} clickableComponent={ diff --git a/packages/twenty-front/src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerDatabaseEventForm.tsx b/packages/twenty-front/src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerDatabaseEventForm.tsx index 431fd9e854..46e31caf4f 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerDatabaseEventForm.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerDatabaseEventForm.tsx @@ -174,7 +174,7 @@ export const WorkflowEditTriggerDatabaseEventForm = ({ Record Type