Skip to content

Commit db29330

Browse files
authored
Merge pull request Expensify#86906 from bernhardoj/feat/81558-revamp-search-actions-bar-filters-chip
[No QA] Create new filter popups
2 parents fdfdad4 + 44a6a85 commit db29330

35 files changed

Lines changed: 1634 additions & 203 deletions

src/CONST/index.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8846,8 +8846,10 @@ const CONST = {
88468846
FILTER_FROM: 'Search-FilterFrom',
88478847
FILTER_WORKSPACE: 'Search-FilterWorkspace',
88488848
FILTER_GROUP_BY: 'Search-FilterGroupBy',
8849+
FILTER_SORT_BY: 'Search-FilterSortBy',
88498850
FILTER_GROUP_CURRENCY: 'Search-FilterGroupCurrency',
88508851
FILTER_VIEW: 'Search-FilterView',
8852+
FILTER_LIMIT: 'Search-FilterLimit',
88518853
FILTER_FEED: 'Search-FilterFeed',
88528854
FILTER_POSTED: 'Search-FilterPosted',
88538855
FILTER_WITHDRAWN: 'Search-FilterWithdrawn',
@@ -8870,10 +8872,19 @@ const CONST = {
88708872
FILTER_POPUP_APPLY_MULTI_SELECT: 'Search-FilterPopupApplyMultiSelect',
88718873
FILTER_POPUP_RESET_TEXT_INPUT: 'Search-FilterPopupResetTextInput',
88728874
FILTER_POPUP_APPLY_TEXT_INPUT: 'Search-FilterPopupApplyTextInput',
8875+
FILTER_POPUP_RESET_AMOUNT: 'Search-FilterPopupResetAmount',
8876+
FILTER_POPUP_APPLY_AMOUNT: 'Search-FilterPopupApplyAmount',
8877+
FILTER_POPUP_SAVE_AMOUNT: 'Search-FilterPopupSaveAmount',
88738878
FILTER_POPUP_RESET_DATE: 'Search-FilterPopupResetDate',
88748879
FILTER_POPUP_APPLY_DATE: 'Search-FilterPopupApplyDate',
88758880
FILTER_POPUP_RESET_USER: 'Search-FilterPopupResetUser',
88768881
FILTER_POPUP_APPLY_USER: 'Search-FilterPopupApplyUser',
8882+
FILTER_POPUP_RESET_REPORT: 'Search-FilterPopupResetReport',
8883+
FILTER_POPUP_APPLY_REPORT: 'Search-FilterPopupApplyReport',
8884+
FILTER_POPUP_RESET_REPORT_FIELD: 'Search-FilterPopupResetReportField',
8885+
FILTER_POPUP_APPLY_REPORT_FIELD: 'Search-FilterPopupApplyReportField',
8886+
FILTER_POPUP_RESET_CARD: 'Search-FilterPopupResetCard',
8887+
FILTER_POPUP_APPLY_CARD: 'Search-FilterPopupApplyCard',
88778888
TRANSACTION_LIST_ITEM_CHECKBOX: 'Search-TransactionListItemCheckbox',
88788889
EXPANDED_TRANSACTION_ROW: 'Search-ExpandedTransactionRow',
88798890
EXPANDED_TRANSACTION_ROW_CHECKBOX: 'Search-ExpandedTransactionRowCheckbox',

src/components/Search/FilterDropdowns/DateSelectPopup/ActionButtons.tsx renamed to src/components/Search/FilterDropdowns/ActionButtons.tsx

File renamed without changes.
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
import React, {useState} from 'react';
2+
import type {ValueOf} from 'type-fest';
3+
import AmountWithoutCurrencyInput from '@components/AmountWithoutCurrencyInput';
4+
import MenuItem from '@components/MenuItem';
5+
import type {SearchAmountFilterKeys} from '@components/Search/types';
6+
import useLocalize from '@hooks/useLocalize';
7+
import useThemeStyles from '@hooks/useThemeStyles';
8+
import {convertToBackendAmount, convertToFrontendAmountAsString} from '@libs/CurrencyUtils';
9+
import CONST from '@src/CONST';
10+
import type {SearchAdvancedFiltersForm} from '@src/types/form';
11+
import BasePopup from './BasePopup';
12+
13+
type AmountPopupProps = {
14+
filterKey: SearchAmountFilterKeys;
15+
label: string;
16+
value: Record<ValueOf<typeof CONST.SEARCH.AMOUNT_MODIFIERS>, string | undefined>;
17+
updateFilterForm: (value: Partial<SearchAdvancedFiltersForm>) => void;
18+
closeOverlay: () => void;
19+
};
20+
21+
type AmountInputProps = {
22+
title: string;
23+
value: string;
24+
name: string;
25+
onSave: (value: string) => void;
26+
onBackButtonPress: () => void;
27+
};
28+
29+
function AmountInput({title, value, name, onSave, onBackButtonPress}: AmountInputProps) {
30+
const styles = useThemeStyles();
31+
const [amount, setAmount] = useState(value);
32+
33+
return (
34+
<BasePopup
35+
label={title}
36+
onReset={() => onSave('')}
37+
onApply={() => onSave(amount)}
38+
resetSentryLabel={CONST.SENTRY_LABEL.SEARCH.FILTER_POPUP_RESET_AMOUNT}
39+
applySentryLabel={CONST.SENTRY_LABEL.SEARCH.FILTER_POPUP_APPLY_AMOUNT}
40+
onBackButtonPress={onBackButtonPress}
41+
>
42+
<AmountWithoutCurrencyInput
43+
containerStyles={[styles.ph4, styles.mb2]}
44+
defaultValue={amount}
45+
onInputChange={setAmount}
46+
label={title}
47+
accessibilityLabel={title}
48+
name={name}
49+
role={CONST.ROLE.PRESENTATION}
50+
inputMode={CONST.INPUT_MODE.DECIMAL}
51+
shouldAllowNegative
52+
autoFocus
53+
/>
54+
</BasePopup>
55+
);
56+
}
57+
58+
function AmountPopup({filterKey, label, value, closeOverlay, updateFilterForm}: AmountPopupProps) {
59+
const {translate} = useLocalize();
60+
const [selectedModifier, setSelectedModifier] = useState<ValueOf<typeof CONST.SEARCH.AMOUNT_MODIFIERS> | null>(null);
61+
const [amountValues, setAmountValues] = useState(value);
62+
63+
const title = {
64+
[CONST.SEARCH.AMOUNT_MODIFIERS.EQUAL_TO]: translate('search.filters.amount.equalTo'),
65+
[CONST.SEARCH.AMOUNT_MODIFIERS.GREATER_THAN]: translate('search.filters.amount.greaterThan'),
66+
[CONST.SEARCH.AMOUNT_MODIFIERS.LESS_THAN]: translate('search.filters.amount.lessThan'),
67+
};
68+
69+
const modifierConfig = [CONST.SEARCH.AMOUNT_MODIFIERS.EQUAL_TO, CONST.SEARCH.AMOUNT_MODIFIERS.GREATER_THAN, CONST.SEARCH.AMOUNT_MODIFIERS.LESS_THAN];
70+
71+
const formatAmount = (amount: string | undefined) => {
72+
return amount ? convertToFrontendAmountAsString(Number(amount), CONST.DEFAULT_CURRENCY_DECIMALS) : '';
73+
};
74+
75+
if (selectedModifier) {
76+
const goBack = () => {
77+
setSelectedModifier(null);
78+
};
79+
80+
const save = (rawAmount: string) => {
81+
if (rawAmount.trim() === '') {
82+
setAmountValues((prevAmountValues) => ({...prevAmountValues, [selectedModifier]: undefined}));
83+
goBack();
84+
return;
85+
}
86+
87+
const newAmount = convertToBackendAmount(Number(rawAmount)).toString();
88+
89+
// When setting an Equal To value, clear Greater Than and Less Than to avoid conflicting filters.
90+
if (selectedModifier === CONST.SEARCH.AMOUNT_MODIFIERS.EQUAL_TO) {
91+
setAmountValues({[CONST.SEARCH.AMOUNT_MODIFIERS.GREATER_THAN]: '', [CONST.SEARCH.AMOUNT_MODIFIERS.LESS_THAN]: '', [selectedModifier]: newAmount});
92+
}
93+
94+
// When setting Greater Than or Less Than, clear Equal To to avoid conflicting filters.
95+
if (selectedModifier === CONST.SEARCH.AMOUNT_MODIFIERS.GREATER_THAN || selectedModifier === CONST.SEARCH.AMOUNT_MODIFIERS.LESS_THAN) {
96+
setAmountValues((prevAmountValues) => ({...prevAmountValues, [CONST.SEARCH.AMOUNT_MODIFIERS.EQUAL_TO]: '', [selectedModifier]: newAmount}));
97+
}
98+
goBack();
99+
};
100+
101+
return (
102+
<AmountInput
103+
title={title[selectedModifier]}
104+
value={formatAmount(amountValues[selectedModifier])}
105+
name={`${filterKey}${selectedModifier}`}
106+
onBackButtonPress={goBack}
107+
onSave={save}
108+
/>
109+
);
110+
}
111+
112+
const onChange = (values: Record<ValueOf<typeof CONST.SEARCH.AMOUNT_MODIFIERS>, string | undefined>) => {
113+
const formValues: Record<string, string | undefined> = {};
114+
formValues[`${filterKey}${CONST.SEARCH.AMOUNT_MODIFIERS.EQUAL_TO}`] = values[CONST.SEARCH.AMOUNT_MODIFIERS.EQUAL_TO];
115+
formValues[`${filterKey}${CONST.SEARCH.AMOUNT_MODIFIERS.GREATER_THAN}`] = values[CONST.SEARCH.AMOUNT_MODIFIERS.GREATER_THAN];
116+
formValues[`${filterKey}${CONST.SEARCH.AMOUNT_MODIFIERS.LESS_THAN}`] = values[CONST.SEARCH.AMOUNT_MODIFIERS.LESS_THAN];
117+
updateFilterForm(formValues);
118+
closeOverlay();
119+
};
120+
121+
const applyChanges = () => onChange(amountValues);
122+
123+
const resetChanges = () => {
124+
onChange({
125+
[CONST.SEARCH.AMOUNT_MODIFIERS.EQUAL_TO]: undefined,
126+
[CONST.SEARCH.AMOUNT_MODIFIERS.GREATER_THAN]: undefined,
127+
[CONST.SEARCH.AMOUNT_MODIFIERS.LESS_THAN]: undefined,
128+
});
129+
};
130+
131+
return (
132+
<BasePopup
133+
label={label}
134+
onReset={resetChanges}
135+
onApply={applyChanges}
136+
resetSentryLabel={CONST.SENTRY_LABEL.SEARCH.FILTER_POPUP_RESET_AMOUNT}
137+
applySentryLabel={CONST.SENTRY_LABEL.SEARCH.FILTER_POPUP_APPLY_AMOUNT}
138+
>
139+
{modifierConfig.map((modifier) => (
140+
<MenuItem
141+
key={modifier}
142+
title={title[modifier]}
143+
description={formatAmount(amountValues[modifier])}
144+
onPress={() => setSelectedModifier(modifier)}
145+
shouldShowRightIcon
146+
viewMode={CONST.OPTION_MODE.COMPACT}
147+
/>
148+
))}
149+
</BasePopup>
150+
);
151+
}
152+
153+
export default AmountPopup;
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import React from 'react';
2+
import {View} from 'react-native';
3+
import type {StyleProp, ViewStyle} from 'react-native';
4+
import HeaderWithBackButton from '@components/HeaderWithBackButton';
5+
import Text from '@components/Text';
6+
import useResponsiveLayout from '@hooks/useResponsiveLayout';
7+
import useThemeStyles from '@hooks/useThemeStyles';
8+
import ActionButtons from './ActionButtons';
9+
10+
type BasePopupProps = React.PropsWithChildren & {
11+
label?: string;
12+
applySentryLabel: string;
13+
resetSentryLabel: string;
14+
style?: StyleProp<ViewStyle>;
15+
onApply: () => void;
16+
onReset: () => void;
17+
onBackButtonPress?: () => void;
18+
};
19+
20+
function BasePopup({children, label, applySentryLabel, resetSentryLabel, style, onApply, onReset, onBackButtonPress}: BasePopupProps) {
21+
// eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
22+
const {isSmallScreenWidth} = useResponsiveLayout();
23+
const styles = useThemeStyles();
24+
25+
return (
26+
<View style={[!isSmallScreenWidth && styles.pv4, style]}>
27+
{onBackButtonPress ? (
28+
<HeaderWithBackButton
29+
shouldDisplayHelpButton={false}
30+
style={[styles.h10, styles.pv1, styles.mb2]}
31+
subtitle={label}
32+
onBackButtonPress={onBackButtonPress}
33+
/>
34+
) : (
35+
isSmallScreenWidth && !!label && <Text style={[styles.textLabel, styles.textSupporting, styles.ph5, styles.pv1, styles.mb2]}>{label}</Text>
36+
)}
37+
{children}
38+
<ActionButtons
39+
containerStyle={[styles.flexRow, styles.gap2, styles.ph5, styles.mt2]}
40+
onReset={onReset}
41+
onApply={onApply}
42+
applySentryLabel={applySentryLabel}
43+
resetSentryLabel={resetSentryLabel}
44+
/>
45+
</View>
46+
);
47+
}
48+
49+
export default BasePopup;

0 commit comments

Comments
 (0)