CT-145 Add warning icon + tooltip to trade order button CTA (#297)
* update * rename to config
This commit is contained in:
@@ -14,6 +14,7 @@ import { ButtonAction, ButtonShape, ButtonSize, ButtonType } from '@/constants/b
|
||||
import { TOKEN_DECIMALS } from '@/constants/numbers';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { MobilePlaceOrderSteps } from '@/constants/trade';
|
||||
|
||||
import { useBreakpoints, useIsFirstRender, useStringGetter, useSubaccount } from '@/hooks';
|
||||
import { useOnLastOrderIndexed } from '@/hooks/useOnLastOrderIndexed';
|
||||
|
||||
@@ -35,7 +36,6 @@ import { Orderbook, orderbookMixins, type OrderbookScrollBehavior } from '@/view
|
||||
import { PositionPreview } from '@/views/forms/TradeForm/PositionPreview';
|
||||
|
||||
import { getCurrentMarketPositionData } from '@/state/accountSelectors';
|
||||
|
||||
import { getCurrentMarketAssetData } from '@/state/assetsSelectors';
|
||||
import { getClosePositionInputErrors, getInputClosePositionData } from '@/state/inputsSelectors';
|
||||
import { getCurrentMarketConfig, getCurrentMarketId } from '@/state/perpetualsSelectors';
|
||||
@@ -291,9 +291,14 @@ export const ClosePositionForm = ({
|
||||
isLoading={isClosingPosition}
|
||||
hasValidationErrors={hasInputErrors}
|
||||
actionStringKey={inputAlert?.actionStringKey}
|
||||
validationErrorString={alertContent}
|
||||
summary={summary ?? undefined}
|
||||
currentStep={currentStep}
|
||||
isClosePosition
|
||||
confirmButtonConfig={{
|
||||
stringKey: STRING_KEYS.CLOSE_ORDER,
|
||||
buttonTextStringKey: STRING_KEYS.CLOSE_POSITION,
|
||||
buttonAction: ButtonAction.Destroy,
|
||||
}}
|
||||
/>
|
||||
</Styled.Footer>
|
||||
</Styled.ClosePositionForm>
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { type FormEvent, useState, Ref, useCallback, useEffect } from 'react';
|
||||
import { type FormEvent, useState, Ref, useCallback } from 'react';
|
||||
import styled, { AnyStyledComponent, css } from 'styled-components';
|
||||
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||
import type { NumberFormatValues, SourceInfo } from 'react-number-format';
|
||||
|
||||
import { OrderSide } from '@dydxprotocol/v4-client-js';
|
||||
|
||||
import { AlertType } from '@/constants/alerts';
|
||||
|
||||
import {
|
||||
@@ -16,7 +18,12 @@ import {
|
||||
import { ButtonAction, ButtonShape, ButtonSize, ButtonType } from '@/constants/buttons';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { USD_DECIMALS } from '@/constants/numbers';
|
||||
import { InputErrorData, TradeBoxKeys, MobilePlaceOrderSteps } from '@/constants/trade';
|
||||
import {
|
||||
InputErrorData,
|
||||
TradeBoxKeys,
|
||||
MobilePlaceOrderSteps,
|
||||
ORDER_TYPE_STRINGS,
|
||||
} from '@/constants/trade';
|
||||
|
||||
import { breakpoints } from '@/styles';
|
||||
import { useStringGetter, useSubaccount } from '@/hooks';
|
||||
@@ -37,11 +44,16 @@ import { WithTooltip } from '@/components/WithTooltip';
|
||||
import { Orderbook } from '@/views/tables/Orderbook';
|
||||
|
||||
import { setTradeFormInputs } from '@/state/inputs';
|
||||
import { getCurrentInput, getTradeFormInputs, useTradeFormData } from '@/state/inputsSelectors';
|
||||
import {
|
||||
getCurrentInput,
|
||||
getInputTradeData,
|
||||
getTradeFormInputs,
|
||||
useTradeFormData,
|
||||
} from '@/state/inputsSelectors';
|
||||
import { getCurrentMarketConfig } from '@/state/perpetualsSelectors';
|
||||
|
||||
import abacusStateManager from '@/lib/abacus';
|
||||
import { getTradeInputAlert } from '@/lib/tradeData';
|
||||
import { getSelectedOrderSide, getSelectedTradeType, getTradeInputAlert } from '@/lib/tradeData';
|
||||
|
||||
import { AdvancedTradeOptions } from './TradeForm/AdvancedTradeOptions';
|
||||
import { TradeSizeInputs } from './TradeForm/TradeSizeInputs';
|
||||
@@ -108,8 +120,21 @@ export const TradeForm = ({
|
||||
const tradeFormInputValues = useSelector(getTradeFormInputs, shallowEqual);
|
||||
const { limitPriceInput, triggerPriceInput, trailingPercentInput } = tradeFormInputValues;
|
||||
|
||||
const currentTradeData = useSelector(getInputTradeData, shallowEqual);
|
||||
|
||||
const { side, type } = currentTradeData || {};
|
||||
|
||||
const selectedTradeType = getSelectedTradeType(type);
|
||||
const selectedOrderSide = getSelectedOrderSide(side);
|
||||
|
||||
const needsAdvancedOptions =
|
||||
needsGoodUntil || timeInForceOptions || executionOptions || (needsPostOnly || postOnlyTooltip) || (needsReduceOnly || reduceOnlyTooltip);
|
||||
needsGoodUntil ||
|
||||
timeInForceOptions ||
|
||||
executionOptions ||
|
||||
needsPostOnly ||
|
||||
postOnlyTooltip ||
|
||||
needsReduceOnly ||
|
||||
reduceOnlyTooltip;
|
||||
|
||||
const tradeFormInputs: TradeBoxInputConfig[] = [];
|
||||
|
||||
@@ -139,6 +164,11 @@ export const TradeForm = ({
|
||||
alertType = inputAlert?.type;
|
||||
}
|
||||
|
||||
const orderSideAction = {
|
||||
[OrderSide.BUY]: ButtonAction.Create,
|
||||
[OrderSide.SELL]: ButtonAction.Destroy,
|
||||
}[selectedOrderSide];
|
||||
|
||||
const onSubmit = async (e: FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -319,9 +349,15 @@ export const TradeForm = ({
|
||||
isLoading={isPlacingOrder}
|
||||
hasValidationErrors={hasInputErrors}
|
||||
actionStringKey={inputAlert?.actionStringKey}
|
||||
validationErrorString={alertContent}
|
||||
summary={summary ?? undefined}
|
||||
currentStep={currentStep}
|
||||
showDeposit={inputAlert?.errorAction === TradeInputErrorAction.DEPOSIT}
|
||||
confirmButtonConfig={{
|
||||
stringKey: ORDER_TYPE_STRINGS[selectedTradeType].orderTypeKey,
|
||||
buttonTextStringKey: STRING_KEYS.PLACE_ORDER,
|
||||
buttonAction: orderSideAction,
|
||||
}}
|
||||
/>
|
||||
</Styled.Footer>
|
||||
</Styled.TradeForm>
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||
import { OrderSide } from '@dydxprotocol/v4-client-js';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import styled, { type AnyStyledComponent, css } from 'styled-components';
|
||||
|
||||
import type { TradeInputSummary } from '@/constants/abacus';
|
||||
import { ButtonAction, ButtonSize, ButtonType } from '@/constants/buttons';
|
||||
import { DialogTypes } from '@/constants/dialogs';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { ORDER_TYPE_STRINGS, MobilePlaceOrderSteps } from '@/constants/trade';
|
||||
import { MobilePlaceOrderSteps } from '@/constants/trade';
|
||||
|
||||
import { useStringGetter, useTokenConfigs } from '@/hooks';
|
||||
|
||||
import { AssetIcon } from '@/components/AssetIcon';
|
||||
import { Button } from '@/components/Button';
|
||||
import { Icon, IconName } from '@/components/Icon';
|
||||
import { Output, OutputType, ShowSign } from '@/components/Output';
|
||||
import { WithDetailsReceipt } from '@/components/WithDetailsReceipt';
|
||||
import { WithTooltip } from '@/components/WithTooltip';
|
||||
@@ -20,30 +21,34 @@ import { OnboardingTriggerButton } from '@/views/dialogs/OnboardingTriggerButton
|
||||
import { calculateCanAccountTrade } from '@/state/accountCalculators';
|
||||
import { getSubaccountId } from '@/state/accountSelectors';
|
||||
import { openDialog } from '@/state/dialogs';
|
||||
import { getCurrentInput, getInputTradeData } from '@/state/inputsSelectors';
|
||||
import { getCurrentInput } from '@/state/inputsSelectors';
|
||||
|
||||
import { getSelectedOrderSide, getSelectedTradeType } from '@/lib/tradeData';
|
||||
type ConfirmButtonConfig = {
|
||||
stringKey: string;
|
||||
buttonTextStringKey: string;
|
||||
buttonAction: ButtonAction;
|
||||
};
|
||||
|
||||
type ElementProps = {
|
||||
isLoading: boolean;
|
||||
isClosePosition?: boolean;
|
||||
actionStringKey?: string;
|
||||
summary?: TradeInputSummary;
|
||||
hasValidationErrors?: boolean;
|
||||
validationErrorString?: string;
|
||||
currentStep?: MobilePlaceOrderSteps;
|
||||
showDeposit?: boolean;
|
||||
showConnectWallet?: boolean;
|
||||
confirmButtonConfig: ConfirmButtonConfig;
|
||||
};
|
||||
|
||||
export const PlaceOrderButtonAndReceipt = ({
|
||||
isLoading,
|
||||
isClosePosition,
|
||||
actionStringKey,
|
||||
summary,
|
||||
hasValidationErrors,
|
||||
validationErrorString,
|
||||
currentStep,
|
||||
showDeposit,
|
||||
showConnectWallet,
|
||||
confirmButtonConfig,
|
||||
}: ElementProps) => {
|
||||
const stringGetter = useStringGetter();
|
||||
const dispatch = useDispatch();
|
||||
@@ -52,18 +57,12 @@ export const PlaceOrderButtonAndReceipt = ({
|
||||
const canAccountTrade = useSelector(calculateCanAccountTrade);
|
||||
const subaccountNumber = useSelector(getSubaccountId);
|
||||
const currentInput = useSelector(getCurrentInput);
|
||||
const currentTradeData = useSelector(getInputTradeData, shallowEqual);
|
||||
|
||||
const hasMissingData = subaccountNumber === undefined;
|
||||
|
||||
const shouldEnableTrade =
|
||||
canAccountTrade && !hasMissingData && !hasValidationErrors && currentInput !== 'transfer';
|
||||
|
||||
const { side, type } = currentTradeData || {};
|
||||
|
||||
const selectedTradeType = getSelectedTradeType(type);
|
||||
const selectedOrderSide = getSelectedOrderSide(side);
|
||||
|
||||
const { fee, price: expectedPrice, total, reward } = summary || {};
|
||||
|
||||
const items = [
|
||||
@@ -110,11 +109,6 @@ export const PlaceOrderButtonAndReceipt = ({
|
||||
},
|
||||
];
|
||||
|
||||
const orderSideAction = {
|
||||
[OrderSide.BUY]: ButtonAction.Create,
|
||||
[OrderSide.SELL]: ButtonAction.Destroy,
|
||||
}[selectedOrderSide];
|
||||
|
||||
const buttonStatesPerStep = {
|
||||
[MobilePlaceOrderSteps.EditOrder]: {
|
||||
buttonTextStringKey: shouldEnableTrade
|
||||
@@ -128,7 +122,7 @@ export const PlaceOrderButtonAndReceipt = ({
|
||||
|
||||
[MobilePlaceOrderSteps.PreviewOrder]: {
|
||||
buttonTextStringKey: STRING_KEYS.CONFIRM_ORDER,
|
||||
buttonAction: isClosePosition ? ButtonAction.Destroy : orderSideAction,
|
||||
buttonAction: confirmButtonConfig.buttonAction,
|
||||
buttonState: { isLoading },
|
||||
},
|
||||
[MobilePlaceOrderSteps.PlacingOrder]: {
|
||||
@@ -145,15 +139,13 @@ export const PlaceOrderButtonAndReceipt = ({
|
||||
|
||||
const buttonAction = currentStep
|
||||
? buttonStatesPerStep[currentStep].buttonAction
|
||||
: isClosePosition
|
||||
? ButtonAction.Destroy
|
||||
: orderSideAction;
|
||||
: confirmButtonConfig.buttonAction;
|
||||
|
||||
let buttonTextStringKey = STRING_KEYS.UNAVAILABLE;
|
||||
if (currentStep) {
|
||||
buttonTextStringKey = buttonStatesPerStep[currentStep].buttonTextStringKey;
|
||||
} else if (shouldEnableTrade) {
|
||||
buttonTextStringKey = isClosePosition ? STRING_KEYS.CLOSE_POSITION : STRING_KEYS.PLACE_ORDER;
|
||||
buttonTextStringKey = confirmButtonConfig.buttonTextStringKey;
|
||||
} else if (actionStringKey) {
|
||||
buttonTextStringKey = actionStringKey;
|
||||
}
|
||||
@@ -165,31 +157,56 @@ export const PlaceOrderButtonAndReceipt = ({
|
||||
isLoading: isLoading || hasMissingData,
|
||||
};
|
||||
|
||||
const depositButton = (
|
||||
<Button
|
||||
action={ButtonAction.Primary}
|
||||
onClick={() => dispatch(openDialog({ type: DialogTypes.Deposit }))}
|
||||
>
|
||||
{stringGetter({ key: STRING_KEYS.DEPOSIT_FUNDS })}
|
||||
</Button>
|
||||
);
|
||||
|
||||
const submitButton = (
|
||||
<Styled.Button
|
||||
state={buttonState}
|
||||
type={ButtonType.Submit}
|
||||
action={buttonAction}
|
||||
slotLeft={
|
||||
hasValidationErrors ? <Styled.WarningIcon iconName={IconName.Warning} /> : undefined
|
||||
}
|
||||
>
|
||||
{stringGetter({
|
||||
key: buttonTextStringKey,
|
||||
params: {
|
||||
ORDER: stringGetter({
|
||||
key: confirmButtonConfig.stringKey,
|
||||
}),
|
||||
},
|
||||
})}
|
||||
</Styled.Button>
|
||||
);
|
||||
|
||||
return (
|
||||
<WithDetailsReceipt detailItems={items}>
|
||||
{!canAccountTrade || showConnectWallet ? (
|
||||
{!canAccountTrade ? (
|
||||
<OnboardingTriggerButton size={ButtonSize.Base} />
|
||||
) : showDeposit ? (
|
||||
<Button
|
||||
action={ButtonAction.Primary}
|
||||
onClick={() => dispatch(openDialog({ type: DialogTypes.Deposit }))}
|
||||
>
|
||||
{stringGetter({ key: STRING_KEYS.DEPOSIT_FUNDS })}
|
||||
</Button>
|
||||
depositButton
|
||||
) : (
|
||||
<Button state={buttonState} type={ButtonType.Submit} action={buttonAction}>
|
||||
{stringGetter({
|
||||
key: buttonTextStringKey,
|
||||
params: {
|
||||
ORDER: stringGetter({
|
||||
key: isClosePosition
|
||||
? STRING_KEYS.CLOSE_ORDER
|
||||
: ORDER_TYPE_STRINGS[selectedTradeType].orderTypeKey,
|
||||
}),
|
||||
},
|
||||
})}
|
||||
</Button>
|
||||
<WithTooltip tooltipString={hasValidationErrors ? validationErrorString : undefined}>
|
||||
{submitButton}
|
||||
</WithTooltip>
|
||||
)}
|
||||
</WithDetailsReceipt>
|
||||
);
|
||||
};
|
||||
|
||||
const Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
Styled.Button = styled(Button)`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
Styled.WarningIcon = styled(Icon)`
|
||||
color: var(--color-warning);
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user