From cc9cfce2a25a7af516a74c5c62329da6b9adba68 Mon Sep 17 00:00:00 2001 From: Wojciech Boman Date: Mon, 15 Jul 2024 17:24:51 +0200 Subject: [PATCH 01/26] Add template of Search Results Page --- assets/images/filters.svg | 6 + .../simple-illustration__filters.svg | 24 ++ src/components/Header.tsx | 8 +- src/components/HeaderWithBackButton/index.tsx | 3 + src/components/HeaderWithBackButton/types.ts | 3 + src/components/Icon/Expensicons.ts | 2 + src/components/Icon/Illustrations.ts | 2 + .../Search/SearchListWithHeader.tsx | 4 +- src/components/Search/SearchPageHeader.tsx | 67 ++++- src/components/Search/SearchV2.tsx | 244 ++++++++++++++++++ src/libs/SearchUtils.ts | 10 + src/pages/Search/SearchPage.tsx | 3 +- src/pages/Search/SearchPageBottomTab.tsx | 4 +- src/pages/Search/SearchResultsFilters.tsx | 101 ++++++++ .../Search/SearchResultsFiltersNarrow.tsx | 93 +++++++ 15 files changed, 559 insertions(+), 15 deletions(-) create mode 100644 assets/images/filters.svg create mode 100644 assets/images/simple-illustrations/simple-illustration__filters.svg create mode 100644 src/components/Search/SearchV2.tsx create mode 100644 src/pages/Search/SearchResultsFilters.tsx create mode 100644 src/pages/Search/SearchResultsFiltersNarrow.tsx diff --git a/assets/images/filters.svg b/assets/images/filters.svg new file mode 100644 index 000000000000..8fc1240f9575 --- /dev/null +++ b/assets/images/filters.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/images/simple-illustrations/simple-illustration__filters.svg b/assets/images/simple-illustrations/simple-illustration__filters.svg new file mode 100644 index 000000000000..c1d574f154f4 --- /dev/null +++ b/assets/images/simple-illustrations/simple-illustration__filters.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 86a7a9cabcb6..e1b4cb7d596f 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -21,9 +21,12 @@ type HeaderProps = { /** Additional header container styles */ containerStyles?: StyleProp; + + /** Whether the subtitle should be displayed above the title */ + showSubtitleAboveTitle?: boolean; }; -function Header({title = '', subtitle = '', textStyles = [], containerStyles = [], shouldShowEnvironmentBadge = false}: HeaderProps) { +function Header({title = '', subtitle = '', textStyles = [], containerStyles = [], shouldShowEnvironmentBadge = false, showSubtitleAboveTitle = false}: HeaderProps) { const styles = useThemeStyles(); const renderedSubtitle = useMemo( () => ( @@ -46,6 +49,7 @@ function Header({title = '', subtitle = '', textStyles = [], containerStyles = [ return ( + {showSubtitleAboveTitle && renderedSubtitle} {typeof title === 'string' ? !!title && ( ) : title} - {renderedSubtitle} + {!showSubtitleAboveTitle && renderedSubtitle} {shouldShowEnvironmentBadge && } diff --git a/src/components/HeaderWithBackButton/index.tsx b/src/components/HeaderWithBackButton/index.tsx index 2d73e3c2dd24..dccb8a6d0fb6 100755 --- a/src/components/HeaderWithBackButton/index.tsx +++ b/src/components/HeaderWithBackButton/index.tsx @@ -60,6 +60,7 @@ function HeaderWithBackButton({ shouldNavigateToTopMostReport = false, progressBarPercentage, style, + showSubtitleAboveTitle = false, }: HeaderWithBackButtonProps) { const theme = useTheme(); const styles = useThemeStyles(); @@ -103,6 +104,7 @@ function HeaderWithBackButton({ title={title} subtitle={stepCounter ? translate('stepCounter', stepCounter) : subtitle} textStyles={[titleColor ? StyleUtils.getTextColorStyle(titleColor) : {}, isCentralPaneSettings && styles.textHeadlineH2]} + showSubtitleAboveTitle={showSubtitleAboveTitle} /> ); }, [ @@ -123,6 +125,7 @@ function HeaderWithBackButton({ title, titleColor, translate, + showSubtitleAboveTitle ]); return ( diff --git a/src/components/HeaderWithBackButton/types.ts b/src/components/HeaderWithBackButton/types.ts index 6b08dd74dc8b..66ba1c63919c 100644 --- a/src/components/HeaderWithBackButton/types.ts +++ b/src/components/HeaderWithBackButton/types.ts @@ -130,6 +130,9 @@ type HeaderWithBackButtonProps = Partial & { /** Additional styles to add to the component */ style?: StyleProp; + + /** Whether the subtitle should be displayed above the title */ + showSubtitleAboveTitle?: boolean; }; export type {ThreeDotsMenuItem}; diff --git a/src/components/Icon/Expensicons.ts b/src/components/Icon/Expensicons.ts index 487df5594212..45232a830c39 100644 --- a/src/components/Icon/Expensicons.ts +++ b/src/components/Icon/Expensicons.ts @@ -81,6 +81,7 @@ import ExpensifyLogoNew from '@assets/images/expensify-logo-new.svg'; import ExpensifyWordmark from '@assets/images/expensify-wordmark.svg'; import EyeDisabled from '@assets/images/eye-disabled.svg'; import Eye from '@assets/images/eye.svg'; +import Filters from '@assets/images/filters.svg'; import Flag from '@assets/images/flag.svg'; import FlagLevelOne from '@assets/images/flag_level_01.svg'; import FlagLevelTwo from '@assets/images/flag_level_02.svg'; @@ -372,4 +373,5 @@ export { CheckCircle, CheckmarkCircle, NetSuiteSquare, + Filters, }; diff --git a/src/components/Icon/Illustrations.ts b/src/components/Icon/Illustrations.ts index b4dd8f254e25..8e24d3e8b5c7 100644 --- a/src/components/Icon/Illustrations.ts +++ b/src/components/Icon/Illustrations.ts @@ -54,6 +54,7 @@ import CreditCardsNew from '@assets/images/simple-illustrations/simple-illustrat import CreditCardEyes from '@assets/images/simple-illustrations/simple-illustration__creditcardeyes.svg'; import EmailAddress from '@assets/images/simple-illustrations/simple-illustration__email-address.svg'; import EmptyState from '@assets/images/simple-illustrations/simple-illustration__empty-state.svg'; +import Filters from '@assets/images/simple-illustrations/simple-illustration__filters.svg'; import FolderOpen from '@assets/images/simple-illustrations/simple-illustration__folder-open.svg'; import Gears from '@assets/images/simple-illustrations/simple-illustration__gears.svg'; import HandCard from '@assets/images/simple-illustrations/simple-illustration__handcard.svg'; @@ -206,4 +207,5 @@ export { FolderWithPapers, VirtualCard, Tire, + Filters, }; diff --git a/src/components/Search/SearchListWithHeader.tsx b/src/components/Search/SearchListWithHeader.tsx index 02da657609ba..ed081acbef7e 100644 --- a/src/components/Search/SearchListWithHeader.tsx +++ b/src/components/Search/SearchListWithHeader.tsx @@ -20,6 +20,7 @@ type SearchListWithHeaderProps = Omit void; + isSearchResultsMode?: boolean; }; function mapTransactionItemToSelectedEntry(item: TransactionListItemType): [string, SelectedTransactionInfo] { @@ -41,7 +42,7 @@ function mapToItemWithSelectionInfo(item: TransactionListItemType | ReportListIt } function SearchListWithHeader( - {ListItem, onSelectRow, query, hash, data, searchType, isMobileSelectionModeActive, setIsMobileSelectionModeActive, ...props}: SearchListWithHeaderProps, + {ListItem, onSelectRow, query, hash, data, searchType, isMobileSelectionModeActive, setIsMobileSelectionModeActive, isSearchResultsMode = false, ...props}: SearchListWithHeaderProps, ref: ForwardedRef, ) { const {isSmallScreenWidth} = useWindowDimensions(); @@ -153,6 +154,7 @@ function SearchListWithHeader( hash={hash} isMobileSelectionModeActive={isMobileSelectionModeActive} setIsMobileSelectionModeActive={setIsMobileSelectionModeActive} + isSearchResultsMode={isSearchResultsMode} /> // eslint-disable-next-line react/jsx-props-no-spreading diff --git a/src/components/Search/SearchPageHeader.tsx b/src/components/Search/SearchPageHeader.tsx index b0f2acfb57d1..9c9c177d4bf2 100644 --- a/src/components/Search/SearchPageHeader.tsx +++ b/src/components/Search/SearchPageHeader.tsx @@ -1,4 +1,5 @@ import React, {useMemo} from 'react'; +import Button from '@components/Button'; import ButtonWithDropdownMenu from '@components/ButtonWithDropdownMenu'; import type {DropdownOption} from '@components/ButtonWithDropdownMenu/types'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -10,6 +11,7 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import * as SearchActions from '@libs/actions/Search'; +import * as SearchUtils from '@libs/SearchUtils'; import SearchSelectedNarrow from '@pages/Search/SearchSelectedNarrow'; import variables from '@styles/variables'; import CONST from '@src/CONST'; @@ -25,22 +27,58 @@ type SearchHeaderProps = { hash: number; isMobileSelectionModeActive?: boolean; setIsMobileSelectionModeActive?: (isMobileSelectionModeActive: boolean) => void; + isSearchResultsMode?: boolean; }; type SearchHeaderOptionValue = DeepValueOf | undefined; -function SearchPageHeader({query, selectedItems = {}, hash, clearSelectedItems, isMobileSelectionModeActive, setIsMobileSelectionModeActive}: SearchHeaderProps) { +function SearchPageHeader({ + query, + selectedItems = {}, + hash, + clearSelectedItems, + isMobileSelectionModeActive, + setIsMobileSelectionModeActive, + isSearchResultsMode = false, +}: SearchHeaderProps) { const {translate} = useLocalize(); const theme = useTheme(); const styles = useThemeStyles(); const {isOffline} = useNetwork(); const {isSmallScreenWidth} = useResponsiveLayout(); - const headerContent: {[key in SearchQuery]: {icon: IconAsset; title: string}} = { - all: {icon: Illustrations.MoneyReceipts, title: translate('common.expenses')}, - shared: {icon: Illustrations.SendMoney, title: translate('common.shared')}, - drafts: {icon: Illustrations.Pencil, title: translate('common.drafts')}, - finished: {icon: Illustrations.CheckmarkCircle, title: translate('common.finished')}, - }; + const headerContent: {[key in SearchQuery]: {icon: IconAsset; title: string}} = useMemo( + () => ({ + all: {icon: Illustrations.MoneyReceipts, title: translate('common.expenses')}, + shared: {icon: Illustrations.SendMoney, title: translate('common.shared')}, + drafts: {icon: Illustrations.Pencil, title: translate('common.drafts')}, + finished: {icon: Illustrations.CheckmarkCircle, title: translate('common.finished')}, + }), + [translate], + ); + + const subtitle = useMemo(() => { + if (!isSearchResultsMode) { + return ''; + } + + return 'Filters'; + }, [isSearchResultsMode]); + + const headerTitle = useMemo(() => { + if (isSearchResultsMode) { + return SearchUtils.getSearchHeaderTitle(query, false); + } + + return headerContent[query]?.title; + }, [headerContent, isSearchResultsMode, query]); + + const headerIcon = useMemo(() => { + if (isSearchResultsMode) { + return Illustrations.Filters; + } + + return headerContent[query]?.icon; + }, [headerContent, isSearchResultsMode, query]); const selectedItemsKeys = Object.keys(selectedItems ?? []); @@ -137,10 +175,20 @@ function SearchPageHeader({query, selectedItems = {}, hash, clearSelectedItems, return ( + {isSearchResultsMode && ( +