import _regeneratorRuntime from 'babel-runtime/regenerator'; import _Promise from 'babel-runtime/core-js/promise'; import _asyncToGenerator from 'babel-runtime/helpers/asyncToGenerator'; import _objectWithoutProperties from 'babel-runtime/helpers/objectWithoutProperties'; var _this = this; /* global Element */ import React from 'react'; import { findDOMNode } from 'react-dom'; import { render } from '../TestUtils'; import { IS_SCROLLING_TIMEOUT } from './utils/onScroll'; import WindowScroller from './WindowScroller'; function ChildComponent(_ref) { var scrollTop = _ref.scrollTop, isScrolling = _ref.isScrolling, height = _ref.height; return React.createElement( 'div', null, 'scrollTop:' + scrollTop + ', isScrolling:' + isScrolling + ', height:' + height ); } function mockGetBoundingClientRectForHeader(_ref2) { var _ref2$documentOffset = _ref2.documentOffset, documentOffset = _ref2$documentOffset === undefined ? 0 : _ref2$documentOffset, height = _ref2.height, width = _ref2.width; // Mock the WindowScroller element and window separately // The only way to mock the former (before its created) is globally Element.prototype.getBoundingClientRect = jest.fn(function () { return { top: height, left: width }; }); document.documentElement.getBoundingClientRect = jest.fn(function () { return { top: documentOffset, left: documentOffset }; }); } function getMarkup() { var _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, headerElements = _ref3.headerElements, documentOffset = _ref3.documentOffset, props = _objectWithoutProperties(_ref3, ['headerElements', 'documentOffset']); var windowScroller = React.createElement( WindowScroller, props, function (_ref4) { var height = _ref4.height, isScrolling = _ref4.isScrolling, scrollTop = _ref4.scrollTop; return React.createElement(ChildComponent, { height: height, isScrolling: isScrolling, scrollTop: scrollTop }); } ); // JSDome doesn't implement a working getBoundingClientRect() // But WindowScroller requires it mockGetBoundingClientRectForHeader({ documentOffset: documentOffset, height: headerElements ? headerElements.props.style.height : 0, width: headerElements ? headerElements.props.style.width : 0 }); if (headerElements) { return React.createElement( 'div', null, headerElements, windowScroller ); } else { return windowScroller; } } function simulateWindowScroll(_ref5) { var _ref5$scrollX = _ref5.scrollX, scrollX = _ref5$scrollX === undefined ? 0 : _ref5$scrollX, _ref5$scrollY = _ref5.scrollY, scrollY = _ref5$scrollY === undefined ? 0 : _ref5$scrollY; document.body.style.height = '10000px'; window.scrollX = scrollX; window.scrollY = scrollY; document.dispatchEvent(new window.Event('scroll', { bubbles: true })); document.body.style.height = ''; } function simulateWindowResize(_ref6) { var _ref6$height = _ref6.height, height = _ref6$height === undefined ? 0 : _ref6$height, _ref6$width = _ref6.width, width = _ref6$width === undefined ? 0 : _ref6$width; window.innerHeight = height; window.innerWidth = width; document.dispatchEvent(new window.Event('resize', { bubbles: true })); } describe('WindowScroller', function () { // Set default window height and scroll position between tests beforeEach(function () { window.scrollY = 0; window.scrollX = 0; window.innerHeight = 500; window.innerWidth = 500; }); // Starts updating scrollTop only when the top position is reached it('should have correct top and left properties to be defined on :_positionFromTop and :_positionFromLeft', function () { var component = render(getMarkup()); var rendered = findDOMNode(component); var _rendered$getBounding = rendered.getBoundingClientRect(), top = _rendered$getBounding.top, left = _rendered$getBounding.left; expect(component._positionFromTop).toEqual(top); expect(component._positionFromLeft).toEqual(left); }); // Test edge-case reported in bvaughn/react-virtualized/pull/346 it('should have correct top and left properties to be defined on :_positionFromTop and :_positionFromLeft if documentElement is scrolled', function () { render.unmount(); // Simulate scrolled documentElement var component = render(getMarkup({ documentOffset: -100 })); var rendered = findDOMNode(component); var _rendered$getBounding2 = rendered.getBoundingClientRect(), top = _rendered$getBounding2.top, left = _rendered$getBounding2.left; expect(component._positionFromTop).toEqual(top + 100); expect(component._positionFromLeft).toEqual(left + 100); // Reset override delete document.documentElement.getBoundingClientRect; }); it('inherits the window height and passes it to child component', function () { var component = render(getMarkup()); var rendered = findDOMNode(component); expect(component.state.height).toEqual(window.innerHeight); expect(component.state.height).toEqual(500); expect(rendered.textContent).toContain('height:500'); }); it('should restore pointerEvents on body after IS_SCROLLING_TIMEOUT', function () { var _ref7 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee(done) { return _regeneratorRuntime.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: render(getMarkup()); document.body.style.pointerEvents = 'all'; simulateWindowScroll({ scrollY: 5000 }); expect(document.body.style.pointerEvents).toEqual('none'); _context.next = 6; return new _Promise(function (resolve) { return setTimeout(resolve, IS_SCROLLING_TIMEOUT); }); case 6: expect(document.body.style.pointerEvents).toEqual('all'); done(); case 8: case 'end': return _context.stop(); } } }, _callee, _this); })); return function (_x2) { return _ref7.apply(this, arguments); }; }()); it('should restore pointerEvents on body after unmount', function () { render(getMarkup()); document.body.style.pointerEvents = 'all'; simulateWindowScroll({ scrollY: 5000 }); expect(document.body.style.pointerEvents).toEqual('none'); render.unmount(); expect(document.body.style.pointerEvents).toEqual('all'); }); describe('onScroll', function () { it('should trigger callback when window scrolls', function () { var _ref8 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee2(done) { var onScroll; return _regeneratorRuntime.wrap(function _callee2$(_context2) { while (1) { switch (_context2.prev = _context2.next) { case 0: onScroll = jest.fn(); render(getMarkup({ onScroll: onScroll })); simulateWindowScroll({ scrollY: 5000 }); // Allow scrolling timeout to complete so that the component computes state _context2.next = 5; return new _Promise(function (resolve) { return setTimeout(resolve, 150); }); case 5: expect(onScroll).toHaveBeenCalledWith({ scrollLeft: 0, scrollTop: 5000 }); simulateWindowScroll({ scrollX: 2500, scrollY: 5000 }); // Allow scrolling timeout to complete so that the component computes state _context2.next = 9; return new _Promise(function (resolve) { return setTimeout(resolve, 150); }); case 9: expect(onScroll).toHaveBeenCalledWith({ scrollLeft: 2500, scrollTop: 5000 }); done(); case 11: case 'end': return _context2.stop(); } } }, _callee2, _this); })); return function (_x3) { return _ref8.apply(this, arguments); }; }()); it('should update :scrollTop when window is scrolled', function () { var _ref9 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee3(done) { var component, rendered, componentScrollTop; return _regeneratorRuntime.wrap(function _callee3$(_context3) { while (1) { switch (_context3.prev = _context3.next) { case 0: component = render(getMarkup()); rendered = findDOMNode(component); // Initial load of the component should have 0 scrollTop expect(rendered.textContent).toContain('scrollTop:0'); simulateWindowScroll({ scrollY: 5000 }); // Allow scrolling timeout to complete so that the component computes state _context3.next = 6; return new _Promise(function (resolve) { return setTimeout(resolve, 150); }); case 6: componentScrollTop = window.scrollY - component._positionFromTop; expect(component.state.scrollTop).toEqual(componentScrollTop); expect(rendered.textContent).toContain('scrollTop:' + componentScrollTop); done(); case 10: case 'end': return _context3.stop(); } } }, _callee3, _this); })); return function (_x4) { return _ref9.apply(this, arguments); }; }()); it('should specify :isScrolling when scrolling and reset after scrolling', function () { var _ref10 = _asyncToGenerator(_regeneratorRuntime.mark(function _callee4(done) { var component, rendered; return _regeneratorRuntime.wrap(function _callee4$(_context4) { while (1) { switch (_context4.prev = _context4.next) { case 0: component = render(getMarkup()); rendered = findDOMNode(component); simulateWindowScroll({ scrollY: 5000 }); expect(rendered.textContent).toContain('isScrolling:true'); _context4.next = 6; return new _Promise(function (resolve) { return setTimeout(resolve, 250); }); case 6: expect(rendered.textContent).toContain('isScrolling:false'); done(); case 8: case 'end': return _context4.stop(); } } }, _callee4, _this); })); return function (_x5) { return _ref10.apply(this, arguments); }; }()); }); describe('onResize', function () { it('should trigger callback when window resizes', function () { var onResizeCalls = []; render(getMarkup({ onResize: function onResize(params) { return onResizeCalls.push(params); } })); simulateWindowResize({ height: 1000, width: 1024 }); expect(onResizeCalls.length).toEqual(1); expect(onResizeCalls[0]).toEqual({ height: 1000, width: 1024 }); }); it('should update height when window resizes', function () { var component = render(getMarkup()); var rendered = findDOMNode(component); // Initial load of the component should have the same window height = 500 expect(component.state.height).toEqual(window.innerHeight); expect(component.state.height).toEqual(500); expect(rendered.textContent).toContain('height:500'); simulateWindowResize({ height: 1000 }); expect(component.state.height).toEqual(window.innerHeight); expect(component.state.height).toEqual(1000); expect(rendered.textContent).toContain('height:1000'); }); }); describe('updatePosition', function () { it('should calculate the initial offset from the top of the page when mounted', function () { var windowScroller = void 0; render(getMarkup({ headerElements: React.createElement('div', { style: { height: 100 } }), ref: function ref(_ref11) { windowScroller = _ref11; } })); expect(windowScroller._positionFromTop).toBe(100); }); it('should recalculate the offset from the top when the window resizes', function () { var windowScroller = void 0; render(getMarkup({ headerElements: React.createElement('div', { id: 'header', style: { height: 100, width: 150 } }), ref: function ref(_ref12) { windowScroller = _ref12; } })); expect(windowScroller._positionFromTop).toBe(100); expect(windowScroller._positionFromLeft).toBe(150); mockGetBoundingClientRectForHeader({ height: 200, width: 300 }); expect(windowScroller._positionFromTop).toBe(100); expect(windowScroller._positionFromLeft).toBe(150); simulateWindowResize({ height: 1000, width: 1000 }); expect(windowScroller._positionFromTop).toBe(200); expect(windowScroller._positionFromLeft).toBe(300); }); it('should recalculate the offset from the top if called externally', function () { var windowScroller = void 0; render(getMarkup({ headerElements: React.createElement('div', { id: 'header', style: { height: 100, width: 150 } }), ref: function ref(_ref13) { windowScroller = _ref13; } })); expect(windowScroller._positionFromTop).toBe(100); expect(windowScroller._positionFromLeft).toBe(150); mockGetBoundingClientRectForHeader({ height: 200, width: 300 }); windowScroller.updatePosition(); expect(windowScroller._positionFromTop).toBe(200); expect(windowScroller._positionFromLeft).toBe(300); }); }); });