)
}
-
- _updateUseDynamicRowHeight (value) {
- this.setState({
- useDynamicRowHeight: value
- })
- }
}
diff --git a/source/VirtualScroll/VirtualScroll.js b/source/VirtualScroll/VirtualScroll.js
index 21d431290..9f87d60cf 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.
@@ -46,15 +46,15 @@ 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,
- /** 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. */
- rowsCount: PropTypes.number.isRequired,
+ rowCount: PropTypes.number.isRequired,
/** Row index to ensure visible (by forcefully scrolling if necessary) */
scrollToIndex: PropTypes.number,
@@ -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,12 +73,11 @@ export default class VirtualScroll extends Component {
noRowsRenderer: () => null,
onRowsRendered: () => null,
onScroll: () => null,
- overscanRowsCount: 10
+ overscanRowCount: 10,
+ style: {}
}
- /**
- * See Grid#recomputeGridSize
- */
+ /** See Grid#recomputeGridSize */
recomputeRowHeights () {
this.refs.Grid.recomputeGridSize()
}
@@ -89,10 +91,11 @@ export default class VirtualScroll extends Component {
onScroll,
rowHeight,
rowRenderer,
- overscanRowsCount,
- rowsCount,
+ overscanRowCount,
+ rowCount,
scrollToIndex,
scrollTop,
+ style,
width
} = this.props
@@ -104,7 +107,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,12 +117,16 @@ export default class VirtualScroll extends Component {
startIndex: rowStartIndex,
stopIndex: rowStopIndex
})}
- overscanRowsCount={overscanRowsCount}
- renderCell={({ columnIndex, rowIndex }) => rowRenderer(rowIndex)}
+ overscanRowCount={overscanRowCount}
+ cellRenderer={({ columnIndex, isScrolling, rowIndex }) => rowRenderer({
+ index: rowIndex,
+ isScrolling
+ })}
rowHeight={rowHeight}
- rowsCount={rowsCount}
+ 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 235df1118..b50169863 100644
--- a/source/VirtualScroll/VirtualScroll.test.js
+++ b/source/VirtualScroll/VirtualScroll.test.js
@@ -15,17 +15,18 @@ describe('VirtualScroll', () => {
function getMarkup ({
className,
height = 100,
- noRowsRenderer = undefined,
- onRowsRendered = undefined,
- onScroll = undefined,
- overscanRowsCount = 0,
+ noRowsRenderer,
+ onRowsRendered,
+ onScroll,
+ overscanRowCount = 0,
rowHeight = 10,
- rowsCount = rendered.size,
- scrollToIndex = undefined,
- scrollTop = undefined,
+ rowCount = rendered.size,
+ scrollToIndex,
+ scrollTop,
+ style,
width = 100
} = {}) {
- function rowRenderer (index) {
+ function rowRenderer ({ index }) {
return (
{
noRowsRenderer={noRowsRenderer}
onRowsRendered={onRowsRendered}
onScroll={onScroll}
- overscanRowsCount={overscanRowsCount}
+ overscanRowCount={overscanRowCount}
rowHeight={rowHeight}
rowRenderer={rowRenderer}
- rowsCount={rowsCount}
+ rowCount={rowCount}
scrollToIndex={scrollToIndex}
scrollTop={scrollTop}
+ style={style}
width={width}
/>
)
@@ -61,7 +63,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 +119,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('')
})
@@ -240,9 +242,15 @@ 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('overscanRowsCount', () => {
+ describe('overscanRowCount', () => {
it('should not overscan by default', () => {
let overscanStartIndex, overscanStopIndex, startIndex, stopIndex
render(getMarkup({
@@ -256,7 +264,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 +277,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 +289,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)
@@ -314,8 +322,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
@@ -326,17 +333,23 @@ 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
}
const component = render(getMarkup({
rowHeight,
- rowsCount: 50
+ rowCount: 50
}))
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)
})
})
})
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' &&
cellSizes[index]
+ size: ({ index }) => cellSizes[index]
})
}
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])
+ })
})
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}`)