TRCL-3322 Implement reduce-only checkbox on frontend (#246)

* implemented

* eol lint again

* update with correct abacus version

* update localization to latest

* fix localization v
This commit is contained in:
moo-onthelawn 2024-01-26 17:03:32 -05:00 committed by GitHub
parent cc8c724f09
commit c9596de9c3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 86 additions and 41 deletions

View File

@ -39,9 +39,9 @@
"@cosmjs/proto-signing": "^0.32.1",
"@cosmjs/stargate": "^0.32.1",
"@cosmjs/tendermint-rpc": "^0.32.1",
"@dydxprotocol/v4-abacus": "^1.2.5",
"@dydxprotocol/v4-abacus": "^1.3.2",
"@dydxprotocol/v4-client-js": "^1.0.17",
"@dydxprotocol/v4-localization": "^1.1.11",
"@dydxprotocol/v4-localization": "^1.1.17",
"@ethersproject/providers": "^5.7.2",
"@js-joda/core": "^5.5.3",
"@radix-ui/react-accordion": "^1.1.2",

16
pnpm-lock.yaml generated
View File

@ -27,14 +27,14 @@ dependencies:
specifier: ^0.32.1
version: 0.32.2
'@dydxprotocol/v4-abacus':
specifier: ^1.2.5
version: 1.2.5
specifier: ^1.3.2
version: 1.3.2
'@dydxprotocol/v4-client-js':
specifier: ^1.0.17
version: 1.0.17
'@dydxprotocol/v4-localization':
specifier: ^1.1.11
version: 1.1.11
specifier: ^1.1.17
version: 1.1.17
'@ethersproject/providers':
specifier: ^5.7.2
version: 5.7.2
@ -1086,8 +1086,8 @@ packages:
resolution: {integrity: sha512-RpfLEtTlyIxeNPGKcokS+p3BZII/Q3bYxryFRglh5H3A3T8q9fsLYm72VYAMEOOIBLEa8o93kFLiBDUWKrwXZA==}
dev: true
/@dydxprotocol/v4-abacus@1.2.5:
resolution: {integrity: sha512-pB3Fv1bzJRUDp29kAN61daZ34gTSGrSTNh+/AP3TUB63/Q/gaXQVBf91xVcso4Hw23xM3FwIcrRK0cvOSWgXKg==}
/@dydxprotocol/v4-abacus@1.3.2:
resolution: {integrity: sha512-zo0IHjGMlJRKOYDgNqNFQ9GtBJKJP4+Y9YY7V0X3Wt61ppKAYzodaYQhc9V/RYchcZTtS/xkicLug444YrvehQ==}
dev: false
/@dydxprotocol/v4-client-js@1.0.17:
@ -1119,8 +1119,8 @@ packages:
- utf-8-validate
dev: false
/@dydxprotocol/v4-localization@1.1.11:
resolution: {integrity: sha512-ZHnyrWD1bEpvY1tctkcmSV6A5+hQSYBFwyjbiyOqepqDqrv24J5a3hlZU94lDyYf+7Sdk3alOvy9Z7Lpk59nvw==}
/@dydxprotocol/v4-localization@1.1.17:
resolution: {integrity: sha512-kal1LrcihLMEv5YxaA/hd6Zl10Mp3x6jicoXDcXvtyNdrczAl3YapyI2nmeifRAPvfueOaY3W/sKkm2BiSKSsA==}
dev: false
/@dydxprotocol/v4-proto@3.0.0-dev.0:

View File

@ -5,7 +5,7 @@ import { Checkbox, CheckboxProps } from '@/components/Checkbox';
import { StoryWrapper } from '.ladle/components';
export const Checkboxes: Story<CheckboxProps> = (args) => {
export const CheckboxStory: Story<CheckboxProps> = (args) => {
const [isChecked, setIsChecked] = useState(false);
return (
@ -21,4 +21,12 @@ export const Checkboxes: Story<CheckboxProps> = (args) => {
);
};
Checkboxes.args = {};
CheckboxStory.args = {};
CheckboxStory.argTypes = {
disabled: {
options: [true, false],
control: { type: 'select' },
defaultValue: false,
}
}

View File

@ -1,4 +1,4 @@
import styled, { type AnyStyledComponent } from 'styled-components';
import styled, { css, type AnyStyledComponent } from 'styled-components';
import { Root, Indicator } from '@radix-ui/react-checkbox';
import { CheckIcon } from '@radix-ui/react-icons';
@ -9,6 +9,7 @@ type ElementProps = {
onCheckedChange: (checked: boolean) => void;
id?: string;
label?: React.ReactNode;
disabled?: boolean;
};
type StyleProps = {
@ -23,14 +24,15 @@ export const Checkbox: React.FC<CheckboxProps> = ({
onCheckedChange,
id,
label,
disabled
}) => (
<Styled.Container>
<Styled.Root className={className} checked={checked} onCheckedChange={onCheckedChange} id={id}>
<Styled.Root className={className} checked={checked} disabled={disabled} onCheckedChange={onCheckedChange} id={id}>
<Styled.Indicator>
<CheckIcon />
</Styled.Indicator>
</Styled.Root>
{label && <label htmlFor={id}>{label}</label>}
{label && <Styled.label disabled={disabled} htmlFor={id}>{label}</Styled.label>}
</Styled.Container>
);
@ -48,17 +50,23 @@ Styled.Container = styled.div`
Styled.Root = styled(Root)`
--checkbox-backgroundColor: var(--color-layer-0);
--checkbox-borderColor: var(--color-border);
min-width: 1.25rem;
height: 1.25rem;
border-radius: 0.375rem;
border: var(--border-width) solid var(--color-border);
border: var(--border-width) solid var(--checkbox-borderColor);
background-color: var(--checkbox-backgroundColor);
&[data-state='checked'] {
--checkbox-backgroundColor: var(--color-accent);
}
&[data-disabled] {
cursor: not-allowed;
--checkbox-backgroundColor: var(--color-layer-1);
}
`;
Styled.Indicator = styled(Indicator)`
@ -68,3 +76,11 @@ Styled.Indicator = styled(Indicator)`
color: var(--color-text-2);
`;
Styled.label = styled.div<{ disabled: boolean; }>`
color: var(--color-text-2);
${({disabled}) => disabled && css`
color: var(--color-text-0);
`}
`;

View File

@ -138,6 +138,10 @@ export const tradeTooltips: TooltipStrings = {
title: stringGetter({ key: TOOLTIP_STRING_KEYS.POST_ONLY_TITLE }),
body: stringGetter({ key: TOOLTIP_STRING_KEYS.POST_ONLY_BODY }),
}),
'post-only-timeinforce-gtt': ({ stringGetter }) => ({
title: stringGetter({ key: TOOLTIP_STRING_KEYS.POST_ONLY_TIMEINFORCE_GTT_TITLE }),
body: stringGetter({ key: TOOLTIP_STRING_KEYS.POST_ONLY_TIMEINFORCE_GTT_BODY }),
}),
'price-impact': ({ stringGetter }) => ({
title: stringGetter({ key: TOOLTIP_STRING_KEYS.PRICE_IMPACT_TITLE }),
body: stringGetter({ key: TOOLTIP_STRING_KEYS.PRICE_IMPACT_BODY }),
@ -151,6 +155,14 @@ export const tradeTooltips: TooltipStrings = {
body: stringGetter({ key: TOOLTIP_STRING_KEYS.REDUCE_ONLY_BODY }),
learnMoreLink: urlConfigs?.reduceOnlyLearnMore,
}),
'reduce-only-execution-ioc-fok': ({ stringGetter }) => ({
title: stringGetter({ key: TOOLTIP_STRING_KEYS.REDUCE_ONLY_EXECUTION_IOC_FOK_TITLE }),
body: stringGetter({ key: TOOLTIP_STRING_KEYS.REDUCE_ONLY_EXECUTION_IOC_FOK_BODY }),
}),
'reduce-only-timeinforce-ioc-fok': ({ stringGetter }) => ({
title: stringGetter({ key: TOOLTIP_STRING_KEYS.REDUCE_ONLY_TIMEINFORCE_IOC_FOK_TITLE }),
body: stringGetter({ key: TOOLTIP_STRING_KEYS.REDUCE_ONLY_TIMEINFORCE_IOC_FOK_BODY }),
}),
spread: () => ({
title: 'Spread',
body: 'The difference in price between the highest bid (the price a buyer is willing to buy for) and lowest ask (the price a seller is willing to sell for) an asset.',

View File

@ -96,6 +96,8 @@ export const useTradeFormData = () => {
needsGoodUntil,
needsPostOnly,
needsReduceOnly,
postOnlyTooltip,
reduceOnlyTooltip,
timeInForceOptions,
} = tradeOptions || {};
@ -111,6 +113,8 @@ export const useTradeFormData = () => {
needsGoodUntil,
needsPostOnly,
needsReduceOnly,
postOnlyTooltip,
reduceOnlyTooltip,
timeInForceOptions,
tradeErrors,

View File

@ -10,7 +10,6 @@ import {
type HumanReadablePlaceOrderPayload,
type Nullable,
TradeInputErrorAction,
TradeInputField,
ValidationError,
} from '@/constants/abacus';
@ -96,6 +95,8 @@ export const TradeForm = ({
needsGoodUntil,
needsPostOnly,
needsReduceOnly,
postOnlyTooltip,
reduceOnlyTooltip,
timeInForceOptions,
tradeErrors,
} = useTradeFormData();
@ -108,7 +109,7 @@ export const TradeForm = ({
const { limitPriceInput, triggerPriceInput, trailingPercentInput } = tradeFormInputValues;
const needsAdvancedOptions =
needsGoodUntil || timeInForceOptions || executionOptions || needsPostOnly || needsReduceOnly;
needsGoodUntil || timeInForceOptions || executionOptions || (needsPostOnly || postOnlyTooltip) || (needsReduceOnly || reduceOnlyTooltip);
const tradeFormInputs: TradeBoxInputConfig[] = [];

View File

@ -31,12 +31,15 @@ export const AdvancedTradeOptions = () => {
const { execution, goodTil, postOnly, reduceOnly, timeInForce, type } = inputTradeData || {};
const { executionOptions, needsGoodUntil, needsPostOnly, needsReduceOnly, timeInForceOptions } =
const { executionOptions, needsGoodUntil, needsPostOnly, needsReduceOnly, postOnlyTooltip, reduceOnlyTooltip, timeInForceOptions } =
currentTradeFormConfig || {};
const { duration, unit } = goodTil || {};
const needsExecution = executionOptions || needsPostOnly || needsReduceOnly;
const showPostOnly = (needsPostOnly || postOnlyTooltip);
const showReduceOnly = (needsReduceOnly || reduceOnlyTooltip);
const needsExecution = executionOptions || showPostOnly || showReduceOnly;
const hasTimeInForce = timeInForceOptions?.toArray()?.length;
return (
@ -129,26 +132,9 @@ export const AdvancedTradeOptions = () => {
))}
</Styled.SelectMenu>
)}
{needsPostOnly && (
<Checkbox
checked={postOnly || false}
onCheckedChange={(checked) =>
abacusStateManager.setTradeValue({
value: checked,
field: TradeInputField.postOnly,
})
}
id="post-only"
label={
<WithTooltip tooltip="post-only" side="right">
{stringGetter({ key: STRING_KEYS.POST_ONLY })}
</WithTooltip>
}
/>
)}
{needsReduceOnly && (
<Checkbox
checked={reduceOnly || false}
{showReduceOnly && <Checkbox
checked={(reduceOnly && !reduceOnlyTooltip) || false}
disabled={!!reduceOnlyTooltip}
onCheckedChange={(checked) =>
abacusStateManager.setTradeValue({
value: checked,
@ -157,11 +143,29 @@ export const AdvancedTradeOptions = () => {
}
id="reduce-only"
label={
<WithTooltip tooltip="reduce-only" side="right">
<WithTooltip tooltip={needsReduceOnly ? "reduce-only" : reduceOnlyTooltip?.titleStringKey.includes("REDUCE_ONLY_EXECUTION_IOC_FOK") ? "reduce-only-execution-ioc-fok" : "reduce-only-timeinforce-ioc-fok"} side="right">
{stringGetter({ key: STRING_KEYS.REDUCE_ONLY })}
</WithTooltip>
}
/>
}
{showPostOnly && (
<Checkbox
checked={(postOnly && !postOnlyTooltip) || false}
disabled={!!postOnlyTooltip}
onCheckedChange={(checked) =>
abacusStateManager.setTradeValue({
value: checked,
field: TradeInputField.postOnly,
})
}
id="post-only"
label={
<WithTooltip tooltip={needsPostOnly ? "post-only" : "post-only-timeinforce-gtt"} side="right">
{stringGetter({ key: STRING_KEYS.POST_ONLY })}
</WithTooltip>
}
/>
)}
</>
)}