Skip to content

Commit

Permalink
feat(connectors): connectPagination (iteration2)
Browse files Browse the repository at this point in the history
  • Loading branch information
iam4x committed Mar 22, 2017
1 parent 6073d94 commit 8a615f6
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 133 deletions.
11 changes: 0 additions & 11 deletions src/connectors/pagination/__tests__/connectPagination-test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@


import sinon from 'sinon';

import jsHelper from 'algoliasearch-helper';
Expand Down Expand Up @@ -43,8 +41,6 @@ describe('connectPagination', () => {
expect(firstRenderingOptions.currentPage).toBe(0);
expect(firstRenderingOptions.nbHits).toBe(0);
expect(firstRenderingOptions.nbPages).toBe(0);
expect(firstRenderingOptions.shouldAutoHideContainer).toBe(true);
expect(firstRenderingOptions.showFirstLast).toBe(true);
}

widget.render({
Expand All @@ -69,22 +65,17 @@ describe('connectPagination', () => {
expect(secondRenderingOptions.currentPage).toBe(0);
expect(secondRenderingOptions.nbHits).toBe(1);
expect(secondRenderingOptions.nbPages).toBe(1);
expect(secondRenderingOptions.shouldAutoHideContainer).toBe(false);
expect(secondRenderingOptions.showFirstLast).toBe(true);
}
});

it('Provides a function to update the refinements at each step', () => {
const container = document.createElement('div');
const scrollTo = document.createElement('div');
scrollTo.scrollIntoView = sinon.stub();

const rendering = sinon.stub();
const makeWidget = connectPagination(rendering);

const widget = makeWidget({
container,
scrollTo,
});

const helper = jsHelper(fakeClient);
Expand All @@ -103,7 +94,6 @@ describe('connectPagination', () => {
setPage(2);
expect(helper.getPage()).toBe(2);
expect(helper.search.callCount).toBe(1);
expect(scrollTo.scrollIntoView.callCount).toBe(1);
}

widget.render({
Expand All @@ -119,7 +109,6 @@ describe('connectPagination', () => {
setPage(7);
expect(helper.getPage()).toBe(7);
expect(helper.search.callCount).toBe(2);
expect(scrollTo.scrollIntoView.callCount).toBe(2);
}
});
});
120 changes: 31 additions & 89 deletions src/connectors/pagination/connectPagination.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
import defaults from 'lodash/defaults';
import cx from 'classnames';
import {
bemHelper,
getContainerNode,
} from '../../lib/utils.js';
import {checkRendering} from '../../lib/utils.js';

const defaultLabels = {
previous: '‹',
next: '›',
first: '«',
last: '»',
};
const bem = bemHelper('ais-pagination');
const usage = `Usage:
var customPagination = connectPagination(function render(params, isFirstRendering) {
// params = {
// createURL,
// currentPage,
// nbHits,
// nbPages,
// setPage,
// }
});
search.addWidget(
customPagination({
[maxPages]
})
);
Full documentation available at https://community.algolia.com/instantsearch.js/connectors/connectPagination.html
`;

/**
* Add a pagination menu to navigate through the results
Expand All @@ -38,107 +43,44 @@ const bem = bemHelper('ais-pagination');
* @param {string|string[]} [options.cssClasses.last] CSS classes added to the last `<li>`
* @param {string|string[]} [options.cssClasses.active] CSS classes added to the active `<li>`
* @param {string|string[]} [options.cssClasses.disabled] CSS classes added to the disabled `<li>`
* @return {Object}
* @return {Object} widget
*/
const usage = `Usage:
pagination({
container,
[ cssClasses.{root,item,page,previous,next,first,last,active,disabled}={} ],
[ labels.{previous,next,first,last} ],
[ maxPages ],
[ padding=3 ],
[ showFirstLast=true ],
[ autoHideContainer=true ],
[ scrollTo='body' ]
})`;
const connectPagination = paginationRendering => ({
container,
cssClasses: userCssClasses = {},
labels: userLabels = {},
maxPages,
padding = 3,
showFirstLast = true,
autoHideContainer = true,
scrollTo: userScrollTo = 'body',
} = {}) => {
let scrollTo = userScrollTo;

if (!container) {
throw new Error(usage);
}

if (scrollTo === true) {
scrollTo = 'body';
}

const containerNode = getContainerNode(container);
const scrollToNode = scrollTo !== false ? getContainerNode(scrollTo) : false;

const cssClasses = {
root: cx(bem(null), userCssClasses.root),
item: cx(bem('item'), userCssClasses.item),
link: cx(bem('link'), userCssClasses.link),
page: cx(bem('item', 'page'), userCssClasses.page),
previous: cx(bem('item', 'previous'), userCssClasses.previous),
next: cx(bem('item', 'next'), userCssClasses.next),
first: cx(bem('item', 'first'), userCssClasses.first),
last: cx(bem('item', 'last'), userCssClasses.last),
active: cx(bem('item', 'active'), userCssClasses.active),
disabled: cx(bem('item', 'disabled'), userCssClasses.disabled),
};
export default function connectPagination(renderFn) {
checkRendering(renderFn, usage);

const labels = defaults(userLabels, defaultLabels);

return {
return ({maxPages}) => ({
init({helper, createURL}) {
this.setPage = page => {
helper.setPage(page);
if (scrollToNode !== false) {
scrollToNode.scrollIntoView();
}
helper.search();
};

this.createURL = state => page => createURL(state.setPage(page));

paginationRendering({
renderFn({
createURL: this.createURL(helper.state),
cssClasses,
currentPage: helper.getPage() || 0,
labels,
nbHits: 0,
nbPages: 0,
padding,
setPage: this.setPage,
shouldAutoHideContainer: autoHideContainer,
showFirstLast,
containerNode,
}, true);
},

getMaxPage(results) {
if (maxPages !== undefined) {
return Math.min(maxPages, results.nbPages);
}
return results.nbPages;
getMaxPage({nbPages}) {
return maxPages !== undefined
? Math.min(maxPages, nbPages)
: nbPages;
},

render({results, state}) {
paginationRendering({
renderFn({
createURL: this.createURL(state),
cssClasses,
currentPage: state.page,
labels,
setPage: this.setPage,
nbHits: results.nbHits,
nbPages: this.getMaxPage(results),
padding,
setPage: this.setPage,
shouldAutoHideContainer: autoHideContainer && results.nbHits === 0,
showFirstLast,
containerNode,
}, false);
},
};
};

export default connectPagination;
});
}
6 changes: 4 additions & 2 deletions src/widgets/pagination/__tests__/pagination-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ describe('pagination()', () => {
const getContainerNode = sinon.stub().returns({
scrollIntoView,
});
connectPagination.__Rewire__('getContainerNode', getContainerNode);
pagination.__Rewire__('getContainerNode', getContainerNode);
});

it('should not scroll', () => {
Expand All @@ -89,7 +89,9 @@ describe('pagination()', () => {
it('should scroll to body', () => {
widget = pagination({container});
widget.init({helper});
widget.setPage(helper, 2);
widget.render({results, helper, state: {page: 0}});
const {props: {setCurrentPage}} = ReactDOM.render.firstCall.args[0];
setCurrentPage(2);
expect(scrollIntoView.calledOnce).toBe(true, 'scrollIntoView called once');
});

Expand Down
152 changes: 121 additions & 31 deletions src/widgets/pagination/pagination.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,79 @@
import defaults from 'lodash/defaults';

import React from 'react';
import ReactDOM from 'react-dom';
import cx from 'classnames';

import Pagination from '../../components/Pagination/Pagination.js';
import connectPagination from '../../connectors/pagination/connectPagination.js';

import {bemHelper, getContainerNode} from '../../lib/utils.js';

const defaultLabels = {
previous: '‹',
next: '›',
first: '«',
last: '»',
};

const bem = bemHelper('ais-pagination');

const renderer = ({
containerNode,
cssClasses,
labels,
showFirstLast,
padding,
autoHideContainer,
scrollToNode,
}) => ({
createURL,
currentPage,
nbHits,
nbPages,
setPage,
}, isFirstRendering) => {
if (isFirstRendering) return;

const setCurrentPage = () => {
setPage();

if (scrollToNode !== false) {
scrollToNode.scrollIntoView();
}
};

const shouldAutoHideContainer = autoHideContainer && nbHits === 0;

ReactDOM.render(
<Pagination
createURL={createURL}
cssClasses={cssClasses}
currentPage={currentPage}
labels={labels}
nbHits={nbHits}
nbPages={nbPages}
padding={padding}
setCurrentPage={setCurrentPage}
shouldAutoHideContainer={shouldAutoHideContainer}
showFirstLast={showFirstLast}
/>,
containerNode
);
};

const usage = `Usage:
pagination({
container,
[ cssClasses.{root,item,page,previous,next,first,last,active,disabled}={} ],
[ labels.{previous,next,first,last} ],
[ maxPages ],
[ padding=3 ],
[ showFirstLast=true ],
[ autoHideContainer=true ],
[ scrollTo='body' ]
})`;

/**
* Add a pagination menu to navigate through the results
* @function pagination
Expand All @@ -28,37 +99,56 @@ import connectPagination from '../../connectors/pagination/connectPagination.js'
* @param {string|string[]} [options.cssClasses.last] CSS classes added to the last `<li>`
* @param {string|string[]} [options.cssClasses.active] CSS classes added to the active `<li>`
* @param {string|string[]} [options.cssClasses.disabled] CSS classes added to the disabled `<li>`
* @return {Object}
* @return {Object} widget
*/
export default connectPagination(defaultRendering);
export default function pagination({
container,
labels: userLabels = defaultLabels,
cssClasses: userCssClasses = {},
maxPages,
padding = 3,
showFirstLast = true,
autoHideContainer = true,
scrollTo: userScrollTo = 'body',
} = {}) {
if (!container) {
throw new Error(usage);
}

function defaultRendering({
createURL,
cssClasses,
currentPage,
labels,
nbHits,
nbPages,
padding,
setPage,
shouldAutoHideContainer,
showFirstLast,
containerNode,
}, isFirstRendering) {
if (isFirstRendering) return;
ReactDOM.render(
<Pagination
createURL={createURL}
cssClasses={cssClasses}
currentPage={currentPage}
labels={labels}
nbHits={nbHits}
nbPages={nbPages}
padding={padding}
setCurrentPage={setPage}
shouldAutoHideContainer={shouldAutoHideContainer}
showFirstLast={showFirstLast}
/>,
containerNode
);
const containerNode = getContainerNode(container);

const scrollTo = userScrollTo === true ? 'body' : userScrollTo;
const scrollToNode = scrollTo !== false ? getContainerNode(scrollTo) : false;

const cssClasses = {
root: cx(bem(null), userCssClasses.root),
item: cx(bem('item'), userCssClasses.item),
link: cx(bem('link'), userCssClasses.link),
page: cx(bem('item', 'page'), userCssClasses.page),
previous: cx(bem('item', 'previous'), userCssClasses.previous),
next: cx(bem('item', 'next'), userCssClasses.next),
first: cx(bem('item', 'first'), userCssClasses.first),
last: cx(bem('item', 'last'), userCssClasses.last),
active: cx(bem('item', 'active'), userCssClasses.active),
disabled: cx(bem('item', 'disabled'), userCssClasses.disabled),
};

const labels = defaults(userLabels, defaultLabels);

const specializedRenderer = renderer({
containerNode,
cssClasses,
labels,
showFirstLast,
padding,
autoHideContainer,
scrollToNode,
});

try {
const makeWidget = connectPagination(specializedRenderer);
return makeWidget({maxPages});
} catch (e) {
throw new Error(usage);
}
}

0 comments on commit 8a615f6

Please sign in to comment.