diff --git a/src/connectors/pagination/__tests__/connectPagination-test.js b/src/connectors/pagination/__tests__/connectPagination-test.js index a41c9bb168..5a8f11a07c 100644 --- a/src/connectors/pagination/__tests__/connectPagination-test.js +++ b/src/connectors/pagination/__tests__/connectPagination-test.js @@ -1,5 +1,3 @@ - - import sinon from 'sinon'; import jsHelper from 'algoliasearch-helper'; @@ -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({ @@ -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); @@ -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({ @@ -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); } }); }); diff --git a/src/connectors/pagination/connectPagination.js b/src/connectors/pagination/connectPagination.js index 29cc163b0a..ad98845327 100644 --- a/src/connectors/pagination/connectPagination.js +++ b/src/connectors/pagination/connectPagination.js @@ -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 @@ -38,107 +43,44 @@ const bem = bemHelper('ais-pagination'); * @param {string|string[]} [options.cssClasses.last] CSS classes added to the last `
  • ` * @param {string|string[]} [options.cssClasses.active] CSS classes added to the active `
  • ` * @param {string|string[]} [options.cssClasses.disabled] CSS classes added to the disabled `
  • ` - * @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; + }); +} diff --git a/src/widgets/pagination/__tests__/pagination-test.js b/src/widgets/pagination/__tests__/pagination-test.js index 153fdf133d..b5bce0f41a 100644 --- a/src/widgets/pagination/__tests__/pagination-test.js +++ b/src/widgets/pagination/__tests__/pagination-test.js @@ -76,7 +76,7 @@ describe('pagination()', () => { const getContainerNode = sinon.stub().returns({ scrollIntoView, }); - connectPagination.__Rewire__('getContainerNode', getContainerNode); + pagination.__Rewire__('getContainerNode', getContainerNode); }); it('should not scroll', () => { @@ -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'); }); diff --git a/src/widgets/pagination/pagination.js b/src/widgets/pagination/pagination.js index 26212813c2..2e77910fe1 100644 --- a/src/widgets/pagination/pagination.js +++ b/src/widgets/pagination/pagination.js @@ -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( + , + 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 @@ -28,37 +99,56 @@ import connectPagination from '../../connectors/pagination/connectPagination.js' * @param {string|string[]} [options.cssClasses.last] CSS classes added to the last `
  • ` * @param {string|string[]} [options.cssClasses.active] CSS classes added to the active `
  • ` * @param {string|string[]} [options.cssClasses.disabled] CSS classes added to the disabled `
  • ` - * @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( - , - 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); + } }