Skip to content

Commit 77cbc27

Browse files
authored
feat(/lib/components/flowbite): remove usePreferences (#582)
* fix(flowbite themeprops): fix bug with user preferences overriding dark = false Found an issue where userpreferences when set to true overrides dark = false in theme props which is a confusing developer experience fix #581 * fix(theme): removed usePreferences Fixes bug with dark = false not working. Removed usePreferences because of potential legal issues due to GDPR BREAKING CHANGE: ThemeProps no longer includes usePreferences fix #565, fix #581 * refactor(theme): refactor useThemeMode Refactored useThemeMode to fetch context state * refactor(theme): improve readability improve readability and fix merge conflicts. Update documentation for ThemePage #565 * refactor(theme): remove === check for false to set light mode Since we're no longer using localStorage no need to hard check for false
1 parent e88cdab commit 77cbc27

File tree

5 files changed

+35
-60
lines changed

5 files changed

+35
-60
lines changed

src/docs/pages/ThemePage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ const ReadTheThemeSection: FC = () => {
246246
{`const theme = useTheme().theme.button; // -> { base: "..", color: { ... }, ... }`}
247247
</SyntaxHighlighter>
248248
<SyntaxHighlighter language="tsx" style={dracula}>
249-
{`const [mode, setMode, toggleMode] = useThemeMode(usePreferences); // -> ["light", ..]`}
249+
{`const [mode, setMode, toggleMode] = useThemeMode(); // -> ["light", ..]`}
250250
</SyntaxHighlighter>
251251
</Card>
252252
</section>

src/lib/components/DarkThemeToggle/DarkThemeToggle.spec.tsx

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,4 @@ describe('Dark theme toggle', () => {
1919
expect(screen.queryByLabelText('Currently light mode')).not.toBeInTheDocument();
2020
expect(screen.queryByLabelText('Currently dark mode')).toBeInTheDocument();
2121
});
22-
23-
it('should toggle the theme with `usePreferences` is false', async () => {
24-
const user = userEvent.setup();
25-
render(
26-
<Flowbite theme={{ usePreferences: false }}>
27-
<DarkThemeToggle />
28-
</Flowbite>,
29-
);
30-
31-
expect(screen.queryByLabelText('Currently light mode')).toBeInTheDocument();
32-
expect(screen.queryByLabelText('Currently dark mode')).not.toBeInTheDocument();
33-
34-
await user.tab();
35-
await user.keyboard('[Space]');
36-
37-
expect(screen.queryByLabelText('Currently light mode')).not.toBeInTheDocument();
38-
expect(screen.queryByLabelText('Currently dark mode')).toBeInTheDocument();
39-
});
4022
});

src/lib/components/Flowbite/Flowbite.spec.tsx

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
import { act, render } from '@testing-library/react';
2-
import { afterEach, describe, expect, it } from 'vitest';
2+
import { describe, expect, it } from 'vitest';
33
import { mergeDeep } from '../../helpers/mergeDeep';
44
import defaultTheme from '../../theme/default';
55
import { Flowbite, useTheme } from '../Flowbite';
66
import type { ThemeContextProps } from './ThemeContext';
77

8-
afterEach(() => {
9-
localStorage.removeItem('theme');
10-
});
11-
128
describe('Components / Flowbite', () => {
139
describe('hook / useTheme', () => {
1410
it('should return default values', () => {
@@ -49,19 +45,6 @@ describe('Components / Flowbite', () => {
4945
expect(theme).toEqual(mergedTheme);
5046
});
5147

52-
it('should return darkmode', () => {
53-
render(
54-
<Flowbite theme={{ dark: true }}>
55-
<TestComponent />
56-
</Flowbite>,
57-
);
58-
59-
const { mode } = context;
60-
61-
expect(mode).toBe('dark');
62-
expect(documentEl()).toHaveClass('dark');
63-
});
64-
6548
it('should toggle mode', () => {
6649
render(
6750
<Flowbite>
@@ -83,6 +66,19 @@ describe('Components / Flowbite', () => {
8366
expect(mode2).toBe('dark');
8467
expect(documentEl()).toHaveClass('dark');
8568
});
69+
70+
it('should return darkmode', () => {
71+
render(
72+
<Flowbite theme={{ dark: true }}>
73+
<TestComponent />
74+
</Flowbite>,
75+
);
76+
77+
const { mode } = context;
78+
79+
expect(mode).toBe('dark');
80+
expect(documentEl()).toHaveClass('dark');
81+
});
8682
});
8783
});
8884

src/lib/components/Flowbite/Flowbite.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { ThemeContext, useTheme, useThemeMode } from './ThemeContext';
1010
export interface ThemeProps {
1111
dark?: boolean;
1212
theme?: DeepPartial<FlowbiteTheme>;
13-
usePreferences?: boolean;
1413
}
1514

1615
interface FlowbiteProps extends HTMLAttributes<HTMLDivElement> {
@@ -19,8 +18,8 @@ interface FlowbiteProps extends HTMLAttributes<HTMLDivElement> {
1918
}
2019

2120
export const Flowbite: FC<FlowbiteProps> = ({ children, theme = {} }) => {
22-
const { theme: customTheme = {}, dark, usePreferences = true } = theme;
23-
const [mode, setMode, toggleMode] = useThemeMode(usePreferences);
21+
const { theme: customTheme = {}, dark } = theme;
22+
const [mode, setMode, toggleMode] = useThemeMode();
2423

2524
const mergedTheme = mergeDeep(defaultTheme, customTheme);
2625

@@ -33,6 +32,14 @@ export const Flowbite: FC<FlowbiteProps> = ({ children, theme = {} }) => {
3332
if (windowExists()) {
3433
document.documentElement.classList.add('dark');
3534
}
35+
} else {
36+
if (setMode != null) {
37+
setMode('light');
38+
}
39+
40+
if (windowExists()) {
41+
document.documentElement.classList.remove('dark');
42+
}
3643
}
3744
}, [dark, setMode]);
3845

src/lib/components/Flowbite/ThemeContext.tsx

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable react-hooks/rules-of-hooks */
12
import type { FC, ReactNode } from 'react';
23
import { createContext, useCallback, useContext, useEffect, useState } from 'react';
34
import windowExists from '../../helpers/window-exists';
@@ -29,20 +30,17 @@ export function useTheme(): ThemeContextProps {
2930
return useContext(ThemeContext);
3031
}
3132

32-
export const useThemeMode = (
33-
usePreferences: boolean,
34-
): [Mode, React.Dispatch<React.SetStateAction<Mode>>, () => void] => {
35-
const [mode, setModeState] = useState<Mode>('light');
36-
37-
const savePreference = (mode: Mode) => localStorage.setItem('theme', mode);
33+
export const useThemeMode = (): [Mode, React.Dispatch<React.SetStateAction<Mode>>, () => void] => {
3834
const userPreferenceIsDark = () => window.matchMedia?.('(prefers-color-scheme: dark)').matches;
39-
const toggleMode = () => {
35+
const getPrefersColorScheme = (): Mode => (userPreferenceIsDark() ? 'dark' : 'light');
36+
const onToggleMode = () => {
4037
const newMode = mode === 'dark' ? 'light' : 'dark';
4138
setMode(newMode);
4239
setModeState(newMode);
4340
};
41+
const { mode: contextMode, toggleMode = onToggleMode } = useContext(ThemeContext);
42+
const [mode, setModeState] = useState<Mode>(contextMode ? contextMode : getPrefersColorScheme());
4443
const setMode = useCallback((mode: Mode) => {
45-
savePreference(mode);
4644
if (!windowExists()) {
4745
return;
4846
}
@@ -56,19 +54,11 @@ export const useThemeMode = (
5654
}, []);
5755

5856
useEffect(() => {
59-
if (usePreferences) {
60-
const getPreference = (): Mode => (localStorage.getItem('theme') as Mode) || getPrefersColorScheme();
61-
const getPrefersColorScheme = (): Mode => (userPreferenceIsDark() ? 'dark' : 'light');
62-
63-
setModeState(getPreference());
64-
}
65-
}, [usePreferences]);
66-
67-
useEffect(() => {
68-
if (usePreferences) {
69-
setMode(mode);
57+
if (contextMode) {
58+
setMode(contextMode);
59+
setModeState(contextMode);
7060
}
71-
}, [mode, setMode, usePreferences]);
61+
}, [contextMode, setMode, setModeState]);
7262

7363
return [mode, setModeState, toggleMode];
7464
};

0 commit comments

Comments
 (0)