-
Notifications
You must be signed in to change notification settings - Fork 3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #51173 from MrMuzyk/feat/push-row-with-modal
feat: PushRowWithModal component
- Loading branch information
Showing
3 changed files
with
226 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<string, string>; | ||
|
||
/** 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 ( | ||
<Modal | ||
onClose={onClose} | ||
isVisible={isVisible} | ||
type={CONST.MODAL.MODAL_TYPE.RIGHT_DOCKED} | ||
onModalHide={onClose} | ||
shouldUseCustomBackdrop | ||
useNativeDriver | ||
> | ||
<ScreenWrapper | ||
includePaddingTop={false} | ||
includeSafeAreaPaddingBottom={false} | ||
testID={PushRowModal.displayName} | ||
> | ||
<HeaderWithBackButton | ||
title={headerTitle} | ||
onBackButtonPress={onClose} | ||
/> | ||
<SelectionList | ||
headerMessage={searchbarInputText.trim().length && !optionListItems.length ? translate('common.noResultsFound') : ''} | ||
textInputLabel={searchInputTitle} | ||
textInputValue={searchbarInputText} | ||
onChangeText={filterShownOptions} | ||
onSelectRow={handleSelectRow} | ||
sections={[{data: optionListItems}]} | ||
initiallyFocusedOptionKey={optionListItems.find((option) => option.value === selectedOption)?.keyForList} | ||
showScrollIndicator | ||
shouldShowTooltips={false} | ||
ListItem={RadioListItem} | ||
/> | ||
</ScreenWrapper> | ||
</Modal> | ||
); | ||
} | ||
|
||
PushRowModal.displayName = 'PushRowModal'; | ||
|
||
export type {ListItemType}; | ||
|
||
export default PushRowModal; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<string, string>; | ||
|
||
/** 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<ViewStyle>; | ||
|
||
/** 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 ( | ||
<> | ||
<MenuItemWithTopDescription | ||
description={description} | ||
title={optionsList[selectedOption]} | ||
shouldShowRightIcon={shouldAllowChange} | ||
onPress={handleModalOpen} | ||
wrapperStyle={wrapperStyles} | ||
interactive={shouldAllowChange} | ||
brickRoadIndicator={errorText ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} | ||
errorText={errorText} | ||
/> | ||
<PushRowModal | ||
isVisible={isModalVisible} | ||
selectedOption={selectedOption} | ||
onOptionChange={handleOptionChange} | ||
onClose={handleModalClose} | ||
optionsList={optionsList} | ||
headerTitle={modalHeaderTitle} | ||
searchInputTitle={searchInputTitle} | ||
/> | ||
</> | ||
); | ||
} | ||
|
||
PushRowWithModal.displayName = 'PushRowWithModal'; | ||
|
||
export default PushRowWithModal; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters