From 4bacfc35e62c811d8345d39c3a0d345d7da336e3 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Thu, 28 Apr 2016 19:11:41 -0700 Subject: [PATCH 01/60] Renamed properties (eg. rowsCount to rowCount) --- docs/ArrowKeyStepper.md | 12 +- docs/AutoSizer.md | 2 +- docs/Collection.md | 8 +- docs/ColumnSizer.md | 8 +- docs/FlexTable.md | 8 +- docs/Grid.md | 22 ++-- docs/InfiniteLoader.md | 6 +- docs/VirtualScroll.md | 8 +- docs/creatingAnInfiniteLoadingList.md | 4 +- docs/reverseList.md | 2 +- playground/grid.js | 10 +- playground/hover.js | 12 +- playground/tree.js | 8 +- .../ArrowKeyStepper.example.js | 14 +- source/ArrowKeyStepper/ArrowKeyStepper.js | 10 +- .../ArrowKeyStepper/ArrowKeyStepper.test.js | 12 +- source/AutoSizer/AutoSizer.example.js | 2 +- source/Collection/Collection.js | 2 +- source/Collection/CollectionView.js | 4 +- source/ColumnSizer/ColumnSizer.example.js | 28 ++-- source/ColumnSizer/ColumnSizer.js | 12 +- source/ColumnSizer/ColumnSizer.test.js | 18 +-- source/FlexTable/FlexTable.example.js | 40 +++--- source/FlexTable/FlexTable.js | 20 +-- source/FlexTable/FlexTable.test.js | 32 ++--- source/Grid/Grid.example.js | 78 +++++------ source/Grid/Grid.js | 124 +++++++++--------- source/Grid/Grid.test.js | 118 ++++++++--------- .../InfiniteLoader/InfiniteLoader.example.js | 26 ++-- source/InfiniteLoader/InfiniteLoader.js | 16 +-- source/InfiniteLoader/InfiniteLoader.test.js | 16 +-- source/ScrollSync/ScrollSync.example.js | 50 +++---- source/VirtualScroll/VirtualScroll.example.js | 36 ++--- source/VirtualScroll/VirtualScroll.js | 20 +-- source/VirtualScroll/VirtualScroll.test.js | 32 ++--- 35 files changed, 410 insertions(+), 410 deletions(-) diff --git a/docs/ArrowKeyStepper.md b/docs/ArrowKeyStepper.md index e88f15cc9..6782a4980 100644 --- a/docs/ArrowKeyStepper.md +++ b/docs/ArrowKeyStepper.md @@ -12,8 +12,8 @@ The appearance of this wrapper element can be customized using the `className` p |:---|:---|:---:|:---| | children | Function | ✓ | Function respondible for rendering children. This function should implement the following signature: `({ onKeyDown, onSectionRendered, scrollToColumn, scrollToRow }) => PropTypes.element` | | className | String | | CSS class name to attach to the wrapper `
`. | -| columnsCount | Number | ✓ | Number of columns in grid; for `FlexTable` and `VirtualScroll` this property should always be `1`. | -| rowsCount | Number | ✓ | Number of rows in grid. | +| columnCount | Number | ✓ | Number of columns in grid; for `FlexTable` and `VirtualScroll` this property should always be `1`. | +| rowCount | Number | ✓ | Number of rows in grid. | ### Children function @@ -38,15 +38,15 @@ import 'react-virtualized/styles.css'; // only needs to be imported once ReactDOM.render( {({ onKeyDown, onSectionRendered, scrollToColumn, scrollToRow }) => (
list[index] // Could also be a DOM element diff --git a/docs/Collection.md b/docs/Collection.md index b7727d0ec..40c17b487 100644 --- a/docs/Collection.md +++ b/docs/Collection.md @@ -9,13 +9,13 @@ Unlike `Grid`, which renders checkerboard data, `Collection` can render arbitrar |:---|:---|:---:|:---| | className | String | | Optional custom CSS class name to attach to root Collection element. | | cellCount | Number | ✓ | Number of cells in collection. | +| cellRenderer | Function | ✓ | Responsible for rendering a cell given an row and column index: `(index: number): PropTypes.node` | +| cellGroupRenderer | Function | ✓ | Responsible for rendering a group of cells given their indices.: `({ cellSizeAndPositionGetter:Function, indices: Array, cellRenderer: Function }): Array` | +| cellSizeAndPositionGetter | Function | ✓ | Callback responsible for returning size and offset/position information for a given cell (index): `(index): { height: number, width: number, x: number, y: number }` | | height | Number | ✓ | Height of Collection; this property determines the number of visible (vs virtualized) rows. | | noContentRenderer | Function | | Optional renderer to be rendered inside the grid when `cellCount` is 0: `(): PropTypes.node` | | onSectionRendered | Function | | Callback invoked with information about the section of the Collection that was just rendered: `(indices: Array): void` | | onScroll | Function | | Callback invoked whenever the scroll offset changes within the inner scrollable region: `({ clientHeight, clientWidth, scrollHeight, scrollLeft, scrollTop, scrollWidth }): void` | -| cellRenderer | Function | ✓ | Responsible for rendering a cell given an row and column index: `(index: number): PropTypes.node` | -| cellGroupRenderer | Function | ✓ | Responsible for rendering a group of cells given their indices.: `({ cellSizeAndPositionGetter:Function, indices: Array, cellRenderer: Function }): Array` | -| cellSizeAndPositionGetter | Function | ✓ | Callback responsible for returning size and offset/position information for a given cell (index): `(index): { height: number, width: number, x: number, y: number }` | | scrollLeft | Number | | Horizontal offset | | scrollToCell | Number | | Cell index to ensure visible (by scrolling if necessary) | | scrollTop | Number | | Vertical offset | @@ -64,7 +64,7 @@ ReactDOM.render( cellCount={list.length} cellRenderer={(index) => list[index].name} cellSizeAndPositionGette={(index) => list[index]} - columnsCount={list.length} + columnCount={list.length} height={300} width={300} />, diff --git a/docs/ColumnSizer.md b/docs/ColumnSizer.md index f604bda90..3c196ab30 100644 --- a/docs/ColumnSizer.md +++ b/docs/ColumnSizer.md @@ -38,18 +38,18 @@ ReactDOM.render( {({ adjustedWidth, getColumnWidth, registerChild }) => ( )} diff --git a/docs/FlexTable.md b/docs/FlexTable.md index 9e8e5969a..9c8ddcea5 100644 --- a/docs/FlexTable.md +++ b/docs/FlexTable.md @@ -13,16 +13,16 @@ This component expects explicit width, height, and padding parameters. | headerClassName | String | | CSS class to apply to all column headers | | headerHeight | Number | ✓ | Fixed height of header row | | height | Number | ✓ | Fixed/available height for out DOM element | -| noRowsRenderer | | Function | Callback used to render placeholder content when :rowsCount is 0 | +| noRowsRenderer | | Function | Callback used to render placeholder content when :rowCount is 0 | | onHeaderClick | | Function | Callback invoked when a user clicks on a table header. `(dataKey: string, columnData: any): void` | | onRowClick | | Function | Callback invoked when a user clicks on a table row. `(rowIndex: number): void` | | onRowsRendered | | Function | Callback invoked with information about the slice of rows that were just rendered: `({ overscanStartIndex, overscanStopIndex, startIndex, stopIndex }): void` | -| overscanRowsCount | | Number | Number of rows to render above/below the visible bounds of the list. This can help reduce flickering during scrolling on certain browers/devices. | +| overscanRowCount | | Number | Number of rows to render above/below the visible bounds of the list. This can help reduce flickering during scrolling on certain browers/devices. | | onScroll | Function | | Callback invoked whenever the scroll offset changes within the inner scrollable region: `({ clientHeight, scrollHeight, scrollTop }): void` | | rowClassName | String or Function | | CSS class to apply to all table rows (including the header row). This value may be either a static string or a function with the signature `(rowIndex: number): string`. Note that for the header row an index of `-1` is provided. | | rowGetter | Function | ✓ | Callback responsible for returning a data row given an index. `(index: int): any` | | rowHeight | Number or Function | ✓ | Either a fixed row height (number) or a function that returns the height of a row given its index: `(index: number): number` | -| rowsCount | Number | ✓ | Number of rows in table. | +| rowCount | Number | ✓ | Number of rows in table. | | scrollToIndex | Number | | Row index to ensure visible (by forcefully scrolling if necessary) | | scrollTop | Number | | Vertical offset | | sort | Function | | Sort function to be called if a sortable header is clicked. `(dataKey: string, sortDirection: SortDirection): void` | @@ -76,7 +76,7 @@ ReactDOM.render( height={300} headerHeight={20} rowHeight={30} - rowsCount={list.length} + rowCount={list.length} rowGetter={index => list[index]} > , columnStartIndex: number, columnStopIndex: number, cellRenderer: Function, rowMetadata:Array, rowStartIndex: number, rowStopIndex: number }): Array` | | className | String | | Optional custom CSS class name to attach to root Grid element. | -| columnsCount | Number | ✓ | Number of columns in grid. | +| columnCount | Number | ✓ | Number of columns in grid. | | columnWidth | Number or Function | ✓ | Either a fixed column width (number) or a function that returns the width of a column given its index: `(index: number): number` | | height | Number | ✓ | Height of Grid; this property determines the number of visible (vs virtualized) rows. | -| noContentRenderer | Function | | Optional renderer to be rendered inside the grid when either `rowsCount` or `columnsCount` is 0: `(): PropTypes.node` | +| noContentRenderer | Function | | Optional renderer to be rendered inside the grid when either `rowCount` or `columnCount` is 0: `(): PropTypes.node` | | onSectionRendered | Function | | Callback invoked with information about the section of the Grid that was just rendered: `({ columnOverscanStartIndex, columnOverscanStopIndex, columnStartIndex, columnStopIndex, rowOverscanStartIndex, rowOverscanStopIndex, rowStartIndex, rowStopIndex }): void` | | onScroll | Function | | Callback invoked whenever the scroll offset changes within the inner scrollable region: `({ clientHeight, clientWidth, scrollHeight, scrollLeft, scrollTop, scrollWidth }): void` | -| overscanColumnsCount | | Number | Number of columns to render before/after the visible slice of the grid. This can help reduce flickering during scrolling on certain browers/devices. | -| overscanRowsCount | | Number | Number of rows to render above/below the visible slice of the grid. This can help reduce flickering during scrolling on certain browers/devices. | -| renderCell | Function | ✓ | Responsible for rendering a cell given an row and column index: `({ columnIndex: number, rowIndex: number }): PropTypes.node` | -| renderCellRanges | Function | ✓ | Responsible for rendering a group of cells given their index ranges.: `({ columnMetadata:Array, columnStartIndex: number, columnStopIndex: number, renderCell: Function, rowMetadata:Array, rowStartIndex: number, rowStopIndex: number }): Array` | -| rowsCount | Number | ✓ | Number of rows in grid. | +| overscanColumnCount | | Number | Number of columns to render before/after the visible slice of the grid. This can help reduce flickering during scrolling on certain browers/devices. | +| overscanRowCount | | Number | Number of rows to render above/below the visible slice of the grid. This can help reduce flickering during scrolling on certain browers/devices. | +| rowCount | Number | ✓ | Number of rows in grid. | | rowHeight | Number or Function | ✓ | Either a fixed row height (number) or a function that returns the height of a row given its index: `(index: number): number` | | scrollLeft | Number | | Horizontal offset | | scrollToColumn | Number | | Column index to ensure visible (by forcefully scrolling if necessary) | @@ -33,7 +33,7 @@ Only a small number of cells are rendered based on the horizontal and vertical s Recomputes row heights and column widths. This function should be called if dynamic column or row sizes have changed but nothing else has. -Since Grid only receives `columnsCount` and `rowsCount` it has no way of detecting when the underlying data changes. +Since Grid only receives `columnCount` and `rowCount` it has no way of detecting when the underlying data changes. ### Class names @@ -68,9 +68,9 @@ ReactDOM.render( height={300} columnWidth={100} rowHeight={30} - columnsCount={list.length} - rowsCount={list.length} - renderCell={({ columnIndex, rowIndex }) => list[rowIndex][columnIndex]} + columnCount={list.length} + rowCount={list.length} + cellRenderer={({ columnIndex, rowIndex }) => list[rowIndex][columnIndex]} />, document.getElementById('example') ); diff --git a/docs/InfiniteLoader.md b/docs/InfiniteLoader.md index 6d021bbac..7c50d8d1f 100644 --- a/docs/InfiniteLoader.md +++ b/docs/InfiniteLoader.md @@ -10,7 +10,7 @@ High-order component that manages just-in-time fetching of data as a user scroll | isRowLoaded | Function | ✓ | Function responsible for tracking the loaded state of each row. It should implement the following signature: `(index: number): boolean` | | loadMoreRows | Function | ✓ | Callback to be invoked when more rows must be loaded. It should implement the following signature: `({ startIndex, stopIndex }): Promise`. The returned Promise should be resolved once row data has finished loading. It will be used to determine when to refresh the list with the newly-loaded data. This callback may be called multiple times in reaction to a single scroll event. | | minimumBatchSize | Number | | Minimum number of rows to be loaded at a time. This property can be used to batch requests to reduce HTTP requests. Defaults to `10`. | -| rowsCount | Number | ✓ | Number of rows in list; can be arbitrary high number if actual number is unknown. | +| rowCount | Number | ✓ | Number of rows in list; can be arbitrary high number if actual number is unknown. | | threshold | Number | | Threshold at which to pre-fetch data. A threshold X means that data will start loading when a user scrolls within X rows. This value defaults to 15. | ### Children function @@ -50,7 +50,7 @@ ReactDOM.render( {({ onRowsRendered, registerChild }) => ( list[index] // Could also be a DOM element diff --git a/docs/VirtualScroll.md b/docs/VirtualScroll.md index ef6a142b9..6a2600c79 100644 --- a/docs/VirtualScroll.md +++ b/docs/VirtualScroll.md @@ -8,13 +8,13 @@ This component renders a virtualized list of elements with either fixed or dynam |:---|:---|:---:|:---| | className | String | | CSS class name | | height | Number | ✓ | Height constraint for list (determines how many actual rows are rendered) | -| noRowsRenderer | Function | | Callback used to render placeholder content when `rowsCount` is 0 | +| noRowsRenderer | Function | | Callback used to render placeholder content when `rowCount` is 0 | | onRowsRendered | Function | | Callback invoked with information about the slice of rows that were just rendered: `({ overscanStartIndex, overscanStopIndex, startIndex, stopIndex }): void` | | onScroll | Function | | Callback invoked whenever the scroll offset changes within the inner scrollable region: `({ clientHeight, scrollHeight, scrollTop }): void` | -| overscanRowsCount | Number | | Number of rows to render above/below the visible bounds of the list. This can help reduce flickering during scrolling on certain browers/devices. | +| overscanRowCount | Number | | Number of rows to render above/below the visible bounds of the list. This can help reduce flickering during scrolling on certain browers/devices. | | rowHeight | Number or Function | ✓ | Either a fixed row height (number) or a function that returns the height of a row given its index: `(index: number): number` | | rowRenderer | Function | ✓ | Responsbile for rendering a row given an index. Signature should look like `(index: number): React.PropTypes.node` | -| rowsCount | Number | ✓ | Number of rows in list. | +| rowCount | Number | ✓ | Number of rows in list. | | scrollToIndex | Number | | Row index to ensure visible (by forcefully scrolling if necessary) | | scrollTop | Number | | Vertical offset | | width | Number | ✓ | Width of the list | @@ -61,7 +61,7 @@ ReactDOM.render( list[index] // Could also be a DOM element diff --git a/docs/creatingAnInfiniteLoadingList.md b/docs/creatingAnInfiniteLoadingList.md index 23864a9d2..6820cc1a2 100644 --- a/docs/creatingAnInfiniteLoadingList.md +++ b/docs/creatingAnInfiniteLoadingList.md @@ -17,7 +17,7 @@ function MyComponent ({ loadNextPage }) { // If there are more items to be loaded then add an extra row to hold a loading indicator. - const rowsCount = hasNextPage + const rowCount = hasNextPage ? list.size + 1 : list.size @@ -43,7 +43,7 @@ function MyComponent ({ {({ onRowsRendered, registerChild }) => ( diff --git a/playground/grid.js b/playground/grid.js index fb5ada1a5..43661270e 100644 --- a/playground/grid.js +++ b/playground/grid.js @@ -11,7 +11,7 @@ function getColumnWidth (columnIndex) { } } -function renderCell (params) { +function cellRenderer (params) { var key = `c:${params.columnIndex}, r:${params.rowIndex}` switch (params.columnIndex % 3) { case 0: @@ -41,13 +41,13 @@ var App = React.createClass({ return React.createElement( ReactVirtualized.Grid, { - columnsCount: 1000, + columnCount: 1000, columnWidth: getColumnWidth, height: params.height, - overscanRowsCount: 0, - renderCell: renderCell, + overscanRowCount: 0, + cellRenderer: cellRenderer, rowHeight: 30, - rowsCount: 1000, + rowCount: 1000, width: params.width } ) diff --git a/playground/hover.js b/playground/hover.js index 55188e7d1..430e6e8ff 100644 --- a/playground/hover.js +++ b/playground/hover.js @@ -4,7 +4,7 @@ var App = React.createClass({ }, render: function() { - var renderCell = this._renderCell + var cellRenderer = this._cellRenderer return React.createElement( ReactVirtualized.AutoSizer, @@ -15,14 +15,14 @@ var App = React.createClass({ return React.createElement( ReactVirtualized.Grid, { - columnsCount: 1000, + columnCount: 1000, columnWidth: 100, height: params.height, - overscanRowsCount: 0, + overscanRowCount: 0, ref: 'Grid', - renderCell: renderCell, + cellRenderer: cellRenderer, rowHeight: 30, - rowsCount: 1000, + rowCount: 1000, width: params.width } ) @@ -30,7 +30,7 @@ var App = React.createClass({ ) }, - _renderCell (params) { + _cellRenderer (params) { var columnIndex = params.columnIndex var rowIndex = params.rowIndex var key = `c:${columnIndex}, r:${rowIndex}` diff --git a/playground/tree.js b/playground/tree.js index 9b74d9849..9a38a47d8 100644 --- a/playground/tree.js +++ b/playground/tree.js @@ -56,7 +56,7 @@ function setRef (ref) { VirtualScroll = ref } -function renderCell (index) { +function cellRenderer (index) { return renderItem(data[index], index) } @@ -74,11 +74,11 @@ var App = React.createClass({ ReactVirtualized.VirtualScroll, { height: params.height, - overscanRowsCount: 10, + overscanRowCount: 10, ref: setRef, rowHeight: rowHeight, - rowRenderer: renderCell, - rowsCount: data.length, + rowRenderer: cellRenderer, + rowCount: data.length, width: params.width } ) diff --git a/source/ArrowKeyStepper/ArrowKeyStepper.example.js b/source/ArrowKeyStepper/ArrowKeyStepper.example.js index 1c82b6530..4253689db 100644 --- a/source/ArrowKeyStepper/ArrowKeyStepper.example.js +++ b/source/ArrowKeyStepper/ArrowKeyStepper.example.js @@ -19,7 +19,7 @@ export default class ArrowKeyStepperExample extends Component { this._getColumnWidth = this._getColumnWidth.bind(this) this._getRowHeight = this._getRowHeight.bind(this) - this._renderCell = this._renderCell.bind(this) + this._cellRenderer = this._cellRenderer.bind(this) } render () { @@ -43,8 +43,8 @@ export default class ArrowKeyStepperExample extends Component { {({ onSectionRendered, scrollToColumn, scrollToRow }) => (
@@ -57,12 +57,12 @@ export default class ArrowKeyStepperExample extends Component { this._renderCell({ columnIndex, rowIndex, scrollToColumn, scrollToRow }) } + cellRenderer={({ columnIndex, rowIndex }) => this._cellRenderer({ columnIndex, rowIndex, scrollToColumn, scrollToRow }) } rowHeight={this._getRowHeight} - rowsCount={100} + rowCount={100} scrollToColumn={scrollToColumn} scrollToRow={scrollToRow} width={width} @@ -88,7 +88,7 @@ export default class ArrowKeyStepperExample extends Component { return (1 + (index % 3)) * 30 } - _renderCell ({ columnIndex, rowIndex, scrollToColumn, scrollToRow }) { + _cellRenderer ({ columnIndex, rowIndex, scrollToColumn, scrollToRow }) { const className = cn(styles.Cell, { [styles.FocusedCell]: columnIndex === scrollToColumn && rowIndex === scrollToRow }) diff --git a/source/ArrowKeyStepper/ArrowKeyStepper.js b/source/ArrowKeyStepper/ArrowKeyStepper.js index b2cf0a887..5d7a800fd 100644 --- a/source/ArrowKeyStepper/ArrowKeyStepper.js +++ b/source/ArrowKeyStepper/ArrowKeyStepper.js @@ -9,8 +9,8 @@ export default class ArrowKeyStepper extends Component { static propTypes = { children: PropTypes.func.isRequired, className: PropTypes.string, - columnsCount: PropTypes.number.isRequired, - rowsCount: PropTypes.number.isRequired + columnCount: PropTypes.number.isRequired, + rowCount: PropTypes.number.isRequired } constructor (props, context) { @@ -53,7 +53,7 @@ export default class ArrowKeyStepper extends Component { } _onKeyDown (event) { - const { columnsCount, rowsCount } = this.props + const { columnCount, rowCount } = this.props // The above cases all prevent default event event behavior. // This is to keep the grid from scrolling after the snap-to update. @@ -61,7 +61,7 @@ export default class ArrowKeyStepper extends Component { case 'ArrowDown': event.preventDefault() this.setState({ - scrollToRow: Math.min(this._rowStopIndex + 1, rowsCount - 1) + scrollToRow: Math.min(this._rowStopIndex + 1, rowCount - 1) }) break case 'ArrowLeft': @@ -73,7 +73,7 @@ export default class ArrowKeyStepper extends Component { case 'ArrowRight': event.preventDefault() this.setState({ - scrollToColumn: Math.min(this._columnStopIndex + 1, columnsCount - 1) + scrollToColumn: Math.min(this._columnStopIndex + 1, columnCount - 1) }) break case 'ArrowUp': diff --git a/source/ArrowKeyStepper/ArrowKeyStepper.test.js b/source/ArrowKeyStepper/ArrowKeyStepper.test.js index 6225f063c..6482f6859 100644 --- a/source/ArrowKeyStepper/ArrowKeyStepper.test.js +++ b/source/ArrowKeyStepper/ArrowKeyStepper.test.js @@ -17,16 +17,16 @@ function ChildComponent ({ scrollToColumn, scrollToRow }) { describe('ArrowKeyStepper', () => { function renderHelper ({ className, - columnsCount = 10, - rowsCount = 10 + columnCount = 10, + rowCount = 10 } = {}) { let onSectionRenderedCallback const node = findDOMNode(render( {({ onSectionRendered, scrollToColumn, scrollToRow }) => { onSectionRenderedCallback = onSectionRendered @@ -71,8 +71,8 @@ describe('ArrowKeyStepper', () => { it('should not scroll past the row and column boundaries provided', () => { const { node } = renderHelper({ - columnsCount: 2, - rowsCount: 2 + columnCount: 2, + rowCount: 2 }) Simulate.keyDown(node, {key: 'ArrowDown'}) Simulate.keyDown(node, {key: 'ArrowDown'}) diff --git a/source/AutoSizer/AutoSizer.example.js b/source/AutoSizer/AutoSizer.example.js index b32d705b0..016c2a10e 100644 --- a/source/AutoSizer/AutoSizer.example.js +++ b/source/AutoSizer/AutoSizer.example.js @@ -66,7 +66,7 @@ export default class AutoSizerExample extends Component { 0 && width > 0 - ? cellLayoutManager.renderCells({ + ? cellLayoutManager.cellRenderers({ height, isScrolling, width, diff --git a/source/ColumnSizer/ColumnSizer.example.js b/source/ColumnSizer/ColumnSizer.example.js index b0a9cf8a6..44f3b5b43 100644 --- a/source/ColumnSizer/ColumnSizer.example.js +++ b/source/ColumnSizer/ColumnSizer.example.js @@ -22,14 +22,14 @@ export default class ColumnSizerExample extends Component { this.state = { columnMaxWidth: 100, columnMinWidth: 75, - columnsCount: 10 + columnCount: 10 } this._noColumnMaxWidthChange = this._noColumnMaxWidthChange.bind(this) this._noColumnMinWidthChange = this._noColumnMinWidthChange.bind(this) - this._onColumnsCountChange = this._onColumnsCountChange.bind(this) + this._onColumnCountChange = this._onColumnCountChange.bind(this) this._noContentRenderer = this._noContentRenderer.bind(this) - this._renderCell = this._renderCell.bind(this) + this._cellRenderer = this._cellRenderer.bind(this) } render () { @@ -38,7 +38,7 @@ export default class ColumnSizerExample extends Component { const { columnMaxWidth, columnMinWidth, - columnsCount + columnCount } = this.state return ( @@ -56,9 +56,9 @@ export default class ColumnSizerExample extends Component { @@ -95,12 +95,12 @@ export default class ColumnSizerExample extends Component {
@@ -141,8 +141,8 @@ export default class ColumnSizerExample extends Component { this.setState({ columnMinWidth }) } - _onColumnsCountChange (event) { - this.setState({ columnsCount: parseInt(event.target.value, 10) || 0 }) + _onColumnCountChange (event) { + this.setState({ columnCount: parseInt(event.target.value, 10) || 0 }) } _noContentRenderer () { @@ -153,7 +153,7 @@ export default class ColumnSizerExample extends Component { ) } - _renderCell ({ columnIndex, rowIndex }) { + _cellRenderer ({ columnIndex, rowIndex }) { const className = columnIndex === 0 ? styles.firstCell : styles.cell diff --git a/source/ColumnSizer/ColumnSizer.js b/source/ColumnSizer/ColumnSizer.js index 2c62c97dd..78e19e261 100644 --- a/source/ColumnSizer/ColumnSizer.js +++ b/source/ColumnSizer/ColumnSizer.js @@ -26,7 +26,7 @@ export default class ColumnSizer extends Component { columnMinWidth: PropTypes.number, /** Number of columns in Grid or FlexTable child */ - columnsCount: PropTypes.number.isRequired, + columnCount: PropTypes.number.isRequired, /** Width of Grid or FlexTable child */ width: PropTypes.number.isRequired @@ -42,14 +42,14 @@ export default class ColumnSizer extends Component { const { columnMaxWidth, columnMinWidth, - columnsCount, + columnCount, width } = this.props if ( columnMaxWidth !== prevProps.columnMaxWidth || columnMinWidth !== prevProps.columnMinWidth || - columnsCount !== prevProps.columnsCount || + columnCount !== prevProps.columnCount || width !== prevProps.width ) { if (this._registeredChild) { @@ -63,7 +63,7 @@ export default class ColumnSizer extends Component { children, columnMaxWidth, columnMinWidth, - columnsCount, + columnCount, width } = this.props @@ -73,12 +73,12 @@ export default class ColumnSizer extends Component { ? Math.min(columnMaxWidth, width) : width - let columnWidth = width / columnsCount + let columnWidth = width / columnCount columnWidth = Math.max(safeColumnMinWidth, columnWidth) columnWidth = Math.min(safeColumnMaxWidth, columnWidth) columnWidth = Math.floor(columnWidth) - let adjustedWidth = Math.min(width, columnWidth * columnsCount) + let adjustedWidth = Math.min(width, columnWidth * columnCount) return children({ adjustedWidth, diff --git a/source/ColumnSizer/ColumnSizer.test.js b/source/ColumnSizer/ColumnSizer.test.js index 427498cb2..7cef1609d 100644 --- a/source/ColumnSizer/ColumnSizer.test.js +++ b/source/ColumnSizer/ColumnSizer.test.js @@ -8,10 +8,10 @@ describe('ColumnSizer', () => { function getMarkup ({ columnMinWidth = undefined, columnMaxWidth = undefined, - columnsCount = 10, + columnCount = 10, width = 200 } = {}) { - function renderCell ({ columnIndex, rowIndex }) { + function cellRenderer ({ columnIndex, rowIndex }) { return (
{`row:${rowIndex}, column:${columnIndex}`} @@ -23,19 +23,19 @@ describe('ColumnSizer', () => { {({ adjustedWidth, getColumnWidth, registerChild }) => (
@@ -87,8 +87,8 @@ describe('ColumnSizer', () => { helper({ width: 300 }, 'columnWidth:30') }) - it('should recompute metadata sizes if :columnsCount changes', () => { - helper({ columnsCount: 2 }, 'columnWidth:100') + it('should recompute metadata sizes if :columnCount changes', () => { + helper({ columnCount: 2 }, 'columnWidth:100') }) }) @@ -112,7 +112,7 @@ describe('ColumnSizer', () => { {({ adjustedWidth, getColumnWidth, registerChild }) => ( diff --git a/source/FlexTable/FlexTable.example.js b/source/FlexTable/FlexTable.example.js index 0affcb85e..2a25d6ffe 100644 --- a/source/FlexTable/FlexTable.example.js +++ b/source/FlexTable/FlexTable.example.js @@ -23,9 +23,9 @@ export default class FlexTableExample extends Component { headerHeight: 30, height: 270, hideIndexRow: false, - overscanRowsCount: 0, + overscanRowCount: 0, rowHeight: 40, - rowsCount: 1000, + rowCount: 1000, scrollToIndex: undefined, sortBy: 'index', sortDirection: SortDirection.ASC, @@ -35,7 +35,7 @@ export default class FlexTableExample extends Component { this._getRowHeight = this._getRowHeight.bind(this) this._headerRenderer = this._headerRenderer.bind(this) this._noRowsRenderer = this._noRowsRenderer.bind(this) - this._onRowsCountChange = this._onRowsCountChange.bind(this) + this._onRowCountChange = this._onRowCountChange.bind(this) this._onScrollToRowChange = this._onScrollToRowChange.bind(this) this._sort = this._sort.bind(this) } @@ -45,9 +45,9 @@ export default class FlexTableExample extends Component { headerHeight, height, hideIndexRow, - overscanRowsCount, + overscanRowCount, rowHeight, - rowsCount, + rowCount, scrollToIndex, sortBy, sortDirection, @@ -109,9 +109,9 @@ export default class FlexTableExample extends Component { this.setState({ overscanRowsCount: parseInt(event.target.value, 10) || 0 })} - value={overscanRowsCount} + name='overscanRowCount' + onChange={event => this.setState({ overscanRowCount: parseInt(event.target.value, 10) || 0 })} + value={overscanRowCount} /> @@ -156,11 +156,11 @@ export default class FlexTableExample extends Component { headerHeight={headerHeight} height={height} noRowsRenderer={this._noRowsRenderer} - overscanRowsCount={overscanRowsCount} + overscanRowCount={overscanRowCount} rowClassName={::this._rowClassName} rowHeight={useDynamicRowHeight ? this._getRowHeight : rowHeight} rowGetter={rowGetter} - rowsCount={rowsCount} + rowCount={rowCount} scrollToIndex={scrollToIndex} sort={this._sort} sortBy={sortBy} @@ -237,9 +237,9 @@ export default class FlexTableExample extends Component { _isSortEnabled () { const { list } = this.props - const { rowsCount } = this.state + const { rowCount } = this.state - return rowsCount <= list.size + return rowCount <= list.size } _noRowsRenderer () { @@ -250,15 +250,15 @@ export default class FlexTableExample extends Component { ) } - _onRowsCountChange (event) { - const rowsCount = parseInt(event.target.value, 10) || 0 + _onRowCountChange (event) { + const rowCount = parseInt(event.target.value, 10) || 0 - this.setState({ rowsCount }) + this.setState({ rowCount }) } _onScrollToRowChange (event) { - const { rowsCount } = this.state - let scrollToIndex = Math.min(rowsCount - 1, parseInt(event.target.value, 10)) + const { rowCount } = this.state + let scrollToIndex = Math.min(rowCount - 1, parseInt(event.target.value, 10)) if (isNaN(scrollToIndex)) { scrollToIndex = undefined diff --git a/source/FlexTable/FlexTable.js b/source/FlexTable/FlexTable.js index a237a7ace..cc67a9c41 100644 --- a/source/FlexTable/FlexTable.js +++ b/source/FlexTable/FlexTable.js @@ -40,7 +40,7 @@ export default class FlexTable extends Component { /** Fixed/available height for out DOM element */ height: PropTypes.number.isRequired, - /** Optional renderer to be used in place of table body rows when rowsCount is 0 */ + /** Optional renderer to be used in place of table body rows when rowCount is 0 */ noRowsRenderer: PropTypes.func, /** @@ -72,7 +72,7 @@ export default class FlexTable extends Component { * Number of rows to render above/below the visible bounds of the list. * These rows can help for smoother scrolling on touch devices. */ - overscanRowsCount: PropTypes.number.isRequired, + overscanRowCount: PropTypes.number.isRequired, /** * Optional CSS class to apply to all table rows (including the header row). @@ -94,7 +94,7 @@ export default class FlexTable extends Component { rowHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.func]).isRequired, /** Number of rows in table. */ - rowsCount: PropTypes.number.isRequired, + rowCount: PropTypes.number.isRequired, /** Row index to ensure visible (by forcefully scrolling if necessary) */ scrollToIndex: PropTypes.number, @@ -124,7 +124,7 @@ export default class FlexTable extends Component { noRowsRenderer: () => null, onRowsRendered: () => null, onScroll: () => null, - overscanRowsCount: 10 + overscanRowCount: 10 } constructor (props) { @@ -161,10 +161,10 @@ export default class FlexTable extends Component { noRowsRenderer, onRowsRendered, onScroll, - overscanRowsCount, + overscanRowCount, rowClassName, rowHeight, - rowsCount, + rowCount, scrollToIndex, scrollTop, width @@ -203,7 +203,7 @@ export default class FlexTable extends Component { ref='Grid' className={'FlexTable__Grid'} columnWidth={width} - columnsCount={1} + columnCount={1} height={availableRowsHeight} noContentRenderer={noRowsRenderer} onScroll={({ clientHeight, scrollHeight, scrollTop }) => onScroll({ clientHeight, scrollHeight, scrollTop })} @@ -213,10 +213,10 @@ export default class FlexTable extends Component { startIndex: rowStartIndex, stopIndex: rowStopIndex })} - overscanRowsCount={overscanRowsCount} - renderCell={({ columnIndex, rowIndex }) => rowRenderer(rowIndex)} + overscanRowCount={overscanRowCount} + cellRenderer={({ columnIndex, rowIndex }) => rowRenderer(rowIndex)} rowHeight={rowHeight} - rowsCount={rowsCount} + rowCount={rowCount} scrollToRow={scrollToIndex} scrollTop={scrollTop} width={width} diff --git a/source/FlexTable/FlexTable.test.js b/source/FlexTable/FlexTable.test.js index 1729d39c5..2624a7b29 100644 --- a/source/FlexTable/FlexTable.test.js +++ b/source/FlexTable/FlexTable.test.js @@ -45,11 +45,11 @@ describe('FlexTable', () => { onRowClick = undefined, onRowsRendered = undefined, onScroll = undefined, - overscanRowsCount = 0, + overscanRowCount = 0, rowClassName = undefined, rowGetter = immutableRowGetter, rowHeight = 10, - rowsCount = list.size, + rowCount = list.size, scrollToIndex, scrollTop, sort, @@ -68,11 +68,11 @@ describe('FlexTable', () => { onRowClick={onRowClick} onRowsRendered={onRowsRendered} onScroll={onScroll} - overscanRowsCount={overscanRowsCount} + overscanRowCount={overscanRowCount} rowClassName={rowClassName} rowGetter={rowGetter} rowHeight={rowHeight} - rowsCount={rowsCount} + rowCount={rowCount} scrollToIndex={scrollToIndex} scrollTop={scrollTop} sort={sort} @@ -185,7 +185,7 @@ describe('FlexTable', () => { const rowHeight = (index) => 10 + index * 10 const rendered = findDOMNode(render(getMarkup({ rowHeight, - rowsCount: 3 + rowCount: 3 }))) const rows = rendered.querySelectorAll('.FlexTable__row') Array.from(rows).forEach((row, index) => { @@ -197,7 +197,7 @@ describe('FlexTable', () => { const rendered = findDOMNode(render(getMarkup({ maxWidth: 75, minWidth: 25, - rowsCount: 1 + rowCount: 1 }))) const columns = rendered.querySelectorAll('.FlexTable__rowColumn') const emailColumn = columns[1] @@ -215,7 +215,7 @@ describe('FlexTable', () => { } const component = render(getMarkup({ rowHeight, - rowsCount: 50 + rowCount: 50 })) highestRowIndex = 0 component.recomputeRowHeights() @@ -426,18 +426,18 @@ describe('FlexTable', () => { }) describe('noRowsRenderer', () => { - it('should call :noRowsRenderer if :rowsCount is 0', () => { + it('should call :noRowsRenderer if :rowCount is 0', () => { const rendered = render(getMarkup({ noRowsRenderer: () =>
No rows!
, - rowsCount: 0 + rowCount: 0 })) const bodyDOMNode = findDOMNode(rendered.refs.Grid) expect(bodyDOMNode.textContent).toEqual('No rows!') }) - it('should render an empty body if :rowsCount is 0 and there is no :noRowsRenderer', () => { + it('should render an empty body if :rowCount is 0 and there is no :noRowsRenderer', () => { const rendered = render(getMarkup({ - rowsCount: 0 + rowCount: 0 })) const bodyDOMNode = findDOMNode(rendered.refs.Grid) expect(bodyDOMNode.textContent).toEqual('') @@ -637,7 +637,7 @@ describe('FlexTable', () => { }) }) - describe('overscanRowsCount', () => { + describe('overscanRowCount', () => { it('should not overscan by default', () => { let overscanStartIndex, overscanStopIndex, startIndex, stopIndex render(getMarkup({ @@ -651,7 +651,7 @@ describe('FlexTable', () => { let overscanStartIndex, overscanStopIndex, startIndex, stopIndex render(getMarkup({ onRowsRendered: params => ({ overscanStartIndex, overscanStopIndex, startIndex, stopIndex } = params), - overscanRowsCount: 10, + overscanRowCount: 10, scrollToIndex: 30 })) expect(overscanStartIndex).toEqual(13) @@ -664,7 +664,7 @@ describe('FlexTable', () => { let overscanStartIndex, overscanStopIndex, startIndex, stopIndex render(getMarkup({ onRowsRendered: params => ({ overscanStartIndex, overscanStopIndex, startIndex, stopIndex } = params), - overscanRowsCount: 10 + overscanRowCount: 10 })) expect(overscanStartIndex).toEqual(0) expect(startIndex).toEqual(0) @@ -676,8 +676,8 @@ describe('FlexTable', () => { let overscanStartIndex, overscanStopIndex, startIndex, stopIndex render(getMarkup({ onRowsRendered: params => ({ overscanStartIndex, overscanStopIndex, startIndex, stopIndex } = params), - overscanRowsCount: 10, - rowsCount: 15 + overscanRowCount: 10, + rowCount: 15 })) expect(overscanStartIndex).toEqual(0) expect(startIndex).toEqual(0) diff --git a/source/Grid/Grid.example.js b/source/Grid/Grid.example.js index 2cdb73f3a..10b96df0b 100644 --- a/source/Grid/Grid.example.js +++ b/source/Grid/Grid.example.js @@ -19,12 +19,12 @@ export default class GridExample extends Component { this.state = { columnWidth: 100, - columnsCount: 1000, + columnCount: 1000, height: 300, - overscanColumnsCount: 0, - overscanRowsCount: 5, + overscanColumnCount: 0, + overscanRowCount: 5, rowHeight: 40, - rowsCount: 1000, + rowCount: 1000, scrollToColumn: undefined, scrollToRow: undefined, useDynamicRowHeight: false @@ -34,12 +34,12 @@ export default class GridExample extends Component { this._getRowClassName = this._getRowClassName.bind(this) this._getRowHeight = this._getRowHeight.bind(this) this._noContentRenderer = this._noContentRenderer.bind(this) - this._onColumnsCountChange = this._onColumnsCountChange.bind(this) - this._onRowsCountChange = this._onRowsCountChange.bind(this) + this._onColumnCountChange = this._onColumnCountChange.bind(this) + this._onRowCountChange = this._onRowCountChange.bind(this) this._onScrollToColumnChange = this._onScrollToColumnChange.bind(this) this._onScrollToRowChange = this._onScrollToRowChange.bind(this) this._renderBodyCell = this._renderBodyCell.bind(this) - this._renderCell = this._renderCell.bind(this) + this._cellRenderer = this._cellRenderer.bind(this) this._renderLeftSideCell = this._renderLeftSideCell.bind(this) } @@ -47,12 +47,12 @@ export default class GridExample extends Component { const { list, ...props } = this.props const { - columnsCount, + columnCount, height, - overscanColumnsCount, - overscanRowsCount, + overscanColumnCount, + overscanRowCount, rowHeight, - rowsCount, + rowCount, scrollToColumn, scrollToRow, useDynamicRowHeight @@ -87,15 +87,15 @@ export default class GridExample extends Component { this.setState({ overscanColumnsCount: parseInt(event.target.value, 10) || 0 })} - value={overscanColumnsCount} + name='overscanColumnCount' + onChange={event => this.setState({ overscanColumnCount: parseInt(event.target.value, 10) || 0 })} + value={overscanColumnCount} /> this.setState({ overscanRowsCount: parseInt(event.target.value, 10) || 0 })} - value={overscanRowsCount} + name='overscanRowCount' + onChange={event => this.setState({ overscanRowCount: parseInt(event.target.value, 10) || 0 })} + value={overscanRowCount} /> @@ -143,14 +143,14 @@ export default class GridExample extends Component { , + * columnStartIndex: number, + * columnStopIndex: number, + * cellRenderer: Function, + * rowMetadata:Array, + * rowStartIndex: number, + * rowStopIndex: number + * }): Array + */ + cellRangeRenderer: PropTypes.func.isRequired, + /** * Optional custom CSS class name to attach to root Grid element. */ @@ -44,7 +64,7 @@ export default class Grid extends Component { /** * Number of columns in grid. */ - columnsCount: PropTypes.number.isRequired, + columnCount: PropTypes.number.isRequired, /** * Either a fixed column width (number) or a function that returns the width of a column given its index. @@ -58,7 +78,7 @@ export default class Grid extends Component { height: PropTypes.number.isRequired, /** - * Optional renderer to be used in place of rows when either :rowsCount or :columnsCount is 0. + * Optional renderer to be used in place of rows when either :rowCount or :columnCount is 0. */ noContentRenderer: PropTypes.func.isRequired, @@ -79,33 +99,13 @@ export default class Grid extends Component { * Number of columns to render before/after the visible section of the grid. * These columns can help for smoother scrolling on touch devices or browsers that send scroll events infrequently. */ - overscanColumnsCount: PropTypes.number.isRequired, + overscanColumnCount: PropTypes.number.isRequired, /** * Number of rows to render above/below the visible section of the grid. * These rows can help for smoother scrolling on touch devices or browsers that send scroll events infrequently. */ - overscanRowsCount: PropTypes.number.isRequired, - - /** - * Responsible for rendering a cell given an row and column index. - * Should implement the following interface: ({ columnIndex: number, rowIndex: number }): PropTypes.node - */ - renderCell: PropTypes.func.isRequired, - - /** - * Responsible for rendering a group of cells given their index ranges. - * Should implement the following interface: ({ - * columnMetadata:Array, - * columnStartIndex: number, - * columnStopIndex: number, - * renderCell: Function, - * rowMetadata:Array, - * rowStartIndex: number, - * rowStopIndex: number - * }): Array - */ - renderCellRanges: PropTypes.func.isRequired, + overscanRowCount: PropTypes.number.isRequired, /** * Either a fixed row height (number) or a function that returns the height of a row given its index. @@ -116,7 +116,7 @@ export default class Grid extends Component { /** * Number of rows in grid. */ - rowsCount: PropTypes.number.isRequired, + rowCount: PropTypes.number.isRequired, /** Horizontal offset. */ scrollLeft: PropTypes.number, @@ -145,9 +145,9 @@ export default class Grid extends Component { noContentRenderer: () => null, onScroll: () => null, onSectionRendered: () => null, - overscanColumnsCount: 0, - overscanRowsCount: 10, - renderCellRanges: defaultRenderCellRanges + overscanColumnCount: 0, + overscanRowCount: 10, + cellRangeRenderer: defaultRenderCellRanges }; constructor (props, context) { @@ -176,7 +176,7 @@ export default class Grid extends Component { /** * Forced recompute of row heights and column widths. * This function should be called if dynamic column or row sizes have changed but nothing else has. - * Since Grid only receives :columnsCount and :rowsCount it has no way of detecting when the underlying data changes. + * Since Grid only receives :columnCount and :rowCount it has no way of detecting when the underlying data changes. */ recomputeGridSize () { this.setState({ @@ -216,7 +216,7 @@ export default class Grid extends Component { * 1) New scroll-to-cell props have been set */ componentDidUpdate (prevProps, prevState) { - const { columnsCount, columnWidth, height, rowHeight, rowsCount, scrollToColumn, scrollToRow, width } = this.props + const { columnCount, columnWidth, height, rowHeight, rowCount, scrollToColumn, scrollToRow, width } = this.props const { scrollLeft, scrollPositionChangeReason, scrollTop } = this.state // Make sure requested changes to :scrollLeft or :scrollTop get applied. @@ -244,10 +244,10 @@ export default class Grid extends Component { // Update scroll offsets if the current :scrollToColumn or :scrollToRow values requires it // @TODO Do we also need this check or can the one in componentWillUpdate() suffice? updateScrollIndexHelper({ - cellCount: columnsCount, + cellCount: columnCount, cellMetadata: this._columnMetadata, cellSize: columnWidth, - previousCellsCount: prevProps.columnsCount, + previousCellsCount: prevProps.columnCount, previousCellSize: prevProps.columnWidth, previousScrollToIndex: prevProps.scrollToColumn, previousSize: prevProps.width, @@ -257,10 +257,10 @@ export default class Grid extends Component { updateScrollIndexCallback: (scrollToColumn) => this._updateScrollLeftForScrollToColumn({ ...this.props, scrollToColumn }) }) updateScrollIndexHelper({ - cellCount: rowsCount, + cellCount: rowCount, cellMetadata: this._rowMetadata, cellSize: rowHeight, - previousCellsCount: prevProps.rowsCount, + previousCellsCount: prevProps.rowCount, previousCellSize: prevProps.rowHeight, previousScrollToIndex: prevProps.scrollToRow, previousSize: prevProps.height, @@ -298,9 +298,9 @@ export default class Grid extends Component { */ componentWillUpdate (nextProps, nextState) { if ( - nextProps.columnsCount === 0 && + nextProps.columnCount === 0 && nextState.scrollLeft !== 0 || - nextProps.rowsCount === 0 && + nextProps.rowCount === 0 && nextState.scrollTop !== 0 ) { this._setScrollPosition({ @@ -319,24 +319,24 @@ export default class Grid extends Component { // Update scroll offsets if the size or number of cells have changed, invalidating the previous value calculateSizeAndPositionDataAndUpdateScrollOffset({ - cellCount: this.props.columnsCount, + cellCount: this.props.columnCount, cellSize: this.props.columnWidth, computeMetadataCallback: this._computeColumnMetadata, computeMetadataCallbackProps: nextProps, computeMetadataOnNextUpdate: nextState.computeGridMetadataOnNextUpdate, - nextCellsCount: nextProps.columnsCount, + nextCellsCount: nextProps.columnCount, nextCellSize: nextProps.columnWidth, nextScrollToIndex: nextProps.scrollToColumn, scrollToIndex: this.props.scrollToColumn, updateScrollOffsetForScrollToIndex: () => this._updateScrollLeftForScrollToColumn(nextProps, nextState) }) calculateSizeAndPositionDataAndUpdateScrollOffset({ - cellCount: this.props.rowsCount, + cellCount: this.props.rowCount, cellSize: this.props.rowHeight, computeMetadataCallback: this._computeRowMetadata, computeMetadataCallbackProps: nextProps, computeMetadataOnNextUpdate: nextState.computeGridMetadataOnNextUpdate, - nextCellsCount: nextProps.rowsCount, + nextCellsCount: nextProps.rowCount, nextCellSize: nextProps.rowHeight, nextScrollToIndex: nextProps.scrollToRow, scrollToIndex: this.props.scrollToRow, @@ -350,15 +350,15 @@ export default class Grid extends Component { render () { const { + cellRenderer, + cellRangeRenderer, className, - columnsCount, + columnCount, height, noContentRenderer, - overscanColumnsCount, - overscanRowsCount, - renderCell, - renderCellRanges, - rowsCount, + overscanColumnCount, + overscanRowCount, + rowCount, width } = this.props @@ -391,15 +391,15 @@ export default class Grid extends Component { this._renderedRowStopIndex = visibleRowIndices.stop const overscanColumnIndices = getOverscanIndices({ - cellCount: columnsCount, - overscanCellsCount: overscanColumnsCount, + cellCount: columnCount, + overscanCellsCount: overscanColumnCount, startIndex: this._renderedColumnStartIndex, stopIndex: this._renderedColumnStopIndex }) const overscanRowIndices = getOverscanIndices({ - cellCount: rowsCount, - overscanCellsCount: overscanRowsCount, + cellCount: rowCount, + overscanCellsCount: overscanRowCount, startIndex: this._renderedRowStartIndex, stopIndex: this._renderedRowStopIndex }) @@ -410,11 +410,11 @@ export default class Grid extends Component { this._rowStartIndex = overscanRowIndices.overscanStartIndex this._rowStopIndex = overscanRowIndices.overscanStopIndex - childrenToDisplay = renderCellRanges({ + childrenToDisplay = cellRangeRenderer({ + cellRenderer, columnMetadata: this._columnMetadata, columnStartIndex: this._columnStartIndex, columnStopIndex: this._columnStopIndex, - renderCell, rowMetadata: this._rowMetadata, rowStartIndex: this._rowStartIndex, rowStopIndex: this._rowStopIndex @@ -478,19 +478,19 @@ export default class Grid extends Component { /* ---------------------------- Helper methods ---------------------------- */ _computeColumnMetadata (props) { - const { columnsCount, columnWidth } = props + const { columnCount, columnWidth } = props this._columnMetadata = initCellMetadata({ - cellCount: columnsCount, + cellCount: columnCount, size: columnWidth }) } _computeRowMetadata (props) { - const { rowHeight, rowsCount } = props + const { rowHeight, rowCount } = props this._rowMetadata = initCellMetadata({ - cellCount: rowsCount, + cellCount: rowCount, size: rowHeight }) } @@ -608,10 +608,10 @@ export default class Grid extends Component { } _updateScrollLeftForScrollToColumn (props = null, state = null) { - const { columnsCount, scrollToColumn, width } = props || this.props + const { columnCount, scrollToColumn, width } = props || this.props const { scrollLeft } = state || this.state - if (scrollToColumn >= 0 && columnsCount > 0) { + if (scrollToColumn >= 0 && columnCount > 0) { const targetIndex = getNearestIndex({ cellCount: this._columnMetadata.length, targetIndex: scrollToColumn @@ -636,10 +636,10 @@ export default class Grid extends Component { } _updateScrollTopForScrollToRow (props = null, state = null) { - const { height, rowsCount, scrollToRow } = props || this.props + const { height, rowCount, scrollToRow } = props || this.props const { scrollTop } = state || this.state - if (scrollToRow >= 0 && rowsCount > 0) { + if (scrollToRow >= 0 && rowCount > 0) { const targetIndex = getNearestIndex({ cellCount: this._rowMetadata.length, targetIndex: scrollToRow @@ -720,10 +720,10 @@ export default class Grid extends Component { } function defaultRenderCellRanges ({ + cellRenderer, columnMetadata, columnStartIndex, columnStopIndex, - renderCell, rowMetadata, rowStartIndex, rowStopIndex @@ -735,7 +735,7 @@ function defaultRenderCellRanges ({ for (let columnIndex = columnStartIndex; columnIndex <= columnStopIndex; columnIndex++) { let columnDatum = columnMetadata[columnIndex] - let renderedCell = renderCell({ columnIndex, rowIndex }) + let renderedCell = cellRenderer({ columnIndex, rowIndex }) let key = `${rowIndex}-${columnIndex}` if (renderedCell == null || renderedCell === false) { diff --git a/source/Grid/Grid.test.js b/source/Grid/Grid.test.js index 3bf7f77be..5b7947cf9 100644 --- a/source/Grid/Grid.test.js +++ b/source/Grid/Grid.test.js @@ -10,18 +10,18 @@ const NUM_COLUMNS = 50 describe('Grid', () => { function getMarkup ({ className, - columnsCount = NUM_COLUMNS, + columnCount = NUM_COLUMNS, columnWidth = 50, height = 100, noContentRenderer, onSectionRendered, onScroll, - overscanColumnsCount = 0, - overscanRowsCount = 0, - renderCell, - renderCellRanges, + overscanColumnCount = 0, + overscanRowCount = 0, + cellRenderer, + cellRangeRenderer, rowHeight = 20, - rowsCount = NUM_ROWS, + rowCount = NUM_ROWS, scrollLeft = undefined, scrollToColumn, scrollToRow, @@ -39,18 +39,18 @@ describe('Grid', () => { return ( { }) it('should not render more rows than available if the area is not filled', () => { - const rendered = findDOMNode(render(getMarkup({ rowsCount: 2 }))) + const rendered = findDOMNode(render(getMarkup({ rowCount: 2 }))) expect(rendered.querySelectorAll('.gridItem').length).toEqual(8) // 2 rows x 4 columns }) it('should not render more columns than available if the area is not filled', () => { - const rendered = findDOMNode(render(getMarkup({ columnsCount: 2 }))) + const rendered = findDOMNode(render(getMarkup({ columnCount: 2 }))) expect(rendered.querySelectorAll('.gridItem').length).toEqual(10) // 5 rows x 2 columns }) // Small performance tweak added in 5.5.6 it('should not render/parent cells that are null or false', () => { - function renderCell ({ columnIndex, rowIndex }) { + function cellRenderer ({ columnIndex, rowIndex }) { if (columnIndex === 0) { return null } else if (rowIndex === 0) { @@ -88,11 +88,11 @@ describe('Grid', () => { } } const rendered = findDOMNode(render(getMarkup({ - columnsCount: 3, - overscanColumnsCount: 0, - overscanRowsCount: 0, - rowsCount: 3, - renderCell + columnCount: 3, + overscanColumnCount: 0, + overscanRowCount: 0, + rowCount: 3, + cellRenderer }))) expect(rendered.querySelectorAll('.Grid__cell').length).toEqual(4) // [1,1], [1,2], [2,1], and [2,2] expect(rendered.textContent).not.toContain('column:0') @@ -102,22 +102,22 @@ describe('Grid', () => { describe('shows and hides scrollbars based on rendered content', () => { it('should set overflowX:hidden on scroll-container if columns fit within the available width', () => { - const rendered = findDOMNode(render(getMarkup({ columnsCount: 4 }))) + const rendered = findDOMNode(render(getMarkup({ columnCount: 4 }))) expect(rendered.style.overflowX).toEqual('hidden') }) it('should leave overflowX:auto on scroll-container if columns require more than the available width', () => { - const rendered = findDOMNode(render(getMarkup({ columnsCount: 25 }))) + const rendered = findDOMNode(render(getMarkup({ columnCount: 25 }))) expect(rendered.style.overflowX).not.toEqual('hidden') }) it('should set overflowY:hidden on scroll-container if rows fit within the available height', () => { - const rendered = findDOMNode(render(getMarkup({ rowsCount: 5 }))) + const rendered = findDOMNode(render(getMarkup({ rowCount: 5 }))) expect(rendered.style.overflowY).toEqual('hidden') }) it('should leave overflowY:auto on scroll-container if rows require more than the available height', () => { - const rendered = findDOMNode(render(getMarkup({ rowsCount: 25 }))) + const rendered = findDOMNode(render(getMarkup({ rowCount: 25 }))) expect(rendered.style.overflowY).not.toEqual('hidden') }) }) @@ -169,8 +169,8 @@ describe('Grid', () => { expect(grid.state.scrollLeft).toEqual(0) expect(grid.state.scrollTop).toEqual(0) grid = render(getMarkup({ - columnsCount: NUM_COLUMNS + 1, - rowsCount: NUM_ROWS + 1, + columnCount: NUM_COLUMNS + 1, + rowCount: NUM_ROWS + 1, scrollToColumn: NUM_COLUMNS, scrollToRow: NUM_ROWS })) @@ -180,14 +180,14 @@ describe('Grid', () => { it('should scroll back to a newly-added cell without a change in prop', () => { let grid = render(getMarkup({ - columnsCount: NUM_COLUMNS, - rowsCount: NUM_ROWS, + columnCount: NUM_COLUMNS, + rowCount: NUM_ROWS, scrollToColumn: NUM_COLUMNS, scrollToRow: NUM_ROWS })) grid = render(getMarkup({ - columnsCount: NUM_COLUMNS + 1, - rowsCount: NUM_ROWS + 1, + columnCount: NUM_COLUMNS + 1, + rowCount: NUM_ROWS + 1, scrollToColumn: NUM_COLUMNS, scrollToRow: NUM_ROWS })) @@ -246,45 +246,45 @@ describe('Grid', () => { it('should update scroll position if size shrinks smaller than the current scroll', () => { let grid = findDOMNode(render(getMarkup({ scrollToColumn: 250 }))) grid = findDOMNode(render(getMarkup())) - grid = findDOMNode(render(getMarkup({ scrollToColumn: 250, columnsCount: 10 }))) + grid = findDOMNode(render(getMarkup({ scrollToColumn: 250, columnCount: 10 }))) expect(grid.textContent).toContain('column:9') }) it('should update scroll position if size shrinks smaller than the current scroll', () => { let grid = findDOMNode(render(getMarkup({ scrollToRow: 500 }))) grid = findDOMNode(render(getMarkup())) - grid = findDOMNode(render(getMarkup({ scrollToRow: 500, rowsCount: 10 }))) + grid = findDOMNode(render(getMarkup({ scrollToRow: 500, rowCount: 10 }))) expect(grid.textContent).toContain('row:9') }) }) describe('noContentRenderer', () => { - it('should call :noContentRenderer if :columnsCount is 0', () => { + it('should call :noContentRenderer if :columnCount is 0', () => { let list = findDOMNode(render(getMarkup({ noContentRenderer: () =>
No data
, - columnsCount: 0 + columnCount: 0 }))) expect(list.textContent).toEqual('No data') }) - it('should call :noContentRenderer if :rowsCount is 0', () => { + it('should call :noContentRenderer if :rowCount is 0', () => { let list = findDOMNode(render(getMarkup({ noContentRenderer: () =>
No data
, - rowsCount: 0 + rowCount: 0 }))) expect(list.textContent).toEqual('No data') }) - it('should render an empty body if :columnsCount is 0 and there is no :noContentRenderer', () => { + it('should render an empty body if :columnCount is 0 and there is no :noContentRenderer', () => { let list = findDOMNode(render(getMarkup({ - columnsCount: 0 + columnCount: 0 }))) expect(list.textContent).toEqual('') }) - it('should render an empty body if :rowsCount is 0 and there is no :noContentRenderer', () => { + it('should render an empty body if :rowCount is 0 and there is no :noContentRenderer', () => { let list = findDOMNode(render(getMarkup({ - rowsCount: 0 + rowCount: 0 }))) expect(list.textContent).toEqual('') }) @@ -488,7 +488,7 @@ describe('Grid', () => { }) }) - describe('overscanRowsCount', () => { + describe('overscanRowCount', () => { function createHelper () { let columnOverscanStartIndex, columnOverscanStopIndex, columnStartIndex, columnStopIndex, rowOverscanStartIndex, rowOverscanStopIndex, rowStartIndex, rowStopIndex @@ -531,8 +531,8 @@ describe('Grid', () => { const helper = createHelper() render(getMarkup({ onSectionRendered: helper.onSectionRendered, - overscanColumnsCount: 2, - overscanRowsCount: 5, + overscanColumnCount: 2, + overscanRowCount: 5, scrollToColumn: 25, scrollToRow: 50 })) @@ -550,10 +550,10 @@ describe('Grid', () => { const helper = createHelper() render(getMarkup({ onSectionRendered: helper.onSectionRendered, - columnsCount: 6, - overscanColumnsCount: 10, - overscanRowsCount: 10, - rowsCount: 5 + columnCount: 6, + overscanColumnCount: 10, + overscanRowCount: 10, + rowCount: 5 })) expect(helper.columnOverscanStartIndex()).toEqual(0) expect(helper.columnOverscanStopIndex()).toEqual(5) @@ -566,25 +566,25 @@ describe('Grid', () => { }) }) - describe('renderCellRanges', () => { - it('should use a custom :renderCellRanges if specified', () => { - let renderCellRangesCalled = 0 - let renderCellRangesParams + describe('cellRangeRenderer', () => { + it('should use a custom :cellRangeRenderer if specified', () => { + let cellRangeRendererCalled = 0 + let cellRangeRendererParams const rendered = findDOMNode(render(getMarkup({ - renderCellRanges: (params) => { - renderCellRangesParams = params - renderCellRangesCalled++ + cellRangeRenderer: (params) => { + cellRangeRendererParams = params + cellRangeRendererCalled++ return [
Fake content
] } }))) - expect(renderCellRangesCalled).toEqual(1) - expect(renderCellRangesParams.columnStartIndex).toEqual(0) - expect(renderCellRangesParams.columnStopIndex).toEqual(3) - expect(renderCellRangesParams.rowStartIndex).toEqual(0) - expect(renderCellRangesParams.rowStopIndex).toEqual(4) + expect(cellRangeRendererCalled).toEqual(1) + expect(cellRangeRendererParams.columnStartIndex).toEqual(0) + expect(cellRangeRendererParams.columnStopIndex).toEqual(3) + expect(cellRangeRendererParams.rowStartIndex).toEqual(0) + expect(cellRangeRendererParams.rowStopIndex).toEqual(4) expect(rendered.textContent).toContain('Fake content') }) }) diff --git a/source/InfiniteLoader/InfiniteLoader.example.js b/source/InfiniteLoader/InfiniteLoader.example.js index c591dcfcc..2193462a0 100644 --- a/source/InfiniteLoader/InfiniteLoader.example.js +++ b/source/InfiniteLoader/InfiniteLoader.example.js @@ -20,9 +20,9 @@ export default class InfiniteLoaderExample extends Component { super(props) this.state = { - loadedRowsCount: 0, + loadedRowCount: 0, loadedRowsMap: {}, - loadingRowsCount: 0, + loadingRowCount: 0, randomScrollToIndex: null } @@ -42,7 +42,7 @@ export default class InfiniteLoaderExample extends Component { render () { const { list, ...props } = this.props - const { loadedRowsCount, loadingRowsCount, randomScrollToIndex } = this.state + const { loadedRowCount, loadingRowCount, randomScrollToIndex } = this.state return ( @@ -67,7 +67,7 @@ export default class InfiniteLoaderExample extends Component {
- {`${loadingRowsCount} loading, ${loadedRowsCount} loaded`} + {`${loadingRowCount} loading, ${loadedRowCount} loaded`}
@@ -75,7 +75,7 @@ export default class InfiniteLoaderExample extends Component { {({ onRowsRendered, registerChild }) => ( @@ -85,7 +85,7 @@ export default class InfiniteLoaderExample extends Component { className={styles.VirtualScroll} height={200} onRowsRendered={onRowsRendered} - rowsCount={list.size} + rowCount={list.size} rowHeight={30} rowRenderer={this._rowRenderer} scrollToIndex={randomScrollToIndex} @@ -105,9 +105,9 @@ export default class InfiniteLoaderExample extends Component { _clearData () { this.setState({ - loadedRowsCount: 0, + loadedRowCount: 0, loadedRowsMap: {}, - loadingRowsCount: 0 + loadingRowCount: 0 }) } @@ -117,7 +117,7 @@ export default class InfiniteLoaderExample extends Component { } _loadMoreRows ({ startIndex, stopIndex }) { - const { loadedRowsMap, loadingRowsCount } = this.state + const { loadedRowsMap, loadingRowCount } = this.state const increment = stopIndex - startIndex + 1 for (var i = startIndex; i <= stopIndex; i++) { @@ -125,11 +125,11 @@ export default class InfiniteLoaderExample extends Component { } this.setState({ - loadingRowsCount: loadingRowsCount + increment + loadingRowCount: loadingRowCount + increment }) const timeoutId = setTimeout(() => { - const { loadedRowsCount, loadingRowsCount } = this.state + const { loadedRowCount, loadingRowCount } = this.state delete this._timeoutIdMap[timeoutId] @@ -138,8 +138,8 @@ export default class InfiniteLoaderExample extends Component { } this.setState({ - loadingRowsCount: loadingRowsCount - increment, - loadedRowsCount: loadedRowsCount + increment + loadingRowCount: loadingRowCount - increment, + loadedRowCount: loadedRowCount + increment }) promiseResolver() diff --git a/source/InfiniteLoader/InfiniteLoader.js b/source/InfiniteLoader/InfiniteLoader.js index 4dfdcadad..285231e50 100644 --- a/source/InfiniteLoader/InfiniteLoader.js +++ b/source/InfiniteLoader/InfiniteLoader.js @@ -43,7 +43,7 @@ export default class InfiniteLoader extends Component { /** * Number of rows in list; can be arbitrary high number if actual number is unknown. */ - rowsCount: PropTypes.number.isRequired, + rowCount: PropTypes.number.isRequired, /** * Threshold at which to pre-fetch data. @@ -55,7 +55,7 @@ export default class InfiniteLoader extends Component { static defaultProps = { minimumBatchSize: 10, - rowsCount: 0, + rowCount: 0, threshold: 15 } @@ -80,7 +80,7 @@ export default class InfiniteLoader extends Component { } _onRowsRendered ({ startIndex, stopIndex }) { - const { isRowLoaded, loadMoreRows, minimumBatchSize, rowsCount, threshold } = this.props + const { isRowLoaded, loadMoreRows, minimumBatchSize, rowCount, threshold } = this.props this._lastRenderedStartIndex = startIndex this._lastRenderedStopIndex = stopIndex @@ -88,9 +88,9 @@ export default class InfiniteLoader extends Component { const unloadedRanges = scanForUnloadedRanges({ isRowLoaded, minimumBatchSize, - rowsCount, + rowCount, startIndex: Math.max(0, startIndex - threshold), - stopIndex: Math.min(rowsCount - 1, stopIndex + threshold) + stopIndex: Math.min(rowCount - 1, stopIndex + threshold) }) unloadedRanges.forEach(unloadedRange => { @@ -139,7 +139,7 @@ export function isRangeVisible ({ export function scanForUnloadedRanges ({ isRowLoaded, minimumBatchSize, - rowsCount, + rowCount, startIndex, stopIndex }) { @@ -167,13 +167,13 @@ export function scanForUnloadedRanges ({ } if (rangeStopIndex !== null) { - // Attempt to satisfy :minimumBatchSize requirement but don't exceed :rowsCount + // Attempt to satisfy :minimumBatchSize requirement but don't exceed :rowCount const potentialStopIndex = Math.min( Math.max( rangeStopIndex, rangeStartIndex + minimumBatchSize - 1 ), - rowsCount - 1 + rowCount - 1 ) for (let i = rangeStopIndex + 1; i <= potentialStopIndex; i++) { diff --git a/source/InfiniteLoader/InfiniteLoader.test.js b/source/InfiniteLoader/InfiniteLoader.test.js index 7677cb267..7f623af8d 100644 --- a/source/InfiniteLoader/InfiniteLoader.test.js +++ b/source/InfiniteLoader/InfiniteLoader.test.js @@ -39,7 +39,7 @@ describe('InfiniteLoader', () => { loadMoreRows = defaultLoadMoreRows, minimumBatchSize = 1, rowHeight = 20, - rowsCount = 100, + rowCount = 100, threshold = 10, width = 200 } = {}) { @@ -48,7 +48,7 @@ describe('InfiniteLoader', () => { isRowLoaded={isRowLoaded} loadMoreRows={loadMoreRows} minimumBatchSize={minimumBatchSize} - rowsCount={rowsCount} + rowCount={rowCount} threshold={threshold} > {({ onRowsRendered, registerChild }) => { @@ -61,7 +61,7 @@ describe('InfiniteLoader', () => { onRowsRendered={onRowsRendered} rowHeight={rowHeight} rowRenderer={rowRenderer} - rowsCount={rowsCount} + rowCount={rowCount} width={width} /> ) @@ -75,8 +75,8 @@ describe('InfiniteLoader', () => { expect(isRowLoadedCalls).toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]) }) - it('should call :isRowLoaded for all rows within the rowsCount each time a range of rows are rendered', () => { - render(getMarkup({ rowsCount: 10 })) + it('should call :isRowLoaded for all rows within the rowCount each time a range of rows are rendered', () => { + render(getMarkup({ rowCount: 10 })) expect(isRowLoadedCalls).toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) }) @@ -85,8 +85,8 @@ describe('InfiniteLoader', () => { expect(loadMoreRowsCalls).toEqual([{ startIndex: 0, stopIndex: 14 }]) }) - it('should call :loadMoreRows for unloaded rows within the rowsCount', () => { - render(getMarkup({ rowsCount: 10 })) + it('should call :loadMoreRows for unloaded rows within the rowCount', () => { + render(getMarkup({ rowCount: 10 })) expect(loadMoreRowsCalls).toEqual([{ startIndex: 0, stopIndex: 9 }]) }) @@ -164,7 +164,7 @@ describe('InfiniteLoader', () => { it('should not exceed boundaries if :minimumBatchSize is larger than needed', () => { render(getMarkup({ minimumBatchSize: 10, - rowsCount: 25, + rowCount: 25, threshold: 0 })) // Simulate a new range of rows being loaded diff --git a/source/ScrollSync/ScrollSync.example.js b/source/ScrollSync/ScrollSync.example.js index d0709e032..373caf714 100644 --- a/source/ScrollSync/ScrollSync.example.js +++ b/source/ScrollSync/ScrollSync.example.js @@ -25,12 +25,12 @@ export default class GridExample extends Component { this.state = { columnWidth: 75, - columnsCount: 50, + columnCount: 50, height: 300, - overscanColumnsCount: 0, - overscanRowsCount: 5, + overscanColumnCount: 0, + overscanRowCount: 5, rowHeight: 40, - rowsCount: 100 + rowCount: 100 } this._renderBodyCell = this._renderBodyCell.bind(this) @@ -42,13 +42,13 @@ export default class GridExample extends Component { const { list, ...props } = this.props const { - columnsCount, + columnCount, columnWidth, height, - overscanColumnsCount, - overscanRowsCount, + overscanColumnCount, + overscanRowCount, rowHeight, - rowsCount + rowCount } = this.state return ( @@ -93,14 +93,14 @@ export default class GridExample extends Component { }} >
@@ -140,12 +140,12 @@ export default class GridExample extends Component { @@ -161,14 +161,14 @@ export default class GridExample extends Component {
diff --git a/source/VirtualScroll/VirtualScroll.example.js b/source/VirtualScroll/VirtualScroll.example.js index 123e94aed..03063c327 100644 --- a/source/VirtualScroll/VirtualScroll.example.js +++ b/source/VirtualScroll/VirtualScroll.example.js @@ -19,8 +19,8 @@ export default class VirtualScrollExample extends Component { super(props) this.state = { - overscanRowsCount: 5, - rowsCount: props.list.size, + overscanRowCount: 5, + rowCount: props.list.size, scrollToIndex: undefined, useDynamicRowHeight: false, virtualScrollHeight: 300, @@ -29,7 +29,7 @@ export default class VirtualScrollExample extends Component { this._getRowHeight = this._getRowHeight.bind(this) this._noRowsRenderer = this._noRowsRenderer.bind(this) - this._onRowsCountChange = this._onRowsCountChange.bind(this) + this._onRowCountChange = this._onRowCountChange.bind(this) this._onScrollToRowChange = this._onScrollToRowChange.bind(this) this._rowRenderer = this._rowRenderer.bind(this) this._updateUseDynamicRowHeight = this._updateUseDynamicRowHeight.bind(this) @@ -39,8 +39,8 @@ export default class VirtualScrollExample extends Component { const { list, ...props } = this.props const { - overscanRowsCount, - rowsCount, + overscanRowCount, + rowCount, scrollToIndex, useDynamicRowHeight, virtualScrollHeight, @@ -76,9 +76,9 @@ export default class VirtualScrollExample extends Component { this.setState({ overscanRowsCount: parseInt(event.target.value, 10) || 0 })} - value={overscanRowsCount} + name='overscanRowCount' + onChange={event => this.setState({ overscanRowCount: parseInt(event.target.value, 10) || 0 })} + value={overscanRowCount} /> @@ -115,9 +115,9 @@ export default class VirtualScrollExample extends Component { ref='VirtualScroll' className={styles.VirtualScroll} height={virtualScrollHeight} - overscanRowsCount={overscanRowsCount} + overscanRowCount={overscanRowCount} noRowsRenderer={this._noRowsRenderer} - rowsCount={rowsCount} + rowCount={rowCount} rowHeight={useDynamicRowHeight ? this._getRowHeight : virtualScrollRowHeight} rowRenderer={this._rowRenderer} scrollToIndex={scrollToIndex} @@ -152,15 +152,15 @@ export default class VirtualScrollExample extends Component { ) } - _onRowsCountChange (event) { - const rowsCount = parseInt(event.target.value, 10) || 0 + _onRowCountChange (event) { + const rowCount = parseInt(event.target.value, 10) || 0 - this.setState({ rowsCount }) + this.setState({ rowCount }) } _onScrollToRowChange (event) { - const { rowsCount } = this.state - let scrollToIndex = Math.min(rowsCount - 1, parseInt(event.target.value, 10)) + const { rowCount } = this.state + let scrollToIndex = Math.min(rowCount - 1, parseInt(event.target.value, 10)) if (isNaN(scrollToIndex)) { scrollToIndex = undefined diff --git a/source/VirtualScroll/VirtualScroll.js b/source/VirtualScroll/VirtualScroll.js index 21d431290..06916f082 100644 --- a/source/VirtualScroll/VirtualScroll.js +++ b/source/VirtualScroll/VirtualScroll.js @@ -22,7 +22,7 @@ export default class VirtualScroll extends Component { /** Height constraint for list (determines how many actual rows are rendered) */ height: PropTypes.number.isRequired, - /** Optional renderer to be used in place of rows when rowsCount is 0 */ + /** Optional renderer to be used in place of rows when rowCount is 0 */ noRowsRenderer: PropTypes.func.isRequired, /** @@ -35,7 +35,7 @@ export default class VirtualScroll extends Component { * Number of rows to render above/below the visible bounds of the list. * These rows can help for smoother scrolling on touch devices. */ - overscanRowsCount: PropTypes.number.isRequired, + overscanRowCount: PropTypes.number.isRequired, /** * Callback invoked whenever the scroll offset changes within the inner scrollable region. @@ -54,7 +54,7 @@ export default class VirtualScroll extends Component { rowRenderer: PropTypes.func.isRequired, /** Number of rows in list. */ - rowsCount: PropTypes.number.isRequired, + rowCount: PropTypes.number.isRequired, /** Row index to ensure visible (by forcefully scrolling if necessary) */ scrollToIndex: PropTypes.number, @@ -70,7 +70,7 @@ export default class VirtualScroll extends Component { noRowsRenderer: () => null, onRowsRendered: () => null, onScroll: () => null, - overscanRowsCount: 10 + overscanRowCount: 10 } /** @@ -89,8 +89,8 @@ export default class VirtualScroll extends Component { onScroll, rowHeight, rowRenderer, - overscanRowsCount, - rowsCount, + overscanRowCount, + rowCount, scrollToIndex, scrollTop, width @@ -104,7 +104,7 @@ export default class VirtualScroll extends Component { aria-label={this.props['aria-label']} className={classNames} columnWidth={width} - columnsCount={1} + columnCount={1} height={height} noContentRenderer={noRowsRenderer} onScroll={({ clientHeight, scrollHeight, scrollTop }) => onScroll({ clientHeight, scrollHeight, scrollTop })} @@ -114,10 +114,10 @@ export default class VirtualScroll extends Component { startIndex: rowStartIndex, stopIndex: rowStopIndex })} - overscanRowsCount={overscanRowsCount} - renderCell={({ columnIndex, rowIndex }) => rowRenderer(rowIndex)} + overscanRowCount={overscanRowCount} + cellRenderer={({ columnIndex, rowIndex }) => rowRenderer(rowIndex)} rowHeight={rowHeight} - rowsCount={rowsCount} + rowCount={rowCount} scrollToRow={scrollToIndex} scrollTop={scrollTop} width={width} diff --git a/source/VirtualScroll/VirtualScroll.test.js b/source/VirtualScroll/VirtualScroll.test.js index 235df1118..a8abbacf1 100644 --- a/source/VirtualScroll/VirtualScroll.test.js +++ b/source/VirtualScroll/VirtualScroll.test.js @@ -18,9 +18,9 @@ describe('VirtualScroll', () => { noRowsRenderer = undefined, onRowsRendered = undefined, onScroll = undefined, - overscanRowsCount = 0, + overscanRowCount = 0, rowHeight = 10, - rowsCount = rendered.size, + rowCount = rendered.size, scrollToIndex = undefined, scrollTop = undefined, width = 100 @@ -43,10 +43,10 @@ describe('VirtualScroll', () => { noRowsRenderer={noRowsRenderer} onRowsRendered={onRowsRendered} onScroll={onScroll} - overscanRowsCount={overscanRowsCount} + overscanRowCount={overscanRowCount} rowHeight={rowHeight} rowRenderer={rowRenderer} - rowsCount={rowsCount} + rowCount={rowCount} scrollToIndex={scrollToIndex} scrollTop={scrollTop} width={width} @@ -61,7 +61,7 @@ describe('VirtualScroll', () => { }) it('should not render more children than available if the list is not filled', () => { - const rendered = findDOMNode(render(getMarkup({ rowsCount: 5 }))) + const rendered = findDOMNode(render(getMarkup({ rowCount: 5 }))) expect(rendered.querySelectorAll('.listItem').length).toEqual(5) }) }) @@ -117,23 +117,23 @@ describe('VirtualScroll', () => { it('should update scroll position if size shrinks smaller than the current scroll', () => { findDOMNode(render(getMarkup({ scrollToIndex: 500 }))) findDOMNode(render(getMarkup())) - const rendered = findDOMNode(render(getMarkup({ scrollToIndex: 500, rowsCount: 10 }))) + const rendered = findDOMNode(render(getMarkup({ scrollToIndex: 500, rowCount: 10 }))) expect(rendered.textContent).toContain('Name 9') }) }) describe('noRowsRenderer', () => { - it('should call :noRowsRenderer if :rowsCount is 0', () => { + it('should call :noRowsRenderer if :rowCount is 0', () => { let rendered = findDOMNode(render(getMarkup({ noRowsRenderer: () =>
No rows!
, - rowsCount: 0 + rowCount: 0 }))) expect(rendered.textContent).toEqual('No rows!') }) - it('should render an empty body if :rowsCount is 0 and there is no :noRowsRenderer', () => { + it('should render an empty body if :rowCount is 0 and there is no :noRowsRenderer', () => { let rendered = findDOMNode(render(getMarkup({ - rowsCount: 0 + rowCount: 0 }))) expect(rendered.textContent).toEqual('') }) @@ -242,7 +242,7 @@ describe('VirtualScroll', () => { }) }) - describe('overscanRowsCount', () => { + describe('overscanRowCount', () => { it('should not overscan by default', () => { let overscanStartIndex, overscanStopIndex, startIndex, stopIndex render(getMarkup({ @@ -256,7 +256,7 @@ describe('VirtualScroll', () => { let overscanStartIndex, overscanStopIndex, startIndex, stopIndex render(getMarkup({ onRowsRendered: params => ({ overscanStartIndex, overscanStopIndex, startIndex, stopIndex } = params), - overscanRowsCount: 10, + overscanRowCount: 10, scrollToIndex: 30 })) expect(overscanStartIndex).toEqual(11) @@ -269,7 +269,7 @@ describe('VirtualScroll', () => { let overscanStartIndex, overscanStopIndex, startIndex, stopIndex render(getMarkup({ onRowsRendered: params => ({ overscanStartIndex, overscanStopIndex, startIndex, stopIndex } = params), - overscanRowsCount: 10 + overscanRowCount: 10 })) expect(overscanStartIndex).toEqual(0) expect(startIndex).toEqual(0) @@ -281,8 +281,8 @@ describe('VirtualScroll', () => { let overscanStartIndex, overscanStopIndex, startIndex, stopIndex render(getMarkup({ onRowsRendered: params => ({ overscanStartIndex, overscanStopIndex, startIndex, stopIndex } = params), - overscanRowsCount: 10, - rowsCount: 15 + overscanRowCount: 10, + rowCount: 15 })) expect(overscanStartIndex).toEqual(0) expect(startIndex).toEqual(0) @@ -332,7 +332,7 @@ describe('VirtualScroll', () => { } const component = render(getMarkup({ rowHeight, - rowsCount: 50 + rowCount: 50 })) highestRowIndex = 0 component.recomputeRowHeights() From 7e9786e311746908fa16b34c1966d098e6ff500f Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Thu, 28 Apr 2016 19:37:12 -0700 Subject: [PATCH 02/60] Updated Collection :onSectionRendered signature to pass a named :indices argumetn --- docs/Collection.md | 2 +- source/Collection/Collection.test.js | 12 +++++----- source/Collection/CollectionView.js | 6 +++-- source/utils/createCallbackMemoizer.js | 16 +++++++++++-- source/utils/createCallbackMemoizer.test.js | 26 +++++++++++++++++++++ 5 files changed, 51 insertions(+), 11 deletions(-) diff --git a/docs/Collection.md b/docs/Collection.md index 40c17b487..15dd1bb0b 100644 --- a/docs/Collection.md +++ b/docs/Collection.md @@ -14,7 +14,7 @@ Unlike `Grid`, which renders checkerboard data, `Collection` can render arbitrar | cellSizeAndPositionGetter | Function | ✓ | Callback responsible for returning size and offset/position information for a given cell (index): `(index): { height: number, width: number, x: number, y: number }` | | height | Number | ✓ | Height of Collection; this property determines the number of visible (vs virtualized) rows. | | noContentRenderer | Function | | Optional renderer to be rendered inside the grid when `cellCount` is 0: `(): PropTypes.node` | -| onSectionRendered | Function | | Callback invoked with information about the section of the Collection that was just rendered: `(indices: Array): void` | +| onSectionRendered | Function | | Callback invoked with information about the section of the Collection that was just rendered: `({ indices: Array }): void` | | onScroll | Function | | Callback invoked whenever the scroll offset changes within the inner scrollable region: `({ clientHeight, clientWidth, scrollHeight, scrollLeft, scrollTop, scrollWidth }): void` | | scrollLeft | Number | | Horizontal offset | | scrollToCell | Number | | Cell index to ensure visible (by scrolling if necessary) | diff --git a/source/Collection/Collection.test.js b/source/Collection/Collection.test.js index 2085f88d5..83552d128 100644 --- a/source/Collection/Collection.test.js +++ b/source/Collection/Collection.test.js @@ -199,7 +199,7 @@ describe('Collection', () => { it('should call :onSectionRendered if at least one cell is rendered', () => { let indices render(getMarkup({ - onSectionRendered: params => indices = params + onSectionRendered: params => indices = params.indices })) compareArrays(indices, [0, 1, 2, 3]) }) @@ -208,7 +208,7 @@ describe('Collection', () => { let numCalls = 0 let indices const onSectionRendered = params => { - indices = params + indices = params.indices numCalls++ } render(getMarkup({ onSectionRendered })) @@ -223,7 +223,7 @@ describe('Collection', () => { let numCalls = 0 let indices const onSectionRendered = params => { - indices = params + indices = params.indices numCalls++ } render(getMarkup({ onSectionRendered })) @@ -258,7 +258,7 @@ describe('Collection', () => { it('should render correctly when an initial :scrollLeft and :scrollTop properties are specified', () => { let indices render(getMarkup({ - onSectionRendered: params => indices = params, + onSectionRendered: params => indices = params.indices, scrollLeft: 2, scrollTop: 2 })) @@ -268,11 +268,11 @@ describe('Collection', () => { it('should render correctly when :scrollLeft and :scrollTop properties are updated', () => { let indices render(getMarkup({ - onSectionRendered: params => indices = params + onSectionRendered: params => indices = params.indices })) compareArrays(indices, [0, 1, 2, 3]) render(getMarkup({ - onSectionRendered: params => indices = params, + onSectionRendered: params => indices = params.indices, scrollLeft: 2, scrollTop: 2 })) diff --git a/source/Collection/CollectionView.js b/source/Collection/CollectionView.js index 92d85c6a5..79e3aceec 100644 --- a/source/Collection/CollectionView.js +++ b/source/Collection/CollectionView.js @@ -65,7 +65,7 @@ export default class CollectionView extends Component { /** * Callback invoked with information about the section of the Collection that was just rendered. - * This callback is passed an array of the most recently rendered section indices. + * This callback is passed a named :indices parameter which is an Array of the most recently rendered section indices. */ onSectionRendered: PropTypes.func.isRequired, @@ -363,7 +363,9 @@ export default class CollectionView extends Component { this._onSectionRenderedMemoizer({ callback: onSectionRendered, - indices: cellLayoutManager.getLastRenderedIndices() + indices: { + indices: cellLayoutManager.getLastRenderedIndices() + } }) } diff --git a/source/utils/createCallbackMemoizer.js b/source/utils/createCallbackMemoizer.js index 93c1465e4..abe174a6d 100644 --- a/source/utils/createCallbackMemoizer.js +++ b/source/utils/createCallbackMemoizer.js @@ -9,10 +9,22 @@ export default function createCallbackMemoizer (requireAllKeys = true) { indices }) => { const keys = Object.keys(indices) - const allInitialized = !requireAllKeys || keys.every(key => indices[key] >= 0) + const allInitialized = !requireAllKeys || keys.every(key => { + const value = indices[key] + return Array.isArray(value) + ? value.length > 0 + : value >= 0 + }) const indexChanged = keys.length !== Object.keys(cachedIndices).length || - keys.some(key => cachedIndices[key] !== indices[key]) + keys.some(key => { + const cachedValue = cachedIndices[key] + const value = indices[key] + + return Array.isArray(value) + ? cachedValue.join(',') !== value.join(',') + : cachedValue !== value + }) cachedIndices = indices diff --git a/source/utils/createCallbackMemoizer.test.js b/source/utils/createCallbackMemoizer.test.js index 7b5bbb5b5..cecc66d98 100644 --- a/source/utils/createCallbackMemoizer.test.js +++ b/source/utils/createCallbackMemoizer.test.js @@ -187,4 +187,30 @@ describe('createCallbackMemoizer', () => { expect(numCalls).toEqual(2) expect(indices).toEqual([0, 1]) }) + + it('should support an attribute containing an array of indices', () => { + let numCalls = 0 + let indices + const callback = params => { + indices = params.indices + numCalls++ + } + const helper = createCallbackMemoizer() + helper({ + callback, + indices: { + indices: [0, 1, 2] + } + }) + expect(numCalls).toEqual(1) + expect(indices).toEqual([0, 1, 2]) + helper({ + callback, + indices: { + indices: [0, 1] + } + }) + expect(numCalls).toEqual(2) + expect(indices).toEqual([0, 1]) + }) }) From adbdfabffee78e055ebbd57d33c4a72bc09f6211 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Thu, 28 Apr 2016 20:39:45 -0700 Subject: [PATCH 03/60] Updated signature for Collection's :cellRendered from (index: number) to ({ index: number }) --- docs/Collection.md | 4 ++-- source/Collection/Collection.example.js | 2 +- source/Collection/Collection.js | 2 +- source/Collection/Collection.test.js | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/Collection.md b/docs/Collection.md index 15dd1bb0b..c08280556 100644 --- a/docs/Collection.md +++ b/docs/Collection.md @@ -9,7 +9,7 @@ Unlike `Grid`, which renders checkerboard data, `Collection` can render arbitrar |:---|:---|:---:|:---| | className | String | | Optional custom CSS class name to attach to root Collection element. | | cellCount | Number | ✓ | Number of cells in collection. | -| cellRenderer | Function | ✓ | Responsible for rendering a cell given an row and column index: `(index: number): PropTypes.node` | +| cellRenderer | Function | ✓ | Responsible for rendering a cell given an row and column index: `({ index: number }): PropTypes.element` | | cellGroupRenderer | Function | ✓ | Responsible for rendering a group of cells given their indices.: `({ cellSizeAndPositionGetter:Function, indices: Array, cellRenderer: Function }): Array` | | cellSizeAndPositionGetter | Function | ✓ | Callback responsible for returning size and offset/position information for a given cell (index): `(index): { height: number, width: number, x: number, y: number }` | | height | Number | ✓ | Height of Collection; this property determines the number of visible (vs virtualized) rows. | @@ -62,7 +62,7 @@ const list = [ ReactDOM.render( list[index].name} + cellRenderer={({ index }) => list[index].name} cellSizeAndPositionGette={(index) => list[index]} columnCount={list.length} height={300} diff --git a/source/Collection/Collection.example.js b/source/Collection/Collection.example.js index 2787a5081..8d8f6ff21 100644 --- a/source/Collection/Collection.example.js +++ b/source/Collection/Collection.example.js @@ -98,7 +98,7 @@ export default class CollectionExample extends Component { return shallowCompare(this, nextProps, nextState) } - _cellRenderer (index) { + _cellRenderer ({ index }) { const { list } = this.props const datum = list.get(index % list.size) diff --git a/source/Collection/Collection.js b/source/Collection/Collection.js index 3c67a9e82..95896d3df 100644 --- a/source/Collection/Collection.js +++ b/source/Collection/Collection.js @@ -182,7 +182,7 @@ function defaultCellGroupRenderer ({ return indices .map((index) => { const cellMetadata = cellSizeAndPositionGetter(index) - const renderedCell = cellRenderer(index) + const renderedCell = cellRenderer({ index }) if (renderedCell == null || renderedCell === false) { return null diff --git a/source/Collection/Collection.test.js b/source/Collection/Collection.test.js index 83552d128..5766004fa 100644 --- a/source/Collection/Collection.test.js +++ b/source/Collection/Collection.test.js @@ -26,7 +26,7 @@ describe('Collection', () => { scrollTop, width = SECTION_SIZE * 2 } = {}) { - function defaultCellRenderer (index) { + function defaultCellRenderer ({ index }) { return (
cell:{index} @@ -81,7 +81,7 @@ describe('Collection', () => { // Small performance tweak added in 5.5.6 it('should not render/parent cells that are null or false', () => { - function cellRenderer (index) { + function cellRenderer ({ index }) { if (index > 2) { return null } else { @@ -363,7 +363,7 @@ describe('Collection', () => { it('should use a custom :cellGroupRenderer if specified', () => { let cellGroupRendererCalled = 0 let cellGroupRendererParams - const cellRenderer = (index) => index + const cellRenderer = ({ index }) => index findDOMNode(render(getMarkup({ cellRenderer, cellGroupRenderer: (params) => { From 245badda5d10194926e34e12a4e65a73ab0ff82c Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Thu, 28 Apr 2016 20:54:48 -0700 Subject: [PATCH 04/60] Updated additional Collection methods to use named arguments. --- docs/Collection.md | 4 ++-- source/Collection/Collection.example.js | 2 +- source/Collection/Collection.js | 8 ++++---- source/Collection/Collection.test.js | 6 +++--- source/Collection/Section.js | 6 ++++-- source/Collection/Section.test.js | 14 +++++++------- source/Collection/SectionManager.js | 8 +++++--- source/Collection/types.js | 4 ++++ .../utils/calculateSizeAndPositionData.js | 2 +- .../utils/calculateSizeAndPositionData.test.js | 4 ++-- 10 files changed, 33 insertions(+), 25 deletions(-) diff --git a/docs/Collection.md b/docs/Collection.md index c08280556..576099c28 100644 --- a/docs/Collection.md +++ b/docs/Collection.md @@ -11,7 +11,7 @@ Unlike `Grid`, which renders checkerboard data, `Collection` can render arbitrar | cellCount | Number | ✓ | Number of cells in collection. | | cellRenderer | Function | ✓ | Responsible for rendering a cell given an row and column index: `({ index: number }): PropTypes.element` | | cellGroupRenderer | Function | ✓ | Responsible for rendering a group of cells given their indices.: `({ cellSizeAndPositionGetter:Function, indices: Array, cellRenderer: Function }): Array` | -| cellSizeAndPositionGetter | Function | ✓ | Callback responsible for returning size and offset/position information for a given cell (index): `(index): { height: number, width: number, x: number, y: number }` | +| cellSizeAndPositionGetter | Function | ✓ | Callback responsible for returning size and offset/position information for a given cell (index): `({ index: number }): { height: number, width: number, x: number, y: number }` | | height | Number | ✓ | Height of Collection; this property determines the number of visible (vs virtualized) rows. | | noContentRenderer | Function | | Optional renderer to be rendered inside the grid when `cellCount` is 0: `(): PropTypes.node` | | onSectionRendered | Function | | Callback invoked with information about the section of the Collection that was just rendered: `({ indices: Array }): void` | @@ -63,7 +63,7 @@ ReactDOM.render( list[index].name} - cellSizeAndPositionGette={(index) => list[index]} + cellSizeAndPositionGette={({ index }) => list[index]} columnCount={list.length} height={300} width={300} diff --git a/source/Collection/Collection.example.js b/source/Collection/Collection.example.js index 8d8f6ff21..80224b7eb 100644 --- a/source/Collection/Collection.example.js +++ b/source/Collection/Collection.example.js @@ -114,7 +114,7 @@ export default class CollectionExample extends Component { ) } - _cellSizeAndPositionGetter (index) { + _cellSizeAndPositionGetter ({ index }) { const { list } = this.props const { columnCount } = this.state diff --git a/source/Collection/Collection.js b/source/Collection/Collection.js index 95896d3df..4e9252fad 100644 --- a/source/Collection/Collection.js +++ b/source/Collection/Collection.js @@ -31,13 +31,13 @@ export default class Collection extends Component { /** * Responsible for rendering a cell given an row and column index. - * Should implement the following interface: (index: number): PropTypes.node + * Should implement the following interface: ({ index: number }): PropTypes.element */ cellRenderer: PropTypes.func.isRequired, /** * Callback responsible for returning size and offset/position information for a given cell (index). - * (index): { height: number, width: number, x: number, y: number } + * ({ index: number }): { height: number, width: number, x: number, y: number } */ cellSizeAndPositionGetter: PropTypes.func.isRequired, @@ -168,7 +168,7 @@ export default class Collection extends Component { return cellGroupRenderer({ cellRenderer, - cellSizeAndPositionGetter: (index) => this._sectionManager.getCellMetadata(index), + cellSizeAndPositionGetter: ({ index }) => this._sectionManager.getCellMetadata({ index }), indices: this._lastRenderedCellIndices }) } @@ -181,7 +181,7 @@ function defaultCellGroupRenderer ({ }) { return indices .map((index) => { - const cellMetadata = cellSizeAndPositionGetter(index) + const cellMetadata = cellSizeAndPositionGetter({ index }) const renderedCell = cellRenderer({ index }) if (renderedCell == null || renderedCell === false) { diff --git a/source/Collection/Collection.test.js b/source/Collection/Collection.test.js index 5766004fa..e20ad0649 100644 --- a/source/Collection/Collection.test.js +++ b/source/Collection/Collection.test.js @@ -26,7 +26,7 @@ describe('Collection', () => { scrollTop, width = SECTION_SIZE * 2 } = {}) { - function defaultCellRenderer ({ index }) { + function defaultCellRenderer ({ index }) { return (
cell:{index} @@ -34,7 +34,7 @@ describe('Collection', () => { ) } - function defaultCellSizeAndPositionGetter (index) { + function defaultCellSizeAndPositionGetter ({ index }) { index = index % cellCount return CELLS[index] @@ -363,7 +363,7 @@ describe('Collection', () => { it('should use a custom :cellGroupRenderer if specified', () => { let cellGroupRendererCalled = 0 let cellGroupRendererParams - const cellRenderer = ({ index }) => index + const cellRenderer = ({ index }) => index findDOMNode(render(getMarkup({ cellRenderer, cellGroupRenderer: (params) => { diff --git a/source/Collection/Section.js b/source/Collection/Section.js index 7357332c5..046fe2bb2 100644 --- a/source/Collection/Section.js +++ b/source/Collection/Section.js @@ -1,5 +1,5 @@ /** @rlow */ -import type { SizeAndPositionInfo } from './types' +import type { Index, SizeAndPositionInfo } from './types' /** * A section of the Window. @@ -24,7 +24,9 @@ export default class Section { } /** Add a cell to this section. */ - addCellIndex (index: number) { + addCellIndex ({ + index + }: Index) { if (!this._indexMap[index]) { this._indexMap[index] = true this._indices.push(index) diff --git a/source/Collection/Section.test.js b/source/Collection/Section.test.js index 3b19fb3a5..ee1224ff6 100644 --- a/source/Collection/Section.test.js +++ b/source/Collection/Section.test.js @@ -18,19 +18,19 @@ describe('Section', () => { it('should add a new cell index', () => { const section = helper() expect(section.getCellIndices()).toEqual([]) - section.addCellIndex(0) + section.addCellIndex({ index: 0 }) expect(section.getCellIndices()).toEqual([0]) - section.addCellIndex(1) + section.addCellIndex({ index: 1 }) expect(section.getCellIndices()).toEqual([0, 1]) }) it('should not add a duplicate cell index', () => { const section = helper() - section.addCellIndex(0) - section.addCellIndex(1) - section.addCellIndex(0) - section.addCellIndex(1) - section.addCellIndex(2) + section.addCellIndex({ index: 0 }) + section.addCellIndex({ index: 1 }) + section.addCellIndex({ index: 0 }) + section.addCellIndex({ index: 1 }) + section.addCellIndex({ index: 2 }) expect(section.getCellIndices()).toEqual([0, 1, 2]) }) diff --git a/source/Collection/SectionManager.js b/source/Collection/SectionManager.js index 20a410ed4..41099277a 100644 --- a/source/Collection/SectionManager.js +++ b/source/Collection/SectionManager.js @@ -4,7 +4,7 @@ * @flow */ import Section from './Section' -import type { SizeAndPositionInfo } from './types' +import type { Index, SizeAndPositionInfo } from './types' const SECTION_SIZE = 100 @@ -50,7 +50,9 @@ export default class SectionManager { } /** Get size and position information for the cell specified. */ - getCellMetadata (index: number): SizeAndPositionInfo { + getCellMetadata ({ + index + }: Index): SizeAndPositionInfo { return this._cellMetadata[index] } @@ -108,6 +110,6 @@ export default class SectionManager { this._cellMetadata[index] = cellMetadatum this.getSections(cellMetadatum) - .forEach((section) => section.addCellIndex(index)) + .forEach((section) => section.addCellIndex({ index })) } } diff --git a/source/Collection/types.js b/source/Collection/types.js index f4b91bb6c..65a5ac35a 100644 --- a/source/Collection/types.js +++ b/source/Collection/types.js @@ -1,5 +1,9 @@ /** @flow */ +export type Index = { + index: number +}; + export type PositionInfo = { x: number, y: number diff --git a/source/Collection/utils/calculateSizeAndPositionData.js b/source/Collection/utils/calculateSizeAndPositionData.js index e036d2fed..60ad611e1 100644 --- a/source/Collection/utils/calculateSizeAndPositionData.js +++ b/source/Collection/utils/calculateSizeAndPositionData.js @@ -11,7 +11,7 @@ export default function calculateSizeAndPositionData ({ let width = 0 for (let index = 0; index < cellCount; index++) { - const cellMetadatum = cellSizeAndPositionGetter(index) + const cellMetadatum = cellSizeAndPositionGetter({ index }) if ( cellMetadatum.height == null || isNaN(cellMetadatum.height) || diff --git a/source/Collection/utils/calculateSizeAndPositionData.test.js b/source/Collection/utils/calculateSizeAndPositionData.test.js index f64f81efa..ff32561da 100644 --- a/source/Collection/utils/calculateSizeAndPositionData.test.js +++ b/source/Collection/utils/calculateSizeAndPositionData.test.js @@ -3,7 +3,7 @@ import calculateSizeAndPositionData from './calculateSizeAndPositionData' describe('calculateSizeAndPositionData', () => { it('should query for size and position of each cell', () => { const cellSizeAndPositionGetterCalls = [] - function cellSizeAndPositionGetter (index) { + function cellSizeAndPositionGetter ({ index }) { cellSizeAndPositionGetterCalls.push(index) return { x: index * 50, @@ -24,7 +24,7 @@ describe('calculateSizeAndPositionData', () => { expect(() => ( calculateSizeAndPositionData({ cellCount: 3, - cellSizeAndPositionGetter: (index) => {} + cellSizeAndPositionGetter: ({ index }) => {} }) )).toThrow() }) From 711a7641d1c9581fbb503036360423abcc7f7379 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Thu, 28 Apr 2016 21:20:44 -0700 Subject: [PATCH 05/60] Updated FlexColumn :cellDataGetter to use named parameters. Added Flow type checking. --- docs/FlexColumn.md | 4 ++-- source/FlexTable/FlexColumn.js | 13 +++++++------ source/FlexTable/FlexColumn.test.js | 25 +++++++++++++++++-------- source/FlexTable/FlexTable.example.js | 2 +- source/FlexTable/FlexTable.js | 2 +- source/FlexTable/FlexTable.test.js | 2 +- source/FlexTable/types.js | 7 +++++++ 7 files changed, 36 insertions(+), 19 deletions(-) create mode 100644 source/FlexTable/types.js diff --git a/docs/FlexColumn.md b/docs/FlexColumn.md index 4a833cd19..200a3ecad 100644 --- a/docs/FlexColumn.md +++ b/docs/FlexColumn.md @@ -27,7 +27,7 @@ Callback responsible for returning a cell's data, given its `dataKey`. It should implement the following signature: ```javascript -function (dataKey: string, rowData: any, columnData: any): any +function ({ columnData: any, dataKey: string, rowData: any }): any ``` A default `cellDataGetter` is provided that simply returns the attribute as a String. @@ -40,7 +40,7 @@ Callback responsible for rendering a cell's contents. It should implement the following signature: ```javascript -function (cellData: any, cellDataKey: string, rowData: any, rowIndex: number, columnData: any): element +function (cellData: any, cellDataKey: string, rowData: any, rowIndex: number, columnData: any): node ``` A default `cellRenderer` is provided that displays an attribute as a simple string diff --git a/source/FlexTable/FlexColumn.js b/source/FlexTable/FlexColumn.js index d5abe1704..51f65d5d9 100644 --- a/source/FlexTable/FlexColumn.js +++ b/source/FlexTable/FlexColumn.js @@ -1,5 +1,6 @@ /** @flow */ import React, { Component, PropTypes } from 'react' +import type { CellDataGetterParams } from './types' import SortIndicator from './SortIndicator' /** @@ -25,11 +26,11 @@ export function defaultCellRenderer ( * This function expects to operate on either a vanilla Object or an Immutable Map. * You should override the column's cellDataGetter if your data is some other type of object. */ -export function defaultCellDataGetter ( - dataKey: string, - rowData: any, - columnData: any -) { +export function defaultCellDataGetter ({ + columnData, + dataKey, + rowData +}: CellDataGetterParams) { if (rowData.get instanceof Function) { return rowData.get(dataKey) } else { @@ -99,7 +100,7 @@ export default class Column extends Component { /** * Callback responsible for rendering a cell's contents. - * (cellData: any, cellDataKey: string, rowData: any, rowIndex: number, columnData: any): element + * (cellData: any, cellDataKey: string, rowData: any, rowIndex: number, columnData: any): node */ cellRenderer: PropTypes.func, diff --git a/source/FlexTable/FlexColumn.test.js b/source/FlexTable/FlexColumn.test.js index 8c7e958db..94ccf1169 100644 --- a/source/FlexTable/FlexColumn.test.js +++ b/source/FlexTable/FlexColumn.test.js @@ -2,31 +2,40 @@ import Immutable from 'immutable' import { defaultCellDataGetter, defaultCellRenderer } from './FlexColumn' describe('Column', () => { - const map = Immutable.Map({ + const rowData = Immutable.Map({ foo: 'Foo', bar: 1 }) describe('defaultCellDataGetter', () => { it('should return a value for specified attributes', () => { - expect(defaultCellDataGetter('foo', map)).toEqual('Foo') - expect(defaultCellDataGetter('bar', map)).toEqual(1) + expect(defaultCellDataGetter({ + dataKey: 'foo', + rowData + })).toEqual('Foo') + expect(defaultCellDataGetter({ + dataKey: 'bar', + rowData + })).toEqual(1) }) it('should return undefined for missing attributes', () => { - expect(defaultCellDataGetter('baz', map)).toEqual(undefined) + expect(defaultCellDataGetter({ + dataKey: 'baz', + rowData + })).toEqual(undefined) }) }) describe('defaultCellRenderer', () => { it('should render a value for specified attributes', () => { - expect(defaultCellRenderer('Foo', 'foo', map, 0)).toEqual('Foo') - expect(defaultCellRenderer(1, 'bar', map, 0)).toEqual('1') + expect(defaultCellRenderer('Foo', 'foo', rowData, 0)).toEqual('Foo') + expect(defaultCellRenderer(1, 'bar', rowData, 0)).toEqual('1') }) it('should render empty string for null or missing attributes', () => { - expect(defaultCellRenderer(null, 'baz', map, 0)).toEqual('') - expect(defaultCellRenderer(undefined, 'baz', map, 0)).toEqual('') + expect(defaultCellRenderer(null, 'baz', rowData, 0)).toEqual('') + expect(defaultCellRenderer(undefined, 'baz', rowData, 0)).toEqual('') }) }) }) diff --git a/source/FlexTable/FlexTable.example.js b/source/FlexTable/FlexTable.example.js index 2a25d6ffe..370195d23 100644 --- a/source/FlexTable/FlexTable.example.js +++ b/source/FlexTable/FlexTable.example.js @@ -171,7 +171,7 @@ export default class FlexTableExample extends Component { rowData.index + ({ columnData, dataKey, rowData }) => rowData.index } dataKey='index' disableSort={!this._isSortEnabled()} diff --git a/source/FlexTable/FlexTable.js b/source/FlexTable/FlexTable.js index cc67a9c41..2e20ffa10 100644 --- a/source/FlexTable/FlexTable.js +++ b/source/FlexTable/FlexTable.js @@ -237,7 +237,7 @@ export default class FlexTable extends Component { dataKey, cellRenderer } = column.props - const cellData = cellDataGetter(dataKey, rowData, columnData) + const cellData = cellDataGetter({ columnData, dataKey, rowData }) const renderedCell = cellRenderer(cellData, dataKey, rowData, rowIndex, columnData) const style = this._getFlexStyleForColumn(column) diff --git a/source/FlexTable/FlexTable.test.js b/source/FlexTable/FlexTable.test.js index 2624a7b29..160d80c72 100644 --- a/source/FlexTable/FlexTable.test.js +++ b/source/FlexTable/FlexTable.test.js @@ -226,7 +226,7 @@ describe('FlexTable', () => { describe('custom getter functions', () => { it('should use a custom cellDataGetter if specified', () => { const rendered = findDOMNode(render(getMarkup({ - cellDataGetter: (dataKey, rowData, columnData) => `Custom ${dataKey} for row ${rowData.get('id')}` + cellDataGetter: ({ columnData, dataKey, rowData }) => `Custom ${dataKey} for row ${rowData.get('id')}` }))) const nameColumns = rendered.querySelectorAll('.FlexTable__rowColumn:first-of-type') Array.from(nameColumns).forEach((nameColumn, index) => { diff --git a/source/FlexTable/types.js b/source/FlexTable/types.js new file mode 100644 index 000000000..08593e6ac --- /dev/null +++ b/source/FlexTable/types.js @@ -0,0 +1,7 @@ +/** @flow */ + +export type CellDataGetterParams = { + columnData: ?any, + dataKey: string, + rowData: any +}; From 59336a426420285e861b13583a81efdd4af64f31 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Thu, 28 Apr 2016 21:51:40 -0700 Subject: [PATCH 06/60] Updated signature for VirtualScroll's :rowRenderer --- docs/VirtualScroll.md | 4 ++-- source/VirtualScroll/VirtualScroll.example.js | 2 +- source/VirtualScroll/VirtualScroll.js | 4 ++-- source/VirtualScroll/VirtualScroll.test.js | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/VirtualScroll.md b/docs/VirtualScroll.md index 6a2600c79..09a58036d 100644 --- a/docs/VirtualScroll.md +++ b/docs/VirtualScroll.md @@ -13,7 +13,7 @@ This component renders a virtualized list of elements with either fixed or dynam | onScroll | Function | | Callback invoked whenever the scroll offset changes within the inner scrollable region: `({ clientHeight, scrollHeight, scrollTop }): void` | | overscanRowCount | Number | | Number of rows to render above/below the visible bounds of the list. This can help reduce flickering during scrolling on certain browers/devices. | | rowHeight | Number or Function | ✓ | Either a fixed row height (number) or a function that returns the height of a row given its index: `(index: number): number` | -| rowRenderer | Function | ✓ | Responsbile for rendering a row given an index. Signature should look like `(index: number): React.PropTypes.node` | +| rowRenderer | Function | ✓ | Responsbile for rendering a row given an index. Signature should look like `({ index: number }): React.PropTypes.node` | | rowCount | Number | ✓ | Number of rows in list. | | scrollToIndex | Number | | Row index to ensure visible (by forcefully scrolling if necessary) | | scrollTop | Number | | Vertical offset | @@ -64,7 +64,7 @@ ReactDOM.render( rowCount={list.length} rowHeight={20} rowRenderer={ - index => list[index] // Could also be a DOM element + ({ index }) => list[index] // Could also be a DOM element } />, document.getElementById('example') diff --git a/source/VirtualScroll/VirtualScroll.example.js b/source/VirtualScroll/VirtualScroll.example.js index 03063c327..bd980e151 100644 --- a/source/VirtualScroll/VirtualScroll.example.js +++ b/source/VirtualScroll/VirtualScroll.example.js @@ -169,7 +169,7 @@ export default class VirtualScrollExample extends Component { this.setState({ scrollToIndex }) } - _rowRenderer (index) { + _rowRenderer ({ index }) { const { useDynamicRowHeight } = this.state const datum = this._getDatum(index) diff --git a/source/VirtualScroll/VirtualScroll.js b/source/VirtualScroll/VirtualScroll.js index 06916f082..dcfa526cd 100644 --- a/source/VirtualScroll/VirtualScroll.js +++ b/source/VirtualScroll/VirtualScroll.js @@ -50,7 +50,7 @@ export default class VirtualScroll extends Component { */ rowHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.func]).isRequired, - /** Responsbile for rendering a row given an index */ + /** Responsbile for rendering a row given an index; ({ index: number }): node */ rowRenderer: PropTypes.func.isRequired, /** Number of rows in list. */ @@ -115,7 +115,7 @@ export default class VirtualScroll extends Component { stopIndex: rowStopIndex })} overscanRowCount={overscanRowCount} - cellRenderer={({ columnIndex, rowIndex }) => rowRenderer(rowIndex)} + cellRenderer={({ columnIndex, rowIndex }) => rowRenderer({ index: rowIndex })} rowHeight={rowHeight} rowCount={rowCount} scrollToRow={scrollToIndex} diff --git a/source/VirtualScroll/VirtualScroll.test.js b/source/VirtualScroll/VirtualScroll.test.js index a8abbacf1..6cd08c72c 100644 --- a/source/VirtualScroll/VirtualScroll.test.js +++ b/source/VirtualScroll/VirtualScroll.test.js @@ -25,7 +25,7 @@ describe('VirtualScroll', () => { scrollTop = undefined, width = 100 } = {}) { - function rowRenderer (index) { + function rowRenderer ({ index }) { return (
Date: Thu, 28 Apr 2016 22:02:00 -0700 Subject: [PATCH 07/60] Updated signature of VirtualScroll's :rowHeight function. --- docs/VirtualScroll.md | 2 +- source/VirtualScroll/VirtualScroll.example.js | 2 +- source/VirtualScroll/VirtualScroll.js | 9 +++++++-- source/VirtualScroll/VirtualScroll.test.js | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/VirtualScroll.md b/docs/VirtualScroll.md index 09a58036d..2111969ae 100644 --- a/docs/VirtualScroll.md +++ b/docs/VirtualScroll.md @@ -12,7 +12,7 @@ This component renders a virtualized list of elements with either fixed or dynam | onRowsRendered | Function | | Callback invoked with information about the slice of rows that were just rendered: `({ overscanStartIndex, overscanStopIndex, startIndex, stopIndex }): void` | | onScroll | Function | | Callback invoked whenever the scroll offset changes within the inner scrollable region: `({ clientHeight, scrollHeight, scrollTop }): void` | | overscanRowCount | Number | | Number of rows to render above/below the visible bounds of the list. This can help reduce flickering during scrolling on certain browers/devices. | -| rowHeight | Number or Function | ✓ | Either a fixed row height (number) or a function that returns the height of a row given its index: `(index: number): number` | +| rowHeight | Number or Function | ✓ | Either a fixed row height (number) or a function that returns the height of a row given its index: `({ index: number }): number` | | rowRenderer | Function | ✓ | Responsbile for rendering a row given an index. Signature should look like `({ index: number }): React.PropTypes.node` | | rowCount | Number | ✓ | Number of rows in list. | | scrollToIndex | Number | | Row index to ensure visible (by forcefully scrolling if necessary) | diff --git a/source/VirtualScroll/VirtualScroll.example.js b/source/VirtualScroll/VirtualScroll.example.js index bd980e151..1957140f1 100644 --- a/source/VirtualScroll/VirtualScroll.example.js +++ b/source/VirtualScroll/VirtualScroll.example.js @@ -140,7 +140,7 @@ export default class VirtualScrollExample extends Component { return list.get(index % list.size) } - _getRowHeight (index) { + _getRowHeight ({ index }) { return this._getDatum(index).size } diff --git a/source/VirtualScroll/VirtualScroll.js b/source/VirtualScroll/VirtualScroll.js index dcfa526cd..d63013062 100644 --- a/source/VirtualScroll/VirtualScroll.js +++ b/source/VirtualScroll/VirtualScroll.js @@ -46,7 +46,7 @@ export default class VirtualScroll extends Component { /** * Either a fixed row height (number) or a function that returns the height of a row given its index. - * (index: number): number + * ({ index: number }): number */ rowHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.func]).isRequired, @@ -98,6 +98,11 @@ export default class VirtualScroll extends Component { const classNames = cn('VirtualScroll', className) + // @TODO version-7 Remove this wrapper once Grid's :rowHeight signature has been updated + const wrappedRowHeight = typeof rowHeight === 'function' + ? index => rowHeight({ index }) + : rowHeight + return ( rowRenderer({ index: rowIndex })} - rowHeight={rowHeight} + rowHeight={wrappedRowHeight} rowCount={rowCount} scrollToRow={scrollToIndex} scrollTop={scrollTop} diff --git a/source/VirtualScroll/VirtualScroll.test.js b/source/VirtualScroll/VirtualScroll.test.js index 6cd08c72c..2b17c03c3 100644 --- a/source/VirtualScroll/VirtualScroll.test.js +++ b/source/VirtualScroll/VirtualScroll.test.js @@ -326,7 +326,7 @@ describe('VirtualScroll', () => { describe('recomputeRowHeights', () => { it('should recompute row heights and other values when called', () => { let highestRowIndex = 0 - const rowHeight = (index) => { + const rowHeight = ({ index }) => { highestRowIndex = Math.max(index, highestRowIndex) return 10 } From d2764be49af633034ca15d07ae1611e47efd7b14 Mon Sep 17 00:00:00 2001 From: MikeMac Date: Fri, 29 Apr 2016 00:04:42 -0500 Subject: [PATCH 08/60] Functional changes to FlexTable#sort for named parameters --- source/FlexTable/FlexTable.js | 7 +++++-- source/FlexTable/FlexTable.test.js | 8 ++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/source/FlexTable/FlexTable.js b/source/FlexTable/FlexTable.js index 2e20ffa10..66dd0d625 100644 --- a/source/FlexTable/FlexTable.js +++ b/source/FlexTable/FlexTable.js @@ -104,7 +104,7 @@ export default class FlexTable extends Component { /** * Sort function to be called if a sortable header is clicked. - * (dataKey: string, sortDirection: SortDirection): void + * ({ dataKey: string, sortDirection: SortDirection }): void */ sort: PropTypes.func, @@ -295,7 +295,10 @@ export default class FlexTable extends Component { : SortDirection.DESC const onClick = () => { - sortEnabled && sort(dataKey, newSortDirection) + sortEnabled && sort({ + dataKey, + sortDirection: newSortDirection + }) onHeaderClick && onHeaderClick(dataKey, columnData) } diff --git a/source/FlexTable/FlexTable.test.js b/source/FlexTable/FlexTable.test.js index 160d80c72..72b21ddaa 100644 --- a/source/FlexTable/FlexTable.test.js +++ b/source/FlexTable/FlexTable.test.js @@ -311,7 +311,7 @@ describe('FlexTable', () => { sortDirections.forEach(sortDirection => { const sortCalls = [] const rendered = findDOMNode(render(getMarkup({ - sort: (dataKey, newSortDirection) => sortCalls.push({dataKey, newSortDirection}), + sort: ({dataKey, sortDirection: newSortDirection}) => sortCalls.push({dataKey, newSortDirection}), sortBy: 'name', sortDirection }))) @@ -330,7 +330,7 @@ describe('FlexTable', () => { it('should call sort with the correct arguments when a new sort-by column header is clicked', () => { const sortCalls = [] const rendered = findDOMNode(render(getMarkup({ - sort: (dataKey, newSortDirection) => sortCalls.push({dataKey, newSortDirection}), + sort: ({dataKey, sortDirection: newSortDirection}) => sortCalls.push({dataKey, newSortDirection}), sortBy: 'email', sortDirection: SortDirection.ASC }))) @@ -347,7 +347,7 @@ describe('FlexTable', () => { it('should call sort when a column header is activated via ENTER or SPACE key', () => { const sortCalls = [] const rendered = findDOMNode(render(getMarkup({ - sort: (dataKey, newSortDirection) => sortCalls.push({dataKey, newSortDirection}), + sort: ({dataKey, sortDirection: newSortDirection}) => sortCalls.push({dataKey, newSortDirection}), sortBy: 'name' }))) const nameColumn = rendered.querySelector('.FlexTable__headerColumn:first-of-type') @@ -392,7 +392,7 @@ describe('FlexTable', () => { const sortCalls = [] const rendered = findDOMNode(render(getMarkup({ headerRenderer: (params) => 'custom header', - sort: (sortKey, sortDirection) => sortCalls.push([sortKey, sortDirection]), + sort: ({dataKey: sortKey, sortDirection}) => sortCalls.push([sortKey, sortDirection]), sortBy: 'name', sortDirection: SortDirection.ASC }))) From 7ef3a36fe5ed752d427a32dcb51df919e41e0a94 Mon Sep 17 00:00:00 2001 From: MikeMac Date: Fri, 29 Apr 2016 00:04:54 -0500 Subject: [PATCH 09/60] FlexTable#sort docs --- docs/FlexTable.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/FlexTable.md b/docs/FlexTable.md index 9c8ddcea5..9d46df23c 100644 --- a/docs/FlexTable.md +++ b/docs/FlexTable.md @@ -25,7 +25,7 @@ This component expects explicit width, height, and padding parameters. | rowCount | Number | ✓ | Number of rows in table. | | scrollToIndex | Number | | Row index to ensure visible (by forcefully scrolling if necessary) | | scrollTop | Number | | Vertical offset | -| sort | Function | | Sort function to be called if a sortable header is clicked. `(dataKey: string, sortDirection: SortDirection): void` | +| sort | Function | | Sort function to be called if a sortable header is clicked. `({ dataKey: string, sortDirection: SortDirection }): void` | | sortBy | String | | Data is currently sorted by this `dataKey` (if it is sorted at all) | | sortDirection | [SortDirection](SortDirection.md) | | Data is currently sorted in this direction (if it is sorted at all) | | width | Number | ✓ | Width of the table | From f67fb8023ad3708ed3934b280ace456811fbb9da Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Thu, 28 Apr 2016 22:09:43 -0700 Subject: [PATCH 10/60] Updated signature of InfiniteLoader's :isRowLoaded. --- docs/InfiniteLoader.md | 4 ++-- source/InfiniteLoader/InfiniteLoader.example.js | 4 ++-- source/InfiniteLoader/InfiniteLoader.js | 16 ++++++++-------- source/InfiniteLoader/InfiniteLoader.test.js | 6 +++--- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/InfiniteLoader.md b/docs/InfiniteLoader.md index 7c50d8d1f..432fc9863 100644 --- a/docs/InfiniteLoader.md +++ b/docs/InfiniteLoader.md @@ -7,7 +7,7 @@ High-order component that manages just-in-time fetching of data as a user scroll | Property | Type | Required? | Description | |:---|:---|:---:|:---| | children | Function | ✓ | Function responsible for rendering a virtualized component. This function should implement the following signature: `({ onRowsRendered, registerChild }) => PropTypes.element` | -| isRowLoaded | Function | ✓ | Function responsible for tracking the loaded state of each row. It should implement the following signature: `(index: number): boolean` | +| isRowLoaded | Function | ✓ | Function responsible for tracking the loaded state of each row. It should implement the following signature: `({ index: number }): boolean` | | loadMoreRows | Function | ✓ | Callback to be invoked when more rows must be loaded. It should implement the following signature: `({ startIndex, stopIndex }): Promise`. The returned Promise should be resolved once row data has finished loading. It will be used to determine when to refresh the list with the newly-loaded data. This callback may be called multiple times in reaction to a single scroll event. | | minimumBatchSize | Number | | Minimum number of rows to be loaded at a time. This property can be used to batch requests to reduce HTTP requests. Defaults to `10`. | | rowCount | Number | ✓ | Number of rows in list; can be arbitrary high number if actual number is unknown. | @@ -34,7 +34,7 @@ import 'react-virtualized/styles.css'; // only needs to be imported once const list = []; -function isRowLoaded (index) { +function isRowLoaded ({ index }) { return !!list[index]; } diff --git a/source/InfiniteLoader/InfiniteLoader.example.js b/source/InfiniteLoader/InfiniteLoader.example.js index 2193462a0..b2811c2ba 100644 --- a/source/InfiniteLoader/InfiniteLoader.example.js +++ b/source/InfiniteLoader/InfiniteLoader.example.js @@ -111,7 +111,7 @@ export default class InfiniteLoaderExample extends Component { }) } - _isRowLoaded (index) { + _isRowLoaded ({ index }) { const { loadedRowsMap } = this.state return !!loadedRowsMap[index] // STATUS_LOADING or STATUS_LOADED } @@ -152,7 +152,7 @@ export default class InfiniteLoaderExample extends Component { return new Promise(resolve => promiseResolver = resolve) } - _rowRenderer (index) { + _rowRenderer ({ index }) { const { list } = this.props const { loadedRowsMap } = this.state diff --git a/source/InfiniteLoader/InfiniteLoader.js b/source/InfiniteLoader/InfiniteLoader.js index 285231e50..b478cdff8 100644 --- a/source/InfiniteLoader/InfiniteLoader.js +++ b/source/InfiniteLoader/InfiniteLoader.js @@ -21,7 +21,7 @@ export default class InfiniteLoader extends Component { /** * Function responsible for tracking the loaded state of each row. - * It should implement the following signature: (index: number): boolean + * It should implement the following signature: ({ index: number }): boolean */ isRowLoaded: PropTypes.func.isRequired, @@ -148,13 +148,13 @@ export function scanForUnloadedRanges ({ let rangeStartIndex = null let rangeStopIndex = null - for (let i = startIndex; i <= stopIndex; i++) { - let loaded = isRowLoaded(i) + for (let index = startIndex; index <= stopIndex; index++) { + let loaded = isRowLoaded({ index }) if (!loaded) { - rangeStopIndex = i + rangeStopIndex = index if (rangeStartIndex === null) { - rangeStartIndex = i + rangeStartIndex = index } } else if (rangeStopIndex !== null) { unloadedRanges.push({ @@ -176,9 +176,9 @@ export function scanForUnloadedRanges ({ rowCount - 1 ) - for (let i = rangeStopIndex + 1; i <= potentialStopIndex; i++) { - if (!isRowLoaded(i)) { - rangeStopIndex = i + for (let index = rangeStopIndex + 1; index <= potentialStopIndex; index++) { + if (!isRowLoaded({ index })) { + rangeStopIndex = index } else { break } diff --git a/source/InfiniteLoader/InfiniteLoader.test.js b/source/InfiniteLoader/InfiniteLoader.test.js index 7f623af8d..539f83147 100644 --- a/source/InfiniteLoader/InfiniteLoader.test.js +++ b/source/InfiniteLoader/InfiniteLoader.test.js @@ -17,7 +17,7 @@ describe('InfiniteLoader', () => { rowRendererCalls = [] }) - function defaultIsRowLoaded (index) { + function defaultIsRowLoaded ({ index }) { isRowLoadedCalls.push(index) return !!isRowLoadedMap[index] } @@ -138,7 +138,7 @@ describe('InfiniteLoader', () => { it('should respect the specified :minimumBatchSize if a user scrolls past the previous range', () => { const isRowLoadedIndices = {} - function isRowLoaded (index) { + function isRowLoaded ({ index }) { if (!isRowLoadedIndices[index]) { isRowLoadedIndices[index] = true @@ -179,7 +179,7 @@ describe('InfiniteLoader', () => { describe('scanForUnloadedRanges', () => { function createIsRowLoaded (rows) { - return index => rows[index] + return ({ index }) => rows[index] } it('should return an empty array for a range of rows that have all been loaded', () => { From f3a7c5fab0a10ad39d398f9c6e5f106ef693df7a Mon Sep 17 00:00:00 2001 From: MikeMac Date: Fri, 29 Apr 2016 00:11:06 -0500 Subject: [PATCH 11/60] Changes to example.js for FlexTable#sort --- source/FlexTable/FlexTable.example.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/FlexTable/FlexTable.example.js b/source/FlexTable/FlexTable.example.js index 370195d23..14047e34e 100644 --- a/source/FlexTable/FlexTable.example.js +++ b/source/FlexTable/FlexTable.example.js @@ -275,7 +275,7 @@ export default class FlexTableExample extends Component { } } - _sort (sortBy, sortDirection) { + _sort ({ dataKey: sortBy, sortDirection }) { this.setState({ sortBy, sortDirection }) } From 276356f2371478b66f71061248f2edf830ec0016 Mon Sep 17 00:00:00 2001 From: MikeMac Date: Fri, 29 Apr 2016 00:37:00 -0500 Subject: [PATCH 12/60] Functional/Testing changes for :rowHeight using named parameters across the app --- source/FlexTable/FlexTable.example.js | 2 +- source/FlexTable/FlexTable.js | 4 ++-- source/FlexTable/FlexTable.test.js | 6 +++--- source/Grid/Grid.example.js | 2 +- source/Grid/Grid.js | 2 +- source/VirtualScroll/VirtualScroll.js | 7 +------ source/utils/TestHelper.js | 2 +- source/utils/initCellMetadata.js | 4 ++-- 8 files changed, 12 insertions(+), 17 deletions(-) diff --git a/source/FlexTable/FlexTable.example.js b/source/FlexTable/FlexTable.example.js index 14047e34e..cf59fb9f1 100644 --- a/source/FlexTable/FlexTable.example.js +++ b/source/FlexTable/FlexTable.example.js @@ -211,7 +211,7 @@ export default class FlexTableExample extends Component { return list.get(index % list.size) } - _getRowHeight (index) { + _getRowHeight ({ index }) { const { list } = this.props return this._getDatum(list, index).size diff --git a/source/FlexTable/FlexTable.js b/source/FlexTable/FlexTable.js index 66dd0d625..1ca25d06b 100644 --- a/source/FlexTable/FlexTable.js +++ b/source/FlexTable/FlexTable.js @@ -89,7 +89,7 @@ export default class FlexTable extends Component { /** * Either a fixed row height (number) or a function that returns the height of a row given its index. - * (index: number): number + * ({ index: number }): number */ rowHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.func]).isRequired, @@ -408,7 +408,7 @@ export default class FlexTable extends Component { const { rowHeight } = this.props return rowHeight instanceof Function - ? rowHeight(rowIndex) + ? rowHeight({ index: rowIndex }) : rowHeight } diff --git a/source/FlexTable/FlexTable.test.js b/source/FlexTable/FlexTable.test.js index 72b21ddaa..5d37431d6 100644 --- a/source/FlexTable/FlexTable.test.js +++ b/source/FlexTable/FlexTable.test.js @@ -182,14 +182,14 @@ describe('FlexTable', () => { }) it('should support a :rowHeight function', () => { - const rowHeight = (index) => 10 + index * 10 + const rowHeight = ({ index }) => 10 + index * 10 const rendered = findDOMNode(render(getMarkup({ rowHeight, rowCount: 3 }))) const rows = rendered.querySelectorAll('.FlexTable__row') Array.from(rows).forEach((row, index) => { - expect(Number.parseInt(row.style.height, 10)).toEqual(rowHeight(index)) + expect(Number.parseInt(row.style.height, 10)).toEqual(rowHeight({ index })) }) }) @@ -209,7 +209,7 @@ describe('FlexTable', () => { describe('recomputeRowHeights', () => { it('should recompute row heights and other values when called', () => { let highestRowIndex = 0 - const rowHeight = (index) => { + const rowHeight = ({ index }) => { highestRowIndex = Math.max(index, highestRowIndex) return 10 } diff --git a/source/Grid/Grid.example.js b/source/Grid/Grid.example.js index 10b96df0b..ad5977910 100644 --- a/source/Grid/Grid.example.js +++ b/source/Grid/Grid.example.js @@ -188,7 +188,7 @@ export default class GridExample extends Component { return row % 2 === 0 ? styles.evenRow : styles.oddRow } - _getRowHeight (index) { + _getRowHeight ({ index }) { return this._getDatum(index).size } diff --git a/source/Grid/Grid.js b/source/Grid/Grid.js index c4a682e45..9175a1455 100644 --- a/source/Grid/Grid.js +++ b/source/Grid/Grid.js @@ -109,7 +109,7 @@ export default class Grid extends Component { /** * Either a fixed row height (number) or a function that returns the height of a row given its index. - * Should implement the following interface: (index: number): number + * Should implement the following interface: ({ index: number }): number */ rowHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.func]).isRequired, diff --git a/source/VirtualScroll/VirtualScroll.js b/source/VirtualScroll/VirtualScroll.js index d63013062..14455d879 100644 --- a/source/VirtualScroll/VirtualScroll.js +++ b/source/VirtualScroll/VirtualScroll.js @@ -98,11 +98,6 @@ export default class VirtualScroll extends Component { const classNames = cn('VirtualScroll', className) - // @TODO version-7 Remove this wrapper once Grid's :rowHeight signature has been updated - const wrappedRowHeight = typeof rowHeight === 'function' - ? index => rowHeight({ index }) - : rowHeight - return ( rowRenderer({ index: rowIndex })} - rowHeight={wrappedRowHeight} + rowHeight={rowHeight} rowCount={rowCount} scrollToRow={scrollToIndex} scrollTop={scrollTop} diff --git a/source/utils/TestHelper.js b/source/utils/TestHelper.js index 1ee1d92f6..b63397d7d 100644 --- a/source/utils/TestHelper.js +++ b/source/utils/TestHelper.js @@ -15,6 +15,6 @@ export function getCellMetadata () { ] return initCellMetadata({ cellCount: cellSizes.length, - size: index => cellSizes[index] + size: ({ index }) => cellSizes[index] }) } diff --git a/source/utils/initCellMetadata.js b/source/utils/initCellMetadata.js index 2b87a54a1..5dd5ff9f9 100644 --- a/source/utils/initCellMetadata.js +++ b/source/utils/initCellMetadata.js @@ -12,13 +12,13 @@ export default function initCellMetadata ({ }) { const sizeGetter = size instanceof Function ? size - : index => size + : ({ index }) => size const cellMetadata = [] let offset = 0 for (var i = 0; i < cellCount; i++) { - let size = sizeGetter(i) + let size = sizeGetter({ index: i }) if (size == null || isNaN(size)) { throw Error(`Invalid size returned for cell ${i} of value ${size}`) From fd1c1aad8c70a98d4084c305ea11bcbfccb15fc7 Mon Sep 17 00:00:00 2001 From: MikeMac Date: Fri, 29 Apr 2016 00:37:18 -0500 Subject: [PATCH 13/60] Docs changes for :rowHeight using named parameters --- docs/FlexTable.md | 2 +- docs/Grid.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/FlexTable.md b/docs/FlexTable.md index 9d46df23c..56ed0e22d 100644 --- a/docs/FlexTable.md +++ b/docs/FlexTable.md @@ -21,7 +21,7 @@ This component expects explicit width, height, and padding parameters. | onScroll | Function | | Callback invoked whenever the scroll offset changes within the inner scrollable region: `({ clientHeight, scrollHeight, scrollTop }): void` | | rowClassName | String or Function | | CSS class to apply to all table rows (including the header row). This value may be either a static string or a function with the signature `(rowIndex: number): string`. Note that for the header row an index of `-1` is provided. | | rowGetter | Function | ✓ | Callback responsible for returning a data row given an index. `(index: int): any` | -| rowHeight | Number or Function | ✓ | Either a fixed row height (number) or a function that returns the height of a row given its index: `(index: number): number` | +| rowHeight | Number or Function | ✓ | Either a fixed row height (number) or a function that returns the height of a row given its index: `({ index: number }): number` | | rowCount | Number | ✓ | Number of rows in table. | | scrollToIndex | Number | | Row index to ensure visible (by forcefully scrolling if necessary) | | scrollTop | Number | | Vertical offset | diff --git a/docs/Grid.md b/docs/Grid.md index 83d3f2c28..cbabc06e7 100644 --- a/docs/Grid.md +++ b/docs/Grid.md @@ -19,7 +19,7 @@ Only a small number of cells are rendered based on the horizontal and vertical s | overscanColumnCount | | Number | Number of columns to render before/after the visible slice of the grid. This can help reduce flickering during scrolling on certain browers/devices. | | overscanRowCount | | Number | Number of rows to render above/below the visible slice of the grid. This can help reduce flickering during scrolling on certain browers/devices. | | rowCount | Number | ✓ | Number of rows in grid. | -| rowHeight | Number or Function | ✓ | Either a fixed row height (number) or a function that returns the height of a row given its index: `(index: number): number` | +| rowHeight | Number or Function | ✓ | Either a fixed row height (number) or a function that returns the height of a row given its index: `({ index: number }): number` | | scrollLeft | Number | | Horizontal offset | | scrollToColumn | Number | | Column index to ensure visible (by forcefully scrolling if necessary) | | scrollToRow | Number | | Row index to ensure visible (by forcefully scrolling if necessary) | From 43abf6b1a288b8b4ce9447dfc558bf0038045f56 Mon Sep 17 00:00:00 2001 From: MikeMac Date: Fri, 29 Apr 2016 00:40:34 -0500 Subject: [PATCH 14/60] Functional/Testing changes for rowDataGetter to use named parameters --- source/FlexTable/FlexTable.example.js | 2 +- source/FlexTable/FlexTable.js | 4 ++-- source/FlexTable/FlexTable.test.js | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/source/FlexTable/FlexTable.example.js b/source/FlexTable/FlexTable.example.js index cf59fb9f1..0f77b7472 100644 --- a/source/FlexTable/FlexTable.example.js +++ b/source/FlexTable/FlexTable.example.js @@ -65,7 +65,7 @@ export default class FlexTableExample extends Component { ) : list - const rowGetter = index => this._getDatum(sortedList, index) + const rowGetter = ({ index }) => this._getDatum(sortedList, index) return ( diff --git a/source/FlexTable/FlexTable.js b/source/FlexTable/FlexTable.js index 1ca25d06b..d1baebbec 100644 --- a/source/FlexTable/FlexTable.js +++ b/source/FlexTable/FlexTable.js @@ -83,7 +83,7 @@ export default class FlexTable extends Component { /** * Callback responsible for returning a data row given an index. - * (index: number): any + * ({ index: number }): any */ rowGetter: PropTypes.func.isRequired, @@ -337,7 +337,7 @@ export default class FlexTable extends Component { const { scrollbarWidth } = this.state const rowClass = rowClassName instanceof Function ? rowClassName(rowIndex) : rowClassName - const rowData = rowGetter(rowIndex) + const rowData = rowGetter({ index: rowIndex }) const renderedRow = React.Children.toArray(children).map( (column, columnIndex) => this._createColumn( diff --git a/source/FlexTable/FlexTable.test.js b/source/FlexTable/FlexTable.test.js index 5d37431d6..eb875fdf7 100644 --- a/source/FlexTable/FlexTable.test.js +++ b/source/FlexTable/FlexTable.test.js @@ -19,12 +19,12 @@ describe('FlexTable', () => { const list = Immutable.fromJS(array) // Works with an Immutable List of Maps - function immutableRowGetter (index) { + function immutableRowGetter ({ index }) { return list.get(index) } // Works with an Array of Objects - function vanillaRowGetter (index) { + function vanillaRowGetter ({ index }) { return array[index] } From 220536f43643d751bd42c1561839ad517c0b1821 Mon Sep 17 00:00:00 2001 From: MikeMac Date: Fri, 29 Apr 2016 00:40:50 -0500 Subject: [PATCH 15/60] Docs changes for rowDataGetter to use named parameters --- docs/FlexTable.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/FlexTable.md b/docs/FlexTable.md index 56ed0e22d..1dc04588b 100644 --- a/docs/FlexTable.md +++ b/docs/FlexTable.md @@ -20,7 +20,7 @@ This component expects explicit width, height, and padding parameters. | overscanRowCount | | Number | Number of rows to render above/below the visible bounds of the list. This can help reduce flickering during scrolling on certain browers/devices. | | onScroll | Function | | Callback invoked whenever the scroll offset changes within the inner scrollable region: `({ clientHeight, scrollHeight, scrollTop }): void` | | rowClassName | String or Function | | CSS class to apply to all table rows (including the header row). This value may be either a static string or a function with the signature `(rowIndex: number): string`. Note that for the header row an index of `-1` is provided. | -| rowGetter | Function | ✓ | Callback responsible for returning a data row given an index. `(index: int): any` | +| rowGetter | Function | ✓ | Callback responsible for returning a data row given an index. `({ index: int }): any` | | rowHeight | Number or Function | ✓ | Either a fixed row height (number) or a function that returns the height of a row given its index: `({ index: number }): number` | | rowCount | Number | ✓ | Number of rows in table. | | scrollToIndex | Number | | Row index to ensure visible (by forcefully scrolling if necessary) | From 9d35bc8e52d9c710ae693ee4cdd3bf67d5f98212 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Fri, 29 Apr 2016 07:55:01 -0700 Subject: [PATCH 16/60] Renamed FlexTable's sort() param from :dataKey to :sortBy --- docs/FlexTable.md | 2 +- source/FlexTable/FlexTable.example.js | 2 +- source/FlexTable/FlexTable.js | 4 ++-- source/FlexTable/FlexTable.test.js | 18 +++++++++--------- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/FlexTable.md b/docs/FlexTable.md index 1dc04588b..336b561e6 100644 --- a/docs/FlexTable.md +++ b/docs/FlexTable.md @@ -25,7 +25,7 @@ This component expects explicit width, height, and padding parameters. | rowCount | Number | ✓ | Number of rows in table. | | scrollToIndex | Number | | Row index to ensure visible (by forcefully scrolling if necessary) | | scrollTop | Number | | Vertical offset | -| sort | Function | | Sort function to be called if a sortable header is clicked. `({ dataKey: string, sortDirection: SortDirection }): void` | +| sort | Function | | Sort function to be called if a sortable header is clicked. `({ sortBy: string, sortDirection: SortDirection }): void` | | sortBy | String | | Data is currently sorted by this `dataKey` (if it is sorted at all) | | sortDirection | [SortDirection](SortDirection.md) | | Data is currently sorted in this direction (if it is sorted at all) | | width | Number | ✓ | Width of the table | diff --git a/source/FlexTable/FlexTable.example.js b/source/FlexTable/FlexTable.example.js index 0f77b7472..c0b35248f 100644 --- a/source/FlexTable/FlexTable.example.js +++ b/source/FlexTable/FlexTable.example.js @@ -275,7 +275,7 @@ export default class FlexTableExample extends Component { } } - _sort ({ dataKey: sortBy, sortDirection }) { + _sort ({ sortBy, sortDirection }) { this.setState({ sortBy, sortDirection }) } diff --git a/source/FlexTable/FlexTable.js b/source/FlexTable/FlexTable.js index d1baebbec..b0f88bb2e 100644 --- a/source/FlexTable/FlexTable.js +++ b/source/FlexTable/FlexTable.js @@ -104,7 +104,7 @@ export default class FlexTable extends Component { /** * Sort function to be called if a sortable header is clicked. - * ({ dataKey: string, sortDirection: SortDirection }): void + * ({ sortBy: string, sortDirection: SortDirection }): void */ sort: PropTypes.func, @@ -296,7 +296,7 @@ export default class FlexTable extends Component { const onClick = () => { sortEnabled && sort({ - dataKey, + sortBy: dataKey, sortDirection: newSortDirection }) onHeaderClick && onHeaderClick(dataKey, columnData) diff --git a/source/FlexTable/FlexTable.test.js b/source/FlexTable/FlexTable.test.js index eb875fdf7..945fa2d27 100644 --- a/source/FlexTable/FlexTable.test.js +++ b/source/FlexTable/FlexTable.test.js @@ -311,7 +311,7 @@ describe('FlexTable', () => { sortDirections.forEach(sortDirection => { const sortCalls = [] const rendered = findDOMNode(render(getMarkup({ - sort: ({dataKey, sortDirection: newSortDirection}) => sortCalls.push({dataKey, newSortDirection}), + sort: ({ sortBy, sortDirection }) => sortCalls.push({ sortBy, sortDirection }), sortBy: 'name', sortDirection }))) @@ -320,9 +320,9 @@ describe('FlexTable', () => { Simulate.click(nameColumn) expect(sortCalls.length).toEqual(1) - const {dataKey, newSortDirection} = sortCalls[0] + const { sortBy, sortDirection: newSortDirection } = sortCalls[0] const expectedSortDirection = sortDirection === SortDirection.ASC ? SortDirection.DESC : SortDirection.ASC - expect(dataKey).toEqual('name') + expect(sortBy).toEqual('name') expect(newSortDirection).toEqual(expectedSortDirection) }) }) @@ -330,7 +330,7 @@ describe('FlexTable', () => { it('should call sort with the correct arguments when a new sort-by column header is clicked', () => { const sortCalls = [] const rendered = findDOMNode(render(getMarkup({ - sort: ({dataKey, sortDirection: newSortDirection}) => sortCalls.push({dataKey, newSortDirection}), + sort: ({ sortBy, sortDirection }) => sortCalls.push({ sortBy, sortDirection }), sortBy: 'email', sortDirection: SortDirection.ASC }))) @@ -339,15 +339,15 @@ describe('FlexTable', () => { Simulate.click(nameColumn) expect(sortCalls.length).toEqual(1) - const {dataKey, newSortDirection} = sortCalls[0] - expect(dataKey).toEqual('name') - expect(newSortDirection).toEqual(SortDirection.ASC) + const {sortBy, sortDirection} = sortCalls[0] + expect(sortBy).toEqual('name') + expect(sortDirection).toEqual(SortDirection.ASC) }) it('should call sort when a column header is activated via ENTER or SPACE key', () => { const sortCalls = [] const rendered = findDOMNode(render(getMarkup({ - sort: ({dataKey, sortDirection: newSortDirection}) => sortCalls.push({dataKey, newSortDirection}), + sort: ({ sortBy, sortDirection }) => sortCalls.push({ sortBy, sortDirection }), sortBy: 'name' }))) const nameColumn = rendered.querySelector('.FlexTable__headerColumn:first-of-type') @@ -392,7 +392,7 @@ describe('FlexTable', () => { const sortCalls = [] const rendered = findDOMNode(render(getMarkup({ headerRenderer: (params) => 'custom header', - sort: ({dataKey: sortKey, sortDirection}) => sortCalls.push([sortKey, sortDirection]), + sort: ({ sortBy, sortDirection }) => sortCalls.push([sortBy, sortDirection]), sortBy: 'name', sortDirection: SortDirection.ASC }))) From 82a73329c55d9f82c11c7a82246c045596cf0c72 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Fri, 29 Apr 2016 08:31:51 -0700 Subject: [PATCH 17/60] Updated Grid :columnWidth signature --- docs/Grid.md | 2 +- source/Grid/Grid.example.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Grid.md b/docs/Grid.md index cbabc06e7..727a03fab 100644 --- a/docs/Grid.md +++ b/docs/Grid.md @@ -11,7 +11,7 @@ Only a small number of cells are rendered based on the horizontal and vertical s | cellRangeRenderer | Function | ✓ | Responsible for rendering a group of cells given their index ranges.: `({ columnMetadata:Array, columnStartIndex: number, columnStopIndex: number, cellRenderer: Function, rowMetadata:Array, rowStartIndex: number, rowStopIndex: number }): Array` | | className | String | | Optional custom CSS class name to attach to root Grid element. | | columnCount | Number | ✓ | Number of columns in grid. | -| columnWidth | Number or Function | ✓ | Either a fixed column width (number) or a function that returns the width of a column given its index: `(index: number): number` | +| columnWidth | Number or Function | ✓ | Either a fixed column width (number) or a function that returns the width of a column given its index: `({ index: number }): number` | | height | Number | ✓ | Height of Grid; this property determines the number of visible (vs virtualized) rows. | | noContentRenderer | Function | | Optional renderer to be rendered inside the grid when either `rowCount` or `columnCount` is 0: `(): PropTypes.node` | | onSectionRendered | Function | | Callback invoked with information about the section of the Grid that was just rendered: `({ columnOverscanStartIndex, columnOverscanStopIndex, columnStartIndex, columnStopIndex, rowOverscanStartIndex, rowOverscanStopIndex, rowStartIndex, rowStopIndex }): void` | diff --git a/source/Grid/Grid.example.js b/source/Grid/Grid.example.js index ad5977910..4062f787c 100644 --- a/source/Grid/Grid.example.js +++ b/source/Grid/Grid.example.js @@ -165,7 +165,7 @@ export default class GridExample extends Component { return shallowCompare(this, nextProps, nextState) } - _getColumnWidth (index) { + _getColumnWidth ({ index }) { switch (index) { case 0: return 50 From c7f39a9895dd20c7856750036ad352bfdad28e87 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Fri, 29 Apr 2016 08:35:45 -0700 Subject: [PATCH 18/60] Updated FlexTable's :rowClassName function signature from (index: number): string to ({ index: number }): string --- docs/FlexTable.md | 2 +- source/FlexTable/FlexTable.example.js | 2 +- source/FlexTable/FlexTable.js | 18 +++++++++--------- source/FlexTable/FlexTable.test.js | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/FlexTable.md b/docs/FlexTable.md index 336b561e6..68cd88193 100644 --- a/docs/FlexTable.md +++ b/docs/FlexTable.md @@ -19,7 +19,7 @@ This component expects explicit width, height, and padding parameters. | onRowsRendered | | Function | Callback invoked with information about the slice of rows that were just rendered: `({ overscanStartIndex, overscanStopIndex, startIndex, stopIndex }): void` | | overscanRowCount | | Number | Number of rows to render above/below the visible bounds of the list. This can help reduce flickering during scrolling on certain browers/devices. | | onScroll | Function | | Callback invoked whenever the scroll offset changes within the inner scrollable region: `({ clientHeight, scrollHeight, scrollTop }): void` | -| rowClassName | String or Function | | CSS class to apply to all table rows (including the header row). This value may be either a static string or a function with the signature `(rowIndex: number): string`. Note that for the header row an index of `-1` is provided. | +| rowClassName | String or Function | | CSS class to apply to all table rows (including the header row). This value may be either a static string or a function with the signature `({ index: number }): string`. Note that for the header row an index of `-1` is provided. | | rowGetter | Function | ✓ | Callback responsible for returning a data row given an index. `({ index: int }): any` | | rowHeight | Number or Function | ✓ | Either a fixed row height (number) or a function that returns the height of a row given its index: `({ index: number }): number` | | rowCount | Number | ✓ | Number of rows in table. | diff --git a/source/FlexTable/FlexTable.example.js b/source/FlexTable/FlexTable.example.js index c0b35248f..1318d8984 100644 --- a/source/FlexTable/FlexTable.example.js +++ b/source/FlexTable/FlexTable.example.js @@ -267,7 +267,7 @@ export default class FlexTableExample extends Component { this.setState({ scrollToIndex }) } - _rowClassName (index) { + _rowClassName ({ index }) { if (index < 0) { return styles.headerRow } else { diff --git a/source/FlexTable/FlexTable.js b/source/FlexTable/FlexTable.js index b0f88bb2e..b69de1e06 100644 --- a/source/FlexTable/FlexTable.js +++ b/source/FlexTable/FlexTable.js @@ -77,7 +77,7 @@ export default class FlexTable extends Component { /** * Optional CSS class to apply to all table rows (including the header row). * This property can be a CSS class name (string) or a function that returns a class name. - * If a function is provided its signature should be: (rowIndex: number): string + * If a function is provided its signature should be: ({ index: number }): string */ rowClassName: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), @@ -179,7 +179,7 @@ export default class FlexTable extends Component { return this._createRow(index) } - const rowClass = rowClassName instanceof Function ? rowClassName(-1) : rowClassName + const rowClass = rowClassName instanceof Function ? rowClassName({ index: -1 }) : rowClassName return (
this._createColumn( column, columnIndex, rowData, - rowIndex + index ) ) @@ -354,16 +354,16 @@ export default class FlexTable extends Component { a11yProps['aria-label'] = 'row' a11yProps.role = 'row' a11yProps.tabIndex = 0 - a11yProps.onClick = () => onRowClick(rowIndex) + a11yProps.onClick = () => onRowClick(index) } return (
diff --git a/source/FlexTable/FlexTable.test.js b/source/FlexTable/FlexTable.test.js index 945fa2d27..9101f0bf5 100644 --- a/source/FlexTable/FlexTable.test.js +++ b/source/FlexTable/FlexTable.test.js @@ -501,7 +501,7 @@ describe('FlexTable', () => { it('should render dynamic classname given :rowClassName as a function', () => { const rendered = findDOMNode(render(getMarkup({ - rowClassName: rowIndex => rowIndex % 2 === 0 ? 'even' : 'odd' + rowClassName: ({ index }) => index % 2 === 0 ? 'even' : 'odd' }))) const rows = rendered.querySelectorAll('.FlexTable__row') Array.from(rows).forEach((row, index) => { From 9c5c8e11d0a4b02b32e72a6d67e052649111412b Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Fri, 29 Apr 2016 08:37:10 -0700 Subject: [PATCH 19/60] Updated FlexTable :onRowClick signature from (index: number): void to ({ index: number }): void --- docs/FlexTable.md | 2 +- source/FlexTable/FlexTable.js | 2 +- source/FlexTable/FlexTable.test.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/FlexTable.md b/docs/FlexTable.md index 68cd88193..8d8098522 100644 --- a/docs/FlexTable.md +++ b/docs/FlexTable.md @@ -15,7 +15,7 @@ This component expects explicit width, height, and padding parameters. | height | Number | ✓ | Fixed/available height for out DOM element | | noRowsRenderer | | Function | Callback used to render placeholder content when :rowCount is 0 | | onHeaderClick | | Function | Callback invoked when a user clicks on a table header. `(dataKey: string, columnData: any): void` | -| onRowClick | | Function | Callback invoked when a user clicks on a table row. `(rowIndex: number): void` | +| onRowClick | | Function | Callback invoked when a user clicks on a table row. `({ index: number }): void` | | onRowsRendered | | Function | Callback invoked with information about the slice of rows that were just rendered: `({ overscanStartIndex, overscanStopIndex, startIndex, stopIndex }): void` | | overscanRowCount | | Number | Number of rows to render above/below the visible bounds of the list. This can help reduce flickering during scrolling on certain browers/devices. | | onScroll | Function | | Callback invoked whenever the scroll offset changes within the inner scrollable region: `({ clientHeight, scrollHeight, scrollTop }): void` | diff --git a/source/FlexTable/FlexTable.js b/source/FlexTable/FlexTable.js index b69de1e06..fce034aaf 100644 --- a/source/FlexTable/FlexTable.js +++ b/source/FlexTable/FlexTable.js @@ -354,7 +354,7 @@ export default class FlexTable extends Component { a11yProps['aria-label'] = 'row' a11yProps.role = 'row' a11yProps.tabIndex = 0 - a11yProps.onClick = () => onRowClick(index) + a11yProps.onClick = () => onRowClick({ index }) } return ( diff --git a/source/FlexTable/FlexTable.test.js b/source/FlexTable/FlexTable.test.js index 9101f0bf5..3610349c8 100644 --- a/source/FlexTable/FlexTable.test.js +++ b/source/FlexTable/FlexTable.test.js @@ -478,7 +478,7 @@ describe('FlexTable', () => { it('should call :onRowClick with the correct :rowIndex when a row is clicked', () => { const onRowClickCalls = [] const rendered = findDOMNode(render(getMarkup({ - onRowClick: index => onRowClickCalls.push(index) + onRowClick: ({ index }) => onRowClickCalls.push(index) }))) const rows = rendered.querySelectorAll('.FlexTable__row') Simulate.click(rows[0]) From cccd3652632c7b71159e0d591572232652a044bf Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Fri, 29 Apr 2016 08:39:08 -0700 Subject: [PATCH 20/60] Fixed a regression in ArrowKeyStepper and AutoSizer examples --- source/ArrowKeyStepper/ArrowKeyStepper.example.js | 4 ++-- source/AutoSizer/AutoSizer.example.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/ArrowKeyStepper/ArrowKeyStepper.example.js b/source/ArrowKeyStepper/ArrowKeyStepper.example.js index 4253689db..3591a34e9 100644 --- a/source/ArrowKeyStepper/ArrowKeyStepper.example.js +++ b/source/ArrowKeyStepper/ArrowKeyStepper.example.js @@ -80,11 +80,11 @@ export default class ArrowKeyStepperExample extends Component { return shallowCompare(this, nextProps, nextState) } - _getColumnWidth (index) { + _getColumnWidth ({ index }) { return (1 + (index % 3)) * 60 } - _getRowHeight (index) { + _getRowHeight ({ index }) { return (1 + (index % 3)) * 30 } diff --git a/source/AutoSizer/AutoSizer.example.js b/source/AutoSizer/AutoSizer.example.js index 016c2a10e..9e7628f62 100644 --- a/source/AutoSizer/AutoSizer.example.js +++ b/source/AutoSizer/AutoSizer.example.js @@ -82,7 +82,7 @@ export default class AutoSizerExample extends Component { return shallowCompare(this, nextProps, nextState) } - _rowRenderer (index) { + _rowRenderer ({ index }) { const { list } = this.props const row = list.get(index) From 52f96754b11b2b4f78ec7c9637c26c1105d19f20 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Fri, 29 Apr 2016 08:41:57 -0700 Subject: [PATCH 21/60] Updated FlexTable's :onHeaderClick signature from to --- source/FlexTable/FlexTable.js | 6 +++--- source/FlexTable/FlexTable.test.js | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/source/FlexTable/FlexTable.js b/source/FlexTable/FlexTable.js index fce034aaf..5f4c0c23e 100644 --- a/source/FlexTable/FlexTable.js +++ b/source/FlexTable/FlexTable.js @@ -45,13 +45,13 @@ export default class FlexTable extends Component { /** * Optional callback when a column's header is clicked. - * (dataKey: string): void + * ({ columnData: any, dataKey: string }): void */ onHeaderClick: PropTypes.func, /** * Callback invoked when a user clicks on a table row. - * (rowIndex: number): void + * ({ index: number }): void */ onRowClick: PropTypes.func, @@ -299,7 +299,7 @@ export default class FlexTable extends Component { sortBy: dataKey, sortDirection: newSortDirection }) - onHeaderClick && onHeaderClick(dataKey, columnData) + onHeaderClick && onHeaderClick({ columnData, dataKey }) } const onKeyDown = (event) => { diff --git a/source/FlexTable/FlexTable.test.js b/source/FlexTable/FlexTable.test.js index 3610349c8..e045b79f9 100644 --- a/source/FlexTable/FlexTable.test.js +++ b/source/FlexTable/FlexTable.test.js @@ -412,7 +412,7 @@ describe('FlexTable', () => { const rendered = findDOMNode(render(getMarkup({ columnData, headerRenderer: (params) => 'custom header', - onHeaderClick: (dataKey, columnData) => onHeaderClickCalls.push([dataKey, columnData]) + onHeaderClick: ({ columnData, dataKey }) => onHeaderClickCalls.push([dataKey, columnData]) }))) const nameColumn = rendered.querySelector('.FlexTable__headerColumn:first-of-type') @@ -449,7 +449,7 @@ describe('FlexTable', () => { let onHeaderClickCalls = [] const rendered = findDOMNode(render(getMarkup({ disableSort: true, - onHeaderClick: (dataKey, columnData) => onHeaderClickCalls.push({dataKey, columnData}) + onHeaderClick: ({ columnData, dataKey }) => onHeaderClickCalls.push({dataKey, columnData}) }))) const nameColumn = rendered.querySelector('.FlexTable__headerColumn:first-of-type') @@ -463,7 +463,7 @@ describe('FlexTable', () => { let onHeaderClickCalls = [] const rendered = findDOMNode(render(getMarkup({ disableSort: false, - onHeaderClick: (dataKey, columnData) => onHeaderClickCalls.push({dataKey, columnData}) + onHeaderClick: ({ columnData, dataKey }) => onHeaderClickCalls.push({dataKey, columnData}) }))) const nameColumn = rendered.querySelector('.FlexTable__headerColumn:first-of-type') From d4d41084b5fb8d8e0e8d389fb260b11898638bba Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Fri, 29 Apr 2016 08:50:29 -0700 Subject: [PATCH 22/60] Updated signature of FlexColumn's :cellRenderer from to --- docs/FlexColumn.md | 2 +- source/FlexTable/FlexColumn.js | 18 ++++++++--------- source/FlexTable/FlexColumn.test.js | 28 +++++++++++++++++++++++---- source/FlexTable/FlexTable.example.js | 2 +- source/FlexTable/FlexTable.js | 2 +- source/FlexTable/FlexTable.test.js | 6 +++--- source/FlexTable/types.js | 8 ++++++++ 7 files changed, 47 insertions(+), 19 deletions(-) diff --git a/docs/FlexColumn.md b/docs/FlexColumn.md index 200a3ecad..fb460fc97 100644 --- a/docs/FlexColumn.md +++ b/docs/FlexColumn.md @@ -40,7 +40,7 @@ Callback responsible for rendering a cell's contents. It should implement the following signature: ```javascript -function (cellData: any, cellDataKey: string, rowData: any, rowIndex: number, columnData: any): node +function ({ cellData: any, columnData: any, dataKey: string, rowData: any, rowIndex: number }): node ``` A default `cellRenderer` is provided that displays an attribute as a simple string diff --git a/source/FlexTable/FlexColumn.js b/source/FlexTable/FlexColumn.js index 51f65d5d9..54beef482 100644 --- a/source/FlexTable/FlexColumn.js +++ b/source/FlexTable/FlexColumn.js @@ -1,19 +1,19 @@ /** @flow */ import React, { Component, PropTypes } from 'react' -import type { CellDataGetterParams } from './types' +import type { CellDataGetterParams, CellRendererParams } from './types' import SortIndicator from './SortIndicator' /** * Default cell renderer that displays an attribute as a simple string * You should override the column's cellRenderer if your data is some other type of object. */ -export function defaultCellRenderer ( - cellData: any, - cellDataKey: string, - rowData: any, - rowIndex: number, - columnData: any -): string { +export function defaultCellRenderer ({ + cellData, + cellDataKey, + columnData, + rowData, + rowIndex +}: CellRendererParams): string { if (cellData === null || cellData === undefined) { return '' } else { @@ -100,7 +100,7 @@ export default class Column extends Component { /** * Callback responsible for rendering a cell's contents. - * (cellData: any, cellDataKey: string, rowData: any, rowIndex: number, columnData: any): node + * ({ cellData: any, columnData: any, dataKey: string, rowData: any, rowIndex: number }): node */ cellRenderer: PropTypes.func, diff --git a/source/FlexTable/FlexColumn.test.js b/source/FlexTable/FlexColumn.test.js index 94ccf1169..559757747 100644 --- a/source/FlexTable/FlexColumn.test.js +++ b/source/FlexTable/FlexColumn.test.js @@ -29,13 +29,33 @@ describe('Column', () => { describe('defaultCellRenderer', () => { it('should render a value for specified attributes', () => { - expect(defaultCellRenderer('Foo', 'foo', rowData, 0)).toEqual('Foo') - expect(defaultCellRenderer(1, 'bar', rowData, 0)).toEqual('1') + expect(defaultCellRenderer({ + cellData: 'Foo', + dataKey: 'foo', + rowData, + rowIndex: 0 + })).toEqual('Foo') + expect(defaultCellRenderer({ + cellData: 1, + dataKey: 'bar', + rowData, + rowIndex: 0 + })).toEqual('1') }) it('should render empty string for null or missing attributes', () => { - expect(defaultCellRenderer(null, 'baz', rowData, 0)).toEqual('') - expect(defaultCellRenderer(undefined, 'baz', rowData, 0)).toEqual('') + expect(defaultCellRenderer({ + cellData: null, + dataKey: 'baz', + rowData, + rowIndex: 0 + })).toEqual('') + expect(defaultCellRenderer({ + cellData: undefined, + dataKey: 'baz', + rowData, + rowIndex: 0 + })).toEqual('') }) }) }) diff --git a/source/FlexTable/FlexTable.example.js b/source/FlexTable/FlexTable.example.js index 1318d8984..da14e7c09 100644 --- a/source/FlexTable/FlexTable.example.js +++ b/source/FlexTable/FlexTable.example.js @@ -191,7 +191,7 @@ export default class FlexTableExample extends Component { dataKey='random' cellClassName={styles.exampleColumn} cellRenderer={ - (cellData, cellDataKey, rowData, rowIndex, columnData) => cellData + ({ cellData, columnData, dataKey, rowData, rowIndex }) => cellData } flexGrow={1} /> diff --git a/source/FlexTable/FlexTable.js b/source/FlexTable/FlexTable.js index 5f4c0c23e..9b2921840 100644 --- a/source/FlexTable/FlexTable.js +++ b/source/FlexTable/FlexTable.js @@ -238,7 +238,7 @@ export default class FlexTable extends Component { cellRenderer } = column.props const cellData = cellDataGetter({ columnData, dataKey, rowData }) - const renderedCell = cellRenderer(cellData, dataKey, rowData, rowIndex, columnData) + const renderedCell = cellRenderer({ cellData, columnData, dataKey, rowData, rowIndex }) const style = this._getFlexStyleForColumn(column) diff --git a/source/FlexTable/FlexTable.test.js b/source/FlexTable/FlexTable.test.js index e045b79f9..e76ded284 100644 --- a/source/FlexTable/FlexTable.test.js +++ b/source/FlexTable/FlexTable.test.js @@ -236,7 +236,7 @@ describe('FlexTable', () => { it('should use a custom cellRenderer if specified', () => { const rendered = findDOMNode(render(getMarkup({ - cellRenderer: (cellData, dataKey, rowData, rowIndex, columnData) => `Custom ${cellData}` + cellRenderer: ({ cellData, columnData, dataKey, rowData, rowIndex }) => `Custom ${cellData}` }))) const nameColumns = rendered.querySelectorAll('.FlexTable__rowColumn:first-of-type') Array.from(nameColumns).forEach((nameColumn, index) => { @@ -247,7 +247,7 @@ describe('FlexTable', () => { it('should set the rendered cell content as the cell :title if it is a string', () => { const rendered = findDOMNode(render(getMarkup({ - cellRenderer: (cellData, dataKey, rowData, rowIndex, columnData) => 'Custom' + cellRenderer: ({ cellData, columnData, dataKey, rowData, rowIndex }) => 'Custom' }))) const nameColumn = rendered.querySelector('.FlexTable__rowColumn:first-of-type') expect(nameColumn.children[0].getAttribute('title')).toContain('Custom') @@ -255,7 +255,7 @@ describe('FlexTable', () => { it('should not set a cell :title if the rendered cell content is not a string', () => { const rendered = findDOMNode(render(getMarkup({ - cellRenderer: (cellData, dataKey, rowData, rowIndex, columnData) =>
Custom
+ cellRenderer: ({ cellData, columnData, dataKey, rowData, rowIndex }) =>
Custom
}))) const nameColumn = rendered.querySelector('.FlexTable__rowColumn:first-of-type') expect(nameColumn.children[0].getAttribute('title')).toEqual(null) diff --git a/source/FlexTable/types.js b/source/FlexTable/types.js index 08593e6ac..2dc3f496a 100644 --- a/source/FlexTable/types.js +++ b/source/FlexTable/types.js @@ -5,3 +5,11 @@ export type CellDataGetterParams = { dataKey: string, rowData: any }; + +export type CellRendererParams = { + cellData: ?any, + columnData: ?any, + dataKey: string, + rowData: any, + rowIndex: number +}; From 37a1d75f7b331a9cda58fea3a7ea5e7cd9d1e9d9 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Fri, 29 Apr 2016 15:25:28 -0700 Subject: [PATCH 23/60] Docs/comment tweak --- source/FlexTable/FlexColumn.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/FlexTable/FlexColumn.js b/source/FlexTable/FlexColumn.js index 54beef482..743e9218b 100644 --- a/source/FlexTable/FlexColumn.js +++ b/source/FlexTable/FlexColumn.js @@ -94,7 +94,7 @@ export default class Column extends Component { /** * Callback responsible for returning a cell's data, given its :dataKey - * (dataKey: string, rowData: any): any + * ({ columnData: any, dataKey: string, rowData: any }): any */ cellDataGetter: PropTypes.func, From 5152512898f8f81e4ffc9dc5f063a184356ecc79 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sat, 30 Apr 2016 11:07:02 -0700 Subject: [PATCH 24/60] Added a codemod for renaming v6 to v7 properties --- codemods/rename-properties.js | 48 +++++++++++++++++++++++++++++++++++ codemods/utils.js | 16 ++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 codemods/rename-properties.js create mode 100644 codemods/utils.js diff --git a/codemods/rename-properties.js b/codemods/rename-properties.js new file mode 100644 index 000000000..ddc158383 --- /dev/null +++ b/codemods/rename-properties.js @@ -0,0 +1,48 @@ +import { attributeBelongsToReactVirtualizedElement } from './utils' + +// Renames react-virtualized version 6.x properties to be version-7 compatible +export default function transformer(file, api) { + const jscodeshift = api.jscodeshift + + let source = file.source + + // Rename variable references + for (var property in propertyRenameMap) { + source = jscodeshift(source) + .findVariableDeclarators(property) + .renameTo(propertyRenameMap[property]) + .toSource() + } + + // Rename JSX attributes + source = jscodeshift(source) + .find(jscodeshift.JSXAttribute) + .filter(shouldAttributeBeRenamed) + .replaceWith(renameReactVirtualizedAttribute) + .toSource() + + return source +} + +// See https://github.com/bvaughn/react-virtualized/wiki/Version-7-Roadmap#clean-up-property-names +const propertyRenameMap = { + columnsCount: 'columnCount', + overscanColumnsCount: 'overscanColumnCount', + overscanRowsCount: 'overscanRowCount', + renderCell: 'cellRenderer', + renderCellRanges: 'cellRangeRenderer', + rowsCount: 'rowCount' +} + +// @param path jscodeshift.JSXAttribute +const shouldAttributeBeRenamed = path => attributeBelongsToReactVirtualizedElement(path) && isAttributeInPropertyRenameMap(path) + +// @param path jscodeshift.JSXAttribute +const isAttributeInPropertyRenameMap = path => propertyRenameMap.hasOwnProperty(path.value.name.name) + +// @param path jscodeshift.JSXAttribute +const renameReactVirtualizedAttribute = path => { + path.value.name.name = propertyRenameMap[path.value.name.name] || path.value.name.name + + return path.node +} diff --git a/codemods/utils.js b/codemods/utils.js new file mode 100644 index 000000000..1c864fa29 --- /dev/null +++ b/codemods/utils.js @@ -0,0 +1,16 @@ +const reactVirtualizedElementNames = [ + 'ArrowKeyStepper', + 'AutoSizer', + 'Collection', + 'ColumnSizer', + 'FlexTable', + 'Grid', + 'ScrollSync', + 'VirtualScroll' +] + +// @param path jscodeshift.JSXAttribute +export const attributeBelongsToReactVirtualizedElement = path => reactVirtualizedElementNames.includes(path.parent.value.name.name) + +// @param path jscodeshift.JSXIdentifier +export const isReactVirtualizedElement = path => reactVirtualizedElementNames.includes(path.value.name) From 7fcd6c21e3d7267e85766821dd9c88bcc0e8db7c Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Mon, 2 May 2016 09:10:40 -0700 Subject: [PATCH 25/60] Added CellSizeAndPositionManager utility for JIT calculating sizes and positions of cells. Tests have not yet been updated. --- codemods/rename-properties.js | 2 +- source/Grid/Grid.js | 183 +++++++------- .../Grid/utils/CellSizeAndPositionManager.js | 230 ++++++++++++++++++ .../utils/CellSizeAndPositionManager.test.js | 1 + ...izeAndPositionDataAndUpdateScrollOffset.js | 3 - source/Grid/utils/getNearestIndex.js | 11 - source/Grid/utils/getNearestIndex.test.js | 27 -- source/Grid/utils/getOverscanIndices.js | 1 + source/Grid/utils/getVisibleCellIndices.js | 89 ------- .../Grid/utils/getVisibleCellIndices.test.js | 43 ---- source/Grid/utils/updateScrollIndexHelper.js | 36 ++- source/VirtualScroll/VirtualScroll.example.js | 4 +- 12 files changed, 338 insertions(+), 292 deletions(-) create mode 100644 source/Grid/utils/CellSizeAndPositionManager.js create mode 100644 source/Grid/utils/CellSizeAndPositionManager.test.js delete mode 100644 source/Grid/utils/getNearestIndex.js delete mode 100644 source/Grid/utils/getNearestIndex.test.js delete mode 100644 source/Grid/utils/getVisibleCellIndices.js delete mode 100644 source/Grid/utils/getVisibleCellIndices.test.js diff --git a/codemods/rename-properties.js b/codemods/rename-properties.js index ddc158383..01abc7f24 100644 --- a/codemods/rename-properties.js +++ b/codemods/rename-properties.js @@ -1,7 +1,7 @@ import { attributeBelongsToReactVirtualizedElement } from './utils' // Renames react-virtualized version 6.x properties to be version-7 compatible -export default function transformer(file, api) { +export default function transformer (file, api) { const jscodeshift = api.jscodeshift let source = file.source diff --git a/source/Grid/Grid.js b/source/Grid/Grid.js index 9175a1455..905ec1b0d 100644 --- a/source/Grid/Grid.js +++ b/source/Grid/Grid.js @@ -2,13 +2,11 @@ import React, { Component, PropTypes } from 'react' import cn from 'classnames' import calculateSizeAndPositionDataAndUpdateScrollOffset from './utils/calculateSizeAndPositionDataAndUpdateScrollOffset' +import CellSizeAndPositionManager from './utils/CellSizeAndPositionManager' import createCallbackMemoizer from '../utils/createCallbackMemoizer' -import getNearestIndex from './utils/getNearestIndex' import getOverscanIndices from './utils/getOverscanIndices' import getScrollbarSize from 'dom-helpers/util/scrollbarSize' import getUpdatedOffsetForIndex from '../utils/getUpdatedOffsetForIndex' -import getVisibleCellIndices from './utils/getVisibleCellIndices' -import initCellMetadata from '../utils/initCellMetadata' import raf from 'raf' import shallowCompare from 'react-addons-shallow-compare' import updateScrollIndexHelper from './utils/updateScrollIndexHelper' @@ -45,11 +43,11 @@ export default class Grid extends Component { /** * Responsible for rendering a group of cells given their index ranges. * Should implement the following interface: ({ - * columnMetadata:Array, + * columnSizeAndPositionManager: CellSizeAndPositionManager, * columnStartIndex: number, * columnStopIndex: number, * cellRenderer: Function, - * rowMetadata:Array, + * rowSizeAndPositionManager: CellSizeAndPositionManager, * rowStartIndex: number, * rowStopIndex: number * }): Array @@ -72,6 +70,18 @@ export default class Grid extends Component { */ columnWidth: PropTypes.oneOfType([PropTypes.number, PropTypes.func]).isRequired, + /** + * Used to estimate the total width of a Grid before all of its columns have actually been measured. + * The estimated total width is adjusted as columns are rendered. + */ + estimatedColumnSize: PropTypes.number.isRequired, + + /** + * Used to estimate the total height of a Grid before all of its rows have actually been measured. + * The estimated total height is adjusted as rows are rendered. + */ + estimatedRowSize: PropTypes.number.isRequired, + /** * Height of Grid; this property determines the number of visible (vs virtualized) rows. */ @@ -142,6 +152,8 @@ export default class Grid extends Component { static defaultProps = { 'aria-label': 'grid', + estimatedColumnSize: 100, + estimatedRowSize: 30, noContentRenderer: () => null, onScroll: () => null, onSectionRendered: () => null, @@ -154,7 +166,6 @@ export default class Grid extends Component { super(props, context) this.state = { - computeGridMetadataOnNextUpdate: false, isScrolling: false, scrollLeft: 0, scrollTop: 0 @@ -165,12 +176,24 @@ export default class Grid extends Component { this._onScrollMemoizer = createCallbackMemoizer(false) // Bind functions to instance so they don't lose context when passed around - this._computeColumnMetadata = this._computeColumnMetadata.bind(this) - this._computeRowMetadata = this._computeRowMetadata.bind(this) this._invokeOnGridRenderedHelper = this._invokeOnGridRenderedHelper.bind(this) this._onScroll = this._onScroll.bind(this) this._updateScrollLeftForScrollToColumn = this._updateScrollLeftForScrollToColumn.bind(this) this._updateScrollTopForScrollToRow = this._updateScrollTopForScrollToRow.bind(this) + + this._columnWidthGetter = this._wrapSizeGetter(props.columnWidth) + this._rowHeightGetter = this._wrapSizeGetter(props.rowHeight) + + this._columnSizeAndPositionManager = new CellSizeAndPositionManager({ + cellCount: props.columnCount, + cellSizeGetter: (index) => this._columnWidthGetter(index), + estimatedCellSize: this._getEstimatedColumnSize(props) + }) + this._rowSizeAndPositionManager = new CellSizeAndPositionManager({ + cellCount: props.rowCount, + cellSizeGetter: (index) => this._rowHeightGetter(index), + estimatedCellSize: this._getEstimatedRowSize(props) + }) } /** @@ -179,9 +202,8 @@ export default class Grid extends Component { * Since Grid only receives :columnCount and :rowCount it has no way of detecting when the underlying data changes. */ recomputeGridSize () { - this.setState({ - computeGridMetadataOnNextUpdate: true - }) + this._columnSizeAndPositionManager.resetCell(0) + this._rowSizeAndPositionManager.resetCell(0) } componentDidMount () { @@ -205,8 +227,8 @@ export default class Grid extends Component { this._invokeOnScrollMemoizer({ scrollLeft: scrollLeft || 0, scrollTop: scrollTop || 0, - totalColumnsWidth: this._getTotalColumnsWidth(), - totalRowsHeight: this._getTotalRowsHeight() + totalColumnsWidth: this._columnSizeAndPositionManager.getTotalSize(), + totalRowsHeight: this._rowSizeAndPositionManager.getTotalSize() }) } @@ -216,7 +238,7 @@ export default class Grid extends Component { * 1) New scroll-to-cell props have been set */ componentDidUpdate (prevProps, prevState) { - const { columnCount, columnWidth, height, rowHeight, rowCount, scrollToColumn, scrollToRow, width } = this.props + const { height, scrollToColumn, scrollToRow, width } = this.props const { scrollLeft, scrollPositionChangeReason, scrollTop } = this.state // Make sure requested changes to :scrollLeft or :scrollTop get applied. @@ -244,9 +266,7 @@ export default class Grid extends Component { // Update scroll offsets if the current :scrollToColumn or :scrollToRow values requires it // @TODO Do we also need this check or can the one in componentWillUpdate() suffice? updateScrollIndexHelper({ - cellCount: columnCount, - cellMetadata: this._columnMetadata, - cellSize: columnWidth, + cellSizeAndPositionManager: this._columnSizeAndPositionManager, previousCellsCount: prevProps.columnCount, previousCellSize: prevProps.columnWidth, previousScrollToIndex: prevProps.scrollToColumn, @@ -257,9 +277,7 @@ export default class Grid extends Component { updateScrollIndexCallback: (scrollToColumn) => this._updateScrollLeftForScrollToColumn({ ...this.props, scrollToColumn }) }) updateScrollIndexHelper({ - cellCount: rowCount, - cellMetadata: this._rowMetadata, - cellSize: rowHeight, + cellSizeAndPositionManager: this._rowSizeAndPositionManager, previousCellsCount: prevProps.rowCount, previousCellSize: prevProps.rowHeight, previousScrollToIndex: prevProps.scrollToRow, @@ -274,11 +292,6 @@ export default class Grid extends Component { this._invokeOnGridRenderedHelper() } - componentWillMount () { - this._computeColumnMetadata(this.props) - this._computeRowMetadata(this.props) - } - componentWillUnmount () { if (this._disablePointerEventsTimeoutId) { clearTimeout(this._disablePointerEventsTimeoutId) @@ -317,13 +330,24 @@ export default class Grid extends Component { }) } + this._columnWidthGetter = this._wrapSizeGetter(nextProps.columnWidth) + this._rowHeightGetter = this._wrapSizeGetter(nextProps.rowHeight) + + this._columnSizeAndPositionManager.configure({ + cellCount: nextProps.columnCount, + estimatedCellSize: this._getEstimatedColumnSize(nextProps) + }) + this._rowSizeAndPositionManager.configure({ + cellCount: nextProps.rowCount, + estimatedCellSize: this._getEstimatedRowSize(nextProps) + }) + // Update scroll offsets if the size or number of cells have changed, invalidating the previous value calculateSizeAndPositionDataAndUpdateScrollOffset({ cellCount: this.props.columnCount, cellSize: this.props.columnWidth, - computeMetadataCallback: this._computeColumnMetadata, + computeMetadataCallback: () => this._columnSizeAndPositionManager.resetCell(0), computeMetadataCallbackProps: nextProps, - computeMetadataOnNextUpdate: nextState.computeGridMetadataOnNextUpdate, nextCellsCount: nextProps.columnCount, nextCellSize: nextProps.columnWidth, nextScrollToIndex: nextProps.scrollToColumn, @@ -333,19 +357,14 @@ export default class Grid extends Component { calculateSizeAndPositionDataAndUpdateScrollOffset({ cellCount: this.props.rowCount, cellSize: this.props.rowHeight, - computeMetadataCallback: this._computeRowMetadata, + computeMetadataCallback: () => this._rowSizeAndPositionManager.resetCell(0), computeMetadataCallbackProps: nextProps, - computeMetadataOnNextUpdate: nextState.computeGridMetadataOnNextUpdate, nextCellsCount: nextProps.rowCount, nextCellSize: nextProps.rowHeight, nextScrollToIndex: nextProps.scrollToRow, scrollToIndex: this.props.scrollToRow, updateScrollOffsetForScrollToIndex: () => this._updateScrollTopForScrollToRow(nextProps, nextState) }) - - this.setState({ - computeGridMetadataOnNextUpdate: false - }) } render () { @@ -372,16 +391,14 @@ export default class Grid extends Component { // Render only enough columns and rows to cover the visible area of the grid. if (height > 0 && width > 0) { - const visibleColumnIndices = getVisibleCellIndices({ - cellMetadata: this._columnMetadata, + const visibleColumnIndices = this._columnSizeAndPositionManager.getVisibleCellRange({ containerSize: width, - currentOffset: scrollLeft + offset: scrollLeft }) - const visibleRowIndices = getVisibleCellIndices({ - cellMetadata: this._rowMetadata, + const visibleRowIndices = this._rowSizeAndPositionManager.getVisibleCellRange({ containerSize: height, - currentOffset: scrollTop + offset: scrollTop }) // Store for _invokeOnGridRenderedHelper() @@ -412,10 +429,10 @@ export default class Grid extends Component { childrenToDisplay = cellRangeRenderer({ cellRenderer, - columnMetadata: this._columnMetadata, + columnSizeAndPositionManager: this._columnSizeAndPositionManager, columnStartIndex: this._columnStartIndex, columnStopIndex: this._columnStopIndex, - rowMetadata: this._rowMetadata, + rowSizeAndPositionManager: this._rowSizeAndPositionManager, rowStartIndex: this._rowStartIndex, rowStopIndex: this._rowStopIndex }) @@ -426,8 +443,8 @@ export default class Grid extends Component { width: width } - const totalColumnsWidth = this._getTotalColumnsWidth() - const totalRowsHeight = this._getTotalRowsHeight() + const totalColumnsWidth = this._columnSizeAndPositionManager.getTotalSize() + const totalRowsHeight = this._rowSizeAndPositionManager.getTotalSize() // Force browser to hide scrollbars when we know they aren't necessary. // Otherwise once scrollbars appear they may not disappear again. @@ -477,24 +494,6 @@ export default class Grid extends Component { /* ---------------------------- Helper methods ---------------------------- */ - _computeColumnMetadata (props) { - const { columnCount, columnWidth } = props - - this._columnMetadata = initCellMetadata({ - cellCount: columnCount, - size: columnWidth - }) - } - - _computeRowMetadata (props) { - const { rowHeight, rowCount } = props - - this._rowMetadata = initCellMetadata({ - cellCount: rowCount, - size: rowHeight - }) - } - /** * Sets an :isScrolling flag for a small window of time. * This flag is used to disable pointer events on the scrollable portion of the Grid. @@ -513,22 +512,16 @@ export default class Grid extends Component { }, IS_SCROLLING_TIMEOUT) } - _getTotalColumnsWidth () { - if (this._columnMetadata.length === 0) { - return 0 - } - - const datum = this._columnMetadata[this._columnMetadata.length - 1] - return datum.offset + datum.size + _getEstimatedColumnSize (props) { + return typeof props.columnWidth === 'number' + ? props.columnWidth + : props.estimatedColumnSize } - _getTotalRowsHeight () { - if (this._rowMetadata.length === 0) { - return 0 - } - - const datum = this._rowMetadata[this._rowMetadata.length - 1] - return datum.offset + datum.size + _getEstimatedRowSize (props) { + return typeof props.rowWidth === 'number' + ? props.rowWidth + : props.estimatedRowSize } _invokeOnGridRenderedHelper () { @@ -607,21 +600,24 @@ export default class Grid extends Component { } } + _wrapSizeGetter (size) { + return typeof size === 'function' + ? size + : () => size + } + _updateScrollLeftForScrollToColumn (props = null, state = null) { const { columnCount, scrollToColumn, width } = props || this.props const { scrollLeft } = state || this.state if (scrollToColumn >= 0 && columnCount > 0) { - const targetIndex = getNearestIndex({ - cellCount: this._columnMetadata.length, - targetIndex: scrollToColumn - }) + const targetIndex = Math.max(0, Math.min(columnCount - 1, scrollToColumn)) - const columnMetadata = this._columnMetadata[targetIndex] + const columnMetadatum = this._columnSizeAndPositionManager.getSizeAndPositionOfCell(targetIndex) const calculatedScrollLeft = getUpdatedOffsetForIndex({ - cellOffset: columnMetadata.offset, - cellSize: columnMetadata.size, + cellOffset: columnMetadatum.offset, + cellSize: columnMetadatum.size, containerSize: width, currentOffset: scrollLeft, targetIndex: scrollToColumn @@ -640,16 +636,13 @@ export default class Grid extends Component { const { scrollTop } = state || this.state if (scrollToRow >= 0 && rowCount > 0) { - const targetIndex = getNearestIndex({ - cellCount: this._rowMetadata.length, - targetIndex: scrollToRow - }) + const targetIndex = Math.max(0, Math.min(rowCount - 1, scrollToRow)) - const rowMetadata = this._rowMetadata[targetIndex] + const rowMetadatum = this._rowSizeAndPositionManager.getSizeAndPositionOfCell(targetIndex) const calculatedScrollTop = getUpdatedOffsetForIndex({ - cellOffset: rowMetadata.offset, - cellSize: rowMetadata.size, + cellOffset: rowMetadatum.offset, + cellSize: rowMetadatum.size, containerSize: height, currentOffset: scrollTop, targetIndex: scrollToRow @@ -680,8 +673,8 @@ export default class Grid extends Component { // We can avoid that by doing some simple bounds checking to ensure that scrollTop never exceeds the total height. const { height, width } = this.props const scrollbarSize = this._scrollbarSize - const totalRowsHeight = this._getTotalRowsHeight() - const totalColumnsWidth = this._getTotalColumnsWidth() + const totalRowsHeight = this._rowSizeAndPositionManager.getTotalSize() + const totalColumnsWidth = this._columnSizeAndPositionManager.getTotalSize() const scrollLeft = Math.min(totalColumnsWidth - width + scrollbarSize, event.target.scrollLeft) const scrollTop = Math.min(totalRowsHeight - height + scrollbarSize, event.target.scrollTop) @@ -721,20 +714,20 @@ export default class Grid extends Component { function defaultRenderCellRanges ({ cellRenderer, - columnMetadata, + columnSizeAndPositionManager, columnStartIndex, columnStopIndex, - rowMetadata, + rowSizeAndPositionManager, rowStartIndex, rowStopIndex }) { const renderedCells = [] for (let rowIndex = rowStartIndex; rowIndex <= rowStopIndex; rowIndex++) { - let rowDatum = rowMetadata[rowIndex] + let rowDatum = rowSizeAndPositionManager.getSizeAndPositionOfCell(rowIndex) for (let columnIndex = columnStartIndex; columnIndex <= columnStopIndex; columnIndex++) { - let columnDatum = columnMetadata[columnIndex] + let columnDatum = columnSizeAndPositionManager.getSizeAndPositionOfCell(columnIndex) let renderedCell = cellRenderer({ columnIndex, rowIndex }) let key = `${rowIndex}-${columnIndex}` diff --git a/source/Grid/utils/CellSizeAndPositionManager.js b/source/Grid/utils/CellSizeAndPositionManager.js new file mode 100644 index 000000000..ea2c502f4 --- /dev/null +++ b/source/Grid/utils/CellSizeAndPositionManager.js @@ -0,0 +1,230 @@ +/** @flow */ + +/** + * Just-in-time calculates and caches size and position information for a collection of cells. + */ +export default class CellSizeAndPositionManager { + constructor ({ + cellCount, + cellSizeGetter, + estimatedCellSize + }: CellSizeAndPositionManagerConstructorParams) { + this._cellSizeGetter = cellSizeGetter + this._cellCount = cellCount + this._estimatedCellSize = estimatedCellSize + + // Cache of size and position data for cells, mapped by cell index. + // Note that invalid values may exist in this map so only rely on cells up to this._lastMeasuredIndex + this._cellSizeAndPositionData = {} + + // Measurements for cells up to this index can be trusted; cells afterward should be estimated. + this._lastMeasuredIndex = -1 + } + + configure ({ + cellCount, + estimatedCellSize + }: ConfigureParams): void { + this._cellCount = cellCount + this._estimatedCellSize = estimatedCellSize + } + + /** + * Searches for the cell (index) nearest the specified offset. + */ + findNearestCell (offset: number): number { + if (Number.isNaN(offset)) { + throw Error(`Invalid offset ${offset} specified`) + } + + const lastMeasuredCellSizeAndPosition = this.getSizeAndPositionOfLastMeasuredCell() + const lastMeasuredIndex = Math.max(0, this._lastMeasuredIndex) + + if (lastMeasuredCellSizeAndPosition.offset >= offset) { + // If we've already measured cells within this range just use a binary search as it's faster. + return this._binarySearch({ + high: lastMeasuredIndex, + low: 0, + offset + }) + } else { + // If we haven't yet measured this high, fallback to an exponential search with an inner binary search. + // The exponential search avoids pre-computing sizes for the full set of cells as a binary search would. + // The overall complexity for this approach is O(log n). + return this._exponentialSearch({ + index: lastMeasuredIndex, + offset + }) + } + } + + getCellCount (): number { + return this._cellCount + } + + /** + * This method returns the size and position for the cell at the specified index. + * It just-in-time calculates (or used cached values) for cells leading up to the index. + */ + getSizeAndPositionOfCell (index: number): SizeAndPositionData { + if (index >= this._cellCount) { + throw Error(`Requested index ${index} is outside of range 0..${this._cellCount}`) + } + + if (index > this._lastMeasuredIndex) { + let lastMeasuredCellSizeAndPosition = this.getSizeAndPositionOfLastMeasuredCell() + let offset = lastMeasuredCellSizeAndPosition.offset + lastMeasuredCellSizeAndPosition.size + + for (var i = this._lastMeasuredIndex + 1; i <= index; i++) { + let size = this._cellSizeGetter({ index: i }) + + if (size == null || isNaN(size)) { + throw Error(`Invalid size returned for cell ${i} of value ${size}`) + } + + this._cellSizeAndPositionData[i] = { + offset, + size + } + + offset += size + } + + this._lastMeasuredIndex = index + } + + return this._cellSizeAndPositionData[index] + } + + getSizeAndPositionOfLastMeasuredCell (): SizeAndPositionData { + return this._lastMeasuredIndex >= 0 + ? this._cellSizeAndPositionData[this._lastMeasuredIndex] + : { + offset: 0, + size: 0 + } + } + + /** + * Total size of all cells being measured. + * This value will be completedly estimated initially. + * As cells as measured the estimate will be updated. + */ + getTotalSize (): number { + const lastMeasuredCellSizeAndPosition = this.getSizeAndPositionOfLastMeasuredCell() + + return lastMeasuredCellSizeAndPosition.offset + lastMeasuredCellSizeAndPosition.size + (this._cellCount - this._lastMeasuredIndex - 1) * this._estimatedCellSize + } + + getVisibleCellRange ({ + containerSize, + offset + }: GetVisibleCellRangeParams): ?VisibleCellRange { + const totalSize = this.getTotalSize() + + if (totalSize === 0) { + return null + } + + const maxOffset = offset + containerSize + const start = this.findNearestCell(offset) + + const datum = this.getSizeAndPositionOfCell(start) + offset = datum.offset + datum.size + + let stop = start + + while (offset < maxOffset && stop < this._cellCount - 1) { + stop++ + + offset += this.getSizeAndPositionOfCell(stop).size + } + + return { + start, + stop + } + } + + /** + * Clear all cached values for cells after the specified index. + * This method should be called for any cell that has changed its size. + * It will not immediately perform any calculations; they'll be performed the next time getSizeAndPositionOfCell() is called. + */ + resetCell (index: number): void { + this._lastMeasuredIndex = index - 1 + } + + _binarySearch ({ + high, + low, + offset + }) { + let middle + let currentOffset + + while (low <= high) { + middle = low + Math.floor((high - low) / 2) + currentOffset = this.getSizeAndPositionOfCell(middle).offset + + if (currentOffset === offset) { + return middle + } else if (currentOffset < offset) { + low = middle + 1 + } else if (currentOffset > offset) { + high = middle - 1 + } + } + + if (low > 0) { + return low - 1 + } + } + + _exponentialSearch ({ + index, + offset + }) { + let interval = 1 + + while ( + index < this._cellCount && + this.getSizeAndPositionOfCell(index).offset < offset + ) { + index += interval + interval *= 2 + } + + return this._binarySearch({ + high: Math.min(index, this._cellCount - 1), + low: Math.floor(index / 2), + offset + }) + } +} + +type CellSizeAndPositionManagerConstructorParams = { + cellCount: number, + cellSizeGetter: Function, + estimatedCellSize: number +}; + +type ConfigureParams = { + cellCount: number, + estimatedCellSize: number +}; + +type GetVisibleCellRangeParams = { + containerSize: number, + offset: number +}; + +type SizeAndPositionData = { + offset: number, + size: number +}; + +type VisibleCellRange = { + start: number, + stop: number +}; diff --git a/source/Grid/utils/CellSizeAndPositionManager.test.js b/source/Grid/utils/CellSizeAndPositionManager.test.js new file mode 100644 index 000000000..65fe19d2d --- /dev/null +++ b/source/Grid/utils/CellSizeAndPositionManager.test.js @@ -0,0 +1 @@ +// @TODO CellSizeAndPositionManager diff --git a/source/Grid/utils/calculateSizeAndPositionDataAndUpdateScrollOffset.js b/source/Grid/utils/calculateSizeAndPositionDataAndUpdateScrollOffset.js index 627e0faa7..a2f734b70 100644 --- a/source/Grid/utils/calculateSizeAndPositionDataAndUpdateScrollOffset.js +++ b/source/Grid/utils/calculateSizeAndPositionDataAndUpdateScrollOffset.js @@ -5,7 +5,6 @@ * @param cellsSize Width or height of cells for the current axis * @param computeMetadataCallback Method to invoke if cell metadata should be recalculated * @param computeMetadataCallbackProps Parameters to pass to :computeMetadataCallback - * @param computeMetadataOnNextUpdate Flag specifying that metadata should be recalculated * @param nextCellsCount Newly updated number of rows or columns in the current axis * @param nextCellsSize Newly updated width or height of cells for the current axis * @param nextScrollToIndex Newly updated scroll-to-index @@ -17,7 +16,6 @@ export default function calculateSizeAndPositionDataAndUpdateScrollOffset ({ cellSize, computeMetadataCallback, computeMetadataCallbackProps, - computeMetadataOnNextUpdate, nextCellsCount, nextCellSize, nextScrollToIndex, @@ -27,7 +25,6 @@ export default function calculateSizeAndPositionDataAndUpdateScrollOffset ({ // Don't compare cell sizes if they are functions because inline functions would cause infinite loops. // In that event users should use the manual recompute methods to inform of changes. if ( - computeMetadataOnNextUpdate || cellCount !== nextCellsCount || ( ( diff --git a/source/Grid/utils/getNearestIndex.js b/source/Grid/utils/getNearestIndex.js deleted file mode 100644 index 13d890ca9..000000000 --- a/source/Grid/utils/getNearestIndex.js +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Finds the nearest valid index to the one specified if the specified index is invalid. - * @param cellCount Number of rows or columns in the current axis - * @param targetIndex Index to use if possible - */ -export default function getNearestIndex ({ - cellCount, - targetIndex -}) { - return Math.max(0, Math.min(cellCount - 1, targetIndex)) -} diff --git a/source/Grid/utils/getNearestIndex.test.js b/source/Grid/utils/getNearestIndex.test.js deleted file mode 100644 index 1aaf6ca78..000000000 --- a/source/Grid/utils/getNearestIndex.test.js +++ /dev/null @@ -1,27 +0,0 @@ -import getNearestIndex from './getNearestIndex' - -describe('getNearestIndex', () => { - it('should return the specified index if target is within range', () => { - const indices = [0, 5, 9] - indices.forEach((targetIndex) => { - expect(getNearestIndex({ - cellCount: 10, - targetIndex - })).toEqual(targetIndex) - }) - }) - - it('should return the first index if target is too low', () => { - expect(getNearestIndex({ - cellCount: 10, - targetIndex: -1 - })).toEqual(0) - }) - - it('should return the last index if target is too high', () => { - expect(getNearestIndex({ - cellCount: 10, - targetIndex: 11 - })).toEqual(9) - }) -}) diff --git a/source/Grid/utils/getOverscanIndices.js b/source/Grid/utils/getOverscanIndices.js index 733a90694..043b7f308 100644 --- a/source/Grid/utils/getOverscanIndices.js +++ b/source/Grid/utils/getOverscanIndices.js @@ -1,6 +1,7 @@ /** * Calculates the number of cells to overscan before and after a specified range. * This function ensures that overscanning doesn't exceed the available cells. + * * @param cellCount Number of rows or columns in the current axis * @param overscanCellsCount Maximum number of cells to over-render in either direction * @param startIndex Begin of range of visible cells diff --git a/source/Grid/utils/getVisibleCellIndices.js b/source/Grid/utils/getVisibleCellIndices.js deleted file mode 100644 index 7e63764ea..000000000 --- a/source/Grid/utils/getVisibleCellIndices.js +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Determines the range of cells to display for a given offset in order to fill the specified container. - * - * @param cellMetadata Metadata initially computed by initCellMetadata() - * @param containerSize Total size (width or height) of the container - * @param currentOffset Container's current (x or y) offset - * @return An object containing :start and :stop attributes, each specifying a cell index - */ -export default function getVisibleCellIndices ({ - cellMetadata, - containerSize, - currentOffset -}) { - const cellCount = cellMetadata.length - - if (cellCount === 0) { - return {} - } - - // TODO Add better guards here against NaN offset - - const lastDatum = cellMetadata[cellMetadata.length - 1] - const totalCellSize = lastDatum.offset + lastDatum.size - - // Ensure offset is within reasonable bounds - currentOffset = Math.max(0, Math.min(totalCellSize - containerSize, currentOffset)) - - const maxOffset = Math.min(totalCellSize, currentOffset + containerSize) - - let start = findNearestCell({ - cellMetadata, - mode: EQUAL_OR_LOWER, - offset: currentOffset - }) - - let datum = cellMetadata[start] - currentOffset = datum.offset + datum.size - - let stop = start - - while (currentOffset < maxOffset && stop < cellCount - 1) { - stop++ - - currentOffset += cellMetadata[stop].size - } - - return { - start, - stop - } -} - -/** - * Binary search function inspired by react-infinite. - */ -function findNearestCell ({ - cellMetadata, - mode, - offset -}) { - let high = cellMetadata.length - 1 - let low = 0 - let middle - let currentOffset - - // TODO Add better guards here against NaN offset - - while (low <= high) { - middle = low + Math.floor((high - low) / 2) - currentOffset = cellMetadata[middle].offset - - if (currentOffset === offset) { - return middle - } else if (currentOffset < offset) { - low = middle + 1 - } else if (currentOffset > offset) { - high = middle - 1 - } - } - - if (mode === EQUAL_OR_LOWER && low > 0) { - return low - 1 - } else if (mode === EQUAL_OR_HIGHER && high < cellMetadata.length - 1) { - return high + 1 - } -} - -const EQUAL_OR_LOWER = 1 -const EQUAL_OR_HIGHER = 2 diff --git a/source/Grid/utils/getVisibleCellIndices.test.js b/source/Grid/utils/getVisibleCellIndices.test.js deleted file mode 100644 index 53c1337d7..000000000 --- a/source/Grid/utils/getVisibleCellIndices.test.js +++ /dev/null @@ -1,43 +0,0 @@ -import getVisibleCellIndices from './getVisibleCellIndices' -import { getCellMetadata } from '../../utils/TestHelper' - -describe('getVisibleCellIndices', () => { - function testHelper (currentOffset, cellMetadata = getCellMetadata()) { - return getVisibleCellIndices({ - cellMetadata, - containerSize: 50, - currentOffset - }) - } - - it('should handle unscrolled', () => { - const { start, stop } = testHelper(0) - expect(start).toEqual(0) - expect(stop).toEqual(3) - }) - - it('should handle scrolled to the middle', () => { - const { start, stop } = testHelper(50) - expect(start).toEqual(3) - expect(stop).toEqual(5) - }) - - it('should handle scrolled to the end', () => { - const { start, stop } = testHelper(110) - expect(start).toEqual(6) - expect(stop).toEqual(8) - }) - - it('should handle scrolled past the end', () => { - const { start, stop } = testHelper(200) - expect(start).toEqual(6) - expect(stop).toEqual(8) - }) - - it('should handle scrolled past the beginning', () => { - const { start, stop } = testHelper(-50) - expect(start).toEqual(0) - expect(stop).toEqual(3) - }) -}) - diff --git a/source/Grid/utils/updateScrollIndexHelper.js b/source/Grid/utils/updateScrollIndexHelper.js index ffc23c6d8..96ef8ade2 100644 --- a/source/Grid/utils/updateScrollIndexHelper.js +++ b/source/Grid/utils/updateScrollIndexHelper.js @@ -1,12 +1,11 @@ -import getNearestIndex from './getNearestIndex' import getUpdatedOffsetForIndex from '../../utils/getUpdatedOffsetForIndex' /** * Helper function that determines when to update scroll offsets to ensure that a scroll-to-index remains visible. + * This function also ensures that the scroll ofset isn't past the last column/row of cells. * - * @param cellMetadata Metadata initially computed by initCellMetadata() - * @param cellCount Number of rows or columns in the current axis * @param cellsSize Width or height of cells for the current axis + * @param cellSizeAndPositionManager Manages size and position metadata of cells * @param previousCellsCount Previous number of rows or columns * @param previousCellsSize Previous width or height of cells * @param previousScrollToIndex Previous scroll-to-index @@ -17,9 +16,8 @@ import getUpdatedOffsetForIndex from '../../utils/getUpdatedOffsetForIndex' * @param updateScrollIndexCallback Callback to invoke with an scroll-to-index value */ export default function updateScrollIndexHelper ({ - cellMetadata, - cellCount, cellSize, + cellSizeAndPositionManager, previousCellsCount, previousCellSize, previousScrollToIndex, @@ -29,6 +27,7 @@ export default function updateScrollIndexHelper ({ size, updateScrollIndexCallback }) { + const cellCount = cellSizeAndPositionManager.getCellCount() const hasScrollToIndex = scrollToIndex >= 0 && scrollToIndex < cellCount const sizeHasChanged = ( size !== previousSize || @@ -60,24 +59,19 @@ export default function updateScrollIndexHelper ({ cellCount < previousCellsCount ) ) { - scrollToIndex = getNearestIndex({ - cellCount, - targetIndex: cellCount - 1 - }) + scrollToIndex = cellCount - 1 - if (scrollToIndex < cellCount) { - const cellMetadatum = cellMetadata[scrollToIndex] - const calculatedScrollOffset = getUpdatedOffsetForIndex({ - cellOffset: cellMetadatum.offset, - cellSize: cellMetadatum.size, - containerSize: size, - currentOffset: scrollOffset - }) + const cellMetadatum = cellSizeAndPositionManager.getSizeAndPositionOfCell(scrollToIndex) + const calculatedScrollOffset = getUpdatedOffsetForIndex({ + cellOffset: cellMetadatum.offset, + cellSize: cellMetadatum.size, + containerSize: size, + currentOffset: scrollOffset + }) - // Only adjust the scroll position if we've scrolled below the last set of rows. - if (calculatedScrollOffset < scrollOffset) { - updateScrollIndexCallback(cellCount - 1) - } + // Only adjust the scroll position if we've scrolled below the last set of rows. + if (calculatedScrollOffset < scrollOffset) { + updateScrollIndexCallback(cellCount - 1) } } } diff --git a/source/VirtualScroll/VirtualScroll.example.js b/source/VirtualScroll/VirtualScroll.example.js index 1957140f1..44b4e9299 100644 --- a/source/VirtualScroll/VirtualScroll.example.js +++ b/source/VirtualScroll/VirtualScroll.example.js @@ -19,12 +19,12 @@ export default class VirtualScrollExample extends Component { super(props) this.state = { - overscanRowCount: 5, + overscanRowCount: 0, rowCount: props.list.size, scrollToIndex: undefined, useDynamicRowHeight: false, virtualScrollHeight: 300, - virtualScrollRowHeight: 60 + virtualScrollRowHeight: 50 } this._getRowHeight = this._getRowHeight.bind(this) From 9d2386027bde0706d8b9c802cf7393d3fe13c067 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Mon, 2 May 2016 15:06:38 -0700 Subject: [PATCH 26/60] Added tests for CellSizeAndPositionManager --- .../Grid/utils/CellSizeAndPositionManager.js | 13 +- .../utils/CellSizeAndPositionManager.test.js | 214 +++++++++++++++++- 2 files changed, 225 insertions(+), 2 deletions(-) diff --git a/source/Grid/utils/CellSizeAndPositionManager.js b/source/Grid/utils/CellSizeAndPositionManager.js index ea2c502f4..350ae7d50 100644 --- a/source/Grid/utils/CellSizeAndPositionManager.js +++ b/source/Grid/utils/CellSizeAndPositionManager.js @@ -31,6 +31,9 @@ export default class CellSizeAndPositionManager { /** * Searches for the cell (index) nearest the specified offset. + * + * If no exact match is found the next lowest cell index will be returned. + * This allows partially visible cells (with offsets just before/above the fold) to be visible. */ findNearestCell (offset: number): number { if (Number.isNaN(offset)) { @@ -62,12 +65,20 @@ export default class CellSizeAndPositionManager { return this._cellCount } + getEstimatedCellSize (): number { + return this._estimatedCellSize + } + + getLastMeasuredIndex (): number { + return this._lastMeasuredIndex + } + /** * This method returns the size and position for the cell at the specified index. * It just-in-time calculates (or used cached values) for cells leading up to the index. */ getSizeAndPositionOfCell (index: number): SizeAndPositionData { - if (index >= this._cellCount) { + if (index < 0 || index >= this._cellCount) { throw Error(`Requested index ${index} is outside of range 0..${this._cellCount}`) } diff --git a/source/Grid/utils/CellSizeAndPositionManager.test.js b/source/Grid/utils/CellSizeAndPositionManager.test.js index 65fe19d2d..69298636a 100644 --- a/source/Grid/utils/CellSizeAndPositionManager.test.js +++ b/source/Grid/utils/CellSizeAndPositionManager.test.js @@ -1 +1,213 @@ -// @TODO CellSizeAndPositionManager +import CellSizeAndPositionManager from './CellSizeAndPositionManager' + +describe('CellSizeAndPositionManager', () => { + function getCellSizeAndPositionManager ({ + cellCount = 100, + estimatedCellSize = 15 + } = {}) { + const cellSizeGetterCalls = [] + const cellSizeAndPositionManager = new CellSizeAndPositionManager({ + cellCount, + cellSizeGetter: ({ index }) => { + cellSizeGetterCalls.push(index) + return 10 + }, + estimatedCellSize + }) + + return { + cellSizeAndPositionManager, + cellSizeGetterCalls + } + } + + describe('configure', () => { + it('should update inner :cellCount and :estimatedCellSize', () => { + const { cellSizeAndPositionManager } = getCellSizeAndPositionManager() + expect(cellSizeAndPositionManager.getCellCount()).toEqual(100) + expect(cellSizeAndPositionManager.getEstimatedCellSize()).toEqual(15) + + cellSizeAndPositionManager.configure({ + cellCount: 20, + estimatedCellSize: 30 + }) + expect(cellSizeAndPositionManager.getCellCount()).toEqual(20) + expect(cellSizeAndPositionManager.getEstimatedCellSize()).toEqual(30) + }) + }) + + describe('findNearestCell', () => { + it('should error if given NaN', () => { + const { cellSizeAndPositionManager } = getCellSizeAndPositionManager() + expect(() => cellSizeAndPositionManager.findNearestCell(NaN)).toThrow() + }) + + // @TODO Should I test bounds outside of the total size? + + it('should find the first cell', () => { + const { cellSizeAndPositionManager } = getCellSizeAndPositionManager() + expect(cellSizeAndPositionManager.findNearestCell(0)).toEqual(0) + expect(cellSizeAndPositionManager.findNearestCell(9)).toEqual(0) + }) + + it('should find the last cell', () => { + const { cellSizeAndPositionManager } = getCellSizeAndPositionManager() + expect(cellSizeAndPositionManager.findNearestCell(990)).toEqual(99) + expect(cellSizeAndPositionManager.findNearestCell(991)).toEqual(99) + }) + + it('should find the a cell that exactly matches a specified offset in the middle', () => { + const { cellSizeAndPositionManager } = getCellSizeAndPositionManager() + expect(cellSizeAndPositionManager.findNearestCell(100)).toEqual(10) + }) + + it('should find the cell closest to (but before) the specified offset in the middle', () => { + const { cellSizeAndPositionManager } = getCellSizeAndPositionManager() + expect(cellSizeAndPositionManager.findNearestCell(101)).toEqual(10) + }) + }) + + describe('getSizeAndPositionOfCell', () => { + it('should error if an invalid index is specified', () => { + const { cellSizeAndPositionManager } = getCellSizeAndPositionManager() + expect(() => cellSizeAndPositionManager.getSizeAndPositionOfCell(-1)).toThrow() + expect(() => cellSizeAndPositionManager.getSizeAndPositionOfCell(100)).toThrow() + }) + + it('should returnt he correct size and position information for the requested cell', () => { + const { cellSizeAndPositionManager } = getCellSizeAndPositionManager() + expect(cellSizeAndPositionManager.getSizeAndPositionOfCell(0).offset).toEqual(0) + expect(cellSizeAndPositionManager.getSizeAndPositionOfCell(0).size).toEqual(10) + expect(cellSizeAndPositionManager.getSizeAndPositionOfCell(1).offset).toEqual(10) + expect(cellSizeAndPositionManager.getSizeAndPositionOfCell(2).offset).toEqual(20) + }) + + it('should only measure the necessary cells to return the information requested', () => { + const { cellSizeAndPositionManager, cellSizeGetterCalls } = getCellSizeAndPositionManager() + cellSizeAndPositionManager.getSizeAndPositionOfCell(0) + expect(cellSizeGetterCalls).toEqual([0]) + }) + + it('should just-in-time measure all cells up to the requested cell if no cells have yet been measured', () => { + const { cellSizeAndPositionManager, cellSizeGetterCalls } = getCellSizeAndPositionManager() + cellSizeAndPositionManager.getSizeAndPositionOfCell(5) + expect(cellSizeGetterCalls).toEqual([0, 1, 2, 3, 4, 5]) + }) + + it('should just-in-time measure cells up to the requested cell if some but not all cells have been measured', () => { + const { cellSizeAndPositionManager, cellSizeGetterCalls } = getCellSizeAndPositionManager() + cellSizeAndPositionManager.getSizeAndPositionOfCell(5) + cellSizeGetterCalls.splice(0) + cellSizeAndPositionManager.getSizeAndPositionOfCell(10) + expect(cellSizeGetterCalls).toEqual([6, 7, 8, 9, 10]) + }) + + it('should return cached size and position data if cell has already been measured', () => { + const { cellSizeAndPositionManager, cellSizeGetterCalls } = getCellSizeAndPositionManager() + cellSizeAndPositionManager.getSizeAndPositionOfCell(5) + cellSizeGetterCalls.splice(0) + cellSizeAndPositionManager.getSizeAndPositionOfCell(5) + expect(cellSizeGetterCalls).toEqual([]) + }) + }) + + describe('getSizeAndPositionOfLastMeasuredCell', () => { + it('should return an empty object if no cached cells are present', () => { + const { cellSizeAndPositionManager } = getCellSizeAndPositionManager() + expect(cellSizeAndPositionManager.getSizeAndPositionOfLastMeasuredCell()).toEqual({ + offset: 0, + size: 0 + }) + }) + + it('should return size and position data for the highest/last measured cell', () => { + const { cellSizeAndPositionManager } = getCellSizeAndPositionManager() + cellSizeAndPositionManager.getSizeAndPositionOfCell(5) + expect(cellSizeAndPositionManager.getSizeAndPositionOfLastMeasuredCell()).toEqual({ + offset: 50, + size: 10 + }) + }) + }) + + describe('getTotalSize', () => { + it('should calculate total size based purely on :estimatedCellSize if no measurements have been done', () => { + const { cellSizeAndPositionManager } = getCellSizeAndPositionManager() + expect(cellSizeAndPositionManager.getTotalSize()).toEqual(1500) + }) + + it('should calculate total size based on a mixture of actual cell sizes and :estimatedCellSize if some cells have been measured', () => { + const { cellSizeAndPositionManager } = getCellSizeAndPositionManager() + cellSizeAndPositionManager.getSizeAndPositionOfCell(49) + expect(cellSizeAndPositionManager.getTotalSize()).toEqual(1250) + }) + + it('should calculate total size based on the actual measured sizes if all cells have been measured', () => { + const { cellSizeAndPositionManager } = getCellSizeAndPositionManager() + cellSizeAndPositionManager.getSizeAndPositionOfCell(99) + expect(cellSizeAndPositionManager.getTotalSize()).toEqual(1000) + }) + }) + + describe('getVisibleCellRange', () => { + it('should return a visible range of cells for the beginning of the list', () => { + const { cellSizeAndPositionManager } = getCellSizeAndPositionManager() + const { + start, + stop + } = cellSizeAndPositionManager.getVisibleCellRange({ + containerSize: 50, + offset: 0 + }) + expect(start).toEqual(0) + expect(stop).toEqual(4) + }) + + it('should return a visible range of cells for the middle of the list where some are partially visible', () => { + const { cellSizeAndPositionManager } = getCellSizeAndPositionManager() + const { + start, + stop + } = cellSizeAndPositionManager.getVisibleCellRange({ + containerSize: 50, + offset: 425 + }) + // 42 and 47 are partially visible + expect(start).toEqual(42) + expect(stop).toEqual(47) + }) + + it('should return a visible range of cells for the end of the list', () => { + const { cellSizeAndPositionManager } = getCellSizeAndPositionManager() + const { + start, + stop + } = cellSizeAndPositionManager.getVisibleCellRange({ + containerSize: 50, + offset: 950 + }) + expect(start).toEqual(95) + expect(stop).toEqual(99) + }) + }) + + describe('resetCell', () => { + it('should clear size and position metadata for the specified index and all cells after it', () => { + const { cellSizeAndPositionManager } = getCellSizeAndPositionManager() + cellSizeAndPositionManager.getSizeAndPositionOfCell(5) + cellSizeAndPositionManager.resetCell(3) + expect(cellSizeAndPositionManager.getLastMeasuredIndex()).toEqual(2) + cellSizeAndPositionManager.resetCell(0) + expect(cellSizeAndPositionManager.getLastMeasuredIndex()).toEqual(-1) + }) + + it('should not clear size and position metadata for cells before the specified index', () => { + const { cellSizeAndPositionManager, cellSizeGetterCalls } = getCellSizeAndPositionManager() + cellSizeAndPositionManager.getSizeAndPositionOfCell(5) + cellSizeGetterCalls.splice(0) + cellSizeAndPositionManager.resetCell(3) + cellSizeAndPositionManager.getSizeAndPositionOfCell(4) + expect(cellSizeGetterCalls).toEqual([3, 4]) + }) + }) +}) From 6afe36b922442320e13e5652cd2c9d994aa34dfa Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Mon, 2 May 2016 15:45:29 -0700 Subject: [PATCH 27/60] fixed test --- source/FlexTable/FlexTable.test.js | 8 ++++- source/Grid/Grid.js | 4 +-- source/Grid/Grid.test.js | 28 ++++++++++++++++ .../Grid/utils/CellSizeAndPositionManager.js | 8 ++--- .../utils/CellSizeAndPositionManager.test.js | 15 +++++++++ ...dPositionDataAndUpdateScrollOffset.test.js | 30 +---------------- .../utils/updateScrollIndexHelper.test.js | 33 ++++++++++++++++--- source/VirtualScroll/VirtualScroll.test.js | 11 +++++-- 8 files changed, 94 insertions(+), 43 deletions(-) diff --git a/source/FlexTable/FlexTable.test.js b/source/FlexTable/FlexTable.test.js index e76ded284..112c8f5f3 100644 --- a/source/FlexTable/FlexTable.test.js +++ b/source/FlexTable/FlexTable.test.js @@ -219,7 +219,13 @@ describe('FlexTable', () => { })) highestRowIndex = 0 component.recomputeRowHeights() - expect(highestRowIndex).toEqual(49) + // Rows won't actually be remeasured until the FlexTable is next rendered. + render(getMarkup({ + rowHeight, + rowCount: 50 + })) + // And then only the rows necessary to fill the visible region. + expect(highestRowIndex).toEqual(7) }) }) diff --git a/source/Grid/Grid.js b/source/Grid/Grid.js index 905ec1b0d..2930df272 100644 --- a/source/Grid/Grid.js +++ b/source/Grid/Grid.js @@ -519,8 +519,8 @@ export default class Grid extends Component { } _getEstimatedRowSize (props) { - return typeof props.rowWidth === 'number' - ? props.rowWidth + return typeof props.rowHeight === 'number' + ? props.rowHeight : props.estimatedRowSize } diff --git a/source/Grid/Grid.test.js b/source/Grid/Grid.test.js index 5b7947cf9..aee064f99 100644 --- a/source/Grid/Grid.test.js +++ b/source/Grid/Grid.test.js @@ -12,6 +12,8 @@ describe('Grid', () => { className, columnCount = NUM_COLUMNS, columnWidth = 50, + estimatedColumnSize, + estimatedRowSize, height = 100, noContentRenderer, onSectionRendered, @@ -41,6 +43,8 @@ describe('Grid', () => { className={className} columnCount={columnCount} columnWidth={columnWidth} + estimatedColumnSize={estimatedColumnSize} + estimatedRowSize={estimatedRowSize} height={height} noContentRenderer={noContentRenderer} onSectionRendered={onSectionRendered} @@ -588,4 +592,28 @@ describe('Grid', () => { expect(rendered.textContent).toContain('Fake content') }) }) + + describe('estimated row and column sizes', () => { + it('should not estimate sizes if actual sizes are numbers', () => { + const grid = render(getMarkup({ + columnWidth: 100, + estimatedColumnSize: 150, + estimatedRowSize: 15, + rowHeight: 20 + })) + expect(grid._getEstimatedColumnSize(grid.props)).toEqual(100) + expect(grid._getEstimatedRowSize(grid.props)).toEqual(20) + }) + + it('should estimate row and column sizes if actual sizes are functions', () => { + const grid = render(getMarkup({ + columnWidth: () => 100, + estimatedColumnSize: 150, + estimatedRowSize: 15, + rowHeight: () => 20 + })) + expect(grid._getEstimatedColumnSize(grid.props)).toEqual(150) + expect(grid._getEstimatedRowSize(grid.props)).toEqual(15) + }) + }) }) diff --git a/source/Grid/utils/CellSizeAndPositionManager.js b/source/Grid/utils/CellSizeAndPositionManager.js index 350ae7d50..fd9a81e9d 100644 --- a/source/Grid/utils/CellSizeAndPositionManager.js +++ b/source/Grid/utils/CellSizeAndPositionManager.js @@ -130,11 +130,11 @@ export default class CellSizeAndPositionManager { getVisibleCellRange ({ containerSize, offset - }: GetVisibleCellRangeParams): ?VisibleCellRange { + }: GetVisibleCellRangeParams): VisibleCellRange { const totalSize = this.getTotalSize() if (totalSize === 0) { - return null + return {} } const maxOffset = offset + containerSize @@ -236,6 +236,6 @@ type SizeAndPositionData = { }; type VisibleCellRange = { - start: number, - stop: number + start: ?number, + stop: ?number }; diff --git a/source/Grid/utils/CellSizeAndPositionManager.test.js b/source/Grid/utils/CellSizeAndPositionManager.test.js index 69298636a..38fb24418 100644 --- a/source/Grid/utils/CellSizeAndPositionManager.test.js +++ b/source/Grid/utils/CellSizeAndPositionManager.test.js @@ -150,6 +150,21 @@ describe('CellSizeAndPositionManager', () => { }) describe('getVisibleCellRange', () => { + it('should not return any indices if :cellCount is 0', () => { + const { cellSizeAndPositionManager } = getCellSizeAndPositionManager({ + cellCount: 0 + }) + const { + start, + stop + } = cellSizeAndPositionManager.getVisibleCellRange({ + containerSize: 50, + offset: 0 + }) + expect(start).toEqual(undefined) + expect(stop).toEqual(undefined) + }) + it('should return a visible range of cells for the beginning of the list', () => { const { cellSizeAndPositionManager } = getCellSizeAndPositionManager() const { diff --git a/source/Grid/utils/calculateSizeAndPositionDataAndUpdateScrollOffset.test.js b/source/Grid/utils/calculateSizeAndPositionDataAndUpdateScrollOffset.test.js index d26d217d7..0a4a02ddc 100644 --- a/source/Grid/utils/calculateSizeAndPositionDataAndUpdateScrollOffset.test.js +++ b/source/Grid/utils/calculateSizeAndPositionDataAndUpdateScrollOffset.test.js @@ -5,7 +5,6 @@ describe('calculateSizeAndPositionDataAndUpdateScrollOffset', () => { cellCount = 100, cellSize = 10, computeMetadataCallbackProps = {}, - computeMetadataOnNextUpdate = false, nextCellsCount = 100, nextCellSize = 10, nextScrollToIndex, @@ -19,7 +18,6 @@ describe('calculateSizeAndPositionDataAndUpdateScrollOffset', () => { cellSize, computeMetadataCallback: params => computeMetadataCallbackCalls.push(params), computeMetadataCallbackProps, - computeMetadataOnNextUpdate, nextCellsCount, nextCellSize, nextScrollToIndex, @@ -33,20 +31,6 @@ describe('calculateSizeAndPositionDataAndUpdateScrollOffset', () => { } } - it('should call :computeMetadataCallback if :computeMetadataOnNextUpdate is true', () => { - const { computeMetadataCallbackCalls } = helper({ computeMetadataOnNextUpdate: true }) - expect(computeMetadataCallbackCalls.length).toEqual(1) - }) - - it('should pass the specified :computeMetadataCallbackProps to :computeMetadataCallback', () => { - const params = { foo: 'bar' } - const { computeMetadataCallbackCalls } = helper({ - computeMetadataCallbackProps: params, - computeMetadataOnNextUpdate: true - }) - expect(computeMetadataCallbackCalls).toEqual([params]) - }) - it('should call :computeMetadataCallback if :cellCount has changed', () => { const { computeMetadataCallbackCalls } = helper({ cellCount: 100, @@ -71,25 +55,13 @@ describe('calculateSizeAndPositionDataAndUpdateScrollOffset', () => { expect(computeMetadataCallbackCalls.length).toEqual(0) }) - it('should call :updateScrollOffsetForScrollToIndex if metadata has changed and :scrollToIndex has not', () => { - const { updateScrollOffsetForScrollToIndexCalls } = helper({ - computeMetadataOnNextUpdate: true, - scrollToIndex: 10, - nextScrollToIndex: 10 - }) - expect(updateScrollOffsetForScrollToIndexCalls.length).toEqual(1) - }) - it('should not call :updateScrollOffsetForScrollToIndex if :scrollToIndex is not specified', () => { - const { updateScrollOffsetForScrollToIndexCalls } = helper({ - computeMetadataOnNextUpdate: true - }) + const { updateScrollOffsetForScrollToIndexCalls } = helper() expect(updateScrollOffsetForScrollToIndexCalls.length).toEqual(0) }) it('should not call :updateScrollOffsetForScrollToIndex if :scrollToIndex has also changed', () => { const { updateScrollOffsetForScrollToIndexCalls } = helper({ - computeMetadataOnNextUpdate: true, scrollToIndex: 10, nextScrollToIndex: 20 }) diff --git a/source/Grid/utils/updateScrollIndexHelper.test.js b/source/Grid/utils/updateScrollIndexHelper.test.js index 81efa889e..792f5526f 100644 --- a/source/Grid/utils/updateScrollIndexHelper.test.js +++ b/source/Grid/utils/updateScrollIndexHelper.test.js @@ -1,10 +1,34 @@ import updateScrollIndexHelper from './updateScrollIndexHelper' -import { getCellMetadata } from '../../utils/TestHelper' +import CellSizeAndPositionManager from './CellSizeAndPositionManager' + +// Default cell sizes and offsets for use in shared tests +export function getCellSizeAndPositionManager ({ + cellCount = CELL_SIZES.length, + estimatedCellSize = 10 +}) { + return new CellSizeAndPositionManager({ + cellCount, + cellSizeGetter: ({ index }) => CELL_SIZES[index % CELL_SIZES.length], + estimatedCellSize + }) +} + +const CELL_SIZES = [ + 10, // 0: 0..0 (min) + 20, // 1: 0..10 + 15, // 2: 0..30 + 10, // 3: 5..45 + 15, // 4: 20..55 + 30, // 5: 50..70 + 20, // 6: 70..100 + 10, // 7: 80..110 + 30 // 8: 110..110 (max) +] describe('updateScrollIndexHelper', () => { function helper ({ cellCount = undefined, - cellMetadata = getCellMetadata(), + cellSizeAndPositionManager, cellSize = 10, previousCellsCount = 100, previousCellSize = 10, @@ -14,8 +38,9 @@ describe('updateScrollIndexHelper', () => { scrollToIndex, size = 50 } = {}) { + cellSizeAndPositionManager = cellSizeAndPositionManager || getCellSizeAndPositionManager({ cellCount }) cellCount = cellCount === undefined - ? cellMetadata.length + ? cellSizeAndPositionManager.getCellCount() : cellCount let updateScrollIndexCallbackCalled = false @@ -26,7 +51,7 @@ describe('updateScrollIndexHelper', () => { updateScrollIndexHelper({ cellCount, - cellMetadata, + cellSizeAndPositionManager, cellSize, previousCellsCount, previousCellSize, diff --git a/source/VirtualScroll/VirtualScroll.test.js b/source/VirtualScroll/VirtualScroll.test.js index 2b17c03c3..e75081ecf 100644 --- a/source/VirtualScroll/VirtualScroll.test.js +++ b/source/VirtualScroll/VirtualScroll.test.js @@ -314,8 +314,7 @@ describe('VirtualScroll', () => { } rendered.refs.Grid.refs.scrollingContainer = target // HACK to work around _onScroll target check Simulate.scroll(findDOMNode(rendered), { target }) - expect(onScrollCalls.length).toEqual(2) - expect(onScrollCalls[1]).toEqual({ + expect(onScrollCalls[onScrollCalls.length - 1]).toEqual({ clientHeight: 100, scrollHeight: 1000, scrollTop: 100 @@ -336,7 +335,13 @@ describe('VirtualScroll', () => { })) highestRowIndex = 0 component.recomputeRowHeights() - expect(highestRowIndex).toEqual(49) + // Rows won't actually be remeasured until the VirtualScroll is next rendered. + render(getMarkup({ + rowHeight, + rowCount: 50 + })) + // And then only the rows necessary to fill the visible region. + expect(highestRowIndex).toEqual(9) }) }) }) From 4ec54237f324d9c2796603cfbf51efcfeb7f4cfc Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Mon, 2 May 2016 23:42:24 -0700 Subject: [PATCH 28/60] Initial pass at CellMeasurer HOC; no tests yet --- source/CellMeasurer/CellMeasurer.example.css | 39 ++++ source/CellMeasurer/CellMeasurer.example.js | 141 ++++++++++++ source/CellMeasurer/CellMeasurer.js | 222 +++++++++++++++++++ source/CellMeasurer/CellMeasurer.test.js | 1 + source/CellMeasurer/index.js | 2 + source/demo/Application.js | 9 +- source/index.js | 1 + 7 files changed, 414 insertions(+), 1 deletion(-) create mode 100644 source/CellMeasurer/CellMeasurer.example.css create mode 100644 source/CellMeasurer/CellMeasurer.example.js create mode 100644 source/CellMeasurer/CellMeasurer.js create mode 100644 source/CellMeasurer/CellMeasurer.test.js create mode 100644 source/CellMeasurer/index.js diff --git a/source/CellMeasurer/CellMeasurer.example.css b/source/CellMeasurer/CellMeasurer.example.css new file mode 100644 index 000000000..9948424aa --- /dev/null +++ b/source/CellMeasurer/CellMeasurer.example.css @@ -0,0 +1,39 @@ +.GridRow { + margin-top: 15px; + display: flex; + flex-direction: row; +} +.GridColumn { + display: flex; + flex-direction: column; + flex: 1 1 auto; +} +.LeftSideGridContainer { + flex: 0 0 50px; +} + +.BodyGrid { + width: 100%; + border: 1px solid #e0e0e0; +} + +.evenRow, +.oddRow { + border-bottom: 1px solid #e0e0e0; +} +.oddRow { + background-color: #fafafa; +} + +.cell { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + justify-content: center; + padding: 0 .5em; +} +.cell { + border-right: 1px solid #e0e0e0; + border-bottom: 1px solid #e0e0e0; +} diff --git a/source/CellMeasurer/CellMeasurer.example.js b/source/CellMeasurer/CellMeasurer.example.js new file mode 100644 index 000000000..361ed66a0 --- /dev/null +++ b/source/CellMeasurer/CellMeasurer.example.js @@ -0,0 +1,141 @@ +/** @flow */ +import Immutable from 'immutable' +import React, { Component, PropTypes } from 'react' +import { ContentBox, ContentBoxHeader, ContentBoxParagraph } from '../demo/ContentBox' +import AutoSizer from '../AutoSizer' +import CellMeasurer from './CellMeasurer' +import Grid from '../Grid' +import shallowCompare from 'react-addons-shallow-compare' +import cn from 'classnames' +import styles from './CellMeasurer.example.css' + +const COLUMN_COUNT = 20 +const COLUMN_WIDTH = 150 +const ROW_COUNT = 50 +const ROW_HEIGHT = 35 + +export default class CellMeasurerExample extends Component { + static propTypes = { + list: PropTypes.instanceOf(Immutable.List).isRequired + } + + constructor (props, context) { + super(props, context) + + this._cellRenderer = this._cellRenderer.bind(this) + } + + render () { + const { list, ...props } = this.props + + return ( + + + + + This component renders content for a given column or row in order to determine the widest or tallest cell. + It can be used to just-in-time measure dynamic content (eg. messages in a chat interface). + + + + {({ width }) => ( +
+

Fixed height, dynamic width

+ this._columnWidthMeasurerRef = ref} + rowCount={ROW_COUNT} + > + {({ getColumnWidth }) => ( + + )} + + +

Fixed width, dynamic height

+ + {({ getRowHeight }) => ( + + )} + +
+ )} +
+
+ ) + } + + shouldComponentUpdate (nextProps, nextState) { + return shallowCompare(this, nextProps, nextState) + } + + _cellRenderer ({ columnIndex, rowIndex }) { + const datum = this._getDatum(rowIndex) + const rowClass = this._getRowClassName(rowIndex) + const classNames = cn(rowClass, styles.cell, { + [styles.centeredCell]: columnIndex > 2 + }) + + let content + + switch (columnIndex % 3) { + case 0: + content = datum.color + break + case 1: + content = datum.name + break + case 2: + content = datum.random + break + } + + return ( +
+ {content} +
+ ) + } + + _getDatum (index) { + const { list } = this.props + + return list.get(index % list.size) + } + + _getRowClassName (row) { + return row % 2 === 0 ? styles.evenRow : styles.oddRow + } +} diff --git a/source/CellMeasurer/CellMeasurer.js b/source/CellMeasurer/CellMeasurer.js new file mode 100644 index 000000000..bb9e8135f --- /dev/null +++ b/source/CellMeasurer/CellMeasurer.js @@ -0,0 +1,222 @@ +/** @flow */ +import React, { Component, PropTypes } from 'react' +import ReactDOM from 'react-dom' +import ReactDOMServer from 'react-dom/server' + +/** + * Measures a Grid cell's contents by rendering them in a way that is not visible to the user. + * Either a fixed width or height may be provided if it is desirable to measure only in one direction. + */ +export default class CellMeasurer extends Component { + + static propTypes = { + /** + * Renders a cell given its indices. + * Should implement the following interface: ({ columnIndex: number, rowIndex: number }): PropTypes.node + */ + cellRenderer: PropTypes.func.isRequired, + + /** + * Function respondible for rendering a virtualized component. + * This function should implement the following signature: + * ({ getColumnWidth, getRowHeight, resetMeasurements }) => PropTypes.element + */ + children: PropTypes.func.isRequired, + + /** + * Number of columns in grid. + */ + columnCount: PropTypes.number.isRequired, + + /** + * A Node, Component instance, or function that returns either. + * If this property is not specified the document body will be used. + */ + container: React.PropTypes.oneOfType([ + React.PropTypes.func, + React.PropTypes.node + ]), + + /** + * Assign a fixed :height in order to measure dynamic text :width only. + */ + height: PropTypes.number, + + /** + * Number of rows in grid. + */ + rowCount: PropTypes.number.isRequired, + + /** + * Assign a fixed :width in order to measure dynamic text :height only. + */ + width: PropTypes.number + }; + + constructor (props, state) { + super(props, state) + + this._cachedColumnWidths = {} + this._cachedRowHeights = {} + + this.getColumnWidth = this.getColumnWidth.bind(this) + this.getRowHeight = this.getRowHeight.bind(this) + this.resetMeasurements = this.resetMeasurements.bind(this) + } + + getColumnWidth ({ index }) { + if (this._cachedColumnWidths[index]) { + return this._cachedColumnWidths[index] + } + + const { rowCount } = this.props + + let maxWidth = 0 + + for (var rowIndex = 0; rowIndex < rowCount; rowIndex++) { + let { width } = this._measureCell({ + columnIndex: index, + rowIndex + }) + + maxWidth = Math.max(maxWidth, width) + } + + this._cachedColumnWidths[index] = maxWidth + + return maxWidth + } + + getRowHeight ({ index }) { + if (this._cachedRowHeights[index]) { + return this._cachedRowHeights[index] + } + + const { rowCount } = this.props + + let maxHeight = 0 + + for (var columnIndex = 0; columnIndex < rowCount; columnIndex++) { + let { height } = this._measureCell({ + columnIndex, + rowIndex: index + }) + + maxHeight = Math.max(maxHeight, height) + } + + this._cachedRowHeights[index] = maxHeight + + return maxHeight + } + + resetMeasurements () { + this._cachedColumnWidths = {} + this._cachedRowHeights = {} + } + + componentDidMount () { + this._renderAndMount() + } + + componentWillReceiveProps (nextProps) { + this._updateDivDimensions(nextProps) + } + + componentWillUnmount () { + this._unrenderAndDestroy() + } + + render () { + const { children } = this.props + + return children({ + getColumnWidth: this.getColumnWidth, + getRowHeight: this.getRowHeight, + resetMeasurements: this.resetMeasurements + }) + } + + _getContainerNode (props) { + const { container } = props + + if (container) { + return ReactDOM.findDOMNode( + typeof container === 'function' + ? container() + : container + ) + } else { + const node = ReactDOM.findDOMNode(this) + + return node.ownerDocument.body + } + } + + _measureCell ({ + columnIndex, + rowIndex + }) { + const { cellRenderer } = this.props + + const rendered = cellRenderer({ + columnIndex, + rowIndex + }) + + this._div.innerHTML = ReactDOMServer.renderToString(rendered) + + const height = this._div.clientHeight + const width = this._div.clientWidth + + return { + height, + width + } + } + + _renderAndMount () { + if (!this._div) { + this._div = document.createElement('div') + this._div.style.display = 'inline-block' + this._div.style.position = 'absolute' + this._div.style.visibility = 'hidden' + this._div.style.zIndex = -1 + + this._updateDivDimensions(this.props) + + this._containerNode = this._getContainerNode(this.props) + this._containerNode.appendChild(this._div) + } + } + + _unrenderAndDestroy () { + if (this._div) { + this._containerNode.removeChild(this._div) + + this._div = null + } + + this._containerNode = null + } + + _updateDivDimensions (props) { + const { height, width } = props + + if ( + height && + height !== this._divHeight + ) { + this._divHeight = height + this._div.style.height = `${height}px` + } + + if ( + width && + width !== this._divWidth + ) { + this._divWidth = width + this._div.style.width = `${width}px` + } + } +} diff --git a/source/CellMeasurer/CellMeasurer.test.js b/source/CellMeasurer/CellMeasurer.test.js new file mode 100644 index 000000000..1673a590b --- /dev/null +++ b/source/CellMeasurer/CellMeasurer.test.js @@ -0,0 +1 @@ +// @TODO diff --git a/source/CellMeasurer/index.js b/source/CellMeasurer/index.js new file mode 100644 index 000000000..406c5bd44 --- /dev/null +++ b/source/CellMeasurer/index.js @@ -0,0 +1,2 @@ +export default from './CellMeasurer' +export CellMeasurer from './CellMeasurer' diff --git a/source/demo/Application.js b/source/demo/Application.js index d003ba07d..637ba0bbf 100644 --- a/source/demo/Application.js +++ b/source/demo/Application.js @@ -1,5 +1,6 @@ import ArrowKeyStepperExample from '../ArrowKeyStepper/ArrowKeyStepper.example' import AutoSizerExample from '../AutoSizer/AutoSizer.example' +import CellMeasurerExample from '../CellMeasurer/CellMeasurer.example' import CollectionExample from '../Collection/Collection.example' import ColumnSizerExample from '../ColumnSizer/ColumnSizer.example' import ComponentLink from './ComponentLink' @@ -18,7 +19,7 @@ import shallowCompare from 'react-addons-shallow-compare' import '../../styles.css' const COMPONENTS = ['Collection', 'Grid', 'FlexTable', 'VirtualScroll'] -const HIGH_ORDER_COMPONENTS = ['ArrowKeyStepper', 'AutoSizer', 'ColumnSizer', 'InfiniteLoader', 'ScrollSync'] +const HIGH_ORDER_COMPONENTS = ['ArrowKeyStepper', 'AutoSizer', 'CellMeasurer', 'ColumnSizer', 'InfiniteLoader', 'ScrollSync'] // HACK Generate arbitrary data for use in example components :) const list = Immutable.List(generateRandomList()) @@ -114,6 +115,12 @@ class Application extends Component { list={list} /> } + {activeComponent === 'CellMeasurer' && + + } {activeComponent === 'Collection' && Date: Tue, 3 May 2016 10:05:36 -0700 Subject: [PATCH 29/60] Renamed a method and replaced some vars with lets. --- source/CellMeasurer/CellMeasurer.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/CellMeasurer/CellMeasurer.js b/source/CellMeasurer/CellMeasurer.js index bb9e8135f..dd6af9204 100644 --- a/source/CellMeasurer/CellMeasurer.js +++ b/source/CellMeasurer/CellMeasurer.js @@ -73,7 +73,7 @@ export default class CellMeasurer extends Component { let maxWidth = 0 - for (var rowIndex = 0; rowIndex < rowCount; rowIndex++) { + for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) { let { width } = this._measureCell({ columnIndex: index, rowIndex @@ -92,11 +92,11 @@ export default class CellMeasurer extends Component { return this._cachedRowHeights[index] } - const { rowCount } = this.props + const { columnCount } = this.props let maxHeight = 0 - for (var columnIndex = 0; columnIndex < rowCount; columnIndex++) { + for (let columnIndex = 0; columnIndex < columnCount; columnIndex++) { let { height } = this._measureCell({ columnIndex, rowIndex: index @@ -124,7 +124,7 @@ export default class CellMeasurer extends Component { } componentWillUnmount () { - this._unrenderAndDestroy() + this._unmountContainer() } render () { @@ -190,7 +190,7 @@ export default class CellMeasurer extends Component { } } - _unrenderAndDestroy () { + _unmountContainer () { if (this._div) { this._containerNode.removeChild(this._div) From f448501d5ad749509057dcfc8194d934c33bfc60 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 3 May 2016 10:05:43 -0700 Subject: [PATCH 30/60] Added playground files for Chat --- playground/chat.html | 48 +++++++++++++++++++ playground/chat.js | 107 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 playground/chat.html create mode 100644 playground/chat.js diff --git a/playground/chat.html b/playground/chat.html new file mode 100644 index 000000000..dd4040a88 --- /dev/null +++ b/playground/chat.html @@ -0,0 +1,48 @@ + + + + + foo + + + + + + + +
+ + + diff --git a/playground/chat.js b/playground/chat.js new file mode 100644 index 000000000..16ef798af --- /dev/null +++ b/playground/chat.js @@ -0,0 +1,107 @@ +function rowRenderer (params) { + var datum = chatHistory[params.index] + + return React.createElement( + 'div', + { + className: 'item' + }, + React.createElement( + 'strong', + null, + datum.name + ), + ':', + datum.text + ) +} + +function cellRenderer (params) { + return rowRenderer({ + index: params.rowIndex + }) +} + +var App = React.createClass({ + render: function () { + return React.createElement( + 'div', + { + className: 'container' + }, + React.createElement( + ReactVirtualized.AutoSizer, + {}, + function (autoSizerParams) { + return React.createElement( + ReactVirtualized.CellMeasurer, + { + cellRenderer: cellRenderer, + columnCount: 1, + rowCount: chatHistory.length, + width: autoSizerParams.width + }, + function (cellMeasurerParams) { + return React.createElement( + ReactVirtualized.VirtualScroll, + { + className: 'chat', + height: autoSizerParams.height, + rowCount: chatHistory.length, + rowHeight: cellMeasurerParams.getRowHeight, + rowRenderer: rowRenderer, + width: autoSizerParams.width + } + ) + } + ) + } + ) + ) + } +}) + +var NAMES = ['Peter Brimer', 'Tera Gaona', 'Kandy Liston', 'Lonna Wrede', 'Kristie Yard', 'Raul Host', 'Yukiko Binger', 'Velvet Natera', 'Donette Ponton', 'Loraine Grim', 'Shyla Mable', 'Marhta Sing', 'Alene Munden', 'Holley Pagel', 'Randell Tolman', 'Wilfred Juneau', 'Naida Madson', 'Marine Amison', 'Glinda Palazzo', 'Lupe Island', 'Cordelia Trotta', 'Samara Berrier', 'Era Stepp', 'Malka Spradlin', 'Edward Haner', 'Clemencia Feather', 'Loretta Rasnake', 'Dana Hasbrouck', 'Sanda Nery', 'Soo Reiling', 'Apolonia Volk', 'Liliana Cacho', 'Angel Couchman', 'Yvonne Adam', 'Jonas Curci', 'Tran Cesar', 'Buddy Panos', 'Rosita Ells', 'Rosalind Tavares', 'Renae Keehn', 'Deandrea Bester', 'Kelvin Lemmon', 'Guadalupe Mccullar', 'Zelma Mayers', 'Laurel Stcyr', 'Edyth Everette', 'Marylin Shevlin', 'Hsiu Blackwelder', 'Mark Ferguson', 'Winford Noggle', 'Shizuko Gilchrist', 'Roslyn Cress', 'Nilsa Lesniak', 'Agustin Grant', 'Earlie Jester', 'Libby Daigle', 'Shanna Maloy', 'Brendan Wilken', 'Windy Knittel', 'Alice Curren', 'Eden Lumsden', 'Klara Morfin', 'Sherryl Noack', 'Gala Munsey', 'Stephani Frew', 'Twana Anthony', 'Mauro Matlock', 'Claudie Meisner', 'Adrienne Petrarca', 'Pearlene Shurtleff', 'Rachelle Piro', 'Louis Cocco', 'Susann Mcsweeney', 'Mandi Kempker', 'Ola Moller', 'Leif Mcgahan', 'Tisha Wurster', 'Hector Pinkett', 'Benita Jemison', 'Kaley Findley', 'Jim Torkelson', 'Freda Okafor', 'Rafaela Markert', 'Stasia Carwile', 'Evia Kahler', 'Rocky Almon', 'Sonja Beals', 'Dee Fomby', 'Damon Eatman', 'Alma Grieve', 'Linsey Bollig', 'Stefan Cloninger', 'Giovanna Blind', 'Myrtis Remy', 'Marguerita Dostal', 'Junior Baranowski', 'Allene Seto', 'Margery Caves', 'Nelly Moudy', 'Felix Sailer'] +var SENTENCES = [ + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', + 'Phasellus vulputate odio commodo tortor sodales, et vehicula ipsum viverra.', + 'Cras tincidunt nisi in urna molestie varius.', + 'Curabitur ac enim dictum arcu varius fermentum vel sodales dui.', + 'Ut tristique augue at congue molestie.', + 'Cras eget enim nec odio feugiat tristique eu quis ante.', + 'Phasellus eget enim vitae nunc luctus sodales a eu erat.', + 'Nulla bibendum quam id velit blandit dictum.', + 'Donec dignissim mi ac libero feugiat, vitae lacinia odio viverra.', + 'Praesent vel lectus venenatis, elementum mauris vitae, ullamcorper nulla.', + 'Quisque sollicitudin nulla nec tellus feugiat hendrerit.', + 'Vestibulum a eros accumsan, lacinia eros non, pretium diam.', + 'Donec ornare felis et dui hendrerit, eget bibendum nibh interdum.', + 'Donec nec diam vel tellus egestas lobortis.', + 'Sed ornare nisl sit amet dolor pellentesque, eu fermentum leo interdum.', + 'Sed eget mauris condimentum, molestie justo eu, feugiat felis.', + 'Sed luctus justo vitae nibh bibendum blandit.', + 'Nulla ac eros vestibulum, mollis ante eu, rutrum nulla.', + 'Sed cursus magna ut vehicula rutrum.' +] + +var chatHistory = [] + +for (var i = 0; i < 1000; i++) { + var name = NAMES[Math.floor(Math.random() * NAMES.length)] + var sentences = Math.ceil(Math.random() * 5) + var texts = [] + + for (var x = 0; x < sentences; x++) { + texts.push(SENTENCES[Math.floor(Math.random() * SENTENCES.length)]) + } + + chatHistory.push({ + name, + text: texts.join(' ') + }) +} + +ReactDOM.render( + React.createElement(App), + document.querySelector('#mount') +) From bd8ba7997bc2d28a37d9f0958c5a51ae25c763b9 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Tue, 3 May 2016 10:45:31 -0700 Subject: [PATCH 31/60] Chat demo is full screen now --- playground/chat.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/playground/chat.html b/playground/chat.html index dd4040a88..74c608082 100644 --- a/playground/chat.html +++ b/playground/chat.html @@ -2,6 +2,7 @@ + foo - - + + diff --git a/playground/grid.html b/playground/grid.html index 44ae279bf..f99a9a523 100644 --- a/playground/grid.html +++ b/playground/grid.html @@ -36,8 +36,8 @@ width: 100%; } - - + +
diff --git a/playground/grid.js b/playground/grid.js index 43661270e..c89de950e 100644 --- a/playground/grid.js +++ b/playground/grid.js @@ -1,7 +1,7 @@ var REACT_VIRTUALIZED_BANNER = 'https://cloud.githubusercontent.com/assets/29597/11737732/0ca1e55e-9f91-11e5-97f3-098f2f8ed866.png' -function getColumnWidth (columnIndex) { - switch (columnIndex % 3) { +function getColumnWidth (params) { + switch (params.index % 3) { case 0: return 65 case 1: diff --git a/playground/hover.html b/playground/hover.html index 86db1bb68..82161015c 100644 --- a/playground/hover.html +++ b/playground/hover.html @@ -30,8 +30,8 @@ background-color: rgba(0, 0, 0, .1); } - - + + diff --git a/playground/tree.html b/playground/tree.html index 4bf3fd397..f6d0300f8 100644 --- a/playground/tree.html +++ b/playground/tree.html @@ -30,8 +30,8 @@ user-select: none; } - - + + diff --git a/playground/tree.js b/playground/tree.js index 9a38a47d8..a048e5ce9 100644 --- a/playground/tree.js +++ b/playground/tree.js @@ -7,6 +7,7 @@ function renderItem (item, keyPrefix) { event.stopPropagation() item.expanded = !item.expanded VirtualScroll.recomputeRowHeights() + VirtualScroll.forceUpdate() } var props = { key: keyPrefix } @@ -56,12 +57,12 @@ function setRef (ref) { VirtualScroll = ref } -function cellRenderer (index) { - return renderItem(data[index], index) +function cellRenderer (params) { + return renderItem(data[params.index], params.index) } -function rowHeight (index) { - return getExpandedItemCount(data[index]) * ROW_HEIGHT +function rowHeight (params) { + return getExpandedItemCount(data[params.index]) * ROW_HEIGHT } var App = React.createClass({ From d15fa15109370ad51b24d71af05f8b0485dabe6e Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sat, 7 May 2016 13:45:40 -0700 Subject: [PATCH 41/60] Added better docs for cellRangeRenderer --- docs/Grid.md | 44 +++++++++++- source/Grid/Grid.js | 71 +------------------- source/Grid/defaultCellRangeRenderer.js | 89 +++++++++++++++++++++++++ 3 files changed, 133 insertions(+), 71 deletions(-) create mode 100644 source/Grid/defaultCellRangeRenderer.js diff --git a/docs/Grid.md b/docs/Grid.md index 19368e406..465b04528 100644 --- a/docs/Grid.md +++ b/docs/Grid.md @@ -8,7 +8,7 @@ Only a small number of cells are rendered based on the horizontal and vertical s | Property | Type | Required? | Description | |:---|:---|:---:|:---| | cellRenderer | Function | ✓ | Responsible for rendering a cell given an row and column index: `({ columnIndex: number, isScrolling: boolean, rowIndex: number }): PropTypes.node` | -| cellRangeRenderer | Function | ✓ | Responsible for rendering a group of cells given their index ranges.: `({ columnMetadata:Array, columnStartIndex: number, columnStopIndex: number, cellRenderer: Function, rowMetadata:Array, rowStartIndex: number, rowStopIndex: number }): Array` | +| cellRangeRenderer | Function | ✓ | Responsible for rendering a group of cells given their index ranges.: `({ cellCache: Map, cellRangeRenderer: Function, columnSizeAndPositionManager: CellSizeAndPositionManager, columnStartIndex: number, columnStopIndex: number, isScrolling: boolean, rowSizeAndPositionManager: CellSizeAndPositionManager, rowStartIndex: number, rowStopIndex: number }): Array` | | className | String | | Optional custom CSS class name to attach to root Grid element. | | columnCount | Number | ✓ | Number of columns in grid. | | columnWidth | Number or Function | ✓ | Either a fixed column width (number) or a function that returns the width of a column given its index: `({ index: number }): number` | @@ -45,6 +45,48 @@ The Grid component supports the following static class names | Grid__innerScrollContainer | Inner scrollable area | | Grid__cell | Individual cell | +### cellRangeRenderer + +This is an advanced property. +It is useful for situations where the `Grid` requires additional, overlayed UI (such as a Gantt chart or a calendar application). +Many use cases can be solved more easily using the `onScroll` callback or the `ScrollSync` HOC. +If you do choose to implement your own range renderer though, consider starting by forking the [`defaultCellRangeRenderer`](https://github.com/bvaughn/react-virtualized/blob/master/source/Grid/defaultCellRangeRenderer.js) function. + +The general shape of your range renderer function should look something like the following: + +```js +function cellRangeRenderer ({ + cellCache, + cellRangeRenderer, + columnSizeAndPositionManager, + columnStartIndex, + columnStopIndex, + isScrolling, + rowSizeAndPositionManager, + rowStartIndex, + rowStopIndex +}) { + const renderedCells = [] + + for (let rowIndex = rowStartIndex; rowIndex <= rowStopIndex; rowIndex++) { + // This contains :offset (top) and :size (height) information for the cell + let rowDatum = rowSizeAndPositionManager.getSizeAndPositionOfCell(rowIndex) + + for (let columnIndex = columnStartIndex; columnIndex <= columnStopIndex; columnIndex++) { + // This contains :offset (left) and :size (width) information for the cell + let columnDatum = columnSizeAndPositionManager.getSizeAndPositionOfCell(columnIndex) + + // Now render your cell and additional UI as you see fit. + // Be sure to provide unique :key attributes. + // Add all rendered children to the :renderedCells Array. + } + } + + return renderedCells +} +} +``` + ### Examples Below is a very basic `Grid` example. The grid displays an array of objects with fixed row and column sizes. (Dynamic sizes are also supported but this example is intended to be basic.) [See here](../source/Grid/Grid.example.js) for a more full-featured example with dynamic cell sizes and more. diff --git a/source/Grid/Grid.js b/source/Grid/Grid.js index 4934283ec..9e4d47394 100644 --- a/source/Grid/Grid.js +++ b/source/Grid/Grid.js @@ -10,6 +10,7 @@ import getUpdatedOffsetForIndex from '../utils/getUpdatedOffsetForIndex' import raf from 'raf' import shallowCompare from 'react-addons-shallow-compare' import updateScrollIndexHelper from './utils/updateScrollIndexHelper' +import defaultCellRangeRenderer from './defaultCellRangeRenderer' /** * Specifies the number of miliseconds during which to disable pointer events while a scroll is in progress. @@ -722,73 +723,3 @@ export default class Grid extends Component { this._invokeOnScrollMemoizer({ scrollLeft, scrollTop, totalColumnsWidth, totalRowsHeight }) } } - -function defaultCellRangeRenderer ({ - cellCache, - cellRenderer, - columnSizeAndPositionManager, - columnStartIndex, - columnStopIndex, - isScrolling, - rowSizeAndPositionManager, - rowStartIndex, - rowStopIndex -}) { - const renderedCells = [] - - for (let rowIndex = rowStartIndex; rowIndex <= rowStopIndex; rowIndex++) { - let rowDatum = rowSizeAndPositionManager.getSizeAndPositionOfCell(rowIndex) - - for (let columnIndex = columnStartIndex; columnIndex <= columnStopIndex; columnIndex++) { - let columnDatum = columnSizeAndPositionManager.getSizeAndPositionOfCell(columnIndex) - let key = `${rowIndex}-${columnIndex}` - let renderedCell - - // Avoid re-creating cells while scrolling. - // This can lead to the same cell being created many times and can cause performance issues for "heavy" cells. - // If a scroll is in progress- cache and reuse cells. - // This cache will be thrown away once scrolling complets. - if (isScrolling) { - if (!cellCache[key]) { - cellCache[key] = cellRenderer({ - columnIndex, - isScrolling, - rowIndex - }) - } - renderedCell = cellCache[key] - // If the user is no longer scrolling, don't cache cells. - // This makes dynamic cell content difficult for users and would also lead to a heavier memory footprint. - } else { - renderedCell = cellRenderer({ - columnIndex, - isScrolling, - rowIndex - }) - } - - if (renderedCell == null || renderedCell === false) { - continue - } - - let child = ( -
- {renderedCell} -
- ) - - renderedCells.push(child) - } - } - - return renderedCells -} diff --git a/source/Grid/defaultCellRangeRenderer.js b/source/Grid/defaultCellRangeRenderer.js new file mode 100644 index 000000000..b5f33cf8e --- /dev/null +++ b/source/Grid/defaultCellRangeRenderer.js @@ -0,0 +1,89 @@ +/** @flow */ +import React from 'react' +import CellSizeAndPositionManager from './utils/CellSizeAndPositionManager' + +/** + * Default implementation of cellRangeRenderer used by Grid. + * This renderer supports cell-caching while the user is scrolling. + */ +export default function defaultCellRangeRenderer ({ + cellCache, + cellRenderer, + columnSizeAndPositionManager, + columnStartIndex, + columnStopIndex, + isScrolling, + rowSizeAndPositionManager, + rowStartIndex, + rowStopIndex +}: DefaultCellRangeRendererParams) { + const renderedCells = [] + + for (let rowIndex = rowStartIndex; rowIndex <= rowStopIndex; rowIndex++) { + let rowDatum = rowSizeAndPositionManager.getSizeAndPositionOfCell(rowIndex) + + for (let columnIndex = columnStartIndex; columnIndex <= columnStopIndex; columnIndex++) { + let columnDatum = columnSizeAndPositionManager.getSizeAndPositionOfCell(columnIndex) + let key = `${rowIndex}-${columnIndex}` + let renderedCell + + // Avoid re-creating cells while scrolling. + // This can lead to the same cell being created many times and can cause performance issues for "heavy" cells. + // If a scroll is in progress- cache and reuse cells. + // This cache will be thrown away once scrolling complets. + if (isScrolling) { + if (!cellCache[key]) { + cellCache[key] = cellRenderer({ + columnIndex, + isScrolling, + rowIndex + }) + } + renderedCell = cellCache[key] + // If the user is no longer scrolling, don't cache cells. + // This makes dynamic cell content difficult for users and would also lead to a heavier memory footprint. + } else { + renderedCell = cellRenderer({ + columnIndex, + isScrolling, + rowIndex + }) + } + + if (renderedCell == null || renderedCell === false) { + continue + } + + let child = ( +
+ {renderedCell} +
+ ) + + renderedCells.push(child) + } + } + + return renderedCells +} + +type DefaultCellRangeRendererParams = { + cellCache: Object, + cellRenderer: Function, + columnSizeAndPositionManager: CellSizeAndPositionManager, + columnStartIndex: number, + columnStopIndex: number, + isScrolling: boolean, + rowSizeAndPositionManager: CellSizeAndPositionManager, + rowStartIndex: number, + rowStopIndex: number +}; From 2cf9d32ae2600bca4445148842fe79a2f43dc195 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sat, 7 May 2016 13:51:55 -0700 Subject: [PATCH 42/60] docs/comment tweak for Grid --- docs/Grid.md | 4 ++-- source/Grid/Grid.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/Grid.md b/docs/Grid.md index 465b04528..e16b693d6 100644 --- a/docs/Grid.md +++ b/docs/Grid.md @@ -8,7 +8,7 @@ Only a small number of cells are rendered based on the horizontal and vertical s | Property | Type | Required? | Description | |:---|:---|:---:|:---| | cellRenderer | Function | ✓ | Responsible for rendering a cell given an row and column index: `({ columnIndex: number, isScrolling: boolean, rowIndex: number }): PropTypes.node` | -| cellRangeRenderer | Function | ✓ | Responsible for rendering a group of cells given their index ranges.: `({ cellCache: Map, cellRangeRenderer: Function, columnSizeAndPositionManager: CellSizeAndPositionManager, columnStartIndex: number, columnStopIndex: number, isScrolling: boolean, rowSizeAndPositionManager: CellSizeAndPositionManager, rowStartIndex: number, rowStopIndex: number }): Array` | +| cellRangeRenderer | Function | ✓ | Responsible for rendering a group of cells given their index ranges.: `({ cellCache: Map, cellRenderer: Function, columnSizeAndPositionManager: CellSizeAndPositionManager, columnStartIndex: number, columnStopIndex: number, isScrolling: boolean, rowSizeAndPositionManager: CellSizeAndPositionManager, rowStartIndex: number, rowStopIndex: number }): Array` | | className | String | | Optional custom CSS class name to attach to root Grid element. | | columnCount | Number | ✓ | Number of columns in grid. | | columnWidth | Number or Function | ✓ | Either a fixed column width (number) or a function that returns the width of a column given its index: `({ index: number }): number` | @@ -57,7 +57,7 @@ The general shape of your range renderer function should look something like the ```js function cellRangeRenderer ({ cellCache, - cellRangeRenderer, + cellRenderer, columnSizeAndPositionManager, columnStartIndex, columnStopIndex, diff --git a/source/Grid/Grid.js b/source/Grid/Grid.js index 9e4d47394..2f414e88b 100644 --- a/source/Grid/Grid.js +++ b/source/Grid/Grid.js @@ -45,7 +45,7 @@ export default class Grid extends Component { * Responsible for rendering a group of cells given their index ranges. * Should implement the following interface: ({ * cellCache: Map, - * cellRangeRenderer: Function, + * cellRenderer: Function, * columnSizeAndPositionManager: CellSizeAndPositionManager, * columnStartIndex: number, * columnStopIndex: number, From c5cfcb9f87cd1b0cabb7d0a5ca5dcae2a8c6431b Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sat, 7 May 2016 14:28:04 -0700 Subject: [PATCH 43/60] Added docs for `CellMeasurer` --- README.md | 1 + docs/CellMeasurer.md | 53 ++++++++++++++++++++++++++++++++++++++++++++ docs/README.md | 1 + 3 files changed, 55 insertions(+) create mode 100644 docs/CellMeasurer.md diff --git a/README.md b/README.md index d60dd9fea..dfe5eb035 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,7 @@ Here are some online demos of each component: * [ArrowKeyStepper](https://bvaughn.github.io/react-virtualized/?component=ArrowKeyStepper) * [AutoSizer](https://bvaughn.github.io/react-virtualized/?component=AutoSizer) +* [CellMeasurer](https://bvaughn.github.io/react-virtualized/?component=CellMeasurer) * [Collection](https://bvaughn.github.io/react-virtualized/?component=Collection) * [ColumnSizer](https://bvaughn.github.io/react-virtualized/?component=ColumnSizer) * [FlexTable](https://bvaughn.github.io/react-virtualized/?component=FlexTable) diff --git a/docs/CellMeasurer.md b/docs/CellMeasurer.md new file mode 100644 index 000000000..e283fbf23 --- /dev/null +++ b/docs/CellMeasurer.md @@ -0,0 +1,53 @@ +CellMeasurer +--------------- + +High-order component for automatically measuring a cell's contents by rendering it in a way that is not visible to the user. +Specify a fixed width or height constraint if you only want to measure one dimension. + +**Warning**: This HOC is fairly experimental and may change in future releases. +At this time it is only intended for use with a `Grid` (not `VirtualScroll` or `FlexTable` as their item rendering and cell measuring signatures are different). + +### Prop Types +| Property | Type | Required? | Description | +|:---|:---|:---:|:---| +| cellRenderer | Function | ✓ | Renders a cell given its indices. `({ columnIndex: number, rowIndex: number }): PropTypes.node` | +| children | Function | ✓ | Function respondible for rendering a virtualized component; `({ getColumnWidth, getRowHeight, resetMeasurements }) => PropTypes.element` | +| columnCount | number | ✓ | Number of columns in the `Grid`; in order to measure a row's height, all of that row's columns must be rendered. | +| container | | | A Node, Component instance, or function that returns either. If this property is not specified the document body will be used. | +| height | number | | Fixed height; specify this property to measure width only. | +| rowCount | number | ✓ | Number of rows in the `Grid`; in order to measure a column's width, all of that column's rows must be rendered. | +| width | number | | Fixed width; specify this property to measure height only. | + +### Examples + +This example shows a `Grid` with fixed column widths and dynamic row heights. +For more examples check out the component [demo page](https://bvaughn.github.io/react-virtualized/?component=CellMeasurer). + +```javascript +import React from 'react'; +import ReactDOM from 'react-dom'; +import { CellMeasurer, Grid } from 'react-virtualized'; +import 'react-virtualized/styles.css'; // only needs to be imported once + +ReactDOM.render( + + {({ getColumnWidth }) => ( + + )} + , + document.getElementById('example') +); +``` diff --git a/docs/README.md b/docs/README.md index ffbe14e4f..0adcc860c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -13,6 +13,7 @@ Documentation ### High-Order Components * [ArrowKeyStepper](ArrowKeyStepper.md) * [AutoSizer](AutoSizer.md) +* [CellMeasurer](CellMeasurer.md) * [ColumnSizer](ColumnSizer.md) * [InfiniteLoader](InfiniteLoader.md) * [ScrollSync](ScrollSync.md) From 0d26e34ac3dcadb125722da35ad8e81697ad7c84 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sat, 7 May 2016 14:30:32 -0700 Subject: [PATCH 44/60] Increase warning tone for CellMeasurer docs --- docs/CellMeasurer.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/CellMeasurer.md b/docs/CellMeasurer.md index e283fbf23..70754312f 100644 --- a/docs/CellMeasurer.md +++ b/docs/CellMeasurer.md @@ -6,6 +6,8 @@ Specify a fixed width or height constraint if you only want to measure one dimen **Warning**: This HOC is fairly experimental and may change in future releases. At this time it is only intended for use with a `Grid` (not `VirtualScroll` or `FlexTable` as their item rendering and cell measuring signatures are different). +Also note that in order to measure a column's width for a `Grid`, that column's content must be rendered for all rows in order to determine the maximum width. +For this reason it may not be a good idea to use this HOC for `Grid`s containing both a large number of columns _and_ cells. ### Prop Types | Property | Type | Required? | Description | From 639c3ec2d2a87110fcab7d950f031354ba97d966 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sat, 7 May 2016 17:51:28 -0700 Subject: [PATCH 45/60] Added :isScrolling param to Collection's :cellRenderer. Added test coverage for this param to Collection and Grid. --- docs/Collection.md | 2 +- source/Collection/Collection.example.js | 24 ++++++++++--- source/Collection/Collection.js | 11 ++++-- source/Collection/Collection.test.js | 47 ++++++++++++++++--------- source/Grid/Grid.test.js | 27 ++++++++++---- 5 files changed, 81 insertions(+), 30 deletions(-) diff --git a/docs/Collection.md b/docs/Collection.md index e19d6d8d1..84d8c8cf1 100644 --- a/docs/Collection.md +++ b/docs/Collection.md @@ -9,7 +9,7 @@ Unlike `Grid`, which renders checkerboard data, `Collection` can render arbitrar |:---|:---|:---:|:---| | className | String | | Optional custom CSS class name to attach to root Collection element. | | cellCount | Number | ✓ | Number of cells in collection. | -| cellRenderer | Function | ✓ | Responsible for rendering a cell given an row and column index: `({ index: number }): PropTypes.element` | +| cellRenderer | Function | ✓ | Responsible for rendering a cell given an row and column index: `({ index: number, isScrolling: boolean }): PropTypes.element` | | cellGroupRenderer | Function | ✓ | Responsible for rendering a group of cells given their indices.: `({ cellSizeAndPositionGetter:Function, indices: Array, cellRenderer: Function }): Array` | | cellSizeAndPositionGetter | Function | ✓ | Callback responsible for returning size and offset/position information for a given cell (index): `({ index: number }): { height: number, width: number, x: number, y: number }` | | height | Number | ✓ | Height of Collection; this property determines the number of visible (vs virtualized) rows. | diff --git a/source/Collection/Collection.example.js b/source/Collection/Collection.example.js index 80224b7eb..533738283 100644 --- a/source/Collection/Collection.example.js +++ b/source/Collection/Collection.example.js @@ -25,7 +25,8 @@ export default class CollectionExample extends Component { cellCount: props.list.size, columnCount: this._getColumnCount(props.list.size), height: 300, - scrollToCell: undefined + scrollToCell: undefined, + showScrollingPlaceholder: true } this._columnYMap = [] @@ -39,7 +40,7 @@ export default class CollectionExample extends Component { } render () { - const { cellCount, height, scrollToCell } = this.state + const { cellCount, height, scrollToCell, showScrollingPlaceholder } = this.state return ( @@ -54,6 +55,19 @@ export default class CollectionExample extends Component { Unlike Grid, which renders checkerboard data, Collection can render arbitrarily positioned- even overlapping- data. + + + + - {index} + {showScrollingPlaceholder && isScrolling ? '...' : index} ) } diff --git a/source/Collection/Collection.js b/source/Collection/Collection.js index 4e9252fad..efe1be079 100644 --- a/source/Collection/Collection.js +++ b/source/Collection/Collection.js @@ -169,7 +169,8 @@ export default class Collection extends Component { return cellGroupRenderer({ cellRenderer, cellSizeAndPositionGetter: ({ index }) => this._sectionManager.getCellMetadata({ index }), - indices: this._lastRenderedCellIndices + indices: this._lastRenderedCellIndices, + isScrolling }) } } @@ -177,12 +178,16 @@ export default class Collection extends Component { function defaultCellGroupRenderer ({ cellRenderer, cellSizeAndPositionGetter, - indices + indices, + isScrolling }) { return indices .map((index) => { const cellMetadata = cellSizeAndPositionGetter({ index }) - const renderedCell = cellRenderer({ index }) + const renderedCell = cellRenderer({ + index, + isScrolling + }) if (renderedCell == null || renderedCell === false) { return null diff --git a/source/Collection/Collection.test.js b/source/Collection/Collection.test.js index e20ad0649..0a7ce6b87 100644 --- a/source/Collection/Collection.test.js +++ b/source/Collection/Collection.test.js @@ -10,6 +10,14 @@ import Collection from './Collection' import { CELLS, SECTION_SIZE } from './TestData' describe('Collection', () => { + function defaultCellRenderer ({ index }) { + return ( +
+ cell:{index} +
+ ) + } + function getMarkup ({ className, cellCount = CELLS.length, @@ -26,14 +34,6 @@ describe('Collection', () => { scrollTop, width = SECTION_SIZE * 2 } = {}) { - function defaultCellRenderer ({ index }) { - return ( -
- cell:{index} -
- ) - } - function defaultCellSizeAndPositionGetter ({ index }) { index = index % cellCount @@ -60,6 +60,12 @@ describe('Collection', () => { ) } + function simulateScroll ({ collection, scrollLeft, scrollTop }) { + const target = { scrollLeft, scrollTop } + collection.refs.CollectionView.refs.scrollingContainer = target // HACK to work around _onScroll target check + Simulate.scroll(findDOMNode(collection), { target }) + } + function compareArrays (array1, array2) { expect(array1.length).toEqual(array2.length) @@ -293,12 +299,6 @@ describe('Collection', () => { }) describe('onScroll', () => { - function helper ({ collection, scrollLeft, scrollTop }) { - const target = { scrollLeft, scrollTop } - collection.refs.CollectionView.refs.scrollingContainer = target // HACK to work around _onScroll target check - Simulate.scroll(findDOMNode(collection), { target }) - } - it('should trigger callback when component is mounted', () => { const onScrollCalls = [] render(getMarkup({ @@ -321,7 +321,7 @@ describe('Collection', () => { const collection = render(getMarkup({ onScroll: params => onScrollCalls.push(params) })) - helper({ + simulateScroll({ collection, scrollLeft: 1, scrollTop: 0 @@ -342,7 +342,7 @@ describe('Collection', () => { const collection = render(getMarkup({ onScroll: params => onScrollCalls.push(params) })) - helper({ + simulateScroll({ collection, scrollLeft: 0, scrollTop: 2 @@ -381,4 +381,19 @@ describe('Collection', () => { compareArrays(cellGroupRendererParams.indices, [0, 1, 2, 3]) }) }) + + it('should pass the cellRenderer an :isScrolling flag when scrolling is in progress', () => { + const cellRendererCalls = [] + function cellRenderer ({ index, isScrolling }) { + cellRendererCalls.push(isScrolling) + return defaultCellRenderer({ index }) + } + const collection = render(getMarkup({ + cellRenderer + })) + expect(cellRendererCalls[0]).toEqual(false) + cellRendererCalls.splice(0) + simulateScroll({ collection, scrollTop: 100 }) + expect(cellRendererCalls[0]).toEqual(true) + }) }) diff --git a/source/Grid/Grid.test.js b/source/Grid/Grid.test.js index bdda373f6..de0d4e02a 100644 --- a/source/Grid/Grid.test.js +++ b/source/Grid/Grid.test.js @@ -8,7 +8,7 @@ const NUM_ROWS = 100 const NUM_COLUMNS = 50 describe('Grid', () => { - function defaultRenderCell ({ columnIndex, rowIndex }) { + function defaultCellRenderer ({ columnIndex, rowIndex }) { return (
{`row:${rowIndex}, column:${columnIndex}`} @@ -27,7 +27,7 @@ describe('Grid', () => { } function getMarkup ({ - cellRenderer = defaultRenderCell, + cellRenderer = defaultCellRenderer, cellRangeRenderer, className, columnCount = NUM_COLUMNS, @@ -50,7 +50,7 @@ describe('Grid', () => { } = {}) { return ( { }) }) + it('should pass the cellRenderer an :isScrolling flag when scrolling is in progress', () => { + const cellRendererCalls = [] + function cellRenderer ({ columnIndex, isScrolling, rowIndex }) { + cellRendererCalls.push(isScrolling) + return defaultCellRenderer({ columnIndex, rowIndex }) + } + const grid = render(getMarkup({ + cellRenderer + })) + expect(cellRendererCalls[0]).toEqual(false) + cellRendererCalls.splice(0) + simulateScroll({ grid, scrollTop: 100 }) + expect(cellRendererCalls[0]).toEqual(true) + }) + describe('cell caching', () => { it('should not cache cells if the Grid is not scrolling', () => { const cellRendererCalls = [] function cellRenderer ({ columnIndex, rowIndex }) { cellRendererCalls.push({ columnIndex, rowIndex }) - return defaultRenderCell({ columnIndex, rowIndex }) + return defaultCellRenderer({ columnIndex, rowIndex }) } const props = { cellRenderer, @@ -662,7 +677,7 @@ describe('Grid', () => { const cellRendererCalls = [] function cellRenderer ({ columnIndex, rowIndex }) { cellRendererCalls.push({ columnIndex, rowIndex }) - return defaultRenderCell({ columnIndex, rowIndex }) + return defaultCellRenderer({ columnIndex, rowIndex }) } const props = { cellRenderer, @@ -707,7 +722,7 @@ describe('Grid', () => { const cellRendererCalls = [] function cellRenderer ({ columnIndex, rowIndex }) { cellRendererCalls.push({ columnIndex, rowIndex }) - return defaultRenderCell({ columnIndex, rowIndex }) + return defaultCellRenderer({ columnIndex, rowIndex }) } const props = { cellRenderer, From 32a5ba3f3b38064523d4fbab7671c4b2eb24d33b Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sun, 8 May 2016 07:55:42 -0700 Subject: [PATCH 46/60] Split FlexColumn default renderers into separate files so the docs could link to them individually --- source/FlexTable/FlexColumn.js | 76 ++--------------------- source/FlexTable/FlexColumn.test.js | 3 +- source/FlexTable/defaultCellDataGetter.js | 19 ++++++ source/FlexTable/defaultCellRenderer.js | 20 ++++++ source/FlexTable/defaultHeaderRenderer.js | 38 ++++++++++++ source/FlexTable/types.js | 9 +++ 6 files changed, 92 insertions(+), 73 deletions(-) create mode 100644 source/FlexTable/defaultCellDataGetter.js create mode 100644 source/FlexTable/defaultCellRenderer.js create mode 100644 source/FlexTable/defaultHeaderRenderer.js diff --git a/source/FlexTable/FlexColumn.js b/source/FlexTable/FlexColumn.js index 743e9218b..36a7675f7 100644 --- a/source/FlexTable/FlexColumn.js +++ b/source/FlexTable/FlexColumn.js @@ -1,76 +1,8 @@ /** @flow */ -import React, { Component, PropTypes } from 'react' -import type { CellDataGetterParams, CellRendererParams } from './types' -import SortIndicator from './SortIndicator' - -/** - * Default cell renderer that displays an attribute as a simple string - * You should override the column's cellRenderer if your data is some other type of object. - */ -export function defaultCellRenderer ({ - cellData, - cellDataKey, - columnData, - rowData, - rowIndex -}: CellRendererParams): string { - if (cellData === null || cellData === undefined) { - return '' - } else { - return String(cellData) - } -} - -/** - * Default accessor for returning a cell value for a given attribute. - * This function expects to operate on either a vanilla Object or an Immutable Map. - * You should override the column's cellDataGetter if your data is some other type of object. - */ -export function defaultCellDataGetter ({ - columnData, - dataKey, - rowData -}: CellDataGetterParams) { - if (rowData.get instanceof Function) { - return rowData.get(dataKey) - } else { - return rowData[dataKey] - } -} - -/** - * Default table header renderer. - */ -export function defaultHeaderRenderer ({ - columnData, - dataKey, - disableSort, - label, - sortBy, - sortDirection -}) { - const showSortIndicator = sortBy === dataKey - const children = [ -
- {label} -
- ] - - if (showSortIndicator) { - children.push( - - ) - } - - return children -} +import { Component, PropTypes } from 'react' +import defaultHeaderRenderer from './defaultHeaderRenderer' +import defaultCellRenderer from './defaultCellRenderer' +import defaultCellDataGetter from './defaultCellDataGetter' /** * Describes the header and cell contents of a table column. diff --git a/source/FlexTable/FlexColumn.test.js b/source/FlexTable/FlexColumn.test.js index 559757747..61df6127f 100644 --- a/source/FlexTable/FlexColumn.test.js +++ b/source/FlexTable/FlexColumn.test.js @@ -1,5 +1,6 @@ import Immutable from 'immutable' -import { defaultCellDataGetter, defaultCellRenderer } from './FlexColumn' +import defaultCellDataGetter from './defaultCellDataGetter' +import defaultCellRenderer from './defaultCellRenderer' describe('Column', () => { const rowData = Immutable.Map({ diff --git a/source/FlexTable/defaultCellDataGetter.js b/source/FlexTable/defaultCellDataGetter.js new file mode 100644 index 000000000..bf21eef97 --- /dev/null +++ b/source/FlexTable/defaultCellDataGetter.js @@ -0,0 +1,19 @@ +/** @flow */ +import type { CellDataGetterParams } from './types' + +/** + * Default accessor for returning a cell value for a given attribute. + * This function expects to operate on either a vanilla Object or an Immutable Map. + * You should override the column's cellDataGetter if your data is some other type of object. + */ +export default function defaultCellDataGetter ({ + columnData, + dataKey, + rowData +}: CellDataGetterParams) { + if (rowData.get instanceof Function) { + return rowData.get(dataKey) + } else { + return rowData[dataKey] + } +} diff --git a/source/FlexTable/defaultCellRenderer.js b/source/FlexTable/defaultCellRenderer.js new file mode 100644 index 000000000..d98163a80 --- /dev/null +++ b/source/FlexTable/defaultCellRenderer.js @@ -0,0 +1,20 @@ +/** @flow */ +import type { CellRendererParams } from './types' + +/** + * Default cell renderer that displays an attribute as a simple string + * You should override the column's cellRenderer if your data is some other type of object. + */ +export default function defaultCellRenderer ({ + cellData, + cellDataKey, + columnData, + rowData, + rowIndex +}: CellRendererParams): string { + if (cellData === null || cellData === undefined) { + return '' + } else { + return String(cellData) + } +} diff --git a/source/FlexTable/defaultHeaderRenderer.js b/source/FlexTable/defaultHeaderRenderer.js new file mode 100644 index 000000000..369d87682 --- /dev/null +++ b/source/FlexTable/defaultHeaderRenderer.js @@ -0,0 +1,38 @@ +/** @flow */ +import React from 'react' +import SortIndicator from './SortIndicator' +import type { HeaderRendererParams } from './types' + +/** + * Default table header renderer. + */ +export default function defaultHeaderRenderer ({ + columnData, + dataKey, + disableSort, + label, + sortBy, + sortDirection +}: HeaderRendererParams) { + const showSortIndicator = sortBy === dataKey + const children = [ +
+ {label} +
+ ] + + if (showSortIndicator) { + children.push( + + ) + } + + return children +} diff --git a/source/FlexTable/types.js b/source/FlexTable/types.js index 2dc3f496a..4a91462bd 100644 --- a/source/FlexTable/types.js +++ b/source/FlexTable/types.js @@ -13,3 +13,12 @@ export type CellRendererParams = { rowData: any, rowIndex: number }; + +export type HeaderRendererParams = { + columnData: ?any, + dataKey: string, + disableSort: ?boolean, + label: ?string, + sortBy: ?string, + sortDirection: ?string +}; From 6c1961fed17e5ddccb7adeb3fefaa4cdf5c1e3b3 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sun, 8 May 2016 08:00:03 -0700 Subject: [PATCH 47/60] Updated FlexTable demo/example to show header-hiding functionality --- source/FlexTable/FlexTable.example.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/source/FlexTable/FlexTable.example.js b/source/FlexTable/FlexTable.example.js index 229556832..2eb2f19d6 100644 --- a/source/FlexTable/FlexTable.example.js +++ b/source/FlexTable/FlexTable.example.js @@ -20,6 +20,7 @@ export default class FlexTableExample extends Component { super(props, context) this.state = { + disableHeader: false, headerHeight: 30, height: 270, hideIndexRow: false, @@ -42,6 +43,7 @@ export default class FlexTableExample extends Component { render () { const { + disableHeader, headerHeight, height, hideIndexRow, @@ -96,13 +98,24 @@ export default class FlexTableExample extends Component { + + @@ -152,6 +165,7 @@ export default class FlexTableExample extends Component { {({ width }) => ( Date: Sun, 8 May 2016 08:13:22 -0700 Subject: [PATCH 48/60] Docs tweak --- README.md | 3 +++ docs/ArrowKeyStepper.md | 25 +++++++++++-------------- docs/AutoSizer.md | 8 ++++---- docs/CellMeasurer.md | 18 ++++++++++++++---- docs/Collection.md | 20 +++++++++++++++----- docs/ColumnSizer.md | 8 ++++---- docs/FlexColumn.md | 12 ++++++------ docs/FlexTable.md | 12 ++++++------ docs/Grid.md | 8 ++++---- docs/InfiniteLoader.md | 9 +++++---- docs/README.md | 2 ++ docs/ScrollSync.md | 2 +- docs/VirtualScroll.md | 6 +++--- docs/reverseList.md | 8 +++++--- 14 files changed, 83 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index dfe5eb035..d4d92afbf 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ There are also a couple of how-to guides: * [Displaying items in reverse order](docs/reverseList.md) * [Using AutoSizer](docs/usingAutoSizer.md) * [Creating an infinite-loading list](docs/creatingAnInfiniteLoadingList.md) +* [Displaying a reverse list](docs/reverseList.md) Examples --------------- @@ -83,6 +84,8 @@ Here are some online demos of each component: And here are some "recipe" type demos: * [Collapsable tree view](https://rawgit.com/bvaughn/react-virtualized/master/playground/tree.html) * [Full-page grid (spreadsheet)](https://rawgit.com/bvaughn/react-virtualized/master/playground/grid.html) +* [Dyanmic cell measuring](https://rawgit.com/bvaughn/react-virtualized/master/playground/chat.html) +* [Cell hover effects](https://rawgit.com/bvaughn/react-virtualized/master/playground/hover.html) Contributions ------------ diff --git a/docs/ArrowKeyStepper.md b/docs/ArrowKeyStepper.md index 6782a4980..0c5572b59 100644 --- a/docs/ArrowKeyStepper.md +++ b/docs/ArrowKeyStepper.md @@ -10,7 +10,7 @@ The appearance of this wrapper element can be customized using the `className` p ### Prop Types | Property | Type | Required? | Description | |:---|:---|:---:|:---| -| children | Function | ✓ | Function respondible for rendering children. This function should implement the following signature: `({ onKeyDown, onSectionRendered, scrollToColumn, scrollToRow }) => PropTypes.element` | +| children | Function | ✓ | Function respondible for rendering children. This function should implement the following signature: `({ onSectionRendered: Function, scrollToColumn: number, scrollToRow: number }) => PropTypes.element` | | className | String | | CSS class name to attach to the wrapper `
`. | | columnCount | Number | ✓ | Number of columns in grid; for `FlexTable` and `VirtualScroll` this property should always be `1`. | | rowCount | Number | ✓ | Number of rows in grid. | @@ -20,8 +20,7 @@ The appearance of this wrapper element can be customized using the `className` p The child function is passed the following named parameters: | Parameter | Type | Description | -|:---|:---|:---:| -| onKeyDown | Function | Key-down event handler to be attached to the DOM hierarchy. | +|:---|:---|:---| | onSectionRendered | Function | Pass-through callback to be attached to child component; informs the key-stepper which range of cells are currently visible. | | scrollToColumn | Number | Specifies which column in the child component should be visible | | scrollToRow | Number | Specifies which row in the child component should be visible | @@ -41,17 +40,15 @@ ReactDOM.render( columnCount={columnCount} rowCount={rowCount} > - {({ onKeyDown, onSectionRendered, scrollToColumn, scrollToRow }) => ( -
- -
+ {({ onSectionRendered, scrollToColumn, scrollToRow }) => ( + )} , document.getElementById('example') diff --git a/docs/AutoSizer.md b/docs/AutoSizer.md index 47dc6cb47..1078b5ae9 100644 --- a/docs/AutoSizer.md +++ b/docs/AutoSizer.md @@ -6,10 +6,10 @@ High-order component that automatically adjusts the width and height of a single ### Prop Types | Property | Type | Required? | Description | |:---|:---|:---:|:---| -| children | Function | ✓ | Function respondible for rendering children. This function should implement the following signature: `({ height, width }) => PropTypes.element` | -| disableHeight | Boolean | | If true the child's `height` property will not be managed | -| disableWidth | Boolean | | If true the child's `width` property will not be managed | -| onResize | Function | Callback to be invoked on-resize; it is passed the following named parameters: `({ height, width })` | +| children | Function | ✓ | Function respondible for rendering children. This function should implement the following signature: `({ height: number, width: number }) => PropTypes.element` | +| disableHeight | Boolean | | Fixed `height`; if specified, the child's `height` property will not be managed | +| disableWidth | Boolean | | Fixed `width`; if specified, the child's `width` property will not be managed | +| onResize | Function | | Callback to be invoked on-resize; it is passed the following named parameters: `({ height: number, width: number })`. | ### Examples diff --git a/docs/CellMeasurer.md b/docs/CellMeasurer.md index 70754312f..e6a58c599 100644 --- a/docs/CellMeasurer.md +++ b/docs/CellMeasurer.md @@ -7,18 +7,28 @@ Specify a fixed width or height constraint if you only want to measure one dimen **Warning**: This HOC is fairly experimental and may change in future releases. At this time it is only intended for use with a `Grid` (not `VirtualScroll` or `FlexTable` as their item rendering and cell measuring signatures are different). Also note that in order to measure a column's width for a `Grid`, that column's content must be rendered for all rows in order to determine the maximum width. -For this reason it may not be a good idea to use this HOC for `Grid`s containing both a large number of columns _and_ cells. +For this reason it may not be a good idea to use this HOC for `Grid`s containing a large number of both columns _and_ cells. ### Prop Types | Property | Type | Required? | Description | |:---|:---|:---:|:---| | cellRenderer | Function | ✓ | Renders a cell given its indices. `({ columnIndex: number, rowIndex: number }): PropTypes.node` | -| children | Function | ✓ | Function respondible for rendering a virtualized component; `({ getColumnWidth, getRowHeight, resetMeasurements }) => PropTypes.element` | +| children | Function | ✓ | Function respondible for rendering a virtualized component; `({ getColumnWidth: Function, getRowHeight: Function, resetMeasurements: Function }) => PropTypes.element` | | columnCount | number | ✓ | Number of columns in the `Grid`; in order to measure a row's height, all of that row's columns must be rendered. | | container | | | A Node, Component instance, or function that returns either. If this property is not specified the document body will be used. | -| height | number | | Fixed height; specify this property to measure width only. | +| height | number | | Fixed height; specify this property to measure cell-width only. | | rowCount | number | ✓ | Number of rows in the `Grid`; in order to measure a column's width, all of that column's rows must be rendered. | -| width | number | | Fixed width; specify this property to measure height only. | +| width | number | | Fixed width; specify this property to measure cell-height only. | + +### Children function + +The child function is passed the following named parameters: + +| Parameter | Type | Description | +|:---|:---|:---| +| getColumnWidth | Function | Callback to set as the `columnWidth` property of a `Grid` | +| getRowHeight | Function | Callback to set as the `rowHeight` property of a `Grid` | +| resetMeasurements | Function | Use this function to clear cached measurements in `CellRenderer`; its size will be remeasured the next time it is requested. | ### Examples diff --git a/docs/Collection.md b/docs/Collection.md index 84d8c8cf1..b9fcc2c9d 100644 --- a/docs/Collection.md +++ b/docs/Collection.md @@ -4,22 +4,24 @@ Collection Renders scattered or non-linear data. Unlike `Grid`, which renders checkerboard data, `Collection` can render arbitrarily positioned- even overlapping- data. +**Note** that this component's measuring and layout phase is more expensive than `Grid` since it can not assume a correlation between a cell's index and position. For this reason it will take signifnicantly longer to initialize than the more linear/checkerboard components. + ### Prop Types | Property | Type | Required? | Description | |:---|:---|:---:|:---| | className | String | | Optional custom CSS class name to attach to root Collection element. | | cellCount | Number | ✓ | Number of cells in collection. | -| cellRenderer | Function | ✓ | Responsible for rendering a cell given an row and column index: `({ index: number, isScrolling: boolean }): PropTypes.element` | | cellGroupRenderer | Function | ✓ | Responsible for rendering a group of cells given their indices.: `({ cellSizeAndPositionGetter:Function, indices: Array, cellRenderer: Function }): Array` | +| cellRenderer | Function | ✓ | Responsible for rendering a cell given an row and column index: `({ index: number, isScrolling: boolean }): PropTypes.element` | | cellSizeAndPositionGetter | Function | ✓ | Callback responsible for returning size and offset/position information for a given cell (index): `({ index: number }): { height: number, width: number, x: number, y: number }` | | height | Number | ✓ | Height of Collection; this property determines the number of visible (vs virtualized) rows. | | noContentRenderer | Function | | Optional renderer to be rendered inside the grid when `cellCount` is 0: `(): PropTypes.node` | | onSectionRendered | Function | | Callback invoked with information about the section of the Collection that was just rendered: `({ indices: Array }): void` | -| onScroll | Function | | Callback invoked whenever the scroll offset changes within the inner scrollable region: `({ clientHeight, clientWidth, scrollHeight, scrollLeft, scrollTop, scrollWidth }): void` | +| onScroll | Function | | Callback invoked whenever the scroll offset changes within the inner scrollable region: `({ clientHeight: number, clientWidth: number, scrollHeight: number, scrollLeft: number, scrollTop: number, scrollWidth: number }): void` | | scrollLeft | Number | | Horizontal offset | | scrollToCell | Number | | Cell index to ensure visible (by scrolling if necessary) | | scrollTop | Number | | Vertical offset | -| sectionSize | Number | | Optionally override the size of the sections a Collection's cells are split into. | +| sectionSize | Number | | Optionally override the size of the sections a Collection's cells are split into. This is an advanced option and should only be used for performance tuning purposes. | | width | Number | ✓ | Width of Collection; this property determines the number of visible (vs virtualized) columns. | ### Public Methods @@ -29,7 +31,7 @@ Unlike `Grid`, which renders checkerboard data, `Collection` can render arbitrar Recomputes cell sizes and positions. This function should be called if cell sizes or positions have changed but nothing else has. -Since Collection only receives `cellCount` it has no way of detecting when the underlying data changes. +Since Collection only receives `cellCount` (and not the underlying List or Array) it has no way of detecting when the underlying data changes. ### Class names @@ -63,7 +65,15 @@ ReactDOM.render( list[index].name} - cellSizeAndPositionGetter={({ index }) => list[index].size} + cellSizeAndPositionGetter={({ index }) => { + const datum = list[index] + return { + height: datum.height, + width: datum.width, + x: datum.x, + y: datum.y + } + }} height={300} width={300} />, diff --git a/docs/ColumnSizer.md b/docs/ColumnSizer.md index 3c196ab30..9ed0e6220 100644 --- a/docs/ColumnSizer.md +++ b/docs/ColumnSizer.md @@ -6,7 +6,7 @@ High-order component that auto-calculates column-widths for `Grid` cells. ### Prop Types | Property | Type | Required? | Description | |:---|:---|:---:|:---| -| children | Function | ✓ | Function respondible for rendering a virtualized Grid. This function should implement the following signature: `({ adjustedWidth, getColumnWidth, registerChild }) => PropTypes.element` | +| children | Function | ✓ | Function respondible for rendering a virtualized Grid. This function should implement the following signature: `({ adjustedWidth: number, getColumnWidth: Function, registerChild: Function }) => PropTypes.element` | | columnMaxWidth | Number | | Optional maximum allowed column width | | columnMinWidth | Number | | Optional minimum allowed column width | | width | Number | ✓ | Width of Grid or `FlexTable` child | @@ -16,14 +16,14 @@ High-order component that auto-calculates column-widths for `Grid` cells. The child function is passed the following named parameters: | Parameter | Type | Description | -|:---|:---|:---:| +|:---|:---|:---| +| adjustedWidth | Number | This number reflects the lesser of the overall `Grid` width or the width of all columns. Use this to make your `Grid` shrink to fit sparse content. | | getColumnWidth | Function | This function should be passed to the `Grid`'s `columnWidth` property. | | registerChild | Function | This function should be set as the child's `ref` property. It enables a set of rows to be refreshed once their data has finished loading. | -| adjustedWidth | Number | This number reflects the lesser of the overall `Grid` width or the width of all columns. Use this to make your `Grid` shrink to fit sparse content. | ### Examples -This example displays a `Grid` that shrinks to fit sparse content (using the `adjustedWidth` parameter). +This example displays a `Grid` that shrinks to fit sparse content (using the `adjustedWidth` parameter). An interactive demo of this component can be seen [here](https://bvaughn.github.io/react-virtualized/?component=ColumnSizer). ```javascript import React from 'react'; diff --git a/docs/FlexColumn.md b/docs/FlexColumn.md index bcf8484e2..eb4ad5261 100644 --- a/docs/FlexColumn.md +++ b/docs/FlexColumn.md @@ -1,7 +1,7 @@ FlexColumn --------------- -Describes the header and cell contents of a table column +Describes the header and cell contents of a table column. #### Prop Types | Property | Type | Required? | Description | @@ -9,8 +9,8 @@ Describes the header and cell contents of a table column | cellClassName | String | | CSS class to apply to cell | | cellDataGetter | Function | | Callback responsible for returning a cell's data, given its `dataKey`. [Learn more](#celldatagetter) | | cellRenderer | Function | | Callback responsible for rendering a cell's contents. [Learn more](#cellrenderer) | -| columnData | Object | | Additional data passed to this column's `cellDataGetter` | -| dataKey | any | ✓ | Uniquely identifies the row-data attribute correspnding to this cell | +| columnData | Object | | Additional data passed to this column's `cellDataGetter`. Use this object to relay action-creators or relational data. | +| dataKey | any | ✓ | Uniquely identifies the row-data attribute correspnding to this cell (eg this might be "name" in an array of user objects). | | disableSort | Boolean | | If sort is enabled for the table at large, disable it for this column | | flexGrow | Number | | Flex grow style; defaults to 0 | | flexShrink | Number | | Flex shrink style; defaults to 1 | @@ -30,7 +30,7 @@ It should implement the following signature: function ({ columnData: any, dataKey: string, rowData: any }): any ``` -A default `cellDataGetter` is provided that simply returns the attribute as a String. +A [default `cellDataGetter`](https://github.com/bvaughn/react-virtualized/blob/master/source/FlexTable/defaultCellDataGetter.js) is provided that simply returns the attribute as a String. This function expects to operate on either a vanilla Object or a Map-like object with a get method. You should override this default method if your data is calculated or requires any custom processing. @@ -43,7 +43,7 @@ It should implement the following signature: function ({ cellData: any, columnData: any, dataKey: string, isScrolling: boolean, rowData: any, rowIndex: number }): node ``` -A default `cellRenderer` is provided that displays an attribute as a simple string +A [default `cellRenderer`](https://github.com/bvaughn/react-virtualized/blob/master/source/FlexTable/defaultCellRenderer.js) is provided that displays an attribute as a simple string You should override this default method if your data is some other type of object or requires custom formatting. #### headerRenderer @@ -55,5 +55,5 @@ It should implement the following signature: function ({ columnData: any, dataKey: string, disableSort: boolean, label: string, sortBy: string, sortDirection: SortDirection }): element ``` -A default `headerRenderer` is provided that displays the column `label` along with a sort indicator if the column is sort-enabled and active. +A [default `headerRenderer`](https://github.com/bvaughn/react-virtualized/blob/master/source/FlexTable/defaultHeaderRenderer.js) is provided that displays the column `label` along with a sort indicator if the column is sort-enabled and active. You should override this default method if you want to customize the appearance of table columns. diff --git a/docs/FlexTable.md b/docs/FlexTable.md index 8d8098522..54a709ceb 100644 --- a/docs/FlexTable.md +++ b/docs/FlexTable.md @@ -7,22 +7,22 @@ This component expects explicit width, height, and padding parameters. ### Prop Types | Property | Type | Required? | Description | |:---|:---|:---:|:---| -| children | [FlexColumn](FlexColumn.md) | ✓ | One or more FlexColumns describing the data displayed in this row | +| children | [FlexColumn](FlexColumn.md) | ✓ | One or more FlexColumns describing the data displayed in this table | | className | String | | CSS class name | -| disableHeader | Boolean | | Disable rendering the header at all | +| disableHeader | Boolean | | Do not render the table header (only the rows) | | headerClassName | String | | CSS class to apply to all column headers | | headerHeight | Number | ✓ | Fixed height of header row | | height | Number | ✓ | Fixed/available height for out DOM element | | noRowsRenderer | | Function | Callback used to render placeholder content when :rowCount is 0 | | onHeaderClick | | Function | Callback invoked when a user clicks on a table header. `(dataKey: string, columnData: any): void` | -| onRowClick | | Function | Callback invoked when a user clicks on a table row. `({ index: number }): void` | -| onRowsRendered | | Function | Callback invoked with information about the slice of rows that were just rendered: `({ overscanStartIndex, overscanStopIndex, startIndex, stopIndex }): void` | +| onRowClick | | Function | Callback invoked when a user clicks on a table row. `({ index: number }): void` | +| onRowsRendered | | Function | Callback invoked with information about the slice of rows that were just rendered: `({ overscanStartIndex: number, overscanStopIndex: number, startIndex: number, stopIndex: number }): void` | | overscanRowCount | | Number | Number of rows to render above/below the visible bounds of the list. This can help reduce flickering during scrolling on certain browers/devices. | -| onScroll | Function | | Callback invoked whenever the scroll offset changes within the inner scrollable region: `({ clientHeight, scrollHeight, scrollTop }): void` | +| onScroll | Function | | Callback invoked whenever the scroll offset changes within the inner scrollable region: `({ clientHeight: number, scrollHeight: number: number, scrollTop: number }): void` | | rowClassName | String or Function | | CSS class to apply to all table rows (including the header row). This value may be either a static string or a function with the signature `({ index: number }): string`. Note that for the header row an index of `-1` is provided. | +| rowCount | Number | ✓ | Number of rows in table. | | rowGetter | Function | ✓ | Callback responsible for returning a data row given an index. `({ index: int }): any` | | rowHeight | Number or Function | ✓ | Either a fixed row height (number) or a function that returns the height of a row given its index: `({ index: number }): number` | -| rowCount | Number | ✓ | Number of rows in table. | | scrollToIndex | Number | | Row index to ensure visible (by forcefully scrolling if necessary) | | scrollTop | Number | | Vertical offset | | sort | Function | | Sort function to be called if a sortable header is clicked. `({ sortBy: string, sortDirection: SortDirection }): void` | diff --git a/docs/Grid.md b/docs/Grid.md index 09cf66392..1b410322f 100644 --- a/docs/Grid.md +++ b/docs/Grid.md @@ -7,15 +7,15 @@ Only a small number of cells are rendered based on the horizontal and vertical s ### Prop Types | Property | Type | Required? | Description | |:---|:---|:---:|:---| +| cellRangeRenderer | Function | | Responsible for rendering a group of cells given their index ranges.: `({ cellCache: Map, cellRenderer: Function, columnSizeAndPositionManager: CellSizeAndPositionManager, columnStartIndex: number, columnStopIndex: number, isScrolling: boolean, rowSizeAndPositionManager: CellSizeAndPositionManager, rowStartIndex: number, rowStopIndex: number }): Array`. [Learn more](#cellRangeRenderer) | | cellRenderer | Function | ✓ | Responsible for rendering a cell given an row and column index: `({ columnIndex: number, isScrolling: boolean, rowIndex: number }): PropTypes.node` | -| cellRangeRenderer | Function | ✓ | Responsible for rendering a group of cells given their index ranges.: `({ cellCache: Map, cellRenderer: Function, columnSizeAndPositionManager: CellSizeAndPositionManager, columnStartIndex: number, columnStopIndex: number, isScrolling: boolean, rowSizeAndPositionManager: CellSizeAndPositionManager, rowStartIndex: number, rowStopIndex: number }): Array` | | className | String | | Optional custom CSS class name to attach to root Grid element. | | columnCount | Number | ✓ | Number of columns in grid. | | columnWidth | Number or Function | ✓ | Either a fixed column width (number) or a function that returns the width of a column given its index: `({ index: number }): number` | | height | Number | ✓ | Height of Grid; this property determines the number of visible (vs virtualized) rows. | -| noContentRenderer | Function | | Optional renderer to be rendered inside the grid when either `rowCount` or `columnCount` is 0: `(): PropTypes.node` | -| onSectionRendered | Function | | Callback invoked with information about the section of the Grid that was just rendered: `({ columnOverscanStartIndex, columnOverscanStopIndex, columnStartIndex, columnStopIndex, rowOverscanStartIndex, rowOverscanStopIndex, rowStartIndex, rowStopIndex }): void` | -| onScroll | Function | | Callback invoked whenever the scroll offset changes within the inner scrollable region: `({ clientHeight, clientWidth, scrollHeight, scrollLeft, scrollTop, scrollWidth }): void` | +| noContentRenderer | Function | | Optional renderer to be rendered inside the grid when either `rowCount` or `columnCount` is empty: `(): PropTypes.node` | +| onSectionRendered | Function | | Callback invoked with information about the section of the Grid that was just rendered: `({ columnOverscanStartIndex: number, columnOverscanStopIndex: number, columnStartIndex: number, columnStopIndex: number, rowOverscanStartIndex: number, rowOverscanStopIndex: number, rowStartIndex: number, rowStopIndex: number }): void` | +| onScroll | Function | | Callback invoked whenever the scroll offset changes within the inner scrollable region: `({ clientHeight: number, clientWidth: number, scrollHeight: number, scrollLeft: number, scrollTop: number, scrollWidth: number }): void` | | overscanColumnCount | Number | | Number of columns to render before/after the visible slice of the grid. This can help reduce flickering during scrolling on certain browers/devices. | | overscanRowCount | Number | | Number of rows to render above/below the visible slice of the grid. This can help reduce flickering during scrolling on certain browers/devices. | | rowCount | Number | ✓ | Number of rows in grid. | diff --git a/docs/InfiniteLoader.md b/docs/InfiniteLoader.md index 432fc9863..ecef9d39f 100644 --- a/docs/InfiniteLoader.md +++ b/docs/InfiniteLoader.md @@ -6,25 +6,26 @@ High-order component that manages just-in-time fetching of data as a user scroll ### Prop Types | Property | Type | Required? | Description | |:---|:---|:---:|:---| -| children | Function | ✓ | Function responsible for rendering a virtualized component. This function should implement the following signature: `({ onRowsRendered, registerChild }) => PropTypes.element` | +| children | Function | ✓ | Function responsible for rendering a virtualized component. This function should implement the following signature: `({ onRowsRendered: Function, registerChild: Function }) => PropTypes.element` | | isRowLoaded | Function | ✓ | Function responsible for tracking the loaded state of each row. It should implement the following signature: `({ index: number }): boolean` | -| loadMoreRows | Function | ✓ | Callback to be invoked when more rows must be loaded. It should implement the following signature: `({ startIndex, stopIndex }): Promise`. The returned Promise should be resolved once row data has finished loading. It will be used to determine when to refresh the list with the newly-loaded data. This callback may be called multiple times in reaction to a single scroll event. | +| loadMoreRows | Function | ✓ | Callback to be invoked when more rows must be loaded. It should implement the following signature: `({ startIndex: number, stopIndex: number }): Promise`. The returned Promise should be resolved once row data has finished loading. It will be used to determine when to refresh the list with the newly-loaded data. This callback may be called multiple times in reaction to a single scroll event. | | minimumBatchSize | Number | | Minimum number of rows to be loaded at a time. This property can be used to batch requests to reduce HTTP requests. Defaults to `10`. | | rowCount | Number | ✓ | Number of rows in list; can be arbitrary high number if actual number is unknown. | -| threshold | Number | | Threshold at which to pre-fetch data. A threshold X means that data will start loading when a user scrolls within X rows. This value defaults to 15. | +| threshold | Number | | Threshold at which to pre-fetch data. A threshold X means that data will start loading when a user scrolls within X rows. Defaults to `15`. | ### Children function The child function is passed the following named parameters: | Parameter | Type | Description | -|:---|:---|:---:| +|:---|:---|:---| | onRowsRendered | Function | This function should be passed as the child's `onRowsRendered` property. It informs the loader when the user is scrolling. | | registerChild | Function | This function should be set as the child's `ref` property. It enables a set of rows to be refreshed once their data has finished loading. | ### Examples This example uses `InfiniteLoader` to prefetch rows in a `VirtualScroll` list as a user scrolls. +An interactive demo can be seen [here](https://bvaughn.github.io/react-virtualized/?component=InfiniteLoader). ```js import React from 'react'; diff --git a/docs/README.md b/docs/README.md index 0adcc860c..c75b12147 100644 --- a/docs/README.md +++ b/docs/README.md @@ -11,6 +11,7 @@ Documentation * [VirtualScroll](VirtualScroll.md) ### High-Order Components + * [ArrowKeyStepper](ArrowKeyStepper.md) * [AutoSizer](AutoSizer.md) * [CellMeasurer](CellMeasurer.md) @@ -24,3 +25,4 @@ Documentation * [Displaying items in reverse order](reverseList.md) * [Using AutoSizer](usingAutoSizer.md) * [Creating an infinite-loading list](creatingAnInfiniteLoadingList.md) +* [Displaying a reverse list](reverseList.md) diff --git a/docs/ScrollSync.md b/docs/ScrollSync.md index 349575ccf..ebc923b02 100644 --- a/docs/ScrollSync.md +++ b/docs/ScrollSync.md @@ -6,7 +6,7 @@ High order component that simplifies the process of synchronizing scrolling betw ### Prop Types | Property | Type | Required? | Description | |:---|:---|:---:|:---| -| children | Function | ✓ | Function responsible for rendering 2 or more virtualized components. This function should implement the following signature: `({ onScroll, scrollLeft, scrollTop }) => PropTypes.element` | +| children | Function | ✓ | Function responsible for rendering 2 or more virtualized components. [See below](#children-function) for details about this function's signature. | ### Children function diff --git a/docs/VirtualScroll.md b/docs/VirtualScroll.md index 94878537f..8a5f3238c 100644 --- a/docs/VirtualScroll.md +++ b/docs/VirtualScroll.md @@ -9,14 +9,14 @@ This component renders a virtualized list of elements with either fixed or dynam | className | String | | CSS class name | | height | Number | ✓ | Height constraint for list (determines how many actual rows are rendered) | | noRowsRenderer | Function | | Callback used to render placeholder content when `rowCount` is 0 | -| onRowsRendered | Function | | Callback invoked with information about the slice of rows that were just rendered: `({ overscanStartIndex, overscanStopIndex, startIndex, stopIndex }): void` | -| onScroll | Function | | Callback invoked whenever the scroll offset changes within the inner scrollable region: `({ clientHeight, scrollHeight, scrollTop }): void` | +| onRowsRendered | Function | | Callback invoked with information about the slice of rows that were just rendered: `({ overscanStartIndex: number, overscanStopIndex: number, startIndex: number, stopIndex: number }): void` | +| onScroll | Function | | Callback invoked whenever the scroll offset changes within the inner scrollable region: `({ clientHeight: number, scrollHeight: number, scrollTop: number }): void` | | overscanRowCount | Number | | Number of rows to render above/below the visible bounds of the list. This can help reduce flickering during scrolling on certain browers/devices. | | rowHeight | Number or Function | ✓ | Either a fixed row height (number) or a function that returns the height of a row given its index: `({ index: number }): number` | | rowRenderer | Function | ✓ | Responsbile for rendering a row given an index. Signature should look like `({ index: number, isScrolling: boolean }): React.PropTypes.node` | | rowCount | Number | ✓ | Number of rows in list. | | scrollToIndex | Number | | Row index to ensure visible (by forcefully scrolling if necessary) | -| scrollTop | Number | | Vertical offset | +| scrollTop | Number | | Forced vertical scroll offset; can be used to synchronize scrolling between components | | width | Number | ✓ | Width of the list | ### Public Methods diff --git a/docs/reverseList.md b/docs/reverseList.md index 99d5dc366..33fd8f2f0 100644 --- a/docs/reverseList.md +++ b/docs/reverseList.md @@ -1,9 +1,9 @@ Displaying Items in Reverse Order --------------- -A few people have asked how to display lists in reverse order (see issues #33, #54, #55, #47). - -The simplest way to do this to add items to the front of the list (`unshift`) instead of the end (`push`). (You can see a demo of the source code below [here](https://s3.amazonaws.com/brianvaughn/react-virtualized/reverse-list/index.html)) +Sometimes it is desirable to dislpay a list in reverse order. +The simplest way to do this is to add items to the front of the list (`unshift`) instead of the end (`push`). +Here is a high level template for doing this: ```js export default class Example extends Component { @@ -61,3 +61,5 @@ export default class Example extends Component { } } ``` + +You can see a demo of this [here](https://s3.amazonaws.com/brianvaughn/react-virtualized/reverse-list/index.html). From cd4289fda8d7f1c517794da1ccdae6ba8530bf67 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sun, 8 May 2016 09:48:19 -0700 Subject: [PATCH 49/60] Updated ignore files. --- .gitignore | 5 +++-- .npmignore | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 27ce11eb8..1df06a941 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .DS_Store -node_modules -npm-debug.log build coverage +dist +node_modules +npm-debug.log diff --git a/.npmignore b/.npmignore index 330439e46..1b201fe21 100644 --- a/.npmignore +++ b/.npmignore @@ -1,4 +1,5 @@ build +codemods coverage docs source From ce15f0efdd024d34858821616a649f4c2aced69e Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sun, 8 May 2016 09:48:25 -0700 Subject: [PATCH 50/60] Package version bump for upcoming release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 27b3237ea..b94eec7b0 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "description": "React components for efficiently rendering large, scrollable lists and tabular data", "author": "Brian Vaughn ", "user": "bvaughn", - "version": "6.3.2", + "version": "7.0.0", "homepage": "https://github.com/bvaughn/react-virtualized", "main": "dist/commonjs/index.js", "jsnext:main": "dist/es/index.js", From 4859604a8a1f6b03f6cc64b2324012e2ee5df4d0 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sun, 8 May 2016 09:48:30 -0700 Subject: [PATCH 51/60] Organized codemods folder. Fixed some warnings --- codemods/{ => 6-to-7}/rename-properties.js | 18 ++++++++++++++++-- codemods/utils.js | 16 ---------------- 2 files changed, 16 insertions(+), 18 deletions(-) rename codemods/{ => 6-to-7}/rename-properties.js (78%) delete mode 100644 codemods/utils.js diff --git a/codemods/rename-properties.js b/codemods/6-to-7/rename-properties.js similarity index 78% rename from codemods/rename-properties.js rename to codemods/6-to-7/rename-properties.js index 01abc7f24..58e377972 100644 --- a/codemods/rename-properties.js +++ b/codemods/6-to-7/rename-properties.js @@ -1,7 +1,7 @@ -import { attributeBelongsToReactVirtualizedElement } from './utils' +'use strict'; // Renames react-virtualized version 6.x properties to be version-7 compatible -export default function transformer (file, api) { +module.exports = function transformer (file, api) { const jscodeshift = api.jscodeshift let source = file.source @@ -24,6 +24,20 @@ export default function transformer (file, api) { return source } +const reactVirtualizedElementNames = [ + 'ArrowKeyStepper', + 'AutoSizer', + 'Collection', + 'ColumnSizer', + 'FlexTable', + 'Grid', + 'ScrollSync', + 'VirtualScroll' +] + +// @param path jscodeshift.JSXAttribute +const attributeBelongsToReactVirtualizedElement = path => reactVirtualizedElementNames.includes(path.parent.value.name.name) + // See https://github.com/bvaughn/react-virtualized/wiki/Version-7-Roadmap#clean-up-property-names const propertyRenameMap = { columnsCount: 'columnCount', diff --git a/codemods/utils.js b/codemods/utils.js deleted file mode 100644 index 1c864fa29..000000000 --- a/codemods/utils.js +++ /dev/null @@ -1,16 +0,0 @@ -const reactVirtualizedElementNames = [ - 'ArrowKeyStepper', - 'AutoSizer', - 'Collection', - 'ColumnSizer', - 'FlexTable', - 'Grid', - 'ScrollSync', - 'VirtualScroll' -] - -// @param path jscodeshift.JSXAttribute -export const attributeBelongsToReactVirtualizedElement = path => reactVirtualizedElementNames.includes(path.parent.value.name.name) - -// @param path jscodeshift.JSXIdentifier -export const isReactVirtualizedElement = path => reactVirtualizedElementNames.includes(path.value.name) From 45a5124d69e7dac3b33a2a0e8f9157d85bdadbaf Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sun, 8 May 2016 09:48:37 -0700 Subject: [PATCH 52/60] Changelog tweak --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c3dbe3e7..7e95fca27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,8 @@ Changelog ------------ # 7.0.0 -Version 7 changes are described in detail on the [Version 7 Roadmap wiki page](https://github.com/bvaughn/react-virtualized/wiki/Version-7-Roadmap).) +Version 7 changes are described in detail on the [Version 7 Roadmap wiki page](https://github.com/bvaughn/react-virtualized/wiki/Version-7-Roadmap). +Upgrade instructions and [jscodeshift](https://github.com/facebook/jscodeshift) mods can also be found there. ##### 6.3.2 Fixed edge-case bug in `Collection` where initial `scrollLeft` and `scrollTop` would not correctly adjust inner offsets. From d7496598b02e8308d847acb2c85afa88a8035913 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sun, 8 May 2016 09:50:29 -0700 Subject: [PATCH 53/60] Fixed lint issue with codemod --- codemods/6-to-7/rename-properties.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codemods/6-to-7/rename-properties.js b/codemods/6-to-7/rename-properties.js index 58e377972..9e6956af4 100644 --- a/codemods/6-to-7/rename-properties.js +++ b/codemods/6-to-7/rename-properties.js @@ -1,4 +1,4 @@ -'use strict'; +'use strict' // Renames react-virtualized version 6.x properties to be version-7 compatible module.exports = function transformer (file, api) { From 4b2fa3f2dca680f524ea4bbd54cc0c0ecffb6523 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sun, 8 May 2016 09:56:35 -0700 Subject: [PATCH 54/60] Small example/demo tweaks --- source/ArrowKeyStepper/ArrowKeyStepper.example.css | 1 + source/Collection/Collection.example.js | 2 +- source/VirtualScroll/VirtualScroll.example.js | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/source/ArrowKeyStepper/ArrowKeyStepper.example.css b/source/ArrowKeyStepper/ArrowKeyStepper.example.css index ee3d156bf..b1015f732 100644 --- a/source/ArrowKeyStepper/ArrowKeyStepper.example.css +++ b/source/ArrowKeyStepper/ArrowKeyStepper.example.css @@ -7,6 +7,7 @@ display: flex; align-items: center; justify-content: center; + text-align: center; border-right: 1px solid #e0e0e0; border-bottom: 1px solid #e0e0e0; } diff --git a/source/Collection/Collection.example.js b/source/Collection/Collection.example.js index 533738283..ef5836760 100644 --- a/source/Collection/Collection.example.js +++ b/source/Collection/Collection.example.js @@ -26,7 +26,7 @@ export default class CollectionExample extends Component { columnCount: this._getColumnCount(props.list.size), height: 300, scrollToCell: undefined, - showScrollingPlaceholder: true + showScrollingPlaceholder: false } this._columnYMap = [] diff --git a/source/VirtualScroll/VirtualScroll.example.js b/source/VirtualScroll/VirtualScroll.example.js index aab9627b2..6000986cb 100644 --- a/source/VirtualScroll/VirtualScroll.example.js +++ b/source/VirtualScroll/VirtualScroll.example.js @@ -22,7 +22,7 @@ export default class VirtualScrollExample extends Component { overscanRowCount: 0, rowCount: props.list.size, scrollToIndex: undefined, - showScrollingPlaceholder: true, + showScrollingPlaceholder: false, useDynamicRowHeight: false, virtualScrollHeight: 300, virtualScrollRowHeight: 50 From 2e1c3ad8dbf2258340a4af0ac8d0c39d5fa4d665 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sun, 8 May 2016 19:51:20 -0700 Subject: [PATCH 55/60] Added more docs about limitations of InfiniteLoader --- docs/InfiniteLoader.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/InfiniteLoader.md b/docs/InfiniteLoader.md index ecef9d39f..547eb5dc5 100644 --- a/docs/InfiniteLoader.md +++ b/docs/InfiniteLoader.md @@ -3,6 +3,10 @@ InfiniteLoader High-order component that manages just-in-time fetching of data as a user scrolls up or down in a list. +Note that this component is inteded to assist with row-loading. +As such it is best suited for use with `FlexTable` and `VirtualScroll` (although it can also be used with `Grid`). +This HOC is not compatible with the `Collection` component. + ### Prop Types | Property | Type | Required? | Description | |:---|:---|:---:|:---| From ef1282c29063f26c33f9c39132ed97a54887afae Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sun, 8 May 2016 19:51:40 -0700 Subject: [PATCH 56/60] Added FlexColumn :cellClassName to :className rename --- codemods/6-to-7/rename-properties.js | 1 + 1 file changed, 1 insertion(+) diff --git a/codemods/6-to-7/rename-properties.js b/codemods/6-to-7/rename-properties.js index 9e6956af4..0852d15ac 100644 --- a/codemods/6-to-7/rename-properties.js +++ b/codemods/6-to-7/rename-properties.js @@ -40,6 +40,7 @@ const attributeBelongsToReactVirtualizedElement = path => reactVirtualizedElemen // See https://github.com/bvaughn/react-virtualized/wiki/Version-7-Roadmap#clean-up-property-names const propertyRenameMap = { + cellClassName: 'className', columnsCount: 'columnCount', overscanColumnsCount: 'overscanColumnCount', overscanRowsCount: 'overscanRowCount', From c03701bd5e6c57c8ab50f0983067025bd3ab7feb Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sun, 8 May 2016 20:10:58 -0700 Subject: [PATCH 57/60] Added support for inline styles to FlexTable, VirtualScroll, Grid, and Collection. Updated docs and tests. --- docs/Collection.md | 1 + docs/FlexColumn.md | 3 +- docs/FlexTable.md | 5 ++- docs/Grid.md | 3 +- docs/VirtualScroll.md | 3 +- source/Collection/Collection.test.js | 8 ++++ source/Collection/CollectionView.js | 22 ++++++---- source/FlexTable/FlexColumn.js | 10 +++-- source/FlexTable/FlexTable.example.js | 2 +- source/FlexTable/FlexTable.js | 38 +++++++++++++---- source/FlexTable/FlexTable.test.js | 49 ++++++++++++++++------ source/Grid/Grid.js | 12 ++++-- source/Grid/Grid.test.js | 12 +++++- source/VirtualScroll/VirtualScroll.js | 8 +++- source/VirtualScroll/VirtualScroll.test.js | 18 +++++--- 15 files changed, 147 insertions(+), 47 deletions(-) diff --git a/docs/Collection.md b/docs/Collection.md index b9fcc2c9d..c4656f706 100644 --- a/docs/Collection.md +++ b/docs/Collection.md @@ -22,6 +22,7 @@ Unlike `Grid`, which renders checkerboard data, `Collection` can render arbitrar | scrollToCell | Number | | Cell index to ensure visible (by scrolling if necessary) | | scrollTop | Number | | Vertical offset | | sectionSize | Number | | Optionally override the size of the sections a Collection's cells are split into. This is an advanced option and should only be used for performance tuning purposes. | +| style | Object | | Optional custom inline style to attach to root Collection element. | | width | Number | ✓ | Width of Collection; this property determines the number of visible (vs virtualized) columns. | ### Public Methods diff --git a/docs/FlexColumn.md b/docs/FlexColumn.md index eb4ad5261..134d2f16c 100644 --- a/docs/FlexColumn.md +++ b/docs/FlexColumn.md @@ -6,9 +6,9 @@ Describes the header and cell contents of a table column. #### Prop Types | Property | Type | Required? | Description | |:---|:---|:---:|:---| -| cellClassName | String | | CSS class to apply to cell | | cellDataGetter | Function | | Callback responsible for returning a cell's data, given its `dataKey`. [Learn more](#celldatagetter) | | cellRenderer | Function | | Callback responsible for rendering a cell's contents. [Learn more](#cellrenderer) | +| className | String | | CSS class to apply to rendered cell container | | columnData | Object | | Additional data passed to this column's `cellDataGetter`. Use this object to relay action-creators or relational data. | | dataKey | any | ✓ | Uniquely identifies the row-data attribute correspnding to this cell (eg this might be "name" in an array of user objects). | | disableSort | Boolean | | If sort is enabled for the table at large, disable it for this column | @@ -19,6 +19,7 @@ Describes the header and cell contents of a table column. | label | String | | Header label for this column | | maxWidth | Number | | Maximum width of column; this property will only be used if :flexGrow is greater than 0 | | minWidth | Number | | Minimum width of column | +| style | Object | | Optional inline style to apply to rendered cell container | | width | Number | ✓ | Flex basis (width) for this column; This value can grow or shrink based on `flexGrow` and `flexShrink` properties | #### cellDataGetter diff --git a/docs/FlexTable.md b/docs/FlexTable.md index 54a709ceb..f5bc73a53 100644 --- a/docs/FlexTable.md +++ b/docs/FlexTable.md @@ -8,10 +8,11 @@ This component expects explicit width, height, and padding parameters. | Property | Type | Required? | Description | |:---|:---|:---:|:---| | children | [FlexColumn](FlexColumn.md) | ✓ | One or more FlexColumns describing the data displayed in this table | -| className | String | | CSS class name | +| className | String | | Optional custom CSS class name to attach to root `FlexTable` element. | | disableHeader | Boolean | | Do not render the table header (only the rows) | | headerClassName | String | | CSS class to apply to all column headers | | headerHeight | Number | ✓ | Fixed height of header row | +| headerStyle | Object | | Optional custom inline style to attach to table header columns. | | height | Number | ✓ | Fixed/available height for out DOM element | | noRowsRenderer | | Function | Callback used to render placeholder content when :rowCount is 0 | | onHeaderClick | | Function | Callback invoked when a user clicks on a table header. `(dataKey: string, columnData: any): void` | @@ -23,11 +24,13 @@ This component expects explicit width, height, and padding parameters. | rowCount | Number | ✓ | Number of rows in table. | | rowGetter | Function | ✓ | Callback responsible for returning a data row given an index. `({ index: int }): any` | | rowHeight | Number or Function | ✓ | Either a fixed row height (number) or a function that returns the height of a row given its index: `({ index: number }): number` | +| rowStyle | Object | | Optional custom inline style to attach to table rows. | | scrollToIndex | Number | | Row index to ensure visible (by forcefully scrolling if necessary) | | scrollTop | Number | | Vertical offset | | sort | Function | | Sort function to be called if a sortable header is clicked. `({ sortBy: string, sortDirection: SortDirection }): void` | | sortBy | String | | Data is currently sorted by this `dataKey` (if it is sorted at all) | | sortDirection | [SortDirection](SortDirection.md) | | Data is currently sorted in this direction (if it is sorted at all) | +| style | Object | | Optional custom inline style to attach to root `FlexTable` element. | | width | Number | ✓ | Width of the table | ### Public Methods diff --git a/docs/Grid.md b/docs/Grid.md index 1b410322f..887d82e05 100644 --- a/docs/Grid.md +++ b/docs/Grid.md @@ -9,7 +9,7 @@ Only a small number of cells are rendered based on the horizontal and vertical s |:---|:---|:---:|:---| | cellRangeRenderer | Function | | Responsible for rendering a group of cells given their index ranges.: `({ cellCache: Map, cellRenderer: Function, columnSizeAndPositionManager: CellSizeAndPositionManager, columnStartIndex: number, columnStopIndex: number, isScrolling: boolean, rowSizeAndPositionManager: CellSizeAndPositionManager, rowStartIndex: number, rowStopIndex: number }): Array`. [Learn more](#cellRangeRenderer) | | cellRenderer | Function | ✓ | Responsible for rendering a cell given an row and column index: `({ columnIndex: number, isScrolling: boolean, rowIndex: number }): PropTypes.node` | -| className | String | | Optional custom CSS class name to attach to root Grid element. | +| className | String | | Optional custom CSS class name to attach to root `Grid` element. | | columnCount | Number | ✓ | Number of columns in grid. | | columnWidth | Number or Function | ✓ | Either a fixed column width (number) or a function that returns the width of a column given its index: `({ index: number }): number` | | height | Number | ✓ | Height of Grid; this property determines the number of visible (vs virtualized) rows. | @@ -24,6 +24,7 @@ Only a small number of cells are rendered based on the horizontal and vertical s | scrollToColumn | Number | | Column index to ensure visible (by forcefully scrolling if necessary) | | scrollToRow | Number | | Row index to ensure visible (by forcefully scrolling if necessary) | | scrollTop | Number | | Vertical offset | +| style | Object | | Optional custom inline style to attach to root `Grid` element. | | width | Number | ✓ | Width of Grid; this property determines the number of visible (vs virtualized) columns. | ### Public Methods diff --git a/docs/VirtualScroll.md b/docs/VirtualScroll.md index 8a5f3238c..be1467cd7 100644 --- a/docs/VirtualScroll.md +++ b/docs/VirtualScroll.md @@ -6,7 +6,7 @@ This component renders a virtualized list of elements with either fixed or dynam ### Prop Types | Property | Type | Required? | Description | |:---|:---|:---:|:---| -| className | String | | CSS class name | +| className | String | | Optional custom CSS class name to attach to root `VirtualScroll` element. | | height | Number | ✓ | Height constraint for list (determines how many actual rows are rendered) | | noRowsRenderer | Function | | Callback used to render placeholder content when `rowCount` is 0 | | onRowsRendered | Function | | Callback invoked with information about the slice of rows that were just rendered: `({ overscanStartIndex: number, overscanStopIndex: number, startIndex: number, stopIndex: number }): void` | @@ -17,6 +17,7 @@ This component renders a virtualized list of elements with either fixed or dynam | rowCount | Number | ✓ | Number of rows in list. | | scrollToIndex | Number | | Row index to ensure visible (by forcefully scrolling if necessary) | | scrollTop | Number | | Forced vertical scroll offset; can be used to synchronize scrolling between components | +| style | Object | | Optional custom inline style to attach to root `VirtualScroll` element. | | width | Number | ✓ | Width of the list | ### Public Methods diff --git a/source/Collection/Collection.test.js b/source/Collection/Collection.test.js index 0a7ce6b87..99e24f862 100644 --- a/source/Collection/Collection.test.js +++ b/source/Collection/Collection.test.js @@ -32,6 +32,7 @@ describe('Collection', () => { scrollLeft, scrollToCell, scrollTop, + style, width = SECTION_SIZE * 2 } = {}) { function defaultCellSizeAndPositionGetter ({ index }) { @@ -55,6 +56,7 @@ describe('Collection', () => { scrollLeft={scrollLeft} scrollToCell={scrollToCell} scrollTop={scrollTop} + style={style} width={width} /> ) @@ -296,6 +298,12 @@ describe('Collection', () => { const rendered = findDOMNode(render(getMarkup({ className: 'foo' }))) expect(rendered.className).toContain('foo') }) + + it('should use a custom :style if specified', () => { + const style = { backgroundColor: 'red' } + const rendered = findDOMNode(render(getMarkup({ style }))) + expect(rendered.style.backgroundColor).toEqual('red') + }) }) describe('onScroll', () => { diff --git a/source/Collection/CollectionView.js b/source/Collection/CollectionView.js index ed9ccfee9..99039df51 100644 --- a/source/Collection/CollectionView.js +++ b/source/Collection/CollectionView.js @@ -84,6 +84,11 @@ export default class CollectionView extends Component { */ scrollTop: PropTypes.number, + /** + * Optional custom inline style to attach to root Collection element. + */ + style: PropTypes.object, + /** * Width of Collection; this property determines the number of visible (vs virtualized) columns. */ @@ -94,7 +99,8 @@ export default class CollectionView extends Component { 'aria-label': 'grid', noContentRenderer: () => null, onScroll: () => null, - onSectionRendered: () => null + onSectionRendered: () => null, + style: {} }; constructor (props, context) { @@ -263,6 +269,7 @@ export default class CollectionView extends Component { className, height, noContentRenderer, + style, width } = this.props @@ -286,19 +293,20 @@ export default class CollectionView extends Component { width: totalWidth } = cellLayoutManager.getTotalSize() - const gridStyle = { - height: height, - width: width + const collectionStyle = { + ...style, + height, + width } // Force browser to hide scrollbars when we know they aren't necessary. // Otherwise once scrollbars appear they may not disappear again. // For more info see issue #116 if (totalHeight <= height) { - gridStyle.overflowY = 'hidden' + collectionStyle.overflowY = 'hidden' } if (totalWidth <= width) { - gridStyle.overflowX = 'hidden' + collectionStyle.overflowX = 'hidden' } return ( @@ -308,7 +316,7 @@ export default class CollectionView extends Component { className={cn('Collection', className)} onScroll={this._onScroll} role='grid' - style={gridStyle} + style={collectionStyle} tabIndex={0} > {childrenToDisplay.length > 0 && diff --git a/source/FlexTable/FlexColumn.js b/source/FlexTable/FlexColumn.js index 36a7675f7..20a120d2e 100644 --- a/source/FlexTable/FlexColumn.js +++ b/source/FlexTable/FlexColumn.js @@ -12,6 +12,7 @@ export default class Column extends Component { static defaultProps = { cellDataGetter: defaultCellDataGetter, cellRenderer: defaultCellRenderer, + cellStyle: {}, flexGrow: 0, flexShrink: 1, headerRenderer: defaultHeaderRenderer @@ -21,9 +22,6 @@ export default class Column extends Component { /** Optional aria-label value to set on the column header */ 'aria-label': PropTypes.string, - /** Optional CSS class to apply to cell */ - cellClassName: PropTypes.string, - /** * Callback responsible for returning a cell's data, given its :dataKey * ({ columnData: any, dataKey: string, rowData: any }): any @@ -36,6 +34,9 @@ export default class Column extends Component { */ cellRenderer: PropTypes.func, + /** Optional CSS class to apply to cell */ + className: PropTypes.string, + /** Optional additional data passed to this column's :cellDataGetter */ columnData: PropTypes.object, @@ -69,6 +70,9 @@ export default class Column extends Component { /** Minimum width of column. */ minWidth: PropTypes.number, + /** Optional inline style to apply to cell */ + style: PropTypes.object, + /** Flex basis (width) for this column; This value can grow or shrink based on :flexGrow and :flexShrink properties. */ width: PropTypes.number.isRequired } diff --git a/source/FlexTable/FlexTable.example.js b/source/FlexTable/FlexTable.example.js index 2eb2f19d6..5596b7079 100644 --- a/source/FlexTable/FlexTable.example.js +++ b/source/FlexTable/FlexTable.example.js @@ -203,7 +203,7 @@ export default class FlexTableExample extends Component { disableSort label='The description label is really long so that it will be truncated' dataKey='random' - cellClassName={styles.exampleColumn} + className={styles.exampleColumn} cellRenderer={ ({ cellData, columnData, dataKey, rowData, rowIndex }) => cellData } diff --git a/source/FlexTable/FlexTable.js b/source/FlexTable/FlexTable.js index 4c8829d31..20a08d0f1 100644 --- a/source/FlexTable/FlexTable.js +++ b/source/FlexTable/FlexTable.js @@ -49,6 +49,9 @@ export default class FlexTable extends Component { */ onHeaderClick: PropTypes.func, + /** Optional custom inline style to attach to table header columns. */ + headerStyle: PropTypes.object, + /** * Callback invoked when a user clicks on a table row. * ({ index: number }): void @@ -96,6 +99,9 @@ export default class FlexTable extends Component { /** Number of rows in table. */ rowCount: PropTypes.number.isRequired, + /** Optional custom inline style to attach to table rows. */ + rowStyle: PropTypes.object, + /** Row index to ensure visible (by forcefully scrolling if necessary) */ scrollToIndex: PropTypes.number, @@ -114,6 +120,9 @@ export default class FlexTable extends Component { /** FlexTable data is currently sorted in this direction (if it is sorted at all) */ sortDirection: PropTypes.oneOf([SortDirection.ASC, SortDirection.DESC]), + /** Optional inline style */ + style: PropTypes.object, + /** Width of list */ width: PropTypes.number.isRequired } @@ -121,10 +130,13 @@ export default class FlexTable extends Component { static defaultProps = { disableHeader: false, headerHeight: 0, + headerStyle: {}, noRowsRenderer: () => null, onRowsRendered: () => null, onScroll: () => null, - overscanRowCount: 10 + overscanRowCount: 10, + rowStyle: {}, + style: {} } constructor (props) { @@ -163,8 +175,10 @@ export default class FlexTable extends Component { rowClassName, rowHeight, rowCount, + rowStyle, scrollToIndex, scrollTop, + style, width } = this.props const { scrollbarWidth } = this.state @@ -185,11 +199,13 @@ export default class FlexTable extends Component { return (
{!disableHeader && (
{ } function getMarkup ({ - cellRenderer, cellDataGetter, + cellRenderer, + cellStyle, className, columnData = { data: 123 }, disableSort = false, headerClassName, headerHeight = 20, - headerRenderer = undefined, + headerRenderer, + headerStyle, height = 100, - maxWidth = undefined, - minWidth = undefined, - noRowsRenderer = undefined, - onHeaderClick = undefined, - onRowClick = undefined, - onRowsRendered = undefined, - onScroll = undefined, + maxWidth, + minWidth, + noRowsRenderer, + onHeaderClick, + onRowClick, + onRowsRendered, + onScroll, overscanRowCount = 0, - rowClassName = undefined, + rowClassName, + rowCount = list.size, rowGetter = immutableRowGetter, rowHeight = 10, - rowCount = list.size, + rowStyle, scrollToIndex, scrollTop, sort, sortBy, sortDirection, + style, width = 100 } = {}) { return ( @@ -62,6 +66,7 @@ describe('FlexTable', () => { className={className} headerClassName={headerClassName} headerHeight={headerHeight} + headerStyle={headerStyle} height={height} noRowsRenderer={noRowsRenderer} onHeaderClick={onHeaderClick} @@ -70,14 +75,16 @@ describe('FlexTable', () => { onScroll={onScroll} overscanRowCount={overscanRowCount} rowClassName={rowClassName} + rowCount={rowCount} rowGetter={rowGetter} rowHeight={rowHeight} - rowCount={rowCount} + rowStyle={rowStyle} scrollToIndex={scrollToIndex} scrollTop={scrollTop} sort={sort} sortBy={sortBy} sortDirection={sortDirection} + style={style} width={width} > { cellDataGetter={cellDataGetter} headerRenderer={headerRenderer} disableSort={disableSort} + style={cellStyle} /> { expect(node.querySelectorAll('.bar').length).toEqual(2) expect(node.querySelectorAll('.baz').length).toEqual(9) }) + + it('should use custom :styles if specified', () => { + const cellStyle = { backgroundColor: 'red' } + const headerStyle = { backgroundColor: 'blue' } + const rowStyle = { backgroundColor: 'green' } + const style = { backgroundColor: 'orange' } + const node = findDOMNode(render(getMarkup({ + cellStyle, + headerStyle, + rowStyle, + style + }))) + expect(node.querySelector('.FlexTable__rowColumn').style.backgroundColor).toEqual('red') + expect(node.querySelector('.FlexTable__headerColumn').style.backgroundColor).toEqual('blue') + expect(node.querySelector('.FlexTable__row').style.backgroundColor).toEqual('green') + expect(node.style.backgroundColor).toEqual('orange') + }) }) describe('overscanRowCount', () => { diff --git a/source/Grid/Grid.js b/source/Grid/Grid.js index 2f414e88b..5bc225011 100644 --- a/source/Grid/Grid.js +++ b/source/Grid/Grid.js @@ -147,6 +147,9 @@ export default class Grid extends Component { */ scrollToRow: PropTypes.number, + /** Optional inline style */ + style: PropTypes.object, + /** * Width of Grid; this property determines the number of visible (vs virtualized) columns. */ @@ -155,6 +158,7 @@ export default class Grid extends Component { static defaultProps = { 'aria-label': 'grid', + cellRangeRenderer: defaultCellRangeRenderer, estimatedColumnSize: 100, estimatedRowSize: 30, noContentRenderer: () => null, @@ -162,7 +166,7 @@ export default class Grid extends Component { onSectionRendered: () => null, overscanColumnCount: 0, overscanRowCount: 10, - cellRangeRenderer: defaultCellRangeRenderer + style: {} }; constructor (props, context) { @@ -384,6 +388,7 @@ export default class Grid extends Component { overscanColumnCount, overscanRowCount, rowCount, + style, width } = this.props @@ -447,8 +452,9 @@ export default class Grid extends Component { } const gridStyle = { - height: height, - width: width + ...style, + height, + width } const totalColumnsWidth = this._columnSizeAndPositionManager.getTotalSize() diff --git a/source/Grid/Grid.test.js b/source/Grid/Grid.test.js index de0d4e02a..0988d10d4 100644 --- a/source/Grid/Grid.test.js +++ b/source/Grid/Grid.test.js @@ -42,10 +42,11 @@ describe('Grid', () => { overscanRowCount = 0, rowHeight = 20, rowCount = NUM_ROWS, - scrollLeft = undefined, + scrollLeft, scrollToColumn, scrollToRow, - scrollTop = undefined, + scrollTop, + style, width = 200 } = {}) { return ( @@ -69,6 +70,7 @@ describe('Grid', () => { scrollToColumn={scrollToColumn} scrollToRow={scrollToRow} scrollTop={scrollTop} + style={style} width={width} /> ) @@ -433,6 +435,12 @@ describe('Grid', () => { const rendered = findDOMNode(render(getMarkup({ className: 'foo' }))) expect(rendered.className).toContain('foo') }) + + it('should use a custom :style if specified', () => { + const style = { backgroundColor: 'red' } + const rendered = findDOMNode(render(getMarkup({ style }))) + expect(rendered.style.backgroundColor).toEqual('red') + }) }) describe('onScroll', () => { diff --git a/source/VirtualScroll/VirtualScroll.js b/source/VirtualScroll/VirtualScroll.js index d23f1ea07..9f87d60cf 100644 --- a/source/VirtualScroll/VirtualScroll.js +++ b/source/VirtualScroll/VirtualScroll.js @@ -62,6 +62,9 @@ export default class VirtualScroll extends Component { /** Vertical offset. */ scrollTop: PropTypes.number, + /** Optional inline style */ + style: PropTypes.object, + /** Width of list */ width: PropTypes.number.isRequired } @@ -70,7 +73,8 @@ export default class VirtualScroll extends Component { noRowsRenderer: () => null, onRowsRendered: () => null, onScroll: () => null, - overscanRowCount: 10 + overscanRowCount: 10, + style: {} } /** See Grid#recomputeGridSize */ @@ -91,6 +95,7 @@ export default class VirtualScroll extends Component { rowCount, scrollToIndex, scrollTop, + style, width } = this.props @@ -121,6 +126,7 @@ export default class VirtualScroll extends Component { rowCount={rowCount} scrollToRow={scrollToIndex} scrollTop={scrollTop} + style={style} width={width} /> ) diff --git a/source/VirtualScroll/VirtualScroll.test.js b/source/VirtualScroll/VirtualScroll.test.js index e75081ecf..b50169863 100644 --- a/source/VirtualScroll/VirtualScroll.test.js +++ b/source/VirtualScroll/VirtualScroll.test.js @@ -15,14 +15,15 @@ describe('VirtualScroll', () => { function getMarkup ({ className, height = 100, - noRowsRenderer = undefined, - onRowsRendered = undefined, - onScroll = undefined, + noRowsRenderer, + onRowsRendered, + onScroll, overscanRowCount = 0, rowHeight = 10, rowCount = rendered.size, - scrollToIndex = undefined, - scrollTop = undefined, + scrollToIndex, + scrollTop, + style, width = 100 } = {}) { function rowRenderer ({ index }) { @@ -49,6 +50,7 @@ describe('VirtualScroll', () => { rowCount={rowCount} scrollToIndex={scrollToIndex} scrollTop={scrollTop} + style={style} width={width} /> ) @@ -240,6 +242,12 @@ describe('VirtualScroll', () => { const node = findDOMNode(render(getMarkup({ className: 'foo' }))) expect(node.className).toContain('foo') }) + + it('should use a custom :style if specified', () => { + const style = { backgroundColor: 'red' } + const rendered = findDOMNode(render(getMarkup({ style }))) + expect(rendered.style.backgroundColor).toEqual('red') + }) }) describe('overscanRowCount', () => { From 52b77d24e4b46967f15d759ffcf48d1f62b6c6e3 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sun, 8 May 2016 20:22:02 -0700 Subject: [PATCH 58/60] Small styling tweak to playground.html --- playground/chat.html | 1 + 1 file changed, 1 insertion(+) diff --git a/playground/chat.html b/playground/chat.html index 5896a2c10..f28f845b2 100644 --- a/playground/chat.html +++ b/playground/chat.html @@ -13,6 +13,7 @@ padding: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; font-size: 12px; + overflow: hidden } * { box-sizing: border-box; From bac4b3a5bfa75a7126312b55e6e2a113b344395f Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sun, 8 May 2016 20:37:54 -0700 Subject: [PATCH 59/60] Updated package to rc1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b94eec7b0..55b9784d4 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "description": "React components for efficiently rendering large, scrollable lists and tabular data", "author": "Brian Vaughn ", "user": "bvaughn", - "version": "7.0.0", + "version": "7.0.0-rc.1", "homepage": "https://github.com/bvaughn/react-virtualized", "main": "dist/commonjs/index.js", "jsnext:main": "dist/es/index.js", From c35495a50cd0dd34c7e0d14bcf4eaa8a4925a2aa Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Mon, 9 May 2016 07:02:46 -0700 Subject: [PATCH 60/60] Updated package.json to v7.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 55b9784d4..b94eec7b0 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "description": "React components for efficiently rendering large, scrollable lists and tabular data", "author": "Brian Vaughn ", "user": "bvaughn", - "version": "7.0.0-rc.1", + "version": "7.0.0", "homepage": "https://github.com/bvaughn/react-virtualized", "main": "dist/commonjs/index.js", "jsnext:main": "dist/es/index.js",