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 `<span>UP</span>`
```jsx
<ScrollToTop showUnder={160}>
<span>UP</span>
</ScrollToTop>
```
## 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<ScrollToTopProps, any> {}
}
================================================
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 <milos.janda@gmail.com>",
"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 =
<div style={propStyle} onClick={this.handleClick}>
{this.props.children}
</div>;
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(`<!DOCTYPE html><html lang="en"><body><p>Test</p></body></html>`);
// 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('<ScrollUp/> 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(
<ScrollUp showUnder={100}>
<span>UP</span>
</ScrollUp>
);
expect(renderedComponent.state.show).to.be.false;
});
it('is shown if the page is scrolled past the `showUnder` point', function () {
let renderedComponent = TestUtils.renderIntoDocument(
<ScrollUp showUnder={100}>
<span>UP</span>
</ScrollUp>
);
// 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('<ScrollUp/> move 1', function () {
let scrollToSpy;
let renderedComponent;
before(() => {
window.pageYOffset = 0;
renderedComponent = TestUtils.renderIntoDocument(
<ScrollUp showUnder={100}>
<span>UP</span>
</ScrollUp>
);
// "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('<ScrollUp/> move 2', function () {
let scrollToSpy;
let renderedComponent;
before(() => {
window.pageYOffset = 0;
renderedComponent = TestUtils.renderIntoDocument(
<ScrollUp showUnder={100} topPosition={100}>
<span>UP</span>
</ScrollUp>
);
// "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('<ScrollUp/> Styles', function () {
it('check rendered styles - default values - hidden', () => {
const wrapper = shallow(
<ScrollUp showUnder={100}>
<span>UP</span>
</ScrollUp>
);
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(
<ScrollUp showUnder={100}>
<span>UP</span>
</ScrollUp>
);
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(
<ScrollUp showUnder={100}
style={{
zIndex: 3,
right: 20,
opacity: 0,
visibility: 'hidden',
transitionProperty: 'opacity'}}>
<span>UP</span>
</ScrollUp>
);
// 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('<ScrollUp/> children', function () {
it('check if children is rendered ', () => {
window.pageYOffset = 500;
const wrapper = shallow(
<ScrollUp showUnder={100}>
<span>UP</span>
</ScrollUp>
);
expect(wrapper.find('span')).to.have.text('UP');
});
});
// describe makes a test group
describe('<ScrollUp/> 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(
<ScrollUp showUnder={100} onShow={() => { calledOnShow = true }}>
<span>UP</span>
</ScrollUp>
);
// 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(
<ScrollUp showUnder={100} onHide={() => { calledOnHide = true }}>
<span>UP</span>
</ScrollUp>
);
// 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
});
});
gitextract_339vejfy/
├── .gitignore
├── .npmignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── index.d.ts
├── index.js
├── package.json
├── scrollUp.jsx
└── test/
└── test.jsx
SYMBOL INDEX (20 symbols across 3 files)
FILE: index.d.ts
type ScrollToTopProps (line 4) | interface ScrollToTopProps {
class ScrollToTop (line 20) | class ScrollToTop extends React.Component<ScrollToTopProps, any> {}
FILE: index.js
function defineProperties (line 12) | function defineProperties(target, props) { for (var i = 0; i < props.len...
function _interopRequireDefault (line 32) | function _interopRequireDefault(obj) { return obj && obj.__esModule ? ob...
function _classCallCheck (line 34) | function _classCallCheck(instance, Constructor) { if (!(instance instanc...
function _possibleConstructorReturn (line 36) | function _possibleConstructorReturn(self, call) { if (!self) { throw new...
function _inherits (line 38) | function _inherits(subClass, superClass) { if (typeof superClass !== "fu...
function ScrollUp (line 43) | function ScrollUp(props) {
FILE: scrollUp.jsx
class ScrollUp (line 14) | class ScrollUp extends React.Component {
method constructor (line 16) | constructor(props) {
method shouldComponentUpdate (line 37) | shouldComponentUpdate(nextProps, nextState) {
method componentDidMount (line 41) | componentDidMount() {
method componentWillUnmount (line 50) | componentWillUnmount() {
method notifyOnShow (line 60) | notifyOnShow() {
method notifyOnHide (line 69) | notifyOnHide() {
method handleScroll (line 78) | handleScroll() {
method handleClick (line 95) | handleClick() {
method scrollStep (line 109) | scrollStep(timestamp) {
method stopScrolling (line 134) | stopScrolling() {
method render (line 141) | render() {
Condensed preview — 11 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (35K chars).
[
{
"path": ".gitignore",
"chars": 193,
"preview": "# Dependency directory\n# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git\nnode_modules\nnpm"
},
{
"path": ".npmignore",
"chars": 46,
"preview": ".idea\nnode_modules\nnpm-debug.log\nscrollUp.jsx\n"
},
{
"path": ".travis.yml",
"chars": 36,
"preview": "language: node_js\nnode_js:\n - \"12\"\n"
},
{
"path": "CHANGELOG.md",
"chars": 1896,
"preview": "\n# [1.4.0]\n* **Compatibility:** update dependecies to react 18\n\n# [1.3.7]\n* **Internal:** Update node version on test\n\n#"
},
{
"path": "LICENSE",
"chars": 1079,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2015 Miloš Janda\n\nPermission is hereby granted, free of charge, to any person obtai"
},
{
"path": "README.md",
"chars": 2539,
"preview": "# react-scroll-up\n[](https://badge.fury.io/js/react-scroll-u"
},
{
"path": "index.d.ts",
"chars": 944,
"preview": "declare module 'react-scroll-up' {\n import React, { ReactNode } from 'react';\n\n export interface ScrollToTopProps {\n "
},
{
"path": "index.js",
"chars": 8920,
"preview": "/**\n * @author Milos Janda\n * @licence MIT\n */\n\n'use strict';\n\nObject.defineProperty(exports, \"__esModule\", {\n value"
},
{
"path": "package.json",
"chars": 1518,
"preview": "{\n \"name\": \"react-scroll-up\",\n \"version\": \"1.4.0\",\n \"description\": \"React component to render element for scroll to t"
},
{
"path": "scrollUp.jsx",
"chars": 5663,
"preview": "/**\n * @author Milos Janda\n * @licence MIT\n */\n\n'use strict';\n\nimport React from 'react';\nimport PropTypes from 'prop-t"
},
{
"path": "test/test.jsx",
"chars": 10733,
"preview": "// JSDom is used to allow the tests to run right from the command line (no browsers needed)\nconst jsdom = require(\"jsdom"
}
]
About this extraction
This page contains the full source code of the milosjanda/react-scroll-up GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 11 files (32.8 KB), approximately 8.2k tokens, and a symbol index with 20 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.