Feat/627 show asset details (#960)
* feat: show asset details (627) * feat: show asset details (627) fixed tests * feat: show asset details (627) pr suggestions * feat: show asset details (627) var name convention * feat: show asset details (627) merge confict, keys * feat: show asset details (627) Introduced zustand store for asset details dialog. * feat: show asset details (627) Made it prettier * fix: fixed lint error in accounts-table
This commit is contained in:
parent
bfe2dbeaae
commit
6211c87389
3
.vscode/extensions.json
vendored
3
.vscode/extensions.json
vendored
@ -4,6 +4,7 @@
|
||||
"esbenp.prettier-vscode",
|
||||
"firsttris.vscode-jest-runner",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"stevejpurves.cucumber"
|
||||
"stevejpurves.cucumber",
|
||||
"streetsidesoftware.code-spell-checker"
|
||||
]
|
||||
}
|
||||
|
16
.vscode/settings.json
vendored
Normal file
16
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"cSpell.language": "en-GB",
|
||||
"cSpell.words": ["vegaprotocol"],
|
||||
"workbench.colorCustomizations": {
|
||||
"activityBar.background": "#000000",
|
||||
"activityBar.foreground": "#ffffff",
|
||||
"activityBar.inactiveForeground": "#474B0A", // vega-yellow-dark
|
||||
"activityBar.activeBorder": "#DFFF0B", // vega-yellow
|
||||
"titleBar.activeBackground": "#DFFF0B", // vega-yellow
|
||||
"titleBar.activeForeground": "#000000",
|
||||
"titleBar.inactiveBackground": "#474B0A", // vega-yellow-dark
|
||||
"titleBar.inactiveForeground": "#000000",
|
||||
"activityBarBadge.background": "#DFFF0B",
|
||||
"activityBarBadge.foreground": "#000000" // vega-yellow
|
||||
}
|
||||
}
|
@ -14,9 +14,18 @@ import { AppLoader } from '../components/app-loader';
|
||||
import { VegaWalletConnectButton } from '../components/vega-wallet-connect-button';
|
||||
import './styles.css';
|
||||
import { useGlobalStore } from '../stores';
|
||||
import {
|
||||
AssetDetailsDialog,
|
||||
useAssetDetailsDialogStore,
|
||||
} from '@vegaprotocol/market-list';
|
||||
|
||||
function AppBody({ Component, pageProps }: AppProps) {
|
||||
const store = useGlobalStore();
|
||||
const {
|
||||
isAssetDetailsDialogOpen,
|
||||
assetDetailsDialogSymbol,
|
||||
setAssetDetailsDialogOpen,
|
||||
} = useAssetDetailsDialogStore();
|
||||
const [theme, toggleTheme] = useThemeSwitcher();
|
||||
|
||||
return (
|
||||
@ -54,6 +63,11 @@ function AppBody({ Component, pageProps }: AppProps) {
|
||||
dialogOpen={store.vegaWalletManageDialog}
|
||||
setDialogOpen={(open) => store.setVegaWalletManageDialog(open)}
|
||||
/>
|
||||
<AssetDetailsDialog
|
||||
assetSymbol={assetDetailsDialogSymbol}
|
||||
open={isAssetDetailsDialogOpen}
|
||||
onChange={(open) => setAssetDetailsDialogOpen(open)}
|
||||
/>
|
||||
</AppLoader>
|
||||
</div>
|
||||
</ThemeContext.Provider>
|
||||
|
@ -26,10 +26,12 @@ import { useGlobalStore } from '../../stores';
|
||||
import { AccountsContainer } from '@vegaprotocol/accounts';
|
||||
import { DepthChartContainer } from '@vegaprotocol/market-depth';
|
||||
import { CandlesChartContainer } from '@vegaprotocol/candles-chart';
|
||||
import { useAssetDetailsDialogStore } from '@vegaprotocol/market-list';
|
||||
import {
|
||||
Tab,
|
||||
Tabs,
|
||||
PriceCellChange,
|
||||
Button,
|
||||
Tooltip,
|
||||
ResizablePanel,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
@ -58,9 +60,13 @@ export const TradeMarketHeader = ({
|
||||
market,
|
||||
className,
|
||||
}: TradeMarketHeaderProps) => {
|
||||
const { setAssetDetailsDialogOpen, setAssetDetailsDialogSymbol } =
|
||||
useAssetDetailsDialogStore();
|
||||
const candlesClose: string[] = (market?.candles || [])
|
||||
.map((candle) => candle?.close)
|
||||
.filter((c): c is CandleClose => c !== null);
|
||||
const symbol =
|
||||
market.tradableInstrument.instrument.product?.settlementAsset?.symbol;
|
||||
const headerItemClassName = 'whitespace-nowrap flex flex-col ';
|
||||
const itemClassName =
|
||||
'font-sans font-normal mb-0 text-black-60 dark:text-white-80 text-ui-small';
|
||||
@ -132,15 +138,20 @@ export const TradeMarketHeader = ({
|
||||
: '-'}
|
||||
</span>
|
||||
</div>
|
||||
{market.tradableInstrument.instrument.product?.settlementAsset
|
||||
?.symbol && (
|
||||
{symbol && (
|
||||
<div className={headerItemClassName}>
|
||||
<span className={itemClassName}>{t('Settlement asset')}</span>
|
||||
<span data-testid="trading-mode" className={itemValueClassName}>
|
||||
{
|
||||
market.tradableInstrument.instrument.product?.settlementAsset
|
||||
?.symbol
|
||||
}
|
||||
<Button
|
||||
variant="inline-link"
|
||||
className="no-underline hover:underline"
|
||||
onClick={() => {
|
||||
setAssetDetailsDialogOpen(true);
|
||||
setAssetDetailsDialogSymbol(symbol);
|
||||
}}
|
||||
>
|
||||
{symbol}
|
||||
</Button>
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import type { SetState } from 'zustand';
|
||||
import create from 'zustand';
|
||||
|
||||
interface GlobalStore {
|
||||
@ -14,7 +13,7 @@ interface GlobalStore {
|
||||
setMarketId: (marketId: string) => void;
|
||||
}
|
||||
|
||||
export const useGlobalStore = create((set: SetState<GlobalStore>) => ({
|
||||
export const useGlobalStore = create<GlobalStore>((set) => ({
|
||||
vegaWalletConnectDialog: false,
|
||||
setVegaWalletConnectDialog: (isOpen: boolean) => {
|
||||
set({ vegaWalletConnectDialog: isOpen });
|
||||
|
@ -1,5 +1,9 @@
|
||||
import { forwardRef } from 'react';
|
||||
import type { ColumnApi, ValueFormatterParams } from 'ag-grid-community';
|
||||
import type {
|
||||
ColumnApi,
|
||||
GroupCellRendererParams,
|
||||
ValueFormatterParams,
|
||||
} from 'ag-grid-community';
|
||||
import {
|
||||
PriceCell,
|
||||
addDecimalsFormatNumber,
|
||||
@ -12,6 +16,7 @@ import { AgGridColumn } from 'ag-grid-react';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import type { Accounts_party_accounts } from './__generated__/Accounts';
|
||||
import { getId } from './accounts-data-provider';
|
||||
import { useAssetDetailsDialogStore } from '@vegaprotocol/market-list';
|
||||
|
||||
interface AccountsTableProps {
|
||||
data: Accounts_party_accounts[] | null;
|
||||
@ -85,6 +90,8 @@ const comparator = (
|
||||
|
||||
export const AccountsTable = forwardRef<AgGridReact, AccountsTableProps>(
|
||||
({ data }, ref) => {
|
||||
const { setAssetDetailsDialogOpen, setAssetDetailsDialogSymbol } =
|
||||
useAssetDetailsDialogStore();
|
||||
return (
|
||||
<AgGrid
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
@ -117,6 +124,21 @@ export const AccountsTable = forwardRef<AgGridReact, AccountsTableProps>(
|
||||
sortable
|
||||
sortingOrder={['asc', 'desc']}
|
||||
comparator={comparator}
|
||||
cellRenderer={({ value }: GroupCellRendererParams) =>
|
||||
value && value.length > 0 ? (
|
||||
<button
|
||||
className="hover:underline"
|
||||
onClick={() => {
|
||||
setAssetDetailsDialogOpen(true);
|
||||
setAssetDetailsDialogSymbol(value);
|
||||
}}
|
||||
>
|
||||
{value}
|
||||
</button>
|
||||
) : (
|
||||
''
|
||||
)
|
||||
}
|
||||
/>
|
||||
<AgGridColumn
|
||||
headerName={t('Type')}
|
||||
|
@ -259,4 +259,13 @@ describe('Deposit form', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
it('shows "View asset details" button when an asset is selected', async () => {
|
||||
render(<DepositForm {...props} selectedAsset={asset} />);
|
||||
expect(await screen.getByTestId('view-asset-details')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not shows "View asset details" button when no asset is selected', async () => {
|
||||
render(<DepositForm {...props} />);
|
||||
expect(await screen.queryAllByTestId('view-asset-details')).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
@ -25,6 +25,7 @@ import type { ReactNode } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { Controller, useForm, useWatch } from 'react-hook-form';
|
||||
import { DepositLimits } from './deposit-limits';
|
||||
import { useAssetDetailsDialogStore } from '@vegaprotocol/market-list';
|
||||
|
||||
interface FormFields {
|
||||
asset: string;
|
||||
@ -64,6 +65,8 @@ export const DepositForm = ({
|
||||
allowance,
|
||||
isFaucetable,
|
||||
}: DepositFormProps) => {
|
||||
const { setAssetDetailsDialogOpen, setAssetDetailsDialogSymbol } =
|
||||
useAssetDetailsDialogStore();
|
||||
const { account } = useWeb3React();
|
||||
const { keypair } = useVegaWallet();
|
||||
const {
|
||||
@ -180,6 +183,18 @@ export const DepositForm = ({
|
||||
{t(`Get ${selectedAsset.symbol}`)}
|
||||
</UseButton>
|
||||
)}
|
||||
{!errors.asset?.message && selectedAsset && (
|
||||
<button
|
||||
data-testid="view-asset-details"
|
||||
className="text-ui underline"
|
||||
onClick={() => {
|
||||
setAssetDetailsDialogOpen(true);
|
||||
setAssetDetailsDialogSymbol(selectedAsset);
|
||||
}}
|
||||
>
|
||||
{t('View asset details')}
|
||||
</button>
|
||||
)}
|
||||
</FormGroup>
|
||||
<FormGroup label={t('To (Vega key)')} labelFor="to" className="relative">
|
||||
<Input
|
||||
|
84
libs/market-list/src/lib/components/asset-details-dialog/__generated__/AssetsConnection.ts
generated
Normal file
84
libs/market-list/src/lib/components/asset-details-dialog/__generated__/AssetsConnection.ts
generated
Normal file
@ -0,0 +1,84 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @generated
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
// ====================================================
|
||||
// GraphQL query operation: AssetsConnection
|
||||
// ====================================================
|
||||
|
||||
export interface AssetsConnection_assetsConnection_edges_node_source_BuiltinAsset {
|
||||
__typename: "BuiltinAsset";
|
||||
}
|
||||
|
||||
export interface AssetsConnection_assetsConnection_edges_node_source_ERC20 {
|
||||
__typename: "ERC20";
|
||||
/**
|
||||
* The address of the erc20 contract
|
||||
*/
|
||||
contractAddress: string;
|
||||
/**
|
||||
* The lifetime limits deposit per address
|
||||
* Note: this is a temporary measure for restricted mainnet
|
||||
*/
|
||||
lifetimeLimit: string;
|
||||
/**
|
||||
* The maximum allowed per withdraw
|
||||
* Note: this is a temporary measure for restricted mainnet
|
||||
*/
|
||||
withdrawThreshold: string;
|
||||
}
|
||||
|
||||
export type AssetsConnection_assetsConnection_edges_node_source = AssetsConnection_assetsConnection_edges_node_source_BuiltinAsset | AssetsConnection_assetsConnection_edges_node_source_ERC20;
|
||||
|
||||
export interface AssetsConnection_assetsConnection_edges_node {
|
||||
__typename: "Asset";
|
||||
/**
|
||||
* The id of the asset
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* The full name of the asset (e.g: Great British Pound)
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* The symbol of the asset (e.g: GBP)
|
||||
*/
|
||||
symbol: string;
|
||||
/**
|
||||
* The total supply of the market
|
||||
*/
|
||||
totalSupply: string;
|
||||
/**
|
||||
* The precision of the asset
|
||||
*/
|
||||
decimals: number;
|
||||
/**
|
||||
* The minimum economically meaningful amount in the asset
|
||||
*/
|
||||
quantum: string;
|
||||
/**
|
||||
* The origin source of the asset (e.g: an erc20 asset)
|
||||
*/
|
||||
source: AssetsConnection_assetsConnection_edges_node_source;
|
||||
}
|
||||
|
||||
export interface AssetsConnection_assetsConnection_edges {
|
||||
__typename: "AssetEdge";
|
||||
node: AssetsConnection_assetsConnection_edges_node;
|
||||
}
|
||||
|
||||
export interface AssetsConnection_assetsConnection {
|
||||
__typename: "AssetsConnection";
|
||||
/**
|
||||
* The assets
|
||||
*/
|
||||
edges: (AssetsConnection_assetsConnection_edges | null)[] | null;
|
||||
}
|
||||
|
||||
export interface AssetsConnection {
|
||||
/**
|
||||
* The list of all assets in use in the vega network or the specified asset if id is provided
|
||||
*/
|
||||
assetsConnection: AssetsConnection_assetsConnection;
|
||||
}
|
@ -0,0 +1,341 @@
|
||||
import { MockedProvider } from '@apollo/react-testing';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import {
|
||||
AssetDetailsDialog,
|
||||
ASSETS_CONNECTION_QUERY,
|
||||
} from './asset-details-dialog';
|
||||
|
||||
const mockedData = {
|
||||
data: {
|
||||
assetsConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
id: 'XYZalpha',
|
||||
name: 'XYZ (α alpha)',
|
||||
symbol: 'XYZalpha',
|
||||
totalSupply: '10000000000000000000000000000',
|
||||
decimals: 5,
|
||||
quantum: '1',
|
||||
source: {
|
||||
__typename: 'BuiltinAsset',
|
||||
},
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'AssetEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: 'XYZbeta',
|
||||
name: 'XYZ (β beta)',
|
||||
symbol: 'XYZbeta',
|
||||
totalSupply: '1000000000',
|
||||
decimals: 5,
|
||||
quantum: '1',
|
||||
source: {
|
||||
__typename: 'BuiltinAsset',
|
||||
},
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'AssetEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: 'XYZdelta',
|
||||
name: 'XYZ (δ delta)',
|
||||
symbol: 'XYZdelta',
|
||||
totalSupply: '1000000000',
|
||||
decimals: 5,
|
||||
quantum: '1',
|
||||
source: {
|
||||
__typename: 'BuiltinAsset',
|
||||
},
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'AssetEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: 'XYZepsilon',
|
||||
name: 'XYZ (ε epsilon)',
|
||||
symbol: 'XYZepsilon',
|
||||
totalSupply: '1000000000',
|
||||
decimals: 5,
|
||||
quantum: '1',
|
||||
source: {
|
||||
__typename: 'BuiltinAsset',
|
||||
},
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'AssetEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: 'XYZgamma',
|
||||
name: 'XYZ (γ gamma)',
|
||||
symbol: 'XYZgamma',
|
||||
totalSupply: '1000000000',
|
||||
decimals: 5,
|
||||
quantum: '1',
|
||||
source: {
|
||||
__typename: 'BuiltinAsset',
|
||||
},
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'AssetEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: '2282ffc06a557173d297739305cc69f6444cdbbb1089df7d9aef32bbfd735ba1',
|
||||
name: 'Tim Token (Vega)',
|
||||
symbol: 'TIM',
|
||||
totalSupply: '1000000000000000000000000000',
|
||||
decimals: 18,
|
||||
quantum: '1',
|
||||
source: {
|
||||
contractAddress: '0x920B2375BCAC8cCDfDEFD74426c55C48e0304e4F',
|
||||
lifetimeLimit: '0',
|
||||
withdrawThreshold: '0',
|
||||
__typename: 'ERC20',
|
||||
},
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'AssetEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: '449dbfb66e7a444c485b4fdc77ddc6bbf81abbf7c8e247ac299c25e9557b99cf',
|
||||
name: 'Taker Reward Token (Vega)',
|
||||
symbol: 'TAK',
|
||||
totalSupply: '1000000000000000000000000000',
|
||||
decimals: 18,
|
||||
quantum: '1',
|
||||
source: {
|
||||
contractAddress: '0xf700Ce952B6EA11c01b43e5579C6D63286ff8CF0',
|
||||
lifetimeLimit: '0',
|
||||
withdrawThreshold: '0',
|
||||
__typename: 'ERC20',
|
||||
},
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'AssetEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: '5cfa87844724df6069b94e4c8a6f03af21907d7bc251593d08e4251043ee9f7c',
|
||||
name: 'tBTC TEST',
|
||||
symbol: 'tBTC',
|
||||
totalSupply: '21000000',
|
||||
decimals: 5,
|
||||
quantum: '1',
|
||||
source: {
|
||||
contractAddress: '0xC912F059b4eCCEF6C969B2E0e2544A1A2581C094',
|
||||
lifetimeLimit: '0',
|
||||
withdrawThreshold: '0',
|
||||
__typename: 'ERC20',
|
||||
},
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'AssetEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: '6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61',
|
||||
name: 'tDAI TEST',
|
||||
symbol: 'tDAI',
|
||||
totalSupply: '21000000',
|
||||
decimals: 5,
|
||||
quantum: '1',
|
||||
source: {
|
||||
contractAddress: '0xF4A2bcC43D24D14C4189Ef45fCf681E870675333',
|
||||
lifetimeLimit: '0',
|
||||
withdrawThreshold: '0',
|
||||
__typename: 'ERC20',
|
||||
},
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'AssetEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: '8b52d4a3a4b0ffe733cddbc2b67be273816cfeb6ca4c8b339bac03ffba08e4e4',
|
||||
name: 'tEURO TEST',
|
||||
symbol: 'tEURO',
|
||||
totalSupply: '21000000',
|
||||
decimals: 5,
|
||||
quantum: '1',
|
||||
source: {
|
||||
contractAddress: '0xD52b6C949E35A6E4C64b987B1B192A8608931a7b',
|
||||
lifetimeLimit: '0',
|
||||
withdrawThreshold: '0',
|
||||
__typename: 'ERC20',
|
||||
},
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'AssetEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: '98032ba34576f8012de9b822e1da3ed4b6223a4f4e05f573002d441ffb4bf314',
|
||||
name: 'Liquidity Reward Token (Vega)',
|
||||
symbol: 'LIQ',
|
||||
totalSupply: '1000000200000000000000000000',
|
||||
decimals: 18,
|
||||
quantum: '1',
|
||||
source: {
|
||||
contractAddress: '0xD2f21E37e78dD91b60FE3dD74A112e1a53b33057',
|
||||
lifetimeLimit: '0',
|
||||
withdrawThreshold: '0',
|
||||
__typename: 'ERC20',
|
||||
},
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'AssetEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: '993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede',
|
||||
name: 'tUSDC TEST',
|
||||
symbol: 'tUSDC',
|
||||
totalSupply: '21000000',
|
||||
decimals: 5,
|
||||
quantum: '1',
|
||||
source: {
|
||||
contractAddress: '0x3773A5c7aFF77e014cBF067dd31801b4C6dc4136',
|
||||
lifetimeLimit: '0',
|
||||
withdrawThreshold: '0',
|
||||
__typename: 'ERC20',
|
||||
},
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'AssetEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: 'ba98cdeeec849a053e60cc03808e91e90d9d2e62425c76a590617b95ad41a066',
|
||||
name: 'Steve Token (Vega)',
|
||||
symbol: 'STE',
|
||||
totalSupply: '1000000000000000000000000000',
|
||||
decimals: 18,
|
||||
quantum: '1',
|
||||
source: {
|
||||
contractAddress: '0x5a16941cca2Db4AcdFC28Ac77a3e9652Fdf102e1',
|
||||
lifetimeLimit: '0',
|
||||
withdrawThreshold: '0',
|
||||
__typename: 'ERC20',
|
||||
},
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'AssetEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: 'ce3fb1ab0717f0adbce019d7aef53aacdbadefe2d30ad1647b55f134d4072c90',
|
||||
name: 'Woz Token (Vega)',
|
||||
symbol: 'WOZ',
|
||||
totalSupply: '1000000000000000000000000000',
|
||||
decimals: 18,
|
||||
quantum: '1',
|
||||
source: {
|
||||
contractAddress: '0x5E4b9aDA947130Fc320a144cd22bC1641e5c9d81',
|
||||
lifetimeLimit: '0',
|
||||
withdrawThreshold: '0',
|
||||
__typename: 'ERC20',
|
||||
},
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'AssetEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: 'ebcd94151ae1f0d39a4bde3b21a9c7ae81a80ea4352fb075a92e07608d9c953d',
|
||||
name: 'Maker Reward Token (Vega)',
|
||||
symbol: 'MAK',
|
||||
totalSupply: '1000000000000000000000000000',
|
||||
decimals: 18,
|
||||
quantum: '1',
|
||||
source: {
|
||||
contractAddress: '0x8ec701DA58394F5d2c8C2873D31039454D5845C1',
|
||||
lifetimeLimit: '0',
|
||||
withdrawThreshold: '0',
|
||||
__typename: 'ERC20',
|
||||
},
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'AssetEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: 'fc7fd956078fb1fc9db5c19b88f0874c4299b2a7639ad05a47a28c0aef291b55',
|
||||
name: 'Vega (testnet)',
|
||||
symbol: 'VEGA',
|
||||
totalSupply: '64999723000000000000000000',
|
||||
decimals: 18,
|
||||
quantum: '1',
|
||||
source: {
|
||||
contractAddress: '0xDc335304979D378255015c33AbFf09B60c31EBAb',
|
||||
lifetimeLimit: '0',
|
||||
withdrawThreshold: '0',
|
||||
__typename: 'ERC20',
|
||||
},
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'AssetEdge',
|
||||
},
|
||||
],
|
||||
__typename: 'AssetsConnection',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const mocks = [
|
||||
{
|
||||
request: {
|
||||
query: ASSETS_CONNECTION_QUERY,
|
||||
variables: {},
|
||||
},
|
||||
result: mockedData,
|
||||
},
|
||||
];
|
||||
|
||||
const WrappedAssetDetailsDialog = ({
|
||||
assetSymbol,
|
||||
}: {
|
||||
assetSymbol: string;
|
||||
}) => (
|
||||
<MockedProvider mocks={mocks}>
|
||||
<AssetDetailsDialog
|
||||
assetSymbol={assetSymbol}
|
||||
open={true}
|
||||
onChange={() => false}
|
||||
></AssetDetailsDialog>
|
||||
</MockedProvider>
|
||||
);
|
||||
|
||||
describe('AssetDetailsDialog', () => {
|
||||
it('should show no data message given unknown asset symbol', () => {
|
||||
render(<WrappedAssetDetailsDialog assetSymbol={'UNKNOWN_FOR_SURE'} />);
|
||||
expect(screen.getByText('No data')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const cases = [
|
||||
['tDAI', 'tDAI', 'tDAI TEST', '21,000,000'],
|
||||
['VEGA', 'VEGA', 'Vega (testnet)', '64,999,723,000,000,000,000,000,000'],
|
||||
['tUSDC', 'tUSDC', 'tUSDC TEST', '21,000,000'],
|
||||
];
|
||||
it.each(cases)(
|
||||
'should show correct data given %p symbol',
|
||||
async (requestedSymbol, symbol, name, totalSupply) => {
|
||||
render(<WrappedAssetDetailsDialog assetSymbol={requestedSymbol} />);
|
||||
expect((await screen.findByTestId('symbol_value')).textContent).toContain(
|
||||
symbol
|
||||
);
|
||||
expect((await screen.findByTestId('name_value')).textContent).toContain(
|
||||
name
|
||||
);
|
||||
expect(
|
||||
(await screen.findByTestId('totalsupply_value')).textContent
|
||||
).toContain(totalSupply);
|
||||
}
|
||||
);
|
||||
});
|
@ -0,0 +1,205 @@
|
||||
import { gql, useQuery } from '@apollo/client';
|
||||
import { formatNumber, t, toBigNum } from '@vegaprotocol/react-helpers';
|
||||
import type { Asset } from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
Icon,
|
||||
Intent,
|
||||
Splash,
|
||||
Tooltip,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import type {
|
||||
AssetsConnection,
|
||||
AssetsConnection_assetsConnection_edges_node_source_ERC20,
|
||||
} from './__generated__/AssetsConnection';
|
||||
import create from 'zustand';
|
||||
|
||||
export type AssetDetailsDialogStore = {
|
||||
isAssetDetailsDialogOpen: boolean;
|
||||
assetDetailsDialogSymbol: string | Asset;
|
||||
setAssetDetailsDialogOpen: (isOpen: boolean) => void;
|
||||
setAssetDetailsDialogSymbol: (symbol: string | Asset) => void;
|
||||
};
|
||||
|
||||
export const useAssetDetailsDialogStore = create<AssetDetailsDialogStore>(
|
||||
(set) => ({
|
||||
isAssetDetailsDialogOpen: false,
|
||||
assetDetailsDialogSymbol: '',
|
||||
setAssetDetailsDialogOpen: (isOpen: boolean) => {
|
||||
set({ isAssetDetailsDialogOpen: isOpen });
|
||||
},
|
||||
setAssetDetailsDialogSymbol: (symbol: string | Asset) => {
|
||||
set({ assetDetailsDialogSymbol: symbol });
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
type AssetDetails = {
|
||||
key: string;
|
||||
label: string;
|
||||
value: string;
|
||||
tooltip: string;
|
||||
}[];
|
||||
|
||||
export const ASSETS_CONNECTION_QUERY = gql`
|
||||
query AssetsConnection {
|
||||
assetsConnection {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
symbol
|
||||
totalSupply
|
||||
decimals
|
||||
quantum
|
||||
source {
|
||||
... on ERC20 {
|
||||
contractAddress
|
||||
lifetimeLimit
|
||||
withdrawThreshold
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export interface AssetDetailsDialogProps {
|
||||
assetSymbol: string | Asset;
|
||||
open: boolean;
|
||||
onChange: (open: boolean) => void;
|
||||
}
|
||||
|
||||
export const AssetDetailsDialog = ({
|
||||
assetSymbol,
|
||||
open,
|
||||
onChange,
|
||||
}: AssetDetailsDialogProps) => {
|
||||
const { data } = useQuery<AssetsConnection>(ASSETS_CONNECTION_QUERY);
|
||||
const symbol =
|
||||
typeof assetSymbol === 'string' ? assetSymbol : assetSymbol.symbol;
|
||||
const asset = data?.assetsConnection.edges?.find(
|
||||
(e) => e?.node.symbol === symbol
|
||||
);
|
||||
|
||||
let details: AssetDetails = [];
|
||||
if (asset != null) {
|
||||
details = [
|
||||
{
|
||||
key: 'name',
|
||||
label: t('Name'),
|
||||
value: asset.node.name,
|
||||
tooltip: '', // t('Name of the asset (e.g: Great British Pound)')
|
||||
},
|
||||
{
|
||||
key: 'symbol',
|
||||
label: t('Symbol'),
|
||||
value: asset.node.symbol,
|
||||
tooltip: '', // t('Symbol of the asset (e.g: GBP)')
|
||||
},
|
||||
{
|
||||
key: 'decimals',
|
||||
label: t('Decimals'),
|
||||
value: asset.node.decimals.toString(),
|
||||
tooltip: t('Number of decimal / precision handled by this asset'),
|
||||
},
|
||||
{
|
||||
key: 'quantum',
|
||||
label: t('Quantum'),
|
||||
value: asset.node.quantum,
|
||||
tooltip: t('The minimum economically meaningful amount in the asset'),
|
||||
},
|
||||
{
|
||||
key: 'totalsupply',
|
||||
label: t('Total supply'),
|
||||
value: formatNumber(toBigNum(asset.node.totalSupply, 0)),
|
||||
tooltip: t('Total circulating supply for the asset'),
|
||||
},
|
||||
{
|
||||
key: 'contractaddress',
|
||||
label: t('Contract address'),
|
||||
value: (
|
||||
asset.node
|
||||
.source as AssetsConnection_assetsConnection_edges_node_source_ERC20
|
||||
).contractAddress,
|
||||
tooltip: t(
|
||||
'The address of the contract for the token, on the ethereum network'
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'withdrawalthreshold',
|
||||
label: t('Withdrawal threshold'),
|
||||
value: (
|
||||
asset.node
|
||||
.source as AssetsConnection_assetsConnection_edges_node_source_ERC20
|
||||
).withdrawThreshold,
|
||||
tooltip: t(
|
||||
'The maximum allowed per withdraw note: this is a temporary measure for restricted mainnet'
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'lifetimelimit',
|
||||
label: t('Lifetime limit'),
|
||||
value: (
|
||||
asset.node
|
||||
.source as AssetsConnection_assetsConnection_edges_node_source_ERC20
|
||||
).lifetimeLimit,
|
||||
tooltip: t(
|
||||
'The lifetime limits deposit per address note: this is a temporary measure for restricted mainnet'
|
||||
),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const content = asset ? (
|
||||
<div className="pt-8 pb-20 table w-full">
|
||||
{details
|
||||
.filter(({ value }) => value && value.length > 0)
|
||||
.map(({ key, label, value, tooltip }) => (
|
||||
<div key={key} className="flex flex-col md:table-row">
|
||||
<div
|
||||
data-testid={`${key}_label`}
|
||||
className="table-cell w-1/3 py-2 first-letter:uppercase"
|
||||
>
|
||||
{tooltip.length > 0 ? (
|
||||
<Tooltip description={tooltip}>
|
||||
<span className="underline underline-offset-2 decoration-dotted cursor-help">
|
||||
{label}
|
||||
</span>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<span>{label}</span>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
data-testid={`${key}_value`}
|
||||
className="table-cell pb-5 md:pb-0"
|
||||
>
|
||||
{value}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="py-40">
|
||||
<Splash>{t('No data')}</Splash>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
title={t(`Asset details - ${symbol}`)}
|
||||
intent={Intent.Primary}
|
||||
icon={<Icon name="info-sign"></Icon>}
|
||||
open={open}
|
||||
onChange={(isOpen) => onChange(isOpen)}
|
||||
>
|
||||
{content}
|
||||
<Button className="w-1/4" onClick={() => onChange(false)}>
|
||||
Close
|
||||
</Button>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
@ -0,0 +1 @@
|
||||
export * from './asset-details-dialog';
|
@ -1,4 +1,5 @@
|
||||
export * from './markets-container';
|
||||
export * from './asset-details-dialog';
|
||||
export * from './select-market-columns';
|
||||
export * from './select-market-table';
|
||||
export * from './select-market';
|
||||
|
@ -1,5 +1,8 @@
|
||||
import { forwardRef } from 'react';
|
||||
import type { ValueFormatterParams } from 'ag-grid-community';
|
||||
import type {
|
||||
GroupCellRendererParams,
|
||||
ValueFormatterParams,
|
||||
} from 'ag-grid-community';
|
||||
import {
|
||||
PriceFlashCell,
|
||||
addDecimalsFormatNumber,
|
||||
@ -18,6 +21,7 @@ import type {
|
||||
MarketList_markets,
|
||||
MarketList_markets_data,
|
||||
} from '../../__generated__';
|
||||
import { useAssetDetailsDialogStore } from '../asset-details-dialog';
|
||||
|
||||
type Props = AgGridReactProps | AgReactUiProps;
|
||||
|
||||
@ -29,6 +33,8 @@ type MarketListTableValueFormatterParams = Omit<
|
||||
};
|
||||
|
||||
export const MarketListTable = forwardRef<AgGridReact, Props>((props, ref) => {
|
||||
const { setAssetDetailsDialogOpen, setAssetDetailsDialogSymbol } =
|
||||
useAssetDetailsDialogStore();
|
||||
return (
|
||||
<AgGrid
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
@ -50,6 +56,21 @@ export const MarketListTable = forwardRef<AgGridReact, Props>((props, ref) => {
|
||||
<AgGridColumn
|
||||
headerName={t('Settlement asset')}
|
||||
field="tradableInstrument.instrument.product.settlementAsset.symbol"
|
||||
cellRenderer={({ value }: GroupCellRendererParams) =>
|
||||
value && value.length > 0 ? (
|
||||
<button
|
||||
className="hover:underline"
|
||||
onClick={() => {
|
||||
setAssetDetailsDialogOpen(true);
|
||||
setAssetDetailsDialogSymbol(value);
|
||||
}}
|
||||
>
|
||||
{value}
|
||||
</button>
|
||||
) : (
|
||||
''
|
||||
)
|
||||
}
|
||||
/>
|
||||
<AgGridColumn
|
||||
headerName={t('Trading mode')}
|
||||
|
@ -4,7 +4,7 @@ import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import { MarketListTable } from './market-list-table';
|
||||
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import type { IGetRowsParams } from 'ag-grid-community';
|
||||
import type { IGetRowsParams, RowClickedEvent } from 'ag-grid-community';
|
||||
import type {
|
||||
MarketList_markets,
|
||||
MarketList_markets_data,
|
||||
@ -56,9 +56,13 @@ export const MarketsContainer = () => {
|
||||
rowModelType="infinite"
|
||||
datasource={{ getRows }}
|
||||
ref={gridRef}
|
||||
onRowClicked={({ data }: { data: MarketList_markets }) =>
|
||||
push(`/markets/${data.id}`)
|
||||
}
|
||||
onRowClicked={(rowEvent: RowClickedEvent) => {
|
||||
const { data, event } = rowEvent;
|
||||
// filters out clicks on the symbol column because it should display asset details
|
||||
if ((event?.target as HTMLElement).tagName.toUpperCase() === 'BUTTON')
|
||||
return;
|
||||
push(`/markets/${(data as MarketList_markets).id}`);
|
||||
}}
|
||||
/>
|
||||
</AsyncRenderer>
|
||||
);
|
||||
|
@ -15,9 +15,10 @@ import {
|
||||
} from '../../utils/class-names';
|
||||
import classnames from 'classnames';
|
||||
|
||||
type Variant = 'primary' | 'secondary' | 'trade' | 'accent' | 'inline-link';
|
||||
interface CommonProps {
|
||||
children?: ReactNode;
|
||||
variant?: 'primary' | 'secondary' | 'trade' | 'accent' | 'inline-link';
|
||||
variant?: Variant;
|
||||
className?: string;
|
||||
prependIconName?: IconName;
|
||||
appendIconName?: IconName;
|
||||
@ -33,7 +34,7 @@ export interface AnchorButtonProps
|
||||
|
||||
export const getButtonClasses = (
|
||||
className?: string,
|
||||
variant?: 'primary' | 'secondary' | 'trade' | 'accent' | 'inline-link',
|
||||
variant?: Variant,
|
||||
boxShadow?: boolean
|
||||
) => {
|
||||
const paddingLeftProvided = includesLeftPadding(className);
|
||||
|
Loading…
Reference in New Issue
Block a user