Skip to content

Commit

Permalink
feat(connector): connectStats second iteration
Browse files Browse the repository at this point in the history
  • Loading branch information
iam4x committed Mar 21, 2017
1 parent 291f7ab commit 82b1cb3
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 99 deletions.
13 changes: 2 additions & 11 deletions src/connectors/stats/__tests__/connectStats-test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@


import sinon from 'sinon';

import jsHelper from 'algoliasearch-helper';
Expand Down Expand Up @@ -39,17 +38,13 @@ describe('connectStats', () => {
expect(isFirstRendering).toBe(true);

// should provide good values for the first rendering
const {containerNode, hitsPerPage, nbHits, nbPages, page,
processingTimeMS, query, collapsible, shouldAutoHideContainer} = rendering.lastCall.args[0];
expect(containerNode).toBe(container);
const {hitsPerPage, nbHits, nbPages, page, processingTimeMS, query} = rendering.lastCall.args[0];
expect(hitsPerPage).toBe(helper.state.hitsPerPage);
expect(nbHits).toBe(0);
expect(nbPages).toBe(0);
expect(page).toBe(helper.state.page);
expect(processingTimeMS).toBe(-1);
expect(query).toBe(helper.state.query);
expect(collapsible).toBe(false);
expect(shouldAutoHideContainer).toBe(true);
}

widget.render({
Expand All @@ -73,17 +68,13 @@ describe('connectStats', () => {
expect(isFirstRendering).toBe(false);

// should provide good values after the first search
const {containerNode, hitsPerPage, nbHits, nbPages, page,
processingTimeMS, query, collapsible, shouldAutoHideContainer} = rendering.lastCall.args[0];
expect(containerNode).toBe(container);
const {hitsPerPage, nbHits, nbPages, page, processingTimeMS, query} = rendering.lastCall.args[0];
expect(hitsPerPage).toBe(helper.state.hitsPerPage);
expect(nbHits).toBe(1);
expect(nbPages).toBe(1);
expect(page).toBe(helper.state.page);
expect(processingTimeMS).toBe(12);
expect(query).toBe(helper.state.query);
expect(collapsible).toBe(false);
expect(shouldAutoHideContainer).toBe(false);
}
});
});
84 changes: 24 additions & 60 deletions src/connectors/stats/connectStats.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,4 @@
import {
bemHelper,
prepareTemplateProps,
getContainerNode,
} from '../../lib/utils.js';
import cx from 'classnames';
import defaultTemplates from './defaultTemplates.js';

const bem = bemHelper('ais-stats');
import {checkRendering} from '../../lib/utils.js';

/**
* Display various stats about the current search state
Expand All @@ -28,75 +20,47 @@ const bem = bemHelper('ais-stats');
* @return {Object}
*/
const usage = `Usage:
stats({
container,
[ templates.{header,body,footer} ],
[ transformData.{body} ],
[ autoHideContainer]
})`;
const connectStats = statsRendering => ({
container,
cssClasses: userCssClasses = {},
autoHideContainer = true,
templates = defaultTemplates,
collapsible = false,
transformData,
} = {}) => {
if (!container) throw new Error(usage);
const containerNode = getContainerNode(container);

if (!containerNode) {
throw new Error(usage);
}
var customStats = connectState(function render(params, isFirstRendering) {
// params = {
// hitsPerPage,
// nbHits,
// nbPages,
// page,
// processingTimeMS,
// query,
// }
});
search.addWidget(customStats());
Full documentation available at https://community.algolia.com/instantsearch.js/connectors/connectStats.html`;

const cssClasses = {
body: cx(bem('body'), userCssClasses.body),
footer: cx(bem('footer'), userCssClasses.footer),
header: cx(bem('header'), userCssClasses.header),
root: cx(bem(null), userCssClasses.root),
time: cx(bem('time'), userCssClasses.time),
};
export default function connectStats(renderFn) {
checkRendering(renderFn, usage);

return {
init({templatesConfig, helper}) {
this._templateProps = prepareTemplateProps({
transformData,
defaultTemplates,
templatesConfig,
templates,
});
return () => ({
init({helper, instantSearchInstance}) {
this._instantSearchInstance = instantSearchInstance;

statsRendering({
collapsible,
cssClasses,
renderFn({
instantSearchInstance,
hitsPerPage: helper.state.hitsPerPage,
nbHits: 0,
nbPages: 0,
page: helper.state.page,
processingTimeMS: -1,
query: helper.state.query,
shouldAutoHideContainer: autoHideContainer,
templateProps: this._templateProps,
containerNode,
}, true);
},

render({results}) {
statsRendering({
collapsible,
cssClasses,
renderFn({
instantSearchInstance: this._instantSearchInstance,
hitsPerPage: results.hitsPerPage,
nbHits: results.nbHits,
nbPages: results.nbPages,
page: results.page,
processingTimeMS: results.processingTimeMS,
query: results.query,
shouldAutoHideContainer: autoHideContainer && results.nbHits === 0,
templateProps: this._templateProps,
containerNode,
}, false);
},
};
};

export default connectStats;
});
}
5 changes: 5 additions & 0 deletions src/widgets/stats/__tests__/stats-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ describe('stats()', () => {
processingTimeMS: 42,
query: 'a query',
};

widget.init({
helper: {state: {}},
instantSearchInstance: {templatesConfig: undefined},
});
});

it('configures nothing', () => {
Expand Down
File renamed without changes.
128 changes: 100 additions & 28 deletions src/widgets/stats/stats.js
Original file line number Diff line number Diff line change
@@ -1,43 +1,48 @@
import React from 'react';
import ReactDOM from 'react-dom';
import cx from 'classnames';

import Stats from '../../components/Stats/Stats.js';
import connectStats from '../../connectors/stats/connectStats.js';
import defaultTemplates from './defaultTemplates.js';

/**
* Display various stats about the current search state
* @function stats
* @param {string|DOMElement} options.container CSS Selector or DOMElement to insert the widget
* @param {Object} [options.templates] Templates to use for the widget
* @param {string|Function} [options.templates.header=''] Header template
* @param {string|Function} [options.templates.body] Body template, provided with `hasManyResults`,
* `hasNoResults`, `hasOneResult`, `hitsPerPage`, `nbHits`, `nbPages`, `page`, `processingTimeMS`, `query`
* @param {string|Function} [options.templates.footer=''] Footer template
* @param {Function} [options.transformData.body] Function to change the object passed to the `body` template
* @param {boolean} [options.autoHideContainer=true] Hide the container when no results match
* @param {Object} [options.cssClasses] CSS classes to add
* @param {string|string[]} [options.cssClasses.root] CSS class to add to the root element
* @param {string|string[]} [options.cssClasses.header] CSS class to add to the header element
* @param {string|string[]} [options.cssClasses.body] CSS class to add to the body element
* @param {string|string[]} [options.cssClasses.footer] CSS class to add to the footer element
* @param {string|string[]} [options.cssClasses.time] CSS class to add to the element wrapping the time processingTimeMs
* @return {Object}
*/
export default connectStats(defaultRendering);
function defaultRendering({
collapsible,
import {
bemHelper,
prepareTemplateProps,
getContainerNode,
} from '../../lib/utils.js';

const bem = bemHelper('ais-stats');

const renderer = ({
containerNode,
cssClasses,
collapsible,
autoHideContainer,
renderState,
templates,
transformData,
}) => ({
hitsPerPage,
nbHits,
nbPages,
page,
processingTimeMS,
query,
shouldAutoHideContainer,
templateProps,
containerNode,
}, isFirstRendering) {
if (isFirstRendering) return;
instantSearchInstance: {templatesConfig},
}, isFirstRendering) => {
if (isFirstRendering) {
renderState.templateProps = prepareTemplateProps({
transformData,
defaultTemplates,
templatesConfig,
templates,
});
return;
}

const shouldAutoHideContainer = autoHideContainer && nbHits === 0;

ReactDOM.render(
<Stats
collapsible={collapsible}
Expand All @@ -49,8 +54,75 @@ function defaultRendering({
processingTimeMS={processingTimeMS}
query={query}
shouldAutoHideContainer={shouldAutoHideContainer}
templateProps={templateProps}
templateProps={renderState.templateProps}
/>,
containerNode
);
};

const usage = `Usage:
stats({
container,
[ templates.{header,body,footer} ],
[ transformData.{body} ],
[ autoHideContainer]
})`;

/**
* Display various stats about the current search state
* @function stats
* @param {string|DOMElement} options.container CSS Selector or DOMElement to insert the widget
* @param {Object} [options.templates] Templates to use for the widget
* @param {string|Function} [options.templates.header=''] Header template
* @param {string|Function} [options.templates.body] Body template, provided with `hasManyResults`,
* `hasNoResults`, `hasOneResult`, `hitsPerPage`, `nbHits`, `nbPages`, `page`, `processingTimeMS`, `query`
* @param {string|Function} [options.templates.footer=''] Footer template
* @param {Function} [options.transformData.body] Function to change the object passed to the `body` template
* @param {boolean} [options.autoHideContainer=true] Hide the container when no results match
* @param {Object} [options.cssClasses] CSS classes to add
* @param {string|string[]} [options.cssClasses.root] CSS class to add to the root element
* @param {string|string[]} [options.cssClasses.header] CSS class to add to the header element
* @param {string|string[]} [options.cssClasses.body] CSS class to add to the body element
* @param {string|string[]} [options.cssClasses.footer] CSS class to add to the footer element
* @param {string|string[]} [options.cssClasses.time] CSS class to add to the element wrapping the time processingTimeMs
* @return {Object} widget
*/
export default function stats({
container,
cssClasses: userCssClasses = {},
autoHideContainer = true,
collapsible = false,
transformData,
templates = defaultTemplates,
} = {}) {
if (!container) {
throw new Error(usage);
}

const containerNode = getContainerNode(container);

const cssClasses = {
body: cx(bem('body'), userCssClasses.body),
footer: cx(bem('footer'), userCssClasses.footer),
header: cx(bem('header'), userCssClasses.header),
root: cx(bem(null), userCssClasses.root),
time: cx(bem('time'), userCssClasses.time),
};

const specializedRenderer = renderer({
containerNode,
cssClasses,
collapsible,
autoHideContainer,
renderState: {},
templates,
transformData,
});

try {
const makeWidget = connectStats(specializedRenderer);
return makeWidget();
} catch (e) {
throw new Error(usage);
}
}

0 comments on commit 82b1cb3

Please sign in to comment.