From 58cb1f769aed1053899e98a7599b47cde580fe0e Mon Sep 17 00:00:00 2001 From: Nicolas Rouanne Date: Tue, 12 Nov 2024 18:22:50 +0100 Subject: [PATCH 1/7] fix(SingleEntitySelectMenuItems): extract `Add New` button from entitiesToSelect - and add it as a separate element - to be consistent with `MultiRecordSelect` --- .../SingleEntitySelectMenuItems.tsx | 59 +++---------------- .../SingleEntitySelectMenuItemsWithSearch.tsx | 28 ++++----- 2 files changed, 18 insertions(+), 69 deletions(-) diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItems.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItems.tsx index 8367ca8ce781..468b97324133 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItems.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItems.tsx @@ -1,15 +1,13 @@ import { isNonEmptyString } from '@sniptt/guards'; -import { Fragment, useRef } from 'react'; +import { useRef } from 'react'; import { useRecoilValue } from 'recoil'; import { Key } from 'ts-key-enum'; -import { IconComponent, IconPlus, MenuItem, MenuItemSelect } from 'twenty-ui'; +import { IconComponent, MenuItem, MenuItemSelect } from 'twenty-ui'; import { SelectableMenuItemSelect } from '@/object-record/relation-picker/components/SelectableMenuItemSelect'; import { SINGLE_ENTITY_SELECT_BASE_LIST } from '@/object-record/relation-picker/constants/SingleEntitySelectBaseList'; -import { CreateNewButton } from '@/ui/input/relation-picker/components/CreateNewButton'; import { DropdownMenuSkeletonItem } from '@/ui/input/relation-picker/components/skeletons/DropdownMenuSkeletonItem'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; -import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; @@ -26,7 +24,6 @@ export type SingleEntitySelectMenuItemsProps = { onCancel?: () => void; onEntitySelected: (entity?: EntityForSelect) => void; selectedEntity?: EntityForSelect; - onCreate?: () => void; showCreateButton?: boolean; SelectAllIcon?: IconComponent; selectAllLabel?: string; @@ -46,8 +43,6 @@ export const SingleEntitySelectMenuItems = ({ onCancel, onEntitySelected, selectedEntity, - onCreate, - showCreateButton, SelectAllIcon, selectAllLabel, isAllEntitySelected, @@ -59,14 +54,6 @@ export const SingleEntitySelectMenuItems = ({ }: SingleEntitySelectMenuItemsProps) => { const containerRef = useRef(null); - const createNewRecord = showCreateButton - ? { - __typename: '', - id: 'add-new', - name: 'Add New', - } - : null; - const selectNone = emptyLabel ? { __typename: '', @@ -88,7 +75,6 @@ export const SingleEntitySelectMenuItems = ({ selectNone, selectedEntity, ...entitiesToSelect, - createNewRecord, ].filter( (entity): entity is EntityForSelect => isDefined(entity) && isNonEmptyString(entity.name), @@ -98,10 +84,6 @@ export const SingleEntitySelectMenuItems = ({ SINGLE_ENTITY_SELECT_BASE_LIST, ); - const isSelectedAddNewButton = useRecoilValue( - isSelectedItemIdSelector('add-new'), - ); - const isSelectedSelectNoneButton = useRecoilValue( isSelectedItemIdSelector('select-none'), ); @@ -129,14 +111,10 @@ export const SingleEntitySelectMenuItems = ({ selectableItemIdArray={selectableItemIds} hotkeyScope={hotkeyScope} onEnter={(itemId) => { - if (itemId === 'add-new' && showCreateButton === true) { - onCreate?.(); - } else { - const entityIndex = entitiesInDropdown.findIndex( - (entity) => entity.id === itemId, - ); - onEntitySelected(entitiesInDropdown[entityIndex]); - } + const entityIndex = entitiesInDropdown.findIndex( + (entity) => entity.id === itemId, + ); + onEntitySelected(entitiesInDropdown[entityIndex]); resetSelectedItem(); }} > @@ -146,33 +124,10 @@ export const SingleEntitySelectMenuItems = ({ ) : entitiesInDropdown.length === 0 && !isAllEntitySelectShown && !loading ? ( - <> - - {entitiesToSelect.length > 0 && } - - + ) : ( entitiesInDropdown?.map((entity) => { switch (entity.id) { - case 'add-new': { - return ( - - {entitiesToSelect.length > 0 && } - - - ); - } case 'select-none': { return ( emptyLabel && ( diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch.tsx index 9b8e56262977..8deaf58911c6 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch.tsx @@ -4,9 +4,11 @@ import { } from '@/object-record/relation-picker/components/SingleEntitySelectMenuItems'; import { useEntitySelectSearch } from '@/object-record/relation-picker/hooks/useEntitySelectSearch'; import { useRelationPickerEntitiesOptions } from '@/object-record/relation-picker/hooks/useRelationPickerEntitiesOptions'; +import { CreateNewButton } from '@/ui/input/relation-picker/components/CreateNewButton'; import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { Placement } from '@floating-ui/react'; +import { IconPlus } from 'twenty-ui'; import { isDefined } from '~/utils/isDefined'; import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; @@ -49,21 +51,13 @@ export const SingleEntitySelectMenuItemsWithSearch = ({ excludedRelationRecordIds, }); - const showCreateButton = isDefined(onCreate); - - let onCreateWithInput = undefined; - - if (isDefined(onCreate)) { - onCreateWithInput = () => { - if (onCreate.length > 0) { - (onCreate as (searchInput?: string) => void)( - relationPickerSearchFilter, - ); - } else { - (onCreate as () => void)(); - } - }; - } + const createNewButton = isDefined(onCreate) && ( + onCreate?.(relationPickerSearchFilter)} + LeftIcon={IconPlus} + text="Add New" + /> + ); const results = ( ); @@ -104,6 +96,8 @@ export const SingleEntitySelectMenuItemsWithSearch = ({ {results} )} + {entities.entitiesToSelect.length > 0 && } + {createNewButton} ); }; From 9421b02b8b5724c63736b3e29270561e6d7e119f Mon Sep 17 00:00:00 2001 From: Nicolas Rouanne Date: Wed, 13 Nov 2024 12:01:29 +0100 Subject: [PATCH 2/7] fix(SingleEntitySelectMenuItems): wrap "Add New" in for padding --- .../SingleEntitySelectMenuItemsWithSearch.tsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch.tsx index 8deaf58911c6..f221a6e187b0 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch.tsx @@ -5,6 +5,7 @@ import { import { useEntitySelectSearch } from '@/object-record/relation-picker/hooks/useEntitySelectSearch'; import { useRelationPickerEntitiesOptions } from '@/object-record/relation-picker/hooks/useRelationPickerEntitiesOptions'; import { CreateNewButton } from '@/ui/input/relation-picker/components/CreateNewButton'; +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 { Placement } from '@floating-ui/react'; @@ -52,11 +53,13 @@ export const SingleEntitySelectMenuItemsWithSearch = ({ }); const createNewButton = isDefined(onCreate) && ( - onCreate?.(relationPickerSearchFilter)} - LeftIcon={IconPlus} - text="Add New" - /> + + onCreate?.(relationPickerSearchFilter)} + LeftIcon={IconPlus} + text="Add New" + /> + ); const results = ( From d22bbc3e54e86a325f350d81c47aac02be13fab5 Mon Sep 17 00:00:00 2001 From: Nicolas Rouanne Date: Wed, 13 Nov 2024 14:04:33 +0100 Subject: [PATCH 3/7] fix(SingleEntitySelectMenuItems): remove deleted prop from type --- .../relation-picker/components/SingleEntitySelectMenuItems.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItems.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItems.tsx index 468b97324133..89b22a54e601 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItems.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItems.tsx @@ -24,7 +24,6 @@ export type SingleEntitySelectMenuItemsProps = { onCancel?: () => void; onEntitySelected: (entity?: EntityForSelect) => void; selectedEntity?: EntityForSelect; - showCreateButton?: boolean; SelectAllIcon?: IconComponent; selectAllLabel?: string; isAllEntitySelected?: boolean; From e638a9aa78e4262670ea9ce4450a09221a9d1ec8 Mon Sep 17 00:00:00 2001 From: Nicolas Rouanne Date: Wed, 13 Nov 2024 15:07:15 +0100 Subject: [PATCH 4/7] fix(SingleEntitySelectMenuItems): correctly position "Add New" in case of an "*end" positioned `dropdownPlacement` - display is inverted when the `Placement` is at the `end`, following the `MultiRecordSelect` component design - add a story to display the `end` placement - pass down the `dropdownPlacement` prop in `SingleEntitySelect` --- .../components/SingleEntitySelect.tsx | 2 ++ .../SingleEntitySelectMenuItemsWithSearch.tsx | 22 +++++++++++-------- .../SingleEntitySelect.stories.tsx | 4 ++++ 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelect.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelect.tsx index b98c3f36b0f1..19142e4bbf01 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelect.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelect.tsx @@ -15,6 +15,7 @@ export type SingleEntitySelectProps = { export const SingleEntitySelect = ({ disableBackgroundBlur = false, + dropdownPlacement, EmptyIcon, emptyLabel, excludedRelationRecordIds, @@ -52,6 +53,7 @@ export const SingleEntitySelect = ({ > - onCreate?.(relationPickerSearchFilter)} - LeftIcon={IconPlus} - text="Add New" - /> - + onCreate?.(relationPickerSearchFilter)} + LeftIcon={IconPlus} + text="Add New" + /> ); const results = ( @@ -87,6 +85,10 @@ export const SingleEntitySelectMenuItemsWithSearch = ({ <> {dropdownPlacement?.includes('end') && ( <> + + {createNewButton} + + {entities.entitiesToSelect.length > 0 && } {results} @@ -97,10 +99,12 @@ export const SingleEntitySelectMenuItemsWithSearch = ({ <> {results} + {entities.entitiesToSelect.length > 0 && } + + {createNewButton} + )} - {entities.entitiesToSelect.length > 0 && } - {createNewButton} ); }; diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/__stories__/SingleEntitySelect.stories.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/__stories__/SingleEntitySelect.stories.tsx index 683c83459720..ddba928b6ac7 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/__stories__/SingleEntitySelect.stories.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/__stories__/SingleEntitySelect.stories.tsx @@ -80,3 +80,7 @@ export const WithSearchFilter: Story = { }); }, }; + +export const WithEndPlacement: Story = { + args: { dropdownPlacement: 'bottom-end' }, +}; From c255ae5b2fcd8f40b6eb305f1f9e318250c703f6 Mon Sep 17 00:00:00 2001 From: Nicolas Rouanne Date: Thu, 14 Nov 2024 11:42:43 +0100 Subject: [PATCH 5/7] fix(SingleEntitySelect): revert exposing `dropdownPlacement` prop in `SingleEntitySelect` - and passing it down to `SingleEntitySelectMenuItemsWithSearch` - since it broke the position of the dropdown, see https://github.com/twentyhq/twenty/pull/8474#issuecomment-2474405877 --- .../relation-picker/components/SingleEntitySelect.tsx | 2 -- .../components/__stories__/SingleEntitySelect.stories.tsx | 4 ---- 2 files changed, 6 deletions(-) diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelect.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelect.tsx index 19142e4bbf01..b98c3f36b0f1 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelect.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelect.tsx @@ -15,7 +15,6 @@ export type SingleEntitySelectProps = { export const SingleEntitySelect = ({ disableBackgroundBlur = false, - dropdownPlacement, EmptyIcon, emptyLabel, excludedRelationRecordIds, @@ -53,7 +52,6 @@ export const SingleEntitySelect = ({ > Date: Fri, 15 Nov 2024 17:51:55 +0100 Subject: [PATCH 6/7] Don't shop separator if no create --- .../SingleEntitySelectMenuItemsWithSearch.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch.tsx index 6515de4ea2b3..e3425b9d64c1 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch.tsx @@ -99,10 +99,14 @@ export const SingleEntitySelectMenuItemsWithSearch = ({ <> {results} - {entities.entitiesToSelect.length > 0 && } - - {createNewButton} - + {entities.entitiesToSelect.length > 0 && isDefined(onCreate) && ( + + )} + {isDefined(onCreate) && ( + + {createNewButton} + + )} )} From b7ece62f1fa2cc163f457b011ba292a92482b74f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Malfait?= Date: Fri, 15 Nov 2024 21:28:23 +0100 Subject: [PATCH 7/7] Small ui adjustments --- .../components/MultiRecordSelect.tsx | 16 ++++++++-------- .../SingleEntitySelectMenuItemsWithSearch.tsx | 4 ++-- .../ui/layout/dropdown/components/Dropdown.tsx | 17 +++++++++++++---- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/MultiRecordSelect.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/MultiRecordSelect.tsx index b1c10cbbb6f1..e8067019754a 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/MultiRecordSelect.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/MultiRecordSelect.tsx @@ -140,11 +140,13 @@ export const MultiRecordSelect = ({ {dropdownPlacement?.includes('end') && ( <> - - {createNewButton} - + {isDefined(onCreate) && ( + + {createNewButton} + + )} - {results} + {objectRecordsIdsMultiSelect?.length > 0 && results} {recordMultiSelectIsLoading && !relationPickerSearchFilter && ( <> @@ -171,13 +173,11 @@ export const MultiRecordSelect = ({ )} - {results} + {objectRecordsIdsMultiSelect?.length > 0 && results} {objectRecordsIdsMultiSelect?.length > 0 && ( )} - - {createNewButton} - + {isDefined(onCreate) &&
{createNewButton}
} )}
diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch.tsx index e3425b9d64c1..6654da7ec86e 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch.tsx @@ -89,7 +89,7 @@ export const SingleEntitySelectMenuItemsWithSearch = ({ {createNewButton} {entities.entitiesToSelect.length > 0 && } - {results} + {entities.entitiesToSelect.length > 0 && results} )} @@ -98,7 +98,7 @@ export const SingleEntitySelectMenuItemsWithSearch = ({ isUndefinedOrNull(dropdownPlacement)) && ( <> - {results} + {entities.entitiesToSelect.length > 0 && results} {entities.entitiesToSelect.length > 0 && isDefined(onCreate) && ( )} 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 2ec7a8471f96..92b334321cd5 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 @@ -7,7 +7,7 @@ import { size, useFloating, } from '@floating-ui/react'; -import { MouseEvent, ReactNode, useRef } from 'react'; +import { MouseEvent, ReactNode, useEffect, useRef } from 'react'; import { Keys } from 'react-hotkeys-hook'; import { Key } from 'ts-key-enum'; @@ -65,8 +65,13 @@ export const Dropdown = ({ }: DropdownProps) => { const containerRef = useRef(null); - const { isDropdownOpen, toggleDropdown, closeDropdown, dropdownWidth } = - useDropdown(dropdownId); + const { + isDropdownOpen, + toggleDropdown, + closeDropdown, + dropdownWidth, + setDropdownPlacement, + } = useDropdown(dropdownId); const offsetMiddlewares = []; @@ -78,7 +83,7 @@ export const Dropdown = ({ offsetMiddlewares.push(offset({ mainAxis: dropdownOffset.y })); } - const { refs, floatingStyles } = useFloating({ + const { refs, floatingStyles, placement } = useFloating({ placement: dropdownPlacement, middleware: [ flip(), @@ -100,6 +105,10 @@ export const Dropdown = ({ strategy: dropdownStrategy, }); + useEffect(() => { + setDropdownPlacement(placement); + }, [placement, setDropdownPlacement]); + const handleHotkeyTriggered = () => { toggleDropdown(); };