From 8b49c803ecdc91dbc1d7c10495cfb34e06b69ee4 Mon Sep 17 00:00:00 2001 From: Lucas Bordeau Date: Thu, 13 Mar 2025 15:20:23 +0100 Subject: [PATCH] Fixed minor bugs on advanced filters (#10847) This PR fixes minor bugs on advanced filters : - We couldn't close the advanced filter dropdown after removing a rule, because the rule options dropdown wasn't closed, so focus dropdown id was in a corrupted state. - The text filter input in filter dropdown and the search input were the same component, which was causing conflicts with state management but this conflict didn't happen with the simple filter dropdown implementation, the advanced filter dropdown brought this bug to light. - The chevron down icon disappeared from the filter update button group, this PR fixes it. Fixes https://github.com/twentyhq/core-team-issues/issues/557 Fixes https://github.com/twentyhq/core-team-issues/issues/558 --- ...FilterRecordFilterGroupOptionsDropdown.tsx | 5 ++ ...ancedFilterRecordFilterOptionsDropdown.tsx | 7 +- .../ObjectFilterDropdownFilterInput.tsx | 6 +- .../ObjectFilterDropdownFilterSelect.tsx | 10 +-- .../ObjectFilterDropdownTextInput.tsx | 74 +++++++++++++++++++ .../components/UpdateViewButtonGroup.tsx | 7 +- 6 files changed, 93 insertions(+), 16 deletions(-) create mode 100644 packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownTextInput.tsx diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRecordFilterGroupOptionsDropdown.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRecordFilterGroupOptionsDropdown.tsx index 18f924161..337f4d3ca 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRecordFilterGroupOptionsDropdown.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRecordFilterGroupOptionsDropdown.tsx @@ -4,6 +4,7 @@ import { useRemoveRecordFilter } from '@/object-record/record-filter/hooks/useRe 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 { IconButton, IconDotsVertical, MenuItem } from 'twenty-ui'; type AdvancedFilterRecordFilterGroupOptionsDropdownProps = { @@ -15,6 +16,8 @@ export const AdvancedFilterRecordFilterGroupOptionsDropdown = ({ }: AdvancedFilterRecordFilterGroupOptionsDropdownProps) => { const dropdownId = `advanced-filter-record-filter-group-options-${recordFilterGroupId}`; + const { closeDropdown } = useDropdown(dropdownId); + const { removeRecordFilter } = useRemoveRecordFilter(); const { removeRecordFilterGroup } = useRemoveRecordFilterGroup(); @@ -28,6 +31,8 @@ export const AdvancedFilterRecordFilterGroupOptionsDropdown = ({ } removeRecordFilterGroup(recordFilterGroupId); + + closeDropdown(); }; return ( diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRecordFilterOptionsDropdown.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRecordFilterOptionsDropdown.tsx index 8b4c2ff2b..bbec255b8 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRecordFilterOptionsDropdown.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRecordFilterOptionsDropdown.tsx @@ -6,6 +6,7 @@ import { currentRecordFiltersComponentState } from '@/object-record/record-filte 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 { isDefined } from 'twenty-shared'; import { IconButton, IconDotsVertical, MenuItem } from 'twenty-ui'; @@ -19,6 +20,8 @@ export const AdvancedFilterRecordFilterOptionsDropdown = ({ }: AdvancedFilterRecordFilterOptionsDropdownProps) => { const dropdownId = `advanced-filter-record-filter-options-${recordFilterId}`; + const { closeDropdown } = useDropdown(dropdownId); + const { removeRecordFilter } = useRemoveRecordFilter(); const { removeRecordFilterGroup } = useRemoveRecordFilterGroup(); @@ -36,7 +39,7 @@ export const AdvancedFilterRecordFilterOptionsDropdown = ({ }); const handleRemove = async () => { - removeRecordFilter({ recordFilterId: recordFilterId }); + closeDropdown(); if (isDefined(currentRecordFilter?.recordFilterGroupId)) { const isOnlyViewFilterInGroup = @@ -46,6 +49,8 @@ export const AdvancedFilterRecordFilterOptionsDropdown = ({ removeRecordFilterGroup(currentRecordFilter.recordFilterGroupId); } } + + removeRecordFilter({ recordFilterId: recordFilterId }); }; return ( diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterInput.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterInput.tsx index 3121c2dd6..c2ce2765f 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterInput.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterInput.tsx @@ -5,13 +5,13 @@ import { ObjectFilterDropdownRatingInput } from '@/object-record/object-filter-d import { ObjectFilterDropdownRecordSelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordSelect'; import { ObjectFilterDropdownSearchInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownSearchInput'; import { ObjectFilterDropdownSourceSelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownSourceSelect'; -import { ObjectFilterDropdownTextSearchInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownTextSearchInput'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; import { isDefined } from 'twenty-shared'; import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; import { ObjectFilterDropdownBooleanSelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownBooleanSelect'; +import { ObjectFilterDropdownTextInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownTextInput'; import { DATE_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/DateFilterTypes'; import { NUMBER_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/NumberFilterTypes'; import { TEXT_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/TextFilterTypes'; @@ -75,9 +75,7 @@ export const ObjectFilterDropdownFilterInput = ({ {isConfigurable && selectedOperandInDropdown && ( <> {TEXT_FILTER_TYPES.includes(filterType) && - !isActorSourceCompositeFilter && ( - - )} + !isActorSourceCompositeFilter && } {NUMBER_FILTER_TYPES.includes(filterType) && ( )} diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect.tsx index 9d575494e..8e2b4ef69 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect.tsx @@ -27,6 +27,7 @@ import { advancedFilterViewFilterIdComponentState } from '@/object-record/object import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState'; import { FiltersHotkeyScope } from '@/object-record/object-filter-dropdown/types/FiltersHotkeyScope'; import { useFilterableFieldMetadataItemsInRecordIndexContext } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItemsInRecordIndexContext'; +import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly'; import { useLingui } from '@lingui/react/macro'; @@ -65,17 +66,12 @@ export const ObjectFilterDropdownFilterSelect = ({ }: ObjectFilterDropdownFilterSelectProps) => { const { recordIndexId } = useRecordIndexContextOrThrow(); - const setObjectFilterDropdownSearchInput = useSetRecoilComponentStateV2( - objectFilterDropdownSearchInputComponentState, - ); - const advancedFilterViewFilterId = useRecoilComponentValueV2( advancedFilterViewFilterIdComponentState, ); - const objectFilterDropdownSearchInput = useRecoilComponentValueV2( - objectFilterDropdownSearchInputComponentState, - ); + const [objectFilterDropdownSearchInput, setObjectFilterDropdownSearchInput] = + useRecoilComponentStateV2(objectFilterDropdownSearchInputComponentState); const { closeAdvancedFilterDropdown } = useAdvancedFilterDropdown( advancedFilterViewFilterId, diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownTextInput.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownTextInput.tsx new file mode 100644 index 000000000..e22856d88 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownTextInput.tsx @@ -0,0 +1,74 @@ +import { ChangeEvent, useCallback, useState } from 'react'; +import { v4 } from 'uuid'; + +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 { DropdownMenuInput } from '@/ui/layout/dropdown/components/DropdownMenuInput'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; + +export const ObjectFilterDropdownTextInput = () => { + const selectedOperandInDropdown = useRecoilComponentValueV2( + selectedOperandInDropdownComponentState, + ); + + const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2( + fieldMetadataItemUsedInDropdownComponentSelector, + ); + + const selectedFilter = useRecoilComponentValueV2( + selectedFilterComponentState, + ); + + const { applyRecordFilter } = useApplyRecordFilter(); + + const [hasFocused, setHasFocused] = useState(false); + + const [inputValue, setInputValue] = useState( + () => selectedFilter?.value || '', + ); + + const handleInputRef = useCallback( + (node: HTMLInputElement | null) => { + if (Boolean(node) && !hasFocused) { + node?.focus(); + node?.select(); + setHasFocused(true); + } + }, + [hasFocused], + ); + + return ( + fieldMetadataItemUsedInDropdown && + selectedOperandInDropdown && ( + ) => { + const newValue = event.target.value; + + setInputValue(newValue); + + applyRecordFilter({ + id: selectedFilter?.id ? selectedFilter.id : v4(), + fieldMetadataId: fieldMetadataItemUsedInDropdown?.id ?? '', + value: newValue, + operand: selectedOperandInDropdown, + displayValue: newValue, + type: getFilterTypeFromFieldType( + fieldMetadataItemUsedInDropdown.type, + ), + label: fieldMetadataItemUsedInDropdown.label, + recordFilterGroupId: selectedFilter?.recordFilterGroupId, + }); + }} + /> + ) + ); +}; diff --git a/packages/twenty-front/src/modules/views/components/UpdateViewButtonGroup.tsx b/packages/twenty-front/src/modules/views/components/UpdateViewButtonGroup.tsx index ac539dd96..6df4d6c35 100644 --- a/packages/twenty-front/src/modules/views/components/UpdateViewButtonGroup.tsx +++ b/packages/twenty-front/src/modules/views/components/UpdateViewButtonGroup.tsx @@ -2,6 +2,7 @@ import styled from '@emotion/styled'; import { Button, ButtonGroup, + IconButton, IconChevronDown, IconPlus, MenuItem, @@ -32,9 +33,7 @@ const StyledContainer = styled.div` margin-right: ${({ theme }) => theme.spacing(2)}; position: relative; `; -const StyledButton = styled(Button)` - padding: ${({ theme }) => theme.spacing(1)}; -`; + export type UpdateViewButtonGroupProps = { hotkeyScope: HotkeyScope; }; @@ -116,7 +115,7 @@ export const UpdateViewButtonGroup = ({ dropdownId={UPDATE_VIEW_BUTTON_DROPDOWN_ID} dropdownHotkeyScope={hotkeyScope} clickableComponent={ -