Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/ClearButtonAndSpinner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ interface ClearButtonAndSpinnerProps
isInputEmpty: boolean;
isLoading: boolean;
onPress: () => void;
testIDSuffix?: string;
}

const ClearButtonAndSpinner: React.FC<ClearButtonAndSpinnerProps> = ({
Expand All @@ -32,13 +33,15 @@ const ClearButtonAndSpinner: React.FC<ClearButtonAndSpinnerProps> = ({
isInputEmpty,
onPress,
clearElement,
testIDSuffix = '',
}) => {
// Loading indicator
if (isLoading) {
if (!showLoadingIndicator) return null;

return (
<ActivityIndicator
testID={`loading-indicator${testIDSuffix}`}
style={[styles.loadingIndicator]}
size={'small'}
color={style.loadingIndicator?.color || '#000000'}
Expand All @@ -57,6 +60,7 @@ const ClearButtonAndSpinner: React.FC<ClearButtonAndSpinnerProps> = ({
// Clear button
return (
<TouchableOpacity
testID={`clear-button${testIDSuffix}`}
style={[styles.clearButton]}
onPress={onPress}
accessibilityRole="button"
Expand Down
31 changes: 28 additions & 3 deletions src/GooglePlacesTextInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,11 @@ interface GooglePlacesTextInputProps
enableDebug?: boolean;
accessibilityLabels?: GooglePlacesAccessibilityLabels;
suggestionTextProps?: SuggestionTextProps;
/**
* Test ID prefix for all internal components (for E2E testing)
* Components will be prefixed: textinput-{testID}, suggestion-touchableopacity-{index}-{testID}, etc.
*/
testID?: string;
}

interface GooglePlacesTextInputRef {
Expand Down Expand Up @@ -202,9 +207,13 @@ const GooglePlacesTextInput = forwardRef<
onBlur,
accessibilityLabels = {},
suggestionTextProps = {},
testID,
...restTextInputProps
} = props;

// Test ID suffix for all components
const testIDSuffix = testID ? `-${testID}` : '';

const [predictions, setPredictions] = useState<PredictionItem[]>([]);
const [loading, setLoading] = useState<boolean>(false);
const [inputText, setInputText] = useState<string>(value || '');
Expand Down Expand Up @@ -527,6 +536,7 @@ const GooglePlacesTextInput = forwardRef<

return (
<TouchableOpacity
testID={`suggestion-touchableopacity-${index}${testIDSuffix}`}
accessibilityRole="button"
accessibilityLabel={accessibilityLabel}
accessibilityHint="Double tap to select this place"
Expand All @@ -552,6 +562,7 @@ const GooglePlacesTextInput = forwardRef<
} as any))}
>
<Text
testID={`suggestion-text-${index}${testIDSuffix}`}
style={[styles.mainText, style.suggestionText?.main, getTextAlign()]}
numberOfLines={suggestionTextProps.mainTextNumberOfLines}
ellipsizeMode={suggestionTextProps.ellipsizeMode || 'tail'}
Expand All @@ -560,6 +571,7 @@ const GooglePlacesTextInput = forwardRef<
</Text>
{secondaryText && (
<Text
testID={`suggestion-secondarytext-${index}${testIDSuffix}`}
style={[
styles.secondaryText,
style.suggestionText?.secondary,
Expand Down Expand Up @@ -613,20 +625,28 @@ const GooglePlacesTextInput = forwardRef<
}, []);

return (
<View style={[styles.container, style.container]}>
<View style={[styles.inputContainer, style.inputContainer]}>
<View
testID={`container${testIDSuffix}`}
style={[styles.container, style.container]}
>
<View
testID={`input-wrapper${testIDSuffix}`}
style={[styles.inputContainer, style.inputContainer]}
>
{/* Render Button and Loader here if is forced RTL */}
{isRTL !== isDeviceRTL && (
<ClearButtonAndSpinner
{...props}
isLoading={loading || detailsLoading}
onPress={handleClearPress}
isInputEmpty={inputText.length === 0}
testIDSuffix={testIDSuffix}
/>
)}

<TextInput
{...restTextInputProps}
testID={`textinput${testIDSuffix}`}
ref={inputRef}
style={[styles.input, style.input, getTextAlign()]}
placeholder={placeHolderText}
Expand All @@ -646,14 +666,19 @@ const GooglePlacesTextInput = forwardRef<
isLoading={loading || detailsLoading}
onPress={handleClearPress}
isInputEmpty={inputText.length === 0}
testIDSuffix={testIDSuffix}
/>
)}
</View>

{/* Suggestions */}
{showSuggestions && predictions.length > 0 && (
<View style={[styles.suggestionsContainer, style.suggestionsContainer]}>
<View
testID={`suggestions-container${testIDSuffix}`}
style={[styles.suggestionsContainer, style.suggestionsContainer]}
>
<FlatList
testID={`suggestions-list${testIDSuffix}`}
data={predictions}
renderItem={renderSuggestion}
keyExtractor={(item) => item.placePrediction.placeId}
Expand Down