Skip to content

Commit

Permalink
feat(connectors): connectSortBySelector (iteration 2)
Browse files Browse the repository at this point in the history
  • Loading branch information
iam4x committed Mar 21, 2017
1 parent 7350c3a commit dec2d31
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 96 deletions.
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 @@ -44,14 +43,12 @@ describe('connectSortBySelector', () => {
expect(isFirstRendering).toBe(true);

// should provide good values for the first rendering
const {containerNode, currentValue, options, shouldAutoHideContainer} = rendering.lastCall.args[0];
expect(containerNode).toBe(container);
const {currentValue, options} = rendering.lastCall.args[0];
expect(currentValue).toBe(helper.state.index);
expect(options).toEqual([
{label: 'Sort products by relevance', value: 'relevance'},
{label: 'Sort products by price', value: 'priceASC'},
]);
expect(shouldAutoHideContainer).toBe(false);
}

widget.render({
Expand All @@ -67,14 +64,12 @@ describe('connectSortBySelector', () => {
expect(isFirstRendering).toBe(false);

// should provide good values after the first search
const {containerNode, currentValue, options, shouldAutoHideContainer} = rendering.lastCall.args[0];
expect(containerNode).toBe(container);
const {currentValue, options} = rendering.lastCall.args[0];
expect(currentValue).toBe(helper.state.index);
expect(options).toEqual([
{label: 'Sort products by relevance', value: 'relevance'},
{label: 'Sort products by price', value: 'priceASC'},
]);
expect(shouldAutoHideContainer).toBe(false);
}
});

Expand Down
121 changes: 50 additions & 71 deletions src/connectors/sort-by-selector/connectSortBySelector.js
Original file line number Diff line number Diff line change
@@ -1,87 +1,66 @@
import findIndex from 'lodash/findIndex';
import map from 'lodash/map';
import {
bemHelper,
getContainerNode,
} from '../../lib/utils.js';
import cx from 'classnames';
import {checkRendering} from '../../lib/utils.js';

const bem = bemHelper('ais-sort-by-selector');
/**
* Instantiate a dropdown element to choose the current targeted index
* @function sortBySelector
* @param {string|DOMElement} options.container CSS Selector or DOMElement to insert the widget
* @function connectSortBySelector
* @param {Array} options.indices Array of objects defining the different indices to choose from.
* @param {string} options.indices[0].name Name of the index to target
* @param {string} options.indices[0].label Label displayed in the dropdown
* @param {boolean} [options.autoHideContainer=false] Hide the container when no results match
* @param {Object} [options.cssClasses] CSS classes to be added
* @param {string|string[]} [options.cssClasses.root] CSS classes added to the parent <select>
* @param {string|string[]} [options.cssClasses.item] CSS classes added to each <option>
* @return {Object}
*/
const usage = `Usage:
sortBySelector({
container,
indices,
[cssClasses.{root,item}={}],
[autoHideContainer=false]
})`;
const connectSortBySelector = sortBySelectorRendering => ({
container,
indices,
cssClasses: userCssClasses = {},
autoHideContainer = false,
} = {}) => {
if (!container || !indices) {
throw new Error(usage);
}
var customSortBySelector = connectSortBySelector(function render(params, isFirstRendering) {
// params = {
// currentValue,
// options,
// setValue,
// nbHits,
// }
});
search.addWidget(
customSortBySelector({ indices })
);
Full documentation available at https://community.algolia.com/instantsearch.js/connectors/connectSortBySelector.html
`;
export default function connectSortBySelector(renderFn) {
checkRendering(renderFn, usage);

const containerNode = getContainerNode(container);
return ({indices}) => {
if (!indices) {
throw new Error(usage);
}

// in order to fit the component parameters, name has to be mapped to value
const selectorOptions = map(
indices,
index => ({label: index.label, value: index.name})
);
const selectorOptions = indices.map(({label, name}) => ({label, value: name}));

const cssClasses = {
root: cx(bem(null), userCssClasses.root),
item: cx(bem('item'), userCssClasses.item),
};
return {
init({helper}) {
const currentIndex = helper.getIndex();
const isIndexInList = indices.find(({name}) => name === currentIndex);

return {
init({helper}) {
const currentIndex = helper.getIndex();
const isIndexInList = findIndex(indices, {name: currentIndex}) !== -1;
if (!isIndexInList) {
throw new Error(`[sortBySelector]: Index ${currentIndex} not present in \`indices\``);
}
this.setIndex = indexName => helper
.setIndex(indexName)
.search();
if (!isIndexInList) {
throw new Error(`[sortBySelector]: Index ${currentIndex} not present in \`indices\``);
}

sortBySelectorRendering({
cssClasses,
currentValue: helper.getIndex(),
options: selectorOptions,
setValue: this.setIndex,
shouldAutoHideContainer: autoHideContainer,
containerNode,
}, true);
},
this.setIndex = indexName => helper
.setIndex(indexName)
.search();

render({helper, results}) {
sortBySelectorRendering({
cssClasses,
currentValue: helper.getIndex(),
options: selectorOptions,
setValue: this.setIndex,
shouldAutoHideContainer: autoHideContainer && results.nbHits === 0,
containerNode,
}, false);
},
};
};
renderFn({
currentValue: currentIndex,
options: selectorOptions,
setValue: this.setIndex,
nbHits: 0,
}, true);
},

export default connectSortBySelector;
render({helper, results}) {
renderFn({
currentValue: helper.getIndex(),
options: selectorOptions,
setValue: this.setIndex,
nbHits: results.nbHits,
}, false);
},
};
};
}
82 changes: 64 additions & 18 deletions src/widgets/sort-by-selector/sort-by-selector.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,27 @@
import React from 'react';
import ReactDOM from 'react-dom';
import cx from 'classnames';

import Selector from '../../components/Selector.js';
import connectSortBySelector from '../../connectors/sort-by-selector/connectSortBySelector.js';
import {bemHelper, getContainerNode} from '../../lib/utils.js';

/**
* Instantiate a dropdown element to choose the current targeted index
* @function sortBySelector
* @param {string|DOMElement} options.container CSS Selector or DOMElement to insert the widget
* @param {Array} options.indices Array of objects defining the different indices to choose from.
* @param {string} options.indices[0].name Name of the index to target
* @param {string} options.indices[0].label Label displayed in the dropdown
* @param {boolean} [options.autoHideContainer=false] Hide the container when no results match
* @param {Object} [options.cssClasses] CSS classes to be added
* @param {string|string[]} [options.cssClasses.root] CSS classes added to the parent <select>
* @param {string|string[]} [options.cssClasses.item] CSS classes added to each <option>
* @return {Object}
*/
const bem = bemHelper('ais-sort-by-selector');

export default connectSortBySelector(defaultRendering);
function defaultRendering({
const renderer = ({
containerNode,
cssClasses,
autoHideContainer,
}) => ({
currentValue,
options,
setValue,
shouldAutoHideContainer,
containerNode,
}, isFirstRendering) {
nbHits,
}, isFirstRendering) => {
if (isFirstRendering) return;

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

ReactDOM.render(
<Selector
cssClasses={cssClasses}
Expand All @@ -38,4 +32,56 @@ function defaultRendering({
/>,
containerNode
);
};

const usage = `Usage:
sortBySelector({
container,
indices,
[cssClasses.{root,item}={}],
[autoHideContainer=false]
})`;

/**
* Instantiate a dropdown element to choose the current targeted index
* @function sortBySelector
* @param {string|DOMElement} options.container CSS Selector or DOMElement to insert the widget
* @param {Array} options.indices Array of objects defining the different indices to choose from.
* @param {string} options.indices[0].name Name of the index to target
* @param {string} options.indices[0].label Label displayed in the dropdown
* @param {boolean} [options.autoHideContainer=false] Hide the container when no results match
* @param {Object} [options.cssClasses] CSS classes to be added
* @param {string|string[]} [options.cssClasses.root] CSS classes added to the parent <select>
* @param {string|string[]} [options.cssClasses.item] CSS classes added to each <option>
* @return {Object} widget
*/
export default function sortBySelector({
container,
indices,
cssClasses: userCssClasses = {},
autoHideContainer = false,
}) {
if (!container) {
throw new Error(usage);
}

const containerNode = getContainerNode(container);

const cssClasses = {
root: cx(bem(null), userCssClasses.root),
item: cx(bem('item'), userCssClasses.item),
};

const specializedRenderer = renderer({
containerNode,
cssClasses,
autoHideContainer,
});

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

0 comments on commit dec2d31

Please sign in to comment.