Skip to content

Commit

Permalink
feat(autocomplete-js): enable HTML templating (#920)
Browse files Browse the repository at this point in the history
Co-authored-by: Haroen Viaene <hello@haroen.me>
  • Loading branch information
sarahdayan and Haroenv authored Apr 7, 2022
1 parent 4131bd6 commit f5bbf34
Show file tree
Hide file tree
Showing 22 changed files with 1,448 additions and 57 deletions.
2 changes: 1 addition & 1 deletion bundlesize.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
},
{
"path": "packages/autocomplete-js/dist/umd/index.production.js",
"maxSize": "16.75 kB"
"maxSize": "17.5 kB"
},
{
"path": "packages/autocomplete-preset-algolia/dist/umd/index.production.js",
Expand Down
34 changes: 34 additions & 0 deletions examples/html-templates/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Autocomplete with HTML templates example

This example shows how to use HTML templates in Autocomplete.

<p align="center"><img src="capture.png?raw=true" alt="A capture of the Autocomplete with HTML templates example" /></p>

## Demo

[Access the demo](https://codesandbox.io/s/github/algolia/autocomplete/tree/next/examples/html-templates)

## How to run this example locally

### 1. Clone this repository

```sh
git clone git@github.com:algolia/autocomplete.git
```

### 2. Install the dependencies and run the server

```sh
yarn
yarn workspace @algolia/autocomplete-example-html-templates start
```

Alternatively, you may use npm:

```sh
cd examples/html-templates
npm install
npm start
```

Open <http://localhost:1234> to see your app.
65 changes: 65 additions & 0 deletions examples/html-templates/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { autocomplete, getAlgoliaResults } from '@algolia/autocomplete-js';
import algoliasearch from 'algoliasearch/lite';

import '@algolia/autocomplete-theme-classic';

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

autocomplete({
container: '#autocomplete',
placeholder: 'Search',
getSources({ query }) {
return [
{
sourceId: 'products',
getItems() {
return getAlgoliaResults({
searchClient,
queries: [
{
indexName: 'instant_search',
query,
},
],
});
},
templates: {
item({ item, components, html }) {
return html`<div class="aa-ItemWrapper">
<div class="aa-ItemContent">
<div
class="aa-ItemIcon aa-ItemIcon--picture aa-ItemIcon--alignTop"
>
<img
src="${item.image}"
alt="${item.name}"
width="40"
height="40"
/>
</div>
<div class="aa-ItemContentBody">
<div class="aa-ItemContentTitle">
${components.Highlight({ hit: item, attribute: 'name' })}
</div>
<div class="aa-ItemContentDescription">
By <strong>${item.brand}</strong> in ${' '}
<strong>${item.categories[0]}</strong>
</div>
</div>
</div>
</div>`;
},
},
},
];
},
render({ children, render, html }, root) {
render(html`<div class="aa-SomeResults">${children}</div>`, root);
},
renderNoResults({ children, render, html }, root) {
render(html`<div class="aa-NoResults">${children}</div>`, root);
},
});
Binary file added examples/html-templates/capture.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions examples/html-templates/env.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Parcel picks the `source` field of the monorepo packages and thus doesn't
// apply the Babel config. We therefore need to manually override the constants
// in the app.
// See https://twitter.com/devongovett/status/1134231234605830144
global.__DEV__ = process.env.NODE_ENV !== 'production';
global.__TEST__ = false;
Binary file added examples/html-templates/favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions examples/html-templates/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<link rel="shortcut icon" href="favicon.png" type="image/x-icon" />
<link rel="stylesheet" href="style.css" />

<title>HTML Templates | Autocomplete</title>
</head>

<body>
<div class="container">
<div id="autocomplete"></div>
</div>

<script src="env.js"></script>
<script src="app.js"></script>
</body>
</html>
25 changes: 25 additions & 0 deletions examples/html-templates/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "@algolia/autocomplete-example-html-templates",
"description": "Autocomplete example with HTML templates",
"version": "1.5.7",
"private": true,
"license": "MIT",
"scripts": {
"build": "parcel build index.html",
"start": "parcel index.html"
},
"dependencies": {
"@algolia/autocomplete-js": "1.5.7",
"@algolia/autocomplete-theme-classic": "1.5.7",
"algoliasearch": "4.9.1"
},
"devDependencies": {
"@algolia/client-search": "4.9.1",
"parcel": "2.0.0-beta.2"
},
"keywords": [
"algolia",
"autocomplete",
"javascript"
]
}
20 changes: 20 additions & 0 deletions examples/html-templates/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
* {
box-sizing: border-box;
}

body {
background-color: rgb(244, 244, 249);
color: rgb(65, 65, 65);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
padding: 1rem;
}

.container {
margin: 0 auto;
max-width: 640px;
width: 100%;
}
3 changes: 2 additions & 1 deletion packages/autocomplete-js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
"@algolia/autocomplete-core": "1.5.7",
"@algolia/autocomplete-preset-algolia": "1.5.7",
"@algolia/autocomplete-shared": "1.5.7",
"preact": "^10.0.0"
"preact": "^10.0.0",
"htm": "^3.0.0"
},
"devDependencies": {
"@algolia/client-search": "4.9.1"
Expand Down
76 changes: 75 additions & 1 deletion packages/autocomplete-js/src/__tests__/api.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { createAutocomplete } from '@algolia/autocomplete-core';
import { waitFor } from '@testing-library/dom';
import { fireEvent, waitFor } from '@testing-library/dom';
import {
createElement as preactCreateElement,
render as preactRender,
} from 'preact';

import { createCollection } from '../../../../test/utils';
import { autocomplete } from '../autocomplete';
Expand Down Expand Up @@ -371,6 +375,76 @@ describe('api', () => {
document.querySelector<HTMLButtonElement>('.aa-SubmitButton')
).toHaveAttribute('title', 'Envoyer');
});

test('updates the renderer', async () => {
const container = document.createElement('div');
const panelContainer = document.createElement('div');
document.body.appendChild(container);
document.body.appendChild(panelContainer);

const CustomFragment = (props: any) => props.children;
const mockCreateElement1 = jest.fn(preactCreateElement);
const mockCreateElement2 = jest.fn(preactCreateElement);
const mockRender = jest.fn().mockImplementation(preactRender);

const { update } = autocomplete<{ label: string }>({
container,
panelContainer,
getSources() {
return [
{
sourceId: 'testSource',
getItems({ query }) {
return [{ label: query }];
},
templates: {
item({ item, html }) {
return html`<div>${item.label}</div>`;
},
},
},
];
},
renderer: {
Fragment: CustomFragment,
render: mockRender,
createElement: mockCreateElement1,
},
});

const input = container.querySelector<HTMLInputElement>('.aa-Input');

fireEvent.input(input, { target: { value: 'apple' } });

await waitFor(() => {
expect(
panelContainer.querySelector<HTMLElement>('.aa-Panel')
).toHaveTextContent('apple');
expect(mockCreateElement1).toHaveBeenCalled();
});

mockCreateElement1.mockClear();

update({
renderer: {
Fragment: CustomFragment,
render: mockRender,
createElement: mockCreateElement2,
},
});

fireEvent.input(input, { target: { value: 'iphone' } });

await waitFor(() => {
expect(
panelContainer.querySelector<HTMLElement>('.aa-Panel')
).toHaveTextContent('iphone');
// The `createElement` function was updated, so the previous
// implementation should no longer be called.
expect(mockCreateElement1).not.toHaveBeenCalled();
expect(mockCreateElement2).toHaveBeenCalled();
});
});
});

describe('destroy', () => {
Expand Down
Loading

0 comments on commit f5bbf34

Please sign in to comment.