Skip to content

Commit dc7f690

Browse files
authoredFeb 22, 2018
Merge pull request #5 from lawnstarter/update-items-from-props
Update items from props
2 parents f1858fe + 0cdb41e commit dc7f690

File tree

7 files changed

+116
-56
lines changed

7 files changed

+116
-56
lines changed
 

‎.eslintrc.js

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module.exports = {
22
"extends": "airbnb",
33
"rules": {
4+
'no-use-before-define': 0,
45
'react/jsx-filename-extension': 0,
56
'object-curly-newline': [2, { 'consistent': true }]
67
}

‎README.md

+2-3
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ For either platform, you can alternatively pass down a child element that will b
1717
### Installing
1818

1919
`npm install react-native-picker-select`
20-
or
21-
`yarn add react-native-picker-select`
2220

2321
### Usage
2422

@@ -40,14 +38,15 @@ or
4038
**Optional Props**
4139
* `placeholder` - object
4240
* An override for the default placeholder object with a label of `Select an item...` and a value of `null`
41+
* An empty object can be used if you'd like to disable the placeholder entirely
4342
* `hideDoneBar` - boolean
4443
* For the iOS component, hides the bar with tabbing arrows and Done link to exit the modal. While this is typical on `<select>` elements on the web, the [interface guidelines](https://developer.apple.com/ios/human-interface-guidelines/controls/pickers/) does not include it.
4544
* `hideIcon` - boolean
4645
* For the iOS component, hides the floating downward arrow on the right side of the input box
4746
* `disabled` - boolean
4847
* Disables interaction with the component
4948
* `value` - any
50-
* Will attempt to locate a matching value from the `items` array by checking each item's `value` key. If found, it will update the component to show that item as selected. If the value is not found, it will default to the placeholder.
49+
* Will attempt to locate a matching value from the `items` array by checking each item's `value` property. If found, it will update the component to show that item as selected. If the value is not found, it will default to the first item.
5150
* `style` - object
5251
* Style overrides for most parts of the component. More details below.
5352

‎example/example-headless.js

+19-17
Original file line numberDiff line numberDiff line change
@@ -6,35 +6,37 @@ import {
66
} from 'react-native';
77
import RNPickerSelect from 'react-native-picker-select';
88

9-
import CustomButton from '.components/CustomButton'
9+
import CustomButton from '.components/CustomButton';
1010

1111
export default class App extends React.Component {
1212
constructor(props) {
1313
super(props);
1414

1515
this.state = {
1616
favColor: '',
17+
items: [
18+
{
19+
label: 'Red',
20+
value: 'red',
21+
},
22+
{
23+
label: 'Orange',
24+
value: 'orange',
25+
},
26+
{
27+
label: 'Blue',
28+
value: 'blue',
29+
},
30+
],
1731
};
1832
}
1933

2034
render() {
2135
return (
2236
<View style={styles.container}>
2337
<RNPickerSelect
24-
items={[
25-
{
26-
label: 'Red',
27-
value: 'red',
28-
},
29-
{
30-
label: 'Orange',
31-
value: 'orange',
32-
},
33-
{
34-
label: 'Blue',
35-
value: 'blue',
36-
},
37-
]}
38+
items={this.state.items}
39+
placeholder={{}}
3840
onSelect={
3941
(item) => {
4042
this.setState({
@@ -43,7 +45,7 @@ export default class App extends React.Component {
4345
}
4446
}
4547
>
46-
<CustomButton text="Select your favorite color"/>
48+
<CustomButton text="Select your favorite color" />
4749
</RNPickerSelect>
4850
</View>
4951
);
@@ -57,4 +59,4 @@ const styles = StyleSheet.create({
5759
justifyContent: 'center',
5860
paddingHorizontal: 10,
5961
},
60-
});
62+
});

‎example/example.js

+23-16
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,37 @@ export default class App extends React.Component {
1212

1313
this.state = {
1414
favColor: '',
15+
items: [
16+
{
17+
label: 'Red',
18+
value: 'red',
19+
},
20+
{
21+
label: 'Orange',
22+
value: 'orange',
23+
},
24+
{
25+
label: 'Blue',
26+
value: 'blue',
27+
},
28+
],
1529
};
1630
}
1731

18-
// if the component is using the optional `value` prop, the parent
19-
// has the abililty to both set the initial value and also update it
2032
componentDidMount() {
33+
// if the component is using the optional `value` prop, the parent
34+
// has the abililty to both set the initial value and also update it
2135
setTimeout(() => {
2236
this.setState({
2337
favColor: 'red',
2438
});
2539
}, 1000);
40+
41+
setTimeout(() => {
42+
this.setState({
43+
items: items.concat([{ value: 'purple', label: 'Purple' }]),
44+
});
45+
}, 2000);
2646
}
2747

2848
render() {
@@ -34,20 +54,7 @@ export default class App extends React.Component {
3454
label: 'Select a color...',
3555
value: null,
3656
}}
37-
items={[
38-
{
39-
label: 'Red',
40-
value: 'red',
41-
},
42-
{
43-
label: 'Orange',
44-
value: 'orange',
45-
},
46-
{
47-
label: 'Blue',
48-
value: 'blue',
49-
},
50-
]}
57+
items={this.state.items}
5158
onSelect={
5259
(item) => {
5360
this.setState({

‎package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-native-picker-select",
3-
"version": "1.0.2",
3+
"version": "1.1.0",
44
"description": "A Picker component for React Native which emulates the native <select> interfaces for each platform",
55
"license": "MIT",
66
"author": "Michael Lefkowitz <lefkowitz.michael@gmail.com>",

‎src/index.js

+15-6
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,12 @@ export default class RNPickerSelect extends PureComponent {
2222
this.placeholder = { label: props.placeholder, value: null };
2323
}
2424

25-
this.itemsWithPlaceholder = [this.placeholder].concat(props.items);
25+
this.noPlaceholder = isEqual(this.placeholder, {});
26+
this.items = this.noPlaceholder ? props.items : [this.placeholder].concat(props.items);
2627

2728
this.state = {
28-
items: this.itemsWithPlaceholder,
29-
selectedItem: this.itemsWithPlaceholder.find(item => isEqual(item.value, props.value)) || this.placeholder,
29+
items: this.items,
30+
selectedItem: this.items.find(item => isEqual(item.value, props.value)) || this.items[0],
3031
showPicker: false,
3132
};
3233

@@ -35,8 +36,16 @@ export default class RNPickerSelect extends PureComponent {
3536
}
3637

3738
componentWillReceiveProps(nextProps) {
38-
if (!nextProps.value) { return; }
39-
const newSelectedItem = this.state.items.find(item => isEqual(item.value, nextProps.value)) || this.placeholder;
39+
// update items if items prop changes
40+
if (!isEqual(this.state.items, nextProps.items)) {
41+
this.setState({
42+
items: this.noPlaceholder ? nextProps.items : [this.placeholder].concat(nextProps.items),
43+
});
44+
}
45+
46+
// update selectedItem if value prop is defined and differs from currently selected item
47+
if (this.props.value === 'undefined') { return; }
48+
const newSelectedItem = this.state.items.find(item => isEqual(item.value, nextProps.value)) || this.items[0];
4049
if (this.state.selectedItem !== newSelectedItem) {
4150
this.setState({
4251
selectedItem: newSelectedItem,
@@ -65,7 +74,7 @@ export default class RNPickerSelect extends PureComponent {
6574

6675
renderPlaceholderStyle() {
6776
const styleModifiers = {};
68-
if (this.state.selectedItem.label === this.placeholder.label) {
77+
if (!this.noPlaceholder && this.state.selectedItem.label === this.placeholder.label) {
6978
styleModifiers.color = this.props.style.placeholderColor || '#C7C7CD';
7079
}
7180
return styleModifiers;

‎test/test.js

+55-13
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,29 @@ import { Platform } from 'react-native';
33
import RNPickerSelect from '../src/';
44

55
const selectItems = [
6+
{
7+
label: 'Red',
8+
value: 'red',
9+
},
610
{
711
label: 'Orange',
812
value: 'orange',
913
},
1014
{
11-
label: 'Red',
12-
value: 'red',
15+
label: 'Yellow',
16+
value: 'yellow',
1317
},
1418
{
1519
label: 'Green',
1620
value: 'green',
1721
},
1822
{
19-
label: 'Purple',
20-
value: 'purple',
23+
label: 'Blue',
24+
value: 'blue',
2125
},
2226
{
23-
label: 'Yellow',
24-
value: 'yellow',
27+
label: 'Indigo',
28+
value: 'indigo',
2529
},
2630
];
2731

@@ -31,14 +35,14 @@ const placeholder = {
3135
};
3236

3337
describe('RNPickerSelect', () => {
34-
it('should set the picked value to state', () => {
38+
it('should set the selected value to state', () => {
3539
const wrapper = shallow(<RNPickerSelect
3640
items={selectItems}
3741
placeholder={placeholder}
3842
onSelect={() => {}}
3943
/>);
4044

41-
wrapper.find('[testId="RNPickerSelectIOS"]').props().onValueChange('orange', 1);
45+
wrapper.find('[testId="RNPickerSelectIOS"]').props().onValueChange('orange', 2);
4246
expect(wrapper.state().selectedItem.value).toEqual('orange');
4347
});
4448

@@ -50,8 +54,8 @@ describe('RNPickerSelect', () => {
5054
onSelect={onSelectSpy}
5155
/>);
5256

53-
wrapper.find('[testId="RNPickerSelectIOS"]').props().onValueChange('orange', 1);
54-
expect(onSelectSpy).toHaveBeenCalledWith({ index: 1, value: 'orange' });
57+
wrapper.find('[testId="RNPickerSelectIOS"]').props().onValueChange('orange', 2);
58+
expect(onSelectSpy).toHaveBeenCalledWith({ index: 2, value: 'orange' });
5559
});
5660

5761
it('should show the picker when pressed', () => {
@@ -79,7 +83,7 @@ describe('RNPickerSelect', () => {
7983
expect(wrapper.state().showPicker).toEqual(false);
8084
});
8185

82-
it('should update the picked value when the parent updates', () => {
86+
it('should update the selected value when the `value` prop updates', () => {
8387
const wrapper = shallow(<RNPickerSelect
8488
items={selectItems}
8589
placeholder={placeholder}
@@ -92,15 +96,53 @@ describe('RNPickerSelect', () => {
9296
expect(wrapper.state().selectedItem.value).toEqual('orange');
9397
});
9498

95-
it('should set the picked value to state (Android)', () => {
99+
it('should update the items when the `item` prop updates', () => {
100+
const wrapper = shallow(<RNPickerSelect
101+
items={selectItems}
102+
placeholder={placeholder}
103+
onSelect={() => {}}
104+
/>);
105+
106+
expect(wrapper.state().items).toEqual([placeholder].concat(selectItems));
107+
108+
const selectItemsPlusViolet = selectItems.concat([{ label: 'Violet', value: 'violet' }]);
109+
110+
wrapper.setProps({ items: selectItemsPlusViolet });
111+
expect(wrapper.state().items).toEqual([placeholder].concat(selectItemsPlusViolet));
112+
});
113+
114+
it('should should handle having no placeholder', () => {
115+
const wrapper = shallow(<RNPickerSelect
116+
items={selectItems}
117+
placeholder={{}}
118+
onSelect={() => {}}
119+
/>);
120+
121+
expect(wrapper.state().items).toEqual(selectItems);
122+
});
123+
124+
it('should should reset to the first item (typically the placeholder) if a value is passed in that doesn\'t exist in the `items` array', () => {
125+
const wrapper = shallow(<RNPickerSelect
126+
items={selectItems}
127+
placeholder={placeholder}
128+
onSelect={() => {}}
129+
/>);
130+
131+
wrapper.find('[testId="RNPickerSelectIOS"]').props().onValueChange('orange', 2);
132+
expect(wrapper.state().selectedItem.value).toEqual('orange');
133+
wrapper.setProps({ value: 'violet' });
134+
expect(wrapper.state().selectedItem).toEqual(placeholder);
135+
});
136+
137+
it('should set the selected value to state (Android)', () => {
96138
Platform.OS = 'android';
97139
const wrapper = shallow(<RNPickerSelect
98140
items={selectItems}
99141
placeholder={placeholder}
100142
onSelect={() => {}}
101143
/>);
102144

103-
wrapper.find('[testId="RNPickerSelectAndroid"]').props().onValueChange('orange', 1);
145+
wrapper.find('[testId="RNPickerSelectAndroid"]').props().onValueChange('orange', 2);
104146
expect(wrapper.state().selectedItem.value).toEqual('orange');
105147
});
106148
});

0 commit comments

Comments
 (0)
Please sign in to comment.