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
93 changes: 93 additions & 0 deletions apps/src/tests/issue-tests/TestSearchBarKeyboardReopen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import React from 'react';
import { NavigationContainer, ParamListBase } from '@react-navigation/native';
import {
NativeStackNavigationProp,
createNativeStackNavigator,
} from '@react-navigation/native-stack';
import { Alert, Button, ScrollView, Text, View } from 'react-native';
import { ListItem } from '../../shared';

type StackRouteParamList = {
Home: undefined;
Second: undefined;
};

type NavigationProp<ParamList extends ParamListBase> = {
navigation: NativeStackNavigationProp<ParamList>;
};

type StackNavigationProp = NavigationProp<StackRouteParamList>;

const Stack = createNativeStackNavigator<StackRouteParamList>();

const items = Array.from({ length: 20 }, (_, i) => `Item ${i + 1}`);

function Second({}: StackNavigationProp) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Second screen</Text>
</View>
);
}

function Home({ navigation }: StackNavigationProp) {
const [searchQuery, setSearchQuery] = React.useState('');

React.useLayoutEffect(() => {
navigation.setOptions({
headerSearchBarOptions: {
placement: 'stacked',
hideWhenScrolling: false,
onChangeText: event => setSearchQuery(event.nativeEvent.text),
},
headerRight: () => (
<Button
title="Menu"
onPress={() => Alert.alert('Menu', 'Header button pressed')}
testID="home-header-right-button"
/>
),
});
}, [navigation]);

return (
<View style={{ flex: 1 }}>
<ScrollView
contentInsetAdjustmentBehavior="automatic"
contentContainerStyle={{ marginTop: 15 }}>
<Text style={{ padding: 15, color: '#666', lineHeight: 22 }}>
Steps to reproduce:{'\n'}
1. Tap the search bar to focus it{'\n'}
2. Dismiss the keyboard (tap Cancel or tap away){'\n'}
3. Tap the "Menu" button in the header{'\n'}
4. Bug: The keyboard opens again instead of just triggering the button
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The description in the comment says "4. Bug: The keyboard opens again instead of just triggering the button" but this is describing the bug that was fixed, not the current expected behavior. Since this is a test file documenting the fix, the comment should clarify that this was the previous buggy behavior, or update it to describe the expected behavior after the fix (keyboard should NOT reopen).

Suggested change
4. Bug: The keyboard opens again instead of just triggering the button
4. Expected: The keyboard should remain dismissed and only the button press should be handled (previously, the keyboard would reopen)

Copilot uses AI. Check for mistakes.
</Text>
<Button
title="Open Second"
onPress={() => navigation.navigate('Second')}
testID="home-button-open-second"
/>
{items
.filter(
name =>
searchQuery === '' ||
name.toLowerCase().includes(searchQuery.toLowerCase()),
)
.map(name => (
<ListItem key={name} title={name} onPress={() => {}} />
))}
</ScrollView>
</View>
);
}

export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="Second" component={Second} />
</Stack.Navigator>
</NavigationContainer>
);
}
1 change: 1 addition & 0 deletions apps/src/tests/issue-tests/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,4 +201,5 @@ export { default as TestBottomTabsOrientation } from './TestBottomTabsOrientatio
export { default as TestScreenStack } from './TestScreenStack';
export { default as TestSplit } from './TestSplit';
export { default as TestSafeAreaViewIOS } from './TestSafeAreaViewIOS';
export { default as TestSearchBarKeyboardReopen } from './TestSearchBarKeyboardReopen';
export { default as TestStackNesting } from './TestStackNesting';
7 changes: 6 additions & 1 deletion ios/RNSScreenStackHeaderConfig.mm
Original file line number Diff line number Diff line change
Expand Up @@ -673,7 +673,12 @@ + (void)updateViewController:(UIViewController *)vc
#if !TARGET_OS_TV
RNSSearchBar *searchBar = subview.subviews[0];
searchBarPresent = true;
navitem.searchController = searchBar.controller;
// Only assign searchController if it changed to avoid UIKit re-activating the search bar
// and triggering the keyboard to appear unexpectedly (e.g. when pressing a header menu button
// after the search bar was previously focused and dismissed).
if (navitem.searchController != searchBar.controller) {
navitem.searchController = searchBar.controller;
}
navitem.hidesSearchBarWhenScrolling = searchBar.hideWhenScrolling;
#if RNS_IPHONE_OS_VERSION_AVAILABLE(16_0)
if (@available(iOS 16.0, *)) {
Expand Down
1 change: 1 addition & 0 deletions ios/RNSSearchBar.mm
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ - (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
{
_controller.searchBar.text = @"";
[_controller.searchBar resignFirstResponder];
[self resignFirstResponder];
[self hideCancelButton];

Expand Down
Loading