Skip to content

Commit

Permalink
Added to to handle case when header items change or resize. also bett…
Browse files Browse the repository at this point in the history
…er handles window resize events.
  • Loading branch information
Brian Vaughn committed Dec 3, 2016
1 parent d44dde1 commit 2e91ec7
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 14 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
Changelog
------------

##### 8.7.0
Added `updatePosition` to `WindowScroller` to handle case when header items change or resize. `WindowScroller` also better handles window resize events.

##### 8.6.1
Updated `CellSizeCache` interface for the better perfomance by removing `has` methods, reducing a double hashtable lookup to a single lookup. Special thanks to @arusakov for this contribution!

Expand Down
8 changes: 8 additions & 0 deletions docs/WindowScroller.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ This may change with a future release but for the time being this HOC is should
| onResize | Function | | Callback to be invoked on-resize; it is passed the following named parameters: `({ height: number })`. |
| onScroll | Function | | Callback to be invoked on-scroll; it is passed the following named parameters: `({ scrollTop: number })`. |

### Public Methods

##### updatePosition

Recalculates scroll position from the top of page.

This methoed is automatically triggered when the component mounts as well as when the browser resizes. It should be manually called if the page header (eg any items in the DOM "above" the `WindowScroller`) resizes or changes.

### Examples

```javascript
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"description": "React components for efficiently rendering large, scrollable lists and tabular data",
"author": "Brian Vaughn <brian.david.vaughn@gmail.com>",
"user": "bvaughn",
"version": "8.6.1",
"version": "8.7.0",
"homepage": "https://github.com/bvaughn/react-virtualized",
"main": "dist/commonjs/index.js",
"module": "dist/es/index.js",
Expand Down
48 changes: 40 additions & 8 deletions source/WindowScroller/WindowScroller.example.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,17 @@ export default class AutoSizerExample extends Component {
constructor (props) {
super(props)

this.state = {
showHeaderText: true
}

this._hideHeader = this._hideHeader.bind(this)
this._rowRenderer = this._rowRenderer.bind(this)
}

render () {
const { list } = this.context
const { showHeaderText } = this.state

return (
<ContentBox>
Expand All @@ -31,23 +37,38 @@ export default class AutoSizerExample extends Component {
docsLink='https://github.com/bvaughn/react-virtualized/blob/master/docs/WindowScroller.md'
/>

<ContentBoxParagraph>
This component decorates <code>List</code>, <code>Table</code>, or any other component
and manages the window scroll to scroll through the list
</ContentBoxParagraph>
{showHeaderText && (
<ContentBoxParagraph>
This component decorates <code>List</code>, <code>Table</code>, or any other component
and manages the window scroll to scroll through the list
</ContentBoxParagraph>
)}

{showHeaderText && (
<ContentBoxParagraph>
<button onClick={this._hideHeader}>
Hide header text
</button>
</ContentBoxParagraph>
)}

<div className={styles.WindowScrollerWrapper}>
<WindowScroller>
<WindowScroller
ref={(ref) => {
this._windowScroller = ref
}}
>
{({ height, isScrolling, scrollTop }) => (
<AutoSizer disableHeight>
{({ width }) => (
<List
autoHeight
className={styles.List}
height={height}
overscanRowCount={2}
rowCount={list.size}
rowHeight={30}
rowRenderer={({ index, key, style }) => this._rowRenderer({ index, isScrolling, key, style })}
rowRenderer={({ index, isVisible, key, style }) => this._rowRenderer({ index, isScrolling, isVisible, key, style })}
scrollTop={scrollTop}
width={width}
/>
Expand All @@ -64,11 +85,22 @@ export default class AutoSizerExample extends Component {
return shallowCompare(this, nextProps, nextState)
}

_rowRenderer ({ index, isScrolling, key, style }) {
_hideHeader () {
const { showHeaderText } = this.state

this.setState({
showHeaderText: !showHeaderText
}, () => {
this._windowScroller.updatePosition()
})
}

_rowRenderer ({ index, isScrolling, isVisible, key, style }) {
const { list } = this.context
const row = list.get(index)
const className = cn(styles.row, {
[styles.rowScrolling]: isScrolling
[styles.rowScrolling]: isScrolling,
isVisible: isVisible
})

return (
Expand Down
13 changes: 10 additions & 3 deletions source/WindowScroller/WindowScroller.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,18 @@ export default class WindowScroller extends Component {
this._enablePointerEventsAfterDelayCallback = this._enablePointerEventsAfterDelayCallback.bind(this)
}

componentDidMount () {
const { height } = this.state

updatePosition () {
// Subtract documentElement top to handle edge-case where a user is navigating back (history) from an already-scrolled bage.
// In this case the body's top position will be a negative number and this element's top will be increased (by that amount).
this._positionFromTop =
ReactDOM.findDOMNode(this).getBoundingClientRect().top -
document.documentElement.getBoundingClientRect().top
}

componentDidMount () {
const { height } = this.state

this.updatePosition()

if (height !== window.innerHeight) {
this.setState({
Expand All @@ -59,6 +63,7 @@ export default class WindowScroller extends Component {
}

registerScrollListener(this)

window.addEventListener('resize', this._onResizeWindow, false)
}

Expand Down Expand Up @@ -92,6 +97,8 @@ export default class WindowScroller extends Component {
_onResizeWindow (event) {
const { onResize } = this.props

this.updatePosition()

const height = window.innerHeight || 0

this.setState({ height })
Expand Down
73 changes: 71 additions & 2 deletions source/WindowScroller/WindowScroller.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@ describe('WindowScroller', () => {
document.dispatchEvent(new window.Event('resize', { bubbles: true }))
}

function getMarkup (props = {}) {
return (
function getMarkup ({
headerElements,
...props
} = {}) {
const windowScroller = (
<WindowScroller {...props}>
{({ height, isScrolling, scrollTop }) => (
<ChildComponent
Expand All @@ -39,6 +42,17 @@ describe('WindowScroller', () => {
)}
</WindowScroller>
)

if (headerElements) {
return (
<div>
{headerElements}
{windowScroller}
</div>
)
} else {
return windowScroller
}
}

// Starts updating scrollTop only when the top position is reached
Expand Down Expand Up @@ -185,4 +199,59 @@ describe('WindowScroller', () => {
window.innerHeight = 500
})
})

describe('updatePosition', () => {
it('should calculate the initial offset from the top of the page when mounted', () => {
let windowScroller

render(getMarkup({
headerElements: <div style={{ height: 100 }}></div>,
ref: (ref) => {
windowScroller = ref
}
}))

expect(windowScroller._positionFromTop > 100).toBeTruthy()
})

it('should recalculate the offset from the top when the window resizes', () => {
let windowScroller

const rendered = render(getMarkup({
headerElements: <div id='header' style={{ height: 100 }}></div>,
ref: (ref) => {
windowScroller = ref
}
}))

expect(windowScroller._positionFromTop < 200).toBeTruthy()

const header = findDOMNode(rendered).querySelector('#header')
header.style.height = '200px'

simulateWindowResize({ height: 1000 })

expect(windowScroller._positionFromTop > 200).toBeTruthy()
})

it('should recalculate the offset from the top if called externally', () => {
let windowScroller

const rendered = render(getMarkup({
headerElements: <div id='header' style={{ height: 100 }}></div>,
ref: (ref) => {
windowScroller = ref
}
}))

expect(windowScroller._positionFromTop < 200).toBeTruthy()

const header = findDOMNode(rendered).querySelector('#header')
header.style.height = '200px'

windowScroller.updatePosition()

expect(windowScroller._positionFromTop > 200).toBeTruthy()
})
})
})

0 comments on commit 2e91ec7

Please sign in to comment.