Skip to content

Commit

Permalink
fix(toggle): support negative numeric values for on/off (#1551)
Browse files Browse the repository at this point in the history
fixes #1537

Using stock:-2 as a facetRefinement is supported only if you escape
the minus sign, otherwise it's considered as negative facet refinement
(remove "-2" from facets).
  • Loading branch information
vvo authored and bobylito committed Nov 15, 2016
1 parent 2ca586d commit e4d88e0
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 90 deletions.
47 changes: 47 additions & 0 deletions src/widgets/toggle/implementations/__tests__/currentToggle-test.js
Expand Up @@ -187,6 +187,53 @@ describe('currentToggle()', () => {
expect(ReactDOM.render.secondCall.args[0]).toEqualJSX(<RefinementList {...props} />);
});

it('supports negative numeric off or on values', () => {
results = {
hits: [{Hello: ', world!'}],
nbHits: 1,
getFacetValues: sinon.stub().returns([
{name: '-2', count: 2, isRefined: true},
{name: '5', count: 1, isRefined: false},
]),
};
widget = currentToggle({
containerNode,
hasAnOffValue: true,
attributeName,
label,
cssClasses,
userValues: {
off: -2,
on: 5,
},
RefinementList,
collapsible,
});
widget.getConfiguration();
widget.init({state, helper});
widget.render({results, helper, state, createURL});
widget.render({results, helper, state, createURL});

props = {
facetValues: [{
count: 1,
isRefined: false,
name: label,
offFacetValue: {count: 2, name: label, isRefined: true},
onFacetValue: {count: 1, name: label, isRefined: false},
}],
shouldAutoHideContainer: false,
...props,
};

expect(ReactDOM.render.firstCall.args[0]).toEqualJSX(<RefinementList {...props} />);
expect(ReactDOM.render.secondCall.args[0]).toEqualJSX(<RefinementList {...props} />);

widget.toggleRefinement(helper, 'facetValueToRefine', true);
expect(helper.removeDisjunctiveFacetRefinement.calledWith(attributeName, 5)).toBe(true);
expect(helper.addDisjunctiveFacetRefinement.calledWith(attributeName, '\\-2')).toBe(true);
});

it('without facet values', () => {
results = {
hits: [],
Expand Down
194 changes: 104 additions & 90 deletions src/widgets/toggle/implementations/currentToggle.js
Expand Up @@ -19,106 +19,120 @@ const currentToggle = ({
containerNode,
RefinementList,
cssClasses,
} = {}) => ({
getConfiguration() {
return {
disjunctiveFacets: [attributeName],
};
},
toggleRefinement(helper, facetValue, isRefined) {
const on = userValues.on;
const off = userValues.off;
} = {}) => {
const on = userValues ? escapeRefinement(userValues.on) : undefined;
const off = userValues ? escapeRefinement(userValues.off) : undefined;

// Checking
if (!isRefined) {
if (hasAnOffValue) {
helper.removeDisjunctiveFacetRefinement(attributeName, off);
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.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,
});
helper.search();
},
init({state, helper, templatesConfig}) {
this._templateProps = prepareTemplateProps({
transformData,
defaultTemplates,
templatesConfig,
templates,
});

this.toggleRefinement = this.toggleRefinement.bind(this, helper);
this.toggleRefinement = this.toggleRefinement.bind(this, helper);

// no need to refine anything at init if no custom off values
if (!hasAnOffValue) {
return;
}
// 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, userValues.on);
if (!isRefined) {
helper.addDisjunctiveFacetRefinement(attributeName, userValues.off);
}
},
render({helper, results, state, createURL}) {
const isRefined = helper.state.isDisjunctiveFacetRefined(attributeName, userValues.on);
const onValue = userValues.on;
const offValue = userValues.off === undefined ? false : userValues.off;
const allFacetValues = results.getFacetValues(attributeName);
const onData = find(allFacetValues, {name: onValue.toString()});
const onFacetValue = {
name: label,
isRefined: onData !== undefined ? onData.isRefined : false,
count: onData === undefined ? null : onData.count,
};
const offData = hasAnOffValue ? find(allFacetValues, {name: offValue.toString()}) : undefined;
const offFacetValue = {
name: label,
isRefined: offData !== undefined ? offData.isRefined : false,
count: offData === undefined ? results.nbHits : offData.count,
};
// Add filtering on the 'off' value if set
const isRefined = state.isDisjunctiveFacetRefined(attributeName, on);
if (!isRefined) {
helper.addDisjunctiveFacetRefinement(attributeName, off);
}
},
render({helper, results, state, createURL}) {
const isRefined = helper.state.isDisjunctiveFacetRefined(attributeName, on);
const onValue = on;
const offValue = off === undefined ? false : off;
const allFacetValues = results.getFacetValues(attributeName);
const onData = find(allFacetValues, {name: unescapeRefinement(onValue)});
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;
// 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,
};
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 ? onValue : userValues.off)
.addDisjunctiveFacetRefinement(attributeName, isRefined ? userValues.off : onValue)
// Bind createURL to this specific attribute
function _createURL() {
return createURL(
state
.removeDisjunctiveFacetRefinement(attributeName, isRefined ? onValue : off)
.addDisjunctiveFacetRefinement(attributeName, isRefined ? off : onValue)
);
}

ReactDOM.render(
<RefinementList
collapsible={collapsible}
createURL={_createURL}
cssClasses={cssClasses}
facetValues={[facetValue]}
shouldAutoHideContainer={(facetValue.count === 0 || facetValue.count === null)}
templateProps={this._templateProps}
toggleRefinement={this.toggleRefinement}
/>,
containerNode
);
}
},
};
};

function escapeRefinement(value) {
if (typeof value === 'number' && value < 0) {
value = String(value).replace('-', '\\-');
}

return value;
}

ReactDOM.render(
<RefinementList
collapsible={collapsible}
createURL={_createURL}
cssClasses={cssClasses}
facetValues={[facetValue]}
shouldAutoHideContainer={(facetValue.count === 0 || facetValue.count === null)}
templateProps={this._templateProps}
toggleRefinement={this.toggleRefinement}
/>,
containerNode
);
},
});
function unescapeRefinement(value) {
return String(value).replace(/^\\-/, '-');
}

export default currentToggle;

0 comments on commit e4d88e0

Please sign in to comment.