-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Suggested Search & Filter Revamp #61170
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
🚧 @JS00001 has triggered a test app build. You can view the workflow run here. |
Creating another test build, still waiting on two BE changes, so unless you have auth running with those changes, then the query for 'Pay' and 'Export' will fail |
🧪🧪 Use the links below to test this adhoc build on Android, iOS, Desktop, and Web. Happy testing! 🧪🧪 |
Feeling good on my end, thanks for addressing my feedback! I will keep testing though to try to find more for ya! |
@getusha can you please prioritize another review? We're trying to get this merged this week. |
@JS00001 we have conflicts |
It'd be a nice improvement cc @Expensify/design for thoughts. That being said, I don't think we need to block this PR on that. |
🤔 I don't know if I see that as invalid though. It's certainly silly, but what if my intention was to see all expenses before May 2nd and all expenses after May 3rd? Related: are those date inputs inclusive or exclusive? |
That'd result in no data since those filters apply with an |
This is from the design doc, we are still injecting |
Oooohhhhh, I understand now. Well in that case I agree, it would be nice to let the user know they created a borked query, but I don't think it needs to block this PR. |
We should remove this from the frontend and just assume |
@JS00001 we should remove the diff for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Chatted 1:1 with @JS00001 and it seems like we missed a condition for the payer
filter. Jack is working on the BE fix for it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
))} | ||
{shouldShowSavedSearchesMenuItemTitle && ( | ||
<View> | ||
<Text style={[styles.sectionTitle, styles.pb1, styles.mt3]}>{translate('search.savedSearchesMenuItemTitle')}</Text> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const listData = useMemo(() => { | ||
const optionsList = getValidOptions( | ||
{ | ||
reports: options.reports, | ||
personalDetails: options.personalDetails, | ||
}, | ||
{excludeLogins: CONST.EXPENSIFY_EMAILS_OBJECT}, | ||
); | ||
|
||
const filteredOptionsList = filterAndOrderOptions(optionsList, cleanSearchTerm, { | ||
excludeLogins: CONST.EXPENSIFY_EMAILS_OBJECT, | ||
maxRecentReportsToShow: CONST.IOU.MAX_RECENT_REPORTS_TO_SHOW, | ||
}); | ||
|
||
const personalDetailList = filteredOptionsList.personalDetails.map((participant) => ({ | ||
...participant, | ||
isSelected: selectedOptions.some((selectedOption) => selectedOption.accountID === participant.accountID), | ||
})); | ||
|
||
const recentReportList = filteredOptionsList.recentReports.map((report) => ({ | ||
...report, | ||
isSelected: selectedOptions.some((selectedOption) => selectedOption.accountID === report.accountID), | ||
})); | ||
|
||
const currentUserOption = filteredOptionsList.currentUserOption | ||
? { | ||
...filteredOptionsList.currentUserOption, | ||
isSelected: selectedOptions.some((selectedOption) => selectedOption.accountID === filteredOptionsList.currentUserOption?.accountID), | ||
} | ||
: null; | ||
|
||
return {personalDetails: personalDetailList, recentReports: recentReportList, currentUserOption}; | ||
}, [cleanSearchTerm, options.personalDetails, options.reports, selectedOptions]); | ||
|
||
const {sections, headerMessage} = useMemo(() => { | ||
const newSections: Section[] = [ | ||
...(listData.currentUserOption ? [{title: '', data: [listData.currentUserOption], shouldShow: true}] : []), | ||
{ | ||
title: '', | ||
data: listData.recentReports, | ||
shouldShow: listData.recentReports.length > 0, | ||
}, | ||
{ | ||
title: '', | ||
data: listData.personalDetails, | ||
shouldShow: listData.personalDetails.length > 0, | ||
}, | ||
]; | ||
|
||
const noResultsFound = listData.personalDetails.length === 0 && listData.recentReports.length === 0 && !listData.currentUserOption; | ||
const message = noResultsFound ? translate('common.noResultsFound') : undefined; | ||
|
||
return { | ||
sections: newSections, | ||
headerMessage: message, | ||
}; | ||
}, [listData, translate]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NAB: we could slightly simplify this
const listData = useMemo(() => { | |
const optionsList = getValidOptions( | |
{ | |
reports: options.reports, | |
personalDetails: options.personalDetails, | |
}, | |
{excludeLogins: CONST.EXPENSIFY_EMAILS_OBJECT}, | |
); | |
const filteredOptionsList = filterAndOrderOptions(optionsList, cleanSearchTerm, { | |
excludeLogins: CONST.EXPENSIFY_EMAILS_OBJECT, | |
maxRecentReportsToShow: CONST.IOU.MAX_RECENT_REPORTS_TO_SHOW, | |
}); | |
const personalDetailList = filteredOptionsList.personalDetails.map((participant) => ({ | |
...participant, | |
isSelected: selectedOptions.some((selectedOption) => selectedOption.accountID === participant.accountID), | |
})); | |
const recentReportList = filteredOptionsList.recentReports.map((report) => ({ | |
...report, | |
isSelected: selectedOptions.some((selectedOption) => selectedOption.accountID === report.accountID), | |
})); | |
const currentUserOption = filteredOptionsList.currentUserOption | |
? { | |
...filteredOptionsList.currentUserOption, | |
isSelected: selectedOptions.some((selectedOption) => selectedOption.accountID === filteredOptionsList.currentUserOption?.accountID), | |
} | |
: null; | |
return {personalDetails: personalDetailList, recentReports: recentReportList, currentUserOption}; | |
}, [cleanSearchTerm, options.personalDetails, options.reports, selectedOptions]); | |
const {sections, headerMessage} = useMemo(() => { | |
const newSections: Section[] = [ | |
...(listData.currentUserOption ? [{title: '', data: [listData.currentUserOption], shouldShow: true}] : []), | |
{ | |
title: '', | |
data: listData.recentReports, | |
shouldShow: listData.recentReports.length > 0, | |
}, | |
{ | |
title: '', | |
data: listData.personalDetails, | |
shouldShow: listData.personalDetails.length > 0, | |
}, | |
]; | |
const noResultsFound = listData.personalDetails.length === 0 && listData.recentReports.length === 0 && !listData.currentUserOption; | |
const message = noResultsFound ? translate('common.noResultsFound') : undefined; | |
return { | |
sections: newSections, | |
headerMessage: message, | |
}; | |
}, [listData, translate]); | |
const listData = useMemo(() => { | |
const optionsList = getValidOptions( | |
{ | |
reports: options.reports, | |
personalDetails: options.personalDetails, | |
}, | |
{ | |
excludeLogins: CONST.EXPENSIFY_EMAILS_OBJECT, | |
selectedOptions, | |
includeSelectedOptions: true, | |
includeSelfDM: true, | |
}, | |
); | |
const { personalDetails: filteredOptionsList, recentReports } = filterAndOrderOptions(optionsList, cleanSearchTerm, { | |
excludeLogins: CONST.EXPENSIFY_EMAILS_OBJECT, | |
maxRecentReportsToShow: 5 | |
}); | |
const personalDetails = filteredOptionsList.map((participant) => ({ | |
...participant, | |
isSelected: selectedOptions.some((selectedOption) => selectedOption.accountID === participant.accountID), | |
})); | |
return [...(recentReports ?? []), ...(personalDetails ?? [])]; | |
}, [cleanSearchTerm, options.personalDetails, options.reports, selectedOptions]); | |
const {sections, headerMessage} = useMemo(() => { | |
const newSections: Section[] = [ | |
{ | |
title: '', | |
data: listData, | |
shouldShow: !isEmpty(listData), | |
}, | |
]; | |
const noResultsFound = isEmpty(listData); | |
const message = noResultsFound ? translate('common.noResultsFound') : undefined; | |
return { | |
sections: newSections, | |
headerMessage: message, | |
}; | |
}, [listData, translate]); |
import ONYXKEYS from '@src/ONYXKEYS'; | ||
|
||
function getSelectedOptionData(option: Option) { | ||
return {...option, selected: true}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we need this?
<ButtonWithDropdownMenu | ||
onPress={() => null} | ||
shouldAlwaysShowDropdownMenu | ||
buttonSize={CONST.DROPDOWN_BUTTON_SIZE.SMALL} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const content: EmptySearchViewItem = useMemo(() => { | ||
// Begin by going through all of our To-do searches, and returning their empty state | ||
// if it exists | ||
for (const menuItem of typeMenuItems) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Explanation of Change
Implemented suggested search! See below for the expected behavior.
Fixed Issues
$ #61112
$ #61114
$ #61116
$ #61117
$ #61189
Tests
Test all variations of search. The following are supposed to be in the LHN:
Expenses, Reports, Chats: Always in LHN
'Submit' - Only in LHN when user is in a workspace
'Approve' - Only when a user is the policys 'approver', OR the user is the 'submitsTo' value for another user
'Pay' - Only when the user is a workspace admin
'Export' - Only when the user is an exporter for one of the accounting integrations
Ensure that all expected results show up, ensure that all new 'filter dropdowns' show the expected behavior and behave as expected, ensure that the new 'Filters' button works & has the expected behavior
QA Steps
Same as Tests
PR Author Checklist
### Fixed Issues
section aboveTests
sectionOffline steps
sectionQA steps
sectiontoggleReport
and notonIconClick
)src/languages/*
files and using the translation methodSTYLE.md
) were followedAvatar
, I verified the components usingAvatar
are working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG)
)Avatar
is modified, I verified thatAvatar
is working as expected in all cases)Design
label and/or tagged@Expensify/design
so the design team can review the changes.ScrollView
component to make it scrollable when more elements are added to the page.main
branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTest
steps./** comment above it */
this
properly so there are no scoping issues (i.e. foronClick={this.submit}
the methodthis.submit
should be bound tothis
in the constructor)this
are necessary to be bound (i.e. avoidthis.submit = this.submit.bind(this);
ifthis.submit
is never passed to a component event handler likeonClick
)Screenshots/Videos
Android: Native
Screen.Recording.2025-05-09.at.11.59.29.AM.mov
Screen.Recording.2025-05-09.at.11.42.49.AM.mov
Screen.Recording.2025-05-09.at.11.35.58.AM.mov