Repository: milosjanda/react-scroll-up
Branch: master
Commit: 7c70c91b3c93
Files: 11
Total size: 32.8 KB
Directory structure:
gitextract_339vejfy/
├── .gitignore
├── .npmignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── index.d.ts
├── index.js
├── package.json
├── scrollUp.jsx
└── test/
└── test.jsx
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Dependency directory
# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
node_modules
npm-debug.log
# IntelliJ project files
.idea
package-lock.json
.DS_Store
================================================
FILE: .npmignore
================================================
.idea
node_modules
npm-debug.log
scrollUp.jsx
================================================
FILE: .travis.yml
================================================
language: node_js
node_js:
- "12"
================================================
FILE: CHANGELOG.md
================================================
# [1.4.0]
* **Compatibility:** update dependecies to react 18
# [1.3.7]
* **Internal:** Update node version on test
# [1.3.6]
* **Internal:** Update react dependency and build
# [1.3.5]
* **Internal:** Add typescript declaration file
# [1.3.4]
* **Feature:** Add onShow and onHide react props
# [1.3.3]
* **Bugfix:** Fix peer dependencies to be compliant with semver ranges
# [1.3.2]
* **Compatibility:** update dependecies to react 16
# [1.3.1]
* **Bugfix:** Merge style property instead of override it
# [1.3.0]
* **Internal:** Rewrite source to ES6 syntax
* **Performance:**: Better performance in handleScroll
# [1.2.3]
* **Compatibility:** Fix deprecated React.PropTypes
# [1.2.2]
* **Compatibility:** Fix deprecated React.createClass
# [1.2.1]
* **Bugfix:** fix require of detect-passive-events
# [1.2.0]
* **Performance:** use of passive listeners to improve scrolling performance
# [1.1.5]
* **Compatibility:** update dependecies to react 15
# [1.1.4]
* **Compatibility:** update for react 15.0
# [1.1.3]
* **Compatibility:** Replace function window.scrollY with window.pageYOffset for better compatibility
# [1.1.2]
* **Bugfix:** Fix stop scrolling if top position reached and add touch events
* **Bugfix:** Show element after browser refresh, if page is under top position
# [1.1.1]
* **Dependency:** update tween function
* **Dependency:** update dev dependency
* **NPM:** Update npmignore
# [1.1.0]
### Other
* **Compatibility:** update for react 0.14
# [1.0.4]
### Bug
* **Dependency:** fix wrong dependencies
# [1.0.3]
### Bug
* **Dependency:** fix wrong dependencies
# [1.0.2]
### Bug
* **Visibility:** set visibility hidden after hide button
* **Dependency:** Update dependency information
### Other
* **Default value:** change default value of easing
# [1.0.1]
### Feature
* **Performance:** animate over requestAnimationFrame
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2015 Miloš Janda
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# react-scroll-up
[](https://badge.fury.io/js/react-scroll-up)
[](https://github.com/milosjanda/react-scroll-up/blob/master/LICENSE)
[]()
[]()
[](https://travis-ci.org/milosjanda/react-scroll-up)
[]()
React component to add custom button (it can be something what you want) for scroll to top of page.
Library uses [requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame),
if you want better browser compatibility (IE9 and older), you can use something like [https://gist.github.com/paulirish/1579671].
## Install
```npm
npm install react-scroll-up
```
## How to use it
[Live demo](http://milosjanda.github.io/react-scroll-up/)
You have to define children element, for example `UP`
```jsx
UP
```
## Parameters
### showUnder:number in px (required)
What position (and below) the button will be displayed.
### topPosition:number in px (optional)
default: 0
The position to which the scrollbar be moved after clicked.
### easing:string (optional)
default: easeOutCubic
Type of scrolling easing. You can specify some of this type of easing: https://github.com/chenglou/tween-functions
In graphical representation: http://sole.github.io/tween.js/examples/03_graphs.html
### duration:number in miliseconds (optional)
default: 250
Time to reach the `topPosition`
### onShow:function (optional)
Callback function to be called when the button is being displayed.
### onHide:function (optional)
Callback function to be called when the button is being hidden.
### style:object (optional)
default:
```javascript
{
position: 'fixed',
bottom: 50,
right: 30,
cursor: 'pointer',
transitionDuration: '0.2s',
transitionTimingFunction: 'linear',
transitionDelay: '0s'
}
```
You can specify you own style and position of the button.
Hide/show button is based on opacity, so this styles `opacity` and `transitionProperty` will be all time overwrite.
If you can positioned button to left site, you have to reset css property `right: 'auto'`, and similar.
================================================
FILE: index.d.ts
================================================
declare module 'react-scroll-up' {
import React, { ReactNode } from 'react';
export interface ScrollToTopProps {
showUnder: number,
topPosition?: number,
easing?: 'linear' | 'easeInQuad' | 'easeOutQuad' | 'easeInOutQuad' | 'easeInCubic' |
'easeOutCubic' | 'easeInOutCubic' | 'easeInQuart' | 'easeOutQuart' | 'easeInOutQuart' | 'easeInQuint' |
'easeOutQuint' | 'easeInOutQuint' | 'easeInSine' | 'easeOutSine' | 'easeInOutSine' | 'easeInExpo' | 'easeOutExpo' |
'easeInOutExpo' | 'easeInCirc' | 'easeOutCirc' | 'easeInOutCirc' | 'easeInElastic' | 'easeOutElastic' |
'easeInOutElastic' | 'easeInBack' | 'easeOutBack' | 'easeInOutBack' | 'easeInBounce' | 'easeOutBounce' |
'easeInOutBounce',
duration?: number,
style?: object,
onShow?: () => void,
onHide?: () => void,
children?: ReactNode
}
export default class ScrollToTop extends React.Component {}
}
================================================
FILE: index.js
================================================
/**
* @author Milos Janda
* @licence MIT
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _react = require('react');
var _react2 = _interopRequireDefault(_react);
var _propTypes = require('prop-types');
var _propTypes2 = _interopRequireDefault(_propTypes);
var _tweenFunctions = require('tween-functions');
var _tweenFunctions2 = _interopRequireDefault(_tweenFunctions);
var _detectPassiveEvents = require('detect-passive-events');
var _objectAssign = require('object-assign');
var _objectAssign2 = _interopRequireDefault(_objectAssign);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var ScrollUp = function (_React$Component) {
_inherits(ScrollUp, _React$Component);
function ScrollUp(props) {
_classCallCheck(this, ScrollUp);
// set default state
var _this = _possibleConstructorReturn(this, (ScrollUp.__proto__ || Object.getPrototypeOf(ScrollUp)).call(this, props));
_this.state = { show: false };
// default property `data`
_this.data = {
startValue: 0,
currentTime: 0, // store current time of animation
startTime: null,
rafId: null
};
// bind
_this.handleClick = _this.handleClick.bind(_this);
_this.handleScroll = _this.handleScroll.bind(_this);
_this.scrollStep = _this.scrollStep.bind(_this);
_this.stopScrolling = _this.stopScrolling.bind(_this);
return _this;
}
_createClass(ScrollUp, [{
key: 'shouldComponentUpdate',
value: function shouldComponentUpdate(nextProps, nextState) {
return nextState.show !== this.state.show;
}
}, {
key: 'componentDidMount',
value: function componentDidMount() {
this.handleScroll(); // initialize state
// Add all listeners which can start scroll
window.addEventListener('scroll', this.handleScroll);
window.addEventListener("wheel", this.stopScrolling, _detectPassiveEvents.supportsPassiveEvents ? { passive: true } : false);
window.addEventListener("touchstart", this.stopScrolling, _detectPassiveEvents.supportsPassiveEvents ? { passive: true } : false);
}
}, {
key: 'componentWillUnmount',
value: function componentWillUnmount() {
// Remove all listeners which was registered
window.removeEventListener('scroll', this.handleScroll);
window.removeEventListener("wheel", this.stopScrolling, false);
window.removeEventListener("touchstart", this.stopScrolling, false);
}
/**
* call onShow callback if passed valid props
*/
}, {
key: 'notifyOnShow',
value: function notifyOnShow() {
if (this.props.onShow && typeof this.props.onShow === "function") {
this.props.onShow();
}
}
/**
* call onHide callback if passed valid props
*/
}, {
key: 'notifyOnHide',
value: function notifyOnHide() {
if (this.props.onHide && typeof this.props.onHide === "function") {
this.props.onHide();
}
}
/**
* Evaluate show/hide this component, depend on new position
*/
}, {
key: 'handleScroll',
value: function handleScroll() {
if (window.pageYOffset > this.props.showUnder) {
if (!this.state.show) {
this.setState({ show: true });
this.notifyOnShow();
}
} else {
if (this.state.show) {
this.setState({ show: false });
this.notifyOnHide();
}
}
}
/**
* Handle click on the button
*/
}, {
key: 'handleClick',
value: function handleClick() {
this.stopScrolling();
this.data.startValue = window.pageYOffset;
this.data.currentTime = 0;
this.data.startTime = null;
this.data.rafId = window.requestAnimationFrame(this.scrollStep);
}
/**
* Calculate new position
* and scroll screen to new position or stop scrolling
* @param timestamp
*/
}, {
key: 'scrollStep',
value: function scrollStep(timestamp) {
if (!this.data.startTime) {
this.data.startTime = timestamp;
}
this.data.currentTime = timestamp - this.data.startTime;
var position = _tweenFunctions2.default[this.props.easing](this.data.currentTime, this.data.startValue, this.props.topPosition, this.props.duration);
if (window.pageYOffset <= this.props.topPosition) {
this.stopScrolling();
} else {
window.scrollTo(window.pageYOffset, position);
this.data.rafId = window.requestAnimationFrame(this.scrollStep);
}
}
/**
* Stop Animation Frame
*/
}, {
key: 'stopScrolling',
value: function stopScrolling() {
window.cancelAnimationFrame(this.data.rafId);
}
/**
* Render component
*/
}, {
key: 'render',
value: function render() {
var propStyle = this.props.style;
var element = _react2.default.createElement(
'div',
{ style: propStyle, onClick: this.handleClick },
this.props.children
);
var style = (0, _objectAssign2.default)({}, ScrollUp.defaultProps.style);
style = (0, _objectAssign2.default)(style, propStyle);
style.opacity = this.state.show ? 1 : 0;
style.visibility = this.state.show ? 'visible' : 'hidden';
style.transitionProperty = 'opacity, visibility';
return _react2.default.cloneElement(element, { style: style });
}
}]);
return ScrollUp;
}(_react2.default.Component);
// Set default props
exports.default = ScrollUp;
ScrollUp.defaultProps = {
duration: 250,
easing: 'easeOutCubic',
style: {
position: 'fixed',
bottom: 50,
right: 30,
cursor: 'pointer',
transitionDuration: '0.2s',
transitionTimingFunction: 'linear',
transitionDelay: '0s'
},
topPosition: 0
};
// Set validation property types
ScrollUp.propTypes = {
topPosition: _propTypes2.default.number,
showUnder: _propTypes2.default.number.isRequired, // show button under this position,
easing: _propTypes2.default.oneOf(['linear', 'easeInQuad', 'easeOutQuad', 'easeInOutQuad', 'easeInCubic', 'easeOutCubic', 'easeInOutCubic', 'easeInQuart', 'easeOutQuart', 'easeInOutQuart', 'easeInQuint', 'easeOutQuint', 'easeInOutQuint', 'easeInSine', 'easeOutSine', 'easeInOutSine', 'easeInExpo', 'easeOutExpo', 'easeInOutExpo', 'easeInCirc', 'easeOutCirc', 'easeInOutCirc', 'easeInElastic', 'easeOutElastic', 'easeInOutElastic', 'easeInBack', 'easeOutBack', 'easeInOutBack', 'easeInBounce', 'easeOutBounce', 'easeInOutBounce']),
duration: _propTypes2.default.number, // seconds
style: _propTypes2.default.object,
onShow: _propTypes2.default.func,
onHide: _propTypes2.default.func
};
================================================
FILE: package.json
================================================
{
"name": "react-scroll-up",
"version": "1.4.0",
"description": "React component to render element for scroll to top of page",
"author": "Milos Janda ",
"scripts": {
"test": "mocha --require babel-core/register test/test.jsx",
"build": "babel scrollUp.jsx --out-file index.js",
"prepublish": "npm run test && npm run build"
},
"main": "index.js",
"repository": {
"type": "git",
"url": "https://github.com/milosjanda/react-scroll-up.git"
},
"bug": {
"url": "https://github.com/milosjanda/react-scroll-up/issues"
},
"keywords": [
"scroll",
"scrollUp",
"scrollToTop",
"animation",
"effects",
"react",
"react-component"
],
"babel": {
"presets": [
"es2015",
"react"
]
},
"dependencies": {
"detect-passive-events": "^2.0.2",
"object-assign": "^4.0.1",
"prop-types": "^15.5.8",
"tween-functions": "^1.1.0"
},
"peerDependencies": {
"react": "0.13 - 18"
},
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-core": "^6.26.3",
"babel-preset-es2015": "6.24.1",
"babel-preset-react": "6.24.1",
"chai": "^4.2.0",
"chai-enzyme": "^1.0.0-beta.1",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.5",
"jsdom": "^16.4.0",
"mocha": "^8.2.1",
"raf": "^3.4.1",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-test-renderer": "^18.0.0",
"sinon": "^9.2.1"
},
"readmeFilename": "README.md",
"license": "MIT"
}
================================================
FILE: scrollUp.jsx
================================================
/**
* @author Milos Janda
* @licence MIT
*/
'use strict';
import React from 'react';
import PropTypes from 'prop-types';
import TweenFunctions from 'tween-functions';
import { supportsPassiveEvents } from 'detect-passive-events';
import objectAssign from 'object-assign';
export default class ScrollUp extends React.Component {
constructor(props) {
super(props);
// set default state
this.state = {show: false};
// default property `data`
this.data = {
startValue: 0,
currentTime: 0, // store current time of animation
startTime: null,
rafId: null
};
// bind
this.handleClick = this.handleClick.bind(this);
this.handleScroll = this.handleScroll.bind(this);
this.scrollStep = this.scrollStep.bind(this);
this.stopScrolling = this.stopScrolling.bind(this);
}
shouldComponentUpdate(nextProps, nextState) {
return nextState.show !== this.state.show;
}
componentDidMount() {
this.handleScroll(); // initialize state
// Add all listeners which can start scroll
window.addEventListener('scroll', this.handleScroll);
window.addEventListener("wheel", this.stopScrolling, supportsPassiveEvents ? { passive: true } : false);
window.addEventListener("touchstart", this.stopScrolling, supportsPassiveEvents ? { passive: true } : false);
}
componentWillUnmount() {
// Remove all listeners which was registered
window.removeEventListener('scroll', this.handleScroll);
window.removeEventListener("wheel", this.stopScrolling, false);
window.removeEventListener("touchstart", this.stopScrolling, false);
}
/**
* call onShow callback if passed valid props
*/
notifyOnShow() {
if (this.props.onShow && typeof this.props.onShow === "function") {
this.props.onShow();
}
}
/**
* call onHide callback if passed valid props
*/
notifyOnHide() {
if (this.props.onHide && typeof this.props.onHide === "function") {
this.props.onHide();
}
}
/**
* Evaluate show/hide this component, depend on new position
*/
handleScroll() {
if (window.pageYOffset > this.props.showUnder) {
if (!this.state.show) {
this.setState({show: true});
this.notifyOnShow();
}
} else {
if (this.state.show) {
this.setState({show: false});
this.notifyOnHide();
}
}
}
/**
* Handle click on the button
*/
handleClick() {
this.stopScrolling();
this.data.startValue = window.pageYOffset;
this.data.currentTime = 0;
this.data.startTime = null;
this.data.rafId = window.requestAnimationFrame(this.scrollStep);
}
/**
* Calculate new position
* and scroll screen to new position or stop scrolling
* @param timestamp
*/
scrollStep(timestamp) {
if (!this.data.startTime) {
this.data.startTime = timestamp;
}
this.data.currentTime = timestamp - this.data.startTime;
let position = TweenFunctions[this.props.easing](
this.data.currentTime,
this.data.startValue,
this.props.topPosition,
this.props.duration
);
if (window.pageYOffset <= this.props.topPosition) {
this.stopScrolling();
} else {
window.scrollTo(window.pageYOffset, position);
this.data.rafId = window.requestAnimationFrame(this.scrollStep);
}
}
/**
* Stop Animation Frame
*/
stopScrolling() {
window.cancelAnimationFrame(this.data.rafId);
}
/**
* Render component
*/
render() {
let propStyle = this.props.style;
let element =
{this.props.children}
;
let style = objectAssign({}, ScrollUp.defaultProps.style);
style = objectAssign(style, propStyle);
style.opacity = (this.state.show ? 1 : 0);
style.visibility = (this.state.show ? 'visible' : 'hidden');
style.transitionProperty = 'opacity, visibility';
return React.cloneElement(element, {style: style});
}
}
// Set default props
ScrollUp.defaultProps = {
duration: 250,
easing: 'easeOutCubic',
style: {
position: 'fixed',
bottom: 50,
right: 30,
cursor: 'pointer',
transitionDuration: '0.2s',
transitionTimingFunction: 'linear',
transitionDelay: '0s'
},
topPosition: 0
};
// Set validation property types
ScrollUp.propTypes = {
topPosition: PropTypes.number,
showUnder: PropTypes.number.isRequired, // show button under this position,
easing: PropTypes.oneOf(['linear', 'easeInQuad', 'easeOutQuad', 'easeInOutQuad', 'easeInCubic',
'easeOutCubic', 'easeInOutCubic', 'easeInQuart', 'easeOutQuart', 'easeInOutQuart', 'easeInQuint',
'easeOutQuint', 'easeInOutQuint', 'easeInSine', 'easeOutSine', 'easeInOutSine', 'easeInExpo', 'easeOutExpo',
'easeInOutExpo', 'easeInCirc', 'easeOutCirc', 'easeInOutCirc', 'easeInElastic', 'easeOutElastic',
'easeInOutElastic', 'easeInBack', 'easeOutBack', 'easeInOutBack', 'easeInBounce', 'easeOutBounce',
'easeInOutBounce']),
duration: PropTypes.number, // seconds
style: PropTypes.object,
onShow: PropTypes.func,
onHide: PropTypes.func
};
================================================
FILE: test/test.jsx
================================================
// JSDom is used to allow the tests to run right from the command line (no browsers needed)
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
const dom = new JSDOM(`Test
`);
// noinspection JSConstantReassignment
global.window = dom.window;
// noinspection JSConstantReassignment
global.document = dom.window.document;
// Also apply a requestAnimationFrame polyfill
require('raf').polyfill();
import React from 'react';
import { after, before, beforeEach, describe, it } from "mocha";
import sinon from 'sinon';
import { expect } from 'chai';
import { shallow } from 'enzyme'; // https://github.com/airbnb/enzyme/issues/465 shallow vs mount vs render
import chai from 'chai'; // https://github.com/producthunt/chai-enzyme#setup
import chaiEnzyme from 'chai-enzyme';
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
chai.use(chaiEnzyme()); // Note the invocation at the end
import TestUtils from 'react-dom/test-utils';
import ScrollUp from '../scrollUp';
Enzyme.configure({ adapter: new Adapter() });
// describe makes a test group
describe(' states', function () {
// This will be run before each test to reset the scroll position
beforeEach(() => {
window.pageYOffset = 0;
});
// and each `it` function describes an individual test
it('is hidden when first rendered', function () {
let renderedComponent = TestUtils.renderIntoDocument(
UP
);
expect(renderedComponent.state.show).to.be.false;
});
it('is shown if the page is scrolled past the `showUnder` point', function () {
let renderedComponent = TestUtils.renderIntoDocument(
UP
);
// Set the scroll position to 200 and trigger the event manually
window.pageYOffset = 200;
renderedComponent.handleScroll();
expect(renderedComponent.state.show).to.be.true;
});
});
// describe makes a test group
describe(' move 1', function () {
let scrollToSpy;
let renderedComponent;
before(() => {
window.pageYOffset = 0;
renderedComponent = TestUtils.renderIntoDocument(
UP
);
// "stub" the window.scrollTo function (because we want to see how it's called)
scrollToSpy = sinon.stub(global.window, 'scrollTo').callsFake( (x, y) => {
window.pageXOffset = x;
window.pageYOffset = y;
renderedComponent.handleScroll(); // And make sure to trigger the handleScroll for each call
});
});
after(() => {
scrollToSpy.restore();
});
it('scrolls back up to the top when clicked', function (done) {
// Ensure topPosition is set correctly
expect(renderedComponent.props.topPosition).to.equal(0);
// Set the scroll position to 200 and trigger the event manually
window.pageYOffset = 200;
renderedComponent.handleScroll();
// Now activate the click function
renderedComponent.handleClick();
// Give it a bit to scroll back up
setTimeout(() => {
expect(scrollToSpy.lastCall.args[1]).to.within(-0.1, 0.1);
expect(renderedComponent.state.show).to.be.false;
done();
}, 500);
});
});
// describe makes a test group
describe(' move 2', function () {
let scrollToSpy;
let renderedComponent;
before(() => {
window.pageYOffset = 0;
renderedComponent = TestUtils.renderIntoDocument(
UP
);
// "stub" the window.scrollTo function (because we want to see how it's called)
scrollToSpy = sinon.stub(global.window, 'scrollTo').callsFake( (x, y) => {
window.pageXOffset = x;
window.pageYOffset = y;
renderedComponent.handleScroll(); // And make sure to trigger the handleScroll for each call
});
});
after(() => {
scrollToSpy.restore();
});
it('scrolls to `topPosition` when clicked', (done) => {
// Ensure topPosition is set correctly
expect(renderedComponent.props.topPosition).to.equal(100);
// Set the scroll position to 200 and trigger the event manually
window.pageYOffset = 200;
renderedComponent.handleScroll();
// Now activate the click function
renderedComponent.handleClick();
// Give it a bit to scroll back up
setTimeout(() => {
expect(scrollToSpy.lastCall.args[1]).to.be.within(95, 105);
expect(renderedComponent.state.show).to.be.false;
done();
}, 500);
});
});
// describe makes a test group
describe(' Styles', function () {
it('check rendered styles - default values - hidden', () => {
const wrapper = shallow(
UP
);
wrapper.setState({show: false});
// default styles
expect(wrapper).to.have.style("position", 'fixed');
expect(wrapper).to.have.style("bottom", '50px');
expect(wrapper).to.have.style("right", '30px');
expect(wrapper).to.have.style("cursor", 'pointer');
expect(wrapper).to.have.style("transition-duration", '0.2s');
expect(wrapper).to.have.style("transition-timing-function", 'linear');
expect(wrapper).to.have.style("transition-delay", '0s');
expect(wrapper).to.have.style("opacity" , '0');
expect(wrapper).to.have.style("visibility", 'hidden');
expect(wrapper).to.have.style("transition-property", 'opacity, visibility');
});
it('check rendered styles - default values - visible', () => {
const wrapper = shallow(
UP
);
wrapper.setState({show: true});
// default styles
expect(wrapper).to.have.style("position", 'fixed');
expect(wrapper).to.have.style("bottom", '50px');
expect(wrapper).to.have.style("right", '30px');
expect(wrapper).to.have.style("cursor", 'pointer');
expect(wrapper).to.have.style("transition-duration", '0.2s');
expect(wrapper).to.have.style("transition-timing-function", 'linear');
expect(wrapper).to.have.style("transition-delay", '0s');
expect(wrapper).to.have.style("opacity" , '1');
expect(wrapper).to.have.style("visibility", 'visible');
expect(wrapper).to.have.style("transition-property", 'opacity, visibility');
});
it('check rendered styles - custom values', () => {
const wrapper = shallow(
UP
);
// to check override opacity
wrapper.setState({show: true});
// default styles
expect(wrapper).to.have.style("position", 'fixed');
expect(wrapper).to.have.style("bottom", '50px');
expect(wrapper).to.have.style("cursor", 'pointer');
expect(wrapper).to.have.style("transition-duration", '0.2s');
expect(wrapper).to.have.style("transition-timing-function", 'linear');
expect(wrapper).to.have.style("transition-delay", '0s');
// overrides by props, but have to still value respective state
expect(wrapper).to.have.style("opacity" , '1');
expect(wrapper).to.have.style("visibility", 'visible');
expect(wrapper).to.have.style("transition-property", 'opacity, visibility');
// overrides default values
expect(wrapper).to.have.style("right", '20px');
// props styles
expect(wrapper).to.have.style("z-index", '3');
});
});
// describe makes a test group
describe(' children', function () {
it('check if children is rendered ', () => {
window.pageYOffset = 500;
const wrapper = shallow(
UP
);
expect(wrapper.find('span')).to.have.text('UP');
});
});
// describe makes a test group
describe(' onShow onHide props', function () {
// This will be run before each test to reset the scroll position
beforeEach(() => {
window.pageYOffset = 0;
});
it('check onShow callback is working properly', function () {
let calledOnShow = false;
let renderedComponent = TestUtils.renderIntoDocument(
{ calledOnShow = true }}>
UP
);
// is hidden when first rendered
expect(renderedComponent.state.show).to.be.false;
// Set the scroll position to 200 and trigger the event manually
window.pageYOffset = 200;
renderedComponent.handleScroll();
expect(renderedComponent.state.show).to.be.true; // button is now displayed
expect(calledOnShow).to.be.true; // and callback was called
});
it('check onHide callback is working properly', function () {
let calledOnHide = false;
let renderedComponent = TestUtils.renderIntoDocument(
{ calledOnHide = true }}>
UP
);
// is hidden when first rendered
expect(renderedComponent.state.show).to.be.false;
// Set the scroll position to 200 and trigger the event manually
window.pageYOffset = 200;
renderedComponent.handleScroll();
expect(renderedComponent.state.show).to.be.true; // button is now displayed
expect(calledOnHide).to.be.false; // and callback was not yet called
// Set the scroll position to 50 and trigger the event manually
window.pageYOffset = 50;
renderedComponent.handleScroll();
expect(renderedComponent.state.show).to.be.false; // button is now hidden
expect(calledOnHide).to.be.true; // and callback was called
});
});