mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-01-25 11:46:12 +01:00
🏷️ chore: Add Missing Localizations for Agents, Categories, Bookmarks (#9266)
* fix: error when updating bookmarks if no query data * feat: localize bookmark dialog, form labels and validation messages, also improve validation * feat: add localization for EmptyPromptPreview component and update translation.json * chore: add missing localizations for static UI text * chore: update AgentPanelContextType and useGetAgentsConfig to support null configurations * refactor: update agent categories to support localization and custom properties, improve related typing * ci: add localization for 'All' category and update tab names in accessibility tests * chore: remove unused AgentCategoryDisplay component and its tests * chore: add localization handling for agent category selector * chore: enhance AgentCard to support localized category labels and add related tests * chore: enhance i18n unused keys detection to include additional source directories and improve handling for agent category keys
This commit is contained in:
parent
94426a3cae
commit
bbfe4002eb
22 changed files with 328 additions and 252 deletions
|
|
@ -61,6 +61,7 @@ const mockLocalize = jest.fn((key: string, options?: any) => {
|
|||
com_agents_search_empty_heading: 'No search results',
|
||||
com_agents_created_by: 'by',
|
||||
com_agents_top_picks: 'Top Picks',
|
||||
com_agents_all_category: 'All',
|
||||
// ErrorDisplay translations
|
||||
com_agents_error_suggestion_generic: 'Try refreshing the page or check your network connection',
|
||||
com_agents_error_network_title: 'Network Error',
|
||||
|
|
@ -199,7 +200,7 @@ describe('Accessibility Improvements', () => {
|
|||
/>,
|
||||
);
|
||||
|
||||
const promotedTab = screen.getByRole('tab', { name: /Top Picks tab/ });
|
||||
const promotedTab = screen.getByRole('tab', { name: /Top Picks category/ });
|
||||
|
||||
// Test arrow key navigation
|
||||
fireEvent.keyDown(promotedTab, { key: 'ArrowRight' });
|
||||
|
|
@ -226,8 +227,8 @@ describe('Accessibility Improvements', () => {
|
|||
/>,
|
||||
);
|
||||
|
||||
const promotedTab = screen.getByRole('tab', { name: /Top Picks tab/ });
|
||||
const allTab = screen.getByRole('tab', { name: /All tab/ });
|
||||
const promotedTab = screen.getByRole('tab', { name: /Top Picks category/ });
|
||||
const allTab = screen.getByRole('tab', { name: /All category/ });
|
||||
|
||||
// Active tab should be focusable
|
||||
expect(promotedTab).toHaveAttribute('tabIndex', '0');
|
||||
|
|
|
|||
|
|
@ -8,10 +8,42 @@ import type t from 'librechat-data-provider';
|
|||
jest.mock('~/hooks/useLocalize', () => () => (key: string) => {
|
||||
const mockTranslations: Record<string, string> = {
|
||||
com_agents_created_by: 'Created by',
|
||||
com_agents_agent_card_label: '{{name}} agent. {{description}}',
|
||||
com_agents_category_general: 'General',
|
||||
com_agents_category_hr: 'Human Resources',
|
||||
};
|
||||
return mockTranslations[key] || key;
|
||||
});
|
||||
|
||||
// Mock useAgentCategories hook
|
||||
jest.mock('~/hooks', () => ({
|
||||
useLocalize: () => (key: string, values?: Record<string, string>) => {
|
||||
const mockTranslations: Record<string, string> = {
|
||||
com_agents_created_by: 'Created by',
|
||||
com_agents_agent_card_label: '{{name}} agent. {{description}}',
|
||||
com_agents_category_general: 'General',
|
||||
com_agents_category_hr: 'Human Resources',
|
||||
};
|
||||
let translation = mockTranslations[key] || key;
|
||||
|
||||
// Replace placeholders with actual values
|
||||
if (values) {
|
||||
Object.entries(values).forEach(([placeholder, value]) => {
|
||||
translation = translation.replace(new RegExp(`{{${placeholder}}}`, 'g'), value);
|
||||
});
|
||||
}
|
||||
|
||||
return translation;
|
||||
},
|
||||
useAgentCategories: () => ({
|
||||
categories: [
|
||||
{ value: 'general', label: 'com_agents_category_general' },
|
||||
{ value: 'hr', label: 'com_agents_category_hr' },
|
||||
{ value: 'custom', label: 'Custom Category' }, // Non-localized custom category
|
||||
],
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('AgentCard', () => {
|
||||
const mockAgent: t.Agent = {
|
||||
id: '1',
|
||||
|
|
@ -200,6 +232,49 @@ describe('AgentCard', () => {
|
|||
|
||||
const card = screen.getByRole('button');
|
||||
expect(card).toHaveAttribute('tabIndex', '0');
|
||||
expect(card).toHaveAttribute('aria-label', 'com_agents_agent_card_label');
|
||||
expect(card).toHaveAttribute(
|
||||
'aria-label',
|
||||
'Test Agent agent. A test agent for testing purposes',
|
||||
);
|
||||
});
|
||||
|
||||
it('displays localized category label', () => {
|
||||
const agentWithCategory = {
|
||||
...mockAgent,
|
||||
category: 'general',
|
||||
};
|
||||
|
||||
render(<AgentCard agent={agentWithCategory} onClick={mockOnClick} />);
|
||||
|
||||
expect(screen.getByText('General')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays custom category label', () => {
|
||||
const agentWithCustomCategory = {
|
||||
...mockAgent,
|
||||
category: 'custom',
|
||||
};
|
||||
|
||||
render(<AgentCard agent={agentWithCustomCategory} onClick={mockOnClick} />);
|
||||
|
||||
expect(screen.getByText('Custom Category')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays capitalized fallback for unknown category', () => {
|
||||
const agentWithUnknownCategory = {
|
||||
...mockAgent,
|
||||
category: 'unknown',
|
||||
};
|
||||
|
||||
render(<AgentCard agent={agentWithUnknownCategory} onClick={mockOnClick} />);
|
||||
|
||||
expect(screen.getByText('Unknown')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not display category tag when category is not provided', () => {
|
||||
render(<AgentCard agent={mockAgent} onClick={mockOnClick} />);
|
||||
|
||||
expect(screen.queryByText('General')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Unknown')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,90 +0,0 @@
|
|||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import AgentCategoryDisplay from '../AgentCategoryDisplay';
|
||||
|
||||
// Mock the useAgentCategories hook
|
||||
jest.mock('~/hooks/Agents', () => ({
|
||||
useAgentCategories: () => ({
|
||||
categories: [
|
||||
{
|
||||
value: 'general',
|
||||
label: 'General',
|
||||
icon: <span data-testid="icon-general">{''}</span>,
|
||||
className: 'w-full',
|
||||
},
|
||||
{
|
||||
value: 'hr',
|
||||
label: 'HR',
|
||||
icon: <span data-testid="icon-hr">{''}</span>,
|
||||
className: 'w-full',
|
||||
},
|
||||
{
|
||||
value: 'rd',
|
||||
label: 'R&D',
|
||||
icon: <span data-testid="icon-rd">{''}</span>,
|
||||
className: 'w-full',
|
||||
},
|
||||
{
|
||||
value: 'finance',
|
||||
label: 'Finance',
|
||||
icon: <span data-testid="icon-finance">{''}</span>,
|
||||
className: 'w-full',
|
||||
},
|
||||
],
|
||||
emptyCategory: {
|
||||
value: '',
|
||||
label: 'General',
|
||||
className: 'w-full',
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('AgentCategoryDisplay', () => {
|
||||
it('should display the proper label for a category', () => {
|
||||
render(<AgentCategoryDisplay category="rd" />);
|
||||
expect(screen.getByText('R&D')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display the icon when showIcon is true', () => {
|
||||
render(<AgentCategoryDisplay category="finance" showIcon={true} />);
|
||||
expect(screen.getByTestId('icon-finance')).toBeInTheDocument();
|
||||
expect(screen.getByText('Finance')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not display the icon when showIcon is false', () => {
|
||||
render(<AgentCategoryDisplay category="hr" showIcon={false} />);
|
||||
expect(screen.queryByTestId('icon-hr')).not.toBeInTheDocument();
|
||||
expect(screen.getByText('HR')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should apply custom classnames', () => {
|
||||
render(<AgentCategoryDisplay category="general" className="test-class" />);
|
||||
expect(screen.getByText('General').parentElement).toHaveClass('test-class');
|
||||
});
|
||||
|
||||
it('should not render anything for unknown categories', () => {
|
||||
const { container } = render(<AgentCategoryDisplay category="unknown" />);
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
|
||||
it('should not render anything when no category is provided', () => {
|
||||
const { container } = render(<AgentCategoryDisplay />);
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
|
||||
it('should not render anything for empty category when showEmptyFallback is false', () => {
|
||||
const { container } = render(<AgentCategoryDisplay category="" />);
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
|
||||
it('should render empty category placeholder when showEmptyFallback is true', () => {
|
||||
render(<AgentCategoryDisplay category="" showEmptyFallback={true} />);
|
||||
expect(screen.getByText('General')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should apply custom iconClassName to the icon', () => {
|
||||
render(<AgentCategoryDisplay category="general" iconClassName="custom-icon-class" />);
|
||||
const iconElement = screen.getByTestId('icon-general').parentElement;
|
||||
expect(iconElement).toHaveClass('custom-icon-class');
|
||||
});
|
||||
});
|
||||
|
|
@ -9,7 +9,8 @@ import type t from 'librechat-data-provider';
|
|||
jest.mock('~/hooks/useLocalize', () => () => (key: string) => {
|
||||
const mockTranslations: Record<string, string> = {
|
||||
com_agents_top_picks: 'Top Picks',
|
||||
com_agents_all: 'All',
|
||||
com_agents_all: 'All Agents',
|
||||
com_agents_all_category: 'All',
|
||||
com_ui_no_categories: 'No categories available',
|
||||
com_agents_category_tabs_label: 'Agent Categories',
|
||||
com_ui_agent_category_general: 'General',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue