Skip to content

Commit

Permalink
Merge pull request #298 from algolia/feat/bem-toggle
Browse files Browse the repository at this point in the history
feat(toggle): Adding BEM class naming
  • Loading branch information
Vincent Voyer committed Oct 21, 2015
2 parents f9abc0c + 8730c97 commit 586e3c2
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 23 deletions.
56 changes: 51 additions & 5 deletions README.md
Expand Up @@ -621,15 +621,20 @@ Note that we are not toggling from `true` to `false` here, but from `true` to
* @param {String|DOMElement} options.container CSS Selector or DOMElement to insert the widget
* @param {String} options.facetName Name of the attribute for faceting (eg. "free_shipping")
* @param {String} options.label Human-readable name of the filter (eg. "Free Shipping")
* @param {Object} [options.cssClasses] CSS classes to add to the wrapping elements: root, list, item
* @param {Object} [options.cssClasses] CSS classes to add
* @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 the item 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.label] CSS class to add to each label element (when using the default template)
* @param {String|String[]} [options.cssClasses.checkbox] CSS class to add to each checkbox element (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='<label>
<input type="checkbox" {{#isRefined}}checked{{/isRefined}} />{{name}} <span>{{count}}</span>
</label>'] Item template
* @param {String|Function} [options.templates.item] Item template
* @param {String|Function} [options.templates.footer=''] Footer template
* @param {Function} [options.transformData] Function to change the object passed to the item template
* @param {boolean} [hideWhenNoResults=true] Hide the container when there's no results
Expand All @@ -656,6 +661,47 @@ search.addWidget(
);
```

#### Styling

```html
<div class="ais-toggle">
<div class="ais-toggle--header ais-header">[custom header template]</div>
<div class="ais-toggle--body">
<div class="ais-toggle--list">
<div class="ais-toggle--item">
<label class="ais-toggle--label">
<input type="checkbox" class="ais-toggle--checkbox" value="your_value"> Your value
<span class="ais-toggle--count">42</span>
</label>
</div>
</div>
</div>
<div class="ais-toggle--footer ais-footer">[custom footer template]</div>
</div>
```

```css
.ais-toggle {
}
.ais-toggle--header {
}
.ais-toggle--body {
}
.ais-toggle--list {
}
.ais-toggle--item {
}
.ais-toggle--item__active {
}
.ais-toggle--label {
}
.ais-toggle--checkbox {
}
.ais-toggle--count {
}
.ais-toggle--footer {
}
```
### refinementList

![Example of the refinementList widget][refinementList]
Expand Down
7 changes: 4 additions & 3 deletions example/app.js
Expand Up @@ -105,11 +105,12 @@ search.addWidget(
facetName: 'free_shipping',
label: 'Free Shipping',
cssClasses: {
header: 'panel-heading'
header: 'panel-heading',
item: 'panel-body',
count: 'badge pull-right'
},
templates: {
header: 'Shipping',
item: require('./templates/free-shipping.html')
header: 'Shipping'
}
})
);
Expand Down
3 changes: 2 additions & 1 deletion example/style.css
Expand Up @@ -76,6 +76,7 @@ body {
padding-left: 20px;
}

.ais-refinement-list--label {
.ais-refinement-list--label,
.ais-toggle--label {
display: block;
}
20 changes: 20 additions & 0 deletions themes/default/default.css
Expand Up @@ -115,6 +115,26 @@
}

/* TOGGLE */
.ais-toggle {
}
.ais-toggle--header {
}
.ais-toggle--body {
}
.ais-toggle--list {
}
.ais-toggle--item {
}
.ais-toggle--item__active {
}
.ais-toggle--label {
}
.ais-toggle--checkbox {
}
.ais-toggle--count {
}
.ais-toggle--footer {
}

/* HIERARCHICAL MENU */

Expand Down
19 changes: 14 additions & 5 deletions widgets/toggle/__tests__/toggle-test.js
Expand Up @@ -12,8 +12,6 @@ describe('toggle()', () => {
jsdom();

context('bad usage', () => {
var usage = 'Usage: toggle({container, facetName, label[, template, transformData]})';

it('throws when no container', () => {
expect(() => {
toggle();
Expand All @@ -23,13 +21,13 @@ describe('toggle()', () => {
it('throws when no facetName', () => {
expect(() => {
toggle({container: document.createElement('div')});
}).toThrow(usage);
}).toThrow(/Usage: /);
});

it('throws when no label', () => {
expect(() => {
toggle({container: document.createElement('div'), facetName: 'Hello'});
}).toThrow(usage);
}).toThrow(/Usage: /);
});
});

Expand Down Expand Up @@ -88,7 +86,18 @@ describe('toggle()', () => {
search: sinon.spy()
};
props = {
cssClasses: {},
cssClasses: {
root: 'ais-toggle',
header: 'ais-toggle--header',
body: 'ais-toggle--body',
footer: 'ais-toggle--footer',
list: 'ais-toggle--list',
item: 'ais-toggle--item',
active: 'ais-toggle--item__active',
label: 'ais-toggle--label',
checkbox: 'ais-toggle--checkbox',
count: 'ais-toggle--count'
},
hideWhenNoResults: true,
templateProps,
toggleRefinement: function() {},
Expand Down
5 changes: 3 additions & 2 deletions widgets/toggle/defaultTemplates.js
@@ -1,7 +1,8 @@
module.exports = {
header: '',
item: `<label>
<input type="checkbox" {{#isRefined}}checked{{/isRefined}} />{{name}} <span>{{count}}</span>
item: `<label class="{{cssClasses.label}}">
<input type="checkbox" class="{{cssClasses.checkbox}}" value="{{name}}" {{#isRefined}}checked{{/isRefined}} />{{name}}
<span class="{{cssClasses.count}}">{{count}}</span>
</label>`,
footer: ''
};
34 changes: 27 additions & 7 deletions widgets/toggle/toggle.js
Expand Up @@ -3,6 +3,8 @@ var React = require('react');
var ReactDOM = require('react-dom');

var utils = require('../../lib/utils.js');
var bem = utils.bemHelper('ais-toggle');
var cx = require('classnames/dedupe');

var autoHide = require('../../decorators/autoHide');
var headerFooter = require('../../decorators/headerFooter');
Expand All @@ -16,15 +18,20 @@ var defaultTemplates = require('./defaultTemplates');
* @param {String|DOMElement} options.container CSS Selector or DOMElement to insert the widget
* @param {String} options.facetName Name of the attribute for faceting (eg. "free_shipping")
* @param {String} options.label Human-readable name of the filter (eg. "Free Shipping")
* @param {Object} [options.cssClasses] CSS classes to add to the wrapping elements: root, list, item
* @param {Object} [options.cssClasses] CSS classes to add
* @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 the item 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.label] CSS class to add to each label element (when using the default template)
* @param {String|String[]} [options.cssClasses.checkbox] CSS class to add to each checkbox element (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='<label>
<input type="checkbox" {{#isRefined}}checked{{/isRefined}} />{{name}} <span>{{count}}</span>
</label>'] Item template
* @param {String|Function} [options.templates.item] Item template
* @param {String|Function} [options.templates.footer=''] Footer template
* @param {Function} [options.transformData] Function to change the object passed to the item template
* @param {boolean} [hideWhenNoResults=true] Hide the container when there's no results
Expand All @@ -41,9 +48,9 @@ function toggle({
} = {}) {
var RefinementList = autoHide(headerFooter(require('../../components/RefinementList')));
var containerNode = utils.getContainerNode(container);
var usage = 'Usage: toggle({container, facetName, label[, template, transformData]})';
var usage = 'Usage: toggle({container, facetName, label[, cssClasses.{root,header,body,footer,list,item,active,label,checkbox,count}, templates.{header,item,footer}, transformData, hideWhenNoResults]})';

if (container === undefined || facetName === undefined || label === undefined) {
if (!container || !facetName || !label) {
throw new Error(usage);
}

Expand All @@ -68,6 +75,19 @@ function toggle({
count: values && values.count || null
};

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),
label: cx(bem('label'), cssClasses.label),
checkbox: cx(bem('checkbox'), cssClasses.checkbox),
count: cx(bem('count'), cssClasses.count)
};

ReactDOM.render(
<RefinementList
createURL={() => createURL(state.toggleRefinement(facetName, facetValue.isRefined))}
Expand Down

0 comments on commit 586e3c2

Please sign in to comment.