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
This commit is contained in:
Lucas Bordeau
2025-03-13 15:20:23 +01:00
committed by GitHub
parent a2004e6220
commit 8b49c803ec
6 changed files with 93 additions and 16 deletions

View File

@ -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 (

View File

@ -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 (

View File

@ -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 && (
<ObjectFilterDropdownTextSearchInput />
)}
!isActorSourceCompositeFilter && <ObjectFilterDropdownTextInput />}
{NUMBER_FILTER_TYPES.includes(filterType) && (
<ObjectFilterDropdownNumberInput />
)}

View File

@ -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,

View File

@ -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 && (
<DropdownMenuInput
ref={handleInputRef}
value={inputValue}
autoFocus
type="text"
placeholder={fieldMetadataItemUsedInDropdown.label}
onChange={(event: ChangeEvent<HTMLInputElement>) => {
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,
});
}}
/>
)
);
};

View File

@ -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={
<StyledButton
<IconButton
size="small"
accent="blue"
Icon={IconChevronDown}