feat: [console-lite] - order review - add info tooltips (#1029)
* feat: [console-lite] - order review - add info tooltips * feat: [console-lite] - order review - add info tooltips - upgrade lib version, fix tests * feat: [console-lite] - info tooltip improve * feat: [console-lite] - make tooltip focusable for mobile handling * feat: [console-lite] - make tooltip add lame int test for mobile * feat: [console-lite] - make tooltip - refactor component with tooltip * feat: [console-lite] - make tooltip - fix failling int test Co-authored-by: maciek <maciek@vegaprotocol.io>
This commit is contained in:
parent
8174962574
commit
dac9b8b015
@ -113,6 +113,9 @@ describe('Market trade', () => {
|
||||
.eq(0)
|
||||
.find('button')
|
||||
.should('have.text', '1');
|
||||
|
||||
cy.getByTestId('max-label').should('have.text', '21');
|
||||
|
||||
cy.getByTestId('percentage-selector')
|
||||
.find('button')
|
||||
.contains('Max')
|
||||
@ -203,6 +206,14 @@ describe('Market trade', () => {
|
||||
connectVegaWallet();
|
||||
cy.get('h3').contains('Review Trade').click();
|
||||
|
||||
cy.getByTestId('review-trade')
|
||||
.get('#contracts_tooltip_trigger')
|
||||
.trigger('click')
|
||||
.realTouch();
|
||||
|
||||
cy.get('[data-radix-popper-content-wrapper]').contains(
|
||||
'The number of contracts determines'
|
||||
);
|
||||
cy.get('#step-3-panel').find('dd').eq(1).should('have.text', '1');
|
||||
|
||||
cy.get('#step-3-panel').find('dd').eq(2).should('have.text', '98.93006');
|
||||
@ -221,4 +232,23 @@ describe('Market trade', () => {
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('info tooltip on mobile view should work well', () => {
|
||||
if (markets?.length) {
|
||||
cy.viewport('iphone-xr');
|
||||
cy.visit(`/trading/${markets[0].id}`);
|
||||
cy.get('h3').contains('Review Trade').click();
|
||||
cy.getByTestId('review-trade')
|
||||
.get('#contracts_tooltip_trigger')
|
||||
.realTouch();
|
||||
cy.get('[data-radix-popper-content-wrapper]').contains(
|
||||
'The number of contracts determines'
|
||||
);
|
||||
|
||||
cy.getByTestId('review-trade').get('div.cursor-help').eq(1).realTouch();
|
||||
cy.get('[data-radix-popper-content-wrapper]').contains(
|
||||
'The notional size represents the position size'
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -23,5 +23,5 @@ NX_VEGA_URL="https://lb.testnet.vega.xyz/query"
|
||||
NX_VEGA_WALLET_URL=http://localhost:1789/api/v1
|
||||
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
|
||||
NX_VEGA_NETWORKS={\"MAINNET\":\"https://alpha.console.vega.xyz\"}
|
||||
NX_VEGA_NETWORKS={"MAINNET":"https://alpha.console.vega.xyz"}
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
||||
|
@ -0,0 +1,17 @@
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
|
||||
export const EST_MARGIN_TOOLTIP_TEXT = t(
|
||||
'When opening a position on a futures market, you must post margin to cover any potential losses that you may incur. The margin is typically a fraction of the notional position size. For example, for a notional position size of $500, if the margin requirement is 10%, then the estimated margin would be approximately $50.'
|
||||
);
|
||||
export const CONTRACTS_MARGIN_TOOLTIP_TEXT = t(
|
||||
'The number of contracts determines how many units of the futures contract to buy or sell. For example, this is similar to buying one share of a listed company. The value of 1 contract is equivalent to the price of the contract. For example, if the current price is $50, then one contract is worth $50.'
|
||||
);
|
||||
export const EST_CLOSEOUT_TOOLTIP_TEXT = t(
|
||||
'Because you only need to post a fraction of your position size as margin when trading futures, it is possible to obtain leverage meaning your notional position size exceeds your account balance. In this scenario, if the market moves against your position, it will sometimes be necessary to force close your position due to insufficient funds. The estimated close out tells you the price at which that would happen based on current position and account balance.'
|
||||
);
|
||||
export const NOTIONAL_SIZE_TOOLTIP_TEXT = t(
|
||||
'The notional size represents the position size in the settlement asset of the futures contract. The notional size is calculated by multiplying the number of contracts by the price of the contract. For example, ten contracts traded at a price of $50 has a notional size of $500.'
|
||||
);
|
||||
export const EST_FEES_TOOLTIP_TEXT = t(
|
||||
'When you execute a new buy or sell order, you must pay a small amount of commission to the network for doing so. This fee is used to provide income to the node operates of the network and market makers who make prices on the futures market you are trading.'
|
||||
);
|
@ -1,6 +1,9 @@
|
||||
import React from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { Icon, Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import * as constants from './constants';
|
||||
|
||||
interface DealTicketEstimatesProps {
|
||||
quoteName?: string;
|
||||
@ -24,6 +27,27 @@ const DataTitle = ({ children, quoteName = '' }: DataTitleProps) => (
|
||||
</dt>
|
||||
);
|
||||
|
||||
interface ValueTooltipProps {
|
||||
value: string;
|
||||
description: string;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
const ValueTooltipRow = ({ value, description, id }: ValueTooltipProps) => (
|
||||
<dd className="flex gap-x-5 items-center">
|
||||
{value}
|
||||
<Tooltip align="center" description={description}>
|
||||
<div className="cursor-help" id={id || ''} tabIndex={-1}>
|
||||
<Icon
|
||||
name={IconNames.ISSUE}
|
||||
className="block rotate-180"
|
||||
ariaLabel={description}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</dd>
|
||||
);
|
||||
|
||||
export const DealTicketEstimates = ({
|
||||
price,
|
||||
quoteName,
|
||||
@ -36,8 +60,12 @@ export const DealTicketEstimates = ({
|
||||
<dl className="text-black dark:text-white">
|
||||
{size && (
|
||||
<div className="flex justify-between mb-8">
|
||||
<DataTitle>{t('No. of Contracts')}</DataTitle>
|
||||
<dd>{size}</dd>
|
||||
<DataTitle>{t('Contracts')}</DataTitle>
|
||||
<ValueTooltipRow
|
||||
value={size}
|
||||
description={constants.CONTRACTS_MARGIN_TOOLTIP_TEXT}
|
||||
id="contracts_tooltip_trigger"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{price && (
|
||||
@ -49,19 +77,28 @@ export const DealTicketEstimates = ({
|
||||
{notionalSize && (
|
||||
<div className="flex justify-between mb-8">
|
||||
<DataTitle quoteName={quoteName}>{t('Est. Position Size')}</DataTitle>
|
||||
<dd>{notionalSize}</dd>
|
||||
<ValueTooltipRow
|
||||
value={notionalSize}
|
||||
description={constants.NOTIONAL_SIZE_TOOLTIP_TEXT}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{fees && (
|
||||
<div className="flex justify-between mb-8">
|
||||
<DataTitle quoteName={quoteName}>{t('Est. Fees')}</DataTitle>
|
||||
<dd>{fees}</dd>
|
||||
<ValueTooltipRow
|
||||
value={fees}
|
||||
description={constants.EST_FEES_TOOLTIP_TEXT}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{estMargin && (
|
||||
<div className="flex justify-between mb-8">
|
||||
<DataTitle quoteName={quoteName}>{t('Est. Margin')}</DataTitle>
|
||||
<dd>{estMargin}</dd>
|
||||
<ValueTooltipRow
|
||||
value={estMargin}
|
||||
description={constants.EST_MARGIN_TOOLTIP_TEXT}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{estCloseOut && (
|
||||
@ -71,7 +108,10 @@ export const DealTicketEstimates = ({
|
||||
|
||||
<small>({quoteName})</small>
|
||||
</dt>
|
||||
<dd>{estCloseOut}</dd>
|
||||
<ValueTooltipRow
|
||||
value={estCloseOut}
|
||||
description={constants.EST_CLOSEOUT_TOOLTIP_TEXT}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</dl>
|
||||
|
@ -109,8 +109,8 @@ export const DealTicketSize = ({
|
||||
) : (
|
||||
<div>
|
||||
<div className="flex justify-between text-black dark:text-white mb-8">
|
||||
<span>{min}</span>
|
||||
<span>{max}</span>
|
||||
<span data-testid="min-label">{min}</span>
|
||||
<span data-testid="max-label">{max}</span>
|
||||
</div>
|
||||
<SliderRoot
|
||||
className="mb-8"
|
||||
|
@ -64,7 +64,7 @@ export default ({
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="mb-8 text-black dark:text-white">
|
||||
<div className="mb-8 text-black dark:text-white" data-testid="review-trade">
|
||||
<KeyValueTable>
|
||||
<KeyValueTableRow noBorder>
|
||||
<div className="flex flex-none gap-x-5 items-center">
|
||||
|
@ -51,6 +51,7 @@ export default ({ marketId, partyId }: Props): PositionMargin => {
|
||||
pollInterval: 5000,
|
||||
variables: { partyId },
|
||||
skip: !partyId,
|
||||
fetchPolicy: 'no-cache',
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -23,8 +23,11 @@ export function createClient(base?: string) {
|
||||
|
||||
const cache = new InMemoryCache({
|
||||
typePolicies: {
|
||||
Market: {
|
||||
merge: true,
|
||||
},
|
||||
Party: {
|
||||
keyFields: false,
|
||||
merge: true,
|
||||
},
|
||||
Query: {},
|
||||
Account: {
|
||||
|
@ -87,6 +87,35 @@ const vegaCustomClassesLite = plugin(function ({ addUtilities }) {
|
||||
color: theme.colors.black.DEFAULT,
|
||||
},
|
||||
},
|
||||
'.tooltip-content': {
|
||||
'& > div': {
|
||||
fontSize: '12px',
|
||||
borderWeight: '1px',
|
||||
borderRadius: '7px',
|
||||
borderColor: theme.colors.black.DEFAULT,
|
||||
backgroundColor: theme.colors.black.DEFAULT,
|
||||
color: theme.colors.white.DEFAULT,
|
||||
},
|
||||
'& svg[width="10"]': {
|
||||
fill: theme.colors.black.DEFAULT,
|
||||
},
|
||||
'& svg[width="8"]': {
|
||||
fill: theme.colors.black.DEFAULT,
|
||||
},
|
||||
},
|
||||
'.tooltip-content-dark': {
|
||||
'& > div': {
|
||||
borderColor: theme.colors.black.DEFAULT,
|
||||
backgroundColor: '#dcdcc8',
|
||||
color: '#333333',
|
||||
},
|
||||
'& svg[width="10"]': {
|
||||
fill: theme.colors.white.DEFAULT,
|
||||
},
|
||||
'& svg[width="8"]': {
|
||||
fill: '#dcdcc8',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1,7 +1,19 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import { Tooltip } from './tooltip';
|
||||
|
||||
it('Renders a tooltip', async () => {
|
||||
const props = {
|
||||
description: 'description',
|
||||
children: <button>Tooltip</button>,
|
||||
};
|
||||
render(<Tooltip {...props} />);
|
||||
// radix applies the data-state attribute
|
||||
expect(screen.getByRole('button')).toHaveAttribute('data-state', 'closed');
|
||||
fireEvent.pointerMove(screen.getByRole('button'));
|
||||
expect(await screen.findByRole('tooltip')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders a controlled tooltip', async () => {
|
||||
const props = {
|
||||
description: 'description',
|
||||
children: <button>Tooltip</button>,
|
||||
@ -15,6 +27,20 @@ it('Renders a tooltip', async () => {
|
||||
});
|
||||
|
||||
it('Doesnt render a tooltip if no description provided', () => {
|
||||
const props = {
|
||||
description: undefined,
|
||||
children: <button>Tooltip</button>,
|
||||
};
|
||||
render(<Tooltip {...props} />);
|
||||
expect(screen.getByRole('button')).not.toHaveAttribute(
|
||||
'data-state',
|
||||
'closed'
|
||||
);
|
||||
fireEvent.pointerMove(screen.getByRole('button'));
|
||||
expect(screen.queryByRole('tooltip')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Doesnt render a controlled tooltip if no description provided', () => {
|
||||
const props = {
|
||||
description: undefined,
|
||||
children: <button>Tooltip</button>,
|
||||
|
@ -24,7 +24,11 @@ export const Tooltip = ({ children, description, open, align }: TooltipProps) =>
|
||||
<Trigger asChild>{children}</Trigger>
|
||||
{description && (
|
||||
<Portal>
|
||||
<Content align={align} alignOffset={8}>
|
||||
<Content
|
||||
align={align}
|
||||
alignOffset={8}
|
||||
className="tooltip-content dark:tooltip-content-dark"
|
||||
>
|
||||
<div className="relative z-0 p-8 bg-black-50 border border-black-60 text-white rounded-sm max-w-sm text-ui">
|
||||
{description}
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user