Implement sub-field filtering on CURRENCY field type (#11726)

This PR implements sub-field filtering on CURRENCY field type and
improves many related zones.

- Created a ObjectFilterDropdownCurrencySelect dropdown component for
filtering on multiple currencies
- Added currencyCode sub-field to CurrencyFilter type
- Created getDefaultSubFieldNameForCompositeFilterableFieldType to avoid
situation where we don't have any sub field name in sub field filtering
situations.
- Implemented filtering for currencyCode in
computeFilterRecordGqlOperationFilter
- Implemented CURRENCY type in getRecordFilterOperands
- Implemented isMatchingCurrencyFilter for using in
isRecordMatchingFilter for proper optimistic rendering
- Created turnCurrencyIntoSelectableItem to help
ObjectFilterDropdownCurrencySelect

Testing : 
- Added test for currency sub fields in getOperandsForFilterType
- Completely reworked isMatchingCurrencyFilter test

Improvements : 
- Created a unique CURRENCIES constant to avoid re-creating it at
various places
- Derive the type FilterableFieldType from a constant array
FILTERABLE_FIELD_TYPES, so it's easier to work with
- Added areCompositeTypeSubFieldsFilterable
- Fixed a bug with empty value '[]' that was preventing the auto-removal
of a filter chip

Miscellaneous : 
- Created isExpectedSubFieldName util to do a type-safe check of a
subFieldName
- Better naming : renamed isCompositeField to isCompositeFieldType
- Created isCompositeTypeFilterableWithAny to specify which field types
are filterable by any sub field
- Better naming : renamed
ObjectFilterDropdownFilterSelectCompositeFieldSubMenu to
ObjectFilterDropdownSubFieldSelect
- Better naming : renamed ObjectFilterDropdownFilterSelect to
ObjectFilterDropdownFieldSelect
- Created isEmptinessOperand util instead of duplicating the same
hard-coded check in multiple places
- Better naming : used subFieldName instead of compositeFieldName for
consistency
- UseEffect removal : removed unnecessary useEffect in
MultipleSelectDropdown

Fixes a bug where Empty and Not weren't appearing in filter chip in
particular cases
Fixes https://github.com/twentyhq/core-team-issues/issues/498
Fixes https://github.com/twentyhq/twenty/issues/7558
This commit is contained in:
Lucas Bordeau
2025-04-25 19:33:00 +02:00
committed by GitHub
parent f201091c68
commit 50cb32d122
51 changed files with 1358 additions and 422 deletions

View File

@ -7,8 +7,10 @@ import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { CurrencyPickerHotkeyScope } from '../types/CurrencyPickerHotkeyScope';
import { CurrencyPickerDropdownSelect } from './CurrencyPickerDropdownSelect';
import { CURRENCIES } from '@/settings/data-model/constants/Currencies';
import { Currency } from '@/ui/input/components/internal/types/Currency';
import { IconChevronDown } from 'twenty-ui/display';
import { CurrencyPickerDropdownSelect } from './CurrencyPickerDropdownSelect';
const StyledDropdownButtonContainer = styled.div`
align-items: center;
@ -41,20 +43,12 @@ const StyledIconContainer = styled.div`
}
`;
export type Currency = {
label: string;
value: string;
Icon: any;
};
export const CurrencyPickerDropdownButton = ({
valueCode,
selectedCurrencyCode,
onChange,
currencies,
}: {
valueCode: string;
selectedCurrencyCode: string;
onChange: (currency: Currency) => void;
currencies: Currency[];
}) => {
const theme = useTheme();
@ -67,7 +61,9 @@ export const CurrencyPickerDropdownButton = ({
closeDropdown();
};
const currency = currencies.find(({ value }) => value === valueCode);
const currency = CURRENCIES.find(
({ value }) => value === selectedCurrencyCode,
);
const currencyCode = currency?.value ?? CurrencyCode.USD;
@ -85,7 +81,6 @@ export const CurrencyPickerDropdownButton = ({
}
dropdownComponents={
<CurrencyPickerDropdownSelect
currencies={currencies}
selectedCurrency={currency}
onChange={handleChange}
/>

View File

@ -4,15 +4,15 @@ import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { Currency } from './CurrencyPickerDropdownButton';
import { CURRENCIES } from '@/settings/data-model/constants/Currencies';
import { Currency } from '@/ui/input/components/internal/types/Currency';
import { MenuItem, MenuItemSelectAvatar } from 'twenty-ui/navigation';
export const CurrencyPickerDropdownSelect = ({
currencies,
selectedCurrency,
onChange,
}: {
currencies: Currency[];
selectedCurrency?: Currency;
onChange: (currency: Currency) => void;
}) => {
@ -20,14 +20,14 @@ export const CurrencyPickerDropdownSelect = ({
const filteredCurrencies = useMemo(
() =>
currencies.filter(
CURRENCIES.filter(
({ value, label }) =>
value
.toLocaleLowerCase()
.includes(searchFilter.toLocaleLowerCase()) ||
label.toLocaleLowerCase().includes(searchFilter.toLocaleLowerCase()),
),
[currencies, searchFilter],
[searchFilter],
);
return (
@ -49,7 +49,7 @@ export const CurrencyPickerDropdownSelect = ({
key={selectedCurrency.value}
selected={true}
onClick={() => onChange(selectedCurrency)}
text={`${selectedCurrency.label} (${selectedCurrency.value})`}
text={selectedCurrency.label}
/>
)}
{filteredCurrencies.map((item) =>

View File

@ -0,0 +1,5 @@
export type Currency = {
label: string;
value: string;
Icon: any;
};