diff --git a/docgen/src/guide/Default_refinements.md b/docgen/src/guide/Default_refinements.md index 6b06dc6956..88f7aedbdc 100644 --- a/docgen/src/guide/Default_refinements.md +++ b/docgen/src/guide/Default_refinements.md @@ -28,6 +28,39 @@ const App = () => ; ``` +## Hiding default refinements + +In some situations not only you want default refinements but you also do not want the user to be able to unselect them. + +To do this, you can use a [``](guide/Virtual_widgets.html). It allows you to pre refine any widget without +rendering anything. + +By default the [``](widgets/CurrentRefinements.html) widget or the +[`connectCurrentRefinements`](connectors/connectCurrentRefinements.html) connector will display your default refinements. If you want to hide them, you need to filter the items with `transformItems`. + +```jsx +import {InstantSearch, SearchBox, Menu} from 'react-instantsearch/dom'; +import {connectMenu} from 'react-instantsearch/connectors'; + +const VirtualMenu = connectMenu(() => null); + +const App = () => + +
+ items.filter(item => item.currentRefinement !== 'Orange')} + /> + + + +
+
; +``` + **Notes:** * The [search state guide](guide/Search_state.html) details all widgets and connectors state values... * Default refinements are handy when used as [Virtual widgets](guide/Virtual_widgets.html). @@ -39,4 +72,4 @@ const App = () => - \ No newline at end of file + diff --git a/docgen/src/guide/Sorting_and_filtering.md b/docgen/src/guide/Sorting_and_filtering.md index 3cc9d94471..b8721f7e1c 100644 --- a/docgen/src/guide/Sorting_and_filtering.md +++ b/docgen/src/guide/Sorting_and_filtering.md @@ -11,8 +11,10 @@ A frequent question that comes up is "How do I sort or filter the items of my wi For this, widgets and connectors that are handling items expose a `transformItems` prop. This prop is a function that has the `items` provided prop as a parameter. It will expect in return the `items` prop back. +## Supported widgets and connectors + This props can be found on every widgets or connectors that handle a list of `items`: -* [``](widgets/CurrentRefinements.html) and [`connectCurrentRefinements`](connectors/connectCurrentRefinements.html) +* [``](widgets/CurrentRefinements.html), [``](widgets/ClearAll.html) and [`connectCurrentRefinements`](connectors/connectCurrentRefinements.html) * [``](widgets/HierarchicalMenu.html) and [`connectHierarchicalMenu`](connectors/connectHierarchicalMenu.html) * [``](widgets/Menu.html) and [`connectMenu`](connectors/connectMenu.html) * [``](widgets/RefinementList.html) and [`connectRefinementList`](connectors/connectRefinementList.html) @@ -20,6 +22,8 @@ This props can be found on every widgets or connectors that handle a list of `it * [``](widgets/HitsPerPage.html) and [`connectHitsPerPage`](connectors/connectHitsPerPage.html) * [``](widgets/MultiRange.html) and [`connectMultiRange`](connectors/connectMultiRange.html) +## Example + The following example will show you how to change the default sort order of the [``](widgets/RefinementList.html) widget. ```jsx @@ -38,8 +42,9 @@ const App = () => ; ``` -**Notes:** -* The example shows how to sort items, but you can also filter them +## Common use cases +* Sorting items +* Filtering items
diff --git a/packages/react-instantsearch/src/connectors/connectHierarchicalMenu.js b/packages/react-instantsearch/src/connectors/connectHierarchicalMenu.js index 1311681c9d..c9b6c25b88 100644 --- a/packages/react-instantsearch/src/connectors/connectHierarchicalMenu.js +++ b/packages/react-instantsearch/src/connectors/connectHierarchicalMenu.js @@ -210,7 +210,7 @@ export default createConnector({ attributeName: rootAttribute, value: nextState => ({ ...nextState, - [namespace]: {[id]: ''}, + [namespace]: {...nextState[namespace], [id]: ''}, }), currentRefinement, }], diff --git a/packages/react-instantsearch/src/connectors/connectHierarchicalMenu.test.js b/packages/react-instantsearch/src/connectors/connectHierarchicalMenu.test.js index 45fdc2257c..852e5d06ec 100644 --- a/packages/react-instantsearch/src/connectors/connectHierarchicalMenu.test.js +++ b/packages/react-instantsearch/src/connectors/connectHierarchicalMenu.test.js @@ -209,9 +209,14 @@ describe('connectHierarchicalMenu', () => { value: metadata.items[0].value, }], }); + }); + + it('items value function should clear it from the search state', () => { + const metadata = getMetadata({attributes: ['one']}, {hierarchicalMenu: {one: 'one', two: 'two'}}); + + const searchState = metadata.items[0].value({hierarchicalMenu: {one: 'one', two: 'two'}}); - const searchState = metadata.items[0].value({hierarchicalMenu: {ok: 'wat'}}); - expect(searchState).toEqual({hierarchicalMenu: {ok: ''}}); + expect(searchState).toEqual({hierarchicalMenu: {one: '', two: 'two'}}); }); it('should return the right searchState when clean up', () => { diff --git a/packages/react-instantsearch/src/connectors/connectMenu.js b/packages/react-instantsearch/src/connectors/connectMenu.js index e3242fb0e5..029b81dd13 100644 --- a/packages/react-instantsearch/src/connectors/connectMenu.js +++ b/packages/react-instantsearch/src/connectors/connectMenu.js @@ -143,7 +143,7 @@ export default createConnector({ attributeName: props.attributeName, value: nextState => ({ ...nextState, - [namespace]: {[id]: ''}, + [namespace]: {...nextState[namespace], [id]: ''}, }), currentRefinement, }], diff --git a/packages/react-instantsearch/src/connectors/connectMenu.test.js b/packages/react-instantsearch/src/connectors/connectMenu.test.js index 7fe14c6444..9892f1514d 100644 --- a/packages/react-instantsearch/src/connectors/connectMenu.test.js +++ b/packages/react-instantsearch/src/connectors/connectMenu.test.js @@ -184,9 +184,14 @@ describe('connectMenu', () => { value: metadata.items[0].value, }], }); + }); + + it('items value function should clear it from the search state', () => { + const metadata = getMetadata({attributeName: 'one'}, {menu: {one: 'one', two: 'two'}}); + + const searchState = metadata.items[0].value({menu: {one: 'one', two: 'two'}}); - const searchState = metadata.items[0].value({menu: {wot: 'wat'}}); - expect(searchState).toEqual({menu: {wot: ''}}); + expect(searchState).toEqual({menu: {one: '', two: 'two'}}); }); it('should return the right searchState when clean up', () => { diff --git a/packages/react-instantsearch/src/connectors/connectMultiRange.js b/packages/react-instantsearch/src/connectors/connectMultiRange.js index c24f1b282f..eba82bec84 100644 --- a/packages/react-instantsearch/src/connectors/connectMultiRange.js +++ b/packages/react-instantsearch/src/connectors/connectMultiRange.js @@ -132,7 +132,7 @@ export default createConnector({ currentRefinement: label, value: nextState => ({ ...nextState, - [namespace]: {[id]: ''}, + [namespace]: {...nextState[namespace], [id]: ''}, }), }); } diff --git a/packages/react-instantsearch/src/connectors/connectMultiRange.test.js b/packages/react-instantsearch/src/connectors/connectMultiRange.test.js index aae0272284..808937f60e 100644 --- a/packages/react-instantsearch/src/connectors/connectMultiRange.test.js +++ b/packages/react-instantsearch/src/connectors/connectMultiRange.test.js @@ -144,9 +144,24 @@ describe('connectMultiRange', () => { currentRefinement: 'YAY', }], }); + }); + + it('items value function should clear it from the search state', () => { + const metadata = getMetadata( + { + attributeName: 'one', + items: [{ + label: 'YAY', + start: 100, + end: 200, + }], + }, + {multiRange: {one: '100:200', two: '200:400'}} + ); + + const searchState = metadata.items[0].value({multiRange: {one: '100:200', two: '200:400'}}); - const searchState = metadata.items[0].value({multiRange: {wot: '100:200'}}); - expect(searchState).toEqual({multiRange: {wot: ''}}); + expect(searchState).toEqual({multiRange: {one: '', two: '200:400'}}); }); it('should return the right searchState when clean up', () => { diff --git a/packages/react-instantsearch/src/connectors/connectPagination.js b/packages/react-instantsearch/src/connectors/connectPagination.js index 7ceec18e6e..618abbeef5 100644 --- a/packages/react-instantsearch/src/connectors/connectPagination.js +++ b/packages/react-instantsearch/src/connectors/connectPagination.js @@ -80,9 +80,6 @@ export default createConnector({ }, getMetadata() { - const id = getId(); - return { - id, - }; + return {id: getId()}; }, }); diff --git a/packages/react-instantsearch/src/connectors/connectRange.js b/packages/react-instantsearch/src/connectors/connectRange.js index 8f0c5d9c5e..692309044b 100644 --- a/packages/react-instantsearch/src/connectors/connectRange.js +++ b/packages/react-instantsearch/src/connectors/connectRange.js @@ -157,7 +157,7 @@ export default createConnector({ attributeName: props.attributeName, value: nextState => ({ ...nextState, - [namespace]: {[id]: {}}, + [namespace]: {...nextState[namespace], [id]: {}}, }), }; } diff --git a/packages/react-instantsearch/src/connectors/connectRange.test.js b/packages/react-instantsearch/src/connectors/connectRange.test.js index d14d471508..ed7c7d96ba 100644 --- a/packages/react-instantsearch/src/connectors/connectRange.test.js +++ b/packages/react-instantsearch/src/connectors/connectRange.test.js @@ -161,6 +161,14 @@ describe('connectRange', () => { }); }); + it('items value function should clear it from the search state', () => { + const metadata = getMetadata({attributeName: 'one'}, {range: {one: {min: 5}, two: {max: 4}}}); + + const searchState = metadata.items[0].value({range: {one: {min: 5}, two: {max: 4}}}); + + expect(searchState).toEqual({range: {one: {}, two: {max: 4}}}); + }); + it('should return the right searchState when clean up', () => { let searchState = cleanUp({attributeName: 'name'}, { range: {name: 'searchState', name2: 'searchState'}, diff --git a/packages/react-instantsearch/src/connectors/connectRefinementList.js b/packages/react-instantsearch/src/connectors/connectRefinementList.js index 3a48fa6f4c..810d1bb209 100644 --- a/packages/react-instantsearch/src/connectors/connectRefinementList.js +++ b/packages/react-instantsearch/src/connectors/connectRefinementList.js @@ -185,7 +185,7 @@ export default createConnector({ currentRefinement: getCurrentRefinement(props, searchState), value: nextState => ({ ...nextState, - [namespace]: {[id]: ''}, + [namespace]: {...nextState[namespace], [id]: ''}, }), items: getCurrentRefinement(props, searchState).map(item => ({ label: `${item}`, @@ -196,7 +196,7 @@ export default createConnector({ return { ...nextState, - [namespace]: {[id]: nextSelectedItems.length > 0 ? nextSelectedItems : ''}, + [namespace]: {...nextState[namespace], [id]: nextSelectedItems.length > 0 ? nextSelectedItems : ''}, }; }, })), diff --git a/packages/react-instantsearch/src/connectors/connectRefinementList.test.js b/packages/react-instantsearch/src/connectors/connectRefinementList.test.js index d6bbbaa738..630a2c214d 100644 --- a/packages/react-instantsearch/src/connectors/connectRefinementList.test.js +++ b/packages/react-instantsearch/src/connectors/connectRefinementList.test.js @@ -207,11 +207,21 @@ describe('connectRefinementList', () => { }, ], }); + }); + + it('items value function should clear it from the search state', () => { + const metadata = getMetadata( + {attributeName: 'one'}, + {refinementList: {one: ['one1', 'one2'], two: ['two']}} + ); + + let searchState = metadata.items[0].items[0].value({refinementList: {one: ['one1', 'one2'], two: ['two']}}); + + expect(searchState).toEqual({refinementList: {one: ['one2'], two: ['two']}}); - let searchState = metadata.items[0].items[0].value({refinementList: {wot: ['wat', 'wut']}}); - expect(searchState).toEqual({refinementList: {wot: ['wut']}}); searchState = metadata.items[0].items[1].value(searchState); - expect(searchState).toEqual({refinementList: {wot: ''}}); + + expect(searchState).toEqual({refinementList: {one: '', two: ['two']}}); }); it('should return the right searchState when clean up', () => { diff --git a/packages/react-instantsearch/src/connectors/connectToggle.js b/packages/react-instantsearch/src/connectors/connectToggle.js index ade552fcff..6c8f3555b0 100644 --- a/packages/react-instantsearch/src/connectors/connectToggle.js +++ b/packages/react-instantsearch/src/connectors/connectToggle.js @@ -95,7 +95,7 @@ export default createConnector({ attributeName: props.attributeName, value: nextState => ({ ...nextState, - [namespace]: {[id]: false}, + [namespace]: {...nextState[namespace], [id]: false}, }), }); } diff --git a/packages/react-instantsearch/src/connectors/connectToggle.test.js b/packages/react-instantsearch/src/connectors/connectToggle.test.js index 888fda563b..ab7a991077 100644 --- a/packages/react-instantsearch/src/connectors/connectToggle.test.js +++ b/packages/react-instantsearch/src/connectors/connectToggle.test.js @@ -78,9 +78,14 @@ describe('connectToggle', () => { ], id: 't', }); + }); + + it('items value function should clear it from the search state', () => { + const metadata = getMetadata({attributeName: 'one', label: 'yep'}, {toggle: {one: true, two: false}}); + + const searchState = metadata.items[0].value({toggle: {one: true, two: false}}); - const searchState = metadata.items[0].value({toggle: {t: true}}); - expect(searchState).toEqual({toggle: {t: false}}); + expect(searchState).toEqual({toggle: {one: false, two: false}}); }); it('should return the right searchState when clean up', () => { diff --git a/packages/react-instantsearch/src/widgets/ClearAll.js b/packages/react-instantsearch/src/widgets/ClearAll.js index 9d88bc603a..7bc36e647f 100644 --- a/packages/react-instantsearch/src/widgets/ClearAll.js +++ b/packages/react-instantsearch/src/widgets/ClearAll.js @@ -6,6 +6,7 @@ import ClearAllComponent from '../components/ClearAll.js'; * to the search. * @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. * @themeKey ais-ClearAll__root - the widget button * @translationKey reset - the clear all button value * @example