-
Notifications
You must be signed in to change notification settings - Fork 35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
#34 #37 #40 datatable row cell components #51
Changes from all commits
3241043
07e546f
0269051
4f02722
f5e8ee4
786057b
6124571
e3518a3
05c7b2c
4e2d5f9
100f774
a96418f
584d5d7
7717202
c6719c6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,7 @@ | ||
{ | ||
"extends": "airbnb", | ||
"parser": "babel-eslint", | ||
"root": true, | ||
"extends": "@react-native-community", | ||
"rules": { | ||
"no-param-reassign": "off", | ||
"jsx-boolean-value": "off", | ||
"no-use-before-define": "off", | ||
"object-shorthand": "off", | ||
"strict": 0, | ||
"no-confusing-arrow": ["error", {"allowParens": true}], | ||
"global-require": "off", | ||
"react/jsx-boolean-value": "off", | ||
"semi": 0 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"trailingComma": "es5", | ||
"tabWidth": 2, | ||
"singleQuote": true, | ||
"semi": false | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,7 @@ | ||
module.exports = { | ||
root: true, | ||
extends: '@react-native-community', | ||
rules: { | ||
semi: 0, | ||
} | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"trailingComma": "es5", | ||
"tabWidth": 2, | ||
"singleQuote": true, | ||
"semi": false | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
/** | ||
* Table experiment hole | ||
* https://github.com/facebook/react-native | ||
* | ||
* @format | ||
* @flow | ||
*/ | ||
|
||
import React, { Fragment, useState, useCallback, useReducer } from 'react' | ||
import { Button, StatusBar } from 'react-native' | ||
import { DataTable } from './components/DataTable' | ||
import { Row } from './components/Row' | ||
import { Cell } from './components/Cell' | ||
|
||
// Configurable constants for demo data volume | ||
const rowCount = 10 | ||
const columnCount = 4 | ||
|
||
const baseData = [] | ||
const baseColumns = [] | ||
|
||
// Make columns definition used in demo app | ||
for (let index = 0; index < columnCount; index++) { | ||
baseColumns.push({ key: `col${index}`, editable: index === columnCount - 1 }) // index === columnCount - 1 | ||
} | ||
|
||
// Generate data for use in demo app | ||
for (let index = 0; index < rowCount; index++) { | ||
const rowValues = {} | ||
baseColumns.forEach((column, columnIndex) => { | ||
rowValues[column.key] = `row${index}Col${columnIndex}` | ||
}) | ||
|
||
baseData.push({ id: `r${index}`, ...rowValues }) | ||
} | ||
|
||
const keyExtractor = item => item.id | ||
const dataReducer = (data, action) => { | ||
switch (action.type) { | ||
case 'editCell': | ||
const { value, rowKey, columnKey } = action | ||
const rowIndex = data.findIndex(row => keyExtractor(row) === rowKey) | ||
|
||
// Immutable array editing so only the row/cell edited are re-rendered. | ||
// If you don't do this, every row will re-render as well as the cell | ||
// edited. | ||
return data.map((row, index) => { | ||
if (index !== rowIndex) { | ||
return row | ||
} | ||
const rowEdited = { ...row } | ||
rowEdited[columnKey] = value | ||
return rowEdited | ||
}) | ||
case 'reverseData': | ||
return [...data.reverse()] | ||
default: | ||
return data | ||
} | ||
} | ||
|
||
// Action yay | ||
const editCell = (value, rowKey, columnKey) => ({ | ||
type: 'editCell', | ||
value, | ||
rowKey, | ||
columnKey, | ||
}) | ||
|
||
const App = () => { | ||
const [isButtonOof, toggleButton] = useState(false) | ||
const [data, dataDispatch] = useReducer(dataReducer, baseData) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is where we start getting a hint on how to use/form the new API, but there are decisions to be made on how prescribed we make this thing, mostly relating to how we pass around |
||
const columns = baseColumns | ||
|
||
const renderCells = useCallback( | ||
(rowData, rowKey) => { | ||
return columns.map(col => ( | ||
<Cell | ||
key={col.key} | ||
value={rowData[col.key]} | ||
rowKey={rowKey} | ||
columnKey={col.key} | ||
editable={col.editable} | ||
editAction={editCell} | ||
dataDispatch={dataDispatch} | ||
/> | ||
)) | ||
}, | ||
[columns, dataDispatch] | ||
) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The current design has Ok I experimented with giving a callBack to the cell that calls the dispatch with what ever you want. But then you have another function reference to manage (boom all cells on the row re-render on an edit). useCallback isn't allowed inside a useCallback hook haha. If only there was some kind of "actionCreator" pattern prescribed by some very common library 🤔 |
||
|
||
const renderItem = useCallback( | ||
({ item, index }) => { | ||
const rowKey = keyExtractor(item) | ||
return ( | ||
<Row | ||
rowData={data[index]} | ||
rowKey={rowKey} | ||
renderCells={renderCells} | ||
dataDispatch={dataDispatch} | ||
/> | ||
) | ||
}, | ||
[data, renderCells, dataDispatch] | ||
) | ||
|
||
return ( | ||
<Fragment> | ||
<StatusBar hidden /> | ||
<Button | ||
title={'sort data'} | ||
onPress={() => dataDispatch({ type: 'reverseData' })} | ||
/> | ||
<Button | ||
title={isButtonOof ? 'oof' : 'Press me'} | ||
onPress={() => toggleButton(!isButtonOof)} | ||
/> | ||
<DataTable | ||
data={data} | ||
renderRow={renderItem} | ||
keyExtractor={keyExtractor} | ||
/> | ||
</Fragment> | ||
) | ||
} | ||
|
||
export default App |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import React from 'react' | ||
import PropTypes from 'prop-types' | ||
import { View, Text, TextInput, StyleSheet, ViewPropTypes } from 'react-native' | ||
|
||
export const Cell = React.memo( | ||
({ value, rowKey, columnKey, editable, editAction, dataDispatch }) => { | ||
const _onEdit = newValue => | ||
dataDispatch(editAction(newValue, rowKey, columnKey)) | ||
|
||
console.log(`cell: ${value}`) | ||
return ( | ||
<View style={defaultStyles.cell}> | ||
{editable ? ( | ||
<TextInput value={value} onChangeText={_onEdit} /> | ||
) : ( | ||
<Text>{value}</Text> | ||
)} | ||
</View> | ||
) | ||
} | ||
) | ||
|
||
Cell.propTypes = { | ||
...ViewPropTypes, | ||
style: ViewPropTypes.style, | ||
textStyle: Text.propTypes.style, | ||
width: PropTypes.number, | ||
} | ||
|
||
Cell.defaultProps = { | ||
width: 1, | ||
} | ||
|
||
const defaultStyles = StyleSheet.create({ | ||
cell: { | ||
flex: 1, | ||
justifyContent: 'center', | ||
}, | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/* @flow weak */ | ||
|
||
/** | ||
* mSupply Mobile | ||
* Sustainable Solutions (NZ) Ltd. 2016 | ||
*/ | ||
|
||
import PropTypes from 'prop-types' | ||
import React from 'react' | ||
import { | ||
StyleSheet, | ||
VirtualizedList, | ||
VirtualizedListPropTypes, | ||
} from 'react-native' | ||
|
||
export const DataTable = React.memo(({ renderRow, ...otherProps }) => ( | ||
<VirtualizedList | ||
style={defaultStyles.virtualizedList} | ||
renderItem={renderRow} | ||
{...otherProps} | ||
/> | ||
)) | ||
|
||
DataTable.propTypes = { | ||
...VirtualizedListPropTypes, | ||
renderHeader: PropTypes.func, | ||
renderRow: PropTypes.func.isRequired, | ||
} | ||
DataTable.defaultProps = { | ||
getItem: (items, index) => items[index], | ||
getItemCount: items => items.length, | ||
} | ||
|
||
const defaultStyles = StyleSheet.create({ | ||
virtualizedList: { | ||
flex: 1, | ||
}, | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import PropTypes from 'prop-types' | ||
import React from 'react' | ||
import { View, StyleSheet, ViewPropTypes } from 'react-native' | ||
|
||
export const Row = React.memo( | ||
({ rowData, rowKey, renderCells, dataDispatch }) => { | ||
console.log('====================================') | ||
console.log(`Row: ${rowKey}`) | ||
console.log('====================================') | ||
|
||
return <View style={defaultStyles.row}>{renderCells(rowData, rowKey)}</View> | ||
} | ||
) | ||
|
||
Row.propTypes = { | ||
style: ViewPropTypes.style, | ||
onPress: PropTypes.func, | ||
} | ||
|
||
const defaultStyles = StyleSheet.create({ | ||
row: { | ||
flex: 1, | ||
flexDirection: 'row', | ||
}, | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd just ignore the root project stuff. Though you might be interested in a discussion around whether to use airbnb linter rules or not. You might even note that I've gone sour on semi colons.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not really sure what to contribute right now, but I'd say it's pretty appropriate to create a
@openmsupply/configs
repo to export linter and prettier rulesI guess I prefer semi-colons, but I don't really care as long as it's consistent. I mean, especially if a linter is doing it for me
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also agree with exporting linter and prettier rules (BE are doing this). Re configs, if they're consistent, I don't mind (although I prefer semi-colons). I think the best way to go is just use defaults across the board, just because it stops any disagreements.