[add] Picker and Picker.Item components

Close #705
This commit is contained in:
Kenneth Kufluk
2017-11-06 14:26:53 -08:00
committed by Nicolas Gallagher
parent 02e62ad5d6
commit b7e970f4e6
7 changed files with 392 additions and 4 deletions
@@ -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;
@@ -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;
@@ -0,0 +1,17 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/Picker prop "children" renders items 1`] = `
<select
className="rn-fontFamily-poiln3 rn-fontSize-7cikom rn-marginTop-1mnahxq rn-marginRight-61z16t rn-marginBottom-p1pxzi rn-marginLeft-11wrixw"
onChange={[Function]}
>
<PickerItem
label="label-1"
value="value-1"
/>
<PickerItem
label="label-2"
value="value-2"
/>
</select>
`;
@@ -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 = (
<Picker>
<Picker.Item label="label-1" value="value-1" />
<Picker.Item label="label-2" value="value-2" />
</Picker>
);
const component = shallow(picker);
expect(component).toMatchSnapshot();
});
});
describe('prop "enabled"', () => {
test('picker is disabled if false', () => {
const picker = (
<Picker enabled={false}>
<Picker.Item label="label-1" value="value-1" />
<Picker.Item label="label-2" value="value-2" />
</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 = (
<Picker onValueChange={onValueChange} selectedValue="value-1">
<Picker.Item label="label-1" value="value-1" />
<Picker.Item label="label-2" value="value-2" />
</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 = (
<Picker selectedValue="value-2">
<Picker.Item label="label-1" value="value-1" />
<Picker.Item label="label-2" value="value-2" />
</Picker>
);
const component = shallow(picker);
expect(component.find('select').prop('value')).toBe('value-2');
});
test('selects the correct item (number)', () => {
const picker = (
<Picker selectedValue={22}>
<Picker.Item label="label-1" value={11} />
<Picker.Item label="label-2" value={22} />
</Picker>
);
const component = shallow(picker);
expect(component.find('select').prop('value')).toBe(22);
});
});
});
+114 -4
View File
@@ -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<ItemProps> {
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<typeof PickerItem>,
enabled?: boolean,
onValueChange?: Function,
selectedValue?: number | string,
style?: pickerStyleType,
testID?: string,
/* compat */
itemStyle?: itemStylePropType,
mode?: string,
prompt?: string
};
class Picker extends Component<Props> {
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);