Skip to content

Commit 7a876f3

Browse files
author
Alexandre Stanislawski
committed
feat(connector): pagination connector
1 parent 0dc42d2 commit 7a876f3

File tree

3 files changed

+179
-109
lines changed

3 files changed

+179
-109
lines changed
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import defaults from 'lodash/defaults';
2+
import cx from 'classnames';
3+
import {
4+
bemHelper,
5+
getContainerNode,
6+
} from '../../lib/utils.js';
7+
8+
const defaultLabels = {
9+
previous: '‹',
10+
next: '›',
11+
first: '«',
12+
last: '»',
13+
};
14+
const bem = bemHelper('ais-pagination');
15+
16+
/**
17+
* Add a pagination menu to navigate through the results
18+
* @function pagination
19+
* @param {string|DOMElement} options.container CSS Selector or DOMElement to insert the widget
20+
* @param {Object} [options.labels] Text to display in the various links (prev, next, first, last)
21+
* @param {string} [options.labels.previous] Label for the Previous link
22+
* @param {string} [options.labels.next] Label for the Next link
23+
* @param {string} [options.labels.first] Label for the First link
24+
* @param {string} [options.labels.last] Label for the Last link
25+
* @param {number} [options.maxPages] The max number of pages to browse
26+
* @param {number} [options.padding=3] The number of pages to display on each side of the current page
27+
* @param {string|DOMElement|boolean} [options.scrollTo='body'] Where to scroll after a click, set to `false` to disable
28+
* @param {boolean} [options.showFirstLast=true] Define if the First and Last links should be displayed
29+
* @param {boolean} [options.autoHideContainer=true] Hide the container when no results match
30+
* @param {Object} [options.cssClasses] CSS classes to be added
31+
* @param {string|string[]} [options.cssClasses.root] CSS classes added to the parent `<ul>`
32+
* @param {string|string[]} [options.cssClasses.item] CSS classes added to each `<li>`
33+
* @param {string|string[]} [options.cssClasses.link] CSS classes added to each link
34+
* @param {string|string[]} [options.cssClasses.page] CSS classes added to page `<li>`
35+
* @param {string|string[]} [options.cssClasses.previous] CSS classes added to the previous `<li>`
36+
* @param {string|string[]} [options.cssClasses.next] CSS classes added to the next `<li>`
37+
* @param {string|string[]} [options.cssClasses.first] CSS classes added to the first `<li>`
38+
* @param {string|string[]} [options.cssClasses.last] CSS classes added to the last `<li>`
39+
* @param {string|string[]} [options.cssClasses.active] CSS classes added to the active `<li>`
40+
* @param {string|string[]} [options.cssClasses.disabled] CSS classes added to the disabled `<li>`
41+
* @return {Object}
42+
*/
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;
76+
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+
};
89+
90+
const labels = defaults(userLabels, defaultLabels);
91+
92+
return {
93+
init({helper, createURL}) {
94+
this.setCurrentPage = page => {
95+
helper.setCurrentPage(page);
96+
if (scrollToNode !== false) {
97+
scrollToNode.scrollIntoView();
98+
}
99+
helper.search();
100+
};
101+
102+
this.createURL = state => page => createURL(state.setPage(page));
103+
104+
paginationRendering({
105+
createURL: this.createURL(helper.state),
106+
cssClasses,
107+
currentPage: helper.getPage(),
108+
labels,
109+
nbHits: 0,
110+
nbPages: 0,
111+
padding,
112+
setCurrentPage: this.setCurrentPage,
113+
shouldAutoHideContainer: autoHideContainer,
114+
showFirstLast,
115+
containerNode,
116+
}, true);
117+
},
118+
119+
getMaxPage(results) {
120+
if (maxPages !== undefined) {
121+
return Math.min(maxPages, results.nbPages);
122+
}
123+
return results.nbPages;
124+
},
125+
126+
render({results, state}) {
127+
paginationRendering({
128+
createURL: this.createURL(state),
129+
cssClasses,
130+
currentPage: results.page,
131+
labels,
132+
nbHits: results.nbHits,
133+
nbPages: this.getMaxPage(results),
134+
padding,
135+
setCurrentPage: this.setCurrentPage,
136+
shouldAutoHideContainer: autoHideContainer && results.nbHits === 0,
137+
showFirstLast,
138+
containerNode,
139+
}, false);
140+
},
141+
};
142+
};
143+
144+
export default connectPagination;

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import expectJSX from 'expect-jsx';
77
expect.extend(expectJSX);
88
import pagination from '../pagination';
99
import Pagination from '../../../components/Pagination/Pagination';
10+
import connectPagination from '../../../connectors/pagination/connectPagination.js';
1011

1112
describe('pagination call', () => {
1213
it('throws an exception when no container', () => {
@@ -24,7 +25,6 @@ describe('pagination()', () => {
2425
beforeEach(() => {
2526
ReactDOM = {render: sinon.spy()};
2627
pagination.__Rewire__('ReactDOM', ReactDOM);
27-
pagination.__Rewire__('autoHideContainerHOC', sinon.stub().returns(Pagination));
2828

2929
container = document.createElement('div');
3030
cssClasses = {
@@ -44,6 +44,7 @@ describe('pagination()', () => {
4444
helper = {
4545
setCurrentPage: sinon.spy(),
4646
search: sinon.spy(),
47+
getPage: () => 0,
4748
};
4849
widget.init({helper});
4950
});
@@ -77,7 +78,7 @@ describe('pagination()', () => {
7778
const getContainerNode = sinon.stub().returns({
7879
scrollIntoView,
7980
});
80-
pagination.__Rewire__('getContainerNode', getContainerNode);
81+
connectPagination.__Rewire__('getContainerNode', getContainerNode);
8182
});
8283

8384
it('should not scroll', () => {
@@ -142,7 +143,6 @@ describe('pagination MaxPage', () => {
142143
beforeEach(() => {
143144
ReactDOM = {render: sinon.spy()};
144145
pagination.__Rewire__('ReactDOM', ReactDOM);
145-
pagination.__Rewire__('autoHideContainerHOC', sinon.stub().returns(Pagination));
146146

147147
container = document.createElement('div');
148148
cssClasses = {
Lines changed: 32 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,7 @@
11
import React from 'react';
22
import ReactDOM from 'react-dom';
3-
import defaults from 'lodash/defaults';
4-
import cx from 'classnames';
5-
import {
6-
bemHelper,
7-
getContainerNode,
8-
} from '../../lib/utils.js';
9-
import autoHideContainerHOC from '../../decorators/autoHideContainer.js';
10-
import PaginationComponent from '../../components/Pagination/Pagination.js';
11-
12-
const defaultLabels = {
13-
previous: '‹',
14-
next: '›',
15-
first: '«',
16-
last: '»',
17-
};
18-
const bem = bemHelper('ais-pagination');
3+
import Pagination from '../../components/Pagination/Pagination.js';
4+
import connectPagination from '../../connectors/pagination/connectPagination.js';
195

206
/**
217
* Add a pagination menu to navigate through the results
@@ -44,95 +30,35 @@ const bem = bemHelper('ais-pagination');
4430
* @param {string|string[]} [options.cssClasses.disabled] CSS classes added to the disabled `<li>`
4531
* @return {Object}
4632
*/
47-
const usage = `Usage:
48-
pagination({
49-
container,
50-
[ cssClasses.{root,item,page,previous,next,first,last,active,disabled}={} ],
51-
[ labels.{previous,next,first,last} ],
52-
[ maxPages ],
53-
[ padding=3 ],
54-
[ showFirstLast=true ],
55-
[ autoHideContainer=true ],
56-
[ scrollTo='body' ]
57-
})`;
58-
function pagination({
59-
container,
60-
cssClasses: userCssClasses = {},
61-
labels: userLabels = {},
62-
maxPages,
63-
padding = 3,
64-
showFirstLast = true,
65-
autoHideContainer = true,
66-
scrollTo: userScrollTo = 'body',
67-
} = {}) {
68-
let scrollTo = userScrollTo;
69-
70-
if (!container) {
71-
throw new Error(usage);
72-
}
73-
74-
if (scrollTo === true) {
75-
scrollTo = 'body';
76-
}
77-
78-
const containerNode = getContainerNode(container);
79-
const scrollToNode = scrollTo !== false ? getContainerNode(scrollTo) : false;
80-
let Pagination = PaginationComponent;
81-
if (autoHideContainer === true) {
82-
Pagination = autoHideContainerHOC(Pagination);
83-
}
84-
85-
const cssClasses = {
86-
root: cx(bem(null), userCssClasses.root),
87-
item: cx(bem('item'), userCssClasses.item),
88-
link: cx(bem('link'), userCssClasses.link),
89-
page: cx(bem('item', 'page'), userCssClasses.page),
90-
previous: cx(bem('item', 'previous'), userCssClasses.previous),
91-
next: cx(bem('item', 'next'), userCssClasses.next),
92-
first: cx(bem('item', 'first'), userCssClasses.first),
93-
last: cx(bem('item', 'last'), userCssClasses.last),
94-
active: cx(bem('item', 'active'), userCssClasses.active),
95-
disabled: cx(bem('item', 'disabled'), userCssClasses.disabled),
96-
};
33+
export default connectPagination(defaultRendering);
9734

98-
const labels = defaults(userLabels, defaultLabels);
99-
100-
return {
101-
init({helper}) {
102-
this.setCurrentPage = page => {
103-
helper.setCurrentPage(page);
104-
if (scrollToNode !== false) {
105-
scrollToNode.scrollIntoView();
106-
}
107-
helper.search();
108-
};
109-
},
110-
111-
getMaxPage(results) {
112-
if (maxPages !== undefined) {
113-
return Math.min(maxPages, results.nbPages);
114-
}
115-
return results.nbPages;
116-
},
117-
118-
render({results, state, createURL}) {
119-
ReactDOM.render(
120-
<Pagination
121-
createURL={page => createURL(state.setPage(page))}
122-
cssClasses={cssClasses}
123-
currentPage={results.page}
124-
labels={labels}
125-
nbHits={results.nbHits}
126-
nbPages={this.getMaxPage(results)}
127-
padding={padding}
128-
setCurrentPage={this.setCurrentPage}
129-
shouldAutoHideContainer={results.nbHits === 0}
130-
showFirstLast={showFirstLast}
131-
/>,
132-
containerNode
133-
);
134-
},
135-
};
35+
function defaultRendering({
36+
createURL,
37+
cssClasses,
38+
currentPage,
39+
labels,
40+
nbHits,
41+
nbPages,
42+
padding,
43+
setCurrentPage,
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={setCurrentPage}
59+
shouldAutoHideContainer={shouldAutoHideContainer}
60+
showFirstLast={showFirstLast}
61+
/>,
62+
containerNode
63+
);
13664
}
137-
138-
export default pagination;

0 commit comments

Comments
 (0)