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 2.5.0 to 2.6.0

source/utils.js

3

CHANGELOG.md
Changelog
------------
#### 2.6.0
`VirtualScroll` and `FlexTable` now support dynamic row heights by accepting a function as the `rowHeight` property.
#### 2.5.0

@@ -5,0 +8,0 @@ Added `AutoSizer` component for wrapping `FlexTable` or `VirtualScroll` and growing to fill the parent container. This should hopefully simplify usage of these components.

@@ -7,3 +7,3 @@ FlexTable

#### Prop Types
### Prop Types
| Property | Type | Required? | Description |

@@ -23,3 +23,3 @@ |:---|:---|:---:|:---|

| rowGetter | Function | ✓ | Callback responsible for returning a data row given an index. `(index: int): any` |
| rowHeight | | ✓ | Fixed height of table row |
| 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. |

@@ -31,1 +31,12 @@ | sort | Function | | Sort function to be called if a sortable header is clicked. `(dataKey: string, sortDirection: SortDirection): void` |

| verticalPadding | Number | | Vertical padding of outer DOM element |
### Public Methods
##### recomputeRowHeights
Recompute row heights and offsets.
VirtualScroll has no way of knowing when its underlying list data has changed since it only receives a `rowHeight` property. If the `rowHeight` is a number it can compare before and after values but if it is a function that comparison is error prone. In the event that a dynamic `rowHeight` function is in use and the row heights have changed this function should be manually called by the "smart" container parent.
##### scrollToRow
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.)

@@ -6,3 +6,3 @@ VirtualScroll

#### Prop Types
### Prop Types
| Property | Type | Required? | Description |

@@ -12,7 +12,18 @@ |:---|:---|:---:|:---|

| 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 `rowsCount` is 0 |
| onRowsRendered | | Function | Callback invoked with information about the slice of rows that were just rendered: `({ startIndex, stopIndex }): void` |
| rowHeight | Number | ✓ | Fixed row height; the number of rows displayed is calculated by dividing height by rowHeight |
| 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. Rendered rows must have a unique `key` attribute. |
| rowsCount | Number | ✓ | Number of rows in list. |
| scrollToIndex | Number | | Row index to ensure visible (by forcefully scrolling if necessary) |
### Public Methods
##### recomputeRowHeights
Recompute row heights and offsets.
VirtualScroll has no way of knowing when its underlying list data has changed since it only receives a `rowHeight` property. If the `rowHeight` is a number it can compare before and after values but if it is a function that comparison is error prone. In the event that a dynamic `rowHeight` function is in use and the row heights have changed this function should be manually called by the "smart" container parent.
##### scrollToRow
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.)

4

package.json

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

"user": "bvaughn",
"version": "2.5.0",
"version": "2.6.0",
"scripts": {

@@ -63,2 +63,4 @@ "build": "npm run build:demo && npm run build:dist",

"expect",
"fdescribe",
"fit",
"it",

@@ -65,0 +67,0 @@ "jasmine"

import React, { PropTypes } from 'react'
import cn from 'classnames'
import styles from './LabeledInput.css'
export function LabeledInput ({ label, name, onChange, placeholder, value }) {
export function LabeledInput ({
disabled,
label,
name,
onChange,
placeholder,
value
}) {
const labelClassName = cn(styles.Label, {
[styles.LabelDisabled]: disabled
})
return (
<div className={styles.LabeledInput}>
<label className={styles.Label}>
<label className={labelClassName}>
{label}

@@ -16,2 +28,3 @@ </label>

value={value}
disabled={disabled}
/>

@@ -22,2 +35,3 @@ </div>

LabeledInput.propTypes = {
disabled: PropTypes.bool,
label: PropTypes.string.isRequired,

@@ -24,0 +38,0 @@ name: PropTypes.string.isRequired,

@@ -28,2 +28,4 @@ /** @flow */

export default class FlexTable extends Component {
static shouldComponentUpdate = shouldPureComponentUpdate
static defaultProps = {

@@ -109,9 +111,11 @@ disableHeader: false,

/**
* Scroll the table to ensure the specified index is visible.
*
* @private
* Why was this functionality implemented as a method instead of a property?
* Short answer: A user of this component may want to scroll to the same row twice.
* In this case the scroll-to-row property would not change and so it would not be picked up by the component.
* See VirtualScroll#recomputeRowHeights
*/
recomputeRowHeights () {
this.refs.VirtualScroll.recomputeRowHeights()
}
/**
* See VirtualScroll#scrollToRow
*/
scrollToRow (scrollToIndex) {

@@ -121,10 +125,2 @@ this.refs.VirtualScroll.scrollToRow(scrollToIndex)

getRenderedHeaderRow () {
const { children, disableHeader } = this.props
const items = disableHeader ? [] : children
return React.Children.map(items, (column, columnIndex) =>
this._createHeader(column, columnIndex)
)
}
render () {

@@ -168,3 +164,3 @@ const {

>
{this.getRenderedHeaderRow()}
{this._getRenderedHeaderRow()}
</div>

@@ -313,4 +309,11 @@ )}

}
_getRenderedHeaderRow () {
const { children, disableHeader } = this.props
const items = disableHeader ? [] : children
return React.Children.map(items, (column, columnIndex) =>
this._createHeader(column, columnIndex)
)
}
}
FlexTable.prototype.shouldComponentUpdate = shouldPureComponentUpdate

@@ -317,0 +320,0 @@ /**

@@ -123,6 +123,5 @@ import React from 'react'

// 100px height should fit 1 header (20px) and 9 rows (10px each) -
// 8 to fill the remaining space and 1 to account for partial scrolling
// 100px height should fit 1 header (20px) and 8 rows (10px each) -
expect(findAll(tableDOMNode, '.headerRow').length).toEqual(1)
expect(findAll(tableDOMNode, '.row').length).toEqual(9)
expect(findAll(tableDOMNode, '.row').length).toEqual(8)
})

@@ -151,3 +150,3 @@

const rows = findAll(tableDOMNode, '.row')
expect(rows.length).toEqual(3)
expect(rows.length).toEqual(2)

@@ -356,3 +355,3 @@ for (let index = 0; index < rows.length; index++) {

expect(startIndex).toEqual(0)
expect(stopIndex).toEqual(8)
expect(stopIndex).toEqual(7)
})

@@ -359,0 +358,0 @@

@@ -20,3 +20,4 @@ /**

color: BADGE_COLORS[i % BADGE_COLORS.length],
name: NAMES[i % NAMES.length]
name: NAMES[i % NAMES.length],
height: ROW_HEIGHTS[Math.floor(Math.random() * ROW_HEIGHTS.length)]
})

@@ -28,2 +29,3 @@ }

scrollToIndex: undefined,
useDynamicRowHeight: false,
virtualScrollHeight: 300,

@@ -33,2 +35,3 @@ virtualScrollRowHeight: 60

this._getRowHeight = this._getRowHeight.bind(this)
this._noRowsRenderer = this._noRowsRenderer.bind(this)

@@ -38,6 +41,13 @@ this._onRowsCountChange = this._onRowsCountChange.bind(this)

this._rowRenderer = this._rowRenderer.bind(this)
this._updateUseDynamicRowHeight = this._updateUseDynamicRowHeight.bind(this)
}
render () {
const { rowsCount, scrollToIndex, virtualScrollHeight, virtualScrollRowHeight } = this.state
const {
rowsCount,
scrollToIndex,
useDynamicRowHeight,
virtualScrollHeight,
virtualScrollRowHeight
} = this.state

@@ -57,2 +67,14 @@ return (

<ContentBoxParagraph>
<label className={styles.checkboxLabel}>
<input
className={styles.checkbox}
type='checkbox'
value={useDynamicRowHeight}
onChange={event => this._updateUseDynamicRowHeight(event.target.checked)}
/>
Use dynamic row heights?
</label>
</ContentBoxParagraph>
<InputRow>

@@ -79,2 +101,3 @@ <LabeledInput

<LabeledInput
disabled={useDynamicRowHeight}
label='Row height'

@@ -88,2 +111,3 @@ name='virtualScrollRowHeight'

<VirtualScroll
ref='VirtualScroll'
className={styles.VirtualScroll}

@@ -94,3 +118,3 @@ width={310}

rowsCount={rowsCount}
rowHeight={virtualScrollRowHeight}
rowHeight={useDynamicRowHeight ? this._getRowHeight : virtualScrollRowHeight}
rowRenderer={this._rowRenderer}

@@ -103,2 +127,6 @@ scrollToIndex={scrollToIndex}

_getRowHeight (index) {
return this._list[index].height
}
_noRowsRenderer () {

@@ -131,9 +159,22 @@ return (

_rowRenderer (index) {
const { virtualScrollRowHeight } = this.state
const rowStyle = {
height: virtualScrollRowHeight
const { useDynamicRowHeight, virtualScrollRowHeight } = this.state
const datum = this._list[index]
const height = useDynamicRowHeight
? datum.height
: virtualScrollRowHeight
let additionalContent
if (useDynamicRowHeight) {
switch (datum.height) {
case 75:
additionalContent = <div>It is medium-sized.</div>
break
case 100:
additionalContent = <div>It is large-sized.<br/>It has a 3rd row.</div>
break
}
}
const data = this._list[index]
return (

@@ -143,3 +184,3 @@ <div

className={styles.row}
style={rowStyle}
style={{ height }}
>

@@ -149,10 +190,10 @@ <div

style={{
backgroundColor: data.color
backgroundColor: datum.color
}}
>
{data.name.charAt(0)}
{datum.name.charAt(0)}
</div>
<div>
<div className={styles.name}>
{data.name}
{datum.name}
</div>

@@ -162,6 +203,19 @@ <div className={styles.index}>

</div>
{additionalContent}
</div>
{useDynamicRowHeight &&
<span className={styles.height}>
{datum.height}px
</span>
}
</div>
)
}
_updateUseDynamicRowHeight (value) {
this.setState({
rowsCount: 100,
useDynamicRowHeight: value
})
}
}

@@ -171,1 +225,2 @@

const 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']
const ROW_HEIGHTS = [50, 75, 100]

@@ -6,2 +6,7 @@ /** @flow */

import raf from 'raf'
import {
getUpdatedOffsetForIndex,
getVisibleRowIndices,
initCellMetadata
} from '../utils'
import styles from './VirtualScroll.css'

@@ -23,2 +28,4 @@

export default class VirtualScroll extends Component {
static shouldComponentUpdate = shouldPureComponentUpdate
static propTypes = {

@@ -36,4 +43,4 @@ /** Optional CSS class name */

onRowsRendered: PropTypes.func,
/** Fixed row height; the number of rows displayed is calculated by dividing height by rowHeight */
rowHeight: PropTypes.number.isRequired,
/** Either a fixed row height (number) or a function that returns the height of a row given its index. */
rowHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.func]).isRequired,
/** Responsbile for rendering a row given an index */

@@ -56,2 +63,3 @@ rowRenderer: PropTypes.func.isRequired,

this.state = {
computeCellMetadataOnNextUpdate: false,
isScrolling: false,

@@ -67,2 +75,13 @@ scrollTop: 0

/**
* Forced recompute of row heights.
* This function should be called if dynamic row heights have changed but nothing else has.
* Since VirtualScroll receives a :rowsCount it has no way of knowing if the underlying list data has changed.
*/
recomputeRowHeights () {
this.setState({
computeCellMetadataOnNextUpdate: true
})
}
/**
* Scroll the list to ensure the row at the specified index is visible.

@@ -88,14 +107,2 @@ * This method exists so that a user can forcefully scroll to the same row twice.

componentWillUnmount () {
if (this._disablePointerEventsTimeoutId) {
clearTimeout(this._disablePointerEventsTimeoutId)
}
if (this._scrollTopId) {
clearImmediate(this._scrollTopId)
}
if (this._setNextStateAnimationFrameId) {
raf.cancel(this._setNextStateAnimationFrameId)
}
}
componentDidUpdate (prevProps, prevState) {

@@ -105,4 +112,2 @@ const { height, rowsCount, rowHeight, scrollToIndex } = this.props

const previousRowsCount = prevProps.rowsCount
// Make sure any changes to :scrollTop (from :scrollToIndex) get applied

@@ -114,3 +119,10 @@ if (scrollTop >= 0 && scrollTop !== prevState.scrollTop) {

const hasScrollToIndex = scrollToIndex >= 0 && scrollToIndex < rowsCount
const sizeHasChanged = height !== prevProps.height || rowHeight !== prevProps.rowHeight
const sizeHasChanged = (
height !== prevProps.height ||
!prevProps.rowHeight ||
(
rowHeight instanceof Number &&
rowHeight !== prevProps.rowHeight
)
)

@@ -124,9 +136,8 @@ // If we have a new scroll target OR if height/row-height has changed,

// Make sure we aren't scrolled too far past the current content.
} else if (!hasScrollToIndex && (height < prevProps.height || rowsCount < previousRowsCount)) {
const calculatedScrollTop = VirtualScroll._calculateScrollTopForIndex({
height,
rowHeight,
rowsCount,
scrollTop,
scrollToIndex: rowsCount - 1
} else if (!hasScrollToIndex && (height < prevProps.height || rowsCount < prevProps.rowsCount)) {
const calculatedScrollTop = getUpdatedOffsetForIndex({
cellMetadata: this._cellMetadata,
containerSize: height,
currentOffset: scrollTop,
targetIndex: rowsCount - 1
})

@@ -141,8 +152,51 @@

componentWillUpdate (prevProps, prevState) {
const { rowsCount } = this.props
componentWillMount () {
this._computeCellMetadata(this.props)
}
if (rowsCount === 0) {
componentWillUnmount () {
if (this._disablePointerEventsTimeoutId) {
clearTimeout(this._disablePointerEventsTimeoutId)
}
if (this._scrollTopId) {
clearImmediate(this._scrollTopId)
}
if (this._setNextStateAnimationFrameId) {
raf.cancel(this._setNextStateAnimationFrameId)
}
}
componentWillUpdate (nextProps, nextState) {
if (
nextProps.rowsCount === 0 &&
nextState.scrollTop !== 0
) {
this.setState({ scrollTop: 0 })
}
// Don't compare rowHeight if it's a function because inline functions would cause infinite loops.
// In that event users should use recomputeRowHeights() to inform of changes.
if (
nextState.computeCellMetadataOnNextUpdate ||
this.props.rowsCount !== nextProps.rowsCount ||
(
(
typeof this.props.rowHeight === 'number' ||
typeof nextProps.rowHeight === 'number'
) &&
this.props.rowHeight !== nextProps.rowHeight
)
) {
this._computeCellMetadata(nextProps)
this.setState({
computeCellMetadataOnNextUpdate: false
})
// Updated cell metadata may have hidden the previous scrolled-to item.
// In this case we should also update the scrollTop to ensure it stays visible.
if (this.props.scrollToIndex === nextProps.scrollToIndex) {
this._updateScrollTopForScrollToIndex()
}
}
}

@@ -157,3 +211,2 @@

rowsCount,
rowHeight,
rowRenderer

@@ -167,8 +220,2 @@ } = this.props

const totalRowsHeight = rowsCount * rowHeight
// Shift the visible rows down so that they remain visible while scrolling.
// This mimicks scrolling behavior within a non-virtualized list.
const paddingTop = scrollTop - (scrollTop % rowHeight)
let childrenToDisplay = []

@@ -179,18 +226,30 @@

const {
rowIndexStart,
rowIndexStop
} = VirtualScroll._getStartAndStopIndexForScrollTop({
height,
rowHeight,
rowsCount,
scrollTop
start,
stop
} = getVisibleRowIndices({
cellCount: rowsCount,
cellMetadata: this._cellMetadata,
containerSize: height,
currentOffset: scrollTop
})
for (let i = rowIndexStart; i <= rowIndexStop; i++) {
childrenToDisplay.push(rowRenderer(i))
for (let i = start; i <= stop; i++) {
let datum = this._cellMetadata[i]
let child = React.cloneElement(
rowRenderer(i), {
style: {
position: 'absolute',
top: datum.offset,
width: '100%',
height: this._getRowHeight(i)
}
}
)
childrenToDisplay.push(child)
}
onRowsRendered({
startIndex: rowIndexStart,
stopIndex: rowIndexStop
startIndex: start,
stopIndex: stop
})

@@ -215,5 +274,4 @@ }

style={{
height: totalRowsHeight,
maxHeight: totalRowsHeight,
paddingTop: paddingTop,
height: this._getTotalRowsHeight(),
maxHeight: this._getTotalRowsHeight(),
pointerEvents: isScrolling ? 'none' : 'auto'

@@ -232,49 +290,27 @@ }}

/**
* Scroll the table to ensure the specified index is visible.
*
* @private
* Why was this functionality implemented as a method instead of a property?
* Short answer: A user of this component may want to scroll to the same row twice.
* In this case the scroll-to-row property would not change and so it would not be picked up by the component.
*/
static _calculateScrollTopForIndex ({ rowsCount, height, rowHeight, scrollTop, scrollToIndex }) {
scrollToIndex = Math.max(0, Math.min(rowsCount - 1, scrollToIndex))
_computeCellMetadata (props) {
const { rowHeight, rowsCount } = props
const maxScrollTop = scrollToIndex * rowHeight
const minScrollTop = maxScrollTop - height + rowHeight
const newScrollTop = Math.max(minScrollTop, Math.min(maxScrollTop, scrollTop))
return newScrollTop
this._cellMetadata = initCellMetadata({
cellCount: rowsCount,
size: rowHeight
})
}
/**
* Calculates the maximum number of visible rows based on the row-height and the number of rows in the table.
*/
static _getMaxVisibleRows ({ height, rowHeight, rowsCount }) {
const minNumRowsToFillSpace = Math.ceil(height / rowHeight)
_getRowHeight (index) {
const { rowHeight } = this.props
// Add one to account for partially-clipped rows on the top and bottom
const maxNumRowsToFillSpace = minNumRowsToFillSpace + 1
return Math.min(rowsCount, maxNumRowsToFillSpace)
return rowHeight instanceof Function
? rowHeight(index)
: rowHeight
}
/**
* Calculates the start and end index for visible rows based on a scroll offset.
* Handles edge-cases to ensure that the table never scrolls past the available rows.
*/
static _getStartAndStopIndexForScrollTop ({ height, rowHeight, rowsCount, scrollTop }) {
const maxVisibleRows = VirtualScroll._getMaxVisibleRows({ height, rowHeight, rowsCount })
const totalRowsHeight = rowHeight * rowsCount
const safeScrollTop = Math.max(0, Math.min(totalRowsHeight - height, scrollTop))
_getTotalRowsHeight () {
if (this._cellMetadata.length === 0) {
return 0
}
let scrollPercentage = safeScrollTop / totalRowsHeight
let rowIndexStart = Math.floor(scrollPercentage * rowsCount)
let rowIndexStop = Math.min(rowsCount, rowIndexStart + maxVisibleRows) - 1
const datum = this._cellMetadata[this._cellMetadata.length - 1]
return {
rowIndexStart,
rowIndexStop
}
return datum.offset + datum.size
}

@@ -329,12 +365,11 @@

const { height, rowsCount, rowHeight } = this.props
const { height } = this.props
const { scrollTop } = this.state
if (scrollToIndex >= 0) {
const calculatedScrollTop = VirtualScroll._calculateScrollTopForIndex({
height,
rowHeight,
rowsCount,
scrollTop,
scrollToIndex
const calculatedScrollTop = getUpdatedOffsetForIndex({
cellMetadata: this._cellMetadata,
containerSize: height,
currentOffset: scrollTop,
targetIndex: scrollToIndex
})

@@ -349,5 +384,7 @@

_onKeyPress (event) {
const { rowHeight } = this.props
const { height, rowsCount } = this.props
const { scrollTop } = this.state
let start, datum, newScrollTop
switch (event.key) {

@@ -357,7 +394,17 @@ case 'ArrowDown':

const { height, rowsCount } = this.props
const totalRowsHeight = rowsCount * rowHeight
const newScrollTop = Math.min(totalRowsHeight - height, scrollTop + rowHeight)
start = getVisibleRowIndices({
cellCount: rowsCount,
cellMetadata: this._cellMetadata,
containerSize: height,
currentOffset: scrollTop
}).start
datum = this._cellMetadata[start]
newScrollTop = Math.min(
this._getTotalRowsHeight() - height,
scrollTop + datum.size
)
this.setState({ scrollTop: newScrollTop })
this.setState({
scrollTop: newScrollTop
})
break

@@ -367,5 +414,10 @@ case 'ArrowUp':

this.setState({
scrollTop: Math.max(0, scrollTop - rowHeight)
})
start = getVisibleRowIndices({
cellCount: rowsCount,
cellMetadata: this._cellMetadata,
containerSize: height,
currentOffset: scrollTop
}).start
this.scrollToRow(Math.max(0, start - 1))
break

@@ -387,4 +439,4 @@ }

// We can avoid that by doing some simple bounds checking to ensure that scrollTop never exceeds the total height.
const { height, rowsCount, rowHeight } = this.props
const totalRowsHeight = rowsCount * rowHeight
const { height } = this.props
const totalRowsHeight = this._getTotalRowsHeight()
const scrollTop = Math.min(totalRowsHeight - height, event.target.scrollTop)

@@ -429,2 +481,1 @@

}
VirtualScroll.prototype.shouldComponentUpdate = shouldPureComponentUpdate

@@ -76,7 +76,7 @@ import React from 'react'

describe('number of rendered children', () => {
it('should render enough children to fill the view +1 for partial visibility at top and bottom', () => {
it('should render enough children to fill the view', () => {
const list = renderList()
const listDOMNode = findDOMNode(list)
expect(listDOMNode.querySelectorAll('.listItem').length).toEqual(11)
expect(listDOMNode.querySelectorAll('.listItem').length).toEqual(10)
})

@@ -116,110 +116,2 @@

/** Tests fine-grained control of scrolling from position A to B */
describe('scrollToIndex / _calculateScrollTopForIndex', () => {
function calculateScrollTopForIndex (scrollToIndex, scrollTop = 0) {
return VirtualScroll._calculateScrollTopForIndex({
height: 100,
rowHeight: 10,
rowsCount: list.size,
scrollToIndex,
scrollTop
})
}
it('should scroll to the top', () => {
expect(calculateScrollTopForIndex(0)).toEqual(0)
})
it('should scroll down to the middle', () => {
expect(calculateScrollTopForIndex(49)).toEqual(400)
})
it('should scroll up to the middle', () => {
expect(calculateScrollTopForIndex(49, 800)).toEqual(490)
})
it('should not scroll if an item is already visible', () => {
expect(calculateScrollTopForIndex(49, 470)).toEqual(470)
})
it('should scroll to the bottom', () => {
expect(calculateScrollTopForIndex(99)).toEqual(900)
})
it('should not scroll past the top', () => {
expect(calculateScrollTopForIndex(-5)).toEqual(0)
})
it('should not scroll past the bottom', () => {
expect(calculateScrollTopForIndex(105)).toEqual(900)
})
})
describe('_getMaxVisibleRows', () => {
function getMaxVisibleRows (rowsCount) {
return VirtualScroll._getMaxVisibleRows({
height: 100,
rowHeight: 20,
rowsCount
})
}
it('should handle no rows', () => {
expect(getMaxVisibleRows(0)).toEqual(0)
})
it('should handle when there are fewer rows than available height', () => {
expect(getMaxVisibleRows(2)).toEqual(2)
})
it('should handle when the rows exactly fit', () => {
expect(getMaxVisibleRows(5)).toEqual(5)
})
it('should handle when there are more rows than the available height', () => {
expect(getMaxVisibleRows(100)).toEqual(6) // Exact fit +1 extra for overlap
})
})
describe('_getStartAndStopIndexForScrollTop', () => {
function getStartAndStopIndexForScrollTop (scrollTop) {
return VirtualScroll._getStartAndStopIndexForScrollTop({
height: 100,
rowHeight: 20,
rowsCount: 100,
scrollTop
})
}
it('should handle unscrolled', () => {
const { rowIndexStart, rowIndexStop } = getStartAndStopIndexForScrollTop(0)
expect(rowIndexStart).toEqual(0)
expect(rowIndexStop).toEqual(5)
})
it('should handle scrolled to the middle', () => {
const { rowIndexStart, rowIndexStop } = getStartAndStopIndexForScrollTop(1000)
expect(rowIndexStart).toEqual(50)
expect(rowIndexStop).toEqual(55)
})
it('should handle scrolled to the end', () => {
const { rowIndexStart, rowIndexStop } = getStartAndStopIndexForScrollTop(1920)
expect(rowIndexStart).toEqual(95)
expect(rowIndexStop).toEqual(99)
})
it('should handle scrolled past the end', () => {
const { rowIndexStart, rowIndexStop } = getStartAndStopIndexForScrollTop(3000)
expect(rowIndexStart).toEqual(95)
expect(rowIndexStop).toEqual(99)
})
it('should handle scrolled past the beginning', () => {
const { rowIndexStart, rowIndexStop } = getStartAndStopIndexForScrollTop(-200)
expect(rowIndexStart).toEqual(0)
expect(rowIndexStop).toEqual(5)
})
})
describe('property updates', () => {

@@ -281,3 +173,3 @@ it('should update :scrollToIndex position when :rowHeight changes', () => {

expect(startIndex).toEqual(0)
expect(stopIndex).toEqual(10)
expect(stopIndex).toEqual(9)
})

@@ -284,0 +176,0 @@

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

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