chore: misc quick wins (#1596)

* fix: make sparkline appear after change cell

* fix: make price come after expiry in market header

* fix: refer to size rather than amount in validation

* fix: radio colors in light theme

* fix: remove orange border from vega tx pending state

* chore: combine deposit and withdraw buttons into single cell

* chore: update accounts table test, remove console warning from breakdown tests

* chore: update order validation test

* chore: place market table header in correct position

* chore: use actual change id to avoid clash

* fix: remove assertion in fills table that is flakey

* chore: render fills table with act

* fix: add a wait time for infura queries to resolve
This commit is contained in:
Matthew Russell 2022-10-04 21:00:06 -07:00 committed by GitHub
parent 61721e61f6
commit 1f8f54617b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 119 additions and 118 deletions

View File

@ -25,7 +25,10 @@ before(() => {
// Mock chainId fetch which happens on every page for wallet connection // Mock chainId fetch which happens on every page for wallet connection
cy.mockGQL((req) => { cy.mockGQL((req) => {
aliasQuery(req, 'ChainId', { aliasQuery(req, 'ChainId', {
statistics: { __typename: 'Statistics', chainId: 'test-chain-id' }, statistics: {
__typename: 'Statistics',
chainId: 'vega-fairground-202210041151',
},
}); });
}); });
}); });

View File

@ -24,7 +24,10 @@ before(() => {
// Mock chainId fetch which happens on every page for wallet connection // Mock chainId fetch which happens on every page for wallet connection
cy.mockGQL((req) => { cy.mockGQL((req) => {
aliasQuery(req, 'ChainId', { aliasQuery(req, 'ChainId', {
statistics: { __typename: 'Statistics', chainId: 'test-chain-id' }, statistics: {
__typename: 'Statistics',
chainId: 'vega-fairground-202210041151',
},
}); });
}); });
}); });

View File

@ -53,7 +53,7 @@ describe('withdraw', { tags: '@smoke' }, () => {
cy.get(toAddressField).should('have.value', ethAddressValue); cy.get(toAddressField).should('have.value', ethAddressValue);
}); });
it('min amount', () => { it('min amount', () => {
cy.get(assetSelectField).select(asset1Name); // Select asset so we have a min viable amount calculated selectAsset(asset1Name);
cy.get(amountField).clear().type('0'); cy.get(amountField).clear().type('0');
cy.getByTestId(submitWithdrawBtn).click(); cy.getByTestId(submitWithdrawBtn).click();
cy.get('[data-testid="input-error-text"]').should( cy.get('[data-testid="input-error-text"]').should(
@ -62,7 +62,7 @@ describe('withdraw', { tags: '@smoke' }, () => {
); );
}); });
it('max amount', () => { it('max amount', () => {
cy.get(assetSelectField).select(asset2Name); // Will be above maximum because the vega wallet doesnt have any collateral selectAsset(asset2Name); // Will be above maximum because the vega wallet doesnt have any collateral
cy.get(amountField).clear().type('1'); cy.get(amountField).clear().type('1');
cy.getByTestId(submitWithdrawBtn).click(); cy.getByTestId(submitWithdrawBtn).click();
cy.get('[data-testid="input-error-text"]').should( cy.get('[data-testid="input-error-text"]').should(
@ -72,7 +72,7 @@ describe('withdraw', { tags: '@smoke' }, () => {
}); });
it('can set amount using use maximum button', () => { it('can set amount using use maximum button', () => {
cy.get(assetSelectField).select(asset1Name); selectAsset(asset1Name);
cy.getByTestId(useMaximumAmount).click(); cy.getByTestId(useMaximumAmount).click();
cy.get(amountField).should('have.value', '1000.00000'); cy.get(amountField).should('have.value', '1000.00000');
}); });
@ -87,7 +87,7 @@ describe('withdraw', { tags: '@smoke' }, () => {
}, },
}, },
}); });
cy.get(assetSelectField).select(asset1Name); selectAsset(asset1Name);
cy.getByTestId('balance-available') cy.getByTestId('balance-available')
.should('contain.text', 'Balance available') .should('contain.text', 'Balance available')
.find('td') .find('td')
@ -110,4 +110,13 @@ describe('withdraw', { tags: '@smoke' }, () => {
it.skip('creates a withdrawal on submit'); // Needs capsule it.skip('creates a withdrawal on submit'); // Needs capsule
it.skip('creates a withdrawal on submit and prompts to complete withdrawal'); // Needs capsule it.skip('creates a withdrawal on submit and prompts to complete withdrawal'); // Needs capsule
const selectAsset = (assetName: string) => {
cy.get(assetSelectField).select(assetName);
// The asset only gets set once the queries (getWithdrawThreshold, getDelay)
// against the Ethereum change resolve, we should fix this but for now just force
// some wait time
// eslint-disable-next-line
cy.wait(1000);
};
}); });

View File

@ -6,7 +6,7 @@ import { generateChainId } from './mocks/generate-chain-id';
registerCypressGrep(); registerCypressGrep();
before(() => { before(() => {
// Mock chainId fetch which happens on every page for wallet connection // Mock chainId fetch which happens on every page wallet connection
cy.mockGQL((req) => { cy.mockGQL((req) => {
aliasQuery(req, 'ChainId', generateChainId()); aliasQuery(req, 'ChainId', generateChainId());
}); });

View File

@ -94,18 +94,18 @@ const headers: Column[] = [
className: cellClassNames, className: cellClassNames,
onlyOnDetailed: false, onlyOnDetailed: false,
}, },
{
kind: ColumnKind.Asset,
value: t('Settlement asset'),
className: `${cellClassNames} hidden sm:table-cell`,
onlyOnDetailed: false,
},
{ {
kind: ColumnKind.Sparkline, kind: ColumnKind.Sparkline,
value: t(''), value: t(''),
className: `${cellClassNames} hidden lg:table-cell`, className: `${cellClassNames} hidden lg:table-cell`,
onlyOnDetailed: false, onlyOnDetailed: false,
}, },
{
kind: ColumnKind.Asset,
value: t('Settlement asset'),
className: `${cellClassNames} hidden sm:table-cell`,
onlyOnDetailed: false,
},
{ {
kind: ColumnKind.High24, kind: ColumnKind.High24,
value: t('24h high'), value: t('24h high'),
@ -209,6 +209,19 @@ export const columns = (
className: cellClassNames, className: cellClassNames,
onlyOnDetailed: false, onlyOnDetailed: false,
}, },
{
kind: ColumnKind.Sparkline,
value: market.candles && (
<Sparkline
width={100}
height={20}
muted={false}
data={candlesClose?.map((c: string) => Number(c)) || []}
/>
),
className: `${cellClassNames} hidden lg:table-cell`,
onlyOnDetailed: false && candlesClose,
},
{ {
kind: ColumnKind.Asset, kind: ColumnKind.Asset,
value: ( value: (
@ -231,19 +244,6 @@ export const columns = (
className: `${cellClassNames} hidden sm:table-cell`, className: `${cellClassNames} hidden sm:table-cell`,
onlyOnDetailed: false, onlyOnDetailed: false,
}, },
{
kind: ColumnKind.Sparkline,
value: market.candles && (
<Sparkline
width={100}
height={20}
muted={false}
data={candlesClose?.map((c: string) => Number(c)) || []}
/>
),
className: `${cellClassNames} hidden lg:table-cell`,
onlyOnDetailed: false && candlesClose,
},
{ {
kind: ColumnKind.High24, kind: ColumnKind.High24,
value: candleHigh ? ( value: candleHigh ? (
@ -390,6 +390,19 @@ export const columnsPositionMarkets = (
className: cellClassNames, className: cellClassNames,
onlyOnDetailed: false, onlyOnDetailed: false,
}, },
{
kind: ColumnKind.Sparkline,
value: candlesClose && (
<Sparkline
width={100}
height={20}
muted={false}
data={candlesClose.map((c: string) => Number(c))}
/>
),
className: `${cellClassNames} hidden lg:table-cell`,
onlyOnDetailed: false,
},
{ {
kind: ColumnKind.Asset, kind: ColumnKind.Asset,
value: ( value: (
@ -412,19 +425,6 @@ export const columnsPositionMarkets = (
className: `${cellClassNames} hidden sm:table-cell`, className: `${cellClassNames} hidden sm:table-cell`,
onlyOnDetailed: false, onlyOnDetailed: false,
}, },
{
kind: ColumnKind.Sparkline,
value: candlesClose && (
<Sparkline
width={100}
height={20}
muted={false}
data={candlesClose.map((c: string) => Number(c))}
/>
),
className: `${cellClassNames} hidden lg:table-cell`,
onlyOnDetailed: false,
},
{ {
kind: ColumnKind.High24, kind: ColumnKind.High24,
value: candleHigh ? ( value: candleHigh ? (

View File

@ -158,6 +158,16 @@ export const TradeMarketHeader = ({
> >
<ExpiryLabel market={market} /> <ExpiryLabel market={market} />
</HeaderStat> </HeaderStat>
<HeaderStat heading={t('Price')}>
<div data-testid="mark-price">
{market.data && market.data.markPrice !== '0'
? addDecimalsFormatNumber(
market.data.markPrice,
market.decimalPlaces
)
: '-'}
</div>
</HeaderStat>
<HeaderStat heading={t('Change (24h)')}> <HeaderStat heading={t('Change (24h)')}>
<PriceCellChange <PriceCellChange
candles={candlesClose} candles={candlesClose}
@ -193,16 +203,6 @@ export const TradeMarketHeader = ({
: MarketTradingModeMapping[market.tradingMode]} : MarketTradingModeMapping[market.tradingMode]}
</div> </div>
</HeaderStat> </HeaderStat>
<HeaderStat heading={t('Price')}>
<div data-testid="mark-price">
{market.data && market.data.markPrice !== '0'
? addDecimalsFormatNumber(
market.data.markPrice,
market.decimalPlaces
)
: '-'}
</div>
</HeaderStat>
{symbol ? ( {symbol ? (
<HeaderStat heading={t('Settlement asset')}> <HeaderStat heading={t('Settlement asset')}>
<div data-testid="trading-mode"> <div data-testid="trading-mode">

View File

@ -32,28 +32,18 @@ const singleRow = {
const singleRowData = [singleRow]; const singleRowData = [singleRow];
describe('AccountsTable', () => { describe('AccountsTable', () => {
it('should render successfully', async () => {
await act(async () => {
render(<AccountTable rowData={[]} onClickAsset={() => null} />);
});
const headers = await screen.getAllByRole('columnheader');
expect(headers).toHaveLength(6);
expect(
headers?.map((h) => h.querySelector('[ref="eText"]')?.textContent?.trim())
).toEqual(['Asset', 'Deposited', 'Used', '', '', '']);
});
it('should render correct columns', async () => { it('should render correct columns', async () => {
await act(async () => { await act(async () => {
render( render(
<AccountTable rowData={singleRowData} onClickAsset={() => null} /> <AccountTable rowData={singleRowData} onClickAsset={() => null} />
); );
}); });
const headers = await screen.getAllByRole('columnheader'); const expectedHeaders = ['Asset', 'Deposited', 'Used', '', ''];
expect(headers).toHaveLength(6); const headers = await screen.findAllByRole('columnheader');
expect(headers).toHaveLength(expectedHeaders.length);
expect( expect(
headers?.map((h) => h.querySelector('[ref="eText"]')?.textContent?.trim()) headers?.map((h) => h.querySelector('[ref="eText"]')?.textContent?.trim())
).toEqual(['Asset', 'Deposited', 'Used', '', '', '']); ).toEqual(expectedHeaders);
}); });
it('should apply correct formatting', async () => { it('should apply correct formatting', async () => {
@ -62,14 +52,13 @@ describe('AccountsTable', () => {
<AccountTable rowData={singleRowData} onClickAsset={() => null} /> <AccountTable rowData={singleRowData} onClickAsset={() => null} />
); );
}); });
const cells = await screen.getAllByRole('gridcell'); const cells = await screen.findAllByRole('gridcell');
const expectedValues = [ const expectedValues = [
'tBTC', 'tBTC',
'1,256.00000', '1,256.00000',
'1,256.00001,256.0000', '1,256.00001,256.0000',
'Collateral breakdown', 'Collateral breakdown',
'Deposit', 'Deposit',
'Withdraw',
]; ];
cells.forEach((cell, i) => { cells.forEach((cell, i) => {
expect(cell).toHaveTextContent(expectedValues[i]); expect(cell).toHaveTextContent(expectedValues[i]);

View File

@ -151,43 +151,34 @@ export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
}} }}
/> />
<AgGridColumn <AgGridColumn
colId="transact"
headerName="" headerName=""
field="deposit" sortable={false}
maxWidth={200}
cellRenderer={({
value,
data,
}: VegaICellRendererParams<AccountFields, 'asset'>) => {
return (
<Button
size="xs"
data-testid="deposit"
onClick={() => {
onClickDeposit && onClickDeposit(data.asset.id);
}}
>
{t('Deposit')}
</Button>
);
}}
/>
<AgGridColumn
headerName=""
field="withdraw"
maxWidth={200}
cellRenderer={({ cellRenderer={({
data, data,
}: VegaICellRendererParams<AccountFields, 'asset'>) => { }: VegaICellRendererParams<AccountFields>) => {
return ( return (
<Button <div className="flex gap-2 justify-end">
size="xs" <Button
data-testid="withdraw" size="xs"
onClick={() => data-testid="deposit"
onClickWithdraw && onClickWithdraw(data.asset.id) onClick={() => {
} onClickDeposit && onClickDeposit(data.asset.id);
> }}
{t('Withdraw')} >
</Button> {t('Deposit')}
</Button>
<Button
size="xs"
data-testid="withdraw"
onClick={() =>
onClickWithdraw && onClickWithdraw(data.asset.id)
}
>
{t('Withdraw')}
</Button>
</div>
); );
}} }}
/> />

View File

@ -32,16 +32,11 @@ const singleRow = {
const singleRowData = [singleRow]; const singleRowData = [singleRow];
describe('BreakdownTable', () => { describe('BreakdownTable', () => {
it('should render successfully', async () => {
const { baseElement } = render(<BreakdownTable data={[]} />);
expect(baseElement).toBeTruthy();
});
it('should render correct columns', async () => { it('should render correct columns', async () => {
await act(async () => { await act(async () => {
render(<BreakdownTable data={singleRowData} />); render(<BreakdownTable data={singleRowData} />);
}); });
const headers = await screen.getAllByRole('columnheader'); const headers = await screen.findAllByRole('columnheader');
expect(headers).toHaveLength(5); expect(headers).toHaveLength(5);
expect( expect(
headers.map((h) => h.querySelector('[ref="eText"]')?.textContent?.trim()) headers.map((h) => h.querySelector('[ref="eText"]')?.textContent?.trim())
@ -52,7 +47,7 @@ describe('BreakdownTable', () => {
await act(async () => { await act(async () => {
render(<BreakdownTable data={singleRowData} />); render(<BreakdownTable data={singleRowData} />);
}); });
const cells = await screen.getAllByRole('gridcell'); const cells = await screen.findAllByRole('gridcell');
const expectedValues = [ const expectedValues = [
'Margin', 'Margin',
'BTCUSD Monthly (30 Jun 2022)', 'BTCUSD Monthly (30 Jun 2022)',

View File

@ -12,10 +12,11 @@ import type { ValidationProps } from './use-order-validation';
import { marketTranslations } from './use-order-validation'; import { marketTranslations } from './use-order-validation';
import { useOrderValidation } from './use-order-validation'; import { useOrderValidation } from './use-order-validation';
import { ERROR_SIZE_DECIMAL } from './validate-size'; import { ERROR_SIZE_DECIMAL } from './validate-size';
import type { DealTicketMarketFragment } from '../deal-ticket/__generated__/DealTicket';
jest.mock('@vegaprotocol/wallet'); jest.mock('@vegaprotocol/wallet');
const market = { const market: DealTicketMarketFragment = {
id: 'market-id', id: 'market-id',
decimalPlaces: 2, decimalPlaces: 2,
positionDecimalPlaces: 1, positionDecimalPlaces: 1,
@ -25,9 +26,18 @@ const market = {
__typename: 'TradableInstrument', __typename: 'TradableInstrument',
instrument: { instrument: {
__typename: 'Instrument', __typename: 'Instrument',
id: 'instrument-id',
name: 'instrument-name',
product: { product: {
__typename: 'Future', __typename: 'Future',
quoteName: 'quote-name', quoteName: 'quote-name',
settlementAsset: {
__typename: 'Asset',
id: 'asset-id',
symbol: 'asset-symbol',
name: 'asset-name',
decimals: 2,
},
}, },
}, },
}, },
@ -67,12 +77,12 @@ const ERROR = {
'Only limit orders are permitted when market is in auction', 'Only limit orders are permitted when market is in auction',
MARKET_CONTINUOUS_TIF: MARKET_CONTINUOUS_TIF:
'Until the auction ends, you can only place GFA, GTT, or GTC limit orders', 'Until the auction ends, you can only place GFA, GTT, or GTC limit orders',
FIELD_SIZE_REQ: 'You need to provide an amount', FIELD_SIZE_REQ: 'You need to provide a size',
FIELD_SIZE_MIN: `The amount cannot be lower than "${defaultOrder.step}"`, FIELD_SIZE_MIN: `Size cannot be lower than "${defaultOrder.step}"`,
FIELD_PRICE_REQ: 'You need to provide a price', FIELD_PRICE_REQ: 'You need to provide a price',
FIELD_PRICE_MIN: 'The price cannot be negative', FIELD_PRICE_MIN: 'The price cannot be negative',
FIELD_PRICE_STEP_NULL: 'Order sizes must be in whole numbers for this market', FIELD_PRICE_STEP_NULL: 'Order sizes must be in whole numbers for this market',
FIELD_PRICE_STEP_DECIMAL: `The amount field accepts up to ${market.positionDecimalPlaces} decimal places`, FIELD_PRICE_STEP_DECIMAL: `The size field accepts up to ${market.positionDecimalPlaces} decimal places`,
}; };
function setup( function setup(

View File

@ -218,14 +218,14 @@ export const useOrderValidation = ({
if (fieldErrors?.size?.type === 'required') { if (fieldErrors?.size?.type === 'required') {
return { return {
isDisabled: true, isDisabled: true,
message: t('You need to provide an amount'), message: t('You need to provide a size'),
}; };
} }
if (fieldErrors?.size?.type === 'min') { if (fieldErrors?.size?.type === 'min') {
return { return {
isDisabled: true, isDisabled: true,
message: t(`The amount cannot be lower than "${minSize}"`), message: t(`Size cannot be lower than "${minSize}"`),
}; };
} }
@ -262,7 +262,7 @@ export const useOrderValidation = ({
return { return {
isDisabled: true, isDisabled: true,
message: t( message: t(
`The amount field accepts up to ${market.positionDecimalPlaces} decimal places` `The size field accepts up to ${market.positionDecimalPlaces} decimal places`
), ),
}; };
} }

View File

@ -1,4 +1,4 @@
import { render, screen, waitFor } from '@testing-library/react'; import { act, render, screen, waitFor } from '@testing-library/react';
import { getDateTimeFormat } from '@vegaprotocol/react-helpers'; import { getDateTimeFormat } from '@vegaprotocol/react-helpers';
import { Side } from '@vegaprotocol/types'; import { Side } from '@vegaprotocol/types';
import type { PartialDeep } from 'type-fest'; import type { PartialDeep } from 'type-fest';
@ -47,11 +47,12 @@ describe('FillsTable', () => {
}); });
it('correct columns are rendered', async () => { it('correct columns are rendered', async () => {
render(<FillsTable partyId="party-id" rowData={[generateFill()]} />); await act(async () => {
render(<FillsTable partyId="party-id" rowData={[generateFill()]} />);
});
await waitForGridToBeInTheDOM(); await waitForGridToBeInTheDOM();
await waitForDataToHaveLoaded(); await waitForDataToHaveLoaded();
await screen.findByText('Market');
const headers = screen.getAllByRole('columnheader'); const headers = screen.getAllByRole('columnheader');
const expectedHeaders = [ const expectedHeaders = [
'Market', 'Market',

View File

@ -54,8 +54,8 @@ export const Radio = ({ id, value, label, disabled }: RadioProps) => {
); );
const indicatorClasses = classNames( const indicatorClasses = classNames(
'block w-[13px] h-[13px] border-4 rounded-full', 'block w-[13px] h-[13px] border-4 rounded-full',
'border-white dark:bg-black', 'bg-white dark:bg-black',
'dark:border-white dark:bg-black' 'border-black dark:border-white'
); );
return ( return (
<div className={wrapperClasses}> <div className={wrapperClasses}>

View File

@ -132,7 +132,7 @@ const getIntent = (transaction: VegaTxState) => {
case VegaTxStatus.Requested: case VegaTxStatus.Requested:
return Intent.Warning; return Intent.Warning;
case VegaTxStatus.Pending: case VegaTxStatus.Pending:
return Intent.Warning; return Intent.None;
case VegaTxStatus.Error: case VegaTxStatus.Error:
return Intent.Danger; return Intent.Danger;
case VegaTxStatus.Complete: case VegaTxStatus.Complete: