Skip to content

Commit

Permalink
Merge pull request #86 from taion/createScrollBehavior
Browse files Browse the repository at this point in the history
Add createScrollBehavior hook
  • Loading branch information
taion committed Nov 8, 2017
2 parents fb8d995 + 5cf4d36 commit aabf205
Show file tree
Hide file tree
Showing 7 changed files with 320 additions and 254 deletions.
9 changes: 9 additions & 0 deletions README.md
Expand Up @@ -66,6 +66,15 @@ useScroll((prevRouterProps, { routes }) => {
});
```

You can customize `useScroll` even further by providing a configuration object with a `createScrollBehavior` callback that creates the scroll behavior object. This allows using a custom subclass of `ScrollBehavior` from scroll-behavior with custom logic. When using a configuration object, you can specify the `shouldUpdateScroll` callback as above under the `shouldUpdateScroll` key.

```js
useScroll({
createScrollBehavior: (config) => new MyScrollBehavior(config),
shouldUpdateScroll,
});
```

### Scrolling elements other than `window`

Use `<ScrollContainer>` in components rendered by a router with the `useScroll` middleware to manage the scroll behavior of elements other than `window`. Each `<ScrollContainer>` must be given a unique `scrollKey`, and can be given an optional `shouldUpdateScroll` callback that behaves as above.
Expand Down
26 changes: 13 additions & 13 deletions package.json
Expand Up @@ -31,7 +31,7 @@
"homepage": "https://github.com/taion/react-router-scroll#readme",
"dependencies": {
"prop-types": "^15.6.0",
"scroll-behavior": "^0.9.3",
"scroll-behavior": "^0.9.5",
"warning": "^3.0.0"
},
"peerDependencies": {
Expand All @@ -48,35 +48,35 @@
"babel-plugin-add-module-exports": "^0.2.1",
"babel-plugin-dev-expression": "^0.2.1",
"babel-polyfill": "^6.26.0",
"babel-preset-env": "^1.6.0",
"babel-preset-env": "^1.6.1",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-1": "^6.24.1",
"chai": "^4.1.2",
"create-react-class": "^15.6.2",
"cross-env": "^5.0.5",
"cross-env": "^5.1.1",
"dirty-chai": "^2.0.1",
"dom-helpers": "^3.2.1",
"eslint": "^4.7.2",
"eslint-config-4catalyzer-react": "^0.3.2",
"eslint-plugin-import": "^2.7.0",
"eslint": "^4.10.0",
"eslint-config-4catalyzer-react": "^0.3.3",
"eslint-plugin-import": "^2.8.0",
"eslint-plugin-jsx-a11y": "^5.1.1",
"eslint-plugin-react": "^7.4.0",
"history": "^2.1.2",
"karma": "^1.7.1",
"karma-chrome-launcher": "^2.2.0",
"karma-firefox-launcher": "^1.0.1",
"karma-mocha": "^1.3.0",
"karma-mocha-reporter": "^2.2.4",
"karma-sinon-chai": "^1.3.2",
"karma-mocha-reporter": "^2.2.5",
"karma-sinon-chai": "^1.3.3",
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^2.0.4",
"mocha": "^3.5.3",
"karma-webpack": "^2.0.5",
"mocha": "^4.0.1",
"react": "^16.0.0",
"react-dom": "^16.0.0",
"react-router": "^2.8.1",
"rimraf": "^2.6.2",
"sinon": "^2.4.1",
"sinon-chai": "^2.13.0",
"webpack": "^3.6.0"
"sinon": "^4.1.2",
"sinon-chai": "^2.14.0",
"webpack": "^3.8.1"
}
}
4 changes: 2 additions & 2 deletions src/ScrollBehaviorContext.js
@@ -1,11 +1,11 @@
import PropTypes from 'prop-types';
import React from 'react';
import ScrollBehavior from 'scroll-behavior';

import StateStorage from './StateStorage';

const propTypes = {
shouldUpdateScroll: PropTypes.func,
createScrollBehavior: PropTypes.func.isRequired,
routerProps: PropTypes.object.isRequired,
children: PropTypes.element.isRequired,
};
Expand All @@ -21,7 +21,7 @@ class ScrollBehaviorContext extends React.Component {
const { routerProps } = props;
const { router } = routerProps;

this.scrollBehavior = new ScrollBehavior({
this.scrollBehavior = props.createScrollBehavior({
addTransitionHook: router.listenBefore,
stateStorage: new StateStorage(router),
getCurrentLocation: () => this.props.routerProps.location,
Expand Down
24 changes: 23 additions & 1 deletion src/useScroll.js
@@ -1,12 +1,34 @@
import React from 'react';
import ScrollBehavior from 'scroll-behavior';

import ScrollBehaviorContext from './ScrollBehaviorContext';

export default function useScroll(shouldUpdateScroll) {
function defaultCreateScrollBehavior(config) {
return new ScrollBehavior(config);
}

export default function useScroll(shouldUpdateScrollOrConfig) {
let shouldUpdateScroll;
let createScrollBehavior;

if (
!shouldUpdateScrollOrConfig ||
typeof shouldUpdateScrollOrConfig === 'function'
) {
shouldUpdateScroll = shouldUpdateScrollOrConfig;
createScrollBehavior = defaultCreateScrollBehavior;
} else {
({
shouldUpdateScroll,
createScrollBehavior = defaultCreateScrollBehavior,
} = shouldUpdateScrollOrConfig);
}

return {
renderRouterContext: (child, props) => (
<ScrollBehaviorContext
shouldUpdateScroll={shouldUpdateScroll}
createScrollBehavior={createScrollBehavior}
routerProps={props}
>
{child}
Expand Down
2 changes: 1 addition & 1 deletion test/run.js
@@ -1,6 +1,6 @@
export function delay(cb) {
// Give throttled scroll listeners time to settle down.
setTimeout(cb, 80);
requestAnimationFrame(() => requestAnimationFrame(cb));
}

export default function run(steps) {
Expand Down
69 changes: 69 additions & 0 deletions test/useScroll.test.js
Expand Up @@ -5,6 +5,7 @@ import createHashHistory from 'history/lib/createHashHistory';
import React from 'react';
import ReactDOM from 'react-dom';
import { applyRouterMiddleware, Router, useRouterHistory } from 'react-router';
import ScrollBehavior from 'scroll-behavior';

import StateStorage from '../src/StateStorage';
import useScroll from '../src/useScroll';
Expand Down Expand Up @@ -144,6 +145,74 @@ describe('useScroll', () => {
container,
);
});

it('should support a custom scroll behavior factory', (done) => {
class MyScrollBehavior extends ScrollBehavior {
scrollToTarget() {
window.scrollTo(0, 50);
}
}

const steps = [
() => {
history.push('/page2');
},
() => {
expect(scrollTop(window)).to.equal(50);

done();
},
];

ReactDOM.render(
<Router
history={history}
routes={routes}
render={applyRouterMiddleware(useScroll({
createScrollBehavior: config => new MyScrollBehavior(config),
}))}
onUpdate={run(steps)}
/>,
container,
);
});

it('should support fully custom behavior', (done) => {
class MyScrollBehavior extends ScrollBehavior {
scrollToTarget(element, target) {
element.scrollTo(20, target[1] + 10);
}
}

function shouldUpdateScroll() {
return [0, 50];
}

const steps = [
() => {
history.push('/page2');
},
() => {
expect(scrollLeft(window)).to.equal(20);
expect(scrollTop(window)).to.equal(60);

done();
},
];

ReactDOM.render(
<Router
history={history}
routes={routes}
render={applyRouterMiddleware(useScroll({
createScrollBehavior: config => new MyScrollBehavior(config),
shouldUpdateScroll,
}))}
onUpdate={run(steps)}
/>,
container,
);
});
});
});
});
Expand Down

0 comments on commit aabf205

Please sign in to comment.