mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-08-27 12:13:30 +00:00
📭 fix: Detect Agent List Pages in useHasData (#15156)
* 🐛 fix: Detect `AgentListResponse` data in `useHasData`
The marketplace agent queries return `AgentListResponse` pages whose
agents live under the `data` field, but `useHasData` only checked for
a non-existent `agents` field, so it always returned `false` for real
agent list pages. Check the `data` field first so cached list pages are
recognized as meaningful data.
* fix: preserve SmartLoader type narrowing
* fix: retain cached agents during refetch
---------
Co-authored-by: Danny Avila <danny@librechat.ai>
This commit is contained in:
parent
c7e8b45419
commit
092bc583a8
6 changed files with 70 additions and 12 deletions
|
|
@ -225,7 +225,7 @@ const AgentGrid: React.FC<AgentGridProps> = ({
|
|||
</div>
|
||||
);
|
||||
|
||||
if (isLoading || (isFetching && !isFetchingNextPage)) {
|
||||
if ((isLoading || (isFetching && !isFetchingNextPage)) && !hasData) {
|
||||
return loadingSpinner;
|
||||
}
|
||||
return mainContent;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { AgentListResponse } from 'librechat-data-provider';
|
||||
import type { AgentListResponse } from 'librechat-data-provider';
|
||||
|
||||
interface SmartLoaderProps {
|
||||
/** Whether the content is currently loading */
|
||||
|
|
@ -73,6 +73,12 @@ export const useHasData = (data: AgentListResponse | undefined): boolean => {
|
|||
|
||||
// Type guard for object data
|
||||
if (typeof data === 'object' && data !== null) {
|
||||
// Check for agent list data (AgentListResponse shape, e.g. marketplace pages)
|
||||
const agents = data.data;
|
||||
if (Array.isArray(agents)) {
|
||||
return agents.length > 0;
|
||||
}
|
||||
|
||||
// Check for agent list data
|
||||
if ('agents' in data) {
|
||||
const agents = (data as any).agents;
|
||||
|
|
|
|||
|
|
@ -225,7 +225,7 @@ const VirtualizedAgentGrid: React.FC<VirtualizedAgentGridProps> = ({
|
|||
}
|
||||
|
||||
// Handle loading state
|
||||
if (isLoading || (isFetching && !isFetchingNextPage)) {
|
||||
if ((isLoading || (isFetching && !isFetchingNextPage)) && !hasData) {
|
||||
return loadingSpinner;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,11 +18,6 @@ jest.mock('~/hooks/Agents', () => ({
|
|||
})),
|
||||
}));
|
||||
|
||||
// Mock SmartLoader
|
||||
jest.mock('../SmartLoader', () => ({
|
||||
useHasData: jest.fn(() => true),
|
||||
}));
|
||||
|
||||
// Mock useLocalize hook
|
||||
jest.mock('~/hooks/useLocalize', () => () => (key: string, options?: any) => {
|
||||
const mockTranslations: Record<string, string> = {
|
||||
|
|
@ -362,6 +357,23 @@ describe('AgentGrid Integration with useGetMarketplaceAgentsQuery', () => {
|
|||
expect(spinner).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should retain cached agents while refetching', () => {
|
||||
mockUseMarketplaceAgentsInfiniteQuery.mockReturnValue({
|
||||
...defaultMockQueryResult,
|
||||
isFetching: true,
|
||||
});
|
||||
|
||||
const Wrapper = createWrapper();
|
||||
render(
|
||||
<Wrapper>
|
||||
<AgentGrid category="finance" searchQuery="" onSelectAgent={mockOnSelectAgent} />
|
||||
</Wrapper>,
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('agent-card-1')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('agent-card-2')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show empty state when no agents are available', () => {
|
||||
mockUseMarketplaceAgentsInfiniteQuery.mockReturnValue({
|
||||
...defaultMockQueryResult,
|
||||
|
|
|
|||
|
|
@ -313,6 +313,35 @@ describe('useHasData', () => {
|
|||
expect(screen.getByTestId('result')).toHaveTextContent('no-data');
|
||||
});
|
||||
|
||||
it('detects empty data array (AgentListResponse) as no data', () => {
|
||||
render(
|
||||
<TestComponent
|
||||
data={{ object: 'list', data: [], first_id: '', last_id: '', has_more: false }}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByTestId('result')).toHaveTextContent('no-data');
|
||||
});
|
||||
|
||||
it('detects non-empty data array (AgentListResponse) as has data', () => {
|
||||
render(
|
||||
<TestComponent
|
||||
data={{
|
||||
object: 'list',
|
||||
data: [{ id: 'agent_1', name: 'Test Agent' }],
|
||||
first_id: 'agent_1',
|
||||
last_id: 'agent_1',
|
||||
has_more: false,
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByTestId('result')).toHaveTextContent('has-data');
|
||||
});
|
||||
|
||||
it('detects invalid data property as no data', () => {
|
||||
render(<TestComponent data={{ data: 'not-array' }} />);
|
||||
expect(screen.getByTestId('result')).toHaveTextContent('no-data');
|
||||
});
|
||||
|
||||
it('detects empty agents array as no data', () => {
|
||||
render(<TestComponent data={{ agents: [] }} />);
|
||||
expect(screen.getByTestId('result')).toHaveTextContent('no-data');
|
||||
|
|
|
|||
|
|
@ -160,10 +160,6 @@ jest.mock('~/hooks', () => ({
|
|||
},
|
||||
}));
|
||||
|
||||
jest.mock('../SmartLoader', () => ({
|
||||
useHasData: () => true,
|
||||
}));
|
||||
|
||||
jest.mock('../AgentCard', () => {
|
||||
return function MockAgentCard({
|
||||
agent,
|
||||
|
|
@ -266,6 +262,21 @@ describe('VirtualizedAgentGrid', () => {
|
|||
expect(spinner).toHaveClass('h-8 w-8 text-text-primary');
|
||||
});
|
||||
|
||||
it('retains cached agents while refetching', () => {
|
||||
const useMarketplaceAgentsInfiniteQuery = (
|
||||
jest.requireMock('~/data-provider/Agents') as MarketplaceAgentsMock
|
||||
).useMarketplaceAgentsInfiniteQuery;
|
||||
useMarketplaceAgentsInfiniteQuery.mockImplementation(() =>
|
||||
createMockInfiniteQuery({ isFetching: true }),
|
||||
);
|
||||
|
||||
renderComponent();
|
||||
|
||||
expect(screen.getByTestId('virtual-list')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('agent-card-1')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('agent-card-2')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('has proper accessibility attributes', () => {
|
||||
renderComponent({ category: 'productivity' });
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue