Skip to content

Commit

Permalink
Merge branch 'feat/instantsearch.js/v2' of github.com:algolia/instant…
Browse files Browse the repository at this point in the history
…search.js into feat/instantsearch.js/v2
  • Loading branch information
Alexandre Stanislawski committed Apr 6, 2017
2 parents 3bde99c + d862fe0 commit 4da1880
Show file tree
Hide file tree
Showing 7 changed files with 341 additions and 21 deletions.
62 changes: 46 additions & 16 deletions dev/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ import allItems from './templates/all-items.html';
import empty from './templates/no-results.html';
import item from './templates/item.html';

import customMenuWidget from './customWidgets/menu.js';
import customClearAll from './customWidgets/clearAll.js';
import customCurrentRefinedValues from './customWidgets/currentRefinedValues.js';
import customHierarchicalMenu from './customWidgets/hierarchicalMenu.js';
import customPagination from './customWidgets/pagination.js';

const search = instantsearch({
appId: 'latency',
apiKey: '6be0576ff61c053d5f9a3225e2a90f76',
Expand Down Expand Up @@ -147,6 +153,13 @@ search.addWidget(
})
);

search.addWidget(
customPagination({
containerNode: $('#custom-pagination'),
maxPages: 20,
})
);

search.addWidget(
instantsearch.widgets.clearAll({
container: '#clear-all',
Expand Down Expand Up @@ -384,6 +397,23 @@ search.addWidget(customMenuWidget({
limit: 3,
}));

search.addWidget(customClearAll({
containerNode: $('#custom-clear-all'),
}));

search.addWidget(customCurrentRefinedValues({
containerNode: $('#custom-current-refined-values'),
}));

search.addWidget(customHierarchicalMenu({
containerNode: $('#custom-hierarchical-menu'),
attributes: [
'hierarchicalCategories.lvl0',
'hierarchicalCategories.lvl1',
'hierarchicalCategories.lvl2',
],
}));

search.addWidget(
instantsearch.widgets.rangeSlider({
container: '#price',
Expand All @@ -404,22 +434,22 @@ search.addWidget(
})
);

// search.addWidget(
// instantsearch.widgets.hierarchicalMenu({
// container: '#hierarchical-categories',
// attributes: ['hierarchicalCategories.lvl0', 'hierarchicalCategories.lvl1', 'hierarchicalCategories.lvl2'],
// cssClasses: {
// header: 'facet-title',
// list: 'hierarchical-categories-list',
// link: 'facet-value',
// count: 'facet-count pull-right',
// },
// rootPath: 'Cameras & Camcorders',
// templates: {
// header: 'Hierarchical categories',
// },
// })
// );
search.addWidget(
instantsearch.widgets.hierarchicalMenu({
container: '#hierarchical-categories',
attributes: ['hierarchicalCategories.lvl0', 'hierarchicalCategories.lvl1', 'hierarchicalCategories.lvl2'],
cssClasses: {
header: 'facet-title',
list: 'hierarchical-categories-list',
link: 'facet-value',
count: 'facet-count pull-right',
},
rootPath: 'Cameras & Camcorders',
templates: {
header: 'Hierarchical categories',
},
})
);

search.addWidget(
instantsearch.widgets.priceRanges({
Expand Down
26 changes: 26 additions & 0 deletions dev/customWidgets/clearAll.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/* eslint-disable import/default */
import instantsearch from '../../index.js';

const renderFn = ({
clearAll,
hasRefinements,
createURL,
widgetParams: {containerNode},
}, isFirstRendering) => {
if (isFirstRendering) {
const markup = window.$('<button id="custom-clear-all">Clear All</button>');
containerNode.append(markup);
}

const clearAllCTA = containerNode.find('#custom-clear-all');

// bind click event
clearAllCTA
.off('click')
.on('click', e => hasRefinements ? clearAll() : e.preventDefault());

// disable button
clearAllCTA.attr('disabled', !hasRefinements);
};

export default instantsearch.connectors.connectClearAll(renderFn);
117 changes: 117 additions & 0 deletions dev/customWidgets/currentRefinedValues.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/* eslint-disable import/default */
import instantsearch from '../../index.js';

const renderFn = ({
clearAllClick,
clearAllURL,
clearRefinementURLs,
clearRefinementClicks,
refinements,
widgetParams: {containerNode},
}, isFirstRendering) => {
// append initial markup on first rendering
// ----------------------------------------
if (isFirstRendering) {
const markup = window.$(`
<div class="facet-title">Custom current refinements</div>
<div id="custom-crv-clear-all-container"></div>
<ul style="list-style-type: none; margin: 0; padding: 0;"></ul>
`);
containerNode.append(markup);
}

if (refinements && refinements.length > 0) {
// append clear all link
// ---------------------
containerNode
.find('#custom-crv-clear-all-container')
.html(`
<a
href="${clearAllURL}"
class="ais-current-refined-values--clear-all"
>
Clear all
</a>
`);

containerNode
.find('#custom-crv-clear-all-container > a')
.off('click')
.on('click', e => {
e.preventDefault();
clearAllClick();
});

// show current refined values
// ---------------------------
const list = refinements
.map(value => {
if (value.hasOwnProperty('operator') && typeof value.operator === 'string') {
let displayedOperator = value.operator;
if (value.operator === '>=') displayedOperator = '&ge;';
if (value.operator === '<=') displayedOperator = '&le;';
value.name = `${displayedOperator} ${value.name}`;
}

return value;
})
.map(value => {
const {name, count} = value;

const afterCount = count ?
`<span class="pull-right facet-count">${count}</span>`
: '';

switch (true) {
case value.attributeName === 'price_range':
return `Price range: ${name.replace(/(\d+)/g, '$$$1')} ${afterCount}`;

case value.attributeName === 'price':
return `Price: ${name.replace(/(\d+)/g, '$$$1')}`;

case value.attributeName === 'free_shipping':
return name === 'true' ? `Free shipping ${afterCount}` : '';

default:
return `${name} ${afterCount}`;
}
})
.map((content, index) => `
<li>
<a
href="${clearRefinementURLs[index]}"
class="facet-value facet-value-removable clearfix"
>
${content}
</a>
</li>
`);

containerNode.find('ul').html(list.join(''));

// bind click events on links
// --------------------------
containerNode
.find('li > a')
.each(function(index) {
window.$(this)
.off('click')
.on('click', e => {
e.preventDefault();
clearRefinementClicks[index]();
});
});

// show container
// --------------
containerNode.find('#custom-current-refined-values').show();
} else {
// remove refinements list and clear all button, hide the container
// ----------------------------------------------------------------
containerNode.find('ul').html('');
containerNode.find('#custom-crv-clear-all-container').html('');
containerNode.find('#custom-current-refined-values').hide();
}
};

export default instantsearch.connectors.connectCurrentRefinedValues(renderFn);
90 changes: 90 additions & 0 deletions dev/customWidgets/hierarchicalMenu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/* eslint-disable import/default */
import instantsearch from '../../index.js';

const formatMenuEntry = (createURL, lvl = 0) => item => {
const countHTML = `
<span class="pull-right facet-count">
${item.count}
</span>
`;

if (
item.isRefined === true
&& Array.isArray(item.data)
&& item.data.length > 0
) {
return `
<div ${lvl === 0 ? 'class="hierarchical-categories-list"' : ''}>
<a
href="${createURL(item)}"
class="facet-value clearfix"
data-refine-path="${item.path}"
>
<strong>${item.name}</strong> ${countHTML}
</a>
<div class="hierarchical-categories-list ais-hierarchical-menu--list__lvl${lvl + 1}">
${item.data.map(formatMenuEntry(createURL, lvl + 1)).join('')}
</div>
</div>
`;
}

return `
<div>
<a
href="${createURL(item)}"
class="facet-value clearfix"
data-refine-path="${item.path}"
>
${item.isRefined
? `<strong>${item.name}</strong>`
: item.name} ${countHTML}
</a>
</div>
`;
};

const renderFn = ({
createURL,
items,
refine,
widgetParams: {containerNode},
currentRefinement,
}, isFirstRendering) => {
if (isFirstRendering) {
const markup = window.$(`
<div class="facet-title">Custom hierarchical</div>
<div id="custom-hierarchical-menu__container"></div>
`);
containerNode.append(markup);
}

// remove event listeners before replacing markup
containerNode
.find('a[data-refine-path]')
.each(function() { window.$(this).off('click'); });

if (items && items.length > 0) {
// replace markup with items
const menuItems = items
.map(item => item.isRefined ? {...item, data: currentRefinement.data} : item)
.map(formatMenuEntry(createURL))
.join('');

containerNode
.find('#custom-hierarchical-menu__container')
.html(menuItems);

// bind links with `data-refine-path`
containerNode
.find('a[data-refine-path]')
.each(function() {
window.$(this).on('click', e => {
e.preventDefault();
refine(window.$(this).data('refine-path'));
});
});
}
};

export default instantsearch.connectors.connectHierarchicalMenu(renderFn);
53 changes: 53 additions & 0 deletions dev/customWidgets/pagination.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/* eslint-disable import/default */
import instantsearch from '../../index.js';

const renderFn = ({
nbPages,
createURL,
refine,
currentRefinement,
widgetParams: {containerNode},
}, isFirstRendering) => {
if (isFirstRendering) {
const markup = `
<div class="facet-title">Custom pagination</div>
<ul class="pagination"></ul>
`;
containerNode.append(markup);
}

// remove event listeners before replacing markup
containerNode
.find('a[data-page]')
.each(function() { window.$(this).off('click'); });

if (nbPages > 0) {
const pages = Array(...{length: nbPages})
.map(Number.call, Number)
.map(page => `
<li ${page === currentRefinement ? 'class="active"' : ''}>
<a
href="${createURL(page)}"
data-page=${page}
>
${page + 1}
</a>
</li>
`);

containerNode
.find('ul.pagination')
.html(pages.join(''));

containerNode
.find('a[data-page]')
.each(function() {
window.$(this).on('click', e => {
e.preventDefault();
refine(window.$(this).data('page'));
});
});
}
};

export default instantsearch.connectors.connectPagination(renderFn);
Loading

0 comments on commit 4da1880

Please sign in to comment.