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:
Art 2022-08-17 16:10:01 +02:00 committed by GitHub
parent bfe2dbeaae
commit 6211c87389
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 762 additions and 17 deletions

View File

@ -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
View 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
}
}

View File

@ -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>

View File

@ -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>
)}

View File

@ -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 });

View File

@ -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')}

View File

@ -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);
});
});

View File

@ -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

View 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;
}

View File

@ -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);
}
);
});

View File

@ -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>
);
};

View File

@ -0,0 +1 @@
export * from './asset-details-dialog';

View File

@ -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';

View File

@ -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')}

View File

@ -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>
);

View File

@ -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);