Skip to content

Commit 3161c9b

Browse files
committed
feat(connectors): connectSearchBox (iteration2)
1 parent e77ce1b commit 3161c9b

File tree

4 files changed

+145
-140
lines changed

4 files changed

+145
-140
lines changed

src/connectors/search-box/__tests__/connectSearchBox-test.js

Lines changed: 2 additions & 16 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';
@@ -39,14 +37,8 @@ describe('connectSearchBox', () => {
3937
expect(isFirstRendering).toBe(true);
4038

4139
// should provide good values for the first rendering
42-
const {query, containerNode, poweredBy,
43-
autofocus, searchOnEnterKeyPressOnly, placeholder} = rendering.lastCall.args[0];
44-
expect(containerNode).toBe(container);
40+
const {query} = rendering.lastCall.args[0];
4541
expect(query).toBe(helper.state.query);
46-
expect(poweredBy).toBe(false);
47-
expect(autofocus).toBe('auto');
48-
expect(searchOnEnterKeyPressOnly).toBe(false);
49-
expect(placeholder).toBe('');
5042
}
5143

5244
widget.render({
@@ -62,14 +54,8 @@ describe('connectSearchBox', () => {
6254
expect(isFirstRendering).toBe(false);
6355

6456
// should provide good values after the first search
65-
const {query, containerNode, poweredBy,
66-
autofocus, searchOnEnterKeyPressOnly, placeholder} = rendering.lastCall.args[0];
67-
expect(containerNode).toBe(container);
57+
const {query} = rendering.lastCall.args[0];
6858
expect(query).toBe(helper.state.query);
69-
expect(poweredBy).toBe(false);
70-
expect(autofocus).toBe('auto');
71-
expect(searchOnEnterKeyPressOnly).toBe(false);
72-
expect(placeholder).toBe('');
7359
}
7460
});
7561

Lines changed: 41 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,20 @@
1-
import {
2-
getContainerNode,
3-
} from '../../lib/utils.js';
4-
import defaultTemplates from './defaultTemplates.js';
1+
import {checkRendering} from '../../lib/utils.js';
2+
3+
const usage = `Usage:
4+
var customSearchBox = connectMenu(function render(params, isFirstRendering) {
5+
// params = {
6+
// query,
7+
// onHistoryChange,
8+
// search,
9+
// }
10+
});
11+
search.addWidget(
12+
customSearchBox({
13+
[ onQueryHook ]
14+
});
15+
);
16+
Full documentation available at https://community.algolia.com/instantsearch.js/connectors/connectSearchBox.html
17+
`;
518

619
/**
720
* Instantiate a searchbox
@@ -27,95 +40,42 @@ import defaultTemplates from './defaultTemplates.js';
2740
* @return {Object}
2841
*/
2942

30-
const usage = `Usage:
31-
searchBox({
32-
container,
33-
[ placeholder ],
34-
[ cssClasses.{input,poweredBy} ],
35-
[ poweredBy=false || poweredBy.{template, cssClasses.{root,link}} ],
36-
[ wrapInput ],
37-
[ autofocus ],
38-
[ searchOnEnterKeyPressOnly ],
39-
[ queryHook ]
40-
})`;
41-
const connectSearchBox = searchBoxRendering => ({
42-
container,
43-
placeholder = '',
44-
cssClasses = {},
45-
poweredBy = false,
46-
wrapInput = true,
47-
autofocus = 'auto',
48-
searchOnEnterKeyPressOnly = false,
49-
queryHook,
50-
}) => {
51-
if (!container) {
52-
throw new Error(usage);
53-
}
54-
55-
const containerNode = getContainerNode(container);
56-
57-
// Only possible values are 'auto', true and false
58-
if (typeof autofocus !== 'boolean') {
59-
autofocus = 'auto';
60-
}
61-
62-
// Convert to object if only set to true
63-
if (poweredBy === true) {
64-
poweredBy = {};
65-
}
43+
export default function connectSearchBox(renderFn) {
44+
checkRendering(renderFn, usage);
6645

67-
const makeSearch = helper => {
68-
let previousQuery;
46+
return ({queryHook}) => ({
47+
init({helper, onHistoryChange}) {
48+
this._search = (() => {
49+
let previousQuery;
6950

70-
const setQueryAndSearch = (q, doSearch = true) => {
71-
if (q !== helper.state.query) {
72-
previousQuery = helper.state.query;
73-
helper.setQuery(q);
74-
}
75-
if (doSearch && previousQuery !== undefined && previousQuery !== q) helper.search();
76-
};
51+
const setQueryAndSearch = (q, doSearch = true) => {
52+
if (q !== helper.state.query) {
53+
previousQuery = helper.state.query;
54+
helper.setQuery(q);
55+
}
56+
if (doSearch && previousQuery !== undefined && previousQuery !== q) helper.search();
57+
};
7758

78-
return queryHook ?
79-
q => queryHook(q, setQueryAndSearch) :
80-
setQueryAndSearch;
81-
};
59+
return queryHook ?
60+
q => queryHook(q, setQueryAndSearch) :
61+
setQueryAndSearch;
62+
})();
8263

83-
return {
84-
init({helper, onHistoryChange}) {
85-
this._search = makeSearch(helper);
8664
this._onHistoryChange = onHistoryChange;
87-
searchBoxRendering({
65+
66+
renderFn({
8867
query: helper.state.query,
89-
containerNode,
9068
onHistoryChange: this._onHistoryChange,
91-
poweredBy,
92-
wrapInput,
93-
autofocus,
94-
searchOnEnterKeyPressOnly,
95-
placeholder,
96-
cssClasses,
97-
templates: defaultTemplates,
9869
search: this._search,
9970
}, true);
10071
},
72+
10173
render({helper}) {
102-
searchBoxRendering({
74+
renderFn({
10375
query: helper.state.query,
104-
containerNode,
105-
onHistoryChange: this.onHistoryChange,
106-
poweredBy,
107-
helper,
108-
wrapInput,
109-
autofocus,
110-
queryHook,
111-
searchOnEnterKeyPressOnly,
112-
placeholder,
113-
cssClasses,
114-
templates: defaultTemplates,
76+
onHistoryChange: this._onHistoryChange,
11577
search: this._search,
11678
}, false);
11779
},
118-
};
119-
};
120-
121-
export default connectSearchBox;
80+
});
81+
}
File renamed without changes.

src/widgets/search-box/search-box.js

Lines changed: 102 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,35 @@
1-
import {
2-
bemHelper,
3-
} from '../../lib/utils.js';
41
import forEach from 'lodash/forEach';
52
import isString from 'lodash/isString';
63
import isFunction from 'lodash/isFunction';
74
import cx from 'classnames';
85
import Hogan from 'hogan.js';
96

10-
const bem = bemHelper('ais-search-box');
11-
const KEY_ENTER = 13;
12-
const KEY_SUPPRESS = 8;
13-
147
import connectSearchBox from '../../connectors/search-box/connectSearchBox.js';
8+
import defaultTemplates from './defaultTemplates.js';
159

16-
/**
17-
* Instantiate a searchbox
18-
* @function searchBox
19-
* @param {string|DOMElement} options.container CSS Selector or DOMElement to insert the widget
20-
* @param {string} [options.placeholder] Input's placeholder [*]
21-
* @param {boolean|Object} [options.poweredBy=false] Define if a "powered by Algolia" link should be added near the input
22-
* @param {function|string} [options.poweredBy.template] Template used for displaying the link. Can accept a function or a Hogan string.
23-
* @param {number} [options.poweredBy.cssClasses] CSS classes to add
24-
* @param {string|string[]} [options.poweredBy.cssClasses.root] CSS class to add to the root element
25-
* @param {string|string[]} [options.poweredBy.cssClasses.link] CSS class to add to the link element
26-
* @param {boolean} [options.wrapInput=true] Wrap the input in a `div.ais-search-box`
27-
* @param {boolean|string} [autofocus='auto'] autofocus on the input
28-
* @param {boolean} [options.searchOnEnterKeyPressOnly=false] If set, trigger the search
29-
* once `<Enter>` is pressed only
30-
* @param {Object} [options.cssClasses] CSS classes to add
31-
* @param {string|string[]} [options.cssClasses.root] CSS class to add to the
32-
* wrapping div (if `wrapInput` set to `true`)
33-
* @param {string|string[]} [options.cssClasses.input] CSS class to add to the input
34-
* @param {function} [options.queryHook] A function that will be called every time a new search would be done. You
35-
* will get the query as first parameter and a search(query) function to call as the second parameter.
36-
* This queryHook can be used to debounce the number of searches done from the searchBox.
37-
* @return {Object}
38-
*/
10+
import {
11+
bemHelper,
12+
getContainerNode,
13+
} from '../../lib/utils.js';
3914

40-
export default connectSearchBox(defaultRendering);
15+
const bem = bemHelper('ais-search-box');
16+
const KEY_ENTER = 13;
17+
const KEY_SUPPRESS = 8;
4118

42-
// the 'input' event is triggered when the input value changes
43-
// in any case: typing, copy pasting with mouse..
44-
// 'onpropertychange' is the IE8 alternative until we support IE8
45-
// but it's flawed: http://help.dottoro.com/ljhxklln.php
46-
function defaultRendering({
47-
query,
19+
const renderer = ({
4820
containerNode,
49-
onHistoryChange,
21+
cssClasses,
22+
placeholder,
5023
poweredBy,
51-
wrapInput,
24+
templates,
5225
autofocus,
5326
searchOnEnterKeyPressOnly,
54-
placeholder,
55-
cssClasses,
56-
templates,
27+
wrapInput,
28+
}) => ({
5729
search,
58-
}, isFirstRendering) {
30+
query,
31+
onHistoryChange,
32+
}, isFirstRendering) => {
5933
if (isFirstRendering) {
6034
const INPUT_EVENT = window.addEventListener ?
6135
'input' :
@@ -125,8 +99,93 @@ function defaultRendering({
12599
input.value = query;
126100
}
127101
}
102+
};
103+
104+
const usage = `Usage:
105+
searchBox({
106+
container,
107+
[ placeholder ],
108+
[ cssClasses.{input,poweredBy} ],
109+
[ poweredBy=false || poweredBy.{template, cssClasses.{root,link}} ],
110+
[ wrapInput ],
111+
[ autofocus ],
112+
[ searchOnEnterKeyPressOnly ],
113+
[ queryHook ]
114+
})`;
115+
116+
/**
117+
* Instantiate a searchbox
118+
* @function searchBox
119+
* @param {string|DOMElement} options.container CSS Selector or DOMElement to insert the widget
120+
* @param {string} [options.placeholder] Input's placeholder [*]
121+
* @param {boolean|Object} [options.poweredBy=false] Define if a "powered by Algolia" link should be added near the input
122+
* @param {function|string} [options.poweredBy.template] Template used for displaying the link. Can accept a function or a Hogan string.
123+
* @param {number} [options.poweredBy.cssClasses] CSS classes to add
124+
* @param {string|string[]} [options.poweredBy.cssClasses.root] CSS class to add to the root element
125+
* @param {string|string[]} [options.poweredBy.cssClasses.link] CSS class to add to the link element
126+
* @param {boolean} [options.wrapInput=true] Wrap the input in a `div.ais-search-box`
127+
* @param {boolean|string} [autofocus='auto'] autofocus on the input
128+
* @param {boolean} [options.searchOnEnterKeyPressOnly=false] If set, trigger the search
129+
* once `<Enter>` is pressed only
130+
* @param {Object} [options.cssClasses] CSS classes to add
131+
* @param {string|string[]} [options.cssClasses.root] CSS class to add to the
132+
* wrapping div (if `wrapInput` set to `true`)
133+
* @param {string|string[]} [options.cssClasses.input] CSS class to add to the input
134+
* @param {function} [options.queryHook] A function that will be called every time a new search would be done. You
135+
* will get the query as first parameter and a search(query) function to call as the second parameter.
136+
* This queryHook can be used to debounce the number of searches done from the searchBox.
137+
* @return {Object} widget
138+
*/
139+
export default function searchBox({
140+
container,
141+
placeholder = '',
142+
cssClasses = {},
143+
poweredBy = false,
144+
wrapInput = true,
145+
autofocus = 'auto',
146+
searchOnEnterKeyPressOnly = false,
147+
queryHook,
148+
}) {
149+
if (!container) {
150+
throw new Error(usage);
151+
}
152+
153+
const containerNode = getContainerNode(container);
154+
155+
// Only possible values are 'auto', true and false
156+
if (typeof autofocus !== 'boolean') {
157+
autofocus = 'auto';
158+
}
159+
160+
// Convert to object if only set to true
161+
if (poweredBy === true) {
162+
poweredBy = {};
163+
}
164+
165+
const specializedRenderer = renderer({
166+
containerNode,
167+
cssClasses,
168+
placeholder,
169+
poweredBy,
170+
templates: defaultTemplates,
171+
autofocus,
172+
searchOnEnterKeyPressOnly,
173+
wrapInput,
174+
});
175+
176+
try {
177+
const makeWidget = connectSearchBox(specializedRenderer);
178+
return makeWidget({queryHook});
179+
} catch (e) {
180+
throw new Error(usage);
181+
}
128182
}
129183

184+
// the 'input' event is triggered when the input value changes
185+
// in any case: typing, copy pasting with mouse..
186+
// 'onpropertychange' is the IE8 alternative until we support IE8
187+
// but it's flawed: http://help.dottoro.com/ljhxklln.php
188+
130189
function createInput(containerNode) {
131190
// Returns reference to targeted input if present, or create a new one
132191
if (containerNode.tagName === 'INPUT') {

0 commit comments

Comments
 (0)