Skip to content

Added support for itemKey property in RNPickerSelect component #586

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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
194 changes: 95 additions & 99 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,14 +158,15 @@
}).concat(this.props.items);
const itemsChanged = !isEqual(prevState.items, items);

// update selectedItem if value prop is defined and differs from currently selected item
// update selectedItem if value or itemKey prop is defined and differs from currently selected item

Check failure on line 161 in src/index.js

View workflow job for this annotation

GitHub Actions / build

This line has a length of 103. Maximum allowed is 100
const { selectedItem, idx } = RNPickerSelect.getSelectedItem({
items,
key: this.props.itemKey,
value: this.props.value,
});
const selectedItemChanged =
!isEqual(this.props.value, undefined) && !isEqual(prevState.selectedItem, selectedItem);
(!isEqual(this.props.value, undefined) && !isEqual(prevState.selectedItem, selectedItem)) ||
(!isEqual(this.props.itemKey, prevProps.itemKey));

Check failure on line 169 in src/index.js

View workflow job for this annotation

GitHub Actions / build

Replace `(!isEqual(this.props.itemKey,·prevProps.itemKey)` with `!isEqual(this.props.itemKey,·prevProps.itemKey`

if (itemsChanged || selectedItemChanged) {
this.props.onValueChange(selectedItem.value, idx);
Expand Down Expand Up @@ -328,8 +329,7 @@
<View
style={[
defaultStyles.chevron,
this.isDarkTheme() ? defaultStyles.chevronDark : {},
this.isDarkTheme() ? style.chevronDark : style.chevron,
style.chevron,
defaultStyles.chevronUp,
style.chevronUp,
onUpArrow ? [defaultStyles.chevronActive, style.chevronActive] : {},
Expand All @@ -343,8 +343,7 @@
<View
style={[
defaultStyles.chevron,
this.isDarkTheme() ? defaultStyles.chevronDark : {},
this.isDarkTheme() ? style.chevronDark : style.chevron,
style.chevron,
defaultStyles.chevronDown,
style.chevronDown,
onDownArrow ? [defaultStyles.chevronActive, style.chevronActive] : {},
Expand All @@ -358,28 +357,41 @@
this.togglePicker(true, onDonePress, true);
}}
onPressIn={() => {
this.setState({ doneDepressed: true });
this.setState({
doneDepressed: true,
});
}}
onPressOut={() => {
this.setState({ doneDepressed: false });
}}
hitSlop={{
top: 4,
right: 4,
bottom: 4,
left: 4,
this.setState({
doneDepressed: false,
});
}}
hitSlop={{ top: 4, right: 4, bottom: 4, left: 4 }}
{...touchableDoneProps}
>
<View testID="needed_for_touchable">
<View
accessible
accessibilityRole="button"
accessibilityLabel={doneText}
style={[
defaultStyles.done,
this.isDarkTheme() ? defaultStyles.doneDark : {},
this.isDarkTheme() ? style.doneDark : style.done,
doneDepressed

Check failure on line 380 in src/index.js

View workflow job for this annotation

GitHub Actions / build

Replace `⏎················?·[defaultStyles.doneDepressed,·style.doneDepressed]⏎···············` with `·?·[defaultStyles.doneDepressed,·style.doneDepressed]`
? [defaultStyles.doneDepressed, style.doneDepressed]
: {},
]}
>
<Text
testID="done_text"
allowFontScaling={false}
adjustsFontSizeToFit={false}
style={[
defaultStyles.done,
this.isDarkTheme() ? defaultStyles.doneDark : {},
this.isDarkTheme() ? style.doneDark : style.done,
doneDepressed ? [defaultStyles.doneDepressed, style.doneDepressed] : {},
defaultStyles.doneText,
this.isDarkTheme() ? defaultStyles.doneTextDark : {},
this.isDarkTheme() ? style.doneTextDark : style.doneText,
doneDepressed

Check failure on line 392 in src/index.js

View workflow job for this annotation

GitHub Actions / build

Replace `⏎··················?·[defaultStyles.doneTextDepressed,·style.doneTextDepressed]⏎·················` with `·?·[defaultStyles.doneTextDepressed,·style.doneTextDepressed]`
? [defaultStyles.doneTextDepressed, style.doneTextDepressed]
: {},
]}
>
{doneText}
Expand All @@ -391,30 +403,32 @@
}

renderIcon() {
const { style, Icon } = this.props;
const { Icon } = this.props;

if (!Icon) {
return null;
if (Icon) {
return <Icon testID="icon_ios" />;
}

return (
<View testID="icon_container" style={[defaultStyles.iconContainer, style.iconContainer]}>
<Icon testID="icon" />
</View>
);
return null;
}

renderTextInputOrChildren() {
const { children, style, textInputProps } = this.props;
const { children, style, textInputProps, fixAndroidTouchableBug } = this.props;
const { selectedItem } = this.state;

const containerStyle =
Platform.OS === 'ios' ? style.inputIOSContainer : style.inputAndroidContainer;
const containerStyle = Platform.OS === 'android' && fixAndroidTouchableBug

Check failure on line 419 in src/index.js

View workflow job for this annotation

GitHub Actions / build

Replace `·Platform.OS·===·'android'·&&·fixAndroidTouchableBug⏎······?·{·height:·0,·width:·0,·flex:·1·}⏎·····` with `⏎······Platform.OS·===·'android'·&&·fixAndroidTouchableBug·?·{·height:·0,·width:·0,·flex:·1·}`
? { height: 0, width: 0, flex: 1 }
: {};

if (children) {
return (
<View pointerEvents="box-only" style={containerStyle}>
{children}
{React.Children.map(children, (child) =>
React.cloneElement(child, {

Check failure on line 427 in src/index.js

View workflow job for this annotation

GitHub Actions / build

Expected no linebreak before this expression
pointerEvents: 'none',
style: [child.props.style, this.getPlaceholderStyle()],
})
)}

Check failure on line 431 in src/index.js

View workflow job for this annotation

GitHub Actions / build

Unexpected newline before ')'
</View>
);
}
Expand All @@ -423,11 +437,8 @@
<View pointerEvents="box-only" style={containerStyle}>
<TextInput
testID="text_input"
style={[
Platform.OS === 'ios' ? style.inputIOS : style.inputAndroid,
this.getPlaceholderStyle(),
]}
value={selectedItem.inputLabel ? selectedItem.inputLabel : selectedItem.label}
style={[style.inputIOS, this.getPlaceholderStyle()]}
value={selectedItem.inputLabel || selectedItem.label}
ref={this.setInputRef}
editable={false}
{...textInputProps}
Expand All @@ -438,8 +449,8 @@
}

renderIOS() {
const { style, modalProps, pickerProps, touchableWrapperProps } = this.props;
const { doneText, modalProps, pickerProps, style } = this.props;
const { animationType, orientation, selectedItem, showPicker } = this.state;

Check failure on line 453 in src/index.js

View workflow job for this annotation

GitHub Actions / build

'orientation' is assigned a value but never used

return (
<View style={[defaultStyles.viewContainer, style.viewContainer]}>
Expand All @@ -449,39 +460,44 @@
this.togglePicker(true);
}}
activeOpacity={1}
{...touchableWrapperProps}
>
{this.renderTextInputOrChildren()}
</TouchableOpacity>

<Modal
testID="ios_modal"
visible={showPicker}
transparent
animationType={animationType}
supportedOrientations={['portrait', 'landscape']}
onDismiss={() => {
this.togglePicker(true, undefined, true);
}}
onRequestClose={() => {
this.togglePicker(true, undefined, true);
}}
onOrientationChange={this.onOrientationChange}
{...modalProps}
>
<TouchableOpacity
style={[defaultStyles.modalViewTop, style.modalViewTop]}
testID="ios_modal_top"
onPress={() => {
this.togglePicker(true);
this.togglePicker(true, undefined, true);
}}
/>
{this.renderInputAccessoryView()}
<View
style={[
defaultStyles.modalViewBottom,
this.isDarkTheme() ? defaultStyles.modalViewBottomDark : {},
{ height: orientation === 'portrait' ? 215 : 162 },
this.isDarkTheme() ? style.modalViewBottomDark : style.modalViewBottom,
]}
>
<Picker
testID="ios_picker"
onValueChange={this.onValueChange}
selectedValue={selectedItem.value}
onValueChange={this.onValueChange}
prompt={doneText}
{...pickerProps}
>
{this.renderPickerItems()}
Expand All @@ -493,67 +509,43 @@
}

renderAndroidHeadless() {
const {
disabled,
Icon,
style,
pickerProps,
onOpen,
touchableWrapperProps,
fixAndroidTouchableBug,
} = this.props;
const { pickerProps, style, disabled } = this.props;
const { selectedItem } = this.state;

const Component = fixAndroidTouchableBug ? View : TouchableOpacity;
return (
<Component
testID="android_touchable_wrapper"
onPress={onOpen}
activeOpacity={1}
{...touchableWrapperProps}
>
<View style={style.headlessAndroidContainer}>
{this.renderTextInputOrChildren()}
<Picker
style={[
Icon ? { backgroundColor: 'transparent' } : {}, // to hide native icon
defaultStyles.headlessAndroidPicker,
style.headlessAndroidPicker,
]}
testID="android_picker_headless"
enabled={!disabled}
onValueChange={this.onValueChange}
selectedValue={selectedItem.value}
{...pickerProps}
>
{this.renderPickerItems()}
</Picker>
</View>
</Component>
<View style={style.headlessAndroidContainer}>
{this.renderTextInputOrChildren()}

<Picker
style={{ position: 'absolute', top: 0, width: '100%', height: '100%' }}
testID="android_picker_headless"
selectedValue={selectedItem.value}
onValueChange={this.onValueChange}
enabled={!disabled}
{...pickerProps}
>
{this.renderPickerItems()}
</Picker>
</View>
);
}

renderAndroidNativePickerStyle() {
const { disabled, Icon, style, pickerProps } = this.props;
const { pickerProps, style, disabled } = this.props;
const { selectedItem } = this.state;

return (
<View style={[defaultStyles.viewContainer, style.viewContainer]}>
<Picker
style={[
Icon ? { backgroundColor: 'transparent' } : {}, // to hide native icon
style.inputAndroid,
this.getPlaceholderStyle(),
]}
style={[defaultStyles.inputAndroid, style.inputAndroid]}
testID="android_picker"
enabled={!disabled}
onValueChange={this.onValueChange}
selectedValue={selectedItem.value}
onValueChange={this.onValueChange}
enabled={!disabled}
{...pickerProps}
>
{this.renderPickerItems()}
</Picker>
{this.renderIcon()}
</View>
);
}
Expand All @@ -564,23 +556,27 @@

return (
<View style={[defaultStyles.viewContainer, style.viewContainer]}>
<Picker
style={[style.inputWeb]}
testID="web_picker"
enabled={!disabled}
onValueChange={this.onValueChange}
selectedValue={selectedItem.value}
<select
disabled={disabled}
onChange={(e) => {
this.onValueChange(e.target.value, e.target.selectedIndex);
}}
style={[defaultStyles.inputWeb, style.inputWeb]}
value={selectedItem.value}
{...pickerProps}
>
{this.renderPickerItems()}
</Picker>
{this.renderIcon()}
{this.renderPickerItems().map((item) => (
<option key={item.key} value={item.props.value}>
{item.props.label}
</option>
))}
</select>
</View>
);
}

render() {
const { children, useNativeAndroidPickerStyle } = this.props;
const { useNativeAndroidPickerStyle, fixAndroidTouchableBug } = this.props;

Check failure on line 579 in src/index.js

View workflow job for this annotation

GitHub Actions / build

'fixAndroidTouchableBug' is assigned a value but never used

if (Platform.OS === 'ios') {
return this.renderIOS();
Expand All @@ -590,11 +586,11 @@
return this.renderWeb();
}

if (children || !useNativeAndroidPickerStyle) {
return this.renderAndroidHeadless();
if (useNativeAndroidPickerStyle) {
return this.renderAndroidNativePickerStyle();
}

return this.renderAndroidNativePickerStyle();
return this.renderAndroidHeadless();
}
}

Expand Down
Loading