Fixed hotkeys on advanced filters (#10733)

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>
This commit is contained in:
Lucas Bordeau
2025-03-10 17:41:53 +01:00
committed by GitHub
parent f79165ff28
commit d8197920dc
7 changed files with 27 additions and 13 deletions

View File

@ -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 = ({
)}
</DropdownMenuItemsContainer>
}
dropdownHotkeyScope={{ scope: ADVANCED_FILTER_DROPDOWN_ID }}
dropdownHotkeyScope={{ scope: dropdownId }}
dropdownOffset={{ y: 8, x: 0 }}
dropdownPlacement="bottom-start"
/>

View File

@ -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 = ({
<ObjectFilterDropdownFilterSelect />
)
}
dropdownHotkeyScope={{ scope: ADVANCED_FILTER_DROPDOWN_ID }}
dropdownHotkeyScope={{ scope: advancedFilterDropdownId }}
dropdownOffset={{ y: 8, x: 0 }}
dropdownPlacement="bottom-start"
/>

View File

@ -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 = ({
))}
</DropdownMenuItemsContainer>
}
dropdownHotkeyScope={{ scope: ADVANCED_FILTER_DROPDOWN_ID }}
dropdownHotkeyScope={{ scope: dropdownId }}
dropdownOffset={{ y: 8, x: 0 }}
dropdownPlacement="bottom-start"
/>

View File

@ -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 = ({
<ObjectFilterDropdownFilterInput />
</DropdownMenuItemsContainer>
}
dropdownHotkeyScope={{ scope: ADVANCED_FILTER_DROPDOWN_ID }}
dropdownHotkeyScope={{ scope: dropdownId }}
dropdownOffset={{ y: 8, x: 0 }}
dropdownPlacement="bottom-start"
dropdownMenuWidth={280}

View File

@ -134,7 +134,9 @@ export const AdvancedFilterButton = () => {
}
closeObjectFilterDropdown();
openAdvancedFilterDropdown();
openAdvancedFilterDropdown({
scope: ADVANCED_FILTER_DROPDOWN_ID,
});
};
return (

View File

@ -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,
);
}
}

View File

@ -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 = <T extends Element>({
!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 = <T extends Element>({
clickedOnAtLeastOneRef,
isMouseDownInside,
isClickedOnExcluded,
activeDropdownFocusId,
previousDropdownFocusId,
hotkeyScope,
enabled,
event,