diff --git a/packages/ra-ui-materialui/src/theme/useTheme.spec.tsx b/packages/ra-ui-materialui/src/theme/useTheme.spec.tsx
index b0923706afa..886a215df31 100644
--- a/packages/ra-ui-materialui/src/theme/useTheme.spec.tsx
+++ b/packages/ra-ui-materialui/src/theme/useTheme.spec.tsx
@@ -4,6 +4,9 @@ import expect from 'expect';
import { render, screen } from '@testing-library/react';
import { renderHook } from '@testing-library/react-hooks';
import { useTheme } from './useTheme';
+import { AdminContext } from '../AdminContext';
+import { ThemeTestWrapper } from '../layout/ThemeTestWrapper';
+import { defaultDarkTheme } from './defaultTheme';
const authProvider = {
login: jest.fn().mockResolvedValueOnce(''),
@@ -23,33 +26,102 @@ const Foo = () => {
};
describe('useTheme', () => {
- it('should return undefined by default', () => {
+ it('should return the light theme by default', () => {
render(
);
- expect(screen.queryByLabelText('has-theme')).toBeNull();
+ expect(screen.queryByText('light')).not.toBeNull();
+ });
+
+ it('should return the light theme when no dark theme is provided even though user prefers dark mode', () => {
+ render(
+
+
+
+
+
+ );
+ expect(screen.queryByText('light')).not.toBeNull();
+ });
+
+ it('should return the light theme when no dark theme is provided even though the stored theme is dark', () => {
+ const store = memoryStore({ theme: 'dark' });
+ render(
+
+
+
+ );
+ expect(screen.queryByText('light')).not.toBeNull();
+ });
+
+ it('should return the user preferred theme by default', async () => {
+ const ssrMatchMedia = query => ({
+ matches: query === '(prefers-color-scheme: dark)' ? true : false,
+ });
+
+ render(
+
+
+
+
+
+ );
+ await screen.findByText('dark');
});
it('should return current theme when set', () => {
render(
-
-
+
);
expect(screen.getByLabelText('has-theme')).not.toBeNull();
+ expect(screen.queryByText('dark')).not.toBeNull();
});
it('should return theme from settings when available', () => {
- const { result: storeResult } = renderHook(() => useStore('theme'));
+ const { result: storeResult } = renderHook(() => useStore('theme'), {
+ wrapper: ({ children }) => (
+
+ {children}
+
+ ),
+ });
const [_, setTheme] = storeResult.current;
setTheme('dark');
- const { result: themeResult } = renderHook(() => useTheme());
+ const { result: themeResult } = renderHook(() => useTheme(), {
+ wrapper: ({ children }) => (
+
+ {children}
+
+ ),
+ });
const [theme, __] = themeResult.current;
expect(theme).toEqual('dark');
diff --git a/packages/ra-ui-materialui/src/theme/useTheme.ts b/packages/ra-ui-materialui/src/theme/useTheme.ts
index ec3ead4cdd7..72954324d49 100644
--- a/packages/ra-ui-materialui/src/theme/useTheme.ts
+++ b/packages/ra-ui-materialui/src/theme/useTheme.ts
@@ -1,5 +1,7 @@
import { useStore } from 'ra-core';
import { RaThemeOptions, ThemeType } from './types';
+import { useMediaQuery } from '@mui/material';
+import { useThemesContext } from './useThemesContext';
export type ThemeSetter = (theme: ThemeType | RaThemeOptions) => void;
@@ -23,7 +25,16 @@ export type ThemeSetter = (theme: ThemeType | RaThemeOptions) => void;
export const useTheme = (
type?: ThemeType | RaThemeOptions
): [ThemeType | RaThemeOptions, ThemeSetter] => {
+ const { darkTheme } = useThemesContext();
+ const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)', {
+ noSsr: true,
+ });
// FIXME: remove legacy mode in v5, and remove the RaThemeOptions type
- const [theme, setter] = useStore('theme', type);
- return [theme, setter];
+ const [theme, setter] = useStore(
+ 'theme',
+ type ?? (prefersDarkMode && darkTheme ? 'dark' : 'light')
+ );
+
+ // Ensure that even though the store has its value set to 'dark', we still use the light theme when no dark theme is available
+ return [darkTheme != null ? theme : 'light', setter];
};