From d8197920dc508479869bcebb1284747be277a381 Mon Sep 17 00:00:00 2001 From: Lucas Bordeau Date: Mon, 10 Mar 2025 17:41:53 +0100 Subject: [PATCH] Fixed hotkeys on advanced filters (#10733) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR fixes hotkey escape on advanced filter dropdown, which wasn't working. It adds a parameters to openDropdown, because in this particular case, the dropdown is not opened from its clickable component but from an openDropdown, in that case it wasn't possible for openDropdown to know which hotkey scope to take, because the hotkey scope is generally passed in the Dropdown component props. We might want to find a more robust solution, where a dropdown knows its hotkey scope without having to be mounted. --------- Co-authored-by: Raphaƫl Bosi <71827178+bosiraphael@users.noreply.github.com> --- .../components/AdvancedFilterAddFilterRuleSelect.tsx | 3 +-- .../AdvancedFilterViewFilterFieldSelect.tsx | 3 +-- .../AdvancedFilterViewFilterOperandSelect.tsx | 3 +-- .../AdvancedFilterViewFilterValueInput.tsx | 3 +-- .../components/AdvancedFilterButton.tsx | 4 +++- .../modules/ui/layout/dropdown/hooks/useDropdown.ts | 12 ++++++++---- .../pointer-event/hooks/useListenClickOutside.ts | 12 ++++++++++++ 7 files changed, 27 insertions(+), 13 deletions(-) diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterAddFilterRuleSelect.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterAddFilterRuleSelect.tsx index 6f8e6f77a..4869b40fa 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterAddFilterRuleSelect.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterAddFilterRuleSelect.tsx @@ -9,7 +9,6 @@ import { getRecordFilterOperands } from '@/object-record/record-filter/utils/get import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; -import { ADVANCED_FILTER_DROPDOWN_ID } from '@/views/constants/AdvancedFilterDropdownId'; import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly'; import { isDefined } from 'twenty-shared'; import { IconLibraryPlus, IconPlus, LightButton, MenuItem } from 'twenty-ui'; @@ -144,7 +143,7 @@ export const AdvancedFilterAddFilterRuleSelect = ({ )} } - dropdownHotkeyScope={{ scope: ADVANCED_FILTER_DROPDOWN_ID }} + dropdownHotkeyScope={{ scope: dropdownId }} dropdownOffset={{ y: 8, x: 0 }} dropdownPlacement="bottom-start" /> diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterViewFilterFieldSelect.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterViewFilterFieldSelect.tsx index 9772e4396..1a4348304 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterViewFilterFieldSelect.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterViewFilterFieldSelect.tsx @@ -11,7 +11,6 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; -import { ADVANCED_FILTER_DROPDOWN_ID } from '@/views/constants/AdvancedFilterDropdownId'; import styled from '@emotion/styled'; const StyledContainer = styled.div` @@ -76,7 +75,7 @@ export const AdvancedFilterViewFilterFieldSelect = ({ ) } - dropdownHotkeyScope={{ scope: ADVANCED_FILTER_DROPDOWN_ID }} + dropdownHotkeyScope={{ scope: advancedFilterDropdownId }} dropdownOffset={{ y: 8, x: 0 }} dropdownPlacement="bottom-start" /> diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterViewFilterOperandSelect.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterViewFilterOperandSelect.tsx index 4c1d7ea40..fbe53fed9 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterViewFilterOperandSelect.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterViewFilterOperandSelect.tsx @@ -11,7 +11,6 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; -import { ADVANCED_FILTER_DROPDOWN_ID } from '@/views/constants/AdvancedFilterDropdownId'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; import styled from '@emotion/styled'; import { isDefined } from 'twenty-shared'; @@ -125,7 +124,7 @@ export const AdvancedFilterViewFilterOperandSelect = ({ ))} } - dropdownHotkeyScope={{ scope: ADVANCED_FILTER_DROPDOWN_ID }} + dropdownHotkeyScope={{ scope: dropdownId }} dropdownOffset={{ y: 8, x: 0 }} dropdownPlacement="bottom-start" /> diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterViewFilterValueInput.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterViewFilterValueInput.tsx index 3194bf835..b6c76448c 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterViewFilterValueInput.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterViewFilterValueInput.tsx @@ -8,7 +8,6 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; -import { ADVANCED_FILTER_DROPDOWN_ID } from '@/views/constants/AdvancedFilterDropdownId'; type AdvancedFilterViewFilterValueInputProps = { viewFilterId: string; @@ -74,7 +73,7 @@ export const AdvancedFilterViewFilterValueInput = ({ } - dropdownHotkeyScope={{ scope: ADVANCED_FILTER_DROPDOWN_ID }} + dropdownHotkeyScope={{ scope: dropdownId }} dropdownOffset={{ y: 8, x: 0 }} dropdownPlacement="bottom-start" dropdownMenuWidth={280} diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/AdvancedFilterButton.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/AdvancedFilterButton.tsx index 99dcefc23..f6e1c5af5 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/AdvancedFilterButton.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/AdvancedFilterButton.tsx @@ -134,7 +134,9 @@ export const AdvancedFilterButton = () => { } closeObjectFilterDropdown(); - openAdvancedFilterDropdown(); + openAdvancedFilterDropdown({ + scope: ADVANCED_FILTER_DROPDOWN_ID, + }); }; return ( 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 b2bd155c8..3fb8e5352 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 @@ -5,6 +5,7 @@ import { useGoBackToPreviousDropdownFocusId } from '@/ui/layout/dropdown/hooks/u import { useSetActiveDropdownFocusIdAndMemorizePrevious } from '@/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious'; import { dropdownHotkeyComponentState } from '@/ui/layout/dropdown/states/dropdownHotkeyComponentState'; import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope'; +import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; import { getScopeIdOrUndefinedFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdOrUndefinedFromComponentId'; import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; import { useCallback } from 'react'; @@ -55,7 +56,7 @@ export const useDropdown = (dropdownId?: string) => { const openDropdown = useRecoilCallback( ({ snapshot }) => - () => { + (dropdownHotkeyScopeFromProps?: HotkeyScope) => { if (!isDropdownOpen) { setIsDropdownOpen(true); setActiveDropdownFocusIdAndMemorizePrevious(dropdownId ?? scopeId); @@ -67,10 +68,13 @@ export const useDropdown = (dropdownId?: string) => { }), ); - if (isDefined(dropdownHotkeyScope)) { + const dropdownHotkeyScopeForOpening = + dropdownHotkeyScopeFromProps ?? dropdownHotkeyScope; + + if (isDefined(dropdownHotkeyScopeForOpening)) { setHotkeyScopeAndMemorizePreviousScope( - dropdownHotkeyScope.scope, - dropdownHotkeyScope.customScopes, + dropdownHotkeyScopeForOpening.scope, + dropdownHotkeyScopeForOpening.customScopes, ); } } diff --git a/packages/twenty-front/src/modules/ui/utilities/pointer-event/hooks/useListenClickOutside.ts b/packages/twenty-front/src/modules/ui/utilities/pointer-event/hooks/useListenClickOutside.ts index 587ec6d70..703eb747c 100644 --- a/packages/twenty-front/src/modules/ui/utilities/pointer-event/hooks/useListenClickOutside.ts +++ b/packages/twenty-front/src/modules/ui/utilities/pointer-event/hooks/useListenClickOutside.ts @@ -1,6 +1,8 @@ import React, { useEffect } from 'react'; import { useRecoilCallback } from 'recoil'; +import { activeDropdownFocusIdState } from '@/ui/layout/dropdown/states/activeDropdownFocusIdState'; +import { previousDropdownFocusIdState } from '@/ui/layout/dropdown/states/previousDropdownFocusIdState'; import { internalHotkeysEnabledScopesState } from '@/ui/utilities/hotkey/states/internal/internalHotkeysEnabledScopesState'; import { useClickOustideListenerStates } from '@/ui/utilities/pointer-event/hooks/useClickOustideListenerStates'; @@ -189,6 +191,14 @@ export const useListenClickOutside = ({ !isClickedOnExcluded; if (CLICK_OUTSIDE_DEBUG_MODE) { + const activeDropdownFocusId = snapshot + .getLoadable(activeDropdownFocusIdState) + .getValue(); + + const previousDropdownFocusId = snapshot + .getLoadable(previousDropdownFocusIdState) + .getValue(); + // eslint-disable-next-line no-console console.log('click outside compare refs', { listenerId, @@ -198,6 +208,8 @@ export const useListenClickOutside = ({ clickedOnAtLeastOneRef, isMouseDownInside, isClickedOnExcluded, + activeDropdownFocusId, + previousDropdownFocusId, hotkeyScope, enabled, event,