From b7a440132d834519c2ea63eb9e2b599cb0571154 Mon Sep 17 00:00:00 2001
From: Maciek
Date: Wed, 26 Apr 2023 17:17:23 +0200
Subject: [PATCH] feat(trading): make Sentry only after opt in (#3448)
---
apps/trading-e2e/src/integration/navbar.cy.ts | 4 +-
.../src/integration/settings.cy.ts | 45 +++++++++++++++++
apps/trading/.env.testnet | 2 +-
apps/trading/client-pages/settings/index.ts | 2 +
.../client-pages/settings/settings-button.tsx | 16 +++++++
.../client-pages/settings/settings.tsx | 45 +++++++++++++++++
apps/trading/components/navbar/navbar.tsx | 10 ++--
.../risk-notice-dialog.spec.tsx | 20 +++-----
.../welcome-dialog/risk-notice-dialog.tsx | 4 ++
.../telemetry-approval.spec.tsx | 19 ++++++++
.../welcome-dialog/telemetry-approval.tsx | 32 +++++++++++++
.../welcome-dialog/welcome-dialog.tsx | 28 +++++------
.../lib/hooks/use-telemetry-approval.spec.ts | 48 +++++++++++++++++++
.../lib/hooks/use-telemetry-approval.ts | 24 ++++++++++
apps/trading/pages/_app.page.tsx | 6 ++-
apps/trading/pages/client-router.tsx | 12 ++++-
apps/trading/sentry.client.config.js | 17 +++----
libs/react-helpers/src/hooks/use-logger.ts | 16 ++-----
.../components/divider/divider.stories.tsx | 41 ++++++++++++++++
.../src/components/divider/divider.tsx | 19 ++++++++
.../src/components/divider/index.ts | 1 +
libs/ui-toolkit/src/components/index.ts | 2 +
.../ui-toolkit/src/components/switch/index.ts | 1 +
.../src/components/switch/switch.stories.tsx | 31 ++++++++++++
.../src/components/switch/switch.tsx | 38 +++++++++++++++
.../theme-switcher/theme-switcher.tsx | 1 +
libs/utils/src/index.ts | 1 +
libs/utils/src/lib/sentry-utils.spec.ts | 26 ++++++++++
libs/utils/src/lib/sentry-utils.ts | 15 ++++++
libs/web3/src/lib/use-eager-connect.ts | 5 +-
package.json | 6 ++-
yarn.lock | 42 +++++++++++++---
32 files changed, 511 insertions(+), 68 deletions(-)
create mode 100644 apps/trading-e2e/src/integration/settings.cy.ts
create mode 100644 apps/trading/client-pages/settings/index.ts
create mode 100644 apps/trading/client-pages/settings/settings-button.tsx
create mode 100644 apps/trading/client-pages/settings/settings.tsx
create mode 100644 apps/trading/components/welcome-dialog/telemetry-approval.spec.tsx
create mode 100644 apps/trading/components/welcome-dialog/telemetry-approval.tsx
create mode 100644 apps/trading/lib/hooks/use-telemetry-approval.spec.ts
create mode 100644 apps/trading/lib/hooks/use-telemetry-approval.ts
create mode 100644 libs/ui-toolkit/src/components/divider/divider.stories.tsx
create mode 100644 libs/ui-toolkit/src/components/divider/divider.tsx
create mode 100644 libs/ui-toolkit/src/components/divider/index.ts
create mode 100644 libs/ui-toolkit/src/components/switch/index.ts
create mode 100644 libs/ui-toolkit/src/components/switch/switch.stories.tsx
create mode 100644 libs/ui-toolkit/src/components/switch/switch.tsx
create mode 100644 libs/utils/src/lib/sentry-utils.spec.ts
create mode 100644 libs/utils/src/lib/sentry-utils.ts
diff --git a/apps/trading-e2e/src/integration/navbar.cy.ts b/apps/trading-e2e/src/integration/navbar.cy.ts
index 6b04eecac..205c77f3a 100644
--- a/apps/trading-e2e/src/integration/navbar.cy.ts
+++ b/apps/trading-e2e/src/integration/navbar.cy.ts
@@ -32,7 +32,7 @@ describe('Navbar', { tags: '@smoke' }, () => {
});
it('Resources dropdown should be correctly rendered', () => {
- const resourceSelector = 'ul li:last-child';
+ const resourceSelector = 'ul li:contains(Resources)';
['Docs', 'Give Feedback'].forEach((text, index) => {
cy.get('nav').find(resourceSelector).contains('Resources').click();
cy.get('nav')
@@ -91,7 +91,7 @@ describe('Navbar', { tags: '@smoke' }, () => {
cy.getByTestId('button-menu-drawer').click();
cy.getByTestId('menu-drawer').should('be.visible');
cy.getByTestId('menu-drawer')
- .find('[data-testid="theme-switcher"]')
+ .find('[data-testid="Settings"]')
.should('be.visible');
cy.getByTestId('button-menu-drawer').click();
cy.getByTestId('menu-drawer').should('not.be.visible');
diff --git a/apps/trading-e2e/src/integration/settings.cy.ts b/apps/trading-e2e/src/integration/settings.cy.ts
new file mode 100644
index 000000000..8074fee0c
--- /dev/null
+++ b/apps/trading-e2e/src/integration/settings.cy.ts
@@ -0,0 +1,45 @@
+describe('Settings page', { tags: '@smoke' }, () => {
+ beforeEach(() => {
+ cy.clearLocalStorage().then(() => {
+ cy.mockTradingPage();
+ cy.mockSubscription();
+ cy.visit('/');
+ cy.get('[role=dialog]').within(() => {
+ cy.getByTestId('dialog-close').click();
+ });
+ cy.get('[aria-label="cog icon"]').click();
+ });
+ });
+ it('telemetry checkbox should work well', () => {
+ cy.location('hash').should('equal', '#/settings');
+ cy.getByTestId('telemetry-approval').should(
+ 'have.attr',
+ 'data-state',
+ 'unchecked'
+ );
+ cy.get('[for="telemetry-approval"]').click();
+ cy.getByTestId('telemetry-approval').should(
+ 'have.attr',
+ 'data-state',
+ 'checked'
+ );
+ cy.reload();
+ cy.getByTestId('telemetry-approval').should(
+ 'have.attr',
+ 'data-state',
+ 'checked'
+ );
+ cy.get('[for="telemetry-approval"]').click();
+ cy.getByTestId('telemetry-approval').should(
+ 'have.attr',
+ 'data-state',
+ 'unchecked'
+ );
+ cy.reload();
+ cy.getByTestId('telemetry-approval').should(
+ 'have.attr',
+ 'data-state',
+ 'unchecked'
+ );
+ });
+});
diff --git a/apps/trading/.env.testnet b/apps/trading/.env.testnet
index 64e584a9c..c4f8bc481 100644
--- a/apps/trading/.env.testnet
+++ b/apps/trading/.env.testnet
@@ -11,4 +11,4 @@ NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
-NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
\ No newline at end of file
+NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
diff --git a/apps/trading/client-pages/settings/index.ts b/apps/trading/client-pages/settings/index.ts
new file mode 100644
index 000000000..9b97e60b3
--- /dev/null
+++ b/apps/trading/client-pages/settings/index.ts
@@ -0,0 +1,2 @@
+export { Settings as default } from './settings';
+export { SettingsButton } from './settings-button';
diff --git a/apps/trading/client-pages/settings/settings-button.tsx b/apps/trading/client-pages/settings/settings-button.tsx
new file mode 100644
index 000000000..4f17c2601
--- /dev/null
+++ b/apps/trading/client-pages/settings/settings-button.tsx
@@ -0,0 +1,16 @@
+import { Icon, NavigationLink } from '@vegaprotocol/ui-toolkit';
+import { t } from '@vegaprotocol/i18n';
+import { Links, Routes } from '../../pages/client-router';
+import { COG } from '@blueprintjs/icons/src/generated/iconNames';
+
+export const SettingsButton = ({ withMobile }: { withMobile?: boolean }) => {
+ return (
+
+ {withMobile ? (
+ t('Settings')
+ ) : (
+
+ )}
+
+ );
+};
diff --git a/apps/trading/client-pages/settings/settings.tsx b/apps/trading/client-pages/settings/settings.tsx
new file mode 100644
index 000000000..cb14ffde2
--- /dev/null
+++ b/apps/trading/client-pages/settings/settings.tsx
@@ -0,0 +1,45 @@
+import { t } from '@vegaprotocol/i18n';
+import { TelemetryApproval } from '../../components/welcome-dialog/telemetry-approval';
+import {
+ Divider,
+ RoundedWrapper,
+ Switch,
+ ThemeSwitcher,
+} from '@vegaprotocol/ui-toolkit';
+import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
+
+export const Settings = () => {
+ const { theme, setTheme } = useThemeSwitcher();
+ const text = t(theme === 'dark' ? 'Light mode' : 'Dark mode');
+ return (
+
+
+
+ {t('Settings')}
+
+
+ {t('Changes are applied automatically.')}
+
+
+
+
+
+
+
+ {text}
+
+
+
setTheme()}
+ checked={theme === 'dark'}
+ />
+
+
+
+
+
+
+
+ );
+};
diff --git a/apps/trading/components/navbar/navbar.tsx b/apps/trading/components/navbar/navbar.tsx
index f4a36e2af..ccbd32b91 100644
--- a/apps/trading/components/navbar/navbar.tsx
+++ b/apps/trading/components/navbar/navbar.tsx
@@ -10,7 +10,6 @@ import { t } from '@vegaprotocol/i18n';
import { useGlobalStore } from '../../stores';
import { VegaWalletConnectButton } from '../vega-wallet-connect-button';
import {
- ThemeSwitcher,
Navigation,
NavigationList,
NavigationItem,
@@ -24,6 +23,7 @@ import {
import { Links, Routes } from '../../pages/client-router';
import { createDocsLinks } from '@vegaprotocol/utils';
+import { SettingsButton } from '../../client-pages/settings';
export const Navbar = ({
theme = 'system',
@@ -45,7 +45,7 @@ export const Navbar = ({
theme={theme}
actions={
<>
-
+
>
}
@@ -108,15 +108,15 @@ export const Navbar = ({
)}
-
-
+
+
diff --git a/apps/trading/components/welcome-dialog/risk-notice-dialog.spec.tsx b/apps/trading/components/welcome-dialog/risk-notice-dialog.spec.tsx
index bf29c411a..9b476130b 100644
--- a/apps/trading/components/welcome-dialog/risk-notice-dialog.spec.tsx
+++ b/apps/trading/components/welcome-dialog/risk-notice-dialog.spec.tsx
@@ -21,12 +21,12 @@ describe('Risk notice dialog', () => {
});
it.each`
- assertion | network
- ${'displays'} | ${Networks.MAINNET}
- ${'does not display'} | ${Networks.CUSTOM}
- ${'does not display'} | ${Networks.DEVNET}
- ${'does not display'} | ${Networks.STAGNET3}
- ${'does not display'} | ${Networks.TESTNET}
+ assertion | network
+ ${'displays'} | ${Networks.MAINNET}
+ ${'displays'} | ${Networks.CUSTOM}
+ ${'displays'} | ${Networks.DEVNET}
+ ${'displays'} | ${Networks.STAGNET3}
+ ${'displays'} | ${Networks.TESTNET}
`(
'$assertion the risk notice on $network',
async ({ assertion, network }) => {
@@ -43,13 +43,7 @@ describe('Risk notice dialog', () => {
{ wrapper: BrowserRouter }
);
- if (assertion === 'displays') {
- // eslint-disable-next-line jest/no-conditional-expect
- expect(screen.queryByText(introText)).toBeInTheDocument();
- } else {
- // eslint-disable-next-line jest/no-conditional-expect
- expect(screen.queryByText(introText)).not.toBeInTheDocument();
- }
+ expect(screen.queryByText(introText)).toBeInTheDocument();
}
);
diff --git a/apps/trading/components/welcome-dialog/risk-notice-dialog.tsx b/apps/trading/components/welcome-dialog/risk-notice-dialog.tsx
index 650d59da7..6c5531b2b 100644
--- a/apps/trading/components/welcome-dialog/risk-notice-dialog.tsx
+++ b/apps/trading/components/welcome-dialog/risk-notice-dialog.tsx
@@ -3,6 +3,7 @@ import { t } from '@vegaprotocol/i18n';
import { Button } from '@vegaprotocol/ui-toolkit';
import { LocalStorage } from '@vegaprotocol/utils';
import { RISK_ACCEPTED_KEY } from '../constants';
+import { TelemetryApproval } from './telemetry-approval';
interface Props {
onClose: () => void;
@@ -32,6 +33,9 @@ export const RiskNoticeDialog = ({ onClose }: Props) => {
)}
{t('I understand, Continue')}
+
+
+
>
);
};
diff --git a/apps/trading/components/welcome-dialog/telemetry-approval.spec.tsx b/apps/trading/components/welcome-dialog/telemetry-approval.spec.tsx
new file mode 100644
index 000000000..dc5ff93a9
--- /dev/null
+++ b/apps/trading/components/welcome-dialog/telemetry-approval.spec.tsx
@@ -0,0 +1,19 @@
+import { render, screen, act } from '@testing-library/react';
+import { TelemetryApproval } from './telemetry-approval';
+
+describe('TelemetryApproval', () => {
+ it('click on checkbox should be properly handled', () => {
+ render( );
+ expect(screen.getByRole('checkbox')).toHaveAttribute(
+ 'data-state',
+ 'unchecked'
+ );
+ act(() => {
+ screen.getByRole('checkbox').click();
+ });
+ expect(screen.getByRole('checkbox')).toHaveAttribute(
+ 'data-state',
+ 'checked'
+ );
+ });
+});
diff --git a/apps/trading/components/welcome-dialog/telemetry-approval.tsx b/apps/trading/components/welcome-dialog/telemetry-approval.tsx
new file mode 100644
index 000000000..bdc648d4b
--- /dev/null
+++ b/apps/trading/components/welcome-dialog/telemetry-approval.tsx
@@ -0,0 +1,32 @@
+import { Checkbox } from '@vegaprotocol/ui-toolkit';
+import { t } from '@vegaprotocol/i18n';
+import { useTelemetryApproval } from '../../lib/hooks/use-telemetry-approval';
+
+export const TelemetryApproval = ({
+ isSettingsPage,
+}: {
+ isSettingsPage?: boolean;
+}) => {
+ const [isApproved, setIsApproved] = useTelemetryApproval();
+ return (
+
+
+ {t('Share usage data')}}
+ checked={isApproved}
+ name="telemetry-approval"
+ onCheckedChange={() => setIsApproved(!isApproved)}
+ />
+
+
+
+ {t(
+ 'Help identify bugs and improve the service by sharing anonymous usage data.'
+ )}
+ {!isSettingsPage &&
+ ' ' + t('You can change this in your settings at any time.')}
+
+
+
+ );
+};
diff --git a/apps/trading/components/welcome-dialog/welcome-dialog.tsx b/apps/trading/components/welcome-dialog/welcome-dialog.tsx
index c3faf3dc6..722f02c1a 100644
--- a/apps/trading/components/welcome-dialog/welcome-dialog.tsx
+++ b/apps/trading/components/welcome-dialog/welcome-dialog.tsx
@@ -4,7 +4,6 @@ import { Dialog } from '@vegaprotocol/ui-toolkit';
import { t } from '@vegaprotocol/i18n';
import { useDataProvider, useLocalStorage } from '@vegaprotocol/react-helpers';
import { activeMarketsProvider } from '@vegaprotocol/market-list';
-import { useEnvironment, Networks } from '@vegaprotocol/environment';
import * as constants from '../constants';
import { RiskNoticeDialog } from './risk-notice-dialog';
import { WelcomeNoticeDialog } from './welcome-notice-dialog';
@@ -13,37 +12,38 @@ import { useGlobalStore } from '../../stores';
export const WelcomeDialog = () => {
const { pathname } = useLocation();
- const { VEGA_ENV } = useEnvironment();
- let dialogContent: React.ReactNode = null;
+ let dialogContent: React.ReactNode;
let title = '';
let size: 'small' | 'medium' = 'small';
+ let onClose: ((open: boolean) => void) | undefined = undefined;
const [riskAccepted] = useLocalStorage(constants.RISK_ACCEPTED_KEY);
const { data } = useDataProvider({
dataProvider: activeMarketsProvider,
variables: undefined,
});
- const { update, shouldDisplayWelcomeDialog } = useGlobalStore((store) => ({
- update: store.update,
- shouldDisplayWelcomeDialog: store.shouldDisplayWelcomeDialog,
- }));
- const isRiskDialogNeeded =
- riskAccepted !== 'true' && VEGA_ENV === Networks.MAINNET;
+ const update = useGlobalStore((store) => store.update);
+ const shouldDisplayWelcomeDialog = useGlobalStore(
+ (store) => store.shouldDisplayWelcomeDialog
+ );
+ const isRiskDialogNeeded = riskAccepted !== 'true' && !('Cypress' in window);
const isWelcomeDialogNeeded = pathname === '/' || shouldDisplayWelcomeDialog;
- const onClose = useCallback(() => {
+ const onCloseDialog = useCallback(() => {
update({ shouldDisplayWelcomeDialog: isRiskDialogNeeded });
- // eslint-disable-next-line react-hooks/exhaustive-deps
- dialogContent = null;
}, [update, isRiskDialogNeeded]);
if (isRiskDialogNeeded) {
- dialogContent = ;
+ dialogContent = ;
title = t('WARNING');
size = 'medium';
} else if (isWelcomeDialogNeeded && data?.length === 0) {
dialogContent = ;
+ onClose = onCloseDialog;
} else if (isWelcomeDialogNeeded && (data?.length || 0) > 0) {
- dialogContent = ;
+ dialogContent = ;
+ onClose = onCloseDialog;
+ } else {
+ dialogContent = null as React.ReactNode;
}
return (
diff --git a/apps/trading/lib/hooks/use-telemetry-approval.spec.ts b/apps/trading/lib/hooks/use-telemetry-approval.spec.ts
new file mode 100644
index 000000000..6a4c9f00d
--- /dev/null
+++ b/apps/trading/lib/hooks/use-telemetry-approval.spec.ts
@@ -0,0 +1,48 @@
+import { renderHook, act, waitFor } from '@testing-library/react';
+import { useLocalStorage } from '@vegaprotocol/react-helpers';
+import { SentryInit, SentryClose } from '@vegaprotocol/utils';
+import { STORAGE_KEY, useTelemetryApproval } from './use-telemetry-approval';
+
+const mockSetValue = jest.fn();
+const mockRemoveValue = jest.fn();
+jest.mock('@vegaprotocol/utils');
+jest.mock('@vegaprotocol/react-helpers', () => ({
+ ...jest.requireActual('@vegaprotocol/react-helpers'),
+ useLocalStorage: jest
+ .fn()
+ .mockImplementation(() => [false, mockSetValue, mockRemoveValue]),
+}));
+
+describe('useTelemetryApproval', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+ it('hook should return proper array', () => {
+ const res = renderHook(() => useTelemetryApproval());
+ expect(res.result.current[0]).toEqual(false);
+ expect(res.result.current[1]).toEqual(expect.any(Function));
+ expect(useLocalStorage).toHaveBeenCalledWith(STORAGE_KEY);
+ });
+
+ it('hook should init stuff properly', async () => {
+ const res = renderHook(() => useTelemetryApproval());
+ await act(() => {
+ res.result.current[1](true);
+ });
+ await waitFor(() => {
+ expect(SentryInit).toHaveBeenCalled();
+ expect(mockSetValue).toHaveBeenCalledWith('1');
+ });
+ });
+
+ it('hook should close stuff properly', async () => {
+ const res = renderHook(() => useTelemetryApproval());
+ await act(() => {
+ res.result.current[1](false);
+ });
+ await waitFor(() => {
+ expect(SentryClose).toHaveBeenCalled();
+ expect(mockRemoveValue).toHaveBeenCalledWith();
+ });
+ });
+});
diff --git a/apps/trading/lib/hooks/use-telemetry-approval.ts b/apps/trading/lib/hooks/use-telemetry-approval.ts
new file mode 100644
index 000000000..21e6a2469
--- /dev/null
+++ b/apps/trading/lib/hooks/use-telemetry-approval.ts
@@ -0,0 +1,24 @@
+import { useLocalStorage } from '@vegaprotocol/react-helpers';
+import { useCallback } from 'react';
+import { SentryInit, SentryClose } from '@vegaprotocol/utils';
+import { ENV } from '../config';
+export const STORAGE_KEY = 'vega_telemetry_approval';
+
+export const useTelemetryApproval = (): [
+ value: boolean,
+ setValue: (value: boolean) => void
+] => {
+ const [value, setValue, removeValue] = useLocalStorage(STORAGE_KEY);
+ const setApprove = useCallback(
+ (value: boolean) => {
+ if (value) {
+ SentryInit(ENV.dsn, ENV.envName);
+ return setValue('1');
+ }
+ SentryClose();
+ removeValue();
+ },
+ [setValue, removeValue]
+ );
+ return [Boolean(value), setApprove];
+};
diff --git a/apps/trading/pages/_app.page.tsx b/apps/trading/pages/_app.page.tsx
index cfd815d31..3996631d3 100644
--- a/apps/trading/pages/_app.page.tsx
+++ b/apps/trading/pages/_app.page.tsx
@@ -36,6 +36,7 @@ import { Navbar } from '../components/navbar';
import { ENV } from '../lib/config';
import { useDataProvider } from '@vegaprotocol/react-helpers';
import { activeOrdersProvider } from '@vegaprotocol/orders';
+import { useTelemetryApproval } from '../lib/hooks/use-telemetry-approval';
const DEFAULT_TITLE = t('Welcome to Vega trading!');
@@ -145,7 +146,10 @@ const PartyData = () => {
const MaybeConnectEagerly = () => {
useVegaEagerConnect(Connectors);
- useEthereumEagerConnect(ENV.dsn);
+ const [isTelemetryApproved] = useTelemetryApproval();
+ useEthereumEagerConnect(
+ isTelemetryApproved ? { dsn: ENV.dsn, env: ENV.envName } : {}
+ );
const { pubKey, connect } = useVegaWallet();
const [searchParams] = useSearchParams();
diff --git a/apps/trading/pages/client-router.tsx b/apps/trading/pages/client-router.tsx
index e5a4659b6..4180df127 100644
--- a/apps/trading/pages/client-router.tsx
+++ b/apps/trading/pages/client-router.tsx
@@ -26,12 +26,17 @@ const LazyPortfolio = dynamic(() => import('../client-pages/portfolio'), {
ssr: false,
});
+const LazySettings = dynamic(() => import('../client-pages/settings'), {
+ ssr: false,
+});
+
export enum Routes {
HOME = '/',
MARKET = '/markets',
MARKETS = '/markets/all',
PORTFOLIO = '/portfolio',
- LIQUIDITY = '/liquidity',
+ LIQUIDITY = 'liquidity/:marketId',
+ SETTINGS = 'settings',
}
type ConsoleLinks = { [r in Routes]: (...args: string[]) => string };
@@ -45,6 +50,7 @@ export const Links: ConsoleLinks = {
marketId
? trimEnd(`${Routes.LIQUIDITY}/${marketId}`, '/')
: Routes.LIQUIDITY,
+ [Routes.SETTINGS]: () => Routes.SETTINGS,
};
const routerConfig: RouteObject[] = [
@@ -87,6 +93,10 @@ const routerConfig: RouteObject[] = [
path: Routes.PORTFOLIO,
element: ,
},
+ {
+ path: Routes.SETTINGS,
+ element: ,
+ },
{
path: '*',
element: (
diff --git a/apps/trading/sentry.client.config.js b/apps/trading/sentry.client.config.js
index 6724c2dd4..7b5a79bb5 100644
--- a/apps/trading/sentry.client.config.js
+++ b/apps/trading/sentry.client.config.js
@@ -1,14 +1,9 @@
-import * as Sentry from '@sentry/nextjs';
-import { BrowserTracing } from '@sentry/tracing';
import { ENV } from './lib/config/env';
+import { LocalStorage, SentryInit } from '@vegaprotocol/utils';
+import { STORAGE_KEY } from './lib/hooks/use-telemetry-approval';
-const { dsn } = ENV;
-
-if (dsn) {
- Sentry.init({
- dsn,
- integrations: [new BrowserTracing()],
- tracesSampleRate: 1,
- environment: ENV.envName,
- });
+const { dsn, envName } = ENV;
+const isTelemetryApproved = !!LocalStorage.getItem(STORAGE_KEY);
+if (dsn && isTelemetryApproved) {
+ SentryInit(dsn, envName);
}
diff --git a/libs/react-helpers/src/hooks/use-logger.ts b/libs/react-helpers/src/hooks/use-logger.ts
index cf62cf218..759d3f3b8 100644
--- a/libs/react-helpers/src/hooks/use-logger.ts
+++ b/libs/react-helpers/src/hooks/use-logger.ts
@@ -1,24 +1,18 @@
import { useRef } from 'react';
-import { BrowserTracing } from '@sentry/tracing';
-import * as Sentry from '@sentry/browser';
import type { LocalLogger, LoggerConf } from '@vegaprotocol/utils';
-import { localLoggerFactory } from '@vegaprotocol/utils';
+import { localLoggerFactory, SentryInit } from '@vegaprotocol/utils';
-interface Props extends LoggerConf {
+export interface LoggerProps extends LoggerConf {
dsn?: string;
+ env?: string;
}
-export const useLogger = ({ dsn, ...props }: Props) => {
+export const useLogger = ({ dsn, env, ...props }: LoggerProps) => {
const logger = useRef(null);
if (!logger.current) {
logger.current = localLoggerFactory(props);
if (dsn) {
- Sentry.init({
- dsn,
- integrations: [new BrowserTracing()],
- tracesSampleRate: 1,
- defaultIntegrations: false,
- });
+ SentryInit(dsn, env);
}
}
return logger.current;
diff --git a/libs/ui-toolkit/src/components/divider/divider.stories.tsx b/libs/ui-toolkit/src/components/divider/divider.stories.tsx
new file mode 100644
index 000000000..069272530
--- /dev/null
+++ b/libs/ui-toolkit/src/components/divider/divider.stories.tsx
@@ -0,0 +1,41 @@
+import type { ComponentStory, ComponentMeta } from '@storybook/react';
+import className from 'classnames';
+import { Divider } from './divider';
+
+export default {
+ title: 'Divider',
+ component: Divider,
+} as ComponentMeta;
+
+const Template: ComponentStory = (args) => {
+ return (
+
+ );
+};
+
+export const Default = Template.bind({});
+Default.args = {};
+
+export const Vertical = Template.bind({});
+Vertical.args = {
+ orientation: 'vertical',
+};
diff --git a/libs/ui-toolkit/src/components/divider/divider.tsx b/libs/ui-toolkit/src/components/divider/divider.tsx
new file mode 100644
index 000000000..136cc3d46
--- /dev/null
+++ b/libs/ui-toolkit/src/components/divider/divider.tsx
@@ -0,0 +1,19 @@
+import { forwardRef } from 'react';
+import * as Separator from '@radix-ui/react-separator';
+
+interface DividerProps {
+ orientation?: 'horizontal' | 'vertical';
+ decorative?: boolean;
+}
+
+export const Divider = forwardRef(
+ ({ orientation = 'horizontal', decorative }, ref) => {
+ return (
+
+ );
+ }
+);
diff --git a/libs/ui-toolkit/src/components/divider/index.ts b/libs/ui-toolkit/src/components/divider/index.ts
new file mode 100644
index 000000000..0dd50b568
--- /dev/null
+++ b/libs/ui-toolkit/src/components/divider/index.ts
@@ -0,0 +1 @@
+export { Divider } from './divider';
diff --git a/libs/ui-toolkit/src/components/index.ts b/libs/ui-toolkit/src/components/index.ts
index c2ef46544..2e2fb84fc 100644
--- a/libs/ui-toolkit/src/components/index.ts
+++ b/libs/ui-toolkit/src/components/index.ts
@@ -9,6 +9,7 @@ export * from './callout';
export * from './checkbox';
export * from './copy-with-tooltip';
export * from './dialog';
+export * from './divider';
export * from './drawer';
export * from './dropdown-menu';
export * from './form-group';
@@ -37,6 +38,7 @@ export * from './simple-grid';
export * from './slider';
export * from './sparkline';
export * from './splash';
+export * from './switch';
export * from './syntax-highlighter';
export * from './tabs';
export * from './text-area';
diff --git a/libs/ui-toolkit/src/components/switch/index.ts b/libs/ui-toolkit/src/components/switch/index.ts
new file mode 100644
index 000000000..675ddba51
--- /dev/null
+++ b/libs/ui-toolkit/src/components/switch/index.ts
@@ -0,0 +1 @@
+export { Switch } from './switch';
diff --git a/libs/ui-toolkit/src/components/switch/switch.stories.tsx b/libs/ui-toolkit/src/components/switch/switch.stories.tsx
new file mode 100644
index 000000000..f3c0f8289
--- /dev/null
+++ b/libs/ui-toolkit/src/components/switch/switch.stories.tsx
@@ -0,0 +1,31 @@
+import type { Story, ComponentMeta } from '@storybook/react';
+import type { SwitchProps } from './switch';
+import { Switch } from './switch';
+import { useState } from 'react';
+
+export default {
+ component: Switch,
+ title: 'Switch',
+} as ComponentMeta;
+
+const Template: Story = (args) => {
+ const [checked, setChecked] = useState(args.checked || false);
+ return (
+ setChecked(checked)}
+ />
+ );
+};
+
+export const Default = Template.bind({});
+Default.args = {
+ name: 'switch',
+};
+
+export const WithTextLabel = Template.bind({});
+WithTextLabel.args = {
+ name: 'switch',
+ labelText: 'Light mode',
+};
diff --git a/libs/ui-toolkit/src/components/switch/switch.tsx b/libs/ui-toolkit/src/components/switch/switch.tsx
new file mode 100644
index 000000000..f20a0daf6
--- /dev/null
+++ b/libs/ui-toolkit/src/components/switch/switch.tsx
@@ -0,0 +1,38 @@
+import type { ReactNode } from 'react';
+import { forwardRef } from 'react';
+import * as RootSwitch from '@radix-ui/react-switch';
+
+export interface SwitchProps {
+ name?: string;
+ onCheckedChange?: (checked: boolean) => void;
+ checked?: boolean;
+ disabled?: boolean;
+ labelText?: ReactNode | string;
+}
+
+export const Switch = forwardRef(
+ (
+ { name = 'switch', labelText, onCheckedChange, checked = false, disabled },
+ ref
+ ) => {
+ return (
+
+
+
+
+ {labelText && (
+
+ {labelText}
+
+ )}
+
+ );
+ }
+);
diff --git a/libs/ui-toolkit/src/components/theme-switcher/theme-switcher.tsx b/libs/ui-toolkit/src/components/theme-switcher/theme-switcher.tsx
index 6ca96ecc6..1b065f75e 100644
--- a/libs/ui-toolkit/src/components/theme-switcher/theme-switcher.tsx
+++ b/libs/ui-toolkit/src/components/theme-switcher/theme-switcher.tsx
@@ -17,6 +17,7 @@ export const ThemeSwitcher = ({
onClick={() => setTheme()}
className={className}
data-testid="theme-switcher"
+ id="theme-switcher"
>
{theme === 'dark' && }
{theme === 'light' && }
diff --git a/libs/utils/src/index.ts b/libs/utils/src/index.ts
index f099ed873..15f600113 100644
--- a/libs/utils/src/index.ts
+++ b/libs/utils/src/index.ts
@@ -17,3 +17,4 @@ export * from './lib/remove-0x';
export * from './lib/remove-pagination-wrapper';
export * from './lib/time';
export * from './lib/validate';
+export * from './lib/sentry-utils';
diff --git a/libs/utils/src/lib/sentry-utils.spec.ts b/libs/utils/src/lib/sentry-utils.spec.ts
new file mode 100644
index 000000000..731405620
--- /dev/null
+++ b/libs/utils/src/lib/sentry-utils.spec.ts
@@ -0,0 +1,26 @@
+import * as Sentry from '@sentry/nextjs';
+import { SentryInit, SentryClose } from './sentry-utils';
+
+jest.mock('@sentry/nextjs');
+
+describe('Sentry utlis', () => {
+ describe('SentryInit', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should initialize', () => {
+ SentryInit('sentry.dsn');
+ expect(Sentry.init).toHaveBeenCalled();
+ });
+ it('should do nothing', () => {
+ SentryInit('');
+ expect(Sentry.init).not.toHaveBeenCalled();
+ });
+
+ it('should close', () => {
+ SentryClose();
+ expect(Sentry.close).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/libs/utils/src/lib/sentry-utils.ts b/libs/utils/src/lib/sentry-utils.ts
new file mode 100644
index 000000000..a308bebb0
--- /dev/null
+++ b/libs/utils/src/lib/sentry-utils.ts
@@ -0,0 +1,15 @@
+import * as Sentry from '@sentry/nextjs';
+import { BrowserTracing } from '@sentry/tracing';
+
+export const SentryInit = (dsn: string, env?: string) => {
+ if (dsn) {
+ Sentry.init({
+ dsn,
+ integrations: [new BrowserTracing()],
+ tracesSampleRate: 1,
+ environment: env || '',
+ });
+ }
+};
+
+export const SentryClose = () => Sentry.close();
diff --git a/libs/web3/src/lib/use-eager-connect.ts b/libs/web3/src/lib/use-eager-connect.ts
index dd1c741ca..647d19e2e 100644
--- a/libs/web3/src/lib/use-eager-connect.ts
+++ b/libs/web3/src/lib/use-eager-connect.ts
@@ -1,3 +1,4 @@
+import type { LoggerProps } from '@vegaprotocol/react-helpers';
import { useLocalStorage, useLogger } from '@vegaprotocol/react-helpers';
import type { Web3ReactHooks } from '@web3-react/core';
import { MetaMask } from '@web3-react/metamask';
@@ -8,12 +9,12 @@ import { useWeb3ConnectStore } from './web3-connect-store';
export const ETHEREUM_EAGER_CONNECT = 'ethereum-eager-connect';
-export const useEagerConnect = (sentryDsn?: string) => {
+export const useEagerConnect = (loggerConf: LoggerProps) => {
const connectors = useWeb3ConnectStore((store) => store.connectors);
const [eagerConnector] = useLocalStorage(ETHEREUM_EAGER_CONNECT);
const attemptedRef = useRef(false);
- const logger = useLogger({ dsn: sentryDsn });
+ const logger = useLogger(loggerConf);
useEffect(() => {
if (attemptedRef.current || 'Cypress' in window) return;
diff --git a/package.json b/package.json
index 4dcb3db01..35ba287e7 100644
--- a/package.json
+++ b/package.json
@@ -31,7 +31,9 @@
"@radix-ui/react-popover": "^1.0.3",
"@radix-ui/react-radio-group": "^1.1.1",
"@radix-ui/react-select": "^1.2.0",
+ "@radix-ui/react-separator": "^1.0.2",
"@radix-ui/react-slider": "^1.1.0",
+ "@radix-ui/react-switch": "^1.0.2",
"@radix-ui/react-tabs": "^1.0.2",
"@radix-ui/react-tooltip": "^1.0.3",
"@sentry/nextjs": "^6.19.3",
@@ -202,6 +204,8 @@
"*.{ts,tsx,js,jsx}": "yarn eslint --fix"
},
"resolutions": {
- "graphql": "15.8.0"
+ "graphql": "15.8.0",
+ "//": "workaround storybook issue: https://github.com/storybookjs/storybook/issues/21642",
+ "@storybook/react-docgen-typescript-plugin": "1.0.6--canary.9.cd77847.0"
}
}
diff --git a/yarn.lock b/yarn.lock
index 3f70c1cf0..0bfb9d0d5 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4609,6 +4609,14 @@
"@babel/runtime" "^7.13.10"
"@radix-ui/react-slot" "1.0.1"
+"@radix-ui/react-primitive@1.0.2":
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-1.0.2.tgz#54e22f49ca59ba88d8143090276d50b93f8a7053"
+ integrity sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+ "@radix-ui/react-slot" "1.0.1"
+
"@radix-ui/react-radio-group@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-radio-group/-/react-radio-group-1.1.1.tgz#564549b3e0a5905367dfe9adfe7b0e245cbdb640"
@@ -4670,6 +4678,14 @@
aria-hidden "^1.1.1"
react-remove-scroll "2.5.5"
+"@radix-ui/react-separator@^1.0.2":
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-separator/-/react-separator-1.0.2.tgz#52417c8bfae1e4ef87356b2f7bffbc3dbbeddf23"
+ integrity sha512-lZoAG/rS2jzb/OSvyBrpN3dmikw20ewmWx1GkM1VldbDyD0DACCbH9LIXSrqyS/2mE1VYKOHmyq5W90Dx4ryqA==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+ "@radix-ui/react-primitive" "1.0.2"
+
"@radix-ui/react-slider@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-slider/-/react-slider-1.1.0.tgz#b3fdaca27619150e9e6067ad9f979a4535f68d5e"
@@ -4696,6 +4712,20 @@
"@babel/runtime" "^7.13.10"
"@radix-ui/react-compose-refs" "1.0.0"
+"@radix-ui/react-switch@^1.0.2":
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-switch/-/react-switch-1.0.2.tgz#e3d1b9fe18b6b1173aadc8b8e6efdc96a28a70f8"
+ integrity sha512-BcG/LKehxt36NXG0wPnoCitIfSMtU9Xo7BmythYA1PAMLtsMvW7kALfBzmduQoHTWcKr0AVcFyh0gChBUp9TiQ==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+ "@radix-ui/primitive" "1.0.0"
+ "@radix-ui/react-compose-refs" "1.0.0"
+ "@radix-ui/react-context" "1.0.0"
+ "@radix-ui/react-primitive" "1.0.2"
+ "@radix-ui/react-use-controllable-state" "1.0.0"
+ "@radix-ui/react-use-previous" "1.0.0"
+ "@radix-ui/react-use-size" "1.0.0"
+
"@radix-ui/react-tabs@^1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@radix-ui/react-tabs/-/react-tabs-1.0.2.tgz#8f5ec73ca41b151a413bdd6e00553408ff34ce07"
@@ -6266,17 +6296,17 @@
unfetch "^4.2.0"
util-deprecate "^1.0.2"
-"@storybook/react-docgen-typescript-plugin@1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0":
- version "1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0"
- resolved "https://registry.yarnpkg.com/@storybook/react-docgen-typescript-plugin/-/react-docgen-typescript-plugin-1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0.tgz#3103532ff494fb7dc3cf835f10740ecf6a26c0f9"
- integrity sha512-eVg3BxlOm2P+chijHBTByr90IZVUtgRW56qEOLX7xlww2NBuKrcavBlcmn+HH7GIUktquWkMPtvy6e0W0NgA5w==
+"@storybook/react-docgen-typescript-plugin@1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0", "@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.cd77847.0":
+ version "1.0.6--canary.9.cd77847.0"
+ resolved "https://registry.yarnpkg.com/@storybook/react-docgen-typescript-plugin/-/react-docgen-typescript-plugin-1.0.6--canary.9.cd77847.0.tgz#35beed1bd0813569fc8852b372c92069fe74a448"
+ integrity sha512-I4oBYmnUCX5IsrZhg+ST72dubSIV4wdwY+SfqJiJ3NHvDpdb240ZjdHAmjIy/yJh5rh42Fl4jbG8Tr4SzwV53Q==
dependencies:
debug "^4.1.1"
endent "^2.0.1"
find-cache-dir "^3.3.1"
flat-cache "^3.0.4"
micromatch "^4.0.2"
- react-docgen-typescript "^2.1.1"
+ react-docgen-typescript "^2.2.2"
tslib "^2.0.0"
"@storybook/react@6.5.10":
@@ -20701,7 +20731,7 @@ react-copy-to-clipboard@^5.0.4:
copy-to-clipboard "^3.3.1"
prop-types "^15.8.1"
-react-docgen-typescript@^2.1.1:
+react-docgen-typescript@^2.2.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/react-docgen-typescript/-/react-docgen-typescript-2.2.2.tgz#4611055e569edc071204aadb20e1c93e1ab1659c"
integrity sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg==