Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion packages/runtime/src/experiences/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,14 @@ function resolveContainer(
}

if (placement === 'inside') {
return { container: target as HTMLElement, cleanup: () => {} };
const div = document.createElement('div');
(target as HTMLElement).appendChild(div);
return {
container: div,
cleanup: () => {
return div.remove();
},
};
}

const div = document.createElement('div');
Expand Down
16 changes: 16 additions & 0 deletions packages/runtime/src/experiences/templates/autocomplete-item.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { Hit, TemplateParams } from 'instantsearch.js/es/types';

import { renderListItem } from './list-item';

export function renderAutocompleteItem(template: Record<string, string>) {
const listItem = renderListItem(template);

// `onSelect` is provided by autocomplete.js but selection is handled by the
// autocomplete widget itself, so we only need the `item` for rendering.
return function item(
data: { item: Hit; onSelect: () => void },
params: TemplateParams
) {
return listItem(data.item, params);
};
}
1 change: 1 addition & 0 deletions packages/runtime/src/experiences/templates/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './autocomplete-item';
export * from './carousel-item';
export * from './list-item';
export * from './section-header';
Expand Down
48 changes: 47 additions & 1 deletion packages/runtime/src/experiences/widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import numericMenu from 'instantsearch.js/es/widgets/numeric-menu/numeric-menu';

import { renderTool } from './renderer';
import {
renderAutocompleteItem,
renderCarouselItem,
renderListItem,
renderSectionHeader,
Expand Down Expand Up @@ -136,7 +137,7 @@ export default (function experience(
'ais.autocomplete': {
widget: EXPERIMENTAL_autocomplete,
async transformParams(params) {
const { showRecent, showSuggestions, ...rest } =
const { showRecent, showSuggestions, indices, ...rest } =
params as typeof params & {
showRecent?: boolean | { templates?: { header?: string } };
showSuggestions?: {
Expand All @@ -147,6 +148,15 @@ export default (function experience(
indexName?: string;
templates?: { header?: string };
};
indices?: Array<{
indexName: string;
hitsPerPage?: number;
templates?: {
header?: string;
item?: Record<string, string>;
};
searchParameters?: Record<string, unknown>;
}>;
};

const showRecentTransformed = showRecent
Expand Down Expand Up @@ -195,6 +205,42 @@ export default (function experience(
},
}
: {}),
...(indices?.length
? {
indices: indices.map(
({
indexName,
hitsPerPage,
templates: entryTemplates,
searchParameters,
}) => {
return {
indexName,
searchParameters: {
...searchParameters,
...(hitsPerPage != null ? { hitsPerPage } : {}),
},
templates: {
...(entryTemplates?.header
? {
header: renderSectionHeader(
entryTemplates.header
),
}
: {}),
...(entryTemplates?.item
? {
item: renderAutocompleteItem(
entryTemplates.item
),
}
: {}),
},
};
}
),
}
: {}),
});
},
},
Expand Down
89 changes: 89 additions & 0 deletions packages/toolbar/__tests__/autocomplete.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,93 @@ describe('ais.autocomplete field behavior', () => {
expect(onParameterChange).toHaveBeenCalledWith('showSuggestions', false);
});
});

describe('indices-config field', () => {
it('renders empty state when indices is an empty array', () => {
const { container } = render({ indices: [] });

expect(container.textContent).toContain(
'No additional indices configured.'
);
});

it('renders the "Add Index" button', () => {
const { container } = render({ indices: [] });

const addButton = Array.from(container.querySelectorAll('button')).find(
(btn) => {
return btn.textContent?.includes('Add Index');
}
);

expect(addButton).toBeDefined();
});

it('calls onParameterChange with a new entry when adding an index', () => {
const { onParameterChange, container } = render({ indices: [] });

const addButton = Array.from(container.querySelectorAll('button')).find(
(btn) => {
return btn.textContent?.includes('Add Index');
}
);

addButton!.click();

expect(onParameterChange).toHaveBeenCalledWith('indices', [
{ indexName: '', hitsPerPage: 5 },
]);
});

it('renders entry cards for populated indices', () => {
const { container } = render({
indices: [
{
indexName: 'products',
hitsPerPage: 5,
templates: { header: 'Products' },
},
{
indexName: 'articles',
hitsPerPage: 3,
templates: { header: 'Articles' },
},
],
});

expect(container.textContent).toContain('Products');
expect(container.textContent).toContain('Articles');
});

it('calls onParameterChange without the removed entry when deleting', () => {
const { onParameterChange, container } = render({
indices: [
{
indexName: 'products',
hitsPerPage: 5,
templates: { header: 'Products' },
},
{
indexName: 'articles',
hitsPerPage: 3,
templates: { header: 'Articles' },
},
],
});

const removeButtons = Array.from(
container.querySelectorAll('button[aria-label="Remove index"]')
);

removeButtons[0]!.click();

expect(onParameterChange).toHaveBeenCalledWith('indices', [
{
indexName: 'articles',
hitsPerPage: 3,
templates: { header: 'Articles' },
},
]);
});
});
});
15 changes: 15 additions & 0 deletions packages/toolbar/src/components/block-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { SwitchField } from './fields/switch-field';
import { TextField } from './fields/text-field';
import { TextPickerField } from './fields/text-picker-field';
import { ItemsListField } from './fields/items-list-field';
import { IndicesConfigField } from './fields/indices-config-field';
import { ItemTemplateField } from './fields/item-template-field';
import { RecentConfigField } from './fields/recent-config-field';
import { SuggestionsConfigField } from './fields/suggestions-config-field';
Expand Down Expand Up @@ -370,6 +371,20 @@ export function BlockEditor({
/>
);
}
case 'indices-config': {
const entries = Array.isArray(value) ? value : [];
return (
<IndicesConfigField
key={key}
label={label}
entries={entries}
onChange={(newEntries) => {
return onParameterChange(key, newEntries);
}}
suggestLists={suggestLists}
/>
);
}
case 'recent-config': {
return (
<RecentConfigField
Expand Down
Loading