diff --git a/.babelrc b/.babelrc index 26af21704..8472ff34e 100644 --- a/.babelrc +++ b/.babelrc @@ -1,6 +1,14 @@ { "env": { "commonjs": { + "ignore": [ + "*.jest.js", + "*.e2e.js", + "*.example.js", + "source/demo", + "source/jest-*.js", + "source/TestUtils.js" + ], "plugins": [ "transform-runtime", "flow-react-proptypes", @@ -51,6 +59,14 @@ ] }, "es": { + "ignore": [ + "*.jest.js", + "*.e2e.js", + "*.example.js", + "source/demo", + "source/jest-*.js", + "source/TestUtils.js" + ], "plugins": [ "transform-runtime", "flow-react-proptypes", diff --git a/package.json b/package.json index 70abcbc87..e1c2156c4 100644 --- a/package.json +++ b/package.json @@ -10,12 +10,12 @@ "jsnext:main": "dist/es/index.js", "license": "MIT", "scripts": { - "build:types": "flow-copy-source --ignore \"**/*.{jest,example}.js\" source/WindowScroller dist/es/WindowScroller", + "build:types": "flow-copy-source --ignore \"**/*.{jest,e2e,example}.js\" source/WindowScroller dist/es/WindowScroller", "build": "npm run build:commonjs && npm run build:css && npm run build:es && npm run build:demo && npm run build:umd", - "build:commonjs": "npm run clean:commonjs && cross-env NODE_ENV=production cross-env BABEL_ENV=commonjs babel source --out-dir dist/commonjs --ignore *.example.js,*.jest.js,source/demo/", + "build:commonjs": "npm run clean:commonjs && cross-env NODE_ENV=production cross-env BABEL_ENV=commonjs babel source --out-dir dist/commonjs", "build:css": "postcss source/styles.css -o styles.css --use autoprefixer", "build:demo": "npm run clean:demo && cross-env NODE_ENV=production webpack --config webpack.config.demo.js -p --bail", - "build:es": "npm run clean:es && npm run build:types && cross-env NODE_ENV=production cross-env BABEL_ENV=es babel source --out-dir dist/es --ignore *.example.js,*.jest.js,source/demo/", + "build:es": "npm run clean:es && npm run build:types && cross-env NODE_ENV=production cross-env BABEL_ENV=es babel source --out-dir dist/es", "build:umd": "npm run clean:umd && cross-env NODE_ENV=production webpack --config webpack.config.umd.js --bail", "check-all": "yarn prettier && yarn lint && yarn flow", "ci-check": "yarn prettier:diff && yarn lint && yarn flow", diff --git a/source/WindowScroller/WindowScroller.e2e.js b/source/WindowScroller/WindowScroller.e2e.js index 4b4cad49b..725fea9ff 100644 --- a/source/WindowScroller/WindowScroller.e2e.js +++ b/source/WindowScroller/WindowScroller.e2e.js @@ -111,3 +111,55 @@ test('save position after resize and then scroll in container', async () => { [{width: 400, height: 600}], ]); }); + +test('react on container resize without window changing', async () => { + const page = await bootstrap(); + const resizeFn = jest.fn(); + await page.exposeFunction('resizeFn', resizeFn); + + await page.evaluate(() => { + const {render} = window.ReactDOM; + const {createElement} = window.React; + const {WindowScroller} = window.ReactVirtualized; + + const wrapper = document.createElement('div'); + wrapper.id = 'wrapper'; + Object.assign(wrapper.style, { + width: '1000px', + height: '800px', + display: 'flex', + }); + const container = document.createElement('div'); + Object.assign(container.style, { + flex: '1', + }); + wrapper.appendChild(container); + document.body.style.margin = 0; + document.body.appendChild(wrapper); + + render( + createElement( + WindowScroller, + {scrollElement: container, onResize: window.resizeFn}, + () => null, + ), + container, + ); + }); + + await delay(100); + + await page.$eval('#wrapper', el => { + el.style.width = '500px'; + el.style.height = '700px'; + }); + + await delay(100); + + await page.close(); + + expect(resizeFn.mock.calls).toEqual([ + [{width: 1000, height: 800}], + [{width: 500, height: 700}], + ]); +}); diff --git a/source/WindowScroller/WindowScroller.js b/source/WindowScroller/WindowScroller.js index 96256d002..fe67a247c 100644 --- a/source/WindowScroller/WindowScroller.js +++ b/source/WindowScroller/WindowScroller.js @@ -12,6 +12,7 @@ import { getPositionOffset, getScrollOffset, } from './utils/dimensions'; +import createDetectElementResize from '../vendor/detectElementResize'; type Props = { /** @@ -57,6 +58,13 @@ type State = { scrollTop: number, }; +type ResizeHandler = (element: Element, onResize: () => void) => void; + +type DetectElementResize = { + addResizeListener: ResizeHandler, + removeResizeListener: ResizeHandler, +}; + /** * Specifies the number of miliseconds during which to disable pointer events while a scroll is in progress. * This improves performance and makes scrolling smoother. @@ -79,6 +87,7 @@ export default class WindowScroller extends React.PureComponent { _isMounted = false; _positionFromTop = 0; _positionFromLeft = 0; + _detectElementResize: DetectElementResize = createDetectElementResize(); state = { ...getDimensions(this.props.scrollElement, this.props), @@ -121,10 +130,9 @@ export default class WindowScroller extends React.PureComponent { if (scrollElement) { registerScrollListener(this, scrollElement); + this._registerResizeListener(scrollElement); } - window.addEventListener('resize', this._onResize, false); - this._isMounted = true; } @@ -141,14 +149,18 @@ export default class WindowScroller extends React.PureComponent { unregisterScrollListener(this, scrollElement); registerScrollListener(this, nextScrollElement); + + this._unregisterResizeListener(scrollElement); + this._registerResizeListener(nextScrollElement); } } componentWillUnmount() { - if (this.props.scrollElement) { - unregisterScrollListener(this, this.props.scrollElement); + const scrollElement = this.props.scrollElement; + if (scrollElement) { + unregisterScrollListener(this, scrollElement); + this._unregisterResizeListener(scrollElement); } - window.removeEventListener('resize', this._onResize, false); this._isMounted = false; } @@ -182,6 +194,22 @@ export default class WindowScroller extends React.PureComponent { } }; + _registerResizeListener = element => { + if (element === window) { + window.addEventListener('resize', this._onResize, false); + } else { + this._detectElementResize.addResizeListener(element, this._onResize); + } + }; + + _unregisterResizeListener = element => { + if (element === window) { + window.removeEventListener('resize', this._onResize, false); + } else if (element) { + this._detectElementResize.removeResizeListener(element, this._onResize); + } + }; + _onResize = () => { this.updatePosition(); };