Skip to content

Commit

Permalink
feat(experiences): implement custom element (#6364)
Browse files Browse the repository at this point in the history
* feat(experiences): implement custom element

* build with wildcard

* fix tests

* only provide custom element
  • Loading branch information
aymeric-giraudet authored Sep 19, 2024
1 parent 1109091 commit 7eeffe6
Show file tree
Hide file tree
Showing 11 changed files with 314 additions and 105 deletions.
4 changes: 3 additions & 1 deletion examples/js/algolia-experiences/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@
<body>
<main>
<h2>Buy historical books now!</h2>
<div data-experience-id="category:historical"></div>
<algolia-experience
experience-id="category:historical"
></algolia-experience>
</main>
</body>
</html>
4 changes: 3 additions & 1 deletion examples/js/algolia-experiences/local.html
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@
<body>
<main>
<h2>Buy historical books now!</h2>
<div data-experience-id="category:historical"></div>
<algolia-experience
experience-id="category:historical"
></algolia-experience>
</main>
</body>
</html>
6 changes: 3 additions & 3 deletions examples/js/algolia-experiences/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
"version": "1.1.0",
"private": true,
"scripts": {
"start": "BABEL_ENV=parcel parcel index.html local.html",
"build": "BABEL_ENV=parcel parcel build index.html local.html --public-url .",
"website:examples": "BABEL_ENV=parcel parcel build index.html local.html --public-url . --dist-dir=../../../website/examples/js/algolia-experiences"
"start": "BABEL_ENV=parcel parcel *.html",
"build": "BABEL_ENV=parcel parcel build *.html --public-url .",
"website:examples": "BABEL_ENV=parcel parcel build *.html --public-url . --dist-dir=../../../website/examples/js/algolia-experiences"
},
"browserslist": "firefox 68, chrome 78, IE 11",
"devDependencies": {
Expand Down
89 changes: 89 additions & 0 deletions examples/js/algolia-experiences/toggle.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Algolia Experiences demo</title>
<link
rel="stylesheet"
href="/packages/instantsearch.css/themes/satellite-min.css"
/>
<style>
body {
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue',
sans-serif;
}
main {
max-width: 80em;
margin: 0 auto;
}
.ais-Hits-list,
.ais-TrendingItems-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 1em;
}
.ais-TrendingItems-item,
.ais-Hits-item {
padding: 0;
}
.ais-TrendingItems-item a,
.ais-Hits-item a {
width: 100%;
padding: 1.5em;
text-decoration: none;
color: inherit;
display: flex;
flex-direction: column;
}
.ais-TrendingItems-item img,
.ais-Hits-item img {
width: 100%;
height: 10em;
border-radius: 4px;
object-fit: contain;
}
.__flex {
display: flex;
gap: 1em;
justify-content: space-between;
}
.__bold {
font-weight: bold;
}
div:has(> .ais-Pagination) {
display: flex;
}
.ais-Pagination {
margin: 1em auto;
}
</style>
<script src="/packages/algolia-experiences/dist/algolia-experiences.development.js"></script>
<meta
name="algolia-configuration"
content='{"appId":"F4T6CUV2AH","apiKey":"72d500963987bc97ff899d350a00f7a8"}'
/>
<script>
let isMounted = false;
function toggle() {
if (isMounted) {
document.querySelector('algolia-experience').remove();
isMounted = false;
return;
}
const element = document.createElement('template');
document.querySelector('main').appendChild(element);
element.outerHTML = `
<algolia-experience experience-id="category:historical" />
`;
isMounted = true;
}
</script>
</head>
<body>
<main>
<h2>Buy historical books now!</h2>
<button onclick="toggle()">Toggle</button>
</main>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { fetchConfiguration } from '../get-configuration';

describe('fetchConfiguration', () => {
it('should fetch and cache the configuration', async () => {
const settings = { appId: 'appId', apiKey: 'apiKey' };

// @ts-ignore
global.fetch = jest.fn(() =>
Promise.resolve({
ok: true,
json: () =>
Promise.resolve({
id: '1',
name: 'name',
indexName: 'indexName',
blocks: [],
createdAt: 'createdAt',
updatedAt: 'updatedAt',
}),
})
);

await fetchConfiguration('1', settings);

expect(global.fetch).toHaveBeenCalledTimes(1);

await fetchConfiguration('1', settings);

expect(global.fetch).toHaveBeenCalledTimes(1);
});
});
61 changes: 19 additions & 42 deletions packages/algolia-experiences/src/__tests__/render.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,28 +27,15 @@ describe('configToIndex', () => {
error.mockClear();
});

it('errors if element not found', () => {
const elements = new Map<string, HTMLElement>();
const config = { id: 'foo', indexName: 'bar', name: 'Foo', blocks: [] };
const result = configToIndex(config, elements);
expect(result).toEqual([]);
expect(error).toHaveBeenCalledTimes(1);
expect(error).toHaveBeenCalledWith(
'[Algolia Experiences] Element with id foo not found'
);
});

it('returns index with widgets', () => {
const elements = new Map<string, HTMLElement>([
['foo', document.createElement('div')],
]);
const element = document.createElement('div');
const config: Configuration = {
id: 'foo',
indexName: 'bar',
name: 'Foo',
blocks: [{ type: 'ais.hits', parameters: {}, children: [] }],
};
const result = configToIndex(config, elements);
const result = configToIndex(config, element);
expect(result).toHaveLength(1);
expect(result[0].getIndexName()).toEqual('bar');
expect(result[0].getIndexId()).toEqual('foo');
Expand All @@ -57,9 +44,7 @@ describe('configToIndex', () => {
});

it('maps to the right widget types', () => {
const elements = new Map<string, HTMLElement>([
['foo', document.createElement('div')],
]);
const element = document.createElement('div');
const config: Configuration = {
id: 'foo',
indexName: 'bar',
Expand Down Expand Up @@ -121,7 +106,7 @@ describe('configToIndex', () => {
},
],
};
const result = configToIndex(config, elements);
const result = configToIndex(config, element);
expect(result).toHaveLength(1);
expect(result[0].getWidgets()).toHaveLength(config.blocks.length);
expect(result[0].getWidgets().map((w) => w.$$type)).toEqual([
Expand Down Expand Up @@ -167,9 +152,7 @@ describe('configToIndex', () => {
),
});
const search = instantsearch({ searchClient });
const elements = new Map<string, HTMLElement>([
['foo', document.createElement('div')],
]);
const element = document.createElement('div');
const config: Configuration = {
id: 'foo',
indexName: 'bar',
Expand Down Expand Up @@ -210,11 +193,11 @@ describe('configToIndex', () => {
},
],
};
const result = configToIndex(config, elements);
const result = configToIndex(config, element);
expect(result).toHaveLength(1);
expect(result[0].getWidgets()).toHaveLength(1);

expect(elements.get('foo')?.innerHTML).toMatchInlineSnapshot(`
expect(element.innerHTML).toMatchInlineSnapshot(`
<div>
</div>
`);
Expand All @@ -223,7 +206,7 @@ describe('configToIndex', () => {
search.start();
await wait(100);

expect(elements.get('foo')?.innerHTML).toMatchInlineSnapshot(`
expect(element.innerHTML).toMatchInlineSnapshot(`
<div>
<div class="ais-Hits">
<ol class="ais-Hits-list">
Expand Down Expand Up @@ -276,9 +259,7 @@ describe('configToIndex', () => {
),
});
const search = instantsearch({ searchClient });
const elements = new Map<string, HTMLElement>([
['foo', document.createElement('div')],
]);
const element = document.createElement('div');

const config: Configuration = {
id: 'foo',
Expand All @@ -292,12 +273,12 @@ describe('configToIndex', () => {
],
};

const result = configToIndex(config, elements);
const result = configToIndex(config, element);

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

expect(elements.get('foo')?.innerHTML).toMatchInlineSnapshot(`
expect(element.innerHTML).toMatchInlineSnapshot(`
<div>
</div>
`);
Expand All @@ -306,7 +287,7 @@ describe('configToIndex', () => {
search.start();
await wait(100);

expect(elements.get('foo')?.innerHTML).toMatchInlineSnapshot(`
expect(element.innerHTML).toMatchInlineSnapshot(`
<div>
<div class="ais-Panel">
<div class="ais-Panel-header">
Expand Down Expand Up @@ -351,9 +332,7 @@ describe('configToIndex', () => {
),
});
const search = instantsearch({ searchClient });
const elements = new Map<string, HTMLElement>([
['foo', document.createElement('div')],
]);
const element = document.createElement('div');

const config: Configuration = {
id: 'foo',
Expand All @@ -367,12 +346,12 @@ describe('configToIndex', () => {
],
};

const result = configToIndex(config, elements);
const result = configToIndex(config, element);

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

expect(elements.get('foo')).toBeEmptyDOMElement();
expect(element).toBeEmptyDOMElement();

search.addWidgets(result);
search.start();
Expand Down Expand Up @@ -400,9 +379,7 @@ describe('configToIndex', () => {
),
});
const search = instantsearch({ searchClient });
const elements = new Map<string, HTMLElement>([
['foo', document.createElement('div')],
]);
const element = document.createElement('div');

const config: Configuration = {
id: 'foo',
Expand All @@ -416,12 +393,12 @@ describe('configToIndex', () => {
],
};

const result = configToIndex(config, elements);
const result = configToIndex(config, element);

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

expect(elements.get('foo')?.innerHTML).toMatchInlineSnapshot(`
expect(element?.innerHTML).toMatchInlineSnapshot(`
<div>
</div>
`);
Expand All @@ -430,7 +407,7 @@ describe('configToIndex', () => {
search.start();
await wait(100);

expect(elements.get('foo')?.innerHTML).toMatchInlineSnapshot(`
expect(element?.innerHTML).toMatchInlineSnapshot(`
<div>
<div class="ais-Pagination ais-Pagination--noRefinement">
<ul class="ais-Pagination-list">
Expand Down
Loading

0 comments on commit 7eeffe6

Please sign in to comment.