* chore: switch theme to zustand, delete context * chore: switch apps/componenets to consume the hook * chore: update storybook theme usage to use documentElement * chore: dry up theme switcher listener for storybooks * feat: optional theme param to allow toggling * chore: add additional check for matchMedia function * chore: change block explorer test to use light theme as its the default * chore: remove unused headerprops for multisig-signer * chore: remove unused props from theme switcher component * chore: dry up validateTheme func * chore: remove unused props from explorer header test * chore: use new theme switcher in account history container
This commit is contained in:
parent
df609d442c
commit
0b4f918208
@ -1,7 +1,5 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { ThemeContext } from '@vegaprotocol/react-helpers';
|
||||
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
|
||||
import { EnvironmentProvider, NetworkLoader } from '@vegaprotocol/environment';
|
||||
import {
|
||||
VegaConnectDialog,
|
||||
@ -18,8 +16,7 @@ import useLocalValues from './hooks/use-local-values';
|
||||
import type { InMemoryCacheConfig } from '@apollo/client';
|
||||
|
||||
function App() {
|
||||
const [theme, toggleTheme] = useThemeSwitcher();
|
||||
const localValues = useLocalValues(theme, toggleTheme);
|
||||
const localValues = useLocalValues();
|
||||
const {
|
||||
vegaWalletDialog,
|
||||
menu: { setMenuOpen },
|
||||
@ -56,25 +53,23 @@ function App() {
|
||||
|
||||
return (
|
||||
<EnvironmentProvider>
|
||||
<ThemeContext.Provider value={theme}>
|
||||
<NetworkLoader cache={cacheConfig}>
|
||||
<VegaWalletProvider>
|
||||
<LocalContext.Provider value={localValues}>
|
||||
<AppLoader>
|
||||
<div className="max-h-full min-h-full dark:bg-lite-black dark:text-neutral-200 bg-white text-neutral-800 grid grid-rows-[min-content,1fr]">
|
||||
<Header />
|
||||
<Main />
|
||||
<VegaConnectDialog connectors={Connectors} />
|
||||
<VegaManageDialog
|
||||
dialogOpen={vegaWalletDialog.manage}
|
||||
setDialogOpen={vegaWalletDialog.setManage}
|
||||
/>
|
||||
</div>
|
||||
</AppLoader>
|
||||
</LocalContext.Provider>
|
||||
</VegaWalletProvider>
|
||||
</NetworkLoader>
|
||||
</ThemeContext.Provider>
|
||||
<NetworkLoader cache={cacheConfig}>
|
||||
<VegaWalletProvider>
|
||||
<LocalContext.Provider value={localValues}>
|
||||
<AppLoader>
|
||||
<div className="max-h-full min-h-full dark:bg-lite-black dark:text-neutral-200 bg-white text-neutral-800 grid grid-rows-[min-content,1fr]">
|
||||
<Header />
|
||||
<Main />
|
||||
<VegaConnectDialog connectors={Connectors} />
|
||||
<VegaManageDialog
|
||||
dialogOpen={vegaWalletDialog.manage}
|
||||
setDialogOpen={vegaWalletDialog.setManage}
|
||||
/>
|
||||
</div>
|
||||
</AppLoader>
|
||||
</LocalContext.Provider>
|
||||
</VegaWalletProvider>
|
||||
</NetworkLoader>
|
||||
</EnvironmentProvider>
|
||||
);
|
||||
}
|
||||
|
@ -1,14 +1,11 @@
|
||||
import React, {
|
||||
forwardRef,
|
||||
useCallback,
|
||||
useContext,
|
||||
useMemo,
|
||||
useRef,
|
||||
} from 'react';
|
||||
import React, { forwardRef, useCallback, useMemo, useRef } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit';
|
||||
import { ThemeContext, useScreenDimensions } from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
useScreenDimensions,
|
||||
useThemeSwitcher,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import type {
|
||||
GridOptions,
|
||||
GetRowIdParams,
|
||||
@ -43,7 +40,7 @@ const ConsoleLiteGrid = <T extends { id?: string }>(
|
||||
) => {
|
||||
const { isMobile, screenSize } = useScreenDimensions();
|
||||
const gridRef = useRef<AgGridReact | null>(null);
|
||||
const theme = useContext(ThemeContext);
|
||||
const { theme } = useThemeSwitcher();
|
||||
const handleOnGridReady = useCallback(() => {
|
||||
(
|
||||
(ref as React.RefObject<AgGridReact>) || gridRef
|
||||
|
@ -11,8 +11,6 @@ const Header = () => {
|
||||
}));
|
||||
const {
|
||||
vegaWalletDialog: { setManage },
|
||||
theme,
|
||||
toggleTheme,
|
||||
} = useContext(LocalContext);
|
||||
return (
|
||||
<div
|
||||
@ -25,7 +23,7 @@ const Header = () => {
|
||||
setConnectDialog={updateVegaWalletDialog}
|
||||
setManageDialog={setManage}
|
||||
/>
|
||||
<ThemeSwitcher theme={theme} onToggle={toggleTheme} className="-my-4" />
|
||||
<ThemeSwitcher className="-my-4" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -14,8 +14,6 @@ interface MenuState {
|
||||
export interface LocalValues {
|
||||
vegaWalletDialog: VegaWalletDialogState;
|
||||
menu: MenuState;
|
||||
theme: 'light' | 'dark';
|
||||
toggleTheme: () => void;
|
||||
}
|
||||
|
||||
const LocalContext = createContext<LocalValues>({} as LocalValues);
|
||||
|
@ -3,8 +3,7 @@ import useLocalValues from './use-local-values';
|
||||
|
||||
describe('local values hook', () => {
|
||||
it('state of wallet dialog should be properly handled', () => {
|
||||
const setTheme = jest.fn();
|
||||
const { result } = renderHook(() => useLocalValues('light', setTheme));
|
||||
const { result } = renderHook(() => useLocalValues());
|
||||
expect(result.current.vegaWalletDialog).toBeDefined();
|
||||
expect(result.current.vegaWalletDialog.manage).toBe(false);
|
||||
act(() => {
|
||||
|
@ -1,17 +1,15 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import type { LocalValues } from '../context/local-context';
|
||||
|
||||
const useLocalValues = (theme: 'light' | 'dark', toggleTheme: () => void) => {
|
||||
const useLocalValues = () => {
|
||||
const [manage, setManage] = useState<boolean>(false);
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
return useMemo<LocalValues>(
|
||||
() => ({
|
||||
vegaWalletDialog: { manage, setManage },
|
||||
menu: { menuOpen, setMenuOpen, onToggle: () => setMenuOpen(!menuOpen) },
|
||||
theme,
|
||||
toggleTheme,
|
||||
}),
|
||||
[manage, theme, toggleTheme, menuOpen]
|
||||
[manage, menuOpen]
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -3,7 +3,6 @@ import { useState, useEffect } from 'react';
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { BrowserTracing } from '@sentry/tracing';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { ThemeContext, useThemeSwitcher } from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
EnvironmentProvider,
|
||||
NetworkLoader,
|
||||
@ -19,7 +18,6 @@ import type { InMemoryCacheConfig } from '@apollo/client';
|
||||
|
||||
function App() {
|
||||
const { VEGA_ENV } = useEnvironment();
|
||||
const [theme, toggleTheme] = useThemeSwitcher();
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
|
||||
const location = useLocation();
|
||||
@ -57,25 +55,18 @@ function App() {
|
||||
);
|
||||
|
||||
return (
|
||||
<ThemeContext.Provider value={theme}>
|
||||
<TendermintWebsocketProvider>
|
||||
<NetworkLoader cache={cacheConfig}>
|
||||
<div className={layoutClasses}>
|
||||
<Header
|
||||
theme={theme}
|
||||
toggleTheme={toggleTheme}
|
||||
menuOpen={menuOpen}
|
||||
setMenuOpen={setMenuOpen}
|
||||
/>
|
||||
<Nav menuOpen={menuOpen} />
|
||||
<Main />
|
||||
<footer className="grid grid-rows-2 grid-cols-[1fr_auto] text-sm md:text-md md:flex md:col-span-2 p-4 gap-4 border-t border-neutral-700 dark:border-neutral-300">
|
||||
<NetworkInfo />
|
||||
</footer>
|
||||
</div>
|
||||
</NetworkLoader>
|
||||
</TendermintWebsocketProvider>
|
||||
</ThemeContext.Provider>
|
||||
<TendermintWebsocketProvider>
|
||||
<NetworkLoader cache={cacheConfig}>
|
||||
<div className={layoutClasses}>
|
||||
<Header menuOpen={menuOpen} setMenuOpen={setMenuOpen} />
|
||||
<Nav menuOpen={menuOpen} />
|
||||
<Main />
|
||||
<footer className="grid grid-rows-2 grid-cols-[1fr_auto] text-sm md:text-md md:flex md:col-span-2 p-4 gap-4 border-t border-neutral-700 dark:border-neutral-300">
|
||||
<NetworkInfo />
|
||||
</footer>
|
||||
</div>
|
||||
</NetworkLoader>
|
||||
</TendermintWebsocketProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -14,12 +14,7 @@ jest.mock('../search', () => ({
|
||||
|
||||
const renderComponent = () => (
|
||||
<MemoryRouter>
|
||||
<Header
|
||||
theme="dark"
|
||||
toggleTheme={jest.fn()}
|
||||
menuOpen={false}
|
||||
setMenuOpen={jest.fn()}
|
||||
/>
|
||||
<Header menuOpen={false} setMenuOpen={jest.fn()} />
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
|
@ -8,18 +8,11 @@ import type { Dispatch, SetStateAction } from 'react';
|
||||
import { NetworkSwitcher } from '@vegaprotocol/environment';
|
||||
|
||||
interface ThemeToggleProps {
|
||||
theme: 'light' | 'dark';
|
||||
toggleTheme: () => void;
|
||||
menuOpen: boolean;
|
||||
setMenuOpen: Dispatch<SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
export const Header = ({
|
||||
theme,
|
||||
toggleTheme,
|
||||
menuOpen,
|
||||
setMenuOpen,
|
||||
}: ThemeToggleProps) => {
|
||||
export const Header = ({ menuOpen, setMenuOpen }: ThemeToggleProps) => {
|
||||
const headerClasses = classnames(
|
||||
'md:col-span-2',
|
||||
'grid grid-rows-2 md:grid-rows-1 grid-cols-[1fr_auto] md:grid-cols-[auto_1fr_auto] items-center',
|
||||
@ -48,7 +41,7 @@ export const Header = ({
|
||||
<Icon name={menuOpen ? 'cross' : 'menu'} />
|
||||
</button>
|
||||
<Search />
|
||||
<ThemeSwitcher theme={theme} onToggle={toggleTheme} className="-my-4" />
|
||||
<ThemeSwitcher className="-my-4" />
|
||||
</header>
|
||||
);
|
||||
};
|
||||
|
@ -88,7 +88,7 @@ describe('NestedDataList', () => {
|
||||
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const item = getByTestId(`T${i}`);
|
||||
const expected = BORDER_COLOURS.dark[i % 5];
|
||||
const expected = BORDER_COLOURS.light[i % 5];
|
||||
expect(item.style.borderColor.toUpperCase()).toBe(expected);
|
||||
}
|
||||
});
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { useCallback, useContext, useMemo, useState } from 'react';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import isObject from 'lodash/isObject';
|
||||
import { ThemeContext } from '@vegaprotocol/react-helpers';
|
||||
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
|
||||
import { Icon } from '@vegaprotocol/ui-toolkit';
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import { VegaColours } from '@vegaprotocol/tailwindcss-config';
|
||||
@ -65,7 +65,7 @@ const NestedDataListItem = ({
|
||||
);
|
||||
const hasChildren = isObject(value) && !!Object.keys(value).length;
|
||||
const title = useMemo(() => camelToTitle(label), [label]);
|
||||
const theme = useContext(ThemeContext);
|
||||
const { theme } = useThemeSwitcher();
|
||||
const currentLevelBorder = useMemo(
|
||||
() => getBorderColour(index, theme),
|
||||
[index, theme]
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { StrictMode } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import { ThemeContext } from '@vegaprotocol/react-helpers';
|
||||
import { EnvironmentProvider, NetworkLoader } from '@vegaprotocol/environment';
|
||||
|
||||
import App from './app/app';
|
||||
@ -36,11 +35,9 @@ root?.render(
|
||||
<StrictMode>
|
||||
<BrowserRouter>
|
||||
<EnvironmentProvider>
|
||||
<ThemeContext.Provider value="light">
|
||||
<NetworkLoader cache={cache}>
|
||||
<App />
|
||||
</NetworkLoader>
|
||||
</ThemeContext.Provider>
|
||||
<NetworkLoader cache={cache}>
|
||||
<App />
|
||||
</NetworkLoader>
|
||||
</EnvironmentProvider>
|
||||
</BrowserRouter>
|
||||
</StrictMode>
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
import { AsyncRenderer, Button, Lozenge } from '@vegaprotocol/ui-toolkit';
|
||||
import type { EthereumConfig } from '@vegaprotocol/web3';
|
||||
import { useEthereumConfig, Web3Provider } from '@vegaprotocol/web3';
|
||||
import { ThemeContext, useThemeSwitcher, t } from '@vegaprotocol/react-helpers';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { ENV } from './config/env';
|
||||
import { ContractsProvider } from './config/contracts/contracts-provider';
|
||||
import {
|
||||
@ -55,7 +55,6 @@ function App() {
|
||||
const { VEGA_ENV, ETHEREUM_PROVIDER_URL } = useEnvironment();
|
||||
const { config, loading, error } = useEthereumConfig();
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [theme, toggleTheme] = useThemeSwitcher();
|
||||
|
||||
useEffect(() => {
|
||||
Sentry.init({
|
||||
@ -73,25 +72,23 @@ function App() {
|
||||
}, [config?.chain_id, ETHEREUM_PROVIDER_URL]);
|
||||
|
||||
return (
|
||||
<ThemeContext.Provider value={theme}>
|
||||
<AsyncRenderer loading={loading} data={config} error={error}>
|
||||
<Web3Provider connectors={Connectors}>
|
||||
<Web3Connector dialogOpen={dialogOpen} setDialogOpen={setDialogOpen}>
|
||||
<EthWalletContainer
|
||||
dialogOpen={dialogOpen}
|
||||
setDialogOpen={setDialogOpen}
|
||||
>
|
||||
<ContractsProvider>
|
||||
<div className={pageWrapperClasses}>
|
||||
<Header theme={theme} toggleTheme={toggleTheme} />
|
||||
<ConnectedApp config={config} />
|
||||
</div>
|
||||
</ContractsProvider>
|
||||
</EthWalletContainer>
|
||||
</Web3Connector>
|
||||
</Web3Provider>
|
||||
</AsyncRenderer>
|
||||
</ThemeContext.Provider>
|
||||
<AsyncRenderer loading={loading} data={config} error={error}>
|
||||
<Web3Provider connectors={Connectors}>
|
||||
<Web3Connector dialogOpen={dialogOpen} setDialogOpen={setDialogOpen}>
|
||||
<EthWalletContainer
|
||||
dialogOpen={dialogOpen}
|
||||
setDialogOpen={setDialogOpen}
|
||||
>
|
||||
<ContractsProvider>
|
||||
<div className={pageWrapperClasses}>
|
||||
<Header />
|
||||
<ConnectedApp config={config} />
|
||||
</div>
|
||||
</ContractsProvider>
|
||||
</EthWalletContainer>
|
||||
</Web3Connector>
|
||||
</Web3Provider>
|
||||
</AsyncRenderer>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -4,19 +4,14 @@ import {
|
||||
VegaLogo,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
interface HeaderProps {
|
||||
theme: 'light' | 'dark';
|
||||
toggleTheme: () => void;
|
||||
}
|
||||
|
||||
export const Header = ({ theme, toggleTheme }: HeaderProps) => {
|
||||
export const Header = () => {
|
||||
return (
|
||||
<header className="relative overflow-hidden py-2 mb-8">
|
||||
<BackgroundVideo />
|
||||
<div className="relative flex justify-center px-2 dark:bg-black bg-white">
|
||||
<div className="w-full max-w-3xl p-5 flex items-center justify-between">
|
||||
<VegaLogo />
|
||||
<ThemeSwitcher theme={theme} onToggle={toggleTheme} />
|
||||
<ThemeSwitcher />
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
@ -15,7 +15,6 @@
|
||||
<ul>
|
||||
<li><a href="./fonts.css">AlphaLyrae font</a></li>
|
||||
<li><a href="./favicon.ico">Favicon</a></li>
|
||||
<li><a href="./theme.js">Theme</a></li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,9 +0,0 @@
|
||||
(function () {
|
||||
var storedTheme = window.localStorage.getItem('theme');
|
||||
if (
|
||||
storedTheme === 'dark' ||
|
||||
(!storedTheme && window.matchMedia('(prefers-color-scheme: dark)').matches)
|
||||
) {
|
||||
document.documentElement.classList.add('dark');
|
||||
}
|
||||
})();
|
@ -1,23 +1,17 @@
|
||||
import { EnvironmentProvider, NetworkLoader } from '@vegaprotocol/environment';
|
||||
import { Header } from './components/header';
|
||||
import { StatsManager } from '@vegaprotocol/network-stats';
|
||||
import { ThemeContext } from '@vegaprotocol/react-helpers';
|
||||
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
|
||||
|
||||
function App() {
|
||||
const [theme, toggleTheme] = useThemeSwitcher();
|
||||
|
||||
return (
|
||||
<ThemeContext.Provider value={theme}>
|
||||
<NetworkLoader>
|
||||
<div className="w-screen min-h-screen grid pb-6 bg-white text-neutral-900 dark:bg-black dark:text-neutral-100">
|
||||
<div className="layout-grid w-screen justify-self-center">
|
||||
<Header theme={theme} toggleTheme={toggleTheme} />
|
||||
<StatsManager className="max-w-3xl px-6" />
|
||||
</div>
|
||||
<NetworkLoader>
|
||||
<div className="w-screen min-h-screen grid pb-6 bg-white text-neutral-900 dark:bg-black dark:text-neutral-100">
|
||||
<div className="layout-grid w-screen justify-self-center">
|
||||
<Header />
|
||||
<StatsManager className="max-w-3xl px-6" />
|
||||
</div>
|
||||
</NetworkLoader>
|
||||
</ThemeContext.Provider>
|
||||
</div>
|
||||
</NetworkLoader>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -4,12 +4,7 @@ import {
|
||||
ThemeSwitcher,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
interface ThemeToggleProps {
|
||||
theme: 'light' | 'dark';
|
||||
toggleTheme: () => void;
|
||||
}
|
||||
|
||||
export const Header = ({ theme, toggleTheme }: ThemeToggleProps) => {
|
||||
export const Header = () => {
|
||||
return (
|
||||
<header className="relative overflow-hidden py-2 mb-10 md:mb-16">
|
||||
<BackgroundVideo />
|
||||
@ -17,7 +12,7 @@ export const Header = ({ theme, toggleTheme }: ThemeToggleProps) => {
|
||||
<div className="relative flex justify-center px-2 dark:bg-black bg-white">
|
||||
<div className="w-full max-w-3xl p-5 flex items-center justify-between">
|
||||
<VegaLogo />
|
||||
<ThemeSwitcher theme={theme} onToggle={toggleTheme} />
|
||||
<ThemeSwitcher />
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
@ -2,12 +2,11 @@ import {
|
||||
addDecimal,
|
||||
fromNanoSeconds,
|
||||
t,
|
||||
ThemeContext,
|
||||
useThemeSwitcher,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import compact from 'lodash/compact';
|
||||
import type { ChangeEvent } from 'react';
|
||||
import { useContext } from 'react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import type { AccountHistoryQuery } from './__generated__/AccountHistory';
|
||||
import { useAccountsWithBalanceQuery } from './__generated__/AccountHistory';
|
||||
@ -209,7 +208,7 @@ export const AccountHistoryChart = ({
|
||||
accountType: Schema.AccountType;
|
||||
asset: AssetFieldsFragment;
|
||||
}) => {
|
||||
const theme = useContext(ThemeContext);
|
||||
const { theme } = useThemeSwitcher();
|
||||
const values: { cols: string[]; rows: [Date, ...number[]][] } | null =
|
||||
useMemo(() => {
|
||||
if (!data?.balanceChanges.edges.length) {
|
||||
|
@ -16,16 +16,10 @@ import {
|
||||
|
||||
type NavbarTheme = 'inherit' | 'dark' | 'yellow';
|
||||
interface NavbarProps {
|
||||
theme: 'light' | 'dark';
|
||||
toggleTheme: () => void;
|
||||
navbarTheme?: NavbarTheme;
|
||||
}
|
||||
|
||||
export const Navbar = ({
|
||||
theme,
|
||||
toggleTheme,
|
||||
navbarTheme = 'inherit',
|
||||
}: NavbarProps) => {
|
||||
export const Navbar = ({ navbarTheme = 'inherit' }: NavbarProps) => {
|
||||
const { VEGA_TOKEN_URL } = useEnvironment();
|
||||
const { marketId } = useGlobalStore((store) => ({
|
||||
marketId: store.marketId,
|
||||
@ -71,7 +65,7 @@ export const Navbar = ({
|
||||
</a>
|
||||
<div className="flex items-center gap-2 ml-auto">
|
||||
<VegaWalletConnectButton />
|
||||
<ThemeSwitcher theme={theme} onToggle={toggleTheme} />
|
||||
<ThemeSwitcher />
|
||||
</div>
|
||||
</Nav>
|
||||
);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import Head from 'next/head';
|
||||
import type { AppProps } from 'next/app';
|
||||
import { Navbar } from '../components/navbar';
|
||||
import { t, ThemeContext, useThemeSwitcher } from '@vegaprotocol/react-helpers';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
useEagerConnect as useVegaEagerConnect,
|
||||
VegaWalletProvider,
|
||||
@ -48,10 +48,9 @@ const Title = () => {
|
||||
function AppBody({ Component }: AppProps) {
|
||||
const location = useLocation();
|
||||
const { VEGA_ENV } = useEnvironment();
|
||||
const [theme, toggleTheme] = useThemeSwitcher();
|
||||
|
||||
return (
|
||||
<ThemeContext.Provider value={theme}>
|
||||
<>
|
||||
<Head>
|
||||
{/* Cannot use meta tags in _document.page.tsx see https://nextjs.org/docs/messages/no-document-viewport-meta */}
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
@ -62,8 +61,6 @@ function AppBody({ Component }: AppProps) {
|
||||
<Web3Provider>
|
||||
<div className="h-full relative dark:bg-black dark:text-white z-0 grid grid-rows-[min-content,1fr,min-content]">
|
||||
<Navbar
|
||||
theme={theme}
|
||||
toggleTheme={toggleTheme}
|
||||
navbarTheme={VEGA_ENV === Networks.TESTNET ? 'yellow' : 'dark'}
|
||||
/>
|
||||
<main data-testid={location.pathname}>
|
||||
@ -76,7 +73,7 @@ function AppBody({ Component }: AppProps) {
|
||||
</Web3Provider>
|
||||
</AppLoader>
|
||||
</VegaWalletProvider>
|
||||
</ThemeContext.Provider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -17,11 +17,6 @@ export default function Document() {
|
||||
type="image/x-icon"
|
||||
href="https://static.vega.xyz/favicon.ico"
|
||||
/>
|
||||
<script
|
||||
src="https://static.vega.xyz/theme.js"
|
||||
type="text/javascript"
|
||||
async
|
||||
/>
|
||||
{['1', 'true'].includes(process.env['NX_USE_ENV_OVERRIDES'] || '') ? (
|
||||
/* eslint-disable-next-line @next/next/no-sync-scripts */
|
||||
<script src="/assets/env-config.js" type="text/javascript" />
|
||||
|
@ -1,6 +1,5 @@
|
||||
import './styles.scss';
|
||||
import { ThemeContext } from '@vegaprotocol/react-helpers';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useStorybookThemeObserver } from '@vegaprotocol/react-helpers';
|
||||
export const parameters = {
|
||||
actions: { argTypesRegex: '^on[A-Z].*' },
|
||||
backgrounds: { disable: true },
|
||||
@ -14,36 +13,12 @@ export const parameters = {
|
||||
};
|
||||
|
||||
export const decorators = [
|
||||
(Story, context) => {
|
||||
// storybook-addon-themes doesn't seem to provide the current selected
|
||||
// theme in context, we need to provide it in JS as some components
|
||||
// rely on it for rendering
|
||||
const [theme, setTheme] = useState(context.parameters.themes.default);
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new MutationObserver((mutationList) => {
|
||||
if (mutationList.length) {
|
||||
const body = mutationList[0].target;
|
||||
if (body.classList.contains('dark')) {
|
||||
setTheme('dark');
|
||||
} else {
|
||||
setTheme('light');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(document.body, { attributes: true });
|
||||
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
};
|
||||
}, []);
|
||||
(Story) => {
|
||||
useStorybookThemeObserver();
|
||||
|
||||
return (
|
||||
<div style={{ width: '100%', height: 500 }}>
|
||||
<ThemeContext.Provider value={theme}>
|
||||
<Story />
|
||||
</ThemeContext.Provider>
|
||||
<Story />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
@ -13,9 +13,9 @@ import {
|
||||
} from 'pennant';
|
||||
import { VegaDataSource } from './data-source';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { useContext, useMemo, useState } from 'react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { ThemeContext } from '@vegaprotocol/react-helpers';
|
||||
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
@ -46,7 +46,7 @@ export const CandlesChartContainer = ({
|
||||
}: CandlesChartContainerProps) => {
|
||||
const client = useApolloClient();
|
||||
const { pubKey } = useVegaWallet();
|
||||
const theme = useContext(ThemeContext);
|
||||
const { theme } = useThemeSwitcher();
|
||||
|
||||
const [interval, setInterval] = useState<Interval>(Interval.I15M);
|
||||
const [chartType, setChartType] = useState<ChartType>(ChartType.CANDLE);
|
||||
|
@ -1,6 +1,5 @@
|
||||
import './styles.css';
|
||||
import { ThemeContext } from '@vegaprotocol/react-helpers';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useStorybookThemeObserver } from '@vegaprotocol/react-helpers';
|
||||
|
||||
export const parameters = {
|
||||
actions: { argTypesRegex: '^on[A-Z].*' },
|
||||
@ -15,36 +14,12 @@ export const parameters = {
|
||||
};
|
||||
|
||||
export const decorators = [
|
||||
(Story, context) => {
|
||||
// storybook-addon-themes doesnt seem to provide the current selected
|
||||
// theme in context, we need to provid it in JS as some components
|
||||
// rely on it for rendering
|
||||
const [theme, setTheme] = useState(context.parameters.themes.default);
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new MutationObserver((mutationList) => {
|
||||
if (mutationList.length) {
|
||||
const body = mutationList[0].target;
|
||||
if (body.classList.contains('dark')) {
|
||||
setTheme('dark');
|
||||
} else {
|
||||
setTheme('light');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(document.body, { attributes: true });
|
||||
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
};
|
||||
}, []);
|
||||
(Story) => {
|
||||
useStorybookThemeObserver();
|
||||
|
||||
return (
|
||||
<div style={{ width: '100%', height: 500 }}>
|
||||
<ThemeContext.Provider value={theme}>
|
||||
<Story />
|
||||
</ThemeContext.Provider>
|
||||
<Story />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
@ -4,18 +4,11 @@ import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
useDataProvider,
|
||||
addDecimal,
|
||||
ThemeContext,
|
||||
getNumberFormat,
|
||||
useThemeSwitcher,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import { marketDepthProvider } from './market-depth-provider';
|
||||
import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
useContext,
|
||||
} from 'react';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { marketDataProvider, marketProvider } from '@vegaprotocol/market-list';
|
||||
import type { MarketData } from '@vegaprotocol/market-list';
|
||||
import type {
|
||||
@ -35,7 +28,7 @@ const formatMidPrice = (midPrice: string, decimalPlaces: number) =>
|
||||
type DepthData = Pick<DepthChartProps, 'data' | 'midPrice'>;
|
||||
|
||||
export const DepthChartContainer = ({ marketId }: DepthChartManagerProps) => {
|
||||
const theme = useContext(ThemeContext);
|
||||
const { theme } = useThemeSwitcher();
|
||||
const variables = useMemo(() => ({ marketId }), [marketId]);
|
||||
const [depthData, setDepthData] = useState<DepthData | null>(null);
|
||||
const dataRef = useRef<DepthData | null>(null);
|
||||
|
@ -8,7 +8,6 @@ import {
|
||||
useState,
|
||||
useMemo,
|
||||
useCallback,
|
||||
useContext,
|
||||
Fragment,
|
||||
} from 'react';
|
||||
import classNames from 'classnames';
|
||||
@ -16,9 +15,9 @@ import classNames from 'classnames';
|
||||
import {
|
||||
addDecimalsFormatNumber,
|
||||
t,
|
||||
ThemeContext,
|
||||
useResizeObserver,
|
||||
formatNumberFixed,
|
||||
useThemeSwitcher,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { OrderbookRow } from './orderbook-row';
|
||||
@ -281,7 +280,7 @@ export const Orderbook = ({
|
||||
fillGaps: initialFillGaps,
|
||||
onResolutionChange,
|
||||
}: OrderbookProps) => {
|
||||
const theme = useContext(ThemeContext);
|
||||
const { theme } = useThemeSwitcher();
|
||||
const scrollElement = useRef<HTMLDivElement>(null);
|
||||
const rootElement = useRef<HTMLDivElement>(null);
|
||||
const gridElement = useRef<HTMLDivElement>(null);
|
||||
|
@ -1,6 +1,5 @@
|
||||
import './styles.scss';
|
||||
import { ThemeContext } from '@vegaprotocol/react-helpers';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useStorybookThemeObserver } from '@vegaprotocol/react-helpers';
|
||||
export const parameters = {
|
||||
actions: { argTypesRegex: '^on[A-Z].*' },
|
||||
backgrounds: { disable: true },
|
||||
@ -14,36 +13,12 @@ export const parameters = {
|
||||
};
|
||||
|
||||
export const decorators = [
|
||||
(Story, context) => {
|
||||
// storybook-addon-themes doesnt seem to provide the current selected
|
||||
// theme in context, we need to provid it in JS as some components
|
||||
// rely on it for rendering
|
||||
const [theme, setTheme] = useState(context.parameters.themes.default);
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new MutationObserver((mutationList) => {
|
||||
if (mutationList.length) {
|
||||
const body = mutationList[0].target;
|
||||
if (body.classList.contains('dark')) {
|
||||
setTheme('dark');
|
||||
} else {
|
||||
setTheme('light');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(document.body, { attributes: true });
|
||||
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
};
|
||||
}, []);
|
||||
(Story) => {
|
||||
useStorybookThemeObserver();
|
||||
|
||||
return (
|
||||
<div style={{ width: '100%', height: 500 }}>
|
||||
<ThemeContext.Provider value={theme}>
|
||||
<Story />
|
||||
</ThemeContext.Provider>
|
||||
<Story />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
@ -9,4 +9,5 @@ export * from './use-resize-observer';
|
||||
export * from './use-resize';
|
||||
export * from './use-screen-dimensions';
|
||||
export * from './use-theme-switcher';
|
||||
export * from './use-storybook-theme-observer';
|
||||
export * from './use-yesterday';
|
||||
|
25
libs/react-helpers/src/hooks/use-storybook-theme-observer.ts
Normal file
25
libs/react-helpers/src/hooks/use-storybook-theme-observer.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { useMutationObserver } from './use-mutation-observer';
|
||||
import { useThemeSwitcher } from './use-theme-switcher';
|
||||
|
||||
/**
|
||||
* Listens for theme classname changes on the body tag and applies the
|
||||
* same theme to the theme store. This is required as some components
|
||||
* (EG AgGrid) rely on the theme as a JS variable rather than purely a className
|
||||
*
|
||||
* Additionally storybook-addon-themes doesn't seem to provide the current selected
|
||||
* theme in context so we listen for changes on the body tag
|
||||
*/
|
||||
export const useStorybookThemeObserver = () => {
|
||||
const { setTheme } = useThemeSwitcher();
|
||||
|
||||
useMutationObserver(document.body, (mutationList) => {
|
||||
if (mutationList.length) {
|
||||
const body = mutationList[0].target as HTMLElement;
|
||||
if (body && body.classList.contains('dark')) {
|
||||
setTheme('dark');
|
||||
} else {
|
||||
setTheme('light');
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
@ -1,42 +1,77 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import create from 'zustand';
|
||||
import { LocalStorage } from '../lib/storage';
|
||||
|
||||
const darkTheme = 'dark';
|
||||
const lightTheme = 'light';
|
||||
type themeVariant = typeof darkTheme | typeof lightTheme;
|
||||
const THEME_STORAGE_KEY = 'theme';
|
||||
const Themes = {
|
||||
DARK: 'dark',
|
||||
LIGHT: 'light',
|
||||
} as const;
|
||||
|
||||
const darkThemeCssClass = darkTheme;
|
||||
type Theme = typeof Themes[keyof typeof Themes];
|
||||
|
||||
const getCurrentTheme = () => {
|
||||
const theme = LocalStorage.getItem('theme');
|
||||
if (
|
||||
theme === darkTheme ||
|
||||
(!theme &&
|
||||
typeof window !== 'undefined' &&
|
||||
window.matchMedia('(prefers-color-scheme: dark)').matches)
|
||||
) {
|
||||
return darkTheme;
|
||||
}
|
||||
return lightTheme;
|
||||
const validateTheme = (theme: string): theme is Theme => {
|
||||
if (Object.values(Themes).includes(theme as Theme)) return true;
|
||||
LocalStorage.removeItem(THEME_STORAGE_KEY);
|
||||
return false;
|
||||
};
|
||||
|
||||
const toggleTheme = () => {
|
||||
const theme = document.documentElement.classList.contains(darkThemeCssClass)
|
||||
? lightTheme
|
||||
: darkTheme;
|
||||
LocalStorage.setItem('theme', theme);
|
||||
const setThemeClassName = (theme: Theme) => {
|
||||
if (typeof window !== 'undefined') {
|
||||
if (theme === Themes.DARK) {
|
||||
document.documentElement.classList.add(Themes.DARK);
|
||||
} else {
|
||||
document.documentElement.classList.remove(Themes.DARK);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const getCurrentTheme = () => {
|
||||
const storedTheme = LocalStorage.getItem(THEME_STORAGE_KEY);
|
||||
|
||||
if (storedTheme && validateTheme(storedTheme)) {
|
||||
setThemeClassName(storedTheme);
|
||||
return storedTheme;
|
||||
}
|
||||
|
||||
const theme =
|
||||
typeof window !== 'undefined' &&
|
||||
typeof window.matchMedia === 'function' && // jest test environment matchMedia is undefined
|
||||
window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
? Themes.DARK
|
||||
: Themes.LIGHT;
|
||||
|
||||
setThemeClassName(theme);
|
||||
return theme;
|
||||
};
|
||||
|
||||
export function useThemeSwitcher(): [themeVariant, () => void] {
|
||||
const [theme, setTheme] = useState<themeVariant>(lightTheme);
|
||||
useEffect(() => setTheme(getCurrentTheme()), []);
|
||||
useEffect(() => {
|
||||
if (theme === darkTheme) {
|
||||
document.documentElement.classList.add(darkThemeCssClass);
|
||||
} else {
|
||||
document.documentElement.classList.remove(darkThemeCssClass);
|
||||
}
|
||||
}, [theme]);
|
||||
return [theme, () => setTheme(toggleTheme)];
|
||||
type ThemeStore = {
|
||||
theme: Theme;
|
||||
setTheme: (theme?: Theme) => void;
|
||||
};
|
||||
|
||||
const useThemeStore = create<ThemeStore>((set) => ({
|
||||
theme: getCurrentTheme(),
|
||||
setTheme: (newTheme) => {
|
||||
set((state) => {
|
||||
let theme: Theme =
|
||||
state.theme === Themes.LIGHT ? Themes.DARK : Themes.LIGHT;
|
||||
|
||||
if (newTheme) {
|
||||
theme = newTheme;
|
||||
}
|
||||
|
||||
LocalStorage.setItem(THEME_STORAGE_KEY, theme);
|
||||
|
||||
setThemeClassName(theme);
|
||||
|
||||
return {
|
||||
theme,
|
||||
};
|
||||
});
|
||||
},
|
||||
}));
|
||||
|
||||
export function useThemeSwitcher(): ThemeStore {
|
||||
const { theme, setTheme } = useThemeStore();
|
||||
return { theme, setTheme };
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
export * from './hooks';
|
||||
export * from './lib/context';
|
||||
export * from './lib/format';
|
||||
export * from './lib/generic-data-provider';
|
||||
export * from './lib/get-nodes';
|
||||
|
@ -1,3 +0,0 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export const ThemeContext = React.createContext<'light' | 'dark'>('dark');
|
@ -1 +0,0 @@
|
||||
export * from './context';
|
@ -1,4 +1,3 @@
|
||||
export * from './context';
|
||||
export * from './format';
|
||||
export * from './grid';
|
||||
export * from './storage';
|
||||
|
@ -1,3 +1,6 @@
|
||||
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
|
||||
import classNames from 'classnames';
|
||||
import { useEffect } from 'react';
|
||||
import '../src/styles.css';
|
||||
export const parameters = {
|
||||
actions: { argTypesRegex: '^on[A-Z].*' },
|
||||
@ -38,47 +41,50 @@ export const globalTypes = {
|
||||
},
|
||||
};
|
||||
|
||||
const StoryWrapper = ({ children, className, style }) => (
|
||||
<div style={style} className={className}>
|
||||
<div className="p-4">
|
||||
<div className="dark:bg-black dark:text-neutral-200 bg-white text-neutral-800">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
const StoryWrapper = ({ children, fill }) => {
|
||||
const classes = classNames(
|
||||
'p-4',
|
||||
'bg-white dark:bg-black',
|
||||
'text-neutral-800 dark:text-neutral-200',
|
||||
{
|
||||
'w-screen h-screen': fill,
|
||||
}
|
||||
);
|
||||
return <div className={classes}>{children}</div>;
|
||||
};
|
||||
|
||||
const lightThemeClasses = 'bg-white text-black';
|
||||
const darkThemeClasses = 'dark bg-black text-white';
|
||||
|
||||
const withTheme = (Story, context) => {
|
||||
const ThemeWrapper = (Story, context) => {
|
||||
const theme = context.parameters.theme || context.globals.theme;
|
||||
const storyClasses = `h-[100vh] w-[100vw] ${
|
||||
theme === 'dark' ? darkThemeClasses : lightThemeClasses
|
||||
}`;
|
||||
const { setTheme } = useThemeSwitcher();
|
||||
|
||||
document.body.classList.toggle('dark', theme === 'dark');
|
||||
useEffect(() => {
|
||||
// in side by side mode a 'dark' class on the html tag will interfere
|
||||
// making the light 'side' dark, so remove it in that case
|
||||
if (theme === 'sideBySide') {
|
||||
document.documentElement.classList.remove('dark');
|
||||
} else {
|
||||
setTheme(theme);
|
||||
}
|
||||
}, [setTheme, theme]);
|
||||
|
||||
return theme === 'sideBySide' ? (
|
||||
<>
|
||||
<div className={lightThemeClasses}>
|
||||
<div className="bg-white text-black">
|
||||
<StoryWrapper>
|
||||
<Story />
|
||||
</StoryWrapper>
|
||||
</div>
|
||||
<div className={darkThemeClasses}>
|
||||
<div className="dark bg-black text-white">
|
||||
<StoryWrapper>
|
||||
<Story />
|
||||
</StoryWrapper>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className={storyClasses}>
|
||||
<StoryWrapper>
|
||||
<Story />
|
||||
</StoryWrapper>
|
||||
</div>
|
||||
<StoryWrapper fill={true}>
|
||||
<Story />
|
||||
</StoryWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export const decorators = [withTheme];
|
||||
export const decorators = [ThemeWrapper];
|
||||
|
@ -1,9 +1,8 @@
|
||||
import type { ReactNode, FunctionComponent } from 'react';
|
||||
import { useContext } from 'react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import type { AgGridReactProps, AgReactUiProps } from 'ag-grid-react';
|
||||
import { AgGridReact } from 'ag-grid-react';
|
||||
import { ThemeContext } from '@vegaprotocol/react-helpers';
|
||||
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
|
||||
import 'ag-grid-community/dist/styles/ag-grid.css';
|
||||
|
||||
interface GridProps {
|
||||
@ -33,7 +32,7 @@ export const AgGridThemed = ({
|
||||
gridRef?: React.ForwardedRef<AgGridReact>;
|
||||
customThemeParams?: string;
|
||||
}) => {
|
||||
const theme = useContext(ThemeContext);
|
||||
const { theme } = useThemeSwitcher();
|
||||
const defaultProps = { rowHeight: 22, headerHeight: 22 };
|
||||
return (
|
||||
<div
|
||||
|
@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import type { AgGridReactProps, AgReactUiProps } from 'ag-grid-react';
|
||||
import { AgGridReact } from 'ag-grid-react';
|
||||
import { ThemeContext } from '@vegaprotocol/react-helpers';
|
||||
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
|
||||
import 'ag-grid-community/dist/styles/ag-grid.css';
|
||||
|
||||
const AgGridLightTheme = React.lazy(() =>
|
||||
@ -23,7 +23,7 @@ export const AgGridThemed = React.forwardRef<
|
||||
className?: string;
|
||||
}
|
||||
>(({ style, className, ...props }, ref) => {
|
||||
const theme = React.useContext(ThemeContext);
|
||||
const { theme } = useThemeSwitcher();
|
||||
return (
|
||||
<div
|
||||
className={`${className ?? ''} ${
|
||||
|
@ -4,14 +4,7 @@ import { ThemeSwitcher } from './theme-switcher';
|
||||
|
||||
describe('ThemeSwitcher', () => {
|
||||
it('should render successfully', () => {
|
||||
const { baseElement } = render(
|
||||
<ThemeSwitcher
|
||||
theme="dark"
|
||||
onToggle={() => {
|
||||
return;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
const { baseElement } = render(<ThemeSwitcher />);
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
@ -1,5 +1,4 @@
|
||||
import type { Story, Meta } from '@storybook/react';
|
||||
import { useState } from 'react';
|
||||
import { ThemeSwitcher } from './theme-switcher';
|
||||
|
||||
export default {
|
||||
@ -8,16 +7,9 @@ export default {
|
||||
} as Meta;
|
||||
|
||||
const Template: Story = () => {
|
||||
const [theme, setTheme] = useState<'light' | 'dark'>('light');
|
||||
return (
|
||||
<div className="p-4">
|
||||
<ThemeSwitcher
|
||||
theme={theme}
|
||||
onToggle={() => {
|
||||
setTheme((curr) => (curr === 'light' ? 'dark' : 'light'));
|
||||
document.body.classList.toggle('dark');
|
||||
}}
|
||||
/>
|
||||
<ThemeSwitcher />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -1,19 +1,12 @@
|
||||
import React from 'react';
|
||||
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
|
||||
import { SunIcon, MoonIcon } from './icons';
|
||||
|
||||
export const ThemeSwitcher = ({
|
||||
theme,
|
||||
onToggle,
|
||||
className,
|
||||
}: {
|
||||
theme: 'light' | 'dark';
|
||||
onToggle: () => void;
|
||||
className?: string;
|
||||
}) => {
|
||||
export const ThemeSwitcher = ({ className }: { className?: string }) => {
|
||||
const { theme, setTheme } = useThemeSwitcher();
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onToggle}
|
||||
onClick={() => setTheme()}
|
||||
className={className}
|
||||
data-testid="theme-switcher"
|
||||
>
|
||||
|
Loading…
Reference in New Issue
Block a user