Skip to content

Commit e7e7677

Browse files
author
vvo
committed
feat(menu): add showMore option
much like the refinementList widget fixes #815
1 parent a020439 commit e7e7677

File tree

7 files changed

+99
-59
lines changed

7 files changed

+99
-59
lines changed

dev/app.js

+10-3
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,8 @@ search.addWidget(
137137
},
138138
showMore: {
139139
templates: {
140-
'active': '<button>Show less</button>',
141-
'inactive': '<button>Show more</button>'
140+
active: '<button>Show less</button>',
141+
inactive: '<button>Show more</button>'
142142
},
143143
limit: 10
144144
}
@@ -230,7 +230,14 @@ search.addWidget(
230230
instantsearch.widgets.menu({
231231
container: '#categories',
232232
attributeName: 'categories',
233-
limit: 10,
233+
limit: 3,
234+
showMore: {
235+
templates: {
236+
active: '<button>Show less</button>',
237+
inactive: '<button>Show more</button>'
238+
},
239+
limit: 10
240+
},
234241
cssClasses: {
235242
header: 'facet-title',
236243
link: 'facet-value',
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import templates from './defaultShowMoreTemplates.js';
2+
3+
const defaultShowMoreConfig = {
4+
templates,
5+
limit: 100
6+
};
7+
8+
export default function getShowMoreConfig(showMoreOptions) {
9+
if (!showMoreOptions) return null;
10+
11+
if (showMoreOptions === true) {
12+
return defaultShowMoreConfig;
13+
}
14+
15+
let config = {...showMoreOptions};
16+
if (!showMoreOptions.templates) {
17+
config.templates = defaultShowMoreConfig.templates;
18+
}
19+
if (!showMoreOptions.limit) {
20+
config.limit = defaultShowMoreConfig.limit;
21+
}
22+
return config;
23+
}

src/lib/utils.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import get from 'lodash/object/get';
55
import isEmpty from 'lodash/lang/isEmpty';
66
import keys from 'lodash/object/keys';
77
import uniq from 'lodash/array/uniq';
8+
import mapKeys from 'lodash/object/mapKeys';
89

910
let utils = {
1011
getContainerNode,
@@ -14,7 +15,8 @@ let utils = {
1415
isDomElement,
1516
getRefinements,
1617
clearRefinementsFromState,
17-
clearRefinementsAndSearch
18+
clearRefinementsAndSearch,
19+
prefixKeys
1820
};
1921

2022
/**
@@ -218,4 +220,12 @@ function clearRefinementsAndSearch(helper, attributeNames) {
218220
helper.setState(clearRefinementsFromState(helper.state, attributeNames)).search();
219221
}
220222

223+
function prefixKeys(prefix, obj) {
224+
if (obj) {
225+
return mapKeys(obj, function(v, k) {
226+
return prefix + k;
227+
});
228+
}
229+
}
230+
221231
export default utils;

src/widgets/hierarchical-menu/hierarchical-menu.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import defaultTemplates from './defaultTemplates.js';
1818
* @param {string} [options.separator=' > '] Separator used in the attributes to separate level values.
1919
* @param {string} [options.rootPath] Prefix path to use if the first level is not the root level.
2020
* @param {string} [options.showParentLevel=false] Show the parent level of the current refined value
21-
* @param {number} [options.limit=100] How much facet values to get
21+
* @param {number} [options.limit=10] How much facet values to get
2222
* @param {string[]|Function} [options.sortBy=['name:asc']] How to sort refinements. Possible values: `count|isRefined|name:asc|desc`
2323
* @param {Object} [options.templates] Templates to use for the widget
2424
* @param {string|Function} [options.templates.header=''] Header template (root level only)
@@ -46,7 +46,7 @@ hierarchicalMenu({
4646
[ separator=' > ' ],
4747
[ rootPath ],
4848
[ showParentLevel=true ],
49-
[ limit=1000 ],
49+
[ limit=10 ],
5050
[ sortBy=['name:asc'] ],
5151
[ cssClasses.{root , header, body, footer, list, depth, item, active, link}={} ],
5252
[ templates.{header, item, footer} ],
@@ -59,7 +59,7 @@ function hierarchicalMenu({
5959
separator = ' > ',
6060
rootPath = null,
6161
showParentLevel = true,
62-
limit = 1000,
62+
limit = 10,
6363
sortBy = ['name:asc'],
6464
cssClasses: userCssClasses = {},
6565
autoHideContainer = true,

src/widgets/menu/menu.js

+44-14
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ let bem = utils.bemHelper('ais-menu');
66
import cx from 'classnames';
77
import autoHideContainerHOC from '../../decorators/autoHideContainer.js';
88
import headerFooterHOC from '../../decorators/headerFooter.js';
9+
import getShowMoreConfig from '../../lib/show-more/getShowMoreConfig.js';
910

1011
import defaultTemplates from './defaultTemplates.js';
1112

@@ -15,7 +16,12 @@ import defaultTemplates from './defaultTemplates.js';
1516
* @param {string|DOMElement} options.container CSS Selector or DOMElement to insert the widget
1617
* @param {string} options.attributeName Name of the attribute for faceting
1718
* @param {string[]|Function} [options.sortBy=['count:desc']] How to sort refinements. Possible values: `count|isRefined|name:asc|desc`
18-
* @param {string} [options.limit=100] How many facets values to retrieve
19+
* @param {string} [options.limit=10] How many facets values to retrieve
20+
* @param {object|boolean} [options.showMore=false] Limit the number of results and display a showMore button
21+
* @param {object} [options.showMore.templates] Templates to use for showMore
22+
* @param {object} [options.showMore.templates.active] Template used when showMore was clicked
23+
* @param {object} [options.showMore.templates.inactive] Template used when showMore not clicked
24+
* @param {object} [options.showMore.limit] Max number of facets values to display when showMore is clicked
1925
* @param {Object} [options.templates] Templates to use for the widget
2026
* @param {string|Function} [options.templates.header=''] Header template
2127
* @param {string|Function} [options.templates.item] Item template, provided with `name`, `count`, `isRefined`, `url` data properties
@@ -39,22 +45,30 @@ menu({
3945
container,
4046
attributeName,
4147
[sortBy],
42-
[limit],
48+
[limit=10],
4349
[cssClasses.{root,list,item}],
4450
[templates.{header,item,footer}],
4551
[transformData],
4652
[autoHideContainer]
53+
[showMore.{templates: {active, inactive}, limit}]
4754
})`;
4855
function menu({
4956
container,
5057
attributeName,
5158
sortBy = ['count:desc'],
52-
limit = 100,
59+
limit = 10,
5360
cssClasses: userCssClasses = {},
5461
templates = defaultTemplates,
5562
transformData,
56-
autoHideContainer = true
63+
autoHideContainer = true,
64+
showMore = false
5765
} = {}) {
66+
let showMoreConfig = getShowMoreConfig(showMore);
67+
if (showMoreConfig && showMoreConfig.limit < limit) {
68+
throw new Error('showMore.limit configuration should be > than the limit in the main configuration'); // eslint-disable-line
69+
}
70+
let widgetMaxValuesPerFacet = (showMoreConfig && showMoreConfig.limit) || limit;
71+
5872
if (!container || !attributeName) {
5973
throw new Error(usage);
6074
}
@@ -69,22 +83,35 @@ function menu({
6983
// of hierarchicalFacet: a flat menu
7084
let hierarchicalFacetName = attributeName;
7185

86+
const showMoreTemplates = showMoreConfig && utils.prefixKeys('show-more-', showMoreConfig.templates);
87+
const allTemplates =
88+
showMoreTemplates ?
89+
{...templates, ...showMoreTemplates} :
90+
templates;
91+
7292
return {
73-
getConfiguration: () => ({
74-
hierarchicalFacets: [{
75-
name: hierarchicalFacetName,
76-
attributes: [attributeName]
77-
}]
78-
}),
93+
getConfiguration: configuration => {
94+
let widgetConfiguration = {
95+
hierarchicalFacets: [{
96+
name: hierarchicalFacetName,
97+
attributes: [attributeName]
98+
}]
99+
};
100+
101+
let currentMaxValuesPerFacet = configuration.maxValuesPerFacet || 0;
102+
widgetConfiguration.maxValuesPerFacet = Math.max(currentMaxValuesPerFacet, widgetMaxValuesPerFacet);
103+
104+
return widgetConfiguration;
105+
},
79106
render: function({results, helper, templatesConfig, state, createURL}) {
80-
let facetValues = getFacetValues(results, hierarchicalFacetName, sortBy, limit);
107+
let facetValues = getFacetValues(results, hierarchicalFacetName, sortBy);
81108
let hasNoFacetValues = facetValues.length === 0;
82109

83110
let templateProps = utils.prepareTemplateProps({
84111
transformData,
85112
defaultTemplates,
86113
templatesConfig,
87-
templates
114+
templates: allTemplates
88115
});
89116

90117
let cssClasses = {
@@ -104,7 +131,10 @@ function menu({
104131
createURL={(facetValue) => createURL(state.toggleRefinement(hierarchicalFacetName, facetValue))}
105132
cssClasses={cssClasses}
106133
facetValues={facetValues}
134+
limitMax={widgetMaxValuesPerFacet}
135+
limitMin={limit}
107136
shouldAutoHideContainer={hasNoFacetValues}
137+
showMore={showMoreConfig !== null}
108138
templateProps={templateProps}
109139
toggleRefinement={toggleRefinement.bind(null, helper, hierarchicalFacetName)}
110140
/>,
@@ -120,11 +150,11 @@ function toggleRefinement(helper, attributeName, facetValue) {
120150
.search();
121151
}
122152

123-
function getFacetValues(results, hierarchicalFacetName, sortBy, limit) {
153+
function getFacetValues(results, hierarchicalFacetName, sortBy) {
124154
let values = results
125155
.getFacetValues(hierarchicalFacetName, {sortBy: sortBy});
126156

127-
return values.data && values.data.slice(0, limit) || [];
157+
return values.data || [];
128158
}
129159

130160
export default menu;

src/widgets/refinement-list/refinement-list.js

+8-38
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
import React from 'react';
22
import ReactDOM from 'react-dom';
3-
import mapKeys from 'lodash/object/mapKeys';
43

54
import utils from '../../lib/utils.js';
65
let bem = utils.bemHelper('ais-refinement-list');
76
import cx from 'classnames';
87

98
import autoHideContainerHOC from '../../decorators/autoHideContainer.js';
109
import headerFooterHOC from '../../decorators/headerFooter.js';
10+
import getShowMoreConfig from '../../lib/show-more/getShowMoreConfig.js';
1111

1212
import defaultTemplates from './defaultTemplates.js';
13-
import defaultShowMoreTemplates from './defaultShowMoreTemplates.js';
1413

1514
/**
1615
* Instantiate a list of refinements based on a facet
@@ -20,11 +19,11 @@ import defaultShowMoreTemplates from './defaultShowMoreTemplates.js';
2019
* @param {string} [options.operator='or'] How to apply refinements. Possible values: `or`, `and`
2120
* @param {string[]|Function} [options.sortBy=['count:desc']] How to sort refinements. Possible values: `count|isRefined|name:asc|desc`
2221
* @param {string} [options.limit=10] How much facet values to get. When the show more feature is activated this is the minimun number of facets requested (the show more button is not in active state).
23-
* @param {object|boolean} [options.showMore] pass a configuration object, or true to use the default configuration
24-
* @param {object} [options.showMore.templates] templates to use
25-
* @param {object} [options.showMore.templates.active] template used when more facets are displayed
26-
* @param {object} [options.showMore.templates.inactive] template used when less facets are displayed
27-
* @param {object} [options.showMore.limit] the max number of facets values to display when the show more feature is active
22+
* @param {object|boolean} [options.showMore=false] Limit the number of results and display a showMore button
23+
* @param {object} [options.showMore.templates] Templates to use for showMore
24+
* @param {object} [options.showMore.templates.active] Template used when showMore was clicked
25+
* @param {object} [options.showMore.templates.inactive] Template used when showMore not clicked
26+
* @param {object} [options.showMore.limit] Max number of facets values to display when showMore is clicked
2827
* @param {Object} [options.templates] Templates to use for the widget
2928
* @param {string|Function} [options.templates.header] Header template
3029
* @param {string|Function} [options.templates.item] Item template, provided with `name`, `count`, `isRefined`, `url` data properties
@@ -67,7 +66,7 @@ function refinementList({
6766
templates = defaultTemplates,
6867
transformData,
6968
autoHideContainer = true,
70-
showMore
69+
showMore = false
7170
}) {
7271
let showMoreConfig = getShowMoreConfig(showMore);
7372
if (showMoreConfig && showMoreConfig.limit < limit) {
@@ -94,7 +93,7 @@ function refinementList({
9493
}
9594
}
9695

97-
const showMoreTemplates = showMoreConfig && prefixKeys('show-more-', showMoreConfig.templates);
96+
const showMoreTemplates = showMoreConfig && utils.prefixKeys('show-more-', showMoreConfig.templates);
9897
const allTemplates =
9998
showMoreTemplates ?
10099
{...templates, ...showMoreTemplates} :
@@ -161,33 +160,4 @@ function refinementList({
161160
};
162161
}
163162

164-
const defaultShowMoreConfig = {
165-
templates: defaultShowMoreTemplates,
166-
limit: 100
167-
};
168-
169-
function getShowMoreConfig(showMoreOptions) {
170-
if (showMoreOptions === true) {
171-
return defaultShowMoreConfig;
172-
} else if (showMoreOptions) {
173-
let config = {...showMoreOptions};
174-
if (!showMoreOptions.templates) {
175-
config.templates = defaultShowMoreConfig.templates;
176-
}
177-
if (!showMoreOptions.limit) {
178-
config.limit = defaultShowMoreConfig.limit;
179-
}
180-
return config;
181-
}
182-
return null;
183-
}
184-
185-
function prefixKeys(prefix, obj) {
186-
if (obj) {
187-
return mapKeys(obj, function(v, k) {
188-
return prefix + k;
189-
});
190-
}
191-
}
192-
193163
export default refinementList;

0 commit comments

Comments
 (0)