This is an automated email from the ASF dual-hosted git repository.
lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git
The following commit(s) were added to refs/heads/rocketmq-studio by this push:
new 505f0ed0b fix(settings): reflect active appearance preferences in
controls (#7219)
505f0ed0b is described below
commit 505f0ed0b680e91d1aef7515940e88fea5756226
Author: coder999o <[email protected]>
AuthorDate: Mon Oct 26 20:08:37 2026 +0800
fix(settings): reflect active appearance preferences in controls (#7219)
* fix(settings): reflect active appearance preferences in controls
---
web/src/pages/settings/GeneralSettingsTab.tsx | 9 ++---
.../settings/__tests__/GeneralSettingsTab.test.tsx | 43 ++++++++++++++++++++++
2 files changed, 46 insertions(+), 6 deletions(-)
diff --git a/web/src/pages/settings/GeneralSettingsTab.tsx
b/web/src/pages/settings/GeneralSettingsTab.tsx
index a654b6073..66cac55ff 100644
--- a/web/src/pages/settings/GeneralSettingsTab.tsx
+++ b/web/src/pages/settings/GeneralSettingsTab.tsx
@@ -39,9 +39,6 @@ import useAuthStore from '../../stores/authStore';
const { Text } = Typography;
-const toThemeMode = (value?: string): ThemeMode =>
- value === 'light' || value === 'dark' ? value : 'system';
-
const buildPayload = (settings: GeneralSettings): GeneralSettingsUpdate => ({
preserveLlmConfig: true,
theme: settings.theme,
@@ -65,7 +62,7 @@ const buildPayload = (settings: GeneralSettings):
GeneralSettingsUpdate => ({
export const GeneralSettingsTab = () => {
const { t } = useLang();
const { message } = App.useApp();
- const { setThemeMode, setCompact } = useTheme();
+ const { themeMode, compact, setThemeMode, setCompact } = useTheme();
const [settings, setSettings] = useState<GeneralSettings | null>(null);
const [loading, setLoading] = useState(true);
const [savingPreference, setSavingPreference] = useState(false);
@@ -265,7 +262,7 @@ export const GeneralSettingsTab = () => {
</Text>
</div>
<Segmented
- value={toThemeMode(settings?.theme)}
+ value={themeMode}
options={[
{ value: 'light', label: t('settings.lightTheme') },
{ value: 'dark', label: t('settings.darkTheme') },
@@ -287,7 +284,7 @@ export const GeneralSettingsTab = () => {
</Text>
</div>
<Switch
- checked={Boolean(settings?.compact)}
+ checked={compact}
disabled={savingPreference}
onChange={(checked) => {
setCompact(checked);
diff --git a/web/src/pages/settings/__tests__/GeneralSettingsTab.test.tsx
b/web/src/pages/settings/__tests__/GeneralSettingsTab.test.tsx
index 9fd25504a..2cf1f3e76 100644
--- a/web/src/pages/settings/__tests__/GeneralSettingsTab.test.tsx
+++ b/web/src/pages/settings/__tests__/GeneralSettingsTab.test.tsx
@@ -21,6 +21,8 @@ import userEvent from '@testing-library/user-event';
import { App } from 'antd';
import { getGeneralSettings, saveGeneralSettings } from
'../../../api/settings';
import { ThemeProvider } from '../../../theme/ThemeProvider';
+import { useTheme } from '../../../theme/useTheme';
+import { COMPACT_STORAGE_KEY, THEME_STORAGE_KEY } from
'../../../theme/themePreference';
import { LangProvider } from '../../../i18n/LangContext';
import { LANGUAGE_STORAGE_KEY } from '../../../i18n/languagePreference';
import { GeneralSettingsTab } from '../GeneralSettingsTab';
@@ -51,11 +53,17 @@ vi.mock('../../../api/settings', () => ({
updateDataSource: vi.fn(),
}));
+const ThemeShortcut = () => {
+ const { toggleTheme } = useTheme();
+ return <button onClick={toggleTheme}>Toggle theme</button>;
+};
+
const renderTab = () =>
render(
<App>
<LangProvider>
<ThemeProvider>
+ <ThemeShortcut />
<GeneralSettingsTab />
</ThemeProvider>
</LangProvider>
@@ -209,6 +217,41 @@ describe('GeneralSettingsTab', () => {
);
});
+ it('displays restored local appearance preferences instead of server
defaultsTest', async () => {
+ localStorage.setItem(THEME_STORAGE_KEY, 'dark');
+ localStorage.setItem(COMPACT_STORAGE_KEY, 'true');
+ renderTab();
+
+ await screen.findByDisplayValue('30');
+
+ expect(screen.getByRole('radio', { name: '深色' })).toBeChecked();
+ expect(screen.getByRole('switch')).toBeChecked();
+ expect(saveGeneralSettings).not.toHaveBeenCalled();
+ });
+
+ it('follows global theme changes and allows returning to system modeTest',
async () => {
+ vi.mocked(saveGeneralSettings).mockResolvedValue();
+ const user = userEvent.setup();
+ renderTab();
+ await screen.findByDisplayValue('30');
+
+ await user.click(screen.getByRole('button', { name: 'Toggle theme' }));
+
+ expect(localStorage.getItem(THEME_STORAGE_KEY)).toBe('dark');
+ expect(screen.getByRole('radio', { name: '深色' })).toBeChecked();
+ expect(saveGeneralSettings).not.toHaveBeenCalled();
+
+ await user.click(screen.getByText('跟随系统'));
+
+ await waitFor(() =>
+ expect(saveGeneralSettings).toHaveBeenCalledWith(
+ expect.objectContaining({ theme: 'system' }),
+ ),
+ );
+ expect(localStorage.getItem(THEME_STORAGE_KEY)).toBe('system');
+ expect(screen.getByRole('radio', { name: '跟随系统' })).toBeChecked();
+ });
+
it('saves llm fields from a fresh read instead of the mount-time snapshot',
async () => {
const baseSettings = {
theme: 'system',