-
Notifications
You must be signed in to change notification settings - Fork 516
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This one is special. It contains a compatibility layer that we might want to remove for good. The tests are disabled on that part.
- Loading branch information
Alexandre Stanislawski
committed
Feb 15, 2017
1 parent
680743b
commit bf9a9c0
Showing
9 changed files
with
412 additions
and
118 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
import { | ||
bemHelper, | ||
getContainerNode, | ||
} from '../../lib/utils.js'; | ||
import defaultTemplates from './defaultTemplates.js'; | ||
import cx from 'classnames'; | ||
import connectCurrent from './implementations/current.js'; | ||
import connectLegacy from './implementations/legacy.js'; | ||
|
||
const bem = bemHelper('ais-toggle'); | ||
|
||
// we cannot use helper. because the facet is not yet declared in the helper | ||
const hasFacetsRefinementsFor = (attributeName, searchParameters) => | ||
searchParameters && | ||
searchParameters.facetsRefinements && | ||
searchParameters.facetsRefinements[attributeName] !== undefined; | ||
|
||
/** | ||
* Instantiate the toggling of a boolean facet filter on and off. | ||
* @function toggle | ||
* @param {string|DOMElement} options.container CSS Selector or DOMElement to insert the widget | ||
* @param {string} options.attributeName Name of the attribute for faceting (eg. "free_shipping") | ||
* @param {string} options.label Human-readable name of the filter (eg. "Free Shipping") | ||
* @param {Object} [options.values] Lets you define the values to filter on when toggling | ||
* @param {string|number|boolean} [options.values.on=true] Value to filter on when checked | ||
* @param {string|number|boolean} [options.values.off=undefined] Value to filter on when unchecked | ||
* element (when using the default template). By default when switching to `off`, no refinement will be asked. So you | ||
* will get both `true` and `false` results. If you set the off value to `false` then you will get only objects | ||
* having `false` has a value for the selected attribute. | ||
* @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, provided with `name`, `count`, `isRefined`, `url` data properties | ||
* count is always the number of hits that would be shown if you toggle the widget. We also provide | ||
* `onFacetValue` and `offFacetValue` objects with according counts. | ||
* @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 there are no results | ||
* @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.list] CSS class to add to the list element | ||
* @param {string|string[]} [options.cssClasses.item] CSS class to add to each item element | ||
* @param {string|string[]} [options.cssClasses.active] CSS class to add to each active element | ||
* @param {string|string[]} [options.cssClasses.label] CSS class to add to each | ||
* label element (when using the default template) | ||
* @param {string|string[]} [options.cssClasses.checkbox] CSS class to add to each | ||
* checkbox element (when using the default template) | ||
* @param {string|string[]} [options.cssClasses.count] CSS class to add to each count | ||
* @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: | ||
toggle({ | ||
container, | ||
attributeName, | ||
label, | ||
[ values={on: true, off: undefined} ], | ||
[ cssClasses.{root,header,body,footer,list,item,active,label,checkbox,count} ], | ||
[ templates.{header,item,footer} ], | ||
[ transformData.{item} ], | ||
[ autoHideContainer=true ], | ||
[ collapsible=false ] | ||
})`; | ||
function connectToggle(toggleRendering) { | ||
const legacyToggle = connectLegacy(toggleRendering); | ||
const currentToggle = connectCurrent(toggleRendering); | ||
|
||
return ({ | ||
container, | ||
attributeName, | ||
label, | ||
values: userValues = {on: true, off: undefined}, | ||
templates = defaultTemplates, | ||
collapsible = false, | ||
cssClasses: userCssClasses = {}, | ||
transformData, | ||
autoHideContainer = true, | ||
} = {}) => { | ||
const containerNode = getContainerNode(container); | ||
|
||
if (!container || !attributeName || !label) { | ||
throw new Error(usage); | ||
} | ||
|
||
const hasAnOffValue = userValues.off !== undefined; | ||
|
||
const cssClasses = { | ||
root: cx(bem(null), userCssClasses.root), | ||
header: cx(bem('header'), userCssClasses.header), | ||
body: cx(bem('body'), userCssClasses.body), | ||
footer: cx(bem('footer'), userCssClasses.footer), | ||
list: cx(bem('list'), userCssClasses.list), | ||
item: cx(bem('item'), userCssClasses.item), | ||
active: cx(bem('item', 'active'), userCssClasses.active), | ||
label: cx(bem('label'), userCssClasses.label), | ||
checkbox: cx(bem('checkbox'), userCssClasses.checkbox), | ||
count: cx(bem('count'), userCssClasses.count), | ||
}; | ||
|
||
// store the computed options for usage in the two toggle implementations | ||
const implemOptions = { | ||
attributeName, | ||
label, | ||
userValues, | ||
templates, | ||
collapsible, | ||
transformData, | ||
hasAnOffValue, | ||
containerNode, | ||
cssClasses, | ||
autoHideContainer, | ||
}; | ||
|
||
return { | ||
getConfiguration(currentSearchParameters, searchParametersFromUrl) { | ||
const useLegacyToggle = | ||
hasFacetsRefinementsFor(attributeName, currentSearchParameters) || | ||
hasFacetsRefinementsFor(attributeName, searchParametersFromUrl); | ||
|
||
const toggleImplementation = useLegacyToggle ? | ||
legacyToggle(implemOptions) : | ||
currentToggle(implemOptions); | ||
|
||
this.init = toggleImplementation.init.bind(toggleImplementation); | ||
this.render = toggleImplementation.render.bind(toggleImplementation); | ||
return toggleImplementation.getConfiguration(currentSearchParameters, searchParametersFromUrl); | ||
}, | ||
init() {}, | ||
render() {}, | ||
}; | ||
}; | ||
} | ||
|
||
export default connectToggle; |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
import find from 'lodash/find'; | ||
import defaultTemplates from '../defaultTemplates.js'; | ||
import { | ||
prepareTemplateProps, | ||
escapeRefinement, | ||
unescapeRefinement, | ||
} from '../../../lib/utils.js'; | ||
|
||
const connectToggle = toggleRendering => ({ | ||
attributeName, | ||
label, | ||
userValues, | ||
templates, | ||
collapsible, | ||
transformData, | ||
hasAnOffValue, | ||
containerNode, | ||
cssClasses, | ||
autoHideContainer, | ||
} = {}) => { | ||
const on = userValues ? escapeRefinement(userValues.on) : undefined; | ||
const off = userValues ? escapeRefinement(userValues.off) : undefined; | ||
|
||
return { | ||
getConfiguration() { | ||
return { | ||
disjunctiveFacets: [attributeName], | ||
}; | ||
}, | ||
toggleRefinement(helper, facetValue, isRefined) { | ||
// Checking | ||
if (!isRefined) { | ||
if (hasAnOffValue) { | ||
helper.removeDisjunctiveFacetRefinement(attributeName, off); | ||
} | ||
helper.addDisjunctiveFacetRefinement(attributeName, on); | ||
} else { | ||
// Unchecking | ||
helper.removeDisjunctiveFacetRefinement(attributeName, on); | ||
if (hasAnOffValue) { | ||
helper.addDisjunctiveFacetRefinement(attributeName, off); | ||
} | ||
} | ||
|
||
helper.search(); | ||
}, | ||
init({state, helper, templatesConfig}) { | ||
this._templateProps = prepareTemplateProps({ | ||
transformData, | ||
defaultTemplates, | ||
templatesConfig, | ||
templates, | ||
}); | ||
|
||
this.toggleRefinement = this.toggleRefinement.bind(this, helper); | ||
|
||
// no need to refine anything at init if no custom off values | ||
if (!hasAnOffValue) { | ||
return; | ||
} | ||
|
||
// Add filtering on the 'off' value if set | ||
const isRefined = state.isDisjunctiveFacetRefined(attributeName, on); | ||
if (!isRefined) { | ||
helper.addDisjunctiveFacetRefinement(attributeName, off); | ||
} | ||
|
||
toggleRendering({ | ||
collapsible, | ||
createURL: () => '', | ||
cssClasses, | ||
facetValues: [], | ||
shouldAutoHideContainer: autoHideContainer, | ||
templateProps: this._templateProps, | ||
toggleRefinement: this.toggleRefinement, | ||
containerNode, | ||
}, false); | ||
}, | ||
render({helper, results, state, createURL}) { | ||
const isRefined = helper.state.isDisjunctiveFacetRefined(attributeName, on); | ||
const offValue = off === undefined ? false : off; | ||
const allFacetValues = results.getFacetValues(attributeName); | ||
const onData = find(allFacetValues, {name: unescapeRefinement(on)}); | ||
const onFacetValue = { | ||
name: label, | ||
isRefined: onData !== undefined ? onData.isRefined : false, | ||
count: onData === undefined ? null : onData.count, | ||
}; | ||
const offData = hasAnOffValue ? find(allFacetValues, {name: unescapeRefinement(offValue)}) : undefined; | ||
const offFacetValue = { | ||
name: label, | ||
isRefined: offData !== undefined ? offData.isRefined : false, | ||
count: offData === undefined ? results.nbHits : offData.count, | ||
}; | ||
|
||
// what will we show by default, | ||
// if checkbox is not checked, show: [ ] free shipping (countWhenChecked) | ||
// if checkbox is checked, show: [x] free shipping (countWhenNotChecked) | ||
const nextRefinement = isRefined ? offFacetValue : onFacetValue; | ||
|
||
const facetValue = { | ||
name: label, | ||
isRefined, | ||
count: nextRefinement === undefined ? null : nextRefinement.count, | ||
onFacetValue, | ||
offFacetValue, | ||
}; | ||
|
||
// Bind createURL to this specific attribute | ||
function _createURL() { | ||
return createURL( | ||
state | ||
.removeDisjunctiveFacetRefinement(attributeName, isRefined ? on : off) | ||
.addDisjunctiveFacetRefinement(attributeName, isRefined ? off : on) | ||
); | ||
} | ||
|
||
toggleRendering({ | ||
collapsible, | ||
createURL: _createURL, | ||
cssClasses, | ||
facetValues: [facetValue], | ||
shouldAutoHideContainer: autoHideContainer && (facetValue.count === 0 || facetValue.count === null), | ||
templateProps: this._templateProps, | ||
toggleRefinement: this.toggleRefinement, | ||
containerNode, | ||
}, false); | ||
}, | ||
}; | ||
}; | ||
|
||
export default connectToggle; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
import find from 'lodash/find'; | ||
import defaultTemplates from '../defaultTemplates.js'; | ||
import { | ||
prepareTemplateProps, | ||
} from '../../../lib/utils.js'; | ||
|
||
const connectToggle = toggleRendering => ({ | ||
attributeName, | ||
label, | ||
userValues, | ||
templates, | ||
collapsible, | ||
transformData, | ||
hasAnOffValue, | ||
autoHideContainer, | ||
cssClasses, | ||
containerNode, | ||
} = {}) => { //eslint-disable-line | ||
return { | ||
getConfiguration() { | ||
return { | ||
facets: [attributeName], | ||
}; | ||
}, | ||
toggleRefinement(helper, facetValue, isRefined) { | ||
const on = userValues.on; | ||
const off = userValues.off; | ||
|
||
// Checking | ||
if (!isRefined) { | ||
if (hasAnOffValue) { | ||
helper.removeFacetRefinement(attributeName, off); | ||
} | ||
helper.addFacetRefinement(attributeName, on); | ||
} else { | ||
// Unchecking | ||
helper.removeFacetRefinement(attributeName, on); | ||
if (hasAnOffValue) { | ||
helper.addFacetRefinement(attributeName, off); | ||
} | ||
} | ||
|
||
helper.search(); | ||
}, | ||
init({state, helper, templatesConfig}) { | ||
this._templateProps = prepareTemplateProps({ | ||
transformData, | ||
defaultTemplates, | ||
templatesConfig, | ||
templates, | ||
}); | ||
this.toggleRefinement = this.toggleRefinement.bind(this, helper); | ||
|
||
// no need to refine anything at init if no custom off values | ||
if (!hasAnOffValue) { | ||
return; | ||
} | ||
// Add filtering on the 'off' value if set | ||
const isRefined = state.isFacetRefined(attributeName, userValues.on); | ||
if (!isRefined) { | ||
helper.addFacetRefinement(attributeName, userValues.off); | ||
} | ||
|
||
toggleRendering({ | ||
collapsible, | ||
createURL: () => '', | ||
cssClasses, | ||
facetValues: [], | ||
shouldAutoHideContainer: autoHideContainer, | ||
templateProps: this._templateProps, | ||
toggleRefinement: this.toggleRefinement, | ||
containerNode, | ||
}, true); | ||
}, | ||
render({helper, results, state, createURL}) { | ||
const isRefined = helper.state.isFacetRefined(attributeName, userValues.on); | ||
const currentRefinement = isRefined ? userValues.on : userValues.off; | ||
let count; | ||
if (typeof currentRefinement === 'number') { | ||
count = results.getFacetStats(attributeName).sum; | ||
} else { | ||
const facetData = find(results.getFacetValues(attributeName), {name: isRefined.toString()}); | ||
count = facetData !== undefined ? facetData.count : null; | ||
} | ||
|
||
const facetValue = { | ||
name: label, | ||
isRefined, | ||
count, | ||
}; | ||
|
||
// Bind createURL to this specific attribute | ||
function _createURL() { | ||
return createURL(state.toggleRefinement(attributeName, isRefined)); | ||
} | ||
|
||
toggleRendering({ | ||
collapsible, | ||
createURL: _createURL, | ||
cssClasses, | ||
facetValues: [facetValue], | ||
shouldAutoHideContainer: autoHideContainer && results.nbHits === 0, | ||
templateProps: this._templateProps, | ||
toggleRefinement: this.toggleRefinement, | ||
containerNode, | ||
}, false); | ||
}, | ||
}; | ||
}; | ||
|
||
export default connectToggle; |
Oops, something went wrong.