diff --git a/dev/app/init-builtin-widgets.js b/dev/app/init-builtin-widgets.js index eb7f76e859..513aa95f38 100644 --- a/dev/app/init-builtin-widgets.js +++ b/dev/app/init-builtin-widgets.js @@ -5,6 +5,137 @@ import instantsearch from '../../index.js'; import wrapWithHits from './wrap-with-hits.js'; export default () => { + storiesOf('Breadcrumb') + .add( + 'default', + wrapWithHits(container => { + container.innerHTML = ` +
+ + `; + + window.search.addWidget( + instantsearch.widgets.breadcrumb({ + container: '#breadcrumb', + attributes: [ + 'hierarchicalCategories.lvl0', + 'hierarchicalCategories.lvl1', + 'hierarchicalCategories.lvl2', + ], + }) + ); + + //Custom Widget to toggle refinement + window.search.addWidget({ + init({ helper }) { + helper.toggleRefinement( + 'hierarchicalCategories.lvl0', + 'Cameras & Camcorders > Digital Cameras' + ); + }, + }); + }) + ) + .add( + 'with custom home label', + wrapWithHits(container => { + container.innerHTML = ` +
+ + `; + + window.search.addWidget( + instantsearch.widgets.breadcrumb({ + container: '#breadcrumb', + attributes: [ + 'hierarchicalCategories.lvl0', + 'hierarchicalCategories.lvl1', + 'hierarchicalCategories.lvl2', + ], + templates: { home: 'Home Page' }, + }) + ); + + //Custom Widget to toggle refinement + window.search.addWidget({ + init({ helper }) { + helper.toggleRefinement( + 'hierarchicalCategories.lvl0', + 'Cameras & Camcorders > Digital Cameras' + ); + }, + }); + }) + ) + .add( + 'with default selected item', + wrapWithHits(container => { + container.innerHTML = ` + +
+ `; + + window.search.addWidget( + instantsearch.widgets.breadcrumb({ + container: '#breadcrumb', + attributes: [ + 'hierarchicalCategories.lvl0', + 'hierarchicalCategories.lvl1', + 'hierarchicalCategories.lvl2', + ], + rootPath: 'Cameras & Camcorders > Digital Cameras', + }) + ); + + window.search.addWidget( + instantsearch.widgets.hierarchicalMenu({ + showParentLevel: false, + container: '#hierarchicalMenu', + attributes: [ + 'hierarchicalCategories.lvl0', + 'hierarchicalCategories.lvl1', + 'hierarchicalCategories.lvl2', + ], + rootPath: 'Cameras & Camcorders', + }) + ); + }) + ) + .add( + 'with hierarchical menu', + wrapWithHits(container => { + container.innerHTML = ` + +
+ `; + + window.search.addWidget( + instantsearch.widgets.breadcrumb({ + container: '#breadcrumb', + separator: ' / ', + attributes: [ + 'hierarchicalCategories.lvl0', + 'hierarchicalCategories.lvl1', + 'hierarchicalCategories.lvl2', + ], + }) + ); + + window.search.addWidget( + instantsearch.widgets.hierarchicalMenu({ + showParentLevel: false, + container: '#hierarchicalMenu', + attributes: [ + 'hierarchicalCategories.lvl0', + 'hierarchicalCategories.lvl1', + 'hierarchicalCategories.lvl2', + ], + rootPath: 'Cameras & Camcorders', + }) + ); + }) + ); + storiesOf('Analytics').add( 'default', wrapWithHits(container => { @@ -699,7 +830,22 @@ export default () => { 'hierarchicalCategories.lvl1', 'hierarchicalCategories.lvl2', ], - rootPath: 'Cameras & Camcorders', + }) + ); + }) + ) + .add( + 'only show current level', + wrapWithHits(container => { + window.search.addWidget( + instantsearch.widgets.hierarchicalMenu({ + container, + showParentLevel: false, + attributes: [ + 'hierarchicalCategories.lvl0', + 'hierarchicalCategories.lvl1', + 'hierarchicalCategories.lvl2', + ], }) ); }) diff --git a/src/components/Breadcrumb/Breadcrumb.js b/src/components/Breadcrumb/Breadcrumb.js new file mode 100644 index 0000000000..2a9d96856f --- /dev/null +++ b/src/components/Breadcrumb/Breadcrumb.js @@ -0,0 +1,82 @@ +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import Template from '../Template.js'; +import autoHideContainerHOC from '../../decorators/autoHideContainer.js'; + +const itemsPropType = PropTypes.arrayOf( + PropTypes.shape({ + name: PropTypes.string, + value: PropTypes.string, + }) +); + +class Breadcrumb extends PureComponent { + static propTypes = { + createURL: PropTypes.func, + cssClasses: PropTypes.objectOf(PropTypes.string), + items: itemsPropType, + refine: PropTypes.func.isRequired, + separator: PropTypes.oneOfType([PropTypes.string, PropTypes.element]), + templateProps: PropTypes.object.isRequired, + translate: PropTypes.func, + }; + + render() { + const { createURL, items, refine, cssClasses } = this.props; + + const breadcrumb = items.map((item, idx) => { + const isLast = idx === items.length - 1; + const label = isLast + ? + {item.name} + + : { + e.preventDefault(); + refine(item.value); + }} + > + {item.name} + ; + + return [ +