Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Adds search bar on Android #1166

Merged
merged 70 commits into from
Nov 12, 2021
Merged
Show file tree
Hide file tree
Changes from 55 commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
f2f8f7e
Work in progress
Sep 29, 2021
254c49d
Adds basic PoC of search bar
Oct 6, 2021
ca7709a
Revert ScreenFragment
Oct 6, 2021
284f69f
Cleans code
Oct 6, 2021
ce62473
Reverts unnecessary changes
Oct 6, 2021
1dc80d3
Adds missing imports
Oct 6, 2021
1f0b1a9
Adds query listeners
Oct 7, 2021
0e0fd45
Adds onChangeText event
Oct 7, 2021
46db93c
Adds onSubmit event
Oct 7, 2021
c9bf847
Adds autocapiatlize, input type props
Oct 8, 2021
ddc9d57
Adds more complex test
Oct 8, 2021
872aa60
Adds onAfterUpdateTransaction and changes names
Oct 11, 2021
ede3bc3
Formats ScreenStackFragment
Oct 11, 2021
c896ebc
Revert ScreenStackHeaderConfig.kt
Oct 11, 2021
efc82f9
Adds platform check for search bar
Oct 11, 2021
ef993d8
Adds custom toolbar
Oct 11, 2021
74d5020
Removes long parent chain
Oct 11, 2021
397e5d2
Adds focus listener
Oct 11, 2021
8cc2769
Format with yarn format
Oct 11, 2021
a4b16a6
Adds platform distinction
Oct 11, 2021
6fa4431
Changes onTextSubmit to onSearchButtonPress
Oct 11, 2021
144feb0
Adds text color and placeholder
Oct 11, 2021
a067984
Removes shouldShowSearchbar
Oct 12, 2021
fb79fc5
Remove unecessary new search bar component
Oct 12, 2021
740c49a
Changes when to if and reformats code
Oct 12, 2021
f30317a
Applies code review suggestions
Oct 12, 2021
21e7f88
Adds undefined checks back for searchbar
Oct 12, 2021
d204017
CHanges isSearchBarAvailable to const
Oct 12, 2021
06c3dac
Formats code
Oct 12, 2021
89198ed
Renames isSearchBarAvailableForCurrentPlatform
Oct 12, 2021
2e8c2e4
Adds tint color
Oct 12, 2021
5ae0cad
Adds back button overriding
Oct 18, 2021
2894907
Adds override back button prop
Oct 18, 2021
83441e6
Adds autofocus and sample to test it
Oct 19, 2021
5af5a47
Updates types files
Oct 19, 2021
32d9806
Adds customization example and fixes tint prop
Oct 19, 2021
146756a
Fixes to short search view issue
Oct 19, 2021
c8ded3e
Adds input types to types
Oct 19, 2021
3734340
Adds more example screens
Oct 19, 2021
10e843e
Removes test file
Oct 19, 2021
4927452
Applies suggestions from code review
Oct 19, 2021
770532c
Extracts back press overrider
Oct 19, 2021
6c0b716
Formates code
Oct 19, 2021
a46a9b9
Refactors code
Oct 19, 2021
07f2cd7
Refactors useBackPressSubscription
Oct 19, 2021
ddc62db
Adds comment explaining exitApp
Oct 19, 2021
9329a7d
Adds search icon
Oct 20, 2021
dc3f72a
Combines search bar tests into one file
Oct 20, 2021
a30852f
Changes Android... folder to Test1166
Oct 20, 2021
d53476e
Adds iconified description and formats code
Oct 20, 2021
986fa6f
Updates docs
Oct 21, 2021
70cff71
Updates comments in types
Oct 21, 2021
f9c5c16
Updates more comments
Oct 21, 2021
5a941a9
Makes conditions simpler and cleans code
Oct 27, 2021
164262d
Merge branch 'master' into @ubax/android-searchbar
Oct 27, 2021
4b7e653
Applies code style suggestions from review
Nov 1, 2021
25be525
Adds comment to useBackPressSubscription
Nov 1, 2021
e892bef
Adds setting mAreListenersSet
Nov 1, 2021
3e5d303
Moves platform check to root
Nov 1, 2021
9b99639
Applies suggestions from review
Nov 2, 2021
18a6837
Moves isSearchBarAvailableForCurrentPlatform to index
Nov 2, 2021
41be2db
Fixes order of search bar props
Nov 2, 2021
d80f8c2
Update src/types.tsx
Ubax Nov 2, 2021
91d9866
Adds searchbar to v4
Nov 2, 2021
4f088e2
Sort props in lexicographica order again
Nov 2, 2021
57770c2
Extracts function to utils
Nov 2, 2021
040b7c5
Moves onAttached and onDetached to props
Nov 3, 2021
2c7962e
Removes search bar v4 code
Nov 3, 2021
91db4c7
Merge branch 'master' into @ubax/android-searchbar
Nov 3, 2021
40603d4
Add go back buttons to tests & format
kacperkapusciak Nov 5, 2021
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
1 change: 1 addition & 0 deletions TestsExample/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import Test1096 from './src/Test1096';
import Test1153 from './src/Test1153';
import Test1157 from './src/Test1157';
import Test1162 from './src/Test1162';
import Test1166 from './src/Test1166';

export default function App() {
return (
Expand Down
Binary file added TestsExample/assets/search_black.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added TestsExample/assets/search_white.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
73 changes: 73 additions & 0 deletions TestsExample/src/Test1166/AndroidDifferentScreenSearch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import * as React from 'react';
import {Image, Text, TouchableOpacity, View} from 'react-native';
import {ParamListBase} from '@react-navigation/native';
import {
createNativeStackNavigator,
NativeStackNavigationProp,
} from 'react-native-screens/native-stack';

const Stack = createNativeStackNavigator();

export default function App() {
return (
<Stack.Navigator screenOptions={{stackAnimation: 'none'}}>
<Stack.Screen name="First" component={First} />
<Stack.Screen name="Search" component={Second} options={{title: ''}} />
</Stack.Navigator>
);
}

function SearchIconButton(props: {onPress: () => void}) {
return (
<TouchableOpacity onPress={props.onPress}>
<Image source={require('../../assets/search_black.png')} />
</TouchableOpacity>
);
}

function First({
navigation,
}: {
navigation: NativeStackNavigationProp<ParamListBase>;
}) {
React.useEffect(() => {
function HeaderSearchButton() {
return <SearchIconButton onPress={() => navigation.navigate('Search')} />;
}
navigation.setOptions({
title: 'Home',
headerRight: HeaderSearchButton,
stackAnimation: 'none',
});
}, [navigation]);
return <View style={{flex: 1, backgroundColor: '#FFF'}}></View>;
}

function Second({
navigation,
}: {
navigation: NativeStackNavigationProp<ParamListBase>;
}) {
const [text, setText] = React.useState('');

React.useEffect(() => {
navigation.setOptions({
title: '',
searchBar: {
autoFocus: true,
onClose: () => navigation.navigate('First'),
onChangeText: (e) => setText(e.nativeEvent.text),
},
stackAnimation: 'none',
});
}, []);
Ubax marked this conversation as resolved.
Show resolved Hide resolved

return (
<View style={{flex: 1, backgroundColor: '#FFF', padding: 12}}>
<Text style={{fontSize: 24, fontWeight: '600', marginBottom: 12}}>
Search Results
</Text>
<Text>{text}</Text>
</View>
);
}
28 changes: 28 additions & 0 deletions TestsExample/src/Test1166/AndroidSearchBarCustomization.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as React from 'react';
import {View} from 'react-native';
import {createNativeStackNavigator} from 'react-native-screens/native-stack';

const Stack = createNativeStackNavigator();

export default function App() {
return (
<Stack.Navigator>
<Stack.Screen
name="First"
component={First}
options={{
searchBar: {
autoCapitalize: 'sentences',
textColor: '#FF00FF',
barTintColor: '#00FF00',
placeholder: 'Test...',
},
}}
/>
</Stack.Navigator>
);
}

function First() {
return <View style={{flex: 1, backgroundColor: '#FFF'}}></View>;
}
46 changes: 46 additions & 0 deletions TestsExample/src/Test1166/AndroidSearchBarEvents.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import * as React from 'react';
import {Text, View} from 'react-native';
import {ParamListBase} from '@react-navigation/native';
import {
createNativeStackNavigator,
NativeStackNavigationProp,
} from 'react-native-screens/native-stack';
import {SearchBarProps} from 'react-native-screens';

const Stack = createNativeStackNavigator();

export default function App() {
return (
<Stack.Navigator>
<Stack.Screen name="First" component={First} />
</Stack.Navigator>
);
}

function First({
navigation,
}: {
navigation: NativeStackNavigationProp<ParamListBase>;
}) {
const [events, setEvents] = React.useState<string[]>([]);
React.useEffect(() => {
const searchBar: SearchBarProps = {
onSearchButtonPress: () => setEvents((prev) => [...prev, 'Search']),
onBlur: () => setEvents((prev) => [...prev, 'Blur']),
onClose: () => setEvents((prev) => [...prev, 'Close']),
onOpen: () => setEvents((prev) => [...prev, 'Open']),
onFocus: () => setEvents((prev) => [...prev, 'Focus']),
};
navigation.setOptions({
searchBar: searchBar,
});
}, []);
Ubax marked this conversation as resolved.
Show resolved Hide resolved

return (
<View style={{flex: 1, backgroundColor: '#FFF', padding: 12}}>
{events.map((event, i) => (
<Text key={i}>{event}</Text>
))}
</View>
);
}
74 changes: 74 additions & 0 deletions TestsExample/src/Test1166/AndroidSearchTypes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import * as React from 'react';
import {Button, View} from 'react-native';
import {ParamListBase} from '@react-navigation/native';
import {
createNativeStackNavigator,
NativeStackNavigationProp,
} from 'react-native-screens/native-stack';

const Stack = createNativeStackNavigator();

export default function App() {
return (
<Stack.Navigator screenOptions={{stackAnimation: 'none'}}>
<Stack.Screen name="First" component={First} />
<Stack.Screen
name="number"
component={Second}
options={{searchBar: {autoFocus: true, inputType: 'number'}}}
/>
<Stack.Screen
name="email"
component={Second}
options={{searchBar: {autoFocus: true, inputType: 'email'}}}
/>
<Stack.Screen
name="phone"
component={Second}
options={{searchBar: {autoFocus: true, inputType: 'phone'}}}
/>
</Stack.Navigator>
);
}

function First({
navigation,
}: {
navigation: NativeStackNavigationProp<ParamListBase>;
}) {
React.useEffect(() => {
function HeaderSearchButtons() {
return (
<View style={{flexDirection: 'row'}}>
<Button title="N" onPress={() => navigation.navigate('number')} />
<Button title="E" onPress={() => navigation.navigate('email')} />
<Button title="P" onPress={() => navigation.navigate('phone')} />
</View>
);
}
navigation.setOptions({
title: 'Home',
headerRight: HeaderSearchButtons,
stackAnimation: 'none',
});
}, [navigation]);
return (
<View style={{flex: 1, backgroundColor: '#FFF', padding: 12}}>
<Button title="Number" onPress={() => navigation.navigate('number')} />
<Button title="Email" onPress={() => navigation.navigate('email')} />
<Button title="Phone" onPress={() => navigation.navigate('phone')} />
</View>
);
}

function Second({
navigation,
}: {
navigation: NativeStackNavigationProp<ParamListBase>;
}) {
return (
<View style={{flex: 1, backgroundColor: '#FFF', padding: 12}}>
<Button title="Go back" onPress={() => navigation.pop()} />
</View>
);
}
68 changes: 68 additions & 0 deletions TestsExample/src/Test1166/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React from 'react';
import {Button, View} from 'react-native';
import {NavigationContainer, ParamListBase} from '@react-navigation/native';
import {
createNativeStackNavigator,
NativeStackNavigationProp,
} from 'react-native-screens/native-stack';
import AndroidDifferentScreenSearch from './AndroidDifferentScreenSearch';
import AndroidSearchBarCustomization from './AndroidSearchBarCustomization';
import AndroidSearchTypes from './AndroidSearchTypes';
import AndroidSearchBarEvents from './AndroidSearchBarEvents';

const Stack = createNativeStackNavigator();

export default function App() {
return (
<NavigationContainer>
<Stack.Navigator screenOptions={{headerShown: false}}>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen
name="Customization"
component={AndroidSearchBarCustomization}
/>
<Stack.Screen name="InputTypes" component={AndroidSearchTypes} />
<Stack.Screen name="Events" component={AndroidSearchBarEvents} />
<Stack.Screen
name="DifferentSearchScreen"
component={AndroidDifferentScreenSearch}
/>
</Stack.Navigator>
</NavigationContainer>
);
}

function Home({
navigation,
}: {
navigation: NativeStackNavigationProp<ParamListBase>;
}) {
return (
<View style={{flex: 1, backgroundColor: '#FFF', justifyContent: 'center', padding: 12}}>
<View style={{marginBottom: 12}}>
<Button
title="Search bar customization"
onPress={() => navigation.navigate('Customization')}
/>
</View>
<View style={{marginBottom: 12}}>
<Button
title="Search bar input types"
onPress={() => navigation.navigate('InputTypes')}
/>
</View>
<View style={{marginBottom: 12}}>
<Button
title="Search bar events"
onPress={() => navigation.navigate('Events')}
/>
</View>
<View style={{marginBottom: 12}}>
<Button
title="Search bar different search results screen"
onPress={() => navigation.navigate('DifferentSearchScreen')}
/>
</View>
</View>
);
}
71 changes: 71 additions & 0 deletions android/src/main/java/com/swmansion/rnscreens/CustomSearchView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package com.swmansion.rnscreens

import android.content.Context
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.widget.SearchView
import androidx.fragment.app.Fragment

class CustomSearchView(context: Context?, fragment: Fragment) : SearchView(context) {
/*
CustomSearchView uses some variables from SearchView. They are listed below with links to documentation
isIconified - https://developer.android.com/reference/android/widget/SearchView#setIconified(boolean)
maxWidth - https://developer.android.com/reference/android/widget/SearchView#setMaxWidth(int)
setOnSearchClickListener - https://developer.android.com/reference/android/widget/SearchView#setOnSearchClickListener(android.view.View.OnClickListener)
setOnCloseListener - https://developer.android.com/reference/android/widget/SearchView#setOnCloseListener(android.widget.SearchView.OnCloseListener)
*/
private var mCustomOnCloseListener: OnCloseListener? = null
private var mCustomOnSearchClickedListener: OnClickListener? = null

private var mOnBackPressedCallback: OnBackPressedCallback =
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
isIconified = true
}
}
private val backPressOverrider = FragmentBackPressOverrider(fragment, mOnBackPressedCallback)
var overrideBackAction: Boolean
set(value) {
backPressOverrider.overrideBackAction = value
}
get() = backPressOverrider.overrideBackAction

fun focus() {
isIconified = false
requestFocusFromTouch()
}

override fun setOnCloseListener(listener: OnCloseListener?) {
mCustomOnCloseListener = listener
}

override fun setOnSearchClickListener(listener: OnClickListener?) {
mCustomOnSearchClickedListener = listener
}
WoLewicki marked this conversation as resolved.
Show resolved Hide resolved

override fun onAttachedToWindow() {
super.onAttachedToWindow()
if (!isIconified) {
backPressOverrider.maybeAddBackCallback()
}
}

override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
backPressOverrider.removeBackCallbackIfAdded()
}

init {
super.setOnSearchClickListener { v ->
mCustomOnSearchClickedListener?.onClick(v)
backPressOverrider.maybeAddBackCallback()
}

super.setOnCloseListener {
val result = mCustomOnCloseListener?.onClose() ?: false
backPressOverrider.removeBackCallbackIfAdded()
result
}

maxWidth = Integer.MAX_VALUE
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.swmansion.rnscreens

import android.content.Context
import androidx.appcompat.widget.Toolbar

// This class is used to store config closer to search bar
WoLewicki marked this conversation as resolved.
Show resolved Hide resolved
open class CustomToolbar(context: Context, val config: ScreenStackHeaderConfig) : Toolbar(context)
Ubax marked this conversation as resolved.
Show resolved Hide resolved
Loading