fix(settings): prevent config panel flash on refresh

This commit is contained in:
Tam Nhu Tran
2026-03-27 13:25:26 -04:00
parent d883951c9e
commit 5782edf627
2 changed files with 93 additions and 6 deletions
+6 -6
View File
@@ -210,12 +210,7 @@ function SettingsPageInner() {
{/* Config Content - scrollable */}
<div className="flex-1 overflow-auto">
{rawConfigLoading ? (
<div className="flex items-center justify-center h-full text-muted-foreground">
<RefreshCw className="w-5 h-5 animate-spin mr-2" />
{t('settings.loading')}
</div>
) : rawConfig ? (
{rawConfig ? (
<CodeEditor
value={rawConfig}
onChange={() => {}}
@@ -224,6 +219,11 @@ function SettingsPageInner() {
minHeight="auto"
className="min-h-full"
/>
) : rawConfigLoading ? (
<div className="flex items-center justify-center h-full text-muted-foreground">
<RefreshCw className="w-5 h-5 animate-spin mr-2" />
{t('settings.loading')}
</div>
) : (
<div className="flex items-center justify-center h-full text-muted-foreground">
<div className="text-center">
@@ -0,0 +1,87 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import type { ReactNode } from 'react';
import { render, screen } from '@tests/setup/test-utils';
const mocks = vi.hoisted(() => ({
useSettingsTab: vi.fn(),
useRawConfig: vi.fn(),
setActiveTab: vi.fn(),
fetchRawConfig: vi.fn(),
copyToClipboard: vi.fn(),
}));
vi.mock('@/pages/settings/hooks', () => ({
useSettingsTab: mocks.useSettingsTab,
useRawConfig: mocks.useRawConfig,
}));
vi.mock('react-resizable-panels', () => ({
PanelGroup: ({ children }: { children: ReactNode }) => <div>{children}</div>,
Panel: ({ children }: { children: ReactNode }) => <div>{children}</div>,
PanelResizeHandle: () => <div data-testid="panel-resize-handle" />,
}));
vi.mock('@/components/shared/code-editor', () => ({
CodeEditor: ({ value }: { value: string }) => (
<textarea aria-label="config editor" readOnly value={value} />
),
}));
vi.mock('@/pages/settings/sections/websearch', () => ({
default: () => <div>WebSearch Section</div>,
}));
vi.mock('@/pages/settings/sections/channels', () => ({
default: () => <div>Channels Section</div>,
}));
vi.mock('@/pages/settings/sections/globalenv-section', () => ({
default: () => <div>Global Env Section</div>,
}));
vi.mock('@/pages/settings/sections/thinking', () => ({
default: () => <div>Thinking Section</div>,
}));
vi.mock('@/pages/settings/sections/proxy', () => ({
default: () => <div>Proxy Section</div>,
}));
vi.mock('@/pages/settings/sections/auth-section', () => ({
default: () => <div>Auth Section</div>,
}));
vi.mock('@/pages/settings/sections/backups-section', () => ({
default: () => <div>Backups Section</div>,
}));
import { SettingsPage } from '@/pages/settings';
describe('SettingsPage raw config panel', () => {
beforeEach(() => {
mocks.setActiveTab.mockReset();
mocks.fetchRawConfig.mockReset();
mocks.copyToClipboard.mockReset();
mocks.useSettingsTab.mockReturnValue({
activeTab: 'websearch',
setActiveTab: mocks.setActiveTab,
});
});
it('keeps the current config editor visible while raw config refreshes', async () => {
mocks.useRawConfig.mockReturnValue({
rawConfig: 'websearch:\n enabled: true\n',
loading: true,
copied: false,
fetchRawConfig: mocks.fetchRawConfig,
copyToClipboard: mocks.copyToClipboard,
});
render(<SettingsPage />);
expect(await screen.findAllByText('WebSearch Section')).toHaveLength(2);
expect(screen.getByLabelText('config editor')).toHaveValue('websearch:\n enabled: true\n');
expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
});
});