Skip to content

Commit 8a615f6

Browse files
committed
feat(connectors): connectPagination (iteration2)
1 parent 6073d94 commit 8a615f6

File tree

4 files changed

+156
-133
lines changed

4 files changed

+156
-133
lines changed

src/connectors/pagination/__tests__/connectPagination-test.js

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
2-
31
import sinon from 'sinon';
42

53
import jsHelper from 'algoliasearch-helper';
@@ -43,8 +41,6 @@ describe('connectPagination', () => {
4341
expect(firstRenderingOptions.currentPage).toBe(0);
4442
expect(firstRenderingOptions.nbHits).toBe(0);
4543
expect(firstRenderingOptions.nbPages).toBe(0);
46-
expect(firstRenderingOptions.shouldAutoHideContainer).toBe(true);
47-
expect(firstRenderingOptions.showFirstLast).toBe(true);
4844
}
4945

5046
widget.render({
@@ -69,22 +65,17 @@ describe('connectPagination', () => {
6965
expect(secondRenderingOptions.currentPage).toBe(0);
7066
expect(secondRenderingOptions.nbHits).toBe(1);
7167
expect(secondRenderingOptions.nbPages).toBe(1);
72-
expect(secondRenderingOptions.shouldAutoHideContainer).toBe(false);
73-
expect(secondRenderingOptions.showFirstLast).toBe(true);
7468
}
7569
});
7670

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

8274
const rendering = sinon.stub();
8375
const makeWidget = connectPagination(rendering);
8476

8577
const widget = makeWidget({
8678
container,
87-
scrollTo,
8879
});
8980

9081
const helper = jsHelper(fakeClient);
@@ -103,7 +94,6 @@ describe('connectPagination', () => {
10394
setPage(2);
10495
expect(helper.getPage()).toBe(2);
10596
expect(helper.search.callCount).toBe(1);
106-
expect(scrollTo.scrollIntoView.callCount).toBe(1);
10797
}
10898

10999
widget.render({
@@ -119,7 +109,6 @@ describe('connectPagination', () => {
119109
setPage(7);
120110
expect(helper.getPage()).toBe(7);
121111
expect(helper.search.callCount).toBe(2);
122-
expect(scrollTo.scrollIntoView.callCount).toBe(2);
123112
}
124113
});
125114
});
Lines changed: 31 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
1-
import defaults from 'lodash/defaults';
2-
import cx from 'classnames';
3-
import {
4-
bemHelper,
5-
getContainerNode,
6-
} from '../../lib/utils.js';
1+
import {checkRendering} from '../../lib/utils.js';
72

8-
const defaultLabels = {
9-
previous: '‹',
10-
next: '›',
11-
first: '«',
12-
last: '»',
13-
};
14-
const bem = bemHelper('ais-pagination');
3+
const usage = `Usage:
4+
var customPagination = connectPagination(function render(params, isFirstRendering) {
5+
// params = {
6+
// createURL,
7+
// currentPage,
8+
// nbHits,
9+
// nbPages,
10+
// setPage,
11+
// }
12+
});
13+
search.addWidget(
14+
customPagination({
15+
[maxPages]
16+
})
17+
);
18+
Full documentation available at https://community.algolia.com/instantsearch.js/connectors/connectPagination.html
19+
`;
1520

1621
/**
1722
* Add a pagination menu to navigate through the results
@@ -38,107 +43,44 @@ const bem = bemHelper('ais-pagination');
3843
* @param {string|string[]} [options.cssClasses.last] CSS classes added to the last `<li>`
3944
* @param {string|string[]} [options.cssClasses.active] CSS classes added to the active `<li>`
4045
* @param {string|string[]} [options.cssClasses.disabled] CSS classes added to the disabled `<li>`
41-
* @return {Object}
46+
* @return {Object} widget
4247
*/
43-
const usage = `Usage:
44-
pagination({
45-
container,
46-
[ cssClasses.{root,item,page,previous,next,first,last,active,disabled}={} ],
47-
[ labels.{previous,next,first,last} ],
48-
[ maxPages ],
49-
[ padding=3 ],
50-
[ showFirstLast=true ],
51-
[ autoHideContainer=true ],
52-
[ scrollTo='body' ]
53-
})`;
54-
const connectPagination = paginationRendering => ({
55-
container,
56-
cssClasses: userCssClasses = {},
57-
labels: userLabels = {},
58-
maxPages,
59-
padding = 3,
60-
showFirstLast = true,
61-
autoHideContainer = true,
62-
scrollTo: userScrollTo = 'body',
63-
} = {}) => {
64-
let scrollTo = userScrollTo;
65-
66-
if (!container) {
67-
throw new Error(usage);
68-
}
69-
70-
if (scrollTo === true) {
71-
scrollTo = 'body';
72-
}
73-
74-
const containerNode = getContainerNode(container);
75-
const scrollToNode = scrollTo !== false ? getContainerNode(scrollTo) : false;
7648

77-
const cssClasses = {
78-
root: cx(bem(null), userCssClasses.root),
79-
item: cx(bem('item'), userCssClasses.item),
80-
link: cx(bem('link'), userCssClasses.link),
81-
page: cx(bem('item', 'page'), userCssClasses.page),
82-
previous: cx(bem('item', 'previous'), userCssClasses.previous),
83-
next: cx(bem('item', 'next'), userCssClasses.next),
84-
first: cx(bem('item', 'first'), userCssClasses.first),
85-
last: cx(bem('item', 'last'), userCssClasses.last),
86-
active: cx(bem('item', 'active'), userCssClasses.active),
87-
disabled: cx(bem('item', 'disabled'), userCssClasses.disabled),
88-
};
49+
export default function connectPagination(renderFn) {
50+
checkRendering(renderFn, usage);
8951

90-
const labels = defaults(userLabels, defaultLabels);
91-
92-
return {
52+
return ({maxPages}) => ({
9353
init({helper, createURL}) {
9454
this.setPage = page => {
9555
helper.setPage(page);
96-
if (scrollToNode !== false) {
97-
scrollToNode.scrollIntoView();
98-
}
9956
helper.search();
10057
};
10158

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

104-
paginationRendering({
61+
renderFn({
10562
createURL: this.createURL(helper.state),
106-
cssClasses,
10763
currentPage: helper.getPage() || 0,
108-
labels,
10964
nbHits: 0,
11065
nbPages: 0,
111-
padding,
11266
setPage: this.setPage,
113-
shouldAutoHideContainer: autoHideContainer,
114-
showFirstLast,
115-
containerNode,
11667
}, true);
11768
},
11869

119-
getMaxPage(results) {
120-
if (maxPages !== undefined) {
121-
return Math.min(maxPages, results.nbPages);
122-
}
123-
return results.nbPages;
70+
getMaxPage({nbPages}) {
71+
return maxPages !== undefined
72+
? Math.min(maxPages, nbPages)
73+
: nbPages;
12474
},
12575

12676
render({results, state}) {
127-
paginationRendering({
77+
renderFn({
12878
createURL: this.createURL(state),
129-
cssClasses,
13079
currentPage: state.page,
131-
labels,
80+
setPage: this.setPage,
13281
nbHits: results.nbHits,
13382
nbPages: this.getMaxPage(results),
134-
padding,
135-
setPage: this.setPage,
136-
shouldAutoHideContainer: autoHideContainer && results.nbHits === 0,
137-
showFirstLast,
138-
containerNode,
13983
}, false);
14084
},
141-
};
142-
};
143-
144-
export default connectPagination;
85+
});
86+
}

src/widgets/pagination/__tests__/pagination-test.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ describe('pagination()', () => {
7676
const getContainerNode = sinon.stub().returns({
7777
scrollIntoView,
7878
});
79-
connectPagination.__Rewire__('getContainerNode', getContainerNode);
79+
pagination.__Rewire__('getContainerNode', getContainerNode);
8080
});
8181

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

Lines changed: 121 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,79 @@
1+
import defaults from 'lodash/defaults';
2+
13
import React from 'react';
24
import ReactDOM from 'react-dom';
5+
import cx from 'classnames';
6+
37
import Pagination from '../../components/Pagination/Pagination.js';
48
import connectPagination from '../../connectors/pagination/connectPagination.js';
59

10+
import {bemHelper, getContainerNode} from '../../lib/utils.js';
11+
12+
const defaultLabels = {
13+
previous: '‹',
14+
next: '›',
15+
first: '«',
16+
last: '»',
17+
};
18+
19+
const bem = bemHelper('ais-pagination');
20+
21+
const renderer = ({
22+
containerNode,
23+
cssClasses,
24+
labels,
25+
showFirstLast,
26+
padding,
27+
autoHideContainer,
28+
scrollToNode,
29+
}) => ({
30+
createURL,
31+
currentPage,
32+
nbHits,
33+
nbPages,
34+
setPage,
35+
}, isFirstRendering) => {
36+
if (isFirstRendering) return;
37+
38+
const setCurrentPage = () => {
39+
setPage();
40+
41+
if (scrollToNode !== false) {
42+
scrollToNode.scrollIntoView();
43+
}
44+
};
45+
46+
const shouldAutoHideContainer = autoHideContainer && nbHits === 0;
47+
48+
ReactDOM.render(
49+
<Pagination
50+
createURL={createURL}
51+
cssClasses={cssClasses}
52+
currentPage={currentPage}
53+
labels={labels}
54+
nbHits={nbHits}
55+
nbPages={nbPages}
56+
padding={padding}
57+
setCurrentPage={setCurrentPage}
58+
shouldAutoHideContainer={shouldAutoHideContainer}
59+
showFirstLast={showFirstLast}
60+
/>,
61+
containerNode
62+
);
63+
};
64+
65+
const usage = `Usage:
66+
pagination({
67+
container,
68+
[ cssClasses.{root,item,page,previous,next,first,last,active,disabled}={} ],
69+
[ labels.{previous,next,first,last} ],
70+
[ maxPages ],
71+
[ padding=3 ],
72+
[ showFirstLast=true ],
73+
[ autoHideContainer=true ],
74+
[ scrollTo='body' ]
75+
})`;
76+
677
/**
778
* Add a pagination menu to navigate through the results
879
* @function pagination
@@ -28,37 +99,56 @@ import connectPagination from '../../connectors/pagination/connectPagination.js'
2899
* @param {string|string[]} [options.cssClasses.last] CSS classes added to the last `<li>`
29100
* @param {string|string[]} [options.cssClasses.active] CSS classes added to the active `<li>`
30101
* @param {string|string[]} [options.cssClasses.disabled] CSS classes added to the disabled `<li>`
31-
* @return {Object}
102+
* @return {Object} widget
32103
*/
33-
export default connectPagination(defaultRendering);
104+
export default function pagination({
105+
container,
106+
labels: userLabels = defaultLabels,
107+
cssClasses: userCssClasses = {},
108+
maxPages,
109+
padding = 3,
110+
showFirstLast = true,
111+
autoHideContainer = true,
112+
scrollTo: userScrollTo = 'body',
113+
} = {}) {
114+
if (!container) {
115+
throw new Error(usage);
116+
}
34117

35-
function defaultRendering({
36-
createURL,
37-
cssClasses,
38-
currentPage,
39-
labels,
40-
nbHits,
41-
nbPages,
42-
padding,
43-
setPage,
44-
shouldAutoHideContainer,
45-
showFirstLast,
46-
containerNode,
47-
}, isFirstRendering) {
48-
if (isFirstRendering) return;
49-
ReactDOM.render(
50-
<Pagination
51-
createURL={createURL}
52-
cssClasses={cssClasses}
53-
currentPage={currentPage}
54-
labels={labels}
55-
nbHits={nbHits}
56-
nbPages={nbPages}
57-
padding={padding}
58-
setCurrentPage={setPage}
59-
shouldAutoHideContainer={shouldAutoHideContainer}
60-
showFirstLast={showFirstLast}
61-
/>,
62-
containerNode
63-
);
118+
const containerNode = getContainerNode(container);
119+
120+
const scrollTo = userScrollTo === true ? 'body' : userScrollTo;
121+
const scrollToNode = scrollTo !== false ? getContainerNode(scrollTo) : false;
122+
123+
const cssClasses = {
124+
root: cx(bem(null), userCssClasses.root),
125+
item: cx(bem('item'), userCssClasses.item),
126+
link: cx(bem('link'), userCssClasses.link),
127+
page: cx(bem('item', 'page'), userCssClasses.page),
128+
previous: cx(bem('item', 'previous'), userCssClasses.previous),
129+
next: cx(bem('item', 'next'), userCssClasses.next),
130+
first: cx(bem('item', 'first'), userCssClasses.first),
131+
last: cx(bem('item', 'last'), userCssClasses.last),
132+
active: cx(bem('item', 'active'), userCssClasses.active),
133+
disabled: cx(bem('item', 'disabled'), userCssClasses.disabled),
134+
};
135+
136+
const labels = defaults(userLabels, defaultLabels);
137+
138+
const specializedRenderer = renderer({
139+
containerNode,
140+
cssClasses,
141+
labels,
142+
showFirstLast,
143+
padding,
144+
autoHideContainer,
145+
scrollToNode,
146+
});
147+
148+
try {
149+
const makeWidget = connectPagination(specializedRenderer);
150+
return makeWidget({maxPages});
151+
} catch (e) {
152+
throw new Error(usage);
153+
}
64154
}

0 commit comments

Comments
 (0)