diff --git a/docs/storybook/1-components/Picker/PickerScreen.js b/docs/storybook/1-components/Picker/PickerScreen.js
new file mode 100644
index 00000000..3943bdd3
--- /dev/null
+++ b/docs/storybook/1-components/Picker/PickerScreen.js
@@ -0,0 +1,113 @@
+/* eslint-disable react/jsx-sort-props, react/jsx-no-bind, no-alert */
+
+/**
+ * @flow
+ */
+
+import React from 'react';
+import PickerExample from './examples/PickerExample';
+import UIExplorer, {
+ AppText,
+ Description,
+ DocItem,
+ Section,
+ StyleList,
+ storiesOf
+} from '../../ui-explorer';
+import { View } from 'react-native';
+
+const PickerScreen = () => (
+
+
+
+ Renders the native <select> component.
+
+
+
+
+
+`
+ }}
+ />
+
+
+ }}
+ />
+
+ (
+ {
+ window.alert(`itemValue: ${itemValue}, itemPosition: ${itemPosition}`);
+ }}
+ />
+ )
+ }}
+ />
+
+
+ }}
+ />
+
+
+ }
+ />
+
+
+
+
+
+
+ Individual selectable item in a Picker.
+
+
+
+
+);
+
+storiesOf('Components', module).add('Picker', PickerScreen);
diff --git a/docs/storybook/1-components/Picker/examples/PickerExample.js b/docs/storybook/1-components/Picker/examples/PickerExample.js
new file mode 100644
index 00000000..df785fe3
--- /dev/null
+++ b/docs/storybook/1-components/Picker/examples/PickerExample.js
@@ -0,0 +1,28 @@
+/**
+ * @flow
+ */
+
+import React from 'react';
+import { Picker, StyleSheet, View } from 'react-native';
+
+const PickerExample = props => (
+
+
+
+
+
+
+
+
+
+
+
+);
+
+const styles = StyleSheet.create({
+ rootl: {
+ alignItems: 'flex-start'
+ }
+});
+
+export default PickerExample;
diff --git a/src/components/Picker/PickerItemPropType.js b/src/components/Picker/PickerItemPropType.js
new file mode 100644
index 00000000..531a2bf0
--- /dev/null
+++ b/src/components/Picker/PickerItemPropType.js
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2017-present, Nicolas Gallagher.
+ * Copyright (c) 2015-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @flow
+ */
+
+import React from 'react';
+import Picker from './';
+
+const PickerItemPropType = (props: Object, propName: string, componentName: string) => {
+ const prop = props[propName];
+ let error = null;
+ React.Children.forEach(prop, function(child) {
+ if (child.type !== Picker.Item) {
+ error = new Error('`Picker` children must be of type `Picker.Item`.');
+ }
+ });
+ return error;
+};
+
+export default PickerItemPropType;
diff --git a/src/components/Picker/PickerStylePropTypes.js b/src/components/Picker/PickerStylePropTypes.js
new file mode 100644
index 00000000..85f35f64
--- /dev/null
+++ b/src/components/Picker/PickerStylePropTypes.js
@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) 2017-present, Nicolas Gallagher.
+ * Copyright (c) 2015-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @flow
+ */
+
+import ColorPropType from '../../propTypes/ColorPropType';
+import ViewStylePropTypes from '../View/ViewStylePropTypes';
+
+const PickerStylePropTypes = {
+ ...ViewStylePropTypes,
+ color: ColorPropType
+};
+
+export default PickerStylePropTypes;
diff --git a/src/components/Picker/__tests__/__snapshots__/index-test.js.snap b/src/components/Picker/__tests__/__snapshots__/index-test.js.snap
new file mode 100644
index 00000000..8a110714
--- /dev/null
+++ b/src/components/Picker/__tests__/__snapshots__/index-test.js.snap
@@ -0,0 +1,17 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`components/Picker prop "children" renders items 1`] = `
+
+`;
diff --git a/src/components/Picker/__tests__/index-test.js b/src/components/Picker/__tests__/index-test.js
new file mode 100644
index 00000000..11677f44
--- /dev/null
+++ b/src/components/Picker/__tests__/index-test.js
@@ -0,0 +1,74 @@
+/* eslint-env jasmine, jest */
+
+import React from 'react';
+import { shallow } from 'enzyme';
+import Picker from '..';
+
+describe('components/Picker', () => {
+ describe('prop "children"', () => {
+ test('renders items', () => {
+ const picker = (
+
+
+
+
+ );
+ const component = shallow(picker);
+ expect(component).toMatchSnapshot();
+ });
+ });
+
+ describe('prop "enabled"', () => {
+ test('picker is disabled if false', () => {
+ const picker = (
+
+
+
+
+ );
+ const component = shallow(picker);
+ expect(component.find('select').props().disabled).toBe(true);
+ });
+ });
+
+ describe('prop "onValueChange"', () => {
+ test('is called with (value, index)', () => {
+ const onValueChange = jest.fn();
+ const picker = (
+
+
+
+
+ );
+ const component = shallow(picker);
+ component.find('select').simulate('change', {
+ target: { selectedIndex: '1', value: 'value-2' }
+ });
+ expect(onValueChange).toHaveBeenCalledWith('value-2', '1');
+ });
+ });
+
+ describe('prop "selectedValue"', () => {
+ test('selects the correct item (string)', () => {
+ const picker = (
+
+
+
+
+ );
+ const component = shallow(picker);
+ expect(component.find('select').prop('value')).toBe('value-2');
+ });
+
+ test('selects the correct item (number)', () => {
+ const picker = (
+
+
+
+
+ );
+ const component = shallow(picker);
+ expect(component.find('select').prop('value')).toBe(22);
+ });
+ });
+});
diff --git a/src/components/Picker/index.js b/src/components/Picker/index.js
index 909a0b90..f6c51d45 100644
--- a/src/components/Picker/index.js
+++ b/src/components/Picker/index.js
@@ -1,4 +1,114 @@
-import UnimplementedView from '../UnimplementedView';
-const Picker = UnimplementedView;
-Picker.Item = UnimplementedView;
-export default Picker;
+/**
+ * Copyright (c) 2017-present, Nicolas Gallagher.
+ * Copyright (c) 2015-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @providesModule Picker
+ * @flow
+ */
+
+import applyNativeMethods from '../../modules/applyNativeMethods';
+import { Component } from 'react';
+import ColorPropType from '../../propTypes/ColorPropType';
+import createElement from '../../modules/createElement';
+import PickerItemPropType from './PickerItemPropType';
+import PickerStylePropTypes from './PickerStylePropTypes';
+import StyleSheetPropType from '../../propTypes/StyleSheetPropType';
+import StyleSheet from '../../apis/StyleSheet';
+import TextStylePropTypes from '../Text/TextStylePropTypes';
+import { arrayOf, bool, func, number, oneOfType, string } from 'prop-types';
+
+const pickerStyleType = StyleSheetPropType(PickerStylePropTypes);
+const itemStylePropType = StyleSheetPropType(TextStylePropTypes);
+
+type ItemProps = {
+ color?: ColorPropType,
+ label: string,
+ testID?: string,
+ value?: number | string
+};
+
+class PickerItem extends Component {
+ static propTypes = {
+ color: ColorPropType,
+ label: string.isRequired,
+ testID: string,
+ value: oneOfType([number, string])
+ };
+
+ render() {
+ const { label, testID, value } = this.props;
+ return createElement('option', { label, testID, value });
+ }
+}
+
+type Props = {
+ children?: Array,
+ enabled?: boolean,
+ onValueChange?: Function,
+ selectedValue?: number | string,
+ style?: pickerStyleType,
+ testID?: string,
+ /* compat */
+ itemStyle?: itemStylePropType,
+ mode?: string,
+ prompt?: string
+};
+
+class Picker extends Component {
+ static propTypes = {
+ children: arrayOf(PickerItemPropType),
+ enabled: bool,
+ onValueChange: func,
+ selectedValue: oneOfType([number, string]),
+ style: pickerStyleType,
+ testID: string
+ };
+
+ static Item = PickerItem;
+
+ render() {
+ const {
+ children,
+ enabled,
+ selectedValue,
+ style,
+ testID,
+ /* eslint-disable */
+ itemStyle,
+ mode,
+ prompt
+ /* eslint-enable */
+ } = this.props;
+
+ return createElement('select', {
+ children,
+ disabled: enabled === false ? true : undefined,
+ onChange: this._handleChange,
+ style: [styles.initial, style],
+ testID,
+ value: selectedValue
+ });
+ }
+
+ _handleChange = (e: Object) => {
+ const { onValueChange } = this.props;
+ const { selectedIndex, value } = e.target;
+ if (onValueChange) {
+ onValueChange(value, selectedIndex);
+ }
+ };
+}
+
+const styles = StyleSheet.create({
+ initial: {
+ fontFamily: 'inherit',
+ fontSize: 'inherit',
+ margin: 0
+ }
+});
+
+export default applyNativeMethods(Picker);