Skip to content

Commit

Permalink
feat: add cancelSearch command on SearchBar (software-mansion#1987)
Browse files Browse the repository at this point in the history
## Description

Add cancelSearch native command to SearchBar imperative API.
The command provides a way for developers to programmatically cancel an
ongoing search operation and clear the search input.

## Changes

- Added JS impl
- Added Native impl (both Android & iOS)
  - RNSSearchBar.mm
  - CustomSearchView.kt
  - SearchBarManager.kt
  - SearchBarView.kt
- Updated JS types
- Updated docs

## Test code and steps to reproduce

Test1097 in both FabricTestExample & TestsExample

## 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/guides/GUIDE_FOR_LIBRARY_AUTHORS.md
- [x]
https://github.com/software-mansion/react-native-screens/blob/main/native-stack/README.md
- [x]
https://github.com/software-mansion/react-native-screens/blob/main/src/types.tsx
- [ ]
https://github.com/software-mansion/react-native-screens/blob/main/src/native-stack/types.tsx
- [ ] Ensured that CI passes

---------

Co-authored-by: Kacper Kafara <[email protected]>
  • Loading branch information
2 people authored and ja1ns committed Oct 9, 2024
1 parent 99444cb commit 6fbd90f
Show file tree
Hide file tree
Showing 12 changed files with 56 additions and 3 deletions.
6 changes: 5 additions & 1 deletion Example/src/screens/SearchBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ const MainScreen = ({ navigation }: MainScreenProps): JSX.Element => {
<Button onPress={() => searchBarRef.current?.focus()} title="Focus" />
<Button
onPress={() => searchBarRef.current?.clearText()}
title="Clear Text"
title="Clear text"
/>
<Button
onPress={() => searchBarRef.current?.toggleCancelButton(true)}
Expand All @@ -187,6 +187,10 @@ const MainScreen = ({ navigation }: MainScreenProps): JSX.Element => {
onPress={() => searchBarRef.current?.toggleCancelButton(false)}
title="Hide cancel"
/>
<Button
onPress={() => searchBarRef.current?.cancelSearch()}
title="Cancel search"
/>
<Text style={styles.heading}>Other</Text>
<Button
onPress={() => navigation.navigate('Search')}
Expand Down
4 changes: 4 additions & 0 deletions FabricTestExample/src/Test1097.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,10 @@ function Third({ navigation }: { navigation: NavigationProp<ParamListBase> }) {
title="Set 'sometext' text"
onPress={() => searchBarRef.current?.setText('sometext')}
/>
<Button
title="Cancel search"
onPress={() => searchBarRef.current?.cancelSearch()}
/>
<Button
title="Tap me for the first screen"
onPress={() => navigation.navigate('First')}
Expand Down
4 changes: 4 additions & 0 deletions TestsExample/src/Test1097.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,10 @@ function Third({ navigation }: { navigation: NavigationProp<ParamListBase> }) {
title="Set 'sometext' text"
onPress={() => searchBarRef.current?.setText('sometext')}
/>
<Button
title="Cancel search"
onPress={() => searchBarRef.current?.cancelSearch()}
/>
<Button
title="Tap me for the first screen"
onPress={() => navigation.navigate('First')}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ class CustomSearchView(context: Context, fragment: Fragment) : SearchView(contex

fun setText(text: String) = setQuery(text, false)

fun cancelSearch() {
clearText()
setIconified(true)
}

override fun setOnCloseListener(listener: OnCloseListener?) {
mCustomOnCloseListener = listener
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ class SearchBarManager : ViewGroupManager<SearchBarView>() {
"clearText" -> root.handleClearTextJsRequest()
"toggleCancelButton" -> root.handleToggleCancelButtonJsRequest(false) // just a dummy argument
"setText" -> root.handleSetTextJsRequest(args?.getString(0))
"cancelSearch" -> root.handleCancelSearchJsRequest()
else -> throw JSApplicationIllegalArgumentException("Unsupported native command received: $commandId")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ class SearchBarView(reactContext: ReactContext?) : ReactViewGroup(reactContext)
text?.let { screenStackFragment?.searchView?.setText(it) }
}

fun handleCancelSearchJsRequest() {
screenStackFragment?.searchView?.cancelSearch()
}

private fun setToolbarElementsVisibility(visibility: Int) {
for (i in 0..(headerConfig?.configSubviewsCount?.minus(1) ?: 0)) {
val subview = headerConfig?.getConfigSubview(i)
Expand Down
1 change: 1 addition & 0 deletions guides/GUIDE_FOR_LIBRARY_AUTHORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,7 @@ Allowed imperative actions on search bar are:
- `blur` - Function to remove focus from search bar.
- `clearText` - Function to clear text in search bar.
- `setText` - Function to set search bar's text to given value.
- `cancelSearch` - Function to cancel search in search bar.
- `toggleCancelButton` - Function toggle cancel button display near search bar. (iOS only)

Below is a list of properties that can be set with `ScreenStackHeaderConfig` component:
Expand Down
16 changes: 16 additions & 0 deletions ios/RNSSearchBar.mm
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,14 @@ - (void)setText:(NSString *)text
[_controller.searchBar setText:text];
}

- (void)cancelSearch
{
#if !TARGET_OS_TV
[self searchBarCancelButtonClicked:_controller.searchBar];
_controller.active = NO;
#endif
}

#pragma mark-- Fabric specific

#ifdef RCT_NEW_ARCH_ENABLED
Expand Down Expand Up @@ -446,6 +454,14 @@ - (UIView *)view
}];
}

RCT_EXPORT_METHOD(cancelSearch : (NSNumber *_Nonnull)reactTag)
{
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) {
RNSSearchBar *searchBar = viewRegistry[reactTag];
[searchBar cancelSearch];
}];
}

#endif /* !RCT_NEW_ARCH_ENABLED */

@end
Expand Down
1 change: 1 addition & 0 deletions native-stack/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,7 @@ A React ref to imperatively modify search bar. Supported actions:
* `blur` - remove focus from search bar
* `clearText` - clear text in search bar
* `setText` - set search bar's content to given string
* `cancelSearch` - cancel search in search bar.
* `toggleCancelButton` (iOS only) - toggle cancel button display near search bar.

### Events
Expand Down
2 changes: 2 additions & 0 deletions src/fabric/SearchBarNativeComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ interface NativeCommands {
flag: boolean
) => void;
setText: (viewRef: React.ElementRef<ComponentType>, text: string) => void;
cancelSearch: (viewRef: React.ElementRef<ComponentType>) => void;
}

export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
Expand All @@ -70,6 +71,7 @@ export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
'clearText',
'toggleCancelButton',
'setText',
'cancelSearch',
],
});

Expand Down
13 changes: 11 additions & 2 deletions src/index.native.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ type SearchBarCommandsType = {
viewRef: React.ElementRef<typeof ScreensNativeModules.NativeSearchBar>,
text: string
) => void;
cancelSearch: (
viewRef: React.ElementRef<typeof ScreensNativeModules.NativeSearchBar>
) => void;
};

// We initialize these lazily so that importing the module doesn't throw error when not linked
Expand Down Expand Up @@ -200,8 +203,8 @@ function DelayedFreeze({ freeze, children }: FreezeWrapperProps) {
});
return () => {
clearImmediate(id);
}
}, [freeze])
};
}, [freeze]);

return <Freeze freeze={freeze ? freezeState : false}>{children}</Freeze>;
}
Expand Down Expand Up @@ -483,6 +486,12 @@ class SearchBar extends React.Component<SearchBarProps> {
);
}

cancelSearch() {
this._callMethodWithRef(ref =>
ScreensNativeModules.NativeSearchBarCommands.cancelSearch(ref)
);
}

render() {
if (!isSearchBarAvailableForCurrentPlatform) {
console.warn(
Expand Down
2 changes: 2 additions & 0 deletions src/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export type SearchBarCommands = {
clearText: () => void;
toggleCancelButton: (show: boolean) => void;
setText: (text: string) => void;
cancelSearch: () => void;
};

export type StackPresentationTypes =
Expand Down Expand Up @@ -552,6 +553,7 @@ export interface SearchBarProps {
* * `blur` - removes focus from the search bar
* * `clearText` - removes any text present in the search bar input field
* * `setText` - sets the search bar's content to given value
* * `cancelSearch` - cancel search in search bar.
* * `toggleCancelButton` - depending on passed boolean value, hides or shows cancel button (iOS only)
*/
ref?: React.RefObject<SearchBarCommands>;
Expand Down

0 comments on commit 6fbd90f

Please sign in to comment.