This commit is contained in:
Pavel committed 2026-09-26 14:18:15 +04:00
1 parent 6bc5467c08
commit 10dbca8177
2 files changed
+82 -1

No files matched your search

+19 -1
View File
@@ -95,6 +95,11 @@ export function usePacedText(text: string, enabled: boolean): string {
return enabled ? text.slice(0, Math.min(visibleLength, text.length)) : text;
}
// Each useDarkTheme() caller holds its own state; a toggle broadcasts the new
// value so every other caller (logo, Mermaid, code blocks) follows without a
// reload.
const THEME_CHANGE_EVENT = 'docsgpt:themechange';
export function useDarkTheme() {
const getSystemThemePreference = () => {
return (
@@ -126,6 +131,15 @@ export function useDarkTheme() {
return () => mediaQuery.removeListener(handleChange);
}, []);
useEffect(() => {
const handleThemeChange = (event: Event) => {
setIsDarkTheme((event as CustomEvent<boolean>).detail);
};
window.addEventListener(THEME_CHANGE_EVENT, handleThemeChange);
return () =>
window.removeEventListener(THEME_CHANGE_EVENT, handleThemeChange);
}, []);
useEffect(() => {
localStorage.setItem('selectedTheme', isDarkTheme ? 'Dark' : 'Light');
const action = isDarkTheme ? 'add' : 'remove';
@@ -148,7 +162,11 @@ export function useDarkTheme() {
}, [isDarkTheme]);
const toggleTheme = () => {
setIsDarkTheme(!isDarkTheme);
const next = !isDarkTheme;
setIsDarkTheme(next);
window.dispatchEvent(
new CustomEvent<boolean>(THEME_CHANGE_EVENT, { detail: next }),
);
};
return [isDarkTheme, toggleTheme, componentMounted] as const;
+63
View File
@@ -0,0 +1,63 @@
import { act } from 'react';
import { createRoot } from 'react-dom/client';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { useDarkTheme } from './index';
Object.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: true });
describe('useDarkTheme', () => {
let container: HTMLDivElement;
beforeEach(() => {
localStorage.setItem('selectedTheme', 'Light');
vi.stubGlobal('matchMedia', (query: string) => ({
matches: false,
media: query,
addListener: () => {},
removeListener: () => {},
}));
container = document.createElement('div');
document.body.appendChild(container);
});
afterEach(() => {
container.remove();
localStorage.clear();
vi.unstubAllGlobals();
});
it('updates every consumer when one of them toggles the theme', async () => {
let toggle: (() => void) | undefined;
let logoIsDark: boolean | undefined;
function Settings() {
const [, toggleTheme] = useDarkTheme();
toggle = toggleTheme;
return null;
}
function Logo() {
[logoIsDark] = useDarkTheme();
return null;
}
const root = createRoot(container);
await act(async () => {
root.render(
<>
<Settings />
<Logo />
</>,
);
});
expect(logoIsDark).toBe(false);
await act(async () => toggle?.());
expect(logoIsDark).toBe(true);
await act(async () => toggle?.());
expect(logoIsDark).toBe(false);
await act(async () => root.unmount());
});
});