Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

react-virtualized

Package Overview
Dependencies
Maintainers
1
Versions
296
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-virtualized - npm Package Compare versions

Comparing version 4.4.3 to 4.5.0

4

CHANGELOG.md
Changelog
------------
#### 4.5.0
Added `onScroll` callback to `Grid`, `FlexTable`, and `VirtualScroll`.
Added `scrollToCell` method to `Grid` and `scrollToRow` to `FlexTable`, and `VirtualScroll`.
#### 4.4.3

@@ -5,0 +9,0 @@ Added `-ms-flex` and `-webkit-flex` browser prefixes to `FlexTable` cells.

@@ -21,2 +21,3 @@ FlexTable

| onRowsRendered | | Function | Callback invoked with information about the slice of rows that were just rendered: `({ startIndex, stopIndex }): void` |
| onScroll | Function | | Callback invoked whenever the scroll offset changes within the inner scrollable region: `({ 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. |

@@ -38,6 +39,14 @@ | rowGetter | Function | ✓ | Callback responsible for returning a data row given an index. `(index: int): any` |

##### scrollToRow
##### scrollToRow(rowIndex)
Scroll the list to ensure the row at the specified index is visible. This method exists so that a user can forcefully scroll to the same row twice. (The `scrollToIndex` property would not change in that case and so it would not be picked up by VirtualScroll.)
##### setScrollTop(scrollTop)
Set the `scrollTop` position within the inner scroll container.
Normally it is best to let `FlexTable` manage this properties or to use a method like `scrollToRow`.
This method enables `FlexTable` to be scroll-synced to another react-virtualized component though.
It is appropriate to use in that case.
### Class names

@@ -44,0 +53,0 @@

@@ -16,2 +16,3 @@ Grid

| onSectionRendered | Function | | Callback invoked with information about the section of the Grid that was just rendered: `({ columnStartIndex, columnStopIndex, rowStartIndex, rowStopIndex }): void` |
| onScroll | Function | | Callback invoked whenever the scroll offset changes within the inner scrollable region: `({ scrollLeft, scrollTop }): void` |
| renderCell | Function | ✓ | Responsible for rendering a cell given an row and column index: `({ columnIndex: number, rowIndex: number }): PropTypes.node` |

@@ -33,3 +34,3 @@ | rowsCount | Number | ✓ | Number of rows in grid. |

##### scrollToCell
##### scrollToCell({ scrollToColumn, scrollToRow })

@@ -40,2 +41,10 @@ Updates the Grid to ensure the cell at the specified row and column indices is visible.

##### setScrollPosition({ scrollLeft, scrollTop })
Set the `scrollLeft` and `scrollTop` position within the inner scroll container.
Normally it is best to let `Grid` manage these properties or to use a method like `scrollToCell`.
This method enables a `Grid` to be scroll-synced to another react-virtualized component though.
It is appropriate to use in that case.
### Class names

@@ -42,0 +51,0 @@

@@ -13,2 +13,3 @@ VirtualScroll

| onRowsRendered | | Function | Callback invoked with information about the slice of rows that were just rendered: `({ startIndex, stopIndex }): void` |
| onScroll | Function | | Callback invoked whenever the scroll offset changes within the inner scrollable region: `({ scrollTop }): void` |
| 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` |

@@ -26,6 +27,15 @@ | rowRenderer | Function | ✓ | Responsbile for rendering a row given an index. Signature should look like `(index: number): React.PropTypes.node` |

##### scrollToRow
##### scrollToRow(rowIndex)
Scroll the list to ensure the row at the specified index is visible. This method exists so that a user can forcefully scroll to the same row twice. (The `scrollToIndex` property would not change in that case and so it would not be picked up by VirtualScroll.)
##### setScrollTop(scrollTop)
Set the `scrollTop` position within the inner scroll container.
Normally it is best to let `VirtualScroll` manage this properties or to use a method like `scrollToRow`.
This method enables `VirtualScroll` to be scroll-synced to another react-virtualized component though.
It is appropriate to use in that case.
### Class names

@@ -32,0 +42,0 @@

2

package.json

@@ -6,3 +6,3 @@ {

"user": "bvaughn",
"version": "4.4.3",
"version": "4.5.0",
"scripts": {

@@ -9,0 +9,0 @@ "build": "npm run build:css && npm run build:dist && npm run build:demo",

@@ -70,2 +70,8 @@ /** @flow */

/**
* Callback invoked whenever the scroll offset changes within the inner scrollable region.
* This callback can be used to sync scrolling between lists, tables, or grids.
* ({ scrollTop }): void
*/
onScroll: PropTypes.func.isRequired,
/**
* Optional CSS class to apply to all table rows (including the header row).

@@ -108,2 +114,3 @@ * This property can be a CSS class name (string) or a function that returns a class name.

onRowsRendered: () => null,
onScroll: () => null,
verticalPadding: 0

@@ -132,2 +139,12 @@ }

/**
* Set the :scrollTop position within the inner scroll container.
* Normally it is best to let FlexTable manage this properties or to use a method like :scrollToRow.
* This method enables FlexTable to be scroll-synced to another react-virtualized component though.
* It is appropriate to use in that case.
*/
setScrollTop (scrollTop) {
this.refs.VirtualScroll.setScrollTop(scrollTop)
}
render () {

@@ -141,2 +158,3 @@ const {

onRowsRendered,
onScroll,
rowClassName,

@@ -178,2 +196,3 @@ rowHeight,

onRowsRendered={onRowsRendered}
onScroll={onScroll}
rowHeight={rowHeight}

@@ -180,0 +199,0 @@ rowRenderer={rowRenderer}

import React from 'react'
import { findDOMNode, render } from 'react-dom'
import { Simulate } from 'react-addons-test-utils'
import TestUtils from 'react-addons-test-utils'
import { renderIntoDocument, Simulate } from 'react-addons-test-utils'
import Immutable from 'immutable'

@@ -38,22 +37,22 @@ import FlexColumn from './FlexColumn'

function getMarkup ({
cellRenderer = undefined,
cellDataGetter = undefined,
className = undefined,
cellRenderer,
cellDataGetter,
className,
disableSort = false,
headerClassName = undefined,
headerClassName,
headerHeight = 20,
height = 100,
noRowsRenderer = undefined,
onHeaderClick = undefined,
onRowClick = undefined,
onRowsRendered = undefined,
rowClassName = undefined,
noRowsRenderer,
onHeaderClick,
onRowClick,
onRowsRendered,
rowClassName,
rowGetter = immutableRowGetter,
rowHeight = 10,
rowsCount = list.size,
scrollToIndex = undefined,
sort = undefined,
sortBy = undefined,
sortDirection = undefined,
styleSheet = undefined,
scrollToIndex,
sort,
sortBy,
sortDirection,
styleSheet,
width = 100

@@ -100,3 +99,3 @@ } = {}) {

function renderTable (props) {
const flexTable = TestUtils.renderIntoDocument(getMarkup(props))
const flexTable = renderIntoDocument(getMarkup(props))

@@ -110,3 +109,3 @@ // Allow initial setImmediate() to set :scrollTop

// Use ReactDOM.render for certain tests so that props changes will update the existing component
// TestUtils.renderIntoDocument creates a new component/instance each time
// renderIntoDocument creates a new component/instance each time
function renderOrUpdateTable (props) {

@@ -502,2 +501,5 @@ let flexTable = render(getMarkup(props), node)

})
// TODO Add tests for :scrollToRow and :setScrollTop.
// This probably requires the creation of an inner test-only class with refs.
})

@@ -41,3 +41,5 @@ /** @flow */

this._onScrollToRowChange = this._onScrollToRowChange.bind(this)
this._renderCell = this._renderCell.bind(this)
this._renderBodyCell = this._renderBodyCell.bind(this)
this._renderHeaderCell = this._renderHeaderCell.bind(this)
this._renderLeftSideCell = this._renderLeftSideCell.bind(this)
}

@@ -65,2 +67,11 @@

<ContentBoxParagraph>
Renders tabular data with virtualization along the vertical and horizontal axes.
Row heights and column widths must be known ahead of time and specified as properties.
</ContentBoxParagraph>
<ContentBoxParagraph>
This example also shows 3 <code>Grid</code> components with synchronized scrolling to demonstrate fixed headers and columns.
</ContentBoxParagraph>
<ContentBoxParagraph>
<label className={styles.checkboxLabel}>

@@ -118,19 +129,59 @@ <input

<div>
<AutoSizer disableHeight>
<Grid
ref='Grid'
className={styles.Grid}
columnWidth={this._getColumnWidth}
columnsCount={columnsCount}
height={height}
noContentRenderer={this._noContentRenderer}
renderCell={this._renderCell}
rowHeight={useDynamicRowHeights ? this._getRowHeight : rowHeight}
rowsCount={rowsCount}
scrollToColumn={scrollToColumn}
scrollToRow={scrollToRow}
width={0}
/>
</AutoSizer>
<div className={styles.GridRow}>
<div
className={styles.LeftSideGridContainer}
style={{ marginTop: rowHeight }}
>
<Grid
ref='LeftSideGrid'
className={styles.LeftSideGrid}
columnWidth={50}
columnsCount={1}
height={height}
onScroll={({ scrollLeft, scrollTop }) => this.refs.BodyGrid.setScrollPosition({ scrollTop })}
renderCell={this._renderLeftSideCell}
rowHeight={rowHeight}
rowsCount={rowsCount}
width={50}
/>
</div>
<div className={styles.GridColumn}>
<div>
<AutoSizer disableHeight>
<Grid
ref='HeaderGrid'
className={styles.HeaderGrid}
columnWidth={this._getColumnWidth}
columnsCount={columnsCount}
height={rowHeight}
renderCell={this._renderHeaderCell}
rowHeight={rowHeight}
rowsCount={1}
width={0}
/>
</AutoSizer>
</div>
<div>
<AutoSizer disableHeight>
<Grid
ref='BodyGrid'
className={styles.BodyGrid}
columnWidth={this._getColumnWidth}
columnsCount={columnsCount}
height={height}
noContentRenderer={this._noContentRenderer}
onScroll={({ scrollLeft, scrollTop }) => {
this.refs.LeftSideGrid.setScrollPosition({ scrollTop })
this.refs.HeaderGrid.setScrollPosition({ scrollLeft })
}}
renderCell={this._renderBodyCell}
rowHeight={useDynamicRowHeights ? this._getRowHeight : rowHeight}
rowsCount={rowsCount}
scrollToColumn={scrollToColumn}
scrollToRow={scrollToRow}
width={0}
/>
</AutoSizer>
</div>
</div>
</div>

@@ -144,6 +195,4 @@ </ContentBox>

case 0:
return 40
return 100
case 1:
return 100
case 2:
return 300

@@ -172,3 +221,3 @@ default:

_renderCell ({ columnIndex, rowIndex }) {
_renderBodyCell ({ columnIndex, rowIndex }) {
const { list } = this.props

@@ -182,8 +231,5 @@ const rowClass = this._getRowClassName(rowIndex)

case 0:
content = datum.name.charAt(0)
content = datum.name
break
case 1:
content = datum.name
break
case 2:
content = datum.random

@@ -197,8 +243,46 @@ break

const classNames = cn(rowClass, styles.cell, {
[styles.centeredCell]: columnIndex > 2,
[styles.centeredCell]: columnIndex > 2
})
return (
<div className={classNames}>
{content}
</div>
)
}
_renderHeaderCell ({ columnIndex, rowIndex }) {
let content
switch (columnIndex) {
case 0:
content = 'Name'
break
case 1:
content = 'Lorem Ipsum'
break
default:
content = `C${columnIndex}`
break
}
const classNames = cn(styles.headerCell, {
[styles.centeredCell]: columnIndex > 2
})
return (
<div className={classNames}>
{content}
</div>
)
}
_renderLeftSideCell ({ columnIndex, rowIndex }) {
const { list } = this.props
const datum = list.get(rowIndex)
const classNames = cn(styles.cell, {
[styles.letterCell]: columnIndex === 0
})
const style = columnIndex === 0
? { backgroundColor: datum.color }
: {}
const style = { backgroundColor: datum.color }

@@ -210,3 +294,3 @@ return (

>
{content}
{datum.name.charAt(0)}
</div>

@@ -246,3 +330,3 @@ )

this.refs.Grid.scrollToCell({ scrollToColumn, scrollToRow })
this.refs.BodyGrid.scrollToCell({ scrollToColumn, scrollToRow })
}

@@ -260,4 +344,4 @@

this.refs.Grid.scrollToCell({ scrollToColumn, scrollToRow })
this.refs.BodyGrid.scrollToCell({ scrollToColumn, scrollToRow })
}
}
/** @flow */
import {
computeCellMetadataAndUpdateScrollOffsetHelper,
createCallbackMemoizer,
getUpdatedOffsetForIndex,
getVisibleCellIndices,
initCellMetadata,
initOnSectionRenderedHelper,
updateScrollIndexHelper

@@ -56,2 +56,9 @@ } from '../utils'

/**
* Callback invoked whenever the scroll offset changes within the inner scrollable region.
* This callback can be used to sync scrolling between lists, tables, or grids.
* ({ scrollLeft, scrollTop }): void
*/
onScroll: PropTypes.func.isRequired,
/**
* Callback invoked with information about the section of the Grid that was just rendered.

@@ -97,2 +104,3 @@ * ({ columnStartIndex, columnStopIndex, rowStartIndex, rowStopIndex }): void

noContentRenderer: () => null,
onScroll: () => null,
onSectionRendered: () => null

@@ -112,3 +120,4 @@ }

// Invokes onSectionRendered callback only when start/stop row or column indices change
this._OnGridRenderedHelper = initOnSectionRenderedHelper()
this._onGridRenderedMemoizer = createCallbackMemoizer()
this._onScrollMemoizer = createCallbackMemoizer(false)

@@ -146,2 +155,27 @@ // Bind functions to instance so they don't lose context when passed around

/**
* Set the :scrollLeft and :scrollTop position within the inner scroll container.
* Normally it is best to let Grid manage these properties or to use a method like :scrollToCell.
* This method enables Grid to be scroll-synced to another react-virtualized component though.
* It is appropriate to use in that case.
*/
setScrollPosition ({ scrollLeft, scrollTop }) {
const props = {}
if (scrollLeft >= 0) {
props.scrollLeft = scrollLeft
}
if (scrollTop >= 0) {
props.scrollTop = scrollTop
}
if (
scrollLeft >= 0 && scrollLeft !== this.state.scrollLeft ||
scrollTop >= 0 && scrollTop !== this.state.scrollTop
) {
this.setState(props)
}
}
componentDidMount () {

@@ -437,3 +471,3 @@ const { scrollToColumn, scrollToRow } = this.props

this._OnGridRenderedHelper({
this._onGridRenderedMemoizer({
callback: onSectionRendered,

@@ -650,3 +684,3 @@ indices: {

// 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 { height, onScroll, width } = this.props
const totalRowsHeight = this._getTotalRowsHeight()

@@ -658,5 +692,15 @@ const totalColumnsWidth = this._getTotalColumnsWidth()

this._setNextStateForScrollHelper({ scrollLeft, scrollTop })
this._onScrollMemoizer({
callback: onScroll,
indices: {
scrollLeft,
scrollTop
}
})
}
_onWheel (event) {
const{ onScroll } = this.props
const scrollLeft = this.refs.scrollingContainer.scrollLeft

@@ -666,3 +710,11 @@ const scrollTop = this.refs.scrollingContainer.scrollTop

this._setNextStateForScrollHelper({ scrollLeft, scrollTop })
this._onScrollMemoizer({
callback: onScroll,
indices: {
scrollLeft,
scrollTop
}
})
}
}
import React from 'react'
import { findDOMNode, render } from 'react-dom'
import TestUtils from 'react-addons-test-utils'
import { renderIntoDocument, Simulate } from 'react-addons-test-utils'
import Grid from './Grid'

@@ -18,12 +18,13 @@

function getMarkup ({
className = undefined,
className,
columnsCount = NUM_COLUMNS,
columnWidth = 50,
height = 100,
noContentRenderer = undefined,
onSectionRendered = undefined,
noContentRenderer,
onSectionRendered,
onScroll,
rowHeight = 20,
rowsCount = NUM_ROWS,
scrollToColumn = undefined,
scrollToRow = undefined,
scrollToColumn,
scrollToRow,
width = 200

@@ -47,2 +48,3 @@ } = {}) {

onSectionRendered={onSectionRendered}
onScroll={onScroll}
renderCell={renderCell}

@@ -59,3 +61,3 @@ rowHeight={rowHeight}

function renderGrid (props) {
const grid = TestUtils.renderIntoDocument(getMarkup(props))
const grid = renderIntoDocument(getMarkup(props))

@@ -69,3 +71,3 @@ // Allow initial setImmediate() to set :scrollTop

// Use ReactDOM.render for certain tests so that props changes will update the existing component
// TestUtils.renderIntoDocument creates a new component/instance each time
// renderIntoDocument creates a new component/instance each time
function renderOrUpdateGrid (props) {

@@ -327,2 +329,39 @@ let grid = render(getMarkup(props), node)

})
describe('onScroll', () => {
function helper ({ grid, scrollLeft, scrollTop }) {
const target = { scrollLeft, scrollTop }
grid.refs.scrollingContainer = target // HACK to work around _onScroll target check
Simulate.scroll(findDOMNode(grid), { target })
}
it('should trigger callback when component scrolls horizontally', () => {
const onScrollCalls = []
const grid = renderGrid({
onScroll: params => onScrollCalls.push(params)
})
helper({
grid,
scrollLeft: 100,
scrollTop: 0
})
expect(onScrollCalls).toEqual([{ scrollLeft: 100, scrollTop: 0 }])
})
it('should trigger callback when component scrolls horizontally', () => {
const onScrollCalls = []
const grid = renderGrid({
onScroll: params => onScrollCalls.push(params)
})
helper({
grid,
scrollLeft: 0,
scrollTop: 100
})
expect(onScrollCalls).toEqual([{ scrollLeft: 0, scrollTop: 100 }])
})
})
// TODO Add tests for :scrollToCell and :setScrollPosition.
// This probably requires the creation of an inner test-only class with refs.
})

@@ -51,2 +51,24 @@ /**

/**
* Helper utility that updates the specified callback whenever any of the specified indices have changed.
*/
export function createCallbackMemoizer (requireAllKeys = true) {
let cachedIndices = {}
return ({
callback,
indices
}) => {
const keys = Object.keys(indices)
const allInitialized = !requireAllKeys || keys.every(key => indices[key] >= 0)
const indexChanged = keys.some(key => cachedIndices[key] !== indices[key])
cachedIndices = indices
if (allInitialized && indexChanged) {
callback(indices)
}
}
}
/**
* Binary search function inspired by react-infinite.

@@ -64,2 +86,4 @@ */

// TODO Add better guards here against NaN offset
while (low <= high) {

@@ -142,2 +166,4 @@ middle = low + Math.floor((high - low) / 2)

// TODO Add better guards here against NaN offset
let start = findNearestCell({

@@ -204,24 +230,2 @@ cellMetadata,

/**
* Helper utility that updates the specified callback whenever any of the specified indices have changed.
*/
export function initOnSectionRenderedHelper () {
let cachedIndices = {}
return ({
callback,
indices
}) => {
const keys = Object.keys(indices)
const allInitialized = keys.every(key => indices[key] >= 0)
const indexChanged = keys.some(key => cachedIndices[key] !== indices[key])
cachedIndices = indices
if (allInitialized && indexChanged) {
callback(indices)
}
}
}
/**
* Helper function that determines when to update scroll offsets to ensure that a scroll-to-index remains visible.

@@ -228,0 +232,0 @@ *

import {
computeCellMetadataAndUpdateScrollOffsetHelper,
createCallbackMemoizer,
getUpdatedOffsetForIndex,
getVisibleCellIndices,
initCellMetadata,
initOnSectionRenderedHelper,
updateScrollIndexHelper

@@ -131,83 +131,3 @@ } from './utils'

describe('getUpdatedOffsetForIndex', () => {
function testHelper (targetIndex, currentOffset, cellMetadata = getCellMetadata()) {
return getUpdatedOffsetForIndex({
cellMetadata,
containerSize: 50,
currentOffset,
targetIndex
})
}
it('should scroll to the beginning', () => {
expect(testHelper(0, 100)).toEqual(0)
})
it('should scroll forward to the middle', () => {
expect(testHelper(4, 0)).toEqual(20)
})
it('should scroll backward to the middle', () => {
expect(testHelper(2, 100)).toEqual(30)
})
it('should not scroll if an item is already visible', () => {
expect(testHelper(2, 20)).toEqual(20)
})
it('should scroll to the end', () => {
expect(testHelper(8, 0)).toEqual(110)
})
it('should not scroll too far backward', () => {
expect(testHelper(-5, 0)).toEqual(0)
})
it('should not scroll too far forward', () => {
expect(testHelper(105, 0)).toEqual(110)
})
})
describe('getVisibleCellIndices', () => {
function testHelper (currentOffset, cellMetadata = getCellMetadata()) {
return getVisibleCellIndices({
cellCount: cellMetadata.length,
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(8) // TODO Should this actually be 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)
})
})
describe('initOnSectionRenderedHelper', () => {
describe('createCallbackMemoizer', () => {
function OnRowsRendered () {

@@ -232,3 +152,3 @@ let numCalls = 0

const util = new OnRowsRendered()
const helper = initOnSectionRenderedHelper()
const helper = createCallbackMemoizer()
helper({

@@ -254,3 +174,3 @@ callback: util.update,

const util = new OnRowsRendered()
const helper = initOnSectionRenderedHelper()
const helper = createCallbackMemoizer()
helper({

@@ -268,5 +188,20 @@ callback: util.update,

it('should call onRowsRendered if startIndex and stopIndex are invalid but :requireAllKeys is false', () => {
const util = new OnRowsRendered()
const helper = createCallbackMemoizer(false)
helper({
callback: util.update,
indices: {
startIndex: undefined,
stopIndex: 1
}
})
expect(util.numCalls()).toEqual(1)
expect(util.startIndex()).toEqual(undefined)
expect(util.stopIndex()).toEqual(1)
})
it('should not call onRowsRendered if startIndex or stopIndex have not changed', () => {
const util = new OnRowsRendered()
const helper = initOnSectionRenderedHelper()
const helper = createCallbackMemoizer()
helper({

@@ -294,3 +229,3 @@ callback: util.update,

const util = new OnRowsRendered()
const helper = initOnSectionRenderedHelper()
const helper = createCallbackMemoizer()
helper({

@@ -329,2 +264,82 @@ callback: util.update,

describe('getUpdatedOffsetForIndex', () => {
function testHelper (targetIndex, currentOffset, cellMetadata = getCellMetadata()) {
return getUpdatedOffsetForIndex({
cellMetadata,
containerSize: 50,
currentOffset,
targetIndex
})
}
it('should scroll to the beginning', () => {
expect(testHelper(0, 100)).toEqual(0)
})
it('should scroll forward to the middle', () => {
expect(testHelper(4, 0)).toEqual(20)
})
it('should scroll backward to the middle', () => {
expect(testHelper(2, 100)).toEqual(30)
})
it('should not scroll if an item is already visible', () => {
expect(testHelper(2, 20)).toEqual(20)
})
it('should scroll to the end', () => {
expect(testHelper(8, 0)).toEqual(110)
})
it('should not scroll too far backward', () => {
expect(testHelper(-5, 0)).toEqual(0)
})
it('should not scroll too far forward', () => {
expect(testHelper(105, 0)).toEqual(110)
})
})
describe('getVisibleCellIndices', () => {
function testHelper (currentOffset, cellMetadata = getCellMetadata()) {
return getVisibleCellIndices({
cellCount: cellMetadata.length,
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(8) // TODO Should this actually be 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)
})
})
describe('updateScrollIndexHelper', () => {

@@ -331,0 +346,0 @@ function helper ({

/** @flow */
import {
computeCellMetadataAndUpdateScrollOffsetHelper,
createCallbackMemoizer,
getUpdatedOffsetForIndex,
getVisibleCellIndices,
initCellMetadata,
initOnSectionRenderedHelper,
updateScrollIndexHelper

@@ -45,2 +45,8 @@ } from '../utils'

/**
* Callback invoked whenever the scroll offset changes within the inner scrollable region.
* This callback can be used to sync scrolling between lists, tables, or grids.
* ({ scrollTop }): void
*/
onScroll: PropTypes.func.isRequired,
/**
* Either a fixed row height (number) or a function that returns the height of a row given its index.

@@ -60,3 +66,4 @@ * (index: number): number

noRowsRenderer: () => null,
onRowsRendered: () => null
onRowsRendered: () => null,
onScroll: () => null
}

@@ -74,3 +81,4 @@

// Invokes onRowsRendered callback only when start/stop row indices change
this._OnRowsRenderedHelper = initOnSectionRenderedHelper()
this._onRowsRenderedMemoizer = createCallbackMemoizer()
this._onScrollMemoizer = createCallbackMemoizer(false)

@@ -106,2 +114,14 @@ // Bind functions to instance so they don't lose context when passed around

/**
* Set the :scrollTop position within the inner scroll container.
* Normally it is best to let VirtualScroll manage this properties or to use a method like :scrollToRow.
* This method enables VirtualScroll to be scroll-synced to another react-virtualized component though.
* It is appropriate to use in that case.
*/
setScrollTop (scrollTop) {
scrollTop = Number.isNaN(scrollTop) ? 0 : scrollTop
this.setState({ scrollTop })
}
componentDidMount () {

@@ -312,3 +332,3 @@ const { scrollToIndex } = this.props

this._OnRowsRenderedHelper({
this._onRowsRenderedMemoizer({
callback: onRowsRendered,

@@ -463,3 +483,3 @@ indices: {

// We can avoid that by doing some simple bounds checking to ensure that scrollTop never exceeds the total height.
const { height } = this.props
const { height, onScroll } = this.props
const totalRowsHeight = this._getTotalRowsHeight()

@@ -469,9 +489,25 @@ const scrollTop = Math.min(totalRowsHeight - height, event.target.scrollTop)

this._setNextStateForScrollHelper({ scrollTop })
this._onScrollMemoizer({
callback: onScroll,
indices: {
scrollTop
}
})
}
_onWheel (event) {
const{ onScroll } = this.props
const scrollTop = this.refs.scrollingContainer.scrollTop
this._setNextStateForScrollHelper({ scrollTop })
this._onScrollMemoizer({
callback: onScroll,
indices: {
scrollTop
}
})
}
}
import React from 'react'
import { findDOMNode, render } from 'react-dom'
import TestUtils from 'react-addons-test-utils'
import { renderIntoDocument, Simulate } from 'react-addons-test-utils'
import Immutable from 'immutable'

@@ -22,10 +22,11 @@ import VirtualScroll from './VirtualScroll'

function getMarkup ({
className = undefined,
className,
height = 100,
noRowsRenderer = undefined,
onRowsRendered = undefined,
noRowsRenderer,
onRowsRendered,
onScroll,
rowHeight = 10,
rowsCount = list.size,
scrollToIndex = undefined,
styleSheet = undefined
scrollToIndex,
styleSheet
} = {}) {

@@ -49,2 +50,3 @@ function rowRenderer (index) {

onRowsRendered={onRowsRendered}
onScroll={onScroll}
rowHeight={rowHeight}

@@ -60,3 +62,3 @@ rowRenderer={rowRenderer}

function renderList (props) {
const virtualScroll = TestUtils.renderIntoDocument(getMarkup(props))
const virtualScroll = renderIntoDocument(getMarkup(props))

@@ -70,3 +72,3 @@ // Allow initial setImmediate() to set :scrollTop

// Use ReactDOM.render for certain tests so that props changes will update the existing component
// TestUtils.renderIntoDocument creates a new component/instance each time
// renderIntoDocument creates a new component/instance each time
function renderOrUpdateList (props) {

@@ -243,2 +245,20 @@ let virtualScroll = render(getMarkup(props), node)

})
describe('onScroll', () => {
it('should trigger callback when component scrolls', () => {
const onScrollCalls = []
const list = renderList({
onScroll: params => onScrollCalls.push(params)
})
const target = {
scrollTop: 100
}
list.refs.scrollingContainer = target // HACK to work around _onScroll target check
Simulate.scroll(findDOMNode(list), { target })
expect(onScrollCalls).toEqual([{ scrollTop: 100 }])
})
})
// TODO Add tests for :scrollToRow and :setScrollTop.
// This probably requires the creation of an inner test-only class with refs.
})

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc