Skip to content

Commit 4fae58d

Browse files
tbobakkafar
authored andcommitted
fix: Change name of focus and blur events to searchFocus and searchBlur (software-mansion#2154)
## Description Right now, while trying to use search bar on new architecture, iOS throws an error, that `topFocus` event cannot both direct and bubbling. That's mainly because `RNSSearchBar` component extends from `View`, which already has `topFocus` event registered. This PR changes the naming of this event (along with `topBlur`, to keep compliance of both behaviours) to `topSearchFocus` and `topSearchBlur`. Fixes react-navigation/react-navigation#11928. ## Changes - Changed topFocus and topBlur event to topSearchFocus and topSearchBlur, - Added type that renames onFocus and onBlur props inside SearchBar native component (to don't introduce breaking change). ## Screenshots / GIFs ### Before https://github.com/software-mansion/react-native-screens/assets/23281839/492a5e96-feaa-4107-ab3f-e0a3e1237fc3 ### After https://github.com/software-mansion/react-native-screens/assets/23281839/2cbce030-3844-43e4-bf3b-582ca82c5603 ## Test code and steps to reproduce You can test `Test758.tsx` test, along with Example -> Search bar to test behaviour of the events on search bar. Eventually, you can use snippet below, that has been enhanced by adding console.logs on every search bar event. <details> <summary>Enhanced test</summary> ```tsx /* eslint-disable react-hooks/exhaustive-deps */ import * as React from 'react'; import { Button, NativeSyntheticEvent, ScrollView } from 'react-native'; import { NavigationContainer, NavigationProp, ParamListBase, } from '@react-navigation/native'; import { createNativeStackNavigator, NativeStackScreenProps, } from '@react-navigation/native-stack'; import { SearchBarProps } from 'react-native-screens'; const AppStack = createNativeStackNavigator(); export default function App(): JSX.Element { return ( <NavigationContainer> <AppStack.Navigator screenOptions={{ headerLargeTitle: true, headerTransparent: false, }}> <AppStack.Screen name="First" component={First} /> <AppStack.Screen name="Second" component={Second} /> </AppStack.Navigator> </NavigationContainer> ); } function First({ navigation }: NativeStackScreenProps<ParamListBase>) { React.useLayoutEffect(() => { navigation.setOptions({ headerSearchBarOptions: searchBarOptions, }); }, [navigation]); const [search, setSearch] = React.useState(''); const searchBarOptions: SearchBarProps = { barTintColor: 'powderblue', tintColor: 'red', textColor: 'red', hideWhenScrolling: true, obscureBackground: false, hideNavigationBar: false, autoCapitalize: 'sentences', placeholder: 'Some text', cancelButtonText: 'Some text', onChangeText: (e: NativeSyntheticEvent<{ text: string }>) => { setSearch(e.nativeEvent.text); console.warn('Search text:', e.nativeEvent.text); }, onCancelButtonPress: () => console.warn('Cancel button pressed'), onSearchButtonPress: () => console.warn('Search button pressed'), onFocus: () => console.warn('onFocus event'), onBlur: () => console.warn('onBlur event'), onOpen: () => console.warn('onOpen event'), onClose: () => console.warn('onClose event'), }; const items = [ 'Apples', 'Pie', 'Juice', 'Cake', 'Nuggets', 'Some', 'Other', 'Stuff', 'To', 'Fill', 'The', 'Scrolling', 'Space', ]; return ( <ScrollView contentInsetAdjustmentBehavior="automatic" keyboardDismissMode="on-drag"> <Button title="Tap me for second screen" onPress={() => navigation.navigate('Second')} /> {items .filter(item => item.toLowerCase().indexOf(search.toLowerCase()) !== -1) .map(item => ( <Button title={item} key={item} onPress={() => { console.warn(`${item} clicked`); }} /> ))} </ScrollView> ); } function Second({ navigation }: { navigation: NavigationProp<ParamListBase> }) { return ( <ScrollView contentInsetAdjustmentBehavior="automatic"> <Button title="Tap me for first screen" onPress={() => navigation.navigate('First')} /> </ScrollView> ); } ``` </details> ## Checklist - [X] Included code example that can be used to test this change - [X] Updated TS types - [X] Updated documentation: <!-- For adding new props to native-stack --> - [X] https://github.com/software-mansion/react-native-screens/blob/main/src/types.tsx - [X] Ensured that CI passes --------- Co-authored-by: Kacper Kafara <kacper.kafara@swmansion.com>
1 parent d92233c commit 4fae58d

File tree

8 files changed

+51
-30
lines changed

8 files changed

+51
-30
lines changed

android/src/main/java/com/swmansion/rnscreens/SearchBarManager.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,13 +113,13 @@ class SearchBarManager : ViewGroupManager<SearchBarView>(), RNSSearchBarManagerI
113113
override fun getExportedCustomDirectEventTypeConstants(): Map<String, Any>? {
114114
return MapBuilder.of(
115115
SearchBarBlurEvent.EVENT_NAME,
116-
MapBuilder.of("registrationName", "onBlur"),
116+
MapBuilder.of("registrationName", "onSearchBlur"),
117117
SearchBarChangeTextEvent.EVENT_NAME,
118118
MapBuilder.of("registrationName", "onChangeText"),
119119
SearchBarCloseEvent.EVENT_NAME,
120120
MapBuilder.of("registrationName", "onClose"),
121121
SearchBarFocusEvent.EVENT_NAME,
122-
MapBuilder.of("registrationName", "onFocus"),
122+
MapBuilder.of("registrationName", "onSearchFocus"),
123123
SearchBarOpenEvent.EVENT_NAME,
124124
MapBuilder.of("registrationName", "onOpen"),
125125
SearchBarSearchButtonPressEvent.EVENT_NAME,

android/src/main/java/com/swmansion/rnscreens/events/SearchBarBlurEvent.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ class SearchBarBlurEvent(surfaceId: Int, viewId: Int) : Event<SearchBarBlurEvent
1313
override fun getEventData(): WritableMap? = Arguments.createMap()
1414

1515
companion object {
16-
const val EVENT_NAME = "topBlur"
16+
const val EVENT_NAME = "topSearchBlur"
1717
}
1818
}

android/src/main/java/com/swmansion/rnscreens/events/SearchBarFocusEvent.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ class SearchBarFocusEvent(surfaceId: Int, viewId: Int) : Event<SearchBarFocusEve
1313
override fun getEventData(): WritableMap? = Arguments.createMap()
1414

1515
companion object {
16-
const val EVENT_NAME = "topFocus"
16+
const val EVENT_NAME = "topSearchFocus"
1717
}
1818
}

ios/RNSSearchBar.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@
2929

3030
#ifdef RCT_NEW_ARCH_ENABLED
3131
#else
32-
@property (nonatomic, copy) RCTBubblingEventBlock onChangeText;
33-
@property (nonatomic, copy) RCTBubblingEventBlock onCancelButtonPress;
34-
@property (nonatomic, copy) RCTBubblingEventBlock onSearchButtonPress;
35-
@property (nonatomic, copy) RCTBubblingEventBlock onFocus;
36-
@property (nonatomic, copy) RCTBubblingEventBlock onBlur;
32+
@property (nonatomic, copy) RCTDirectEventBlock onChangeText;
33+
@property (nonatomic, copy) RCTDirectEventBlock onCancelButtonPress;
34+
@property (nonatomic, copy) RCTDirectEventBlock onSearchButtonPress;
35+
@property (nonatomic, copy) RCTDirectEventBlock onSearchFocus;
36+
@property (nonatomic, copy) RCTDirectEventBlock onSearchBlur;
3737
#endif
3838

3939
@end

ios/RNSSearchBar.mm

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,11 @@ - (void)emitOnFocusEvent
6767
#ifdef RCT_NEW_ARCH_ENABLED
6868
if (_eventEmitter != nullptr) {
6969
std::dynamic_pointer_cast<const react::RNSSearchBarEventEmitter>(_eventEmitter)
70-
->onFocus(react::RNSSearchBarEventEmitter::OnFocus{});
70+
->onSearchFocus(react::RNSSearchBarEventEmitter::OnSearchFocus{});
7171
}
7272
#else
73-
if (self.onFocus) {
74-
self.onFocus(@{});
73+
if (self.onSearchFocus) {
74+
self.onSearchFocus(@{});
7575
}
7676
#endif
7777
}
@@ -81,11 +81,11 @@ - (void)emitOnBlurEvent
8181
#ifdef RCT_NEW_ARCH_ENABLED
8282
if (_eventEmitter != nullptr) {
8383
std::dynamic_pointer_cast<const react::RNSSearchBarEventEmitter>(_eventEmitter)
84-
->onBlur(react::RNSSearchBarEventEmitter::OnBlur{});
84+
->onSearchBlur(react::RNSSearchBarEventEmitter::OnSearchBlur{});
8585
}
8686
#else
87-
if (self.onBlur) {
88-
self.onBlur(@{});
87+
if (self.onSearchBlur) {
88+
self.onSearchBlur(@{});
8989
}
9090
#endif
9191
}
@@ -414,11 +414,11 @@ - (UIView *)view
414414
RCT_EXPORT_VIEW_PROPERTY(cancelButtonText, NSString)
415415
RCT_EXPORT_VIEW_PROPERTY(placement, RNSSearchBarPlacement)
416416

417-
RCT_EXPORT_VIEW_PROPERTY(onChangeText, RCTBubblingEventBlock)
418-
RCT_EXPORT_VIEW_PROPERTY(onCancelButtonPress, RCTBubblingEventBlock)
419-
RCT_EXPORT_VIEW_PROPERTY(onSearchButtonPress, RCTBubblingEventBlock)
420-
RCT_EXPORT_VIEW_PROPERTY(onFocus, RCTBubblingEventBlock)
421-
RCT_EXPORT_VIEW_PROPERTY(onBlur, RCTBubblingEventBlock)
417+
RCT_EXPORT_VIEW_PROPERTY(onChangeText, RCTDirectEventBlock)
418+
RCT_EXPORT_VIEW_PROPERTY(onCancelButtonPress, RCTDirectEventBlock)
419+
RCT_EXPORT_VIEW_PROPERTY(onSearchButtonPress, RCTDirectEventBlock)
420+
RCT_EXPORT_VIEW_PROPERTY(onSearchFocus, RCTDirectEventBlock)
421+
RCT_EXPORT_VIEW_PROPERTY(onSearchBlur, RCTDirectEventBlock)
422422

423423
#ifndef RCT_NEW_ARCH_ENABLED
424424

src/components/SearchBar.tsx

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,18 @@ import { View } from 'react-native';
99
// Native components
1010
import SearchBarNativeComponent, {
1111
Commands as SearchBarNativeCommands,
12+
NativeProps as SearchBarNativeProps,
13+
SearchBarEvent,
14+
SearchButtonPressedEvent,
15+
ChangeTextEvent,
1216
} from '../fabric/SearchBarNativeComponent';
17+
import { DirectEventHandler } from 'react-native/Libraries/Types/CodegenTypes';
1318

14-
export const NativeSearchBar: React.ComponentType<SearchBarProps> &
19+
export const NativeSearchBar: React.ComponentType<
20+
SearchBarNativeProps & { ref?: React.RefObject<SearchBarCommands> }
21+
> &
1522
typeof NativeSearchBarCommands =
16-
SearchBarNativeComponent as unknown as React.ComponentType<SearchBarProps> &
23+
SearchBarNativeComponent as unknown as React.ComponentType<SearchBarNativeProps> &
1724
SearchBarCommandsType;
1825
export const NativeSearchBarCommands: SearchBarCommandsType =
1926
SearchBarNativeCommands as SearchBarCommandsType;
@@ -76,7 +83,21 @@ function SearchBar(props: SearchBarProps, ref: React.Ref<SearchBarCommands>) {
7683
return View as unknown as React.ReactNode;
7784
}
7885

79-
return <NativeSearchBar {...props} ref={searchBarRef} />;
86+
return (
87+
<NativeSearchBar
88+
ref={searchBarRef}
89+
{...props}
90+
onSearchFocus={props.onFocus as DirectEventHandler<SearchBarEvent>}
91+
onSearchBlur={props.onBlur as DirectEventHandler<SearchBarEvent>}
92+
onSearchButtonPress={
93+
props.onSearchButtonPress as DirectEventHandler<SearchButtonPressedEvent>
94+
}
95+
onCancelButtonPress={
96+
props.onCancelButtonPress as DirectEventHandler<SearchBarEvent>
97+
}
98+
onChangeText={props.onChangeText as DirectEventHandler<ChangeTextEvent>}
99+
/>
100+
);
80101
}
81102

82103
export default React.forwardRef<SearchBarCommands, SearchBarProps>(SearchBar);

src/fabric/SearchBarNativeComponent.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,23 @@ import type {
77
} from 'react-native/Libraries/Types/CodegenTypes';
88
import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands';
99

10-
type SearchBarEvent = Readonly<{}>;
10+
export type SearchBarEvent = Readonly<{}>;
1111

12-
type SearchButtonPressedEvent = Readonly<{
12+
export type SearchButtonPressedEvent = Readonly<{
1313
text?: string;
1414
}>;
1515

16-
type ChangeTextEvent = Readonly<{
16+
export type ChangeTextEvent = Readonly<{
1717
text?: string;
1818
}>;
1919

2020
type SearchBarPlacement = 'automatic' | 'inline' | 'stacked';
2121

2222
type AutoCapitalizeType = 'none' | 'words' | 'sentences' | 'characters';
2323

24-
interface NativeProps extends ViewProps {
25-
onFocus?: DirectEventHandler<SearchBarEvent> | null;
26-
onBlur?: DirectEventHandler<SearchBarEvent> | null;
24+
export interface NativeProps extends ViewProps {
25+
onSearchFocus?: DirectEventHandler<SearchBarEvent> | null;
26+
onSearchBlur?: DirectEventHandler<SearchBarEvent> | null;
2727
onSearchButtonPress?: DirectEventHandler<SearchButtonPressedEvent> | null;
2828
onCancelButtonPress?: DirectEventHandler<SearchBarEvent> | null;
2929
onChangeText?: DirectEventHandler<ChangeTextEvent> | null;

0 commit comments

Comments
 (0)