From 05f1dfc6896d3affa7c494a1b955f230d836a4b7 Mon Sep 17 00:00:00 2001 From: Armand Philippot Date: Fri, 27 Oct 2023 18:07:45 +0200 Subject: feat: replace next-themes with a custom ThemeProvider To be honest, next-themes was working fine. However since I use a theme provider for Prism code blocks, some code is duplicated between this app and the library. So I prefer to use a custom Provider without the options I don't need. --- .storybook/preview.tsx | 14 ++-- package.json | 1 - .../organisms/forms/theme-toggle/theme-toggle.tsx | 15 +--- src/pages/_app.tsx | 7 +- src/pages/_document.tsx | 9 +++ src/utils/constants.ts | 1 + src/utils/helpers/index.ts | 1 + src/utils/helpers/themes.ts | 18 +++++ src/utils/hooks/index.ts | 3 + src/utils/hooks/use-match-media/index.ts | 1 + src/utils/hooks/use-match-media/use-match-media.ts | 17 +++++ src/utils/hooks/use-system-color-scheme/index.ts | 1 + .../use-system-color-scheme.ts | 24 ++++++ src/utils/hooks/use-theme/index.ts | 1 + src/utils/hooks/use-theme/use-theme.test.tsx | 82 ++++++++++++++++++++ src/utils/hooks/use-theme/use-theme.ts | 15 ++++ src/utils/providers/index.ts | 1 + src/utils/providers/theme-provider/index.ts | 1 + .../theme-provider/theme-provider.test.tsx | 78 +++++++++++++++++++ .../providers/theme-provider/theme-provider.tsx | 88 ++++++++++++++++++++++ tests/utils/index.tsx | 9 ++- yarn.lock | 5 -- 22 files changed, 361 insertions(+), 31 deletions(-) create mode 100644 src/utils/helpers/themes.ts create mode 100644 src/utils/hooks/use-match-media/index.ts create mode 100644 src/utils/hooks/use-match-media/use-match-media.ts create mode 100644 src/utils/hooks/use-system-color-scheme/index.ts create mode 100644 src/utils/hooks/use-system-color-scheme/use-system-color-scheme.ts create mode 100644 src/utils/hooks/use-theme/index.ts create mode 100644 src/utils/hooks/use-theme/use-theme.test.tsx create mode 100644 src/utils/hooks/use-theme/use-theme.ts create mode 100644 src/utils/providers/theme-provider/index.ts create mode 100644 src/utils/providers/theme-provider/theme-provider.test.tsx create mode 100644 src/utils/providers/theme-provider/theme-provider.tsx diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index f3f374f..d5cc8cc 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -1,13 +1,17 @@ import type { Decorator, Preview } from '@storybook/react'; -import { ThemeProvider, useTheme } from 'next-themes'; import { useDarkMode } from 'storybook-dark-mode'; import { FC, ReactNode, useEffect } from 'react'; import { IntlProvider } from 'react-intl'; -import { AckeeProvider, MotionProvider } from '../src/utils/providers'; +import { + AckeeProvider, + MotionProvider, + ThemeProvider, +} from '../src/utils/providers'; import '../src/styles/globals.scss'; import { DocsContainer } from './overrides/docs-container'; import dark from './themes/dark'; import light from './themes/light'; +import { useTheme } from '../src/utils/hooks'; type ThemeWrapperProps = { children: ReactNode; @@ -28,11 +32,7 @@ export const ThemeWrapper: FC = ({ children }) => { const withAllProviders: Decorator = (Story) => { return ( - + = (props) => { const intl = useIntl(); - const { resolvedTheme, setTheme } = useTheme(); + const { resolvedTheme, toggleTheme } = useTheme(); const isDarkTheme = resolvedTheme === 'dark'; - const updateTheme = useCallback( - (e: ChangeEvent) => { - setTheme(e.target.value === 'light' ? 'light' : 'dark'); - }, - [setTheme] - ); - const themeLabel = intl.formatMessage({ defaultMessage: 'Theme:', description: 'ThemeToggle: theme label', @@ -76,7 +69,7 @@ export const ThemeToggle: FC = (props) => { items={options} legend={{themeLabel}} name="theme" - onSwitch={updateTheme} + onSwitch={toggleTheme} value={isDarkTheme ? 'dark' : 'light'} /> ); diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index c332432..0c92c93 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,5 +1,4 @@ import { useRouter } from 'next/router'; -import { ThemeProvider } from 'next-themes'; import { IntlProvider } from 'react-intl'; import '../styles/globals.scss'; import type { AppPropsWithLayout } from '../types'; @@ -9,6 +8,7 @@ import { AckeeProvider, MotionProvider, PrismThemeProvider, + ThemeProvider, } from '../utils/providers'; const App = ({ Component, pageProps }: AppPropsWithLayout) => { @@ -34,9 +34,8 @@ const App = ({ Component, pageProps }: AppPropsWithLayout) => { messages={translation} > {getLayout(, {})} diff --git a/src/pages/_document.tsx b/src/pages/_document.tsx index 317d3af..7e3b5e6 100644 --- a/src/pages/_document.tsx +++ b/src/pages/_document.tsx @@ -16,6 +16,15 @@ export default function Document() { // eslint-disable-next-line react/jsx-no-literals strategy="beforeInteractive" /> +