Skip to content

Commit

Permalink
feat: Add useAutosuggest hook
Browse files Browse the repository at this point in the history
  • Loading branch information
Anand Gorantala authored and anandgorantala committed Aug 29, 2024
1 parent dc589f7 commit 85f39f3
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 0 deletions.
1 change: 1 addition & 0 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ const config: StorybookConfig = {
name: '@storybook/react-vite',
options: {},
},
staticDirs: ['../stories/assets'],
};
export default config;
133 changes: 133 additions & 0 deletions src/search/hooks/autosuggest.hook.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { renderHook, waitFor } from '@testing-library/react';
import { useAutoSuggest } from './autosuggest.hook';
import { autoSuggest, AutosuggestOptions, Configuration, SuggestResponse } from '@bloomreach/discovery-web-sdk';
import { describe, it, expect, vi, beforeEach, Mock } from 'vitest';

vi.mock('@bloomreach/discovery-web-sdk', () => ({
autoSuggest: vi.fn(),
}));

describe('useAutoSuggest', () => {
const mockConfiguration: Configuration = {
account_id: 1234,
auth_key: '123',
domain_key: '1234'
};
const mockSuggestOptions: AutosuggestOptions = {
_br_uid_2: '123',
q: 'test',
catalog_views: 'test',
url: 'https://example.com'
};
const mockResponse: SuggestResponse = {};

beforeEach(() => {
vi.clearAllMocks();
});

it('should initialize with correct default values', () => {
(autoSuggest as Mock).mockResolvedValue(mockResponse);
const { result } = renderHook(() => useAutoSuggest(mockConfiguration, mockSuggestOptions));

expect(result.current.loading).toBe(false);
expect(result.current.response).toBeNull();
expect(result.current.error).toBeNull();
});

it('should call autoSuggest and update response and loading state correctly on success', async () => {
(autoSuggest as Mock).mockResolvedValue(mockResponse);

const { result } = renderHook(() => useAutoSuggest(mockConfiguration, mockSuggestOptions));

// Wait for the autoSuggest to resolve and the hook to update
await waitFor(() => {
expect(result.current.loading).toBe(false);
expect(result.current.response).toEqual(mockResponse);
});

expect(autoSuggest).toHaveBeenCalledWith(mockConfiguration, mockSuggestOptions);
});

it('should update loading and set error correctly on failure', async () => {
const mockError = new Error('Something went wrong');
(autoSuggest as Mock).mockRejectedValue(mockError);

const { result } = renderHook(() => useAutoSuggest(mockConfiguration, mockSuggestOptions));

// Wait for the error to be set and loading to be false
await waitFor(() => {
expect(result.current.loading).toBe(false);
expect(result.current.error).toBe(mockError);
});
});

it('should call autoSuggest again when suggestOptions change', async () => {
(autoSuggest as Mock).mockResolvedValue(mockResponse);

const { result, rerender } = renderHook(
({ config, options }) => useAutoSuggest(config, options),
{
initialProps: { config: mockConfiguration, options: mockSuggestOptions },
}
);

// Wait for the first autoSuggest call
await waitFor(() => {
expect(result.current.response).toEqual(mockResponse);
});

expect(autoSuggest).toHaveBeenCalledTimes(1);

const newSuggestOptions: AutosuggestOptions = {
_br_uid_2: '123',
q: 'test',
catalog_views: 'new_test',
url: 'https://example.com'
};

// Rerender with new suggestOptions
rerender({ config: mockConfiguration, options: newSuggestOptions });

// Wait for the second autoSuggest call
await waitFor(() => {
expect(result.current.response).toEqual(mockResponse);
});

expect(autoSuggest).toHaveBeenCalledTimes(2);
});

it('should clear the response if the "q" option is empty', async () => {
(autoSuggest as Mock).mockResolvedValue(mockResponse);

const { result, rerender } = renderHook(
({ config, options }) => useAutoSuggest(config, options),
{
initialProps: { config: mockConfiguration, options: mockSuggestOptions },
}
);

// Wait for the first autoSuggest call
await waitFor(() => {
expect(result.current.response).toEqual(mockResponse);
});

expect(autoSuggest).toHaveBeenCalledTimes(1);

const newSuggestOptions: AutosuggestOptions = {
_br_uid_2: '123',
q: '',
catalog_views: 'new_test',
url: 'https://example.com'
};

// Rerender with new suggestOptions
rerender({ config: mockConfiguration, options: newSuggestOptions });

// Wait for the second autoSuggest call
await waitFor(() => {
expect(result.current.response).toEqual(null);
});

expect(autoSuggest).toHaveBeenCalledTimes(1);
});
});
51 changes: 51 additions & 0 deletions src/search/hooks/autosuggest.hook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import {
autoSuggest,
AutosuggestOptions,
SuggestResponse,
Configuration,
} from '@bloomreach/discovery-web-sdk';
import { useEffect, useState } from 'react';

type useAutoSuggest = {
response: SuggestResponse | null;
loading: boolean;
error: unknown;
};

export function useAutoSuggest(
configuration: Configuration,
suggestOptions: AutosuggestOptions
): useAutoSuggest {
const [response, setResponse] = useState<SuggestResponse | null>(null);
const [loading, setLoading] = useState<boolean>(false);
const [error, setError] = useState<unknown>(null);

useEffect(() => {
// If there is no query, clear data and error
if (!suggestOptions.q) {
setLoading(false);
setError(null);
setResponse(null);
return;
}

autoSuggest(configuration, suggestOptions)
.then((res) => {
setResponse(res);
setError(null);
})
.catch((e: unknown) => {
setError(e);
setResponse(null);
})
.finally(() => {
setLoading(false);
})
}, [configuration, suggestOptions, setResponse, setError, setLoading]);

return {
error,
loading,
response,
}
}
1 change: 1 addition & 0 deletions src/search/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export type { SearchBoxProps } from './search-box/search-box.types';
export { SearchBox } from './search-box/search-box';
export { useSearchBox } from './search-box/search-box.hook';
export { useSearch } from './hooks/search.hook';
export { useAutoSuggest } from './hooks/autosuggest.hook';
export { SearchContext, SearchContextProvider, type SearchContextType } from './context/search.context';

0 comments on commit 85f39f3

Please sign in to comment.