[change] modernize Picker

Rewrite Picker to use function components and hooks.
Rewrite the tests to replace enzyme with testing-library.
This commit is contained in:
Nicolas Gallagher
2020-02-04 16:05:41 -08:00
parent fdf53df301
commit 8d37fde5ee
4 changed files with 75 additions and 68 deletions
+4 -7
View File
@@ -10,7 +10,6 @@
import type { ColorValue } from '../../types'; import type { ColorValue } from '../../types';
import React from 'react';
import createElement from '../createElement'; import createElement from '../createElement';
type Props = { type Props = {
@@ -20,10 +19,8 @@ type Props = {
value?: number | string value?: number | string
}; };
export default class PickerItem extends React.Component<Props> { export default function PickerItem(props: Props) {
render() { const { color, label, testID, value } = props;
const { color, label, testID, value } = this.props; const style = { color };
const style = { color }; return createElement('option', { style, testID, value }, label);
return createElement('option', { style, testID, value }, label);
}
} }
@@ -9,14 +9,9 @@ exports[`components/Picker prop "children" items 1`] = `
`; `;
exports[`components/Picker prop "children" renders items 1`] = ` exports[`components/Picker prop "children" renders items 1`] = `
Array [ <option
<PickerItem value="value-1"
label="label-1" >
value="value-1" label-1
/>, </option>
<PickerItem
label="label-2"
value="value-2"
/>,
]
`; `;
@@ -1,9 +1,13 @@
/* eslint-env jasmine, jest */ /* eslint-env jasmine, jest */
import React from 'react'; import React from 'react';
import { shallow } from 'enzyme'; import { render } from '@testing-library/react';
import Picker from '..'; import Picker from '..';
function findSelect(container) {
return container.querySelector('select');
}
describe('components/Picker', () => { describe('components/Picker', () => {
describe('prop "children"', () => { describe('prop "children"', () => {
test('renders items', () => { test('renders items', () => {
@@ -13,14 +17,14 @@ describe('components/Picker', () => {
<Picker.Item label="label-2" value="value-2" /> <Picker.Item label="label-2" value="value-2" />
</Picker> </Picker>
); );
const component = shallow(picker); const { container } = render(picker);
expect(component.children()).toMatchSnapshot(); expect(container.firstChild.firstChild).toMatchSnapshot();
}); });
test('items', () => { test('items', () => {
const pickerItem = <Picker.Item label="label-1" value="value-1" />; const pickerItem = <Picker.Item label="label-1" value="value-1" />;
const component = shallow(pickerItem); const { container } = render(pickerItem);
expect(component).toMatchSnapshot(); expect(container.firstChild).toMatchSnapshot();
}); });
}); });
@@ -32,8 +36,8 @@ describe('components/Picker', () => {
<Picker.Item label="label-2" value="value-2" /> <Picker.Item label="label-2" value="value-2" />
</Picker> </Picker>
); );
const component = shallow(picker); const { container } = render(picker);
expect(component.find('select').prop('disabled')).toBe(true); expect(findSelect(container).disabled).toBe(true);
}); });
}); });
@@ -46,11 +50,14 @@ describe('components/Picker', () => {
<Picker.Item label="label-2" value="value-2" /> <Picker.Item label="label-2" value="value-2" />
</Picker> </Picker>
); );
const component = shallow(picker); const { container } = render(picker);
component.find('select').simulate('change', { const select = findSelect(container);
target: { selectedIndex: '1', value: 'value-2' } // mock change event
}); select.selectedIndex = '1';
expect(onValueChange).toHaveBeenCalledWith('value-2', '1'); select.value = 'value-2';
select.dispatchEvent(new window.Event('change', { bubbles: true }));
expect(onValueChange).toHaveBeenCalledWith('value-2', 1);
}); });
}); });
@@ -62,8 +69,8 @@ describe('components/Picker', () => {
<Picker.Item label="label-2" value="value-2" /> <Picker.Item label="label-2" value="value-2" />
</Picker> </Picker>
); );
const component = shallow(picker); const { container } = render(picker);
expect(component.find('select').prop('value')).toBe('value-2'); expect(findSelect(container).value).toBe('value-2');
}); });
test('selects the correct item (number)', () => { test('selects the correct item (number)', () => {
@@ -73,8 +80,8 @@ describe('components/Picker', () => {
<Picker.Item label="label-2" value={22} /> <Picker.Item label="label-2" value={22} />
</Picker> </Picker>
); );
const component = shallow(picker); const { container } = render(picker);
expect(component.find('select').prop('value')).toBe(22); expect(findSelect(container).value).toBe('22');
}); });
}); });
}); });
+43 -35
View File
@@ -10,11 +10,12 @@
import type { ViewProps } from '../View'; import type { ViewProps } from '../View';
import applyNativeMethods from '../../modules/applyNativeMethods';
import { Component } from 'react';
import createElement from '../createElement'; import createElement from '../createElement';
import setAndForwardRef from '../../modules/setAndForwardRef';
import usePlatformMethods from '../../hooks/usePlatformMethods';
import PickerItem from './PickerItem'; import PickerItem from './PickerItem';
import StyleSheet, { type StyleObj } from '../StyleSheet'; import StyleSheet, { type StyleObj } from '../StyleSheet';
import { forwardRef, useRef } from 'react';
type PickerProps = { type PickerProps = {
...ViewProps, ...ViewProps,
@@ -29,44 +30,51 @@ type PickerProps = {
prompt?: string prompt?: string
}; };
class Picker extends Component<PickerProps> { const Picker = forwardRef<PickerProps, *>((props, ref) => {
static Item = PickerItem; const {
children,
enabled,
onValueChange,
selectedValue,
style,
testID,
/* eslint-disable */
itemStyle,
mode,
prompt,
/* eslint-enable */
...other
} = props;
render() { const hostRef = useRef(null);
const { const setRef = setAndForwardRef({
children, getForwardedRef: () => ref,
enabled, setLocalRef: c => {
selectedValue, hostRef.current = c;
style, }
testID, });
/* eslint-disable */ usePlatformMethods(hostRef, ref, null, style);
itemStyle,
mode,
prompt,
onValueChange,
/* eslint-enable */
...otherProps
} = this.props;
return createElement('select', { function handleChange(e: Object) {
children,
disabled: enabled === false ? true : undefined,
onChange: this._handleChange,
style: [styles.initial, style],
testID,
value: selectedValue,
...otherProps
});
}
_handleChange = (e: Object) => {
const { onValueChange } = this.props;
const { selectedIndex, value } = e.target; const { selectedIndex, value } = e.target;
if (onValueChange) { if (onValueChange) {
onValueChange(value, selectedIndex); onValueChange(value, selectedIndex);
} }
}; }
}
return createElement('select', {
children,
disabled: enabled === false ? true : undefined,
onChange: handleChange,
ref: setRef,
style: [styles.initial, style],
testID,
value: selectedValue,
...other
});
});
Picker.Item = PickerItem;
const styles = StyleSheet.create({ const styles = StyleSheet.create({
initial: { initial: {
@@ -76,4 +84,4 @@ const styles = StyleSheet.create({
} }
}); });
export default applyNativeMethods(Picker); export default Picker;