From 467f49e4178c70e614eecc3b0557036b5caa7b2f Mon Sep 17 00:00:00 2001 From: Pixelastic Date: Tue, 20 Oct 2015 16:58:51 +0200 Subject: [PATCH] feat(menu): Add BEM classes You know the drill. - Updated BEM classes, including root, header, footer - Updated docs BREAKING CHANGE: The default template now has the count element inside the link, not outside. --- README.md | 70 +++++++++++++++++++++++++++++--- example/app.js | 3 +- index.js | 2 +- themes/default/default.css | 26 ++++++++++++ widgets/menu/defaultTemplates.js | 5 +++ widgets/{ => menu}/menu.js | 54 ++++++++++++++---------- 6 files changed, 132 insertions(+), 28 deletions(-) create mode 100644 widgets/menu/defaultTemplates.js rename widgets/{ => menu}/menu.js (63%) diff --git a/README.md b/README.md index 120b8b0ef7..dd05079176 100644 --- a/README.md +++ b/README.md @@ -707,6 +707,12 @@ search.addWidget( ``` ```css +.ais-refinement-list { +} +.ais-refinement-list--header { +} +.ais-refinement-list--body { +} .ais-refinement-list--list { } .ais-refinement-list--item { @@ -719,6 +725,8 @@ search.addWidget( } .ais-refinement-list--count { } +.ais-refinement-list--footer { +} ``` ### menu @@ -735,15 +743,21 @@ search.addWidget( * @param {String[]} [options.sortBy=['count:desc']] How to sort refinements. Possible values: `count|isRefined|name:asc|desc` * @param {String} [options.limit=100] How many facets values to retrieve * @param {Object} [options.cssClasses] CSS classes to add to the wrapping elements: root, list, item - * @param {String|String[]} [options.cssClasses.root] CSS class to be added to the wrapper element - * @param {String|String[]} [options.cssClasses.list] CSS class to be added to the list element - * @param {String|String[]} [options.cssClasses.item] CSS class to be added to each item of the list + * @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.link] CSS class to add to each link (when using the default template) + * @param {String|String[]} [options.cssClasses.count] CSS class to add to each count element (when using the default template) * @param {Object} [options.templates] Templates to use for the widget * @param {String|Function} [options.templates.header=''] Header template - * @param {String|Function} [options.templates.item='{{name}} {{count}}'] Item template, provided with `name`, `count`, `isRefined` + * @param {String|Function} [options.templates.item] Item template, provided with `name`, `count`, `isRefined` * @param {String|Function} [options.templates.footer=''] Footer template * @param {Function} [options.transformData] Method to change the object passed to the item template - * @param {boolean} [hideWhenNoResults=true] Hide the container when no results match + * @param {boolean} [hideWhenNoResults=true] Hide the container when there's no results * @return {Object} */ ``` @@ -764,6 +778,52 @@ search.addWidget( ); ``` +#### Styling + +```html +
+
[custom header template]
+ + +
+``` + +```css +.ais-menu { +} +.ais-menu--header { +} +.ais-menu--body { +} +.ais-menu--list { +} +.ais-menu--item { +} +.ais-menu--item__active { +} +.ais-menu--link { +} +.ais-menu--count { +} +.ais-menu--footer { +} +``` + ### rangeSlider ![Example of the rangeSlider widget][rangeSlider] diff --git a/example/app.js b/example/app.js index e11fe36922..8b3dd3f93f 100644 --- a/example/app.js +++ b/example/app.js @@ -121,7 +121,8 @@ search.addWidget( limit: 10, cssClasses: { header: 'panel-heading', - root: 'list-group' + root: 'list-group', + link: 'list-group-item' }, templates: { header: 'Categories', diff --git a/index.js b/index.js index 0a2e22c80b..20d998a73d 100644 --- a/index.js +++ b/index.js @@ -11,7 +11,7 @@ instantsearch.widgets = { hierarchicalMenu: require('./widgets/hierarchicalMenu'), hits: require('./widgets/hits/hits'), indexSelector: require('./widgets/index-selector/index-selector'), - menu: require('./widgets/menu'), + menu: require('./widgets/menu/menu.js'), refinementList: require('./widgets/refinement-list/refinement-list.js'), pagination: require('./widgets/pagination/pagination'), searchBox: require('./widgets/search-box'), diff --git a/themes/default/default.css b/themes/default/default.css index 7760d012eb..e5d5d98707 100644 --- a/themes/default/default.css +++ b/themes/default/default.css @@ -67,6 +67,12 @@ } /* REFINEMENT LIST */ +.ais-refinement-list { +} +.ais-refinement-list--header { +} +.ais-refinement-list--body { +} .ais-refinement-list--list { } .ais-refinement-list--item { @@ -79,8 +85,28 @@ } .ais-refinement-list--count { } +.ais-refinement-list--footer { +} /* MENU */ +.ais-menu { +} +.ais-menu--header { +} +.ais-menu--body { +} +.ais-menu--list { +} +.ais-menu--item { +} +.ais-menu--item__active { +} +.ais-menu--link { +} +.ais-menu--count { +} +.ais-menu--footer { +} /* TOGGLE */ diff --git a/widgets/menu/defaultTemplates.js b/widgets/menu/defaultTemplates.js new file mode 100644 index 0000000000..f568676e93 --- /dev/null +++ b/widgets/menu/defaultTemplates.js @@ -0,0 +1,5 @@ +module.exports = { + header: '', + item: `{{name}} {{count}}`, + footer: '' +}; diff --git a/widgets/menu.js b/widgets/menu/menu.js similarity index 63% rename from widgets/menu.js rename to widgets/menu/menu.js index e260f2f71a..fafc89c85c 100644 --- a/widgets/menu.js +++ b/widgets/menu/menu.js @@ -1,16 +1,14 @@ var React = require('react'); var ReactDOM = require('react-dom'); -var utils = require('../lib/utils.js'); -var autoHide = require('../decorators/autoHide'); -var headerFooter = require('../decorators/headerFooter'); -var RefinementList = autoHide(headerFooter(require('../components/RefinementList'))); +var utils = require('../../lib/utils.js'); +var bem = utils.bemHelper('ais-menu'); +var cx = require('classnames/dedupe'); +var autoHide = require('../../decorators/autoHide'); +var headerFooter = require('../../decorators/headerFooter'); +var RefinementList = autoHide(headerFooter(require('../../components/RefinementList'))); -var defaultTemplates = { - header: '', - item: '{{name}} {{count}}', - footer: '' -}; +var defaultTemplates = require('./defaultTemplates.js'); /** * Create a menu out of a facet @@ -19,27 +17,29 @@ var defaultTemplates = { * @param {String[]} [options.sortBy=['count:desc']] How to sort refinements. Possible values: `count|isRefined|name:asc|desc` * @param {String} [options.limit=100] How many facets values to retrieve * @param {Object} [options.cssClasses] CSS classes to add to the wrapping elements: root, list, item - * @param {String|String[]} [options.cssClasses.root] CSS class to be added to the wrapper element - * @param {String|String[]} [options.cssClasses.list] CSS class to be added to the list element - * @param {String|String[]} [options.cssClasses.item] CSS class to be added to each item of the list + * @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.link] CSS class to add to each link (when using the default template) + * @param {String|String[]} [options.cssClasses.count] CSS class to add to each count element (when using the default template) * @param {Object} [options.templates] Templates to use for the widget * @param {String|Function} [options.templates.header=''] Header template - * @param {String|Function} [options.templates.item='{{name}} {{count}}'] Item template, provided with `name`, `count`, `isRefined` + * @param {String|Function} [options.templates.item] Item template, provided with `name`, `count`, `isRefined` * @param {String|Function} [options.templates.footer=''] Footer template * @param {Function} [options.transformData] Method to change the object passed to the item template * @param {boolean} [hideWhenNoResults=true] Hide the container when there's no results * @return {Object} */ function menu({ - container = null, - facetName = null, + container, + facetName, sortBy = ['count:desc'], limit = 100, - cssClasses = { - root: null, - list: null, - item: null - }, + cssClasses = {}, templates = defaultTemplates, transformData, hideWhenNoResults = true @@ -47,7 +47,7 @@ function menu({ var containerNode = utils.getContainerNode(container); var usage = 'Usage: menu({container, facetName, [sortBy, limit, cssClasses.{root,list,item}, templates.{header,item,footer}, transformData, hideWhenResults]})'; - if (container === null || facetName === null) { + if (!container || !facetName) { throw new Error(usage); } @@ -72,6 +72,18 @@ function menu({ templates }); + cssClasses = { + root: cx(bem(null), cssClasses.root), + header: cx(bem('header'), cssClasses.header), + body: cx(bem('body'), cssClasses.body), + footer: cx(bem('footer'), cssClasses.footer), + list: cx(bem('list'), cssClasses.list), + item: cx(bem('item'), cssClasses.item), + active: cx(bem('item', 'active'), cssClasses.active), + link: cx(bem('link'), cssClasses.link), + count: cx(bem('count'), cssClasses.count) + }; + ReactDOM.render( createURL(state.toggleRefinement(hierarchicalFacetName, facetValue))}