Skip to content

Commit 7eeffe6

Browse files
feat(experiences): implement custom element (#6364)
* feat(experiences): implement custom element * build with wildcard * fix tests * only provide custom element
1 parent 1109091 commit 7eeffe6

File tree

11 files changed

+314
-105
lines changed

11 files changed

+314
-105
lines changed

examples/js/algolia-experiences/index.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,9 @@
6767
<body>
6868
<main>
6969
<h2>Buy historical books now!</h2>
70-
<div data-experience-id="category:historical"></div>
70+
<algolia-experience
71+
experience-id="category:historical"
72+
></algolia-experience>
7173
</main>
7274
</body>
7375
</html>

examples/js/algolia-experiences/local.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,9 @@
6767
<body>
6868
<main>
6969
<h2>Buy historical books now!</h2>
70-
<div data-experience-id="category:historical"></div>
70+
<algolia-experience
71+
experience-id="category:historical"
72+
></algolia-experience>
7173
</main>
7274
</body>
7375
</html>

examples/js/algolia-experiences/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
"version": "1.1.0",
44
"private": true,
55
"scripts": {
6-
"start": "BABEL_ENV=parcel parcel index.html local.html",
7-
"build": "BABEL_ENV=parcel parcel build index.html local.html --public-url .",
8-
"website:examples": "BABEL_ENV=parcel parcel build index.html local.html --public-url . --dist-dir=../../../website/examples/js/algolia-experiences"
6+
"start": "BABEL_ENV=parcel parcel *.html",
7+
"build": "BABEL_ENV=parcel parcel build *.html --public-url .",
8+
"website:examples": "BABEL_ENV=parcel parcel build *.html --public-url . --dist-dir=../../../website/examples/js/algolia-experiences"
99
},
1010
"browserslist": "firefox 68, chrome 78, IE 11",
1111
"devDependencies": {
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<title>Algolia Experiences demo</title>
6+
<link
7+
rel="stylesheet"
8+
href="/packages/instantsearch.css/themes/satellite-min.css"
9+
/>
10+
<style>
11+
body {
12+
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
13+
Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue',
14+
sans-serif;
15+
}
16+
main {
17+
max-width: 80em;
18+
margin: 0 auto;
19+
}
20+
.ais-Hits-list,
21+
.ais-TrendingItems-list {
22+
display: grid;
23+
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
24+
gap: 1em;
25+
}
26+
.ais-TrendingItems-item,
27+
.ais-Hits-item {
28+
padding: 0;
29+
}
30+
.ais-TrendingItems-item a,
31+
.ais-Hits-item a {
32+
width: 100%;
33+
padding: 1.5em;
34+
text-decoration: none;
35+
color: inherit;
36+
display: flex;
37+
flex-direction: column;
38+
}
39+
.ais-TrendingItems-item img,
40+
.ais-Hits-item img {
41+
width: 100%;
42+
height: 10em;
43+
border-radius: 4px;
44+
object-fit: contain;
45+
}
46+
.__flex {
47+
display: flex;
48+
gap: 1em;
49+
justify-content: space-between;
50+
}
51+
.__bold {
52+
font-weight: bold;
53+
}
54+
div:has(> .ais-Pagination) {
55+
display: flex;
56+
}
57+
.ais-Pagination {
58+
margin: 1em auto;
59+
}
60+
</style>
61+
<script src="/packages/algolia-experiences/dist/algolia-experiences.development.js"></script>
62+
<meta
63+
name="algolia-configuration"
64+
content='{"appId":"F4T6CUV2AH","apiKey":"72d500963987bc97ff899d350a00f7a8"}'
65+
/>
66+
<script>
67+
let isMounted = false;
68+
function toggle() {
69+
if (isMounted) {
70+
document.querySelector('algolia-experience').remove();
71+
isMounted = false;
72+
return;
73+
}
74+
const element = document.createElement('template');
75+
document.querySelector('main').appendChild(element);
76+
element.outerHTML = `
77+
<algolia-experience experience-id="category:historical" />
78+
`;
79+
isMounted = true;
80+
}
81+
</script>
82+
</head>
83+
<body>
84+
<main>
85+
<h2>Buy historical books now!</h2>
86+
<button onclick="toggle()">Toggle</button>
87+
</main>
88+
</body>
89+
</html>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { fetchConfiguration } from '../get-configuration';
2+
3+
describe('fetchConfiguration', () => {
4+
it('should fetch and cache the configuration', async () => {
5+
const settings = { appId: 'appId', apiKey: 'apiKey' };
6+
7+
// @ts-ignore
8+
global.fetch = jest.fn(() =>
9+
Promise.resolve({
10+
ok: true,
11+
json: () =>
12+
Promise.resolve({
13+
id: '1',
14+
name: 'name',
15+
indexName: 'indexName',
16+
blocks: [],
17+
createdAt: 'createdAt',
18+
updatedAt: 'updatedAt',
19+
}),
20+
})
21+
);
22+
23+
await fetchConfiguration('1', settings);
24+
25+
expect(global.fetch).toHaveBeenCalledTimes(1);
26+
27+
await fetchConfiguration('1', settings);
28+
29+
expect(global.fetch).toHaveBeenCalledTimes(1);
30+
});
31+
});

packages/algolia-experiences/src/__tests__/render.test.ts

Lines changed: 19 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -27,28 +27,15 @@ describe('configToIndex', () => {
2727
error.mockClear();
2828
});
2929

30-
it('errors if element not found', () => {
31-
const elements = new Map<string, HTMLElement>();
32-
const config = { id: 'foo', indexName: 'bar', name: 'Foo', blocks: [] };
33-
const result = configToIndex(config, elements);
34-
expect(result).toEqual([]);
35-
expect(error).toHaveBeenCalledTimes(1);
36-
expect(error).toHaveBeenCalledWith(
37-
'[Algolia Experiences] Element with id foo not found'
38-
);
39-
});
40-
4130
it('returns index with widgets', () => {
42-
const elements = new Map<string, HTMLElement>([
43-
['foo', document.createElement('div')],
44-
]);
31+
const element = document.createElement('div');
4532
const config: Configuration = {
4633
id: 'foo',
4734
indexName: 'bar',
4835
name: 'Foo',
4936
blocks: [{ type: 'ais.hits', parameters: {}, children: [] }],
5037
};
51-
const result = configToIndex(config, elements);
38+
const result = configToIndex(config, element);
5239
expect(result).toHaveLength(1);
5340
expect(result[0].getIndexName()).toEqual('bar');
5441
expect(result[0].getIndexId()).toEqual('foo');
@@ -57,9 +44,7 @@ describe('configToIndex', () => {
5744
});
5845

5946
it('maps to the right widget types', () => {
60-
const elements = new Map<string, HTMLElement>([
61-
['foo', document.createElement('div')],
62-
]);
47+
const element = document.createElement('div');
6348
const config: Configuration = {
6449
id: 'foo',
6550
indexName: 'bar',
@@ -121,7 +106,7 @@ describe('configToIndex', () => {
121106
},
122107
],
123108
};
124-
const result = configToIndex(config, elements);
109+
const result = configToIndex(config, element);
125110
expect(result).toHaveLength(1);
126111
expect(result[0].getWidgets()).toHaveLength(config.blocks.length);
127112
expect(result[0].getWidgets().map((w) => w.$$type)).toEqual([
@@ -167,9 +152,7 @@ describe('configToIndex', () => {
167152
),
168153
});
169154
const search = instantsearch({ searchClient });
170-
const elements = new Map<string, HTMLElement>([
171-
['foo', document.createElement('div')],
172-
]);
155+
const element = document.createElement('div');
173156
const config: Configuration = {
174157
id: 'foo',
175158
indexName: 'bar',
@@ -210,11 +193,11 @@ describe('configToIndex', () => {
210193
},
211194
],
212195
};
213-
const result = configToIndex(config, elements);
196+
const result = configToIndex(config, element);
214197
expect(result).toHaveLength(1);
215198
expect(result[0].getWidgets()).toHaveLength(1);
216199

217-
expect(elements.get('foo')?.innerHTML).toMatchInlineSnapshot(`
200+
expect(element.innerHTML).toMatchInlineSnapshot(`
218201
<div>
219202
</div>
220203
`);
@@ -223,7 +206,7 @@ describe('configToIndex', () => {
223206
search.start();
224207
await wait(100);
225208

226-
expect(elements.get('foo')?.innerHTML).toMatchInlineSnapshot(`
209+
expect(element.innerHTML).toMatchInlineSnapshot(`
227210
<div>
228211
<div class="ais-Hits">
229212
<ol class="ais-Hits-list">
@@ -276,9 +259,7 @@ describe('configToIndex', () => {
276259
),
277260
});
278261
const search = instantsearch({ searchClient });
279-
const elements = new Map<string, HTMLElement>([
280-
['foo', document.createElement('div')],
281-
]);
262+
const element = document.createElement('div');
282263

283264
const config: Configuration = {
284265
id: 'foo',
@@ -292,12 +273,12 @@ describe('configToIndex', () => {
292273
],
293274
};
294275

295-
const result = configToIndex(config, elements);
276+
const result = configToIndex(config, element);
296277

297278
expect(result).toHaveLength(1);
298279
expect(result[0].getWidgets()).toHaveLength(1);
299280

300-
expect(elements.get('foo')?.innerHTML).toMatchInlineSnapshot(`
281+
expect(element.innerHTML).toMatchInlineSnapshot(`
301282
<div>
302283
</div>
303284
`);
@@ -306,7 +287,7 @@ describe('configToIndex', () => {
306287
search.start();
307288
await wait(100);
308289

309-
expect(elements.get('foo')?.innerHTML).toMatchInlineSnapshot(`
290+
expect(element.innerHTML).toMatchInlineSnapshot(`
310291
<div>
311292
<div class="ais-Panel">
312293
<div class="ais-Panel-header">
@@ -351,9 +332,7 @@ describe('configToIndex', () => {
351332
),
352333
});
353334
const search = instantsearch({ searchClient });
354-
const elements = new Map<string, HTMLElement>([
355-
['foo', document.createElement('div')],
356-
]);
335+
const element = document.createElement('div');
357336

358337
const config: Configuration = {
359338
id: 'foo',
@@ -367,12 +346,12 @@ describe('configToIndex', () => {
367346
],
368347
};
369348

370-
const result = configToIndex(config, elements);
349+
const result = configToIndex(config, element);
371350

372351
expect(result).toHaveLength(1);
373352
expect(result[0].getWidgets()).toHaveLength(1);
374353

375-
expect(elements.get('foo')).toBeEmptyDOMElement();
354+
expect(element).toBeEmptyDOMElement();
376355

377356
search.addWidgets(result);
378357
search.start();
@@ -400,9 +379,7 @@ describe('configToIndex', () => {
400379
),
401380
});
402381
const search = instantsearch({ searchClient });
403-
const elements = new Map<string, HTMLElement>([
404-
['foo', document.createElement('div')],
405-
]);
382+
const element = document.createElement('div');
406383

407384
const config: Configuration = {
408385
id: 'foo',
@@ -416,12 +393,12 @@ describe('configToIndex', () => {
416393
],
417394
};
418395

419-
const result = configToIndex(config, elements);
396+
const result = configToIndex(config, element);
420397

421398
expect(result).toHaveLength(1);
422399
expect(result[0].getWidgets()).toHaveLength(1);
423400

424-
expect(elements.get('foo')?.innerHTML).toMatchInlineSnapshot(`
401+
expect(element?.innerHTML).toMatchInlineSnapshot(`
425402
<div>
426403
</div>
427404
`);
@@ -430,7 +407,7 @@ describe('configToIndex', () => {
430407
search.start();
431408
await wait(100);
432409

433-
expect(elements.get('foo')?.innerHTML).toMatchInlineSnapshot(`
410+
expect(element?.innerHTML).toMatchInlineSnapshot(`
434411
<div>
435412
<div class="ais-Pagination ais-Pagination--noRefinement">
436413
<ul class="ais-Pagination-list">

0 commit comments

Comments
 (0)