Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
38a296a
init
shaejaz May 21, 2026
8d1e0b4
fix types
shaejaz May 21, 2026
fd8f9dd
Merge branch 'master' into feat/chat-page-suggestions-widget
shaejaz May 22, 2026
599683b
fix duplicate requests
shaejaz May 22, 2026
b867e28
rename current page suggestions
shaejaz May 26, 2026
aee97fd
add suggestions pills component
shaejaz May 26, 2026
01349ec
standalone suggestions
shaejaz May 26, 2026
b33b848
fix ssr
shaejaz May 26, 2026
6881fd8
remove ChatPageSummary widget (split to feat/chat-page-summary-widget)
shaejaz May 26, 2026
5479d0f
Merge branch 'master' into feat/chat-page-suggestions-widget
shaejaz May 26, 2026
fe120b1
fix tests
shaejaz May 27, 2026
a9a6004
update api
shaejaz May 28, 2026
2e0f439
Merge branch 'master' into feat/chat-page-suggestions-widget
shaejaz Jul 7, 2026
3b48a55
update endpoint, remove standalone naming
shaejaz Jul 9, 2026
71db731
add tests
shaejaz Jul 9, 2026
2fd7918
Merge branch 'master' into feat/chat-page-suggestions-widget
shaejaz Jul 9, 2026
922aa54
chore(examples): revert example changes for chat-page-suggestions
shaejaz Jul 9, 2026
953baf4
add task name test
shaejaz Jul 9, 2026
aae135c
address comments
shaejaz Jul 9, 2026
5970f2b
fix bundlesize
shaejaz Jul 9, 2026
9f59a30
fix nextjs and loading states
shaejaz Jul 9, 2026
738eacb
address comments and fix test
shaejaz Jul 9, 2026
6bfbd52
add header
shaejaz Jul 9, 2026
30271bd
add streaming support
shaejaz Jul 12, 2026
82c47a1
rename widget
shaejaz Jul 12, 2026
ff9ce8a
move functionality to connector
shaejaz Jul 13, 2026
42fdb5b
add comments
shaejaz Jul 13, 2026
82241ca
add filters support
shaejaz Jul 13, 2026
0f37cfa
address comments
shaejaz Jul 14, 2026
1f6ab0f
update bundlesize
shaejaz Jul 14, 2026
b588873
Merge branch 'master' into feat/chat-page-suggestions-widget
shaejaz Jul 14, 2026
1b0b390
Merge branch 'master' into feat/chat-page-suggestions-widget
shaejaz Jul 16, 2026
f360a6f
address comments
shaejaz Jul 16, 2026
bd11185
Merge branch 'master' into feat/chat-page-suggestions-widget
shaejaz Jul 17, 2026
fb516d6
fix(chat): guard sessionStorage access for SSR
shaejaz Jul 17, 2026
bf118b4
chore(chat): drop sessionStorage SSR guard (split to separate PR)
shaejaz Jul 17, 2026
eb09ece
test(chat): cover SSR sessionStorage guard
shaejaz Jul 17, 2026
88fdcc1
remove next and ssr changes
shaejaz Jul 17, 2026
6e5a6be
Merge branch 'feat/chat-page-suggestions-widget' into fix/chat-ssr-se…
shaejaz Jul 17, 2026
6384dbd
add nextjs changes
shaejaz Jul 17, 2026
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
10 changes: 5 additions & 5 deletions bundlesize.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,19 @@
},
{
"path": "./packages/instantsearch.js/dist/instantsearch.production.min.js",
"maxSize": "130.5 kB"
"maxSize": "133.5 kB"
},
{
"path": "./packages/instantsearch.js/dist/instantsearch.development.js",
"maxSize": "280 kB"
"maxSize": "282.5 kB"
},
{
"path": "packages/react-instantsearch-core/dist/umd/ReactInstantSearchCore.min.js",
"maxSize": "61.5 kB"
"maxSize": "63.75 kB"
},
{
"path": "packages/react-instantsearch/dist/umd/ReactInstantSearch.min.js",
"maxSize": "103.25 kB"
"maxSize": "106.25 kB"
},
{
"path": "packages/vue-instantsearch/vue2/umd/index.js",
Expand All @@ -46,7 +46,7 @@
},
{
"path": "./packages/instantsearch.css/themes/algolia-min.css",
"maxSize": "10 kB"
"maxSize": "10.25 kB"
},
{
"path": "./packages/instantsearch.css/themes/reset.css",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
/**
* @jest-environment @instantsearch/testutils/jest-environment-jsdom.ts
*/
/** @jsx createElement */
import { render } from '@testing-library/preact';
import { Fragment, createElement } from 'preact';

import { createOnPageSuggestionsComponent } from '../chat/OnPageSuggestions';

describe('OnPageSuggestions', () => {
const OnPageSuggestions = createOnPageSuggestionsComponent({
createElement,
Fragment,
});

test('renders an empty root when there are no suggestions and not loading', () => {
const { container } = render(
<OnPageSuggestions suggestions={[]} onSuggestionClick={jest.fn()} />
);

const root = container.querySelector('.ais-OnPageSuggestions');
expect(root).toBeInTheDocument();
expect(root).toBeEmptyDOMElement();
expect(
container.querySelector('.ais-OnPageSuggestions-skeleton')
).not.toBeInTheDocument();
});

test('forwards HTML attributes and merges className onto the root', () => {
const { container } = render(
<OnPageSuggestions
suggestions={[]}
onSuggestionClick={jest.fn()}
className="CUSTOM"
title="hello"
/>
);

const root = container.querySelector<HTMLDivElement>(
'.ais-OnPageSuggestions'
)!;
expect(root.classList.contains('CUSTOM')).toBe(true);
expect(root.title).toBe('hello');
});

test('renders the default header when there are suggestions', () => {
const { container } = render(
<OnPageSuggestions
suggestions={['A', 'B']}
onSuggestionClick={jest.fn()}
/>
);

const header = container.querySelector('.ais-OnPageSuggestions-header');
expect(header).toBeInTheDocument();
expect(
container.querySelector('.ais-OnPageSuggestions-headerTitle')
).toHaveTextContent('Suggestions');
});

test('translates the header title', () => {
const { container } = render(
<OnPageSuggestions
suggestions={['A']}
translations={{ headerTitle: 'Ideas' }}
onSuggestionClick={jest.fn()}
/>
);

expect(
container.querySelector('.ais-OnPageSuggestions-headerTitle')
).toHaveTextContent('Ideas');
});

test('renders the default header while loading', () => {
const { container } = render(
<OnPageSuggestions
suggestions={[]}
isLoading={true}
onSuggestionClick={jest.fn()}
/>
);

expect(
container.querySelector('.ais-OnPageSuggestions-header')
).toBeInTheDocument();
});

test('does not render the header when empty and not loading', () => {
const { container } = render(
<OnPageSuggestions suggestions={[]} onSuggestionClick={jest.fn()} />
);

expect(
container.querySelector('.ais-OnPageSuggestions-header')
).not.toBeInTheDocument();
});

test('disables the header when headerComponent is false', () => {
const { container } = render(
<OnPageSuggestions
suggestions={['A']}
headerComponent={false}
onSuggestionClick={jest.fn()}
/>
);

expect(
container.querySelector('.ais-OnPageSuggestions-header')
).not.toBeInTheDocument();
expect(
container.querySelectorAll('.ais-OnPageSuggestions-suggestion')
).toHaveLength(1);
});

test('renders a custom header component', () => {
const { container } = render(
<OnPageSuggestions
suggestions={['A']}
headerComponent={() => <div className="custom-header">Custom</div>}
onSuggestionClick={jest.fn()}
/>
);

expect(
container.querySelector('.ais-OnPageSuggestions-header')
).not.toBeInTheDocument();
const custom = container.querySelector('.custom-header');
expect(custom).toBeInTheDocument();
expect(custom).toHaveTextContent('Custom');
});

test('renders the suggestion pills', () => {
const { container } = render(
<OnPageSuggestions
suggestions={['A', 'B']}
onSuggestionClick={jest.fn()}
/>
);

expect(
container.querySelectorAll('.ais-OnPageSuggestions-suggestion')
).toHaveLength(2);
expect(
container.querySelector('.ais-OnPageSuggestions-skeleton')
).not.toBeInTheDocument();
});

test('renders skeletons while loading with no suggestions yet', () => {
const { container } = render(
<OnPageSuggestions
suggestions={[]}
isLoading={true}
onSuggestionClick={jest.fn()}
/>
);

expect(
container.querySelector('.ais-OnPageSuggestions-skeleton')
).toBeInTheDocument();
expect(
container.querySelectorAll('.ais-OnPageSuggestions-skeletonItem')
).toHaveLength(3);
});

test('keeps existing pills (no skeletons) while refetching', () => {
// Loading with suggestions already present must not swap the pills for
// skeletons — that would make existing suggestions disappear mid-refetch.
const { container } = render(
<OnPageSuggestions
suggestions={['A', 'B']}
isLoading={true}
onSuggestionClick={jest.fn()}
/>
);

expect(
container.querySelectorAll('.ais-OnPageSuggestions-suggestion')
).toHaveLength(2);
expect(
container.querySelector('.ais-OnPageSuggestions-skeleton')
).not.toBeInTheDocument();
});
});
30 changes: 16 additions & 14 deletions packages/instantsearch-ui-components/src/components/chat/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import { createChatHeaderComponent } from './ChatHeader';
import { createChatMessagesComponent } from './ChatMessages';
import { createChatOverlayLayoutComponent } from './ChatOverlayLayout';
import { createChatPromptComponent } from './ChatPrompt';
import { createChatPromptSuggestionsComponent } from './ChatPromptSuggestions';
import { createOnPageSuggestionsComponent } from './OnPageSuggestions';

import type { Renderer, ComponentProps, Hooks } from '../../types';
import type { ChatHeaderProps, ChatHeaderOwnProps } from './ChatHeader';
import type { ChatMessagesProps } from './ChatMessages';
import type { ChatPromptProps, ChatPromptOwnProps } from './ChatPrompt';
import type { ChatPromptSuggestionsOwnProps } from './ChatPromptSuggestions';
import type { OnPageSuggestionsOwnProps } from './OnPageSuggestions';
import type { ChatLayoutOwnProps } from './types';

export type ChatClassNames = {
Expand All @@ -21,7 +21,7 @@ export type ChatClassNames = {
messages?: ChatMessagesProps['classNames'];
message?: ChatMessagesProps['messageClassNames'];
prompt?: ChatPromptProps['classNames'];
suggestions?: ChatPromptSuggestionsOwnProps['classNames'];
suggestions?: OnPageSuggestionsOwnProps['classNames'];
};

export type ChatProps = Omit<ComponentProps<'div'>, 'onError' | 'title'> & {
Expand All @@ -46,9 +46,9 @@ export type ChatProps = Omit<ComponentProps<'div'>, 'onError' | 'title'> & {
*/
promptProps: ChatPromptProps;
/*
* Props for the ChatPromptSuggestions component.
* Props for the OnPageSuggestions component.
*/
suggestionsProps: ChatPromptSuggestionsOwnProps;
suggestionsProps: OnPageSuggestionsOwnProps;
/**
* Optional class names for elements
*/
Expand All @@ -68,7 +68,7 @@ export type ChatProps = Omit<ComponentProps<'div'>, 'onError' | 'title'> & {
/**
* Optional suggestions component for the chat
*/
suggestionsComponent?: (props: ChatPromptSuggestionsOwnProps) => JSX.Element;
suggestionsComponent?: (props: OnPageSuggestionsOwnProps) => JSX.Element;
/**
* Function to send a message to the chat.
*/
Expand Down Expand Up @@ -113,7 +113,7 @@ export function createChatComponent({
memo,
});
const ChatPrompt = createChatPromptComponent({ createElement, Fragment });
const ChatPromptSuggestions = createChatPromptSuggestionsComponent({
const OnPageSuggestions = createOnPageSuggestionsComponent({
createElement,
Fragment,
});
Expand Down Expand Up @@ -191,13 +191,15 @@ export function createChatComponent({
error={error}
classNames={classNames.messages}
messageClassNames={classNames.message}
suggestionsElement={createElement(
SuggestionsComponent || ChatPromptSuggestions,
{
...suggestionsProps,
classNames: classNames.suggestions,
}
)}
suggestionsElement={
suggestionsProps.suggestions?.length || suggestionsProps.isLoading
? createElement(SuggestionsComponent || OnPageSuggestions, {
headerComponent: false,
...suggestionsProps,
classNames: classNames.suggestions,
})
: undefined
}
/>
);

Expand Down

This file was deleted.

Loading
Loading