diff --git a/packages/twenty-front/src/modules/spreadsheet-import/components/MatchColumnToFieldSelect.tsx b/packages/twenty-front/src/modules/spreadsheet-import/components/MatchColumnToFieldSelect.tsx index 9a80e9eba..b84a09da5 100644 --- a/packages/twenty-front/src/modules/spreadsheet-import/components/MatchColumnToFieldSelect.tsx +++ b/packages/twenty-front/src/modules/spreadsheet-import/components/MatchColumnToFieldSelect.tsx @@ -137,6 +137,7 @@ export const MatchColumnToFieldSelect = ({ ) } onClickOutside={handleClickOutside} + isDropdownInModal /> ); }; diff --git a/packages/twenty-front/src/modules/spreadsheet-import/steps/components/MatchColumnsStep/components/SubMatchingSelectRowRightDropdown.tsx b/packages/twenty-front/src/modules/spreadsheet-import/steps/components/MatchColumnsStep/components/SubMatchingSelectRowRightDropdown.tsx index 627c97078..c28195cf1 100644 --- a/packages/twenty-front/src/modules/spreadsheet-import/steps/components/MatchColumnsStep/components/SubMatchingSelectRowRightDropdown.tsx +++ b/packages/twenty-front/src/modules/spreadsheet-import/steps/components/MatchColumnsStep/components/SubMatchingSelectRowRightDropdown.tsx @@ -69,6 +69,7 @@ export const SubMatchingSelectRowRightDropdown = ({ onOptionSelected={handleSelect} /> } + isDropdownInModal /> ); diff --git a/packages/twenty-front/src/modules/ui/layout/constants/RootStackingContextZIndices.ts b/packages/twenty-front/src/modules/ui/layout/constants/RootStackingContextZIndices.ts index 508636c5e..5a71a8720 100644 --- a/packages/twenty-front/src/modules/ui/layout/constants/RootStackingContextZIndices.ts +++ b/packages/twenty-front/src/modules/ui/layout/constants/RootStackingContextZIndices.ts @@ -15,9 +15,10 @@ export enum RootStackingContextZIndices { CommandMenu = 21, CommandMenuButton = 22, + DropdownPortalBelowModal = 38, RootModalBackDrop = 39, RootModal = 40, - DropdownPortal = 50, + DropdownPortalAboveModal = 50, Dialog = 9999, SnackBar = 10002, NotFound = 10001, 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 cb2ceff27..82579d934 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 @@ -58,6 +58,7 @@ export type DropdownProps = { onClose?: () => void; onOpen?: () => void; excludedClickOutsideIds?: string[]; + isDropdownInModal?: boolean; }; export const Dropdown = ({ @@ -74,6 +75,7 @@ export const Dropdown = ({ onOpen, clickableComponentWidth = 'auto', excludedClickOutsideIds, + isDropdownInModal = false, }: DropdownProps) => { const { isDropdownOpen, toggleDropdown } = useDropdown(dropdownId); @@ -193,6 +195,7 @@ export const Dropdown = ({ onClickOutside={onClickOutside} onHotkeyTriggered={toggleDropdown} excludedClickOutsideIds={excludedClickOutsideIds} + isDropdownInModal={isDropdownInModal} /> )} { + return ( + <> + + Modal with Dropdown Test + +

+ This modal contains a dropdown that should appear above the modal + (higher z-index). +

+
+ + } + dropdownHotkeyScope={{ scope: 'modal-dropdown' }} + dropdownOffset={{ x: 0, y: 8 }} + dropdownId="modal-dropdown-test" + isDropdownInModal={true} + dropdownComponents={ + +
+ +
+
+ } + /> +
+
+
+ + ); +}; + +const initializeModalState = ({ set }: { set: SetRecoilState }) => { + set( + isModalOpenedComponentState.atomFamily({ + instanceId: modalId, + }), + true, + ); + + set(currentHotkeyScopeState, { + scope: ModalHotkeyScope.ModalFocus, + customScopes: { + commandMenu: true, + goto: false, + keyboardShortcutMenu: false, + }, + }); + + set(internalHotkeysEnabledScopesState, [ModalHotkeyScope.ModalFocus]); + + set(focusStackState, [ + { + focusId: modalId, + componentInstance: { + componentType: FocusComponentType.MODAL, + componentInstanceId: modalId, + }, + globalHotkeysConfig: { + enableGlobalHotkeysWithModifiers: true, + enableGlobalHotkeysConflictingWithKeyboard: true, + }, + }, + ]); +}; + +export const DropdownInsideModal: Story = { + decorators: [I18nFrontDecorator, RootDecorator, ComponentDecorator], + parameters: { + initializeState: initializeModalState, + disableHotkeyInitialization: true, + }, + render: () => , + play: async () => { + const canvas = within(document.body); + + const dropdownButton = await canvas.findByTestId('dropdown-button'); + + await userEvent.click(dropdownButton); + + const dropdownContent = await canvas.findByTestId('dropdown-content'); + + expect(dropdownContent).toBeVisible(); + }, +}; diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/components/internal/DropdownInternalContainer.tsx b/packages/twenty-front/src/modules/ui/layout/dropdown/components/internal/DropdownInternalContainer.tsx index 6be81d48c..395ac0e0e 100644 --- a/packages/twenty-front/src/modules/ui/layout/dropdown/components/internal/DropdownInternalContainer.tsx +++ b/packages/twenty-front/src/modules/ui/layout/dropdown/components/internal/DropdownInternalContainer.tsx @@ -22,9 +22,14 @@ import { Keys } from 'react-hotkeys-hook'; import { useRecoilValue } from 'recoil'; import { Key } from 'ts-key-enum'; -export const StyledDropdownContentContainer = styled.div` +export const StyledDropdownContentContainer = styled.div<{ + isDropdownInModal?: boolean; +}>` display: flex; - z-index: ${RootStackingContextZIndices.DropdownPortal}; + z-index: ${({ isDropdownInModal }) => + isDropdownInModal + ? RootStackingContextZIndices.DropdownPortalAboveModal + : RootStackingContextZIndices.DropdownPortalBelowModal}; `; const StyledDropdownInsideContainer = styled.div` @@ -50,6 +55,7 @@ export type DropdownInternalContainerProps = { dropdownComponents: React.ReactNode; parentDropdownId?: string; excludedClickOutsideIds?: string[]; + isDropdownInModal?: boolean; }; export const DropdownInternalContainer = ({ @@ -63,6 +69,7 @@ export const DropdownInternalContainer = ({ onHotkeyTriggered, dropdownComponents, excludedClickOutsideIds, + isDropdownInModal = false, }: DropdownInternalContainerProps) => { const { isDropdownOpen, closeDropdown, setDropdownPlacement } = useDropdown(dropdownId); @@ -140,6 +147,7 @@ export const DropdownInternalContainer = ({ role="listbox" id={`${dropdownId}-options`} data-click-outside-id={excludedClickOutsideId} + isDropdownInModal={isDropdownInModal} > diff --git a/packages/twenty-front/src/modules/ui/layout/modal/components/__stories__/ConfirmationModal.stories.tsx b/packages/twenty-front/src/modules/ui/layout/modal/components/__stories__/ConfirmationModal.stories.tsx index 7b40c7672..3c9b9a09e 100644 --- a/packages/twenty-front/src/modules/ui/layout/modal/components/__stories__/ConfirmationModal.stories.tsx +++ b/packages/twenty-front/src/modules/ui/layout/modal/components/__stories__/ConfirmationModal.stories.tsx @@ -6,6 +6,7 @@ import { focusStackState } from '@/ui/utilities/focus/states/focusStackState'; import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType'; import { currentHotkeyScopeState } from '@/ui/utilities/hotkey/states/internal/currentHotkeyScopeState'; import { internalHotkeysEnabledScopesState } from '@/ui/utilities/hotkey/states/internal/internalHotkeysEnabledScopesState'; +import { SetRecoilState } from 'recoil'; import { ComponentDecorator } from 'twenty-ui/testing'; import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator'; import { RootDecorator } from '~/testing/decorators/RootDecorator'; @@ -13,7 +14,7 @@ import { sleep } from '~/utils/sleep'; import { isModalOpenedComponentState } from '../../states/isModalOpenedComponentState'; import { ConfirmationModal } from '../ConfirmationModal'; -const initializeState = ({ set }: { set: (atom: any, value: any) => void }) => { +const initializeState = ({ set }: { set: SetRecoilState }) => { set( isModalOpenedComponentState.atomFamily({ instanceId: 'confirmation-modal', diff --git a/packages/twenty-front/src/modules/ui/layout/modal/components/__stories__/Modal.stories.tsx b/packages/twenty-front/src/modules/ui/layout/modal/components/__stories__/Modal.stories.tsx index 3d046d75c..c4b9c9c25 100644 --- a/packages/twenty-front/src/modules/ui/layout/modal/components/__stories__/Modal.stories.tsx +++ b/packages/twenty-front/src/modules/ui/layout/modal/components/__stories__/Modal.stories.tsx @@ -6,6 +6,7 @@ import { focusStackState } from '@/ui/utilities/focus/states/focusStackState'; import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType'; import { currentHotkeyScopeState } from '@/ui/utilities/hotkey/states/internal/currentHotkeyScopeState'; import { internalHotkeysEnabledScopesState } from '@/ui/utilities/hotkey/states/internal/internalHotkeysEnabledScopesState'; +import { SetRecoilState } from 'recoil'; import { ComponentDecorator } from 'twenty-ui/testing'; import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator'; import { RootDecorator } from '~/testing/decorators/RootDecorator'; @@ -13,7 +14,7 @@ import { sleep } from '~/utils/sleep'; import { isModalOpenedComponentState } from '../../states/isModalOpenedComponentState'; import { Modal } from '../Modal'; -const initializeState = ({ set }: { set: (atom: any, value: any) => void }) => { +const initializeState = ({ set }: { set: SetRecoilState }) => { set( isModalOpenedComponentState.atomFamily({ instanceId: 'modal-id',