diff --git a/__tests__/EditableCell.spec.js b/__tests__/EditableCell.spec.js
deleted file mode 100644
index 3056744..0000000
--- a/__tests__/EditableCell.spec.js
+++ /dev/null
@@ -1,63 +0,0 @@
-jest.unmock('../EditableCell');
-jest.unmock('enzyme');
-jest.unmock('sinon');
-
-import { EditableCell } from '../EditableCell';
-import { shallow } from 'enzyme';
-import sinon from 'sinon';
-import React from 'react';
-import {
- TextInput,
- View,
-} from 'react-native';
-
-describe('EditableCell', () => {
- it('renders a View and TextInput with default props', () => {
- const wrapper = shallow(
-
- );
- expect(wrapper.state('value')).toEqual('N/A');
- expect(wrapper.find(View).prop('style')[1]).toEqual({ flex: 1 });
- expect(wrapper.find(View).length).toBe(1);
- expect(wrapper.find(TextInput).length).toBe(1);
- });
-
- it('takes a width argument and applies it to defaultStyles', () => {
- const wrapper = shallow(
-
- );
- expect(wrapper.find(View).prop('style')[1]).toEqual({ flex: 1337 });
- });
-
- it('sets state to value as string', () => {
- const wrapper = shallow(
-
- );
- expect(wrapper.state('value')).toEqual('50');
- });
-
- it('changes state with onChange event', () => {
- const wrapper = shallow(
-
- );
- expect(wrapper.state('value')).toEqual('N/A');
- wrapper.find(TextInput).simulate('changeText', 'foo');
- expect(wrapper.state('value')).toEqual('foo');
- });
-
- it('onEndEditing event triggers callback', () => {
- const callback = sinon.spy();
- const obj = {};
- const wrapper = shallow(
-
- );
- wrapper.find(TextInput).simulate('endEditing');
- expect(callback.calledWithExactly(obj, 'foo')).toBe(true);
- });
-});
diff --git a/src/EditableCell.js b/src/EditableCell.js
deleted file mode 100644
index e02be40..0000000
--- a/src/EditableCell.js
+++ /dev/null
@@ -1,90 +0,0 @@
-/* @flow weak */
-
-/**
- * mSupply Mobile
- * Sustainable Solutions (NZ) Ltd. 2016
- */
-
-import React from 'react';
-import PropTypes from 'prop-types';
-import {
- StyleSheet,
- TextInput,
- View,
- ViewPropTypes,
-} from 'react-native';
-
-export class EditableCell extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- value: 'N/A',
- };
- this.componentWillMount = this.componentWillMount.bind(this);
- this.onEndEditing = this.onEndEditing.bind(this);
- }
-
- componentWillMount() {
- this.setState({
- value: String(this.props.value),
- });
- }
-
- componentWillReceiveProps(nextProps) {
- if (nextProps.value !== this.state.value) {
- this.setState({
- value: String(nextProps.value),
- });
- }
- }
-
- onEndEditing() {
- // If the field is cleared, write null to property
- const newValue = this.state.value === '' ? null : this.state.value;
- this.props.onEndEditing(this.props.target, newValue);
- }
-
- render() {
- const { style, width, textStyle, refCallback, ...textInputProps } = this.props;
- return (
-
- this.setState({ value: text })}
- onEndEditing={this.onEndEditing}
- value={this.state.value}
- />
-
- );
- }
-}
-
-EditableCell.propTypes = {
- style: ViewPropTypes.style,
- refCallback: PropTypes.func,
- textStyle: TextInput.propTypes.style,
- width: PropTypes.number,
- onEndEditing: PropTypes.func,
- target: PropTypes.object,
- value: PropTypes.oneOfType([
- PropTypes.string,
- PropTypes.number,
- ]),
-};
-
-EditableCell.defaultProps = {
- width: 1,
- value: 'N/A',
-};
-
-const defaultStyles = StyleSheet.create({
- cell: {
- flex: 1,
- justifyContent: 'center',
- },
- text: {
- right: -9, // This is to account for RN issue 1287, see https://github.com/facebook/react-native/issues/1287
- },
-});
diff --git a/src/TextInputCell.js b/src/TextInputCell.js
new file mode 100644
index 0000000..8679569
--- /dev/null
+++ b/src/TextInputCell.js
@@ -0,0 +1,146 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+import { View, TextInput } from 'react-native'
+
+import Cell from './Cell'
+import RefContext from './RefContext'
+
+import { getAdjustedStyle } from './utilities'
+
+/**
+ * Renders a cell that on press or focus contains a TextInput for
+ * editing values.
+ *
+ * @param {string|number} value The value to render in cell
+ * @param {string|number} rowKey Unique key associated to row cell is in
+ * @param {string|number} columnKey Unique key associated to column cell is in
+ * @param {bool} isDisabled If `true` will render a plain Cell element with no interaction
+ * @param {String} placeholderColour Placholder text colour
+ * @param {func} dispatch Reducer dispatch callback for handling actions
+ * @param {Object} viewStyle Style object for the wrapping View component
+ * @param {Object} textStyle Style object for the inner Text component
+ * @param {Object} textInputStyle Style object for TextInput component.
+ * @param {Number} width Optional flex property to inject into styles.
+ * @param {bool} debug Logs rendering of this component.
+ * @param {string} keyboardType Type of keyboard for the TextInput.
+ * @param {string} placeholder placeholder text
+ * @param {Object} cellTextStyle text style for the disabled Cell component.
+ * @param {Bool} isLastCell Indicator if this cell is last in a row,
+ * removing the borderRight,
+ * @param {func} editAction Action creator for handling editing of this cell.
+ * `(newValue, rowKey, columnKey) => {...}`
+ * @param {String} underlineColor Underline colour of TextInput on Android.
+ */
+const TextInputCell = React.memo(
+ ({
+ value,
+ rowKey,
+ columnKey,
+ isDisabled,
+ placeholderColour,
+ editAction,
+ dispatch,
+ isLastCell,
+ width,
+ debug,
+ keyboardType,
+ placeholder,
+ viewStyle,
+ rowIndex,
+ textInputStyle,
+ cellTextStyle,
+ underlineColor,
+ }) => {
+ if (debug) console.log(`- TextInputCell: ${value}`)
+
+ const usingPlaceholder = placeholder && !value
+
+ const { focusNextCell, getRefIndex, getCellRef } = React.useContext(
+ RefContext
+ )
+ const refIndex = getRefIndex(rowIndex, columnKey)
+
+ const onEdit = newValue => dispatch(editAction(newValue, rowKey, columnKey))
+ const focusNext = () => focusNextCell(refIndex)
+
+ // Render a plain Cell if disabled.
+ if (isDisabled) {
+ return (
+ |
+ )
+ }
+
+ const internalViewStyle = getAdjustedStyle(viewStyle, width, isLastCell)
+ const internalTextStyle = getAdjustedStyle(textInputStyle, width)
+
+ // Render a Cell with a textInput.
+ return (
+
+
+
+ )
+ }
+)
+
+TextInputCell.propTypes = {
+ value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ rowKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
+ columnKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
+ .isRequired,
+ isDisabled: PropTypes.bool,
+ placeholderColour: PropTypes.string,
+ editAction: PropTypes.func.isRequired,
+ dispatch: PropTypes.func.isRequired,
+ cellTextStyle: PropTypes.object,
+ viewStyle: PropTypes.object,
+ width: PropTypes.number,
+ textInputStyle: PropTypes.object,
+ isLastCell: PropTypes.bool,
+ debug: PropTypes.bool,
+ placeholder: PropTypes.string,
+ rowIndex: PropTypes.number.isRequired,
+ underlineColor: PropTypes.string,
+ keyboardType: PropTypes.oneOf([
+ 'default',
+ 'number-pad',
+ 'decimal-pad',
+ 'numeric',
+ 'email-address',
+ 'phone-pad',
+ ]),
+}
+
+TextInputCell.defaultProps = {
+ value: '',
+ isDisabled: false,
+ viewStyle: {},
+ cellTextStyle: {},
+ textInputStyle: {},
+ isLastCell: false,
+ width: 0,
+ debug: false,
+ keyboardType: 'numeric',
+ placeholder: '',
+ placeholderColour: '#CDCDCD',
+ underlineColor: '#CDCDCD',
+}
+
+export default TextInputCell