Skip to content

Commit

Permalink
feat: newArrival attribute (#207)
Browse files Browse the repository at this point in the history
Today, we have a `newArrivalsCategory` attribute which contains hierarchical facets.
But displaying two hierarchical facets on the UI can create confusion
and duplicates the information.

This PR adds a `newArrival` boolean attribute and a `toggleRefinement`
widget which permits to show a simple refinement checkbox similar to SFRA
default "New Arrival" checkbox.

### Changes

- Refactored the `productModelCustomizer` to also handle the `newArrival` attribute
- Added a `toggleRefinement` widget on the UI, and the CSS to have the same look and feel than SFRA
- Also added to SiteGenesis with a simpler `labelText` and no custom CSS

---
SFCC-406
SFCC-407
  • Loading branch information
sbellone authored Dec 5, 2024
1 parent 11f380a commit c36808c
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -93,29 +93,45 @@ function createAlgoliaLocalizedCategoryObject(category) {
return result;
}

var NEW_ARRIVALS_CATEGORY_ID = 'newarrivals';
var specialValueHandlers = {
newArrivalsCategory: function(productModel) {
if (!empty(productModel.categories)) {
for (var i = 0; i < productModel.categories.length; i += 1) {
var topLevelCategoryId = productModel.categories[i][productModel.categories[i].length - 1].id;
if (topLevelCategoryId === NEW_ARRIVALS_CATEGORY_ID) {
return createAlgoliaLocalizedCategoryObject(productModel.categories[i]);
}
}
}
return null;
},
newArrival: function(productModel) {
if (!empty(productModel.categories)) {
for (var i = 0; i < productModel.categories.length; i += 1) {
var topLevelCategoryId = productModel.categories[i][productModel.categories[i].length - 1].id;
if (topLevelCategoryId === NEW_ARRIVALS_CATEGORY_ID) {
return true;
}
}
}
return false;
}
}

/**
* Customize a Localized Algolia Product.
* Add extra properties to the product model.
* @param {Object} productModel - Algolia product model
* @param {Array} algoliaAttributes - The attributes to index
*/
function customizeLocalizedProductModel(productModel, algoliaAttributes) {
var CATEGORY_ATTRIBUTE = 'newArrivalsCategory';
var CATEGORY_ID = 'newarrivals';

if (algoliaAttributes.indexOf(CATEGORY_ATTRIBUTE) >= 0) {
productModel[CATEGORY_ATTRIBUTE] = null;

if (!empty(productModel.categories)) {
for (var i = 0; i < productModel.categories.length; i += 1) {
var rootCategoryId = productModel.categories[i][productModel.categories[i].length - 1].id;
if (rootCategoryId === CATEGORY_ID) {
productModel[CATEGORY_ATTRIBUTE] = createAlgoliaLocalizedCategoryObject(productModel.categories[i]);
break;
}
}
var specialAttributes = ['newArrival', 'newArrivalsCategory'];
specialAttributes.forEach(function(attributeName) {
if (algoliaAttributes.indexOf(attributeName) >= 0 && specialValueHandlers[attributeName]) {
productModel[attributeName] = specialValueHandlers[attributeName](productModel);
}
}
})
}

module.exports = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,17 @@ function enableInstantSearch(config) {
panelTitle: algoliaData.strings.newArrivals
}),

toggleRefinementWithPanel({
container: '#algolia-newarrival-placeholder',
attribute: 'newArrival',
templates: {
labelText(data, { html }) {
return html`<span> ${algoliaData.strings.newArrivals}</span>`;
},
},
panelTitle: algoliaData.strings.newArrivals,
}),

refinementListWithPanel({
container: '#algolia-brand-list-placeholder',
attribute: 'brand',
Expand Down Expand Up @@ -330,6 +341,15 @@ function enableInstantSearch(config) {
return withPanel(options.attribute, options.panelTitle)(instantsearch.widgets.refinementList)(options)
}

/**
* Builds a refinement toggle with the Panel widget
* @param {Object} options Options object
* @returns {Object} The Panel widget
*/
function toggleRefinementWithPanel(options) {
return withPanel(options.attribute, options.panelTitle)(instantsearch.widgets.toggleRefinement)(options)
}

/**
* Builds a range input with the Panel widget
* @param {Object} options Options object
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<div class="refinements">
<div id="algolia-sort-by-placeholder" class="col-6 col-sm-3 order-sm-1"></div>
<div id="algolia-categories-list-placeholder"></div>
<div id="algolia-newarrival-placeholder"></div>
<div id="algolia-newarrivals-list-placeholder"></div>
<div id="algolia-brand-list-placeholder"></div>
<div id="algolia-size-list-placeholder"></div>
Expand All @@ -16,4 +17,3 @@
<div id="algolia-searchbox-placeholder" ></div>
</div>
</div>

Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@
padding: 0 1em;
}

.ais-ToggleRefinement-checkbox {
position: absolute;
opacity: 0;
height: 0;
width: 0;
}
.ais-ToggleRefinement-label {
cursor: pointer;
}

.auc-Recommend-item .col-12 {
padding: 0;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,22 @@ function enableInstantSearch(config) {
panelTitle: algoliaData.strings.newArrivals
}),

toggleRefinementWithPanel({
container: '#algolia-newarrival-placeholder',
attribute: 'newArrival',
templates: {
labelText(data, { html }) {
return html`
<a style="white-space: nowrap; ${data.isRefined ? 'font-weight: bold;' : ''}">
<i class="fa ${data.isRefined ? 'fa-check-square' : 'fa-square-o'}"></i>
<span> ${algoliaData.strings.newArrivals}</span>
</a>
`;
},
},
panelTitle: algoliaData.strings.newArrivals
}),

refinementListWithPanel({
container: '#algolia-brand-list-placeholder',
attribute: 'brand',
Expand Down Expand Up @@ -303,7 +319,7 @@ function enableInstantSearch(config) {
</div>
</div>
`;
},
},
},
transformItems: function (items, { results }) {
displaySwatches = false;
Expand Down Expand Up @@ -556,6 +572,15 @@ function enableInstantSearch(config) {
return withPanel(options.attribute, options.panelTitle)(instantsearch.widgets.refinementList)(options)
}

/**
* Builds a refinement toggle with the Panel widget
* @param {Object} options Options object
* @returns {Object} The Panel widget
*/
function toggleRefinementWithPanel(options) {
return withPanel(options.attribute, options.panelTitle)(instantsearch.widgets.toggleRefinement)(options)
}

/**
* Builds a range input with the Panel widget
* @param {Object} options Options object
Expand Down Expand Up @@ -663,7 +688,7 @@ function fetchPromoPrices(productIDs) {

// Filter out already fetched product IDs
const unfetchedProductIDs = productIDs.filter(id => !fetchedPrices.has(id));

if (unfetchedProductIDs.length === 0) return Promise.resolve();

return $.ajax({
Expand Down Expand Up @@ -796,4 +821,4 @@ function calculateDisplayPrice(item) {
price: item.price,
calloutMsg: '',
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@

<div class="refinements">
<div id="algolia-categories-list-placeholder"></div>
<div id="algolia-newarrival-placeholder"></div>
<div id="algolia-newarrivals-list-placeholder"></div>
<div id="algolia-brand-list-placeholder"></div>
<div id="algolia-size-list-placeholder"></div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,10 @@ describe('customizeLocalizedProductModel (jobs v2)', () => {
productModelCustomizer.customizeLocalizedProductModel(product, []);
expect(product).not.toHaveProperty('newArrivalsCategory');
});

test('newArrival', () => {
productModelCustomizer.customizeLocalizedProductModel(product, ['newArrival']);
expect(product).toHaveProperty('newArrival');
expect(product.newArrival).toBe(true);
});
});

0 comments on commit c36808c

Please sign in to comment.