Skip to content

Commit

Permalink
feat(recommend): introduce relatedProducts widget (#6154)
Browse files Browse the repository at this point in the history
  • Loading branch information
sarahdayan authored and dhayab committed May 21, 2024
1 parent 82d3001 commit b8602af
Show file tree
Hide file tree
Showing 18 changed files with 1,011 additions and 8 deletions.
4 changes: 2 additions & 2 deletions bundlesize.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
},
{
"path": "./packages/instantsearch.js/dist/instantsearch.production.min.js",
"maxSize": "79.25 kB"
"maxSize": "80 kB"
},
{
"path": "./packages/instantsearch.js/dist/instantsearch.development.js",
"maxSize": "174 kB"
"maxSize": "174.75 kB"
},
{
"path": "packages/react-instantsearch-core/dist/umd/ReactInstantSearchCore.min.js",
Expand Down
4 changes: 2 additions & 2 deletions examples/js/getting-started/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"version": "1.3.0",
"private": true,
"scripts": {
"start": "BABEL_ENV=parcel parcel index.html --port 3000",
"build": "BABEL_ENV=parcel parcel build index.html",
"start": "BABEL_ENV=parcel parcel index.html products.html --port 3000",
"build": "BABEL_ENV=parcel parcel build index.html products.html",
"lint": "eslint .",
"lint:fix": "npm run lint -- --fix"
},
Expand Down
44 changes: 44 additions & 0 deletions examples/js/getting-started/products.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<meta name="theme-color" content="#000000" />

<link rel="shortcut icon" href="./favicon.png" />

<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/instantsearch.css@7/themes/satellite-min.css"
/>
<link rel="stylesheet" href="./src/index.css" />
<link rel="stylesheet" href="./src/app.css" />

<title>InstantSearch.js — Getting started</title>
</head>

<body>
<header class="header">
<h1 class="header-title">
<a href="/">Getting started</a>
</h1>
<p class="header-subtitle">
using
<a href="https://github.com/algolia/instantsearch">
InstantSearch.js
</a>
</p>
</header>

<div class="container">
<a href="/">← Back to search</a>
<div id="hits"></div>
<div id="related-products"></div>
</div>

<script type="module" src="./src/products.js"></script>
</body>
</html>
40 changes: 40 additions & 0 deletions examples/js/getting-started/src/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,43 @@
margin: 2rem auto;
text-align: center;
}

#related-products,
.ais-Hits--single {
margin-top: 1rem;
}

.ais-Hits--single article {
display: flex;
gap: 1rem;
}

.ais-Hits--single img {
width: 150px;
height: 150px;
object-fit: contain;
flex-shrink: 0;
}

.ais-RelatedProducts-list {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 1rem;
}

.ais-RelatedProducts-item {
align-items: start;
}

.ais-RelatedProducts-item img {
width: 100%;
height: 100px;
object-fit: contain;
}

.ais-RelatedProducts-item article {
display: flex;
flex-direction: column;
height: 100%;
justify-content: space-between;
}
7 changes: 6 additions & 1 deletion examples/js/getting-started/src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,13 @@ search.addWidgets([
templates: {
item: (hit, { html, components }) => html`
<article>
<h1>${components.Highlight({ hit, attribute: 'name' })}</h1>
<h1>
<a href="/products.html?pid=${hit.objectID}"
>${components.Highlight({ hit, attribute: 'name' })}</a
>
</h1>
<p>${components.Highlight({ hit, attribute: 'description' })}</p>
<a href="/products.html?pid=${hit.objectID}">See product</a>
</article>
`,
},
Expand Down
58 changes: 58 additions & 0 deletions examples/js/getting-started/src/products.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import algoliasearch from 'algoliasearch/lite';
import instantsearch from 'instantsearch.js';
import { configure, hits, relatedProducts } from 'instantsearch.js/es/widgets';

const searchParams = new URLSearchParams(document.location.search);

const pid = searchParams.get('pid');

const searchClient = algoliasearch(
'latency',
'6be0576ff61c053d5f9a3225e2a90f76'
);

const search = instantsearch({
indexName: 'instant_search',
searchClient,
insights: true,
});

search.addWidgets([
hits({
container: '#hits',
templates: {
item: (hit, { html, components }) => html`
<article>
<img src="${hit.image}" />
<div>
<h1>${components.Highlight({ hit, attribute: 'name' })}</h1>
<p>${components.Highlight({ hit, attribute: 'description' })}</p>
</div>
</article>
`,
},
cssClasses: { root: 'ais-Hits--single' },
}),
relatedProducts({
container: '#related-products',
objectIDs: [pid],
maxRecommendations: 4,
templates: {
item: (hit, { html }) => html`
<article>
<div>
<img src="${hit.image}" />
<h2>${hit.name}</h2>
</div>
<a href="/products.html?pid=${hit.objectID}">See product</a>
</article>
`,
},
}),
configure({
hitsPerPage: 1,
filters: `objectID:${pid}`,
}),
]);

search.start();
1 change: 1 addition & 0 deletions packages/instantsearch.css/src/themes/algolia.scss
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ a[class^='ais-'] {
.ais-RangeSlider,
.ais-RatingMenu,
.ais-RefinementList,
.ais-RelatedProducts,
.ais-SearchBox,
.ais-RelevantSort,
.ais-SortBy,
Expand Down
3 changes: 1 addition & 2 deletions packages/instantsearch.css/src/themes/satellite.scss
Original file line number Diff line number Diff line change
Expand Up @@ -605,8 +605,7 @@ $break-medium: 767px;
}

/**
* Hits and InfiniteHits
* FrequentlyBoughtTogether and RelatedProducts
* Hits, InfiniteHits, FrequentlyBoughtTogether and RelatedProducts
*/

.ais-Hits-item,
Expand Down
20 changes: 19 additions & 1 deletion packages/instantsearch.js/src/__tests__/common-widgets.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
stats,
ratingMenu,
numericMenu,
relatedProducts,
frequentlyBoughtTogether,
} from '../widgets';

Expand Down Expand Up @@ -480,6 +481,22 @@ const testSetups: TestSetupsMap<TestSuites> = {
})
.start();
},
createRelatedProductsWidgetTests({ instantSearchOptions, widgetParams }) {
instantsearch(instantSearchOptions)
.addWidgets([
relatedProducts({
container: document.body.appendChild(document.createElement('div')),
...widgetParams,
}),
])
.on('error', () => {
/*
* prevent rethrowing InstantSearch errors, so tests can be asserted.
* IRL this isn't needed, as the error doesn't stop execution.
*/
})
.start();
},
createFrequentlyBoughtTogetherTests({ instantSearchOptions, widgetParams }) {
instantsearch(instantSearchOptions)
.addWidgets([
Expand All @@ -489,7 +506,7 @@ const testSetups: TestSetupsMap<TestSuites> = {
}),
])
.on('error', () => {
/**
/*
* prevent rethrowing InstantSearch errors, so tests can be asserted.
* IRL this isn't needed, as the error doesn't stop execution.
*/
Expand Down Expand Up @@ -521,6 +538,7 @@ const testOptions: TestOptionsMap<TestSuites> = {
createSortByWidgetTests: undefined,
createStatsWidgetTests: undefined,
createNumericMenuWidgetTests: undefined,
createRelatedProductsWidgetTests: undefined,
createFrequentlyBoughtTogetherTests: undefined,
};

Expand Down
7 changes: 7 additions & 0 deletions packages/instantsearch.js/src/widgets/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,13 @@ function initiateAllWidgets(): Array<[WidgetNames, Widget | IndexWidget]> {
objectIDs: ['objectID'],
});
}
case 'relatedProducts': {
const relatedProducts = widget as Widgets['relatedProducts'];
return relatedProducts({
container,
objectIDs: ['objectID'],
});
}
default: {
const defaultWidget = widget as UnknownWidgetFactory;
return defaultWidget({ container, attribute: 'attr' });
Expand Down
1 change: 1 addition & 0 deletions packages/instantsearch.js/src/widgets/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export { default as places } from './places/places';
export { default as poweredBy } from './powered-by/powered-by';
export { default as queryRuleContext } from './query-rule-context/query-rule-context';
export { default as queryRuleCustomData } from './query-rule-custom-data/query-rule-custom-data';
export { default as relatedProducts } from './related-products/related-products';
export { default as rangeInput } from './range-input/range-input';
export { default as rangeSlider } from './range-slider/range-slider';
export { default as ratingMenu } from './rating-menu/rating-menu';
Expand Down

0 comments on commit b8602af

Please sign in to comment.