Skip to content

Commit

Permalink
feat(connector): add infinite hits connector
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexandre Stanislawski committed Feb 15, 2017
1 parent 04437b0 commit cdf8675
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 86 deletions.
107 changes: 107 additions & 0 deletions src/connectors/infinite-hits/connectInfiniteHits.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import {
bemHelper,
prepareTemplateProps,
getContainerNode,
} from '../../lib/utils.js';
import cx from 'classnames';
import defaultTemplates from './defaultTemplates.js';

const bem = bemHelper('ais-infinite-hits');

/**
* Display the list of results (hits) from the current search
* @function hits
* @param {string|DOMElement} options.container CSS Selector or DOMElement to insert the widget
* @param {number} [options.hitsPerPage=20] The number of hits to display per page [*]
* @param {Object} [options.templates] Templates to use for the widget
* @param {string|Function} [options.templates.empty=''] Template to use when there are no results.
* @param {string|Function} [options.templates.item=''] Template to use for each result. This template will receive an object containing a single record.
* @param {Object} [options.transformData] Method to change the object passed to the templates
* @param {Function} [options.transformData.empty] Method used to change the object passed to the `empty` template
* @param {Function} [options.transformData.item] Method used to change the object passed to the `item` template
* @param {Object} [options.cssClasses] CSS classes to add
* @param {string|string[]} [options.cssClasses.root] CSS class to add to the wrapping element
* @param {string|string[]} [options.cssClasses.empty] CSS class to add to the wrapping element when no results
* @param {string|string[]} [options.7cssClasses.item] CSS class to add to each result
* @return {Object}
*/
const usage = `
Usage:
infiniteHits({
container,
[ cssClasses.{root,empty,item}={} ],
[ templates.{empty,item} | templates.{empty} ],
[ transformData.{empty,item} | transformData.{empty} ],
[ hitsPerPage=20 ]
})`;
const connectInfiniteHits = infiniteHitsRendering => ({
container,
cssClasses: userCssClasses = {},
showMoreLabel = 'Show more results',
templates = defaultTemplates,
transformData,
hitsPerPage = 20,
} = {}) => {
if (!container) {
throw new Error(`Must provide a container.${usage}`);
}

const containerNode = getContainerNode(container);
const cssClasses = {
root: cx(bem(null), userCssClasses.root),
item: cx(bem('item'), userCssClasses.item),
empty: cx(bem(null, 'empty'), userCssClasses.empty),
showmore: cx(bem('showmore'), userCssClasses.showmore),
};

let hitsCache = [];

const getShowMore = helper => () => helper.nextPage().search();

return {
getConfiguration: () => ({hitsPerPage}),
init({templatesConfig, helper}) {
this._templateProps = prepareTemplateProps({
transformData,
defaultTemplates,
templatesConfig,
templates,
});

this.showMore = getShowMore(helper);

infiniteHitsRendering({
cssClasses,
hits: hitsCache,
results: [],
showMore: this.showMore,
showMoreLabel,
templateProps: this._templateProps,
containerNode,
isLastPage: true,
}, true);
},
render({results, state}) {
if (state.page === 0) {
hitsCache = [];
}

hitsCache = [...hitsCache, ...results.hits];

const isLastPage = results.nbPages <= results.page + 1;

infiniteHitsRendering({
cssClasses,
hits: hitsCache,
results,
showMore: this.showMore,
showMoreLabel,
templateProps: this._templateProps,
containerNode,
isLastPage,
}, false);
},
};
};

export default connectInfiniteHits;
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import expect from 'expect';

import defaultTemplates from '../defaultTemplates';
import defaultTemplates from '../../../connectors/infinite-hits/defaultTemplates.js';

describe('hits defaultTemplates', () => {
it('has a `empty` default template', () => {
Expand Down
9 changes: 3 additions & 6 deletions src/widgets/infinite-hits/__tests__/infinite-hits-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import algoliasearchHelper from 'algoliasearch-helper';
import infiniteHits from '../infinite-hits';
import InfiniteHits from '../../../components/InfiniteHits';

import defaultTemplates from '../../../connectors/infinite-hits/defaultTemplates.js';

describe('infiniteHits call', () => {
it('throws an exception when no container', () => {
expect(infiniteHits).toThrow(/^Must provide a container/);
Expand All @@ -25,25 +27,20 @@ describe('infiniteHits()', () => {
let results;
let props;
let helper;
const defaultTemplates = {
hit: 'hit',
empty: 'empty',
};

beforeEach(() => {
helper = algoliasearchHelper({addAlgoliaAgent: () => {}});
helper.search = sinon.spy();

ReactDOM = {render: sinon.spy()};
infiniteHits.__Rewire__('ReactDOM', ReactDOM);
infiniteHits.__Rewire__('defaultTemplates', defaultTemplates);

container = document.createElement('div');
templateProps = {
transformData: undefined,
templatesConfig: undefined,
templates: defaultTemplates,
useCustomCompileOptions: {hit: false, empty: false},
useCustomCompileOptions: {item: false, empty: false},
};
widget = infiniteHits({container, cssClasses: {root: ['root', 'cx']}});
widget.init({helper});
Expand Down
106 changes: 27 additions & 79 deletions src/widgets/infinite-hits/infinite-hits.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
import React from 'react';
import ReactDOM from 'react-dom';
import {
bemHelper,
prepareTemplateProps,
getContainerNode,
} from '../../lib/utils.js';
import cx from 'classnames';
import InfiniteHits from '../../components/InfiniteHits.js';
import defaultTemplates from './defaultTemplates.js';

const bem = bemHelper('ais-infinite-hits');
import connectInfiniteHits from '../../connectors/infinite-hits/connectInfiniteHits.js';

/**
* Display the list of results (hits) from the current search
Expand All @@ -29,75 +22,30 @@ const bem = bemHelper('ais-infinite-hits');
* @param {string|string[]} [options.cssClasses.item] CSS class to add to each result
* @return {Object}
*/
const usage = `
Usage:
infiniteHits({
container,
[ cssClasses.{root,empty,item}={} ],
[ templates.{empty,item} | templates.{empty} ],
[ showMoreLabel="Show more results" ]
[ transformData.{empty,item} | transformData.{empty} ],
[ hitsPerPage=20 ]
})`;
function infiniteHits({
container,
cssClasses: userCssClasses = {},
showMoreLabel = 'Show more results',
templates = defaultTemplates,
transformData,
hitsPerPage = 20,
} = {}) {
if (!container) {
throw new Error(`Must provide a container.${usage}`);
}

const containerNode = getContainerNode(container);
const cssClasses = {
root: cx(bem(null), userCssClasses.root),
item: cx(bem('item'), userCssClasses.item),
empty: cx(bem(null, 'empty'), userCssClasses.empty),
showmore: cx(bem('showmore'), userCssClasses.showmore),
};

let hitsCache = [];

const getShowMore = helper => () => helper.nextPage().search();

return {
getConfiguration: () => ({hitsPerPage}),
init({templatesConfig, helper}) {
this._templateProps = prepareTemplateProps({
transformData,
defaultTemplates,
templatesConfig,
templates,
});

this.showMore = getShowMore(helper);
},
render({results, state}) {
if (state.page === 0) {
hitsCache = [];
}

hitsCache = [...hitsCache, ...results.hits];

const isLastPage = results.nbPages <= results.page + 1;

ReactDOM.render(
<InfiniteHits
cssClasses={cssClasses}
hits={hitsCache}
results={results}
showMore={this.showMore}
showMoreLabel={showMoreLabel}
isLastPage={isLastPage}
templateProps={this._templateProps}
/>,
containerNode
);
},
};
export default connectInfiniteHits(defaultRendering);

function defaultRendering({
cssClasses,
hits,
results,
showMore,
showMoreLabel,
templateProps,
isLastPage,
containerNode,
}, isFirstRendering) {
if (isFirstRendering) return;

ReactDOM.render(
<InfiniteHits
cssClasses={cssClasses}
hits={hits}
results={results}
showMore={showMore}
showMoreLabel={showMoreLabel}
templateProps={templateProps}
isLastPage={isLastPage}
/>,
containerNode
);
}

export default infiniteHits;

0 comments on commit cdf8675

Please sign in to comment.