Skip to content

Commit

Permalink
feat(hits): add banner to instantsearch-js hits widget (#6158)
Browse files Browse the repository at this point in the history
* feat(hits): add banner to instantsearch-js hits widget

* fix: banner in renderState type

* fix(hits): some default templates should not be required

* chore(hits): bump instantsearch.js size threshold

* test(hits): update based on new template data prop value

* Update packages/instantsearch.js/src/connectors/hits/connectHits.ts

Co-authored-by: Haroen Viaene <hello@haroen.me>

---------

Co-authored-by: Aymeric Giraudet <aymeric.giraudet@algolia.com>
Co-authored-by: Haroen Viaene <hello@haroen.me>
  • Loading branch information
3 people committed Apr 26, 2024
1 parent 93cfd60 commit cfc9016
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 5 deletions.
11 changes: 11 additions & 0 deletions packages/instantsearch.js/src/connectors/hits/connectHits.ts
Expand Up @@ -25,6 +25,12 @@ const withUsage = createDocumentationMessageGenerator({
connector: true,
});

type Banner = NonNullable<
NonNullable<
Required<SearchResults<Hit>['renderingContent']>
>['widgets']['banners']
>[number];

export type HitsRenderState<THit extends BaseHit = BaseHit> = {
/**
* The matched hits from Algolia API.
Expand All @@ -36,6 +42,11 @@ export type HitsRenderState<THit extends BaseHit = BaseHit> = {
*/
results?: SearchResults<Hit<THit>>;

/**
* The banner to display above the hits.
*/
banner?: Banner;

/**
* Sends an event to the Insights middleware.
*/
Expand Down
67 changes: 64 additions & 3 deletions packages/instantsearch.js/src/widgets/hits/__tests__/hits.test.tsx
Expand Up @@ -20,6 +20,21 @@ import hits from '../hits';

import type { SearchResponse } from '../../../../src/types';

const bannerWidgetRenderingContent = {
widgets: {
banners: [
{
image: {
urls: [{ url: 'https://via.placeholder.com/550x250' }],
},
link: {
url: 'https://www.algolia.com',
},
},
],
},
};

beforeEach(() => {
document.body.innerHTML = '';
});
Expand Down Expand Up @@ -50,7 +65,11 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/hits/js/"

test('adds custom CSS classes', async () => {
const container = document.createElement('div');
const searchClient = createMockedSearchClient();
const searchClient = createMockedSearchClient({
// @TODO: remove once algoliasearch js client has been updated
// @ts-expect-error
renderingContent: bannerWidgetRenderingContent,
});

const search = instantsearch({
indexName: 'indexName',
Expand All @@ -73,6 +92,9 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/hits/js/"
emptyRoot: 'EMPTY_ROOT',
list: 'LIST',
item: 'ITEM',
bannerRoot: 'BANNER_ROOT',
bannerImage: 'BANNER_IMAGE',
bannerLink: 'BANNER_LINK',
},
}),
]);
Expand All @@ -84,6 +106,15 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/hits/js/"
expect(container.querySelector('.ais-Hits')).toHaveClass('ROOT');
expect(container.querySelector('.ais-Hits-list')).toHaveClass('LIST');
expect(container.querySelector('.ais-Hits-item')).toHaveClass('ITEM');
expect(container.querySelector('.ais-Hits-banner')).toHaveClass(
'BANNER_ROOT'
);
expect(container.querySelector('.ais-Hits-banner-image')).toHaveClass(
'BANNER_IMAGE'
);
expect(container.querySelector('.ais-Hits-banner-link')).toHaveClass(
'BANNER_LINK'
);
});

type CustomHit = { name: string; description: string };
Expand Down Expand Up @@ -276,7 +307,11 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/hits/js/"
test('renders with templates using `html`', async () => {
const container = document.createElement('div');
const searchBoxContainer = document.createElement('div');
const searchClient = createMockedSearchClient();
const searchClient = createMockedSearchClient({
// @TODO: remove once algoliasearch js client has been updated
// @ts-expect-error
renderingContent: bannerWidgetRenderingContent,
});

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

Expand All @@ -303,6 +338,11 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/hits/js/"
empty({ query }, { html }) {
return html`<p>No results for <q>${query}</q></p>`;
},
banner({ banner }, { html }) {
// @TODO: remove once algoliasearch js client has been updated
// @ts-expect-error
return html`<img src="${banner.image.urls[0].url}" />`;
},
},
}),
]);
Expand All @@ -316,6 +356,9 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/hits/js/"
<div
class="ais-Hits"
>
<img
src="https://via.placeholder.com/550x250"
/>
<ol
class="ais-Hits-list"
>
Expand Down Expand Up @@ -491,6 +534,9 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/hits/js/"
<div
class="ais-Hits ais-Hits--empty"
>
<img
src="https://via.placeholder.com/550x250"
/>
<p>
No results for
<q>
Expand All @@ -505,7 +551,11 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/hits/js/"
test('renders with templates using JSX', async () => {
const container = document.createElement('div');
const searchBoxContainer = document.createElement('div');
const searchClient = createMockedSearchClient();
const searchClient = createMockedSearchClient({
// @TODO: remove once algoliasearch js client has been updated
// @ts-expect-error
renderingContent: bannerWidgetRenderingContent,
});

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

Expand Down Expand Up @@ -542,6 +592,11 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/hits/js/"
</p>
);
},
banner({ banner }) {
// @TODO: remove once algoliasearch js client has been updated
// @ts-expect-error
return <img src={`${banner.image.urls[0].url}`} />;
},
},
}),
]);
Expand All @@ -555,6 +610,9 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/hits/js/"
<div
class="ais-Hits"
>
<img
src="https://via.placeholder.com/550x250"
/>
<ol
class="ais-Hits-list"
>
Expand Down Expand Up @@ -730,6 +788,9 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/hits/js/"
<div
class="ais-Hits ais-Hits--empty"
>
<img
src="https://via.placeholder.com/550x250"
/>
<p>
No results for
<q>
Expand Down
Expand Up @@ -2,7 +2,7 @@ import { omit } from '../../lib/utils';

import type { HitsTemplates } from './hits';

const defaultTemplates: Required<HitsTemplates> = {
const defaultTemplates: HitsTemplates = {
empty() {
return 'No results';
},
Expand Down
24 changes: 23 additions & 1 deletion packages/instantsearch.js/src/widgets/hits/hits.tsx
Expand Up @@ -49,7 +49,7 @@ const renderer =
containerNode: HTMLElement;
cssClasses: HitsCSSClasses;
renderState: {
templateProps?: PreparedTemplateProps<Required<HitsTemplates>>;
templateProps?: PreparedTemplateProps<HitsTemplates>;
};
templates: HitsTemplates;
}): Renderer<HitsRenderState, Partial<HitsWidgetParams>> =>
Expand All @@ -61,6 +61,7 @@ const renderer =
insights,
bindEvent,
sendEvent,
banner,
},
isFirstRendering
) => {
Expand Down Expand Up @@ -127,13 +128,26 @@ const renderer =
/>
);

const bannerComponent: HitsUiComponentProps<Hit>['bannerComponent'] = (
props
) => (
<TemplateComponent
{...renderState.templateProps}
templateKey="banner"
data={props}
rootTagName="fragment"
/>
);

render(
<Hits
hits={receivedHits}
itemComponent={itemComponent}
sendEvent={sendEvent}
classNames={cssClasses}
emptyComponent={emptyComponent}
banner={banner}
bannerComponent={templates.banner ? bannerComponent : undefined}
/>,
containerNode
);
Expand All @@ -160,6 +174,14 @@ export type HitsTemplates = Partial<{
__hitIndex: number;
}
>;

/**
* Template to use for the banner.
*/
banner: Template<{
banner: Required<HitsRenderState['banner']>;
className: string;
}>;
}>;

export type HitsWidgetParams = {
Expand Down

0 comments on commit cfc9016

Please sign in to comment.