feat(trading): add negative oracle status banner (#3486)
This commit is contained in:
parent
75cb48a4b9
commit
6371199537
@ -7,7 +7,6 @@ import {
|
|||||||
useThrottledDataProvider,
|
useThrottledDataProvider,
|
||||||
} from '@vegaprotocol/react-helpers';
|
} from '@vegaprotocol/react-helpers';
|
||||||
import { AsyncRenderer, ExternalLink, Splash } from '@vegaprotocol/ui-toolkit';
|
import { AsyncRenderer, ExternalLink, Splash } from '@vegaprotocol/ui-toolkit';
|
||||||
|
|
||||||
import { marketProvider, marketDataProvider } from '@vegaprotocol/market-list';
|
import { marketProvider, marketDataProvider } from '@vegaprotocol/market-list';
|
||||||
import { useGlobalStore, usePageTitleStore } from '../../stores';
|
import { useGlobalStore, usePageTitleStore } from '../../stores';
|
||||||
import { TradeGrid, TradePanels } from './trade-grid';
|
import { TradeGrid, TradePanels } from './trade-grid';
|
||||||
|
@ -12,6 +12,7 @@ import { memo, useState } from 'react';
|
|||||||
import type { ReactNode, ComponentProps } from 'react';
|
import type { ReactNode, ComponentProps } from 'react';
|
||||||
import { DepthChartContainer } from '@vegaprotocol/market-depth';
|
import { DepthChartContainer } from '@vegaprotocol/market-depth';
|
||||||
import { CandlesChartContainer } from '@vegaprotocol/candles-chart';
|
import { CandlesChartContainer } from '@vegaprotocol/candles-chart';
|
||||||
|
import { OracleBanner } from '../../components/banner';
|
||||||
import {
|
import {
|
||||||
Tab,
|
Tab,
|
||||||
LocalStoragePersistTabs as Tabs,
|
LocalStoragePersistTabs as Tabs,
|
||||||
@ -273,7 +274,10 @@ export const TradeGrid = ({
|
|||||||
}: TradeGridProps) => {
|
}: TradeGridProps) => {
|
||||||
return (
|
return (
|
||||||
<div className="h-full grid grid-rows-[min-content_1fr]">
|
<div className="h-full grid grid-rows-[min-content_1fr]">
|
||||||
|
<div>
|
||||||
<TradeMarketHeader market={market} onSelect={onSelect} />
|
<TradeMarketHeader market={market} onSelect={onSelect} />
|
||||||
|
<OracleBanner marketId={market?.id || ''} />
|
||||||
|
</div>
|
||||||
<MainGrid
|
<MainGrid
|
||||||
marketId={market?.id || ''}
|
marketId={market?.id || ''}
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
|
@ -1,16 +1,13 @@
|
|||||||
import { AnnouncementBanner } from '@vegaprotocol/announcements';
|
import { AnnouncementBanner as Banner } from '@vegaprotocol/announcements';
|
||||||
import { useEnvironment } from '@vegaprotocol/environment';
|
import { useEnvironment } from '@vegaprotocol/environment';
|
||||||
|
|
||||||
export const Banner = () => {
|
export const AnnouncementBanner = () => {
|
||||||
const { ANNOUNCEMENTS_CONFIG_URL } = useEnvironment();
|
const { ANNOUNCEMENTS_CONFIG_URL } = useEnvironment();
|
||||||
|
|
||||||
// Return an empty div so that the grid layout in _app.page.ts
|
// Return an empty div so that the grid layout in _app.page.ts
|
||||||
// renders correctly
|
// renders correctly
|
||||||
if (!ANNOUNCEMENTS_CONFIG_URL) {
|
if (!ANNOUNCEMENTS_CONFIG_URL) {
|
||||||
return <div />;
|
return <div />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <Banner app="console" configUrl={ANNOUNCEMENTS_CONFIG_URL} />;
|
||||||
<AnnouncementBanner app="console" configUrl={ANNOUNCEMENTS_CONFIG_URL} />
|
|
||||||
);
|
|
||||||
};
|
};
|
@ -1 +1,2 @@
|
|||||||
export * from './banner';
|
export * from './announcement-banner';
|
||||||
|
export * from './oracle-banner';
|
||||||
|
44
apps/trading/components/banner/oracle-banner.tsx
Normal file
44
apps/trading/components/banner/oracle-banner.tsx
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
import { useMarketOracle } from '@vegaprotocol/market-info';
|
||||||
|
import ReactMarkdown from 'react-markdown';
|
||||||
|
import { Intent, NotificationBanner } from '@vegaprotocol/ui-toolkit';
|
||||||
|
|
||||||
|
const oracleStatuses = {
|
||||||
|
UNKNOWN: t(
|
||||||
|
"This public key's proofs have not been verified yet, or no proofs have been provided yet."
|
||||||
|
),
|
||||||
|
GOOD: t("This public key's proofs have been verified."),
|
||||||
|
SUSPICIOUS: t(
|
||||||
|
'This public key is suspected to be acting in bad faith, pending investigation.'
|
||||||
|
),
|
||||||
|
MALICIOUS: t('This public key has been observed acting in bad faith.'),
|
||||||
|
RETIRED: t('This public key is no longer in use.'),
|
||||||
|
COMPROMISED: t(
|
||||||
|
'This public key is no longer in the control of its original owners.'
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const OracleBanner = ({ marketId }: { marketId: string }) => {
|
||||||
|
const oracle = useMarketOracle(marketId);
|
||||||
|
if (!oracle || oracle.status === 'GOOD') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<NotificationBanner intent={Intent.Danger}>
|
||||||
|
<div>
|
||||||
|
Oracle status for this market is {oracle.status}.{' '}
|
||||||
|
{oracleStatuses[oracle.status]}
|
||||||
|
</div>
|
||||||
|
{oracle.status_reason ? (
|
||||||
|
<ReactMarkdown
|
||||||
|
className="react-markdown-container"
|
||||||
|
skipHtml={true}
|
||||||
|
disallowedElements={['img']}
|
||||||
|
linkTarget="_blank"
|
||||||
|
>
|
||||||
|
{oracle.status_reason}
|
||||||
|
</ReactMarkdown>
|
||||||
|
) : null}
|
||||||
|
</NotificationBanner>
|
||||||
|
);
|
||||||
|
};
|
@ -30,7 +30,7 @@ import ToastsManager from './toasts-manager';
|
|||||||
import { HashRouter, useLocation, useSearchParams } from 'react-router-dom';
|
import { HashRouter, useLocation, useSearchParams } from 'react-router-dom';
|
||||||
import { Connectors } from '../lib/vega-connectors';
|
import { Connectors } from '../lib/vega-connectors';
|
||||||
import { ViewingBanner } from '../components/viewing-banner';
|
import { ViewingBanner } from '../components/viewing-banner';
|
||||||
import { Banner } from '../components/banner';
|
import { AnnouncementBanner } from '../components/banner';
|
||||||
import { AppLoader, DynamicLoader } from '../components/app-loader';
|
import { AppLoader, DynamicLoader } from '../components/app-loader';
|
||||||
import { Navbar } from '../components/navbar';
|
import { Navbar } from '../components/navbar';
|
||||||
import { ENV } from '../lib/config';
|
import { ENV } from '../lib/config';
|
||||||
@ -86,7 +86,7 @@ function AppBody({ Component }: AppProps) {
|
|||||||
</Head>
|
</Head>
|
||||||
<Title />
|
<Title />
|
||||||
<div className={gridClasses}>
|
<div className={gridClasses}>
|
||||||
<Banner />
|
<AnnouncementBanner />
|
||||||
<Navbar theme={VEGA_ENV === Networks.TESTNET ? 'yellow' : 'system'} />
|
<Navbar theme={VEGA_ENV === Networks.TESTNET ? 'yellow' : 'system'} />
|
||||||
<ViewingBanner />
|
<ViewingBanner />
|
||||||
<main data-testid={location.pathname}>
|
<main data-testid={location.pathname}>
|
||||||
|
1
libs/market-info/src/hooks/index.ts
Normal file
1
libs/market-info/src/hooks/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './use-market-oracle';
|
132
libs/market-info/src/hooks/use-market-oracle.spec.ts
Normal file
132
libs/market-info/src/hooks/use-market-oracle.spec.ts
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
import { renderHook } from '@testing-library/react';
|
||||||
|
import { useMarketOracle } from './use-market-oracle';
|
||||||
|
import type { MarketInfoQuery } from '../components/market-info/__generated__/MarketInfo';
|
||||||
|
import type { Provider } from '@vegaprotocol/oracles';
|
||||||
|
|
||||||
|
const ORACLE_PROOFS_URL = 'ORACLE_PROOFS_URL';
|
||||||
|
|
||||||
|
const address = 'address';
|
||||||
|
const key = 'key';
|
||||||
|
|
||||||
|
const mockEnvironment = jest.fn(() => ({ ORACLE_PROOFS_URL }));
|
||||||
|
const mockDataProvider = jest.fn<
|
||||||
|
{ data: MarketInfoQuery['market'] },
|
||||||
|
unknown[]
|
||||||
|
>(() => ({
|
||||||
|
data: {
|
||||||
|
tradableInstrument: {
|
||||||
|
instrument: {
|
||||||
|
product: {
|
||||||
|
dataSourceSpecForSettlementData: {
|
||||||
|
data: {
|
||||||
|
sourceType: {
|
||||||
|
__typename: 'DataSourceDefinitionExternal',
|
||||||
|
sourceType: {
|
||||||
|
signers: [
|
||||||
|
{
|
||||||
|
signer: {
|
||||||
|
__typename: 'ETHAddress',
|
||||||
|
address,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
signer: {
|
||||||
|
__typename: 'PubKey',
|
||||||
|
key,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as MarketInfoQuery['market'],
|
||||||
|
}));
|
||||||
|
|
||||||
|
const mockOracleProofs = jest.fn<{ data?: Provider[] }, unknown[]>(() => ({}));
|
||||||
|
|
||||||
|
jest.mock('@vegaprotocol/environment', () => ({
|
||||||
|
useEnvironment: jest.fn((args) => mockEnvironment()),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('@vegaprotocol/react-helpers', () => ({
|
||||||
|
useDataProvider: jest.fn((args) => mockDataProvider()),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('@vegaprotocol/oracles', () => ({
|
||||||
|
useOracleProofs: jest.fn((args) => mockOracleProofs()),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const marketId = 'marketId';
|
||||||
|
describe('useMarketOracle', () => {
|
||||||
|
it('returns undefined if no market info present', () => {
|
||||||
|
mockDataProvider.mockReturnValueOnce({ data: null });
|
||||||
|
const { result } = renderHook(() => useMarketOracle(marketId));
|
||||||
|
expect(result.current).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns undefined if no oracle proofs present', () => {
|
||||||
|
mockOracleProofs.mockReturnValueOnce({ data: undefined });
|
||||||
|
const { result } = renderHook(() => useMarketOracle(marketId));
|
||||||
|
expect(result.current).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns oracle matched by eth_address', () => {
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
proofs: [
|
||||||
|
{
|
||||||
|
eth_address: 'eth_address',
|
||||||
|
type: 'eth_address',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
oracle: {},
|
||||||
|
} as Provider,
|
||||||
|
{
|
||||||
|
proofs: [
|
||||||
|
{
|
||||||
|
eth_address: address,
|
||||||
|
type: 'eth_address',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
oracle: {},
|
||||||
|
} as Provider,
|
||||||
|
];
|
||||||
|
mockOracleProofs.mockReturnValueOnce({
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
const { result } = renderHook(() => useMarketOracle(marketId));
|
||||||
|
expect(result.current).toBe(data[1].oracle);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns oracle matching by public_key', () => {
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
proofs: [
|
||||||
|
{
|
||||||
|
public_key: 'public_key',
|
||||||
|
type: 'public_key',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
oracle: {},
|
||||||
|
} as Provider,
|
||||||
|
{
|
||||||
|
proofs: [
|
||||||
|
{
|
||||||
|
public_key: key,
|
||||||
|
type: 'public_key',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
oracle: {},
|
||||||
|
} as Provider,
|
||||||
|
];
|
||||||
|
mockOracleProofs.mockReturnValueOnce({
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
const { result } = renderHook(() => useMarketOracle(marketId));
|
||||||
|
expect(result.current).toBe(data[1].oracle);
|
||||||
|
});
|
||||||
|
});
|
43
libs/market-info/src/hooks/use-market-oracle.ts
Normal file
43
libs/market-info/src/hooks/use-market-oracle.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { useEnvironment } from '@vegaprotocol/environment';
|
||||||
|
import { useOracleProofs } from '@vegaprotocol/oracles';
|
||||||
|
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
||||||
|
import { marketInfoProvider } from '../components/market-info/market-info-data-provider';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
|
export const useMarketOracle = (marketId: string) => {
|
||||||
|
const { ORACLE_PROOFS_URL } = useEnvironment();
|
||||||
|
const { data: marketInfo } = useDataProvider({
|
||||||
|
dataProvider: marketInfoProvider,
|
||||||
|
variables: { marketId },
|
||||||
|
});
|
||||||
|
const { data } = useOracleProofs(ORACLE_PROOFS_URL);
|
||||||
|
return useMemo(() => {
|
||||||
|
if (!data || !marketInfo) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const dataSource =
|
||||||
|
marketInfo.tradableInstrument.instrument.product
|
||||||
|
.dataSourceSpecForSettlementData.data;
|
||||||
|
return data.find((provider) =>
|
||||||
|
provider.proofs.some(
|
||||||
|
(proof) =>
|
||||||
|
(proof.type === 'eth_address' &&
|
||||||
|
dataSource.sourceType.__typename ===
|
||||||
|
'DataSourceDefinitionExternal' &&
|
||||||
|
dataSource.sourceType.sourceType.signers?.some(
|
||||||
|
(signer) =>
|
||||||
|
signer.signer.__typename === 'ETHAddress' &&
|
||||||
|
signer.signer.address === proof.eth_address
|
||||||
|
)) ||
|
||||||
|
(proof.type === 'public_key' &&
|
||||||
|
dataSource.sourceType.__typename ===
|
||||||
|
'DataSourceDefinitionExternal' &&
|
||||||
|
dataSource.sourceType.sourceType.signers?.some(
|
||||||
|
(signer) =>
|
||||||
|
signer.signer.__typename === 'PubKey' &&
|
||||||
|
signer.signer.key === proof.public_key
|
||||||
|
))
|
||||||
|
)
|
||||||
|
)?.oracle;
|
||||||
|
}, [data, marketInfo]);
|
||||||
|
};
|
@ -1 +1,2 @@
|
|||||||
export * from './components';
|
export * from './components';
|
||||||
|
export * from './hooks';
|
||||||
|
@ -16,7 +16,6 @@ export const Icon = ({ size = 4, name, className, ariaLabel }: IconProps) => {
|
|||||||
'inline-block',
|
'inline-block',
|
||||||
'fill-current',
|
'fill-current',
|
||||||
'align-text-bottom',
|
'align-text-bottom',
|
||||||
'fill-current',
|
|
||||||
'shrink-0',
|
'shrink-0',
|
||||||
// Cant just concatenate as TW wont pick up that the class is being used
|
// Cant just concatenate as TW wont pick up that the class is being used
|
||||||
// so below syntax is required
|
// so below syntax is required
|
||||||
|
@ -12,6 +12,7 @@ export * from './dialog';
|
|||||||
export * from './drawer';
|
export * from './drawer';
|
||||||
export * from './dropdown-menu';
|
export * from './dropdown-menu';
|
||||||
export * from './form-group';
|
export * from './form-group';
|
||||||
|
export * from './healthbar';
|
||||||
export * from './icon';
|
export * from './icon';
|
||||||
export * from './indicator';
|
export * from './indicator';
|
||||||
export * from './input-error';
|
export * from './input-error';
|
||||||
@ -25,6 +26,7 @@ export * from './nav-dropdown';
|
|||||||
export * from './nav';
|
export * from './nav';
|
||||||
export * from './navigation';
|
export * from './navigation';
|
||||||
export * from './notification';
|
export * from './notification';
|
||||||
|
export * from './notification-banner';
|
||||||
export * from './pagination';
|
export * from './pagination';
|
||||||
export * from './popover';
|
export * from './popover';
|
||||||
export * from './progress-bar';
|
export * from './progress-bar';
|
||||||
@ -48,4 +50,3 @@ export * from './traffic-light';
|
|||||||
export * from './vega-icons';
|
export * from './vega-icons';
|
||||||
export * from './vega-logo';
|
export * from './vega-logo';
|
||||||
export * from './viewing-as-user';
|
export * from './viewing-as-user';
|
||||||
export * from './healthbar';
|
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
export * from './notification-banner';
|
@ -0,0 +1,45 @@
|
|||||||
|
/* eslint-disable jsx-a11y/accessible-emoji */
|
||||||
|
import type { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||||
|
import { Intent } from '../../utils/intent';
|
||||||
|
import { NotificationBanner } from './notification-banner';
|
||||||
|
import { Button } from '../button';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'NotificationBanner',
|
||||||
|
component: NotificationBanner,
|
||||||
|
} as ComponentMeta<typeof NotificationBanner>;
|
||||||
|
|
||||||
|
const Template: ComponentStory<typeof NotificationBanner> = (args) => {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
<NotificationBanner
|
||||||
|
intent={Intent.Warning}
|
||||||
|
onClose={() => {
|
||||||
|
return;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="uppercase ">
|
||||||
|
The network will upgrade to v0.68.5 in{' '}
|
||||||
|
<span className="text-vega-orange-500">9234</span> blocks
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Trading activity will be interrupted, manage your risk appropriately.
|
||||||
|
<a href="/">View details</a>
|
||||||
|
</div>
|
||||||
|
</NotificationBanner>
|
||||||
|
<NotificationBanner intent={Intent.Danger}>
|
||||||
|
The oracle for this market has been flagged as malicious by the
|
||||||
|
community.
|
||||||
|
</NotificationBanner>
|
||||||
|
<NotificationBanner>
|
||||||
|
Viewing as Vega user 0592X...20CKZ
|
||||||
|
<Button size="sm" className="ml-2">
|
||||||
|
Exit view as
|
||||||
|
</Button>
|
||||||
|
</NotificationBanner>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = Template.bind({});
|
||||||
|
Default.args = {};
|
@ -0,0 +1,80 @@
|
|||||||
|
import classNames from 'classnames';
|
||||||
|
import { toastIconMapping } from '../toast';
|
||||||
|
import { Intent } from '../../utils/intent';
|
||||||
|
import { Icon } from '../icon';
|
||||||
|
|
||||||
|
interface NotificationBannerProps {
|
||||||
|
intent?: Intent;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
onClose?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const NotificationBanner = ({
|
||||||
|
intent = Intent.None,
|
||||||
|
children,
|
||||||
|
onClose,
|
||||||
|
}: NotificationBannerProps) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
'flex items-center p-3 border-b min-h-[56px]',
|
||||||
|
'text-[12px] leading-[16px] font-normal',
|
||||||
|
{
|
||||||
|
'bg-vega-light-100 dark:bg-vega-dark-100 ': intent === Intent.None,
|
||||||
|
'bg-vega-blue-300 dark:bg-vega-blue-700': intent === Intent.Primary,
|
||||||
|
'bg-vega-green-300 dark:bg-vega-green-700': intent === Intent.Success,
|
||||||
|
'bg-vega-orange-300 dark:bg-vega-orange-700':
|
||||||
|
intent === Intent.Warning,
|
||||||
|
'bg-vega-pink-300 dark:bg-vega-pink-700': intent === Intent.Danger,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'border-b-vega-light-200 dark:border-b-vega-dark-200 ':
|
||||||
|
intent === Intent.None,
|
||||||
|
|
||||||
|
'border-b-vega-blue-500 dark:border-b-vega-blue-500':
|
||||||
|
intent === Intent.Primary,
|
||||||
|
|
||||||
|
'border-b-vega-green-600 dark:border-b-vega-green-500':
|
||||||
|
intent === Intent.Success,
|
||||||
|
|
||||||
|
'border-b-vega-orange-500 dark:border-b-vega-orange-500':
|
||||||
|
intent === Intent.Warning,
|
||||||
|
|
||||||
|
'border-b-vega-pink-500 dark:border-b-vega-pink-500':
|
||||||
|
intent === Intent.Danger,
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{intent === Intent.None ? null : (
|
||||||
|
<Icon
|
||||||
|
name={toastIconMapping[intent]}
|
||||||
|
size={4}
|
||||||
|
className={classNames('mr-2', {
|
||||||
|
'text-vega-blue-500 dark:text-vega-blue-500':
|
||||||
|
intent === Intent.Primary,
|
||||||
|
|
||||||
|
'text-vega-green-600 dark:text-vega-green-500':
|
||||||
|
intent === Intent.Success,
|
||||||
|
|
||||||
|
'text-vega-orange-500 dark:text-vega-orange-500':
|
||||||
|
intent === Intent.Warning,
|
||||||
|
|
||||||
|
'text-vega-pink-500 dark:text-vega-pink-500':
|
||||||
|
intent === Intent.Danger,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<div className="grow">{children}</div>
|
||||||
|
{onClose ? (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
data-testid="notification-banner-close"
|
||||||
|
onClick={onClose}
|
||||||
|
className="ml-2"
|
||||||
|
>
|
||||||
|
<Icon name="cross" size={4} className="dark:text-white" />
|
||||||
|
</button>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -30,7 +30,7 @@ type ToastProps = Toast & {
|
|||||||
state?: ToastState;
|
state?: ToastState;
|
||||||
};
|
};
|
||||||
|
|
||||||
const toastIconMapping: { [i in Intent]: IconName } = {
|
export const toastIconMapping: { [i in Intent]: IconName } = {
|
||||||
[Intent.None]: IconNames.HELP,
|
[Intent.None]: IconNames.HELP,
|
||||||
[Intent.Primary]: IconNames.INFO_SIGN,
|
[Intent.Primary]: IconNames.INFO_SIGN,
|
||||||
[Intent.Success]: IconNames.TICK_CIRCLE,
|
[Intent.Success]: IconNames.TICK_CIRCLE,
|
||||||
|
Loading…
Reference in New Issue
Block a user