Skip to content

Commit 09eafec

Browse files
committed
InputRowRadioButtons [nfc]: Add optional search bar, to be used soon
1 parent bb0ec43 commit 09eafec

File tree

2 files changed

+39
-5
lines changed

2 files changed

+39
-5
lines changed

src/common/InputRowRadioButtons.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ type Props<TItemKey> = $ReadOnly<{|
5555
items: $ReadOnlyArray<Item<TItemKey>>,
5656

5757
onValueChange: (newValue: TItemKey) => void,
58+
59+
/**
60+
* Whether to offer a search bar at the top for filtering.
61+
*
62+
* The query is checked against items' `title` and `subtitle`.
63+
*/
64+
search?: boolean,
5865
|}>;
5966

6067
/**
@@ -71,7 +78,7 @@ type Props<TItemKey> = $ReadOnly<{|
7178
export default function InputRowRadioButtons<TItemKey: string | number>(
7279
props: Props<TItemKey>,
7380
): Node {
74-
const { navigation, label, description, valueKey, items, onValueChange } = props;
81+
const { navigation, label, description, valueKey, items, onValueChange, search } = props;
7582

7683
const screenKey: string = useRef(`selectable-options-${randString()}`).current;
7784

@@ -97,8 +104,9 @@ export default function InputRowRadioButtons<TItemKey: string | number>(
97104
navigation.goBack();
98105
onValueChange(itemKey);
99106
},
107+
search,
100108
}),
101-
[navigation, label, description, items, valueKey, onValueChange],
109+
[navigation, label, description, items, valueKey, onValueChange, search],
102110
);
103111

104112
const handleRowPressed = useCallback(() => {

src/common/SelectableOptionsScreen.js

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import Screen from './Screen';
1111
import SelectableOptionRow from './SelectableOptionRow';
1212
import ZulipTextIntl from './ZulipTextIntl';
1313
import { createStyleSheet } from '../styles';
14+
import { TranslationContext } from '../boot/TranslationProvider';
1415

1516
type Item<TKey> = $ReadOnly<{|
1617
key: TKey,
@@ -55,6 +56,8 @@ type Props<TItemKey> = $ReadOnly<{|
5556
// variable. Better to offer this explicit, side-effect-free way for
5657
// the data to flow where it should, when it should.
5758
onRequestSelectionChange: (itemKey: TItemKey, requestedValue: boolean) => void,
59+
60+
search?: boolean,
5861
|},
5962
>,
6063
|}>;
@@ -82,7 +85,30 @@ export default function SelectableOptionsScreen<TItemKey: string | number>(
8285
props: Props<TItemKey>,
8386
): Node {
8487
const { route } = props;
85-
const { title, description, items, onRequestSelectionChange } = route.params;
88+
const { title, description, items, onRequestSelectionChange, search } = route.params;
89+
90+
const _ = React.useContext(TranslationContext);
91+
92+
const [filter, setFilter] = React.useState('');
93+
const filteredItems =
94+
filter.trim() === ''
95+
? items
96+
: items.filter(item => {
97+
// TODO: Is this the best way to filter? Where `title` and
98+
// `subtitle` are present, behavior is matched to the language
99+
// picker.
100+
101+
/* eslint-disable prefer-template */
102+
const itemData =
103+
_(item.subtitle ?? { text: '{_}', values: { _: '' } }).toUpperCase()
104+
+ ' '
105+
+ _(item.title ?? { text: '{_}', values: { _: '' } }).toUpperCase();
106+
/* eslint-enable prefer-template */
107+
108+
const filterData = filter.toUpperCase();
109+
110+
return itemData.includes(filterData);
111+
});
86112

87113
const styles = useMemo(
88114
() =>
@@ -93,13 +119,13 @@ export default function SelectableOptionsScreen<TItemKey: string | number>(
93119
);
94120

95121
return (
96-
<Screen title={title} scrollEnabled>
122+
<Screen title={title} scrollEnabled search={search} searchBarOnChange={setFilter}>
97123
{description != null && (
98124
<View style={styles.descriptionWrapper}>
99125
<ZulipTextIntl text={description} />
100126
</View>
101127
)}
102-
{items.map(item => (
128+
{filteredItems.map(item => (
103129
<SelectableOptionRow
104130
key={item.key}
105131
itemKey={item.key}

0 commit comments

Comments
 (0)