Skip to content

Commit 1f3f889

Browse files
author
Alexandre Stanislawski
committedJan 25, 2016
feat(hits): adds allItems template as an alternative to item
This way the user can specify how to do the complete rendering of the hits.
1 parent 0eca736 commit 1f3f889

File tree

8 files changed

+100
-9
lines changed

8 files changed

+100
-9
lines changed
 

‎dev/app.js

+12-1
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,25 @@ search.addWidget(
5050
})
5151
);
5252

53+
search.addWidget(
54+
instantsearch.widgets.hits({
55+
container: '#hits-table',
56+
templates: {
57+
empty: require('./templates/no-results.html'),
58+
allItems: require('./templates/all-items.html')
59+
},
60+
hitsPerPage: 24
61+
})
62+
);
63+
5364
search.addWidget(
5465
instantsearch.widgets.hits({
5566
container: '#hits',
5667
templates: {
5768
empty: require('./templates/no-results.html'),
5869
item: require('./templates/item.html')
5970
},
60-
hitsPerPage: 6
71+
hitsPerPage: 24
6172
})
6273
);
6374

‎dev/index.html

+3
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ <h1><a href="./">Instant search demo</a> <small>using instantsearch.js</small></
6666
</div>
6767
</div>
6868
</div>
69+
<h3>Results overview</h3>
70+
<div id="hits-table"></div>
71+
<h3>Results</h3>
6972
<div id="hits"></div>
7073
<div id="pagination" class="text-center"></div>
7174
</div>

‎dev/templates/all-items.html

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<table class="table table-striped table-condensed">
2+
<thead>
3+
<tr><th>name</th><th>brand</th><th>price</th></tr>
4+
</thead>
5+
<tbody>
6+
{{#hits}}
7+
<tr><td><a href="#hit-{{objectID}}">{{name}}</a></td><td>{{brand}}</td><td>{{price}}</td></tr>
8+
{{/hits}}
9+
</tbody>
10+
</table>

‎dev/templates/item.html

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<div class="hit">
1+
<div class="hit" id="hit-{{objectID}}">
22
<div class="media">
33
<a class="pull-left" href="{{url}}">
44
<img class="media-object" src="{{image}}">
@@ -10,4 +10,5 @@ <h4>{{{_highlightResult.name.value}}}</h4>
1010
{{#free_shipping}}<span class="badge pull-right">Free Shipping</span>{{/free_shipping}}
1111
</div>
1212
</div>
13+
<a href="#">Go back to top</a>
1314
</div>

‎src/components/Hits.js

+19
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,17 @@ class Hits extends React.Component {
2020
return <div className={this.props.cssClasses.root}>{renderedHits}</div>;
2121
}
2222

23+
renderAllResults() {
24+
return (
25+
<Template
26+
cssClass={this.props.cssClasses.allItems}
27+
data={this.props.results}
28+
templateKey="allItems"
29+
{...this.props.templateProps}
30+
/>
31+
);
32+
}
33+
2334
renderNoResults() {
2435
let className = this.props.cssClasses.root + ' ' + this.props.cssClasses.empty;
2536
return (
@@ -34,6 +45,13 @@ class Hits extends React.Component {
3445

3546
render() {
3647
if (this.props.results.hits.length > 0) {
48+
const useAllItemsTemplate =
49+
this.props.templateProps &&
50+
this.props.templateProps.templates &&
51+
this.props.templateProps.templates.allItems;
52+
if (useAllItemsTemplate) {
53+
return this.renderAllResults();
54+
}
3755
return this.renderWithResults();
3856
}
3957
return this.renderNoResults();
@@ -44,6 +62,7 @@ Hits.propTypes = {
4462
cssClasses: React.PropTypes.shape({
4563
root: React.PropTypes.string,
4664
item: React.PropTypes.string,
65+
allItems: React.PropTypes.string,
4766
empty: React.PropTypes.string
4867
}),
4968
results: React.PropTypes.object,

‎src/components/__tests__/Hits-test.js

+37-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ describe('Hits', () => {
2121
templateProps = {};
2222
});
2323

24-
it('render hits when present', () => {
24+
it('render hits when present (item template)', () => {
2525
results = {hits: [{
2626
objectID: 'hello'
2727
}, {
@@ -58,6 +58,42 @@ describe('Hits', () => {
5858
);
5959
});
6060

61+
it('render hits when present (allItems template)', () => {
62+
results = {hits: [{
63+
objectID: 'hello'
64+
}, {
65+
objectID: 'mom'
66+
}]};
67+
68+
const templateProps2 = {
69+
...templateProps,
70+
templates: {
71+
allItems: 'all items'
72+
}
73+
};
74+
75+
let props = {
76+
results,
77+
templateProps: templateProps2,
78+
cssClasses: {
79+
root: 'custom-root',
80+
allItems: 'custom-item',
81+
empty: 'custom-empty'
82+
}
83+
};
84+
renderer.render(<Hits {...props} />);
85+
let out = renderer.getRenderOutput();
86+
87+
expect(out).toEqualJSX(
88+
<Template
89+
cssClass="custom-item"
90+
data={results}
91+
templateKey="allItems"
92+
{...templateProps2}
93+
/>
94+
);
95+
});
96+
6197
it('renders a specific template when no results', () => {
6298
results = {hits: []};
6399

‎src/widgets/hits/__tests__/hits-test.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ describe('hits call', () => {
1515
jsdom({useEach: true});
1616

1717
it('throws an exception when no container', () => {
18-
expect(hits).toThrow(/^Usage:/);
18+
expect(hits).toThrow(/^Must provide a container/);
1919
});
2020
});
2121

@@ -65,6 +65,10 @@ describe('hits()', () => {
6565
expect(ReactDOM.render.secondCall.args[1]).toEqual(container);
6666
});
6767

68+
it('does not accept both item and allItems templates', () => {
69+
expect(hits.bind({container, templates: {item: '', allItems: ''}})).toThrow();
70+
});
71+
6872
afterEach(() => {
6973
hits.__ResetDependency__('ReactDOM');
7074
hits.__ResetDependency__('defaultTemplates');

‎src/widgets/hits/hits.js

+12-5
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,26 @@ import defaultTemplates from './defaultTemplates.js';
1414
* @param {string|DOMElement} options.container CSS Selector or DOMElement to insert the widget
1515
* @param {Object} [options.templates] Templates to use for the widget
1616
* @param {string|Function} [options.templates.empty=''] Template to use when there are no results.
17-
* @param {string|Function} [options.templates.item=''] Template to use for each result.
17+
* @param {string|Function} [options.templates.item=''] Template to use for each result. This template will receive an object containing a single record.
18+
* @param {string|Function} [options.templates.allItems=''] Template to use for each result. (can't be used with item template). This template will receive a complete SearchResults result object, this object contains the key hits that contains all the records retrieved.
1819
* @param {Object} [options.transformData] Method to change the object passed to the templates
1920
* @param {Function} [options.transformData.empty=identity] Method used to change the object passed to the empty template
2021
* @param {Function} [options.transformData.item=identity] Method used to change the object passed to the item template
22+
* @param {Function} [options.transformData.allItems=identity] Method used to change the object passed to the item template
2123
* @param {number} [hitsPerPage=20] The number of hits to display per page
2224
* @param {Object} [options.cssClasses] CSS classes to add
2325
* @param {string|string[]} [options.cssClasses.root] CSS class to add to the wrapping element
2426
* @param {string|string[]} [options.cssClasses.empty] CSS class to add to the wrapping element when no results
2527
* @param {string|string[]} [options.cssClasses.item] CSS class to add to each result
2628
* @return {Object}
2729
*/
28-
const usage = `Usage:
30+
const usage = `
31+
Usage:
2932
hits({
3033
container,
3134
[ cssClasses.{root,empty,item}={} ],
32-
[ templates.{empty,item} ],
33-
[ transformData.{empty=identity,item=identity} ],
35+
[ templates.{empty,item} | templates.{empty, allItems} ],
36+
[ transformData.{empty=identity,item=identity} | transformData.{empty, allItems} ],
3437
[ hitsPerPage=20 ]
3538
})`;
3639
function hits({
@@ -41,7 +44,11 @@ function hits({
4144
hitsPerPage = 20
4245
} = {}) {
4346
if (!container) {
44-
throw new Error(usage);
47+
throw new Error('Must provide a container.' + usage);
48+
}
49+
50+
if (templates.item && templates.allItems) {
51+
throw new Error('Must contain only allItems OR item template.' + usage);
4552
}
4653

4754
let containerNode = utils.getContainerNode(container);

0 commit comments

Comments
 (0)
Please sign in to comment.