fix: order warning messages (#1037)
* fix: order validation updates & warning messages * fix: add extra warning message * fix: order list and test * fix: text-ui on loading market data... * fix: when market is cancelled it can't accept orders * fix: display rejection reason behind stopped orders * fix: remove punctuation marks from warning/reasoning message * fix: format order-feedback * fix: order feedback test * fix: do not use market state to display * fix: format use order valid hook * fix: add required and min price on edit and deal ticket * fix: remove price validation on market orders as there is no price input * fix: check format * fix: format error labels * fix: order validation test * Update package.json with test:all * Update libs/orders/src/lib/components/order-feedback/order-feedback.tsx Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> * fix: add step on input * fix: remove decimal places format on init * fix: input type fix * fix: order edit price with toDecimals stepper * fix: remove set value and null check in form * fix: use form validate on edit * fix: try fixing test after step added * fix: making rejection reason startcase again Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com>
This commit is contained in:
parent
ba3460496d
commit
dbb21a4745
@ -60,6 +60,14 @@ export const getOrderDialogTitle = (
|
|||||||
return t('Order partially filled');
|
return t('Order partially filled');
|
||||||
case OrderStatus.Parked:
|
case OrderStatus.Parked:
|
||||||
return t('Order parked');
|
return t('Order parked');
|
||||||
|
case OrderStatus.Stopped:
|
||||||
|
return t('Order stopped');
|
||||||
|
case OrderStatus.Cancelled:
|
||||||
|
return t('Order cancelled');
|
||||||
|
case OrderStatus.Expired:
|
||||||
|
return t('Order expired');
|
||||||
|
case OrderStatus.Rejected:
|
||||||
|
return t('Order rejected');
|
||||||
default:
|
default:
|
||||||
return t('Submission failed');
|
return t('Submission failed');
|
||||||
}
|
}
|
||||||
@ -71,15 +79,18 @@ export const getOrderDialogIntent = (
|
|||||||
if (!status) {
|
if (!status) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case OrderStatus.Parked:
|
case OrderStatus.Parked:
|
||||||
case OrderStatus.Expired:
|
case OrderStatus.Expired:
|
||||||
|
case OrderStatus.PartiallyFilled:
|
||||||
return Intent.Warning;
|
return Intent.Warning;
|
||||||
case OrderStatus.Rejected:
|
case OrderStatus.Rejected:
|
||||||
case OrderStatus.Stopped:
|
case OrderStatus.Stopped:
|
||||||
case OrderStatus.Cancelled:
|
case OrderStatus.Cancelled:
|
||||||
return Intent.Danger;
|
return Intent.Danger;
|
||||||
|
case OrderStatus.Filled:
|
||||||
|
case OrderStatus.Active:
|
||||||
|
return Intent.Success;
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -132,6 +132,7 @@ export const DealTicket = ({
|
|||||||
</Button>
|
</Button>
|
||||||
{message && (
|
{message && (
|
||||||
<InputError
|
<InputError
|
||||||
|
intent={isDisabled ? 'danger' : 'warning'}
|
||||||
className="mt-12 mb-12"
|
className="mt-12 mb-12"
|
||||||
data-testid="dealticket-error-message"
|
data-testid="dealticket-error-message"
|
||||||
>
|
>
|
||||||
|
@ -100,7 +100,7 @@ export const SelectAllMarketsTableBody = ({
|
|||||||
) : (
|
) : (
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<td className="text-black dark:text-white text-h5">
|
<td className="text-black dark:text-white text-ui">
|
||||||
{t('Loading market data...')}
|
{t('Loading market data...')}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import { formatLabel } from '@vegaprotocol/react-helpers';
|
|
||||||
import {
|
import {
|
||||||
OrderRejectionReason,
|
OrderRejectionReason,
|
||||||
OrderStatus,
|
OrderStatus,
|
||||||
@ -7,6 +6,7 @@ import {
|
|||||||
Side,
|
Side,
|
||||||
} from '@vegaprotocol/types';
|
} from '@vegaprotocol/types';
|
||||||
import { VegaTxStatus } from '@vegaprotocol/wallet';
|
import { VegaTxStatus } from '@vegaprotocol/wallet';
|
||||||
|
import startCase from 'lodash/startCase';
|
||||||
import { generateOrder } from '../mocks/generate-orders';
|
import { generateOrder } from '../mocks/generate-orders';
|
||||||
import type { OrderFeedbackProps } from './order-feedback';
|
import type { OrderFeedbackProps } from './order-feedback';
|
||||||
import { OrderFeedback } from './order-feedback';
|
import { OrderFeedback } from './order-feedback';
|
||||||
@ -45,7 +45,7 @@ describe('OrderFeedback', () => {
|
|||||||
const order = generateOrder(orderFields);
|
const order = generateOrder(orderFields);
|
||||||
render(<OrderFeedback {...props} order={order} />);
|
render(<OrderFeedback {...props} order={order} />);
|
||||||
expect(screen.getByTestId('error-reason')).toHaveTextContent(
|
expect(screen.getByTestId('error-reason')).toHaveTextContent(
|
||||||
`Reason: ${formatLabel(orderFields.rejectionReason)}`
|
`${startCase(orderFields.rejectionReason)}`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
import { useEnvironment } from '@vegaprotocol/environment';
|
import { useEnvironment } from '@vegaprotocol/environment';
|
||||||
import type { OrderEvent_busEvents_event_Order } from '../../order-hooks/__generated__';
|
import type { OrderEvent_busEvents_event_Order } from '../../order-hooks/__generated__';
|
||||||
import {
|
import { addDecimalsFormatNumber, t } from '@vegaprotocol/react-helpers';
|
||||||
addDecimalsFormatNumber,
|
|
||||||
formatLabel,
|
|
||||||
t,
|
|
||||||
} from '@vegaprotocol/react-helpers';
|
|
||||||
import { OrderStatus, OrderType, Side } from '@vegaprotocol/types';
|
import { OrderStatus, OrderType, Side } from '@vegaprotocol/types';
|
||||||
import type { VegaTxState } from '@vegaprotocol/wallet';
|
import type { VegaTxState } from '@vegaprotocol/wallet';
|
||||||
|
import startCase from 'lodash/startCase';
|
||||||
|
|
||||||
export interface OrderFeedbackProps {
|
export interface OrderFeedbackProps {
|
||||||
transaction: VegaTxState;
|
transaction: VegaTxState;
|
||||||
@ -18,46 +15,7 @@ export const OrderFeedback = ({ transaction, order }: OrderFeedbackProps) => {
|
|||||||
const labelClass = 'font-bold text-black dark:text-white';
|
const labelClass = 'font-bold text-black dark:text-white';
|
||||||
if (!order) return null;
|
if (!order) return null;
|
||||||
|
|
||||||
// Order on network but was rejected
|
const orderRejectionReason = getRejectionReason(order);
|
||||||
if (order.status === OrderStatus.Rejected) {
|
|
||||||
return (
|
|
||||||
<p data-testid="error-reason">
|
|
||||||
{order.rejectionReason &&
|
|
||||||
t(`Reason: ${formatLabel(order.rejectionReason)}`)}
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (order.status === OrderStatus.Cancelled) {
|
|
||||||
return (
|
|
||||||
<div data-testid="order-confirmed">
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 mb-8">
|
|
||||||
{order.market && (
|
|
||||||
<div>
|
|
||||||
<p className={labelClass}>{t(`Market`)}</p>
|
|
||||||
<p>{t(`${order.market.name}`)}</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
{transaction.txHash && (
|
|
||||||
<div>
|
|
||||||
<p className={labelClass}>{t('Transaction')}</p>
|
|
||||||
<a
|
|
||||||
className="underline break-words"
|
|
||||||
data-testid="tx-block-explorer"
|
|
||||||
href={`${VEGA_EXPLORER_URL}/txs/0x${transaction.txHash}`}
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
{transaction.txHash}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-testid="order-confirmed">
|
<div data-testid="order-confirmed">
|
||||||
@ -113,6 +71,27 @@ export const OrderFeedback = ({ transaction, order }: OrderFeedbackProps) => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{orderRejectionReason && (
|
||||||
|
<div>
|
||||||
|
<p className={labelClass}>{t(`Reason`)}</p>
|
||||||
|
<p data-testid="error-reason">{t(orderRejectionReason)}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getRejectionReason = (
|
||||||
|
order: OrderEvent_busEvents_event_Order
|
||||||
|
): string | null => {
|
||||||
|
switch (order.status) {
|
||||||
|
case OrderStatus.Stopped:
|
||||||
|
return t(
|
||||||
|
`Your ${order.timeInForce} order was not filled and it has been stopped`
|
||||||
|
);
|
||||||
|
case OrderStatus.Rejected:
|
||||||
|
return order.rejectionReason && t(startCase(order.rejectionReason));
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
addDecimal,
|
|
||||||
t,
|
t,
|
||||||
addDecimalsFormatNumber,
|
addDecimalsFormatNumber,
|
||||||
|
toDecimal,
|
||||||
} from '@vegaprotocol/react-helpers';
|
} from '@vegaprotocol/react-helpers';
|
||||||
import { OrderType } from '@vegaprotocol/types';
|
import { OrderType } from '@vegaprotocol/types';
|
||||||
import {
|
import {
|
||||||
@ -18,7 +18,7 @@ import type { OrderFields } from '../order-data-provider';
|
|||||||
interface OrderEditDialogProps {
|
interface OrderEditDialogProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onChange: (isOpen: boolean) => void;
|
onChange: (isOpen: boolean) => void;
|
||||||
order: OrderFields | null;
|
order: OrderFields;
|
||||||
onSubmit: (fields: FormFields) => void;
|
onSubmit: (fields: FormFields) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,15 +37,9 @@ export const OrderEditDialog = ({
|
|||||||
register,
|
register,
|
||||||
formState: { errors },
|
formState: { errors },
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
} = useForm<FormFields>({
|
} = useForm<FormFields>();
|
||||||
defaultValues: {
|
|
||||||
entryPrice: order?.price
|
|
||||||
? addDecimal(order?.price, order?.market?.decimalPlaces ?? 0)
|
|
||||||
: '',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!order) return null;
|
const step = toDecimal(order.market?.decimalPlaces ?? 0);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
@ -88,9 +82,18 @@ export const OrderEditDialog = ({
|
|||||||
<form onSubmit={handleSubmit(onSubmit)} data-testid="edit-order">
|
<form onSubmit={handleSubmit(onSubmit)} data-testid="edit-order">
|
||||||
<FormGroup label={t('Entry price')} labelFor="entryPrice">
|
<FormGroup label={t('Entry price')} labelFor="entryPrice">
|
||||||
<Input
|
<Input
|
||||||
{...register('entryPrice', { required: t('Required') })}
|
type="number"
|
||||||
|
step={step}
|
||||||
|
{...register('entryPrice', {
|
||||||
|
required: t('You need to provide a price'),
|
||||||
|
validate: {
|
||||||
|
min: (value) =>
|
||||||
|
Number(value) > 0
|
||||||
|
? true
|
||||||
|
: t('The price cannot be negative'),
|
||||||
|
},
|
||||||
|
})}
|
||||||
id="entryPrice"
|
id="entryPrice"
|
||||||
type="text"
|
|
||||||
/>
|
/>
|
||||||
{errors.entryPrice?.message && (
|
{errors.entryPrice?.message && (
|
||||||
<InputError intent="danger" className="mt-4">
|
<InputError intent="danger" className="mt-4">
|
||||||
|
@ -1,9 +1,5 @@
|
|||||||
import { act, render, screen } from '@testing-library/react';
|
import { act, render, screen } from '@testing-library/react';
|
||||||
import {
|
import { addDecimal, getDateTimeFormat } from '@vegaprotocol/react-helpers';
|
||||||
addDecimal,
|
|
||||||
formatLabel,
|
|
||||||
getDateTimeFormat,
|
|
||||||
} from '@vegaprotocol/react-helpers';
|
|
||||||
import { OrderStatus, OrderRejectionReason } from '@vegaprotocol/types';
|
import { OrderStatus, OrderRejectionReason } from '@vegaprotocol/types';
|
||||||
import type { PartialDeep } from 'type-fest';
|
import type { PartialDeep } from 'type-fest';
|
||||||
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
|
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
|
||||||
@ -13,6 +9,7 @@ import { MockedProvider } from '@apollo/client/testing';
|
|||||||
import { OrderListTable } from '../';
|
import { OrderListTable } from '../';
|
||||||
import type { Orders_party_ordersConnection_edges_node } from '../';
|
import type { Orders_party_ordersConnection_edges_node } from '../';
|
||||||
import { limitOrder, marketOrder } from '../mocks/generate-orders';
|
import { limitOrder, marketOrder } from '../mocks/generate-orders';
|
||||||
|
import startCase from 'lodash/startCase';
|
||||||
|
|
||||||
const generateJsx = (
|
const generateJsx = (
|
||||||
orders: Orders_party_ordersConnection_edges_node[] | null,
|
orders: Orders_party_ordersConnection_edges_node[] | null,
|
||||||
@ -122,7 +119,7 @@ describe('OrderListTable', () => {
|
|||||||
});
|
});
|
||||||
const cells = screen.getAllByRole('gridcell');
|
const cells = screen.getAllByRole('gridcell');
|
||||||
expect(cells[3]).toHaveTextContent(
|
expect(cells[3]).toHaveTextContent(
|
||||||
`${rejectedOrder.status}: ${formatLabel(rejectedOrder.rejectionReason)}`
|
`${rejectedOrder.status}: ${startCase(rejectedOrder.rejectionReason)}`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -29,7 +29,7 @@ const Template: Story = (args) => {
|
|||||||
|
|
||||||
const Template2: Story = (args) => {
|
const Template2: Story = (args) => {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [editOrder, setEditOrder] = useState<OrderFields | null>(null);
|
const [editOrder, setEditOrder] = useState<OrderFields>();
|
||||||
const cancel = () => {
|
const cancel = () => {
|
||||||
setOpen(!open);
|
setOpen(!open);
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
@ -57,16 +57,18 @@ const Template2: Story = (args) => {
|
|||||||
onChange={setOpen}
|
onChange={setOpen}
|
||||||
transaction={transaction}
|
transaction={transaction}
|
||||||
/>
|
/>
|
||||||
|
{editOrder && (
|
||||||
<OrderEditDialog
|
<OrderEditDialog
|
||||||
isOpen={Boolean(editOrder)}
|
isOpen={Boolean(editOrder)}
|
||||||
onChange={(isOpen) => {
|
onChange={(isOpen) => {
|
||||||
if (!isOpen) setEditOrder(null);
|
if (!isOpen) setEditOrder(undefined);
|
||||||
}}
|
}}
|
||||||
order={editOrder}
|
order={editOrder}
|
||||||
onSubmit={(fields) => {
|
onSubmit={(fields) => {
|
||||||
return;
|
return;
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,10 +1,5 @@
|
|||||||
import { OrderTimeInForce, OrderStatus, Side } from '@vegaprotocol/types';
|
import { OrderTimeInForce, OrderStatus, Side } from '@vegaprotocol/types';
|
||||||
import {
|
import { addDecimal, getDateTimeFormat, t } from '@vegaprotocol/react-helpers';
|
||||||
addDecimal,
|
|
||||||
formatLabel,
|
|
||||||
getDateTimeFormat,
|
|
||||||
t,
|
|
||||||
} from '@vegaprotocol/react-helpers';
|
|
||||||
import {
|
import {
|
||||||
AgGridDynamic as AgGrid,
|
AgGridDynamic as AgGrid,
|
||||||
Button,
|
Button,
|
||||||
@ -29,6 +24,7 @@ import { useOrderEdit } from '../../order-hooks/use-order-edit';
|
|||||||
import { OrderEditDialog } from './order-edit-dialog';
|
import { OrderEditDialog } from './order-edit-dialog';
|
||||||
import type { OrderFields } from '../order-data-provider/__generated__';
|
import type { OrderFields } from '../order-data-provider/__generated__';
|
||||||
import { OrderFeedback } from '../order-feedback';
|
import { OrderFeedback } from '../order-feedback';
|
||||||
|
import startCase from 'lodash/startCase';
|
||||||
|
|
||||||
type OrderListProps = AgGridReactProps | AgReactUiProps;
|
type OrderListProps = AgGridReactProps | AgReactUiProps;
|
||||||
|
|
||||||
@ -69,6 +65,7 @@ export const OrderList = forwardRef<AgGridReact, OrderListProps>(
|
|||||||
order={orderEdit.updatedOrder}
|
order={orderEdit.updatedOrder}
|
||||||
/>
|
/>
|
||||||
</orderEdit.TransactionDialog>
|
</orderEdit.TransactionDialog>
|
||||||
|
{editOrder && (
|
||||||
<OrderEditDialog
|
<OrderEditDialog
|
||||||
isOpen={Boolean(editOrder)}
|
isOpen={Boolean(editOrder)}
|
||||||
onChange={(isOpen) => {
|
onChange={(isOpen) => {
|
||||||
@ -80,6 +77,7 @@ export const OrderList = forwardRef<AgGridReact, OrderListProps>(
|
|||||||
orderEdit.edit({ price: fields.entryPrice });
|
orderEdit.edit({ price: fields.entryPrice });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -158,7 +156,7 @@ export const OrderListTable = forwardRef<AgGridReact, OrderListTableProps>(
|
|||||||
}
|
}
|
||||||
if (value === OrderStatus.Rejected) {
|
if (value === OrderStatus.Rejected) {
|
||||||
return `${value}: ${
|
return `${value}: ${
|
||||||
data.rejectionReason && formatLabel(data.rejectionReason)
|
data.rejectionReason && startCase(data.rejectionReason)
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
@ -316,8 +314,16 @@ const getEditDialogTitle = (status?: OrderStatus): string | undefined => {
|
|||||||
return t('Order partially filled');
|
return t('Order partially filled');
|
||||||
case OrderStatus.Parked:
|
case OrderStatus.Parked:
|
||||||
return t('Order parked');
|
return t('Order parked');
|
||||||
|
case OrderStatus.Stopped:
|
||||||
|
return t('Order stopped');
|
||||||
|
case OrderStatus.Expired:
|
||||||
|
return t('Order expired');
|
||||||
|
case OrderStatus.Cancelled:
|
||||||
|
return t('Order cancelled');
|
||||||
|
case OrderStatus.Rejected:
|
||||||
|
return t('Order rejected');
|
||||||
default:
|
default:
|
||||||
return t('Submission failed');
|
return t('Order amendment failed');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { act, renderHook } from '@testing-library/react';
|
import { act, renderHook } from '@testing-library/react';
|
||||||
import type { Order } from '../utils';
|
|
||||||
import type {
|
import type {
|
||||||
VegaKeyExtended,
|
VegaKeyExtended,
|
||||||
VegaWalletContextShape,
|
VegaWalletContextShape,
|
||||||
@ -12,6 +11,7 @@ import {
|
|||||||
} from '@vegaprotocol/wallet';
|
} from '@vegaprotocol/wallet';
|
||||||
import { MarketState, MarketTradingMode } from '@vegaprotocol/types';
|
import { MarketState, MarketTradingMode } from '@vegaprotocol/types';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
|
import type { Order } from './use-order-submit';
|
||||||
import { useOrderSubmit } from './use-order-submit';
|
import { useOrderSubmit } from './use-order-submit';
|
||||||
import type {
|
import type {
|
||||||
OrderEvent,
|
OrderEvent,
|
||||||
@ -20,7 +20,6 @@ import type {
|
|||||||
import { ORDER_EVENT_SUB } from './order-event-query';
|
import { ORDER_EVENT_SUB } from './order-event-query';
|
||||||
import type { MockedResponse } from '@apollo/client/testing';
|
import type { MockedResponse } from '@apollo/client/testing';
|
||||||
import { MockedProvider } from '@apollo/client/testing';
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
import type { Market } from '../market';
|
|
||||||
import { toNanoSeconds } from '@vegaprotocol/react-helpers';
|
import { toNanoSeconds } from '@vegaprotocol/react-helpers';
|
||||||
|
|
||||||
const defaultMarket = {
|
const defaultMarket = {
|
||||||
@ -47,7 +46,7 @@ const defaultMarket = {
|
|||||||
price: '100',
|
price: '100',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as Market;
|
};
|
||||||
|
|
||||||
const defaultWalletContext = {
|
const defaultWalletContext = {
|
||||||
keypair: null,
|
keypair: null,
|
||||||
@ -158,7 +157,7 @@ describe('useOrderSubmit', () => {
|
|||||||
keypair,
|
keypair,
|
||||||
});
|
});
|
||||||
|
|
||||||
const order: Order = {
|
const order = {
|
||||||
type: VegaWalletOrderType.Limit,
|
type: VegaWalletOrderType.Limit,
|
||||||
size: '10',
|
size: '10',
|
||||||
timeInForce: VegaWalletOrderTimeInForce.GTT,
|
timeInForce: VegaWalletOrderTimeInForce.GTT,
|
||||||
|
@ -13,11 +13,10 @@ 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 '../utils/validate-size';
|
import { ERROR_SIZE_DECIMAL } from '../utils/validate-size';
|
||||||
import type { Market } from '../market';
|
|
||||||
|
|
||||||
jest.mock('@vegaprotocol/wallet');
|
jest.mock('@vegaprotocol/wallet');
|
||||||
|
|
||||||
const market: Market = {
|
const market = {
|
||||||
__typename: 'Market',
|
__typename: 'Market',
|
||||||
id: 'market-id',
|
id: 'market-id',
|
||||||
decimalPlaces: 2,
|
decimalPlaces: 2,
|
||||||
@ -114,6 +113,8 @@ describe('useOrderValidation', () => {
|
|||||||
${MarketState.Settled}
|
${MarketState.Settled}
|
||||||
${MarketState.Rejected}
|
${MarketState.Rejected}
|
||||||
${MarketState.TradingTerminated}
|
${MarketState.TradingTerminated}
|
||||||
|
${MarketState.Closed}
|
||||||
|
${MarketState.Cancelled}
|
||||||
`(
|
`(
|
||||||
'Returns an error message for market state when not accepting orders',
|
'Returns an error message for market state when not accepting orders',
|
||||||
({ state }) => {
|
({ state }) => {
|
||||||
@ -129,11 +130,8 @@ describe('useOrderValidation', () => {
|
|||||||
|
|
||||||
it.each`
|
it.each`
|
||||||
state
|
state
|
||||||
${MarketState.Suspended}
|
|
||||||
${MarketState.Pending}
|
${MarketState.Pending}
|
||||||
${MarketState.Cancelled}
|
|
||||||
${MarketState.Proposed}
|
${MarketState.Proposed}
|
||||||
${MarketState.Closed}
|
|
||||||
`(
|
`(
|
||||||
'Returns an error message for market state suspended or pending',
|
'Returns an error message for market state suspended or pending',
|
||||||
({ state }) => {
|
({ state }) => {
|
||||||
@ -207,6 +205,7 @@ describe('useOrderValidation', () => {
|
|||||||
({ fieldName, errorType, errorMessage }) => {
|
({ fieldName, errorType, errorMessage }) => {
|
||||||
const { result } = setup({
|
const { result } = setup({
|
||||||
fieldErrors: { [fieldName]: { type: errorType } },
|
fieldErrors: { [fieldName]: { type: errorType } },
|
||||||
|
orderType: VegaWalletOrderType.Limit,
|
||||||
});
|
});
|
||||||
expect(result.current).toStrictEqual({
|
expect(result.current).toStrictEqual({
|
||||||
isDisabled: true,
|
isDisabled: true,
|
||||||
|
@ -10,7 +10,8 @@ import { MarketState, MarketTradingMode } from '@vegaprotocol/types';
|
|||||||
import { ERROR_SIZE_DECIMAL } from '../utils/validate-size';
|
import { ERROR_SIZE_DECIMAL } from '../utils/validate-size';
|
||||||
import type { Order } from './use-order-submit';
|
import type { Order } from './use-order-submit';
|
||||||
|
|
||||||
export type ValidationArgs = {
|
export type ValidationProps = {
|
||||||
|
step?: number;
|
||||||
market: {
|
market: {
|
||||||
state: MarketState;
|
state: MarketState;
|
||||||
tradingMode: MarketTradingMode;
|
tradingMode: MarketTradingMode;
|
||||||
@ -35,7 +36,7 @@ export const useOrderValidation = ({
|
|||||||
fieldErrors = {},
|
fieldErrors = {},
|
||||||
orderType,
|
orderType,
|
||||||
orderTimeInForce,
|
orderTimeInForce,
|
||||||
}: ValidationArgs) => {
|
}: ValidationProps) => {
|
||||||
const { keypair } = useVegaWallet();
|
const { keypair } = useVegaWallet();
|
||||||
const minSize = toDecimal(market.positionDecimalPlaces);
|
const minSize = toDecimal(market.positionDecimalPlaces);
|
||||||
|
|
||||||
@ -56,6 +57,8 @@ export const useOrderValidation = ({
|
|||||||
MarketState.Settled,
|
MarketState.Settled,
|
||||||
MarketState.Rejected,
|
MarketState.Rejected,
|
||||||
MarketState.TradingTerminated,
|
MarketState.TradingTerminated,
|
||||||
|
MarketState.Cancelled,
|
||||||
|
MarketState.Closed,
|
||||||
].includes(market.state)
|
].includes(market.state)
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
@ -68,15 +71,7 @@ export const useOrderValidation = ({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if ([MarketState.Proposed, MarketState.Pending].includes(market.state)) {
|
||||||
[
|
|
||||||
MarketState.Suspended,
|
|
||||||
MarketState.Pending,
|
|
||||||
MarketState.Proposed,
|
|
||||||
MarketState.Cancelled,
|
|
||||||
MarketState.Closed,
|
|
||||||
].includes(market.state)
|
|
||||||
) {
|
|
||||||
return {
|
return {
|
||||||
isDisabled: false,
|
isDisabled: false,
|
||||||
message: t(
|
message: t(
|
||||||
@ -87,65 +82,13 @@ export const useOrderValidation = ({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (market.state !== MarketState.Active) {
|
|
||||||
if (market.state === MarketState.Suspended) {
|
|
||||||
if (market.tradingMode === MarketTradingMode.Continuous) {
|
|
||||||
if (orderType !== OrderType.Limit) {
|
|
||||||
return {
|
|
||||||
isDisabled: true,
|
|
||||||
message: t(
|
|
||||||
'Only limit orders are permitted when market is in auction'
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
[
|
[
|
||||||
OrderTimeInForce.FOK,
|
MarketTradingMode.BatchAuction,
|
||||||
OrderTimeInForce.IOC,
|
MarketTradingMode.MonitoringAuction,
|
||||||
OrderTimeInForce.GFN,
|
MarketTradingMode.OpeningAuction,
|
||||||
].includes(orderTimeInForce)
|
].includes(market.tradingMode)
|
||||||
) {
|
) {
|
||||||
return {
|
|
||||||
isDisabled: true,
|
|
||||||
message: t(
|
|
||||||
'Only GTT, GTC and GFA are permitted when market is in auction'
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
isDisabled: false,
|
|
||||||
message: t(
|
|
||||||
`This market is ${marketTranslations(
|
|
||||||
market.state
|
|
||||||
)} and only accepting liquidity commitment orders`
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
market.state === MarketState.Proposed ||
|
|
||||||
market.state === MarketState.Pending
|
|
||||||
) {
|
|
||||||
return {
|
|
||||||
isDisabled: false,
|
|
||||||
message: t(
|
|
||||||
`This market is ${marketTranslations(
|
|
||||||
market.state
|
|
||||||
)} and only accepting liquidity commitment orders`
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
isDisabled: true,
|
|
||||||
message: t('This market is no longer active.'),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (market.tradingMode !== MarketTradingMode.Continuous) {
|
|
||||||
if (orderType !== OrderType.Limit) {
|
if (orderType !== OrderType.Limit) {
|
||||||
return {
|
return {
|
||||||
isDisabled: true,
|
isDisabled: true,
|
||||||
@ -185,14 +128,17 @@ export const useOrderValidation = ({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fieldErrors?.price?.type === 'required') {
|
if (
|
||||||
|
fieldErrors?.price?.type === 'required' &&
|
||||||
|
orderType !== OrderType.Market
|
||||||
|
) {
|
||||||
return {
|
return {
|
||||||
isDisabled: true,
|
isDisabled: true,
|
||||||
message: t('You need to provide a price'),
|
message: t('You need to provide a price'),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fieldErrors?.price?.type === 'min') {
|
if (fieldErrors?.price?.type === 'min' && orderType !== OrderType.Market) {
|
||||||
return {
|
return {
|
||||||
isDisabled: true,
|
isDisabled: true,
|
||||||
message: t(`The price cannot be negative`),
|
message: t(`The price cannot be negative`),
|
||||||
@ -217,6 +163,21 @@ export const useOrderValidation = ({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
[
|
||||||
|
MarketTradingMode.BatchAuction,
|
||||||
|
MarketTradingMode.MonitoringAuction,
|
||||||
|
MarketTradingMode.OpeningAuction,
|
||||||
|
].includes(market.tradingMode)
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
isDisabled: false,
|
||||||
|
message: t(
|
||||||
|
'Any orders placed now will not trade until the auction ends'
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return { isDisabled: false, message: '' };
|
return { isDisabled: false, message: '' };
|
||||||
}, [
|
}, [
|
||||||
minSize,
|
minSize,
|
||||||
|
@ -4,7 +4,7 @@ import memoize from 'lodash/memoize';
|
|||||||
import { getUserLocale } from './utils';
|
import { getUserLocale } from './utils';
|
||||||
|
|
||||||
export function toDecimal(numberOfDecimals: number) {
|
export function toDecimal(numberOfDecimals: number) {
|
||||||
return Math.pow(10, -numberOfDecimals);
|
return 1 / Math.pow(10, numberOfDecimals);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toBigNum(
|
export function toBigNum(
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useEnvironment } from '@vegaprotocol/environment';
|
import { useEnvironment } from '@vegaprotocol/environment';
|
||||||
import { t } from '@vegaprotocol/react-helpers';
|
import { formatLabel, t } from '@vegaprotocol/react-helpers';
|
||||||
import { Dialog, Icon, Intent, Loader } from '@vegaprotocol/ui-toolkit';
|
import { Dialog, Icon, Intent, Loader } from '@vegaprotocol/ui-toolkit';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import type { VegaTxState } from '../use-vega-transaction';
|
import type { VegaTxState } from '../use-vega-transaction';
|
||||||
@ -72,7 +72,7 @@ export const VegaDialog = ({ transaction }: VegaDialogProps) => {
|
|||||||
if (transaction.status === VegaTxStatus.Error) {
|
if (transaction.status === VegaTxStatus.Error) {
|
||||||
return (
|
return (
|
||||||
<div data-testid={transaction.status}>
|
<div data-testid={transaction.status}>
|
||||||
<p>{transaction.error}</p>
|
<p>{transaction.error && formatLabel(transaction.error)}</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,8 @@
|
|||||||
"start": "nx serve",
|
"start": "nx serve",
|
||||||
"build": "nx build",
|
"build": "nx build",
|
||||||
"test": "nx test",
|
"test": "nx test",
|
||||||
"postinstall": "husky install && yarn tsc -b tools/executors/next && yarn tsc -b tools/executors/webpack"
|
"postinstall": "husky install && yarn tsc -b tools/executors/next && yarn tsc -b tools/executors/webpack",
|
||||||
|
"test:all": "nx run-many --all --target=test"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16.14.0"
|
"node": ">=16.14.0"
|
||||||
|
Loading…
Reference in New Issue
Block a user