Skip to content

Commit

Permalink
feat(ClearAll): add withQuery to also clear the search query (#1958)
Browse files Browse the repository at this point in the history
see #1936
  • Loading branch information
mthuret authored and vvo committed Feb 8, 2017
1 parent 94363f3 commit c0e695b
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 7 deletions.
5 changes: 3 additions & 2 deletions packages/react-instantsearch/src/components/ClearAll.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ class ClearAll extends Component {
translate: PropTypes.func.isRequired,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
refine: PropTypes.func.isRequired,
query: PropTypes.string,
};

render() {
const {translate, items, refine} = this.props;
const isDisabled = items.length === 0;
const {translate, items, refine, query} = this.props;
const isDisabled = items.length === 0 && (!query || query === '');

if (isDisabled) {
return (
Expand Down
19 changes: 19 additions & 0 deletions packages/react-instantsearch/src/components/ClearAll.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,23 @@ describe('ClearAll', () => {
btn.simulate('click');
expect(refine.mock.calls.length).toBe(1);
});

it('the clearAll button should not be disabled if there is a query, items, or both', () => {
const refine = jest.fn();
const wrapper = mount(
<ClearAll refine={refine} items={[]} query="value" />
);
const btn = wrapper.find('button');
expect(refine.mock.calls.length).toBe(0);

btn.simulate('click');

expect(refine.mock.calls.length).toBe(1);

wrapper.setProps({items: [{value: 'test', label: 'test: test'}]});

btn.simulate('click');

expect(refine.mock.calls.length).toBe(2);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import {PropTypes} from 'react';
* @name connectCurrentRefinements
* @kind connector
* @propType {function} [transformItems] - If provided, this function can be used to modify the `items` provided prop of the wrapped component (ex: for filtering or sorting items). this function takes the `items` prop as a parameter and expects it back in return.
* @propType {function} [clearsQuery=false] - If true will clear also the search query
* @providedPropType {function} refine - a function to remove a single filter
* @providedPropType {array.<{label: string, attributeName: string, currentRefinement: string || object, items: array, value: function}>} items - all the filters, the `value` is to pass to the `refine` function for removing all currentrefinements, `label` is for the display. When existing several refinements for the same atribute name, then you get a nested `items` object that contains a `label` and a `value` function to use to remove a single filter. `attributeName` and `currentRefinement` are metadata containing row values.
*/
* @providedPropType {function} query - the search query
*/
export default createConnector({
displayName: 'AlgoliaCurrentRefinements',

Expand All @@ -21,16 +23,19 @@ export default createConnector({
const items = metadata.reduce((res, meta) =>
typeof meta.items !== 'undefined' ? res.concat(meta.items) : res
, []);
const query = props.clearsQuery && searchResults.results ? searchResults.results.query : undefined;

return {
items: props.transformItems ? props.transformItems(items) : items,
canRefine: items.length > 0,
query,
};
},

refine(props, searchState, items) {
// `value` corresponds to our internal clear function computed in each connector metadata.
const refinementsToClear = items instanceof Array ? items.map(item => item.value) : [items];
searchState = props.clearsQuery && searchState.query ? {...searchState, query: ''} : searchState;
return refinementsToClear.reduce((res, clear) => clear(res), searchState);
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,21 @@ describe('connectCurrentRefinements', () => {
});

it('refine applies the selected filters clear method on searchState', () => {
const searchState = refine(null, {wow: 'sweet'}, [{
let searchState = refine({}, {wow: 'sweet'}, [{
value: nextState => ({...nextState, cool: 'neat'}),
}]);
expect(searchState).toEqual({wow: 'sweet', cool: 'neat'});

searchState = refine({clearsQuery: true}, {wow: 'sweet'}, [{
value: nextState => ({...nextState, cool: 'neat'}),
}]);
expect(searchState).toEqual({wow: 'sweet', cool: 'neat'});
});

it('the query should be removed from the search state if the props clearsQuery is passed', () => {
const searchState = refine({clearsQuery: true}, {wow: 'sweet', query: 'value'}, [{
value: nextState => ({...nextState, cool: 'neat'}),
}]);
expect(searchState).toEqual({wow: 'sweet', cool: 'neat', query: ''});
});
});
1 change: 1 addition & 0 deletions packages/react-instantsearch/src/widgets/ClearAll.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import ClearAllComponent from '../components/ClearAll.js';
* @name ClearAll
* @kind widget
* @propType {function} [transformItems] - If provided, this function can be used to modify the `items` provided prop of the wrapped component (ex: for filtering or sorting items). this function takes the `items` prop as a parameter and expects it back in return.
* @propType {function} [clearsQuery=false] - If true will clear also the search query
* @themeKey ais-ClearAll__root - the widget button
* @translationKey reset - the clear all button value
* @example
Expand Down
13 changes: 13 additions & 0 deletions stories/ClearAll.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,16 @@ stories.add('nothing to clear', () =>
<ClearAll />
</WrapWithHits>
);

stories.add('clear all refinements and the query', () =>
<WrapWithHits linkedStoryGroup="ClearAll">
<ClearAll
clearsQuery
translations={{reset: 'Clear refinements and query'}}
/>
<RefinementList
attributeName="category"
defaultRefinement={['Dining']}
/>
</WrapWithHits>
);
6 changes: 3 additions & 3 deletions stories/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,10 @@ const CustomHits = connectHits(({hits}) =>

WrapWithHits.propTypes = {
children: React.PropTypes.node,
searchBox: React.PropTypes.boolean,
searchBox: React.PropTypes.bool,
linkedStoryGroup: React.PropTypes.string,
hasPlayground: React.PropTypes.boolean,
pagination: React.PropTypes.boolean,
hasPlayground: React.PropTypes.bool,
pagination: React.PropTypes.bool,
searchParameters: React.PropTypes.object,
};

Expand Down

0 comments on commit c0e695b

Please sign in to comment.