feat: view proposed market change (#2189)
This commit is contained in:
parent
642bc8072b
commit
246c07f355
@ -1,4 +1,9 @@
|
||||
import { aliasQuery } from '@vegaprotocol/cypress';
|
||||
import { Schema } from '@vegaprotocol/types';
|
||||
import {
|
||||
generateProposals,
|
||||
marketUpdateProposal,
|
||||
} from '../support/mocks/generate-proposals';
|
||||
|
||||
const marketSummaryBlock = 'header-summary';
|
||||
const marketExpiry = 'market-expiry';
|
||||
@ -12,6 +17,42 @@ const priceChangeValue = 'price-change';
|
||||
const itemHeader = 'item-header';
|
||||
const itemValue = 'item-value';
|
||||
|
||||
describe('Market proposal notification', { tags: '@smoke' }, () => {
|
||||
before(() => {
|
||||
cy.mockTradingPage(
|
||||
Schema.MarketState.STATE_ACTIVE,
|
||||
Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
|
||||
Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY
|
||||
);
|
||||
cy.mockGQL((req) => {
|
||||
aliasQuery(
|
||||
req,
|
||||
'ProposalsList',
|
||||
generateProposals([marketUpdateProposal])
|
||||
);
|
||||
});
|
||||
cy.mockGQLSubscription();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@MarketData');
|
||||
cy.getByTestId(marketSummaryBlock).should('be.visible');
|
||||
});
|
||||
it('should display market proposal notification if proposal found', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId('market-proposal-notification').should(
|
||||
'contain.text',
|
||||
'Changes have been proposed for this market'
|
||||
);
|
||||
cy.getByTestId('market-proposal-notification').within(() => {
|
||||
cy.getByTestId('external-link').should(
|
||||
'have.attr',
|
||||
'href',
|
||||
'https://stagnet3.token.vega.xyz/governance/123'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Market trading page', () => {
|
||||
before(() => {
|
||||
cy.mockTradingPage(
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -36,6 +36,7 @@ import { Last24hPriceChange } from '../../components/last-24h-price-change';
|
||||
import { MarketMarkPrice } from '../../components/market-mark-price';
|
||||
import { MarketTradingModeComponent } from '../../components/market-trading-mode';
|
||||
import { Last24hVolume } from '../../components/last-24h-volume';
|
||||
import { MarketProposalNotification } from '@vegaprotocol/governance';
|
||||
|
||||
const NO_MARKET = t('No market');
|
||||
|
||||
@ -177,6 +178,7 @@ export const TradeMarketHeader = ({
|
||||
</div>
|
||||
</HeaderStat>
|
||||
) : null}
|
||||
<MarketProposalNotification marketId={market?.id} />
|
||||
</Header>
|
||||
);
|
||||
};
|
||||
|
@ -10,7 +10,7 @@ interface TradeMarketHeaderProps {
|
||||
|
||||
export const Header = ({ title, children }: TradeMarketHeaderProps) => {
|
||||
return (
|
||||
<header className="w-screen xl:px-4 pt-4 border-b border-default">
|
||||
<header className="w-screen xl:px-4 pt-3 border-b border-default">
|
||||
<div className="xl:flex xl:gap-4 items-start">
|
||||
<div className="mb-4 xl:mb-0 px-4 xl:px-0">{title}</div>
|
||||
<div
|
||||
@ -43,7 +43,7 @@ export const HeaderStat = ({
|
||||
testId?: string;
|
||||
}) => {
|
||||
const itemClass =
|
||||
'min-w-min w-[120px] whitespace-nowrap pb-3 px-4 border-l border-default';
|
||||
'min-w-min w-[120px] whitespace-nowrap pb-3 px-4 border-l border-default mt-1';
|
||||
const itemHeading = 'text-neutral-500 dark:text-neutral-400';
|
||||
|
||||
return (
|
||||
|
@ -144,7 +144,7 @@ export const SelectMarketPopover = ({
|
||||
onCellClick: OnCellClickHandler;
|
||||
}) => {
|
||||
const triggerClasses =
|
||||
'sm:text-lg md:text-xl lg:text-2xl flex items-center gap-2 whitespace-nowrap hover:text-neutral-500 dark:hover:text-neutral-300';
|
||||
'sm:text-lg md:text-xl lg:text-2xl flex items-center gap-2 whitespace-nowrap hover:text-neutral-500 dark:hover:text-neutral-300 mt-1';
|
||||
const { pubKey } = useVegaWallet();
|
||||
const [open, setOpen] = useState(false);
|
||||
const { data, loading: marketsLoading } = useMarketList();
|
||||
|
@ -3,6 +3,7 @@ import { useAssetsDataProvider } from './assets-data-provider';
|
||||
import { Button, Dialog, Icon, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import create from 'zustand';
|
||||
import { AssetDetailsTable } from './asset-details-table';
|
||||
import { AssetProposalNotification } from '@vegaprotocol/governance';
|
||||
|
||||
export type AssetDetailsDialogStore = {
|
||||
isOpen: boolean;
|
||||
@ -49,6 +50,7 @@ export const AssetDetailsDialog = ({
|
||||
|
||||
const content = asset ? (
|
||||
<div className="my-2">
|
||||
<AssetProposalNotification assetId={asset.id} />
|
||||
<AssetDetailsTable asset={asset} />
|
||||
</div>
|
||||
) : (
|
||||
|
@ -0,0 +1,35 @@
|
||||
import { DApp, TOKEN_PROPOSAL, useLinks } from '@vegaprotocol/environment';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { Schema } from '@vegaprotocol/types';
|
||||
import { ExternalLink, Intent, Notification } from '@vegaprotocol/ui-toolkit';
|
||||
import { useUpdateProposal } from '../lib';
|
||||
|
||||
type AssetProposalNotificationProps = {
|
||||
assetId?: string;
|
||||
};
|
||||
export const AssetProposalNotification = ({
|
||||
assetId,
|
||||
}: AssetProposalNotificationProps) => {
|
||||
const tokenLink = useLinks(DApp.Token);
|
||||
const { data: proposal } = useUpdateProposal({
|
||||
id: assetId,
|
||||
proposalType: Schema.ProposalType.TYPE_UPDATE_ASSET,
|
||||
});
|
||||
|
||||
if (proposal) {
|
||||
const proposalLink = tokenLink(
|
||||
TOKEN_PROPOSAL.replace(':id', proposal.id || '')
|
||||
);
|
||||
return (
|
||||
<Notification
|
||||
intent={Intent.Warning}
|
||||
message={t('Changes have been proposed for this asset')}
|
||||
testId="asset-proposal-notification"
|
||||
>
|
||||
<ExternalLink href={proposalLink}>{t('View proposal')}</ExternalLink>
|
||||
</Notification>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
2
libs/governance/src/components/index.ts
Normal file
2
libs/governance/src/components/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './asset-proposal-notification';
|
||||
export * from './market-proposal-notification';
|
@ -0,0 +1,35 @@
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { Schema } from '@vegaprotocol/types';
|
||||
import { ExternalLink, Intent, Notification } from '@vegaprotocol/ui-toolkit';
|
||||
import { DApp, TOKEN_PROPOSAL, useLinks } from '@vegaprotocol/environment';
|
||||
import { useUpdateProposal } from '../lib';
|
||||
|
||||
type MarketProposalNotificationProps = {
|
||||
marketId?: string;
|
||||
};
|
||||
export const MarketProposalNotification = ({
|
||||
marketId,
|
||||
}: MarketProposalNotificationProps) => {
|
||||
const tokenLink = useLinks(DApp.Token);
|
||||
const { data: proposal } = useUpdateProposal({
|
||||
id: marketId,
|
||||
proposalType: Schema.ProposalType.TYPE_UPDATE_MARKET,
|
||||
});
|
||||
|
||||
if (proposal) {
|
||||
const proposalLink = tokenLink(
|
||||
TOKEN_PROPOSAL.replace(':id', proposal.id || '')
|
||||
);
|
||||
return (
|
||||
<Notification
|
||||
intent={Intent.Warning}
|
||||
message={t('Changes have been proposed for this market')}
|
||||
testId="market-proposal-notification"
|
||||
>
|
||||
<ExternalLink href={proposalLink}>{t('View proposal')}</ExternalLink>
|
||||
</Notification>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
@ -1,2 +1,3 @@
|
||||
export * from './lib';
|
||||
export * from './utils';
|
||||
export * from './components';
|
||||
|
@ -12,6 +12,63 @@ fragment NewMarketFields on NewMarket {
|
||||
}
|
||||
}
|
||||
|
||||
fragment UpdateMarketFields on UpdateMarket {
|
||||
marketId
|
||||
updateMarketConfiguration {
|
||||
instrument {
|
||||
code
|
||||
product {
|
||||
quoteName
|
||||
}
|
||||
}
|
||||
priceMonitoringParameters {
|
||||
triggers {
|
||||
horizonSecs
|
||||
probability
|
||||
auctionExtensionSecs
|
||||
}
|
||||
}
|
||||
liquidityMonitoringParameters {
|
||||
targetStakeParameters {
|
||||
timeWindow
|
||||
scalingFactor
|
||||
}
|
||||
triggeringRatio
|
||||
}
|
||||
riskParameters {
|
||||
__typename
|
||||
... on UpdateMarketSimpleRiskModel {
|
||||
simple {
|
||||
factorLong
|
||||
factorShort
|
||||
}
|
||||
}
|
||||
... on UpdateMarketLogNormalRiskModel {
|
||||
logNormal {
|
||||
riskAversionParameter
|
||||
tau
|
||||
params {
|
||||
mu
|
||||
r
|
||||
sigma
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragment UpdateAssetFields on UpdateAsset {
|
||||
assetId
|
||||
quantum
|
||||
source {
|
||||
... on UpdateERC20 {
|
||||
lifetimeLimit
|
||||
withdrawThreshold
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragment ProposalListFields on Proposal {
|
||||
id
|
||||
reference
|
||||
@ -36,6 +93,12 @@ fragment ProposalListFields on Proposal {
|
||||
... on NewMarket {
|
||||
...NewMarketFields
|
||||
}
|
||||
... on UpdateMarket {
|
||||
...UpdateMarketFields
|
||||
}
|
||||
... on UpdateAsset {
|
||||
...UpdateAssetFields
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,11 @@ import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type NewMarketFieldsFragment = { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', code: string, name: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', id: string, name: string, symbol: string } } | null } };
|
||||
|
||||
export type ProposalListFieldsFragment = { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: string, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalWeight: string } }, terms: { __typename?: 'ProposalTerms', closingDatetime: string, enactmentDatetime?: string | null, change: { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', code: string, name: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', id: string, name: string, symbol: string } } | null } } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateNetworkParameter' } } };
|
||||
export type UpdateMarketFieldsFragment = { __typename?: 'UpdateMarket', marketId: string, updateMarketConfiguration: { __typename?: 'UpdateMarketConfiguration', instrument: { __typename?: 'UpdateInstrumentConfiguration', code: string, product: { __typename?: 'UpdateFutureProduct', quoteName: string } }, priceMonitoringParameters: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null }, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: number, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, riskParameters: { __typename: 'UpdateMarketLogNormalRiskModel', logNormal?: { __typename?: 'LogNormalRiskModel', riskAversionParameter: number, tau: number, params: { __typename?: 'LogNormalModelParams', mu: number, r: number, sigma: number } } | null } | { __typename: 'UpdateMarketSimpleRiskModel', simple?: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } | null } } };
|
||||
|
||||
export type UpdateAssetFieldsFragment = { __typename?: 'UpdateAsset', assetId: string, quantum: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } };
|
||||
|
||||
export type ProposalListFieldsFragment = { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: string, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalWeight: string } }, terms: { __typename?: 'ProposalTerms', closingDatetime: string, enactmentDatetime?: string | null, change: { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', code: string, name: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', id: string, name: string, symbol: string } } | null } } | { __typename?: 'UpdateAsset', assetId: string, quantum: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string, updateMarketConfiguration: { __typename?: 'UpdateMarketConfiguration', instrument: { __typename?: 'UpdateInstrumentConfiguration', code: string, product: { __typename?: 'UpdateFutureProduct', quoteName: string } }, priceMonitoringParameters: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null }, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: number, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, riskParameters: { __typename: 'UpdateMarketLogNormalRiskModel', logNormal?: { __typename?: 'LogNormalRiskModel', riskAversionParameter: number, tau: number, params: { __typename?: 'LogNormalModelParams', mu: number, r: number, sigma: number } } | null } | { __typename: 'UpdateMarketSimpleRiskModel', simple?: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } | null } } } | { __typename?: 'UpdateNetworkParameter' } } };
|
||||
|
||||
export type ProposalsListQueryVariables = Types.Exact<{
|
||||
proposalType?: Types.InputMaybe<Types.ProposalType>;
|
||||
@ -13,7 +17,7 @@ export type ProposalsListQueryVariables = Types.Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ProposalsListQuery = { __typename?: 'Query', proposalsConnection?: { __typename?: 'ProposalsConnection', edges?: Array<{ __typename?: 'ProposalEdge', node: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: string, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalWeight: string } }, terms: { __typename?: 'ProposalTerms', closingDatetime: string, enactmentDatetime?: string | null, change: { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', code: string, name: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', id: string, name: string, symbol: string } } | null } } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateNetworkParameter' } } } } | null> | null } | null };
|
||||
export type ProposalsListQuery = { __typename?: 'Query', proposalsConnection?: { __typename?: 'ProposalsConnection', edges?: Array<{ __typename?: 'ProposalEdge', node: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: string, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalWeight: string } }, terms: { __typename?: 'ProposalTerms', closingDatetime: string, enactmentDatetime?: string | null, change: { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', code: string, name: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', id: string, name: string, symbol: string } } | null } } | { __typename?: 'UpdateAsset', assetId: string, quantum: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string, updateMarketConfiguration: { __typename?: 'UpdateMarketConfiguration', instrument: { __typename?: 'UpdateInstrumentConfiguration', code: string, product: { __typename?: 'UpdateFutureProduct', quoteName: string } }, priceMonitoringParameters: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null }, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: number, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, riskParameters: { __typename: 'UpdateMarketLogNormalRiskModel', logNormal?: { __typename?: 'LogNormalRiskModel', riskAversionParameter: number, tau: number, params: { __typename?: 'LogNormalModelParams', mu: number, r: number, sigma: number } } | null } | { __typename: 'UpdateMarketSimpleRiskModel', simple?: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } | null } } } | { __typename?: 'UpdateNetworkParameter' } } } } | null> | null } | null };
|
||||
|
||||
export const NewMarketFieldsFragmentDoc = gql`
|
||||
fragment NewMarketFields on NewMarket {
|
||||
@ -30,6 +34,65 @@ export const NewMarketFieldsFragmentDoc = gql`
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const UpdateMarketFieldsFragmentDoc = gql`
|
||||
fragment UpdateMarketFields on UpdateMarket {
|
||||
marketId
|
||||
updateMarketConfiguration {
|
||||
instrument {
|
||||
code
|
||||
product {
|
||||
quoteName
|
||||
}
|
||||
}
|
||||
priceMonitoringParameters {
|
||||
triggers {
|
||||
horizonSecs
|
||||
probability
|
||||
auctionExtensionSecs
|
||||
}
|
||||
}
|
||||
liquidityMonitoringParameters {
|
||||
targetStakeParameters {
|
||||
timeWindow
|
||||
scalingFactor
|
||||
}
|
||||
triggeringRatio
|
||||
}
|
||||
riskParameters {
|
||||
__typename
|
||||
... on UpdateMarketSimpleRiskModel {
|
||||
simple {
|
||||
factorLong
|
||||
factorShort
|
||||
}
|
||||
}
|
||||
... on UpdateMarketLogNormalRiskModel {
|
||||
logNormal {
|
||||
riskAversionParameter
|
||||
tau
|
||||
params {
|
||||
mu
|
||||
r
|
||||
sigma
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const UpdateAssetFieldsFragmentDoc = gql`
|
||||
fragment UpdateAssetFields on UpdateAsset {
|
||||
assetId
|
||||
quantum
|
||||
source {
|
||||
... on UpdateERC20 {
|
||||
lifetimeLimit
|
||||
withdrawThreshold
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const ProposalListFieldsFragmentDoc = gql`
|
||||
fragment ProposalListFields on Proposal {
|
||||
id
|
||||
@ -55,10 +118,18 @@ export const ProposalListFieldsFragmentDoc = gql`
|
||||
... on NewMarket {
|
||||
...NewMarketFields
|
||||
}
|
||||
... on UpdateMarket {
|
||||
...UpdateMarketFields
|
||||
}
|
||||
... on UpdateAsset {
|
||||
...UpdateAssetFields
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
${NewMarketFieldsFragmentDoc}`;
|
||||
${NewMarketFieldsFragmentDoc}
|
||||
${UpdateMarketFieldsFragmentDoc}
|
||||
${UpdateAssetFieldsFragmentDoc}`;
|
||||
export const ProposalsListDocument = gql`
|
||||
query ProposalsList($proposalType: ProposalType, $inState: ProposalState) {
|
||||
proposalsConnection(proposalType: $proposalType, inState: $inState) {
|
||||
|
@ -1,3 +1,4 @@
|
||||
export * from './__generated__/Proposal';
|
||||
export * from './use-proposal-event';
|
||||
export * from './use-proposal-submit';
|
||||
export * from './use-update-proposal';
|
||||
|
@ -0,0 +1,319 @@
|
||||
/* eslint-disable jest/no-conditional-expect */
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { Schema } from '@vegaprotocol/types';
|
||||
|
||||
import type {
|
||||
ProposalListFieldsFragment,
|
||||
UpdateAssetFieldsFragment,
|
||||
UpdateMarketFieldsFragment,
|
||||
} from '../proposals-data-provider';
|
||||
import {
|
||||
isChangeProposed,
|
||||
UpdateAssetFields,
|
||||
UpdateMarketFields,
|
||||
useUpdateProposal,
|
||||
} from './use-update-proposal';
|
||||
|
||||
const generateUpdateAssetProposal = (
|
||||
id: string,
|
||||
quantum = '',
|
||||
lifetimeLimit = '',
|
||||
withdrawThreshold = ''
|
||||
): ProposalListFieldsFragment => ({
|
||||
reference: '',
|
||||
state: Schema.ProposalState.STATE_OPEN,
|
||||
datetime: '',
|
||||
votes: {
|
||||
__typename: undefined,
|
||||
yes: {
|
||||
__typename: undefined,
|
||||
totalTokens: '',
|
||||
totalNumber: '',
|
||||
totalWeight: '',
|
||||
},
|
||||
no: {
|
||||
__typename: undefined,
|
||||
totalTokens: '',
|
||||
totalNumber: '',
|
||||
totalWeight: '',
|
||||
},
|
||||
},
|
||||
terms: {
|
||||
__typename: 'ProposalTerms',
|
||||
closingDatetime: '',
|
||||
enactmentDatetime: undefined,
|
||||
change: {
|
||||
__typename: 'UpdateAsset',
|
||||
assetId: id,
|
||||
quantum,
|
||||
source: {
|
||||
__typename: 'UpdateERC20',
|
||||
lifetimeLimit,
|
||||
withdrawThreshold,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
type RiskParameters =
|
||||
| {
|
||||
__typename: 'UpdateMarketLogNormalRiskModel';
|
||||
logNormal?: {
|
||||
__typename?: 'LogNormalRiskModel';
|
||||
riskAversionParameter: number;
|
||||
tau: number;
|
||||
params: {
|
||||
__typename?: 'LogNormalModelParams';
|
||||
mu: number;
|
||||
r: number;
|
||||
sigma: number;
|
||||
};
|
||||
} | null;
|
||||
}
|
||||
| {
|
||||
__typename: 'UpdateMarketSimpleRiskModel';
|
||||
simple?: {
|
||||
__typename?: 'SimpleRiskModelParams';
|
||||
factorLong: number;
|
||||
factorShort: number;
|
||||
} | null;
|
||||
};
|
||||
|
||||
const generateRiskParameters = (
|
||||
type:
|
||||
| 'UpdateMarketLogNormalRiskModel'
|
||||
| 'UpdateMarketSimpleRiskModel' = 'UpdateMarketLogNormalRiskModel'
|
||||
): RiskParameters => {
|
||||
if (type === 'UpdateMarketSimpleRiskModel')
|
||||
return {
|
||||
__typename: 'UpdateMarketSimpleRiskModel',
|
||||
simple: {
|
||||
__typename: 'SimpleRiskModelParams',
|
||||
factorLong: 0,
|
||||
factorShort: 0,
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
__typename: 'UpdateMarketLogNormalRiskModel',
|
||||
logNormal: {
|
||||
__typename: 'LogNormalRiskModel',
|
||||
params: {
|
||||
__typename: 'LogNormalModelParams',
|
||||
mu: 0,
|
||||
r: 0,
|
||||
sigma: 0,
|
||||
},
|
||||
riskAversionParameter: 0,
|
||||
tau: 0,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const generateUpdateMarketProposal = (
|
||||
id: string,
|
||||
code = '',
|
||||
quoteName = '',
|
||||
priceMonitoring = false,
|
||||
liquidityMonitoring = false,
|
||||
riskParameters = false,
|
||||
riskParametersType:
|
||||
| 'UpdateMarketLogNormalRiskModel'
|
||||
| 'UpdateMarketSimpleRiskModel' = 'UpdateMarketLogNormalRiskModel'
|
||||
): ProposalListFieldsFragment => ({
|
||||
reference: '',
|
||||
state: Schema.ProposalState.STATE_OPEN,
|
||||
datetime: '',
|
||||
votes: {
|
||||
__typename: undefined,
|
||||
yes: {
|
||||
__typename: undefined,
|
||||
totalTokens: '',
|
||||
totalNumber: '',
|
||||
totalWeight: '',
|
||||
},
|
||||
no: {
|
||||
__typename: undefined,
|
||||
totalTokens: '',
|
||||
totalNumber: '',
|
||||
totalWeight: '',
|
||||
},
|
||||
},
|
||||
terms: {
|
||||
__typename: 'ProposalTerms',
|
||||
closingDatetime: '',
|
||||
enactmentDatetime: undefined,
|
||||
change: {
|
||||
__typename: 'UpdateMarket',
|
||||
marketId: id,
|
||||
updateMarketConfiguration: {
|
||||
__typename: undefined,
|
||||
instrument: {
|
||||
__typename:
|
||||
code.length > 0 || quoteName.length > 0
|
||||
? 'UpdateInstrumentConfiguration'
|
||||
: undefined,
|
||||
code,
|
||||
product: {
|
||||
__typename:
|
||||
quoteName.length > 0 ? 'UpdateFutureProduct' : undefined,
|
||||
quoteName,
|
||||
},
|
||||
},
|
||||
priceMonitoringParameters: {
|
||||
__typename: priceMonitoring ? 'PriceMonitoringParameters' : undefined,
|
||||
triggers: priceMonitoring
|
||||
? [
|
||||
{
|
||||
auctionExtensionSecs: 1,
|
||||
horizonSecs: 2,
|
||||
probability: 3,
|
||||
__typename: 'PriceMonitoringTrigger',
|
||||
},
|
||||
]
|
||||
: [],
|
||||
},
|
||||
liquidityMonitoringParameters: {
|
||||
__typename: liquidityMonitoring
|
||||
? 'LiquidityMonitoringParameters'
|
||||
: undefined,
|
||||
triggeringRatio: 0,
|
||||
targetStakeParameters: {
|
||||
__typename: undefined,
|
||||
scalingFactor: 0,
|
||||
timeWindow: 0,
|
||||
},
|
||||
},
|
||||
riskParameters: riskParameters
|
||||
? generateRiskParameters(riskParametersType)
|
||||
: {
|
||||
__typename: riskParametersType,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const mockDataProviderData: {
|
||||
data: ProposalListFieldsFragment[];
|
||||
error: Error | undefined;
|
||||
loading: boolean;
|
||||
} = {
|
||||
data: [
|
||||
generateUpdateMarketProposal('123'),
|
||||
generateUpdateAssetProposal('456'),
|
||||
],
|
||||
error: undefined,
|
||||
loading: false,
|
||||
};
|
||||
|
||||
const mockDataProvider = jest.fn(() => {
|
||||
return mockDataProviderData;
|
||||
});
|
||||
jest.mock('@vegaprotocol/react-helpers', () => ({
|
||||
...jest.requireActual('@vegaprotocol/react-helpers'),
|
||||
useDataProvider: jest.fn((args) => mockDataProvider()),
|
||||
}));
|
||||
|
||||
describe('useUpdateProposal', () => {
|
||||
it('returns update proposal for a given asset', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useUpdateProposal({
|
||||
id: '456',
|
||||
proposalType: Schema.ProposalType.TYPE_UPDATE_ASSET,
|
||||
})
|
||||
);
|
||||
const change = result.current.data?.terms
|
||||
.change as UpdateAssetFieldsFragment;
|
||||
expect(change.__typename).toEqual('UpdateAsset');
|
||||
expect(change.assetId).toEqual('456');
|
||||
});
|
||||
|
||||
it('returns update proposal for a given market', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useUpdateProposal({
|
||||
id: '123',
|
||||
proposalType: Schema.ProposalType.TYPE_UPDATE_MARKET,
|
||||
})
|
||||
);
|
||||
const change = result.current.data?.terms
|
||||
.change as UpdateMarketFieldsFragment;
|
||||
expect(change.__typename).toEqual('UpdateMarket');
|
||||
expect(change.marketId).toEqual('123');
|
||||
});
|
||||
|
||||
it('does not return a proposal if not found', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useUpdateProposal({
|
||||
id: '789',
|
||||
proposalType: Schema.ProposalType.TYPE_UPDATE_MARKET,
|
||||
})
|
||||
);
|
||||
expect(result.current.data).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('isChangeProposed', () => {
|
||||
it('returns false if a change for the specified asset field is not proposed', () => {
|
||||
const proposal = generateUpdateAssetProposal('123');
|
||||
expect(isChangeProposed(proposal, UpdateAssetFields.Quantum)).toBeFalsy();
|
||||
expect(
|
||||
isChangeProposed(proposal, UpdateAssetFields.LifetimeLimit)
|
||||
).toBeFalsy();
|
||||
expect(
|
||||
isChangeProposed(proposal, UpdateAssetFields.WithdrawThreshold)
|
||||
).toBeFalsy();
|
||||
});
|
||||
|
||||
it('returns true if a change for the specified asset field is proposed', () => {
|
||||
const proposal = generateUpdateAssetProposal('123', '100', '100', '100');
|
||||
expect(isChangeProposed(proposal, UpdateAssetFields.Quantum)).toBeTruthy();
|
||||
expect(
|
||||
isChangeProposed(proposal, UpdateAssetFields.LifetimeLimit)
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
isChangeProposed(proposal, UpdateAssetFields.WithdrawThreshold)
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('returns false if a change for the specified market field is not proposed', () => {
|
||||
const proposal = generateUpdateMarketProposal('123');
|
||||
expect(isChangeProposed(proposal, UpdateMarketFields.Code)).toBeFalsy();
|
||||
expect(
|
||||
isChangeProposed(proposal, UpdateMarketFields.QuoteName)
|
||||
).toBeFalsy();
|
||||
expect(
|
||||
isChangeProposed(proposal, UpdateMarketFields.PriceMonitoring)
|
||||
).toBeFalsy();
|
||||
expect(
|
||||
isChangeProposed(proposal, UpdateMarketFields.LiquidityMonitoring)
|
||||
).toBeFalsy();
|
||||
expect(
|
||||
isChangeProposed(proposal, UpdateMarketFields.RiskParameters)
|
||||
).toBeFalsy();
|
||||
});
|
||||
|
||||
it('returns true if a change for the specified market field is proposed', () => {
|
||||
const proposal = generateUpdateMarketProposal(
|
||||
'123',
|
||||
'ABCDEF',
|
||||
'qABCDEFq',
|
||||
true,
|
||||
true,
|
||||
true
|
||||
);
|
||||
expect(isChangeProposed(proposal, UpdateMarketFields.Code)).toBeFalsy();
|
||||
expect(
|
||||
isChangeProposed(proposal, UpdateMarketFields.QuoteName)
|
||||
).toBeFalsy();
|
||||
expect(
|
||||
isChangeProposed(proposal, UpdateMarketFields.PriceMonitoring)
|
||||
).toBeFalsy();
|
||||
expect(
|
||||
isChangeProposed(proposal, UpdateMarketFields.LiquidityMonitoring)
|
||||
).toBeFalsy();
|
||||
expect(
|
||||
isChangeProposed(proposal, UpdateMarketFields.RiskParameters)
|
||||
).toBeFalsy();
|
||||
});
|
||||
});
|
203
libs/governance/src/lib/proposals-hooks/use-update-proposal.ts
Normal file
203
libs/governance/src/lib/proposals-hooks/use-update-proposal.ts
Normal file
@ -0,0 +1,203 @@
|
||||
import { proposalsListDataProvider } from '..';
|
||||
import { Schema } from '@vegaprotocol/types';
|
||||
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
||||
import { useMemo } from 'react';
|
||||
import first from 'lodash/first';
|
||||
import type { ProposalListFieldsFragment } from '..';
|
||||
|
||||
type UseUpdateProposalProps = {
|
||||
id?: string;
|
||||
proposalType:
|
||||
| Schema.ProposalType.TYPE_UPDATE_ASSET
|
||||
| Schema.ProposalType.TYPE_UPDATE_MARKET;
|
||||
};
|
||||
|
||||
type UseUpdateProposal = {
|
||||
data: ProposalListFieldsFragment | undefined;
|
||||
loading: boolean;
|
||||
error: Error | undefined;
|
||||
};
|
||||
|
||||
const changeCondition = {
|
||||
[Schema.ProposalType.TYPE_UPDATE_ASSET]: (
|
||||
id: string,
|
||||
change: ProposalListFieldsFragment['terms']['change']
|
||||
) => change.__typename === 'UpdateAsset' && change.assetId === id,
|
||||
[Schema.ProposalType.TYPE_UPDATE_MARKET]: (
|
||||
id: string,
|
||||
change: ProposalListFieldsFragment['terms']['change']
|
||||
) => change.__typename === 'UpdateMarket' && change.marketId === id,
|
||||
};
|
||||
|
||||
export const useUpdateProposal = ({
|
||||
id,
|
||||
proposalType,
|
||||
}: UseUpdateProposalProps): UseUpdateProposal => {
|
||||
const variables = useMemo(
|
||||
() => ({
|
||||
proposalType,
|
||||
skipUpdates: true,
|
||||
}),
|
||||
[proposalType]
|
||||
);
|
||||
|
||||
const { data, loading, error } = useDataProvider({
|
||||
dataProvider: proposalsListDataProvider,
|
||||
variables,
|
||||
});
|
||||
|
||||
const proposal = id
|
||||
? first(
|
||||
(data || []).filter(
|
||||
(proposal) =>
|
||||
[
|
||||
Schema.ProposalState.STATE_OPEN,
|
||||
Schema.ProposalState.STATE_PASSED,
|
||||
Schema.ProposalState.STATE_WAITING_FOR_NODE_VOTE,
|
||||
].includes(proposal.state) &&
|
||||
changeCondition[proposalType](id, proposal.terms.change)
|
||||
)
|
||||
)
|
||||
: undefined;
|
||||
|
||||
return { data: proposal, loading, error };
|
||||
};
|
||||
|
||||
export enum UpdateMarketFields {
|
||||
Code,
|
||||
QuoteName,
|
||||
PriceMonitoring,
|
||||
LiquidityMonitoring,
|
||||
RiskParameters,
|
||||
}
|
||||
|
||||
export enum UpdateAssetFields {
|
||||
Quantum,
|
||||
LifetimeLimit,
|
||||
WithdrawThreshold,
|
||||
}
|
||||
|
||||
export type UpdateProposalField = UpdateAssetFields | UpdateMarketFields;
|
||||
|
||||
const fieldGetters = {
|
||||
[UpdateMarketFields.Code]: (
|
||||
change: ProposalListFieldsFragment['terms']['change']
|
||||
) => {
|
||||
if (change.__typename === 'UpdateMarket') {
|
||||
const proposed =
|
||||
change.updateMarketConfiguration.__typename !== undefined &&
|
||||
change.updateMarketConfiguration.instrument.__typename !== undefined;
|
||||
return (
|
||||
proposed && change.updateMarketConfiguration.instrument.code.length > 0
|
||||
);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[UpdateMarketFields.QuoteName]: (
|
||||
change: ProposalListFieldsFragment['terms']['change']
|
||||
) => {
|
||||
if (change.__typename === 'UpdateMarket') {
|
||||
const proposed =
|
||||
change.updateMarketConfiguration.__typename !== undefined &&
|
||||
change.updateMarketConfiguration.instrument.__typename !== undefined &&
|
||||
change.updateMarketConfiguration.instrument.product.__typename !==
|
||||
undefined;
|
||||
return (
|
||||
proposed &&
|
||||
change.updateMarketConfiguration.instrument.product.quoteName.length > 0
|
||||
);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[UpdateMarketFields.PriceMonitoring]: (
|
||||
change: ProposalListFieldsFragment['terms']['change']
|
||||
) => {
|
||||
if (change.__typename === 'UpdateMarket') {
|
||||
const proposed =
|
||||
change.updateMarketConfiguration.__typename !== undefined &&
|
||||
change.updateMarketConfiguration.priceMonitoringParameters
|
||||
.__typename !== undefined &&
|
||||
change.updateMarketConfiguration.priceMonitoringParameters.triggers
|
||||
?.length;
|
||||
return proposed;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[UpdateMarketFields.LiquidityMonitoring]: (
|
||||
change: ProposalListFieldsFragment['terms']['change']
|
||||
) => {
|
||||
if (change.__typename === 'UpdateMarket') {
|
||||
const proposed =
|
||||
change.updateMarketConfiguration.__typename !== undefined &&
|
||||
change.updateMarketConfiguration.liquidityMonitoringParameters
|
||||
.__typename !== undefined;
|
||||
return proposed;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[UpdateMarketFields.RiskParameters]: (
|
||||
change: ProposalListFieldsFragment['terms']['change']
|
||||
) => {
|
||||
if (change.__typename === 'UpdateMarket') {
|
||||
const proposed =
|
||||
change.updateMarketConfiguration.__typename !== undefined &&
|
||||
change.updateMarketConfiguration.riskParameters.__typename !==
|
||||
undefined;
|
||||
const log =
|
||||
change.updateMarketConfiguration.riskParameters.__typename ===
|
||||
'UpdateMarketLogNormalRiskModel' &&
|
||||
change.updateMarketConfiguration.riskParameters.logNormal !== undefined;
|
||||
const simple =
|
||||
change.updateMarketConfiguration.riskParameters.__typename ===
|
||||
'UpdateMarketSimpleRiskModel' &&
|
||||
change.updateMarketConfiguration.riskParameters.simple !== undefined;
|
||||
return proposed && (log || simple);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[UpdateAssetFields.Quantum]: (
|
||||
change: ProposalListFieldsFragment['terms']['change']
|
||||
) => {
|
||||
if (change.__typename === 'UpdateAsset') {
|
||||
const proposed = change.quantum.length > 0;
|
||||
return proposed;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[UpdateAssetFields.LifetimeLimit]: (
|
||||
change: ProposalListFieldsFragment['terms']['change']
|
||||
) => {
|
||||
if (change.__typename === 'UpdateAsset') {
|
||||
const proposed =
|
||||
change.source.__typename === 'UpdateERC20' &&
|
||||
change.source.lifetimeLimit.length > 0;
|
||||
return proposed;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[UpdateAssetFields.WithdrawThreshold]: (
|
||||
change: ProposalListFieldsFragment['terms']['change']
|
||||
) => {
|
||||
if (change.__typename === 'UpdateAsset') {
|
||||
const proposed =
|
||||
change.source.__typename === 'UpdateERC20' &&
|
||||
change.source.withdrawThreshold.length > 0;
|
||||
return proposed;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
||||
export const isChangeProposed = (
|
||||
proposal: ProposalListFieldsFragment | undefined,
|
||||
field: UpdateProposalField
|
||||
) => {
|
||||
if (proposal) {
|
||||
return (
|
||||
(proposal.terms.change.__typename === 'UpdateAsset' ||
|
||||
proposal.terms.change.__typename === 'UpdateMarket') &&
|
||||
fieldGetters[field](proposal.terms.change)
|
||||
);
|
||||
}
|
||||
return false;
|
||||
};
|
@ -31,6 +31,7 @@ import { marketInfoDataProvider } from './market-info-data-provider';
|
||||
import { TokenLinks } from '@vegaprotocol/react-helpers';
|
||||
|
||||
import type { MarketInfoQuery } from './__generated___/MarketInfo';
|
||||
import { MarketProposalNotification } from '@vegaprotocol/governance';
|
||||
|
||||
export interface InfoProps {
|
||||
market: MarketInfoQuery['market'];
|
||||
@ -405,6 +406,7 @@ export const Info = ({ market, onSelect }: InfoProps) => {
|
||||
<Accordion panels={marketDataPanels} />
|
||||
</div>
|
||||
<div className="mb-8">
|
||||
<MarketProposalNotification marketId={market.id} />
|
||||
<p className={headerClassName}>{t('Market specification')}</p>
|
||||
<Accordion panels={marketSpecPanels} />
|
||||
</div>
|
||||
|
@ -38,3 +38,4 @@ export * from './vega-icons';
|
||||
export * from './vega-logo';
|
||||
export * from './traffic-light';
|
||||
export * from './toast';
|
||||
export * from './notification';
|
||||
|
1
libs/ui-toolkit/src/components/notification/index.ts
Normal file
1
libs/ui-toolkit/src/components/notification/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './notification';
|
@ -0,0 +1,57 @@
|
||||
import type { Meta, Story } from '@storybook/react';
|
||||
import { Intent } from '../../utils/intent';
|
||||
import { ExternalLink, Link } from '../link';
|
||||
import { Notification } from './notification';
|
||||
|
||||
export default {
|
||||
component: Notification,
|
||||
title: 'Notification',
|
||||
} as Meta;
|
||||
|
||||
const Template: Story = ({ intent, message, children }) => (
|
||||
<div className="flex">
|
||||
<Notification intent={intent} message={message}>
|
||||
{children}
|
||||
</Notification>
|
||||
</div>
|
||||
);
|
||||
|
||||
const props = {
|
||||
message: 'Exercitationem doloremque neque laborum incidunt consectetur amet',
|
||||
children: (
|
||||
<div className="flex space-x-1">
|
||||
<Link>Action</Link>
|
||||
<ExternalLink>External action</ExternalLink>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {
|
||||
...props,
|
||||
intent: Intent.None,
|
||||
};
|
||||
|
||||
export const Primary = Template.bind({});
|
||||
Primary.args = {
|
||||
...props,
|
||||
intent: Intent.Primary,
|
||||
};
|
||||
|
||||
export const Success = Template.bind({});
|
||||
Success.args = {
|
||||
...props,
|
||||
intent: Intent.Success,
|
||||
};
|
||||
|
||||
export const Warning = Template.bind({});
|
||||
Warning.args = {
|
||||
...props,
|
||||
intent: Intent.Warning,
|
||||
};
|
||||
|
||||
export const Danger = Template.bind({});
|
||||
Danger.args = {
|
||||
...props,
|
||||
intent: Intent.Danger,
|
||||
};
|
71
libs/ui-toolkit/src/components/notification/notification.tsx
Normal file
71
libs/ui-toolkit/src/components/notification/notification.tsx
Normal file
@ -0,0 +1,71 @@
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import type { IconName } from '@blueprintjs/icons';
|
||||
import classNames from 'classnames';
|
||||
import type { ReactNode } from 'react';
|
||||
import { Intent } from '../../utils/intent';
|
||||
import { Icon } from '../icon';
|
||||
|
||||
type NotificationProps = {
|
||||
intent: Intent;
|
||||
message: string;
|
||||
testId?: string;
|
||||
children?: ReactNode;
|
||||
};
|
||||
|
||||
const getIcon = (intent: Intent): IconName => {
|
||||
const mapping = {
|
||||
[Intent.None]: IconNames.HELP,
|
||||
[Intent.Primary]: IconNames.INFO_SIGN,
|
||||
[Intent.Success]: IconNames.TICK_CIRCLE,
|
||||
[Intent.Warning]: IconNames.WARNING_SIGN,
|
||||
[Intent.Danger]: IconNames.ERROR,
|
||||
};
|
||||
return mapping[intent] as IconName;
|
||||
};
|
||||
|
||||
export const Notification = ({
|
||||
intent,
|
||||
message,
|
||||
testId,
|
||||
children,
|
||||
}: NotificationProps) => {
|
||||
return (
|
||||
<div
|
||||
data-testid={testId || 'notification'}
|
||||
className={classNames(
|
||||
{
|
||||
'border-gray-700 dark:border-gray-300': intent === Intent.None,
|
||||
'border-vega-blue': intent === Intent.Primary,
|
||||
'border-vega-green-dark dark:border-vega-green':
|
||||
intent === Intent.Success,
|
||||
'border-yellow-500': intent === Intent.Warning,
|
||||
'border-vega-pink': intent === Intent.Danger,
|
||||
},
|
||||
'border rounded px-3 py-1 text-xs mb-1 mr-1'
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={classNames(
|
||||
{
|
||||
'text-gray-700 dark:text-gray-300': intent === Intent.None,
|
||||
'text-vega-blue': intent === Intent.Primary,
|
||||
'text-vega-green-dark dark:text-vega-green':
|
||||
intent === Intent.Success,
|
||||
'text-yellow-600 dark:text-yellow-500': intent === Intent.Warning,
|
||||
'text-vega-pink': intent === Intent.Danger,
|
||||
},
|
||||
'flex items-start'
|
||||
)}
|
||||
>
|
||||
<Icon size={3} className="mr-1 mt-[2px]" name={getIcon(intent)} />
|
||||
<span
|
||||
title={message}
|
||||
className="whitespace-nowrap overflow-hidden text-ellipsis"
|
||||
>
|
||||
{message}
|
||||
</span>
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
Loading…
Reference in New Issue
Block a user