Skip to content

Commit

Permalink
feat(connector): connectCurrentRefinedValues (iteration 2)
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexandre Stanislawski committed Mar 20, 2017
1 parent 03653f1 commit 68408de
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 158 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,7 @@ describe('connectCurrentRefinedValues', () => {
expect(rendering.lastCall.args[1]).toBe(true);

const firstRenderingOptions = rendering.lastCall.args[0];
expect(firstRenderingOptions.containerNode).toBe(container);
expect(firstRenderingOptions.refinements).toEqual([]);
expect(firstRenderingOptions.collapsible).toBe(false);
expect(firstRenderingOptions.shouldAutoHideContainer).toBe(true);

widget.render({
results: new SearchResults(helper.state, [{}]),
Expand All @@ -50,10 +47,7 @@ describe('connectCurrentRefinedValues', () => {
expect(rendering.lastCall.args[1]).toBe(false);

const secondRenderingOptions = rendering.lastCall.args[0];
expect(secondRenderingOptions.containerNode).toBe(container);
expect(secondRenderingOptions.refinements).toEqual([]);
expect(secondRenderingOptions.collapsible).toBe(false);
expect(secondRenderingOptions.shouldAutoHideContainer).toBe(true);
});

it('Provide a function to clear the refinements at each step', () => {
Expand Down
125 changes: 8 additions & 117 deletions src/connectors/current-refined-values/connectCurrentRefinedValues.js
Original file line number Diff line number Diff line change
@@ -1,82 +1,33 @@
import {
bemHelper,
isDomElement,
getContainerNode,
prepareTemplateProps,
getRefinements,
clearRefinementsFromState,
clearRefinementsAndSearch,
} from '../../lib/utils.js';
import cx from 'classnames';

import isUndefined from 'lodash/isUndefined';
import isBoolean from 'lodash/isBoolean';
import isString from 'lodash/isString';
import isArray from 'lodash/isArray';
import isPlainObject from 'lodash/isPlainObject';
import isFunction from 'lodash/isFunction';
import isEmpty from 'lodash/isEmpty';

import map from 'lodash/map';
import reduce from 'lodash/reduce';
import filter from 'lodash/filter';
import defaultTemplates from './defaultTemplates';

const bem = bemHelper('ais-current-refined-values');

/**
* Instantiate a list of current refinements with the possibility to clear them
* @function currentRefinedValues
* @param {string|DOMElement} options.container CSS Selector or DOMElement to insert the widget
* @param {Array} [option.attributes] Attributes configuration
* @param {string} [option.attributes[].name] Required attribute name
* @param {string} [option.attributes[].label] Attribute label (passed to the item template)
* @param {string|Function} [option.attributes[].template] Attribute specific template
* @param {Function} [option.attributes[].transformData] Attribute specific transformData
* @param {boolean|string} [option.clearAll='before'] Clear all position (one of ('before', 'after', false))
* @param {boolean} [options.onlyListedAttributes=false] Only use declared attributes
* @param {Object} [options.templates] Templates to use for the widget
* @param {string|Function} [options.templates.header] Header template
* @param {string|Function} [options.templates.item] Item template
* @param {string|Function} [options.templates.clearAll] Clear all template
* @param {string|Function} [options.templates.footer] Footer template
* @param {Function} [options.transformData.item] Function to change the object passed to the `item` template
* @param {boolean} [options.autoHideContainer=true] Hide the container when no current refinements
* @param {Object} [options.cssClasses] CSS classes to be added
* @param {string} [options.cssClasses.root] CSS classes added to the root element
* @param {string} [options.cssClasses.header] CSS classes added to the header element
* @param {string} [options.cssClasses.body] CSS classes added to the body element
* @param {string} [options.cssClasses.clearAll] CSS classes added to the clearAll element
* @param {string} [options.cssClasses.list] CSS classes added to the list element
* @param {string} [options.cssClasses.item] CSS classes added to the item element
* @param {string} [options.cssClasses.link] CSS classes added to the link element
* @param {string} [options.cssClasses.count] CSS classes added to the count element
* @param {string} [options.cssClasses.footer] CSS classes added to the footer element
* @param {object|boolean} [options.collapsible=false] Hide the widget body and footer when clicking on header
* @param {boolean} [options.collapsible.collapsed] Initial collapsed state of a collapsible widget
* @return {Object}
*/
const usage = `Usage:
currentRefinedValues({
connectCurrentRefinedValues({
container,
[ attributes: [{name[, label, template, transformData]}] ],
[ onlyListedAttributes = false ],
[ clearAll = 'before' ] // One of ['before', 'after', false]
[ templates.{header,item,clearAll,footer} ],
[ transformData.{item} ],
[ autoHideContainer = true ],
[ cssClasses.{root, header, body, clearAll, list, item, link, count, footer} = {} ],
[ collapsible=false ]
})`;

const connectCurrentRefinedValues = renderCurrentRefinedValues => ({
container,
attributes = [],
onlyListedAttributes = false,
clearAll = 'before',
templates = defaultTemplates,
collapsible = false,
transformData,
autoHideContainer = true,
cssClasses: userCssClasses = {},
}) => {
const attributesOK = isArray(attributes) &&
reduce(
Expand All @@ -90,49 +41,16 @@ const connectCurrentRefinedValues = renderCurrentRefinedValues => ({
(isUndefined(val.transformData) || isFunction(val.transformData)),
true);

const templatesKeys = ['header', 'item', 'clearAll', 'footer'];
const templatesOK = isPlainObject(templates) &&
reduce(
templates,
(res, val, key) =>
res &&
templatesKeys.indexOf(key) !== -1 &&
(isString(val) || isFunction(val)),
true
);

const userCssClassesKeys = ['root', 'header', 'body', 'clearAll', 'list', 'item', 'link', 'count', 'footer'];
const userCssClassesOK = isPlainObject(userCssClasses) &&
reduce(
userCssClasses,
(res, val, key) =>
res &&
userCssClassesKeys.indexOf(key) !== -1 &&
isString(val) || isArray(val),
true);

const transformDataOK = isUndefined(transformData) ||
isFunction(transformData) ||
isPlainObject(transformData) && isFunction(transformData.item);

const showUsage = false ||
!(isString(container) || isDomElement(container)) ||
!isArray(attributes) ||
!attributesOK ||
!isBoolean(onlyListedAttributes) ||
[false, 'before', 'after'].indexOf(clearAll) === -1 ||
!isPlainObject(templates) ||
!templatesOK ||
!transformDataOK ||
!isBoolean(autoHideContainer) ||
!userCssClassesOK;
[false, 'before', 'after'].indexOf(clearAll) === -1;

if (showUsage) {
throw new Error(usage);
}

const containerNode = getContainerNode(container);

const attributeNames = map(attributes, attribute => attribute.name);
const restrictedTo = onlyListedAttributes ? attributeNames : [];

Expand All @@ -141,29 +59,10 @@ const connectCurrentRefinedValues = renderCurrentRefinedValues => ({
return res;
}, {});

const cssClasses = {
root: cx(bem(null), userCssClasses.root),
header: cx(bem('header'), userCssClasses.header),
body: cx(bem('body'), userCssClasses.body),
clearAll: cx(bem('clear-all'), userCssClasses.clearAll),
list: cx(bem('list'), userCssClasses.list),
item: cx(bem('item'), userCssClasses.item),
link: cx(bem('link'), userCssClasses.link),
count: cx(bem('count'), userCssClasses.count),
footer: cx(bem('footer'), userCssClasses.footer),
};

return {
init({helper, templatesConfig, createURL}) {
init({helper, createURL, instantSearchInstance}) {
this._clearRefinementsAndSearch = clearRefinementsAndSearch.bind(null, helper, restrictedTo);

this._templateProps = prepareTemplateProps({
transformData,
defaultTemplates,
templatesConfig,
templates,
});

const clearAllURL = createURL(clearRefinementsFromState(helper.state, restrictedTo));

const refinements = getFilteredRefinements({}, helper.state, attributeNames, onlyListedAttributes);
Expand All @@ -178,15 +77,11 @@ const connectCurrentRefinedValues = renderCurrentRefinedValues => ({
clearAllURL,
clearRefinementClicks,
clearRefinementURLs,
collapsible,
cssClasses,
refinements,
shouldAutoHideContainer: autoHideContainer,
templateProps: this._templateProps,
containerNode,
instantSearchInstance,
}, true);
},
render({results, helper, state, createURL}) {
render({results, helper, state, createURL, instantSearchInstance}) {
const clearAllURL = createURL(clearRefinementsFromState(state, restrictedTo));

const refinements = getFilteredRefinements(results, state, attributeNames, onlyListedAttributes);
Expand All @@ -200,12 +95,8 @@ const connectCurrentRefinedValues = renderCurrentRefinedValues => ({
clearAllURL,
clearRefinementClicks,
clearRefinementURLs,
collapsible,
cssClasses,
refinements,
shouldAutoHideContainer: autoHideContainer && refinements.length === 0,
templateProps: this._templateProps,
containerNode,
instantSearchInstance,
}, false);
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import algoliasearch from 'algoliasearch';
import algoliasearchHelper from 'algoliasearch-helper';
import {prepareTemplateProps} from '../../../lib/utils';
import currentRefinedValues from '../current-refined-values';
import defaultTemplates from '../../../connectors/current-refined-values/defaultTemplates';
import defaultTemplates from '../defaultTemplates';

describe('currentRefinedValues()', () => {
describe('types checking', () => {
Expand Down Expand Up @@ -420,7 +420,9 @@ describe('currentRefinedValues()', () => {
initParameters = {
helper,
createURL: () => '',
templatesConfig: {randomAttributeNeverUsed: 'value'},
instantSearchInstance: {
templatesConfig: {randomAttributeNeverUsed: 'value'},
},
};

renderParameters = {
Expand Down
Loading

0 comments on commit 68408de

Please sign in to comment.