diff --git a/client/src/mobile/screens/admin/MAdminAddonManager.tsx b/client/src/mobile/screens/admin/MAdminAddonManager.tsx index 93517a651..4e161f5b5 100644 --- a/client/src/mobile/screens/admin/MAdminAddonManager.tsx +++ b/client/src/mobile/screens/admin/MAdminAddonManager.tsx @@ -1,6 +1,6 @@ import { type ComponentType } from 'react' import { useTranslation } from '../../../i18n' -import { useSettingsStore } from '../../../store/settingsStore' +import { useIsDark } from '../../../hooks/useIsDark' import { Puzzle, ListChecks, Wallet, FileText, CalendarDays, Globe, Briefcase, Image, Terminal, Link2, Compass, BookOpen, Sparkles, Luggage, Plane, Server, Cloud, Bookmark, Users, Check, Loader2, @@ -56,8 +56,7 @@ function AddonIcon({ name, size = 18, enabled = true }: AddonIconProps) { export default function MAdminAddonManager({ bagTrackingEnabled, onToggleBagTracking, collabFeatures, onToggleCollabFeature }: { bagTrackingEnabled?: boolean; onToggleBagTracking?: () => void; collabFeatures?: CollabFeatures; onToggleCollabFeature?: (key: string) => void }) { const { t } = useTranslation() - const dm = useSettingsStore(s => s.settings.dark_mode) - const dark = dm === true || dm === 'dark' || (dm === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches) + const dark = useIsDark() const { addons, loading, handleToggle, tripAddons, globalAddons, integrationAddons, providerOptions, documentProviderOptions, } = useAddonManager() diff --git a/client/tests/unit/mobile/admin/MAdminAddonManager.test.tsx b/client/tests/unit/mobile/admin/MAdminAddonManager.test.tsx index 8ee9c1c0b..3b77e2c6c 100644 --- a/client/tests/unit/mobile/admin/MAdminAddonManager.test.tsx +++ b/client/tests/unit/mobile/admin/MAdminAddonManager.test.tsx @@ -1,8 +1,8 @@ -// FE-MOB-AADD-001 to FE-MOB-AADD-036 +// FE-MOB-AADD-001 to FE-MOB-AADD-037 import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import userEvent from '@testing-library/user-event'; import { delay, http, HttpResponse } from 'msw'; -import { render, screen, waitFor, within } from '../../../helpers/render'; +import { act, render, screen, waitFor, within } from '../../../helpers/render'; import { server } from '../../../helpers/msw/server'; import { resetAllStores, seedStore } from '../../../helpers/store'; import { buildSettings } from '../../../helpers/factories'; @@ -67,6 +67,7 @@ beforeEach(() => { afterEach(() => { vi.restoreAllMocks(); + document.documentElement.classList.remove('dark'); }); describe('MAdminAddonManager', () => { @@ -92,21 +93,13 @@ describe('MAdminAddonManager', () => { expect(screen.getByAltText('TREK')).toHaveAttribute('src', '/text-dark.svg'); }); - it('FE-MOB-AADD-003: dark mode and auto+prefers-dark swap the wordmark', async () => { - seedStore(useSettingsStore, { settings: buildSettings({ dark_mode: 'dark' }) }); - const { unmount } = render(); - await screen.findByText('No addons available'); - expect(screen.getByAltText('TREK')).toHaveAttribute('src', '/text-light.svg'); - unmount(); - - const matchMedia = vi.fn().mockReturnValue({ matches: true, addEventListener: vi.fn(), removeEventListener: vi.fn() }); - vi.stubGlobal('matchMedia', matchMedia); - seedStore(useSettingsStore, { settings: buildSettings({ dark_mode: 'auto' }) }); + it('FE-MOB-AADD-003: the dark palette swaps the wordmark', async () => { + // The wordmark reads the .dark class, the same source applyAppearance() writes + // for dark and for auto under a dark OS theme. + document.documentElement.classList.add('dark'); render(); await screen.findByText('No addons available'); expect(screen.getByAltText('TREK')).toHaveAttribute('src', '/text-light.svg'); - expect(matchMedia).toHaveBeenCalledWith('(prefers-color-scheme: dark)'); - vi.unstubAllGlobals(); }); it('FE-MOB-AADD-004: a failing load toasts the addon error', async () => { @@ -757,4 +750,15 @@ describe('MAdminAddonManager', () => { expect(document.querySelector('svg.lucide-sparkles')).toBeInTheDocument(); expect(document.querySelector('svg.lucide-puzzle')).not.toBeInTheDocument(); }); + + it('FE-MOB-AADD-037: under auto the wordmark follows an OS theme switch without a re-render', async () => { + seedStore(useSettingsStore, { settings: buildSettings({ dark_mode: 'auto' }) }); + render(); + await screen.findByText('No addons available'); + expect(screen.getByAltText('TREK')).toHaveAttribute('src', '/text-dark.svg'); + + // applyAppearance() flips the .dark class when the OS theme changes under auto. + act(() => document.documentElement.classList.add('dark')); + await waitFor(() => expect(screen.getByAltText('TREK')).toHaveAttribute('src', '/text-light.svg')); + }); });