From 92d4020bcbe4b4a1acfd8b468692f8b592da0dac Mon Sep 17 00:00:00 2001 From: Michal Muzyk Date: Mon, 21 Oct 2024 12:54:44 +0200 Subject: [PATCH 1/2] feat: PushRowWithModal component --- .../PushRowWithModal/PushRowModal.tsx | 120 ++++++++++++++++++ src/components/PushRowWithModal/index.tsx | 88 +++++++++++++ 2 files changed, 208 insertions(+) create mode 100644 src/components/PushRowWithModal/PushRowModal.tsx create mode 100644 src/components/PushRowWithModal/index.tsx diff --git a/src/components/PushRowWithModal/PushRowModal.tsx b/src/components/PushRowWithModal/PushRowModal.tsx new file mode 100644 index 000000000000..79fbc53c1e2c --- /dev/null +++ b/src/components/PushRowWithModal/PushRowModal.tsx @@ -0,0 +1,120 @@ +import React, {useEffect, useState} from 'react'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import Modal from '@components/Modal'; +import ScreenWrapper from '@components/ScreenWrapper'; +import SelectionList from '@components/SelectionList'; +import RadioListItem from '@components/SelectionList/RadioListItem'; +import useLocalize from '@hooks/useLocalize'; +import CONST from '@src/CONST'; + +type PushRowModalProps = { + /** Whether the modal is visible */ + isVisible: boolean; + + /** The currently selected option */ + selectedOption: string; + + /** Function to call when the user selects an option */ + onOptionChange: (option: string) => void; + + /** Function to call when the user closes the modal */ + onClose: () => void; + + /** The list of items to render */ + optionsList: Record; + + /** The title of the modal */ + headerTitle: string; + + /** The title of the search input */ + searchInputTitle?: string; +}; + +type ListItemType = { + value: string; + text: string; + keyForList: string; + isSelected: boolean; +}; + +function PushRowModal({isVisible, selectedOption, onOptionChange, onClose, optionsList, headerTitle, searchInputTitle}: PushRowModalProps) { + const {translate} = useLocalize(); + + const allOptions = Object.entries(optionsList).map(([key, value]) => ({ + value: key, + text: value, + keyForList: key, + isSelected: key === selectedOption, + })); + const [searchbarInputText, setSearchbarInputText] = useState(''); + const [optionListItems, setOptionListItems] = useState(allOptions); + + useEffect(() => { + setOptionListItems((prevOptionListItems) => + prevOptionListItems.map((option) => ({ + ...option, + isSelected: option.value === selectedOption, + })), + ); + }, [selectedOption]); + + const filterShownOptions = (searchText: string) => { + setSearchbarInputText(searchText); + const searchWords = searchText.toLowerCase().match(/[a-z0-9]+/g) ?? []; + setOptionListItems( + allOptions.filter((option) => + searchWords.every((word) => + option.text + .toLowerCase() + .replace(/[^a-z0-9]/g, ' ') + .includes(word), + ), + ), + ); + }; + + const handleSelectRow = (option: ListItemType) => { + onOptionChange(option.value); + onClose(); + }; + + return ( + + + + option.value === selectedOption)?.keyForList} + showScrollIndicator + shouldShowTooltips={false} + ListItem={RadioListItem} + /> + + + ); +} + +PushRowModal.displayName = 'PushRowModal'; + +export type {ListItemType}; + +export default PushRowModal; diff --git a/src/components/PushRowWithModal/index.tsx b/src/components/PushRowWithModal/index.tsx new file mode 100644 index 000000000000..11c7ff4386d4 --- /dev/null +++ b/src/components/PushRowWithModal/index.tsx @@ -0,0 +1,88 @@ +import React, {useState} from 'react'; +import type {StyleProp, ViewStyle} from 'react-native'; +import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; +import CONST from '@src/CONST'; +import PushRowModal from './PushRowModal'; + +type PushRowWithModalProps = { + /** The list of options that we want to display where key is option code and value is option name */ + optionsList: Record; + + /** The currently selected option */ + selectedOption: string; + + /** Function to call when the user selects an option */ + onOptionChange: (value: string) => void; + + /** Additional styles to apply to container */ + wrapperStyles?: StyleProp; + + /** The description for the picker */ + description: string; + + /** The title of the modal */ + modalHeaderTitle: string; + + /** The title of the search input */ + searchInputTitle: string; + + /** Whether the selected option is editable */ + shouldAllowChange?: boolean; + + /** Text to display on error message */ + errorText?: string; +}; + +function PushRowWithModal({ + selectedOption, + onOptionChange, + optionsList, + wrapperStyles, + description, + modalHeaderTitle, + searchInputTitle, + shouldAllowChange = true, + errorText, +}: PushRowWithModalProps) { + const [isModalVisible, setIsModalVisible] = useState(false); + + const handleModalClose = () => { + setIsModalVisible(false); + }; + + const handleModalOpen = () => { + setIsModalVisible(true); + }; + + const handleOptionChange = (value: string) => { + onOptionChange(value); + }; + + return ( + <> + + + + ); +} + +PushRowWithModal.displayName = 'PushRowWithModal'; + +export default PushRowWithModal; From a5db2365d525d8bd8c2d0c862f6dd65775e1365d Mon Sep 17 00:00:00 2001 From: Michal Muzyk Date: Tue, 22 Oct 2024 08:50:03 +0200 Subject: [PATCH 2/2] feat: showcase component usage --- .../NonUSD/Country/substeps/Confirmation.tsx | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/pages/ReimbursementAccount/NonUSD/Country/substeps/Confirmation.tsx b/src/pages/ReimbursementAccount/NonUSD/Country/substeps/Confirmation.tsx index d35a6f4b124f..df4cee627c78 100644 --- a/src/pages/ReimbursementAccount/NonUSD/Country/substeps/Confirmation.tsx +++ b/src/pages/ReimbursementAccount/NonUSD/Country/substeps/Confirmation.tsx @@ -1,17 +1,25 @@ -import React from 'react'; +import React, {useState} from 'react'; import FormProvider from '@components/Form/FormProvider'; +import PushRowWithModal from '@components/PushRowWithModal'; import SafeAreaConsumer from '@components/SafeAreaConsumer'; import ScrollView from '@components/ScrollView'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import type {SubStepProps} from '@hooks/useSubStep/types'; import useThemeStyles from '@hooks/useThemeStyles'; +import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; function Confirmation({onNext}: SubStepProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); + const [selectedCountry, setSelectedCountry] = useState(''); + + const handleSelectingCountry = (country: string) => { + setSelectedCountry(country); + }; + return ( {({safeAreaPaddingBottomStyle}) => ( @@ -27,6 +35,15 @@ function Confirmation({onNext}: SubStepProps) { submitButtonStyles={[styles.mh5, styles.pb0, styles.mbn1]} > {translate('countryStep.confirmBusinessBank')} + {/* This is only to showcase usage of PushRowWithModal component. The actual implementation will come in next issue - https://github.com/Expensify/App/issues/50897 */} + )}