feat(ui-toolkit): form element design changes (#4525)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
This commit is contained in:
parent
e0a91b3850
commit
78414b4429
@ -2,7 +2,7 @@ import { t } from '@vegaprotocol/i18n';
|
||||
import uniqBy from 'lodash/uniqBy';
|
||||
import type { MarketMaybeWithDataAndCandles } from '@vegaprotocol/markets';
|
||||
import {
|
||||
Input,
|
||||
TradingInput,
|
||||
TinyScroll,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
@ -57,7 +57,7 @@ export const MarketSelector = ({
|
||||
/>
|
||||
<div className="text-sm grid grid-cols-[2fr_1fr_1fr] gap-1 ">
|
||||
<div className="flex-1">
|
||||
<Input
|
||||
<TradingInput
|
||||
onChange={(e) =>
|
||||
setFilter((curr) => ({ ...curr, searchTerm: e.target.value }))
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Checkbox } from '@vegaprotocol/ui-toolkit';
|
||||
import { TradingCheckbox } from '@vegaprotocol/ui-toolkit';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useTelemetryApproval } from '../../lib/hooks/use-telemetry-approval';
|
||||
|
||||
@ -7,7 +7,7 @@ export const TelemetryApproval = ({ helpText }: { helpText: string }) => {
|
||||
return (
|
||||
<div className="flex flex-col py-3">
|
||||
<div className="mr-4" role="form">
|
||||
<Checkbox
|
||||
<TradingCheckbox
|
||||
label={<span className="text-lg pl-1">{t('Share usage data')}</span>}
|
||||
checked={isApproved}
|
||||
name="telemetry-approval"
|
||||
|
@ -9,13 +9,13 @@ import {
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import {
|
||||
Button,
|
||||
FormGroup,
|
||||
Input,
|
||||
InputError,
|
||||
RichSelect,
|
||||
Select,
|
||||
TradingFormGroup,
|
||||
TradingInput,
|
||||
TradingInputError,
|
||||
TradingRichSelect,
|
||||
TradingSelect,
|
||||
Tooltip,
|
||||
Checkbox,
|
||||
TradingCheckbox,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import type { Transfer } from '@vegaprotocol/wallet';
|
||||
import { normalizeTransfer } from '@vegaprotocol/wallet';
|
||||
@ -130,12 +130,16 @@ export const TransferForm = ({
|
||||
className="text-sm"
|
||||
data-testid="transfer-form"
|
||||
>
|
||||
<FormGroup label="Vega key" labelFor="to-address">
|
||||
<TradingFormGroup label="Vega key" labelFor="to-address">
|
||||
<AddressField
|
||||
pubKeys={pubKeys}
|
||||
onChange={() => setValue('toAddress', '')}
|
||||
select={
|
||||
<Select {...register('toAddress')} id="to-address" defaultValue="">
|
||||
<TradingSelect
|
||||
{...register('toAddress')}
|
||||
id="to-address"
|
||||
defaultValue=""
|
||||
>
|
||||
<option value="" disabled={true}>
|
||||
{t('Please select')}
|
||||
</option>
|
||||
@ -147,10 +151,10 @@ export const TransferForm = ({
|
||||
{pk}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
</TradingSelect>
|
||||
}
|
||||
input={
|
||||
<Input
|
||||
<TradingInput
|
||||
// eslint-disable-next-line jsx-a11y/no-autofocus
|
||||
autoFocus={true} // focus input immediately after is shown
|
||||
id="to-address"
|
||||
@ -171,12 +175,12 @@ export const TransferForm = ({
|
||||
}
|
||||
/>
|
||||
{errors.toAddress?.message && (
|
||||
<InputError forInput="to-address">
|
||||
<TradingInputError forInput="to-address">
|
||||
{errors.toAddress.message}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
<FormGroup label="Asset" labelFor="asset">
|
||||
</TradingFormGroup>
|
||||
<TradingFormGroup label="Asset" labelFor="asset">
|
||||
<Controller
|
||||
control={control}
|
||||
name="asset"
|
||||
@ -186,7 +190,7 @@ export const TransferForm = ({
|
||||
},
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<RichSelect
|
||||
<TradingRichSelect
|
||||
data-testid="select-asset"
|
||||
id={field.name}
|
||||
name={field.name}
|
||||
@ -208,15 +212,17 @@ export const TransferForm = ({
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</RichSelect>
|
||||
</TradingRichSelect>
|
||||
)}
|
||||
/>
|
||||
{errors.asset?.message && (
|
||||
<InputError forInput="asset">{errors.asset.message}</InputError>
|
||||
<TradingInputError forInput="asset">
|
||||
{errors.asset.message}
|
||||
</TradingInputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
<FormGroup label="Amount" labelFor="amount">
|
||||
<Input
|
||||
</TradingFormGroup>
|
||||
<TradingFormGroup label="Amount" labelFor="amount">
|
||||
<TradingInput
|
||||
id="amount"
|
||||
autoComplete="off"
|
||||
appendElement={
|
||||
@ -239,11 +245,13 @@ export const TransferForm = ({
|
||||
})}
|
||||
/>
|
||||
{errors.amount?.message && (
|
||||
<InputError forInput="amount">{errors.amount.message}</InputError>
|
||||
<TradingInputError forInput="amount">
|
||||
{errors.amount.message}
|
||||
</TradingInputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
</TradingFormGroup>
|
||||
<div className="mb-4">
|
||||
<Checkbox
|
||||
<TradingCheckbox
|
||||
name="include-transfer-fee"
|
||||
disabled={!transferAmount}
|
||||
label={
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Option } from '@vegaprotocol/ui-toolkit';
|
||||
import { TradingOption } from '@vegaprotocol/ui-toolkit';
|
||||
import type { AssetFieldsFragment } from './__generated__/Asset';
|
||||
import classNames from 'classnames';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
@ -28,7 +28,7 @@ export const Balance = ({
|
||||
|
||||
export const AssetOption = ({ asset, balance }: AssetOptionProps) => {
|
||||
return (
|
||||
<Option key={asset.id} value={asset.id}>
|
||||
<TradingOption key={asset.id} value={asset.id}>
|
||||
<div className="flex flex-col items-start">
|
||||
<div className="flex flex-row align-baseline gap-2">
|
||||
<span>{asset.name}</span>{' '}
|
||||
@ -49,6 +49,6 @@ export const AssetOption = ({ asset, balance }: AssetOptionProps) => {
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</Option>
|
||||
</TradingOption>
|
||||
);
|
||||
};
|
||||
|
@ -15,7 +15,7 @@ import {
|
||||
} from 'date-fns';
|
||||
import { formatForInput } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { InputError } from '@vegaprotocol/ui-toolkit';
|
||||
import { TradingInputError } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
const defaultValue: Schema.DateRange = {};
|
||||
export interface DateRangeFilterProps extends IFilterParams {
|
||||
@ -195,7 +195,7 @@ export const DateRangeFilter = forwardRef(
|
||||
}, [value, props]);
|
||||
|
||||
const notification = useMemo(() => {
|
||||
const not = error ? <InputError>{error}</InputError> : null;
|
||||
const not = error ? <TradingInputError>{error}</TradingInputError> : null;
|
||||
return (
|
||||
<div className="ag-filter-apply-panel flex min-h-[2rem]">{not}</div>
|
||||
);
|
||||
|
@ -1,4 +1,8 @@
|
||||
import { FormGroup, Input, InputError } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
TradingFormGroup,
|
||||
TradingInput,
|
||||
TradingInputError,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { toDecimal, validateAmount } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { DealTicketAmountProps } from './deal-ticket-amount';
|
||||
@ -22,17 +26,17 @@ export const DealTicketLimitAmount = ({
|
||||
const renderError = () => {
|
||||
if (sizeError) {
|
||||
return (
|
||||
<InputError testId="deal-ticket-error-message-size-limit">
|
||||
<TradingInputError testId="deal-ticket-error-message-size-limit">
|
||||
{sizeError}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
);
|
||||
}
|
||||
|
||||
if (priceError) {
|
||||
return (
|
||||
<InputError testId="deal-ticket-error-message-price-limit">
|
||||
<TradingInputError testId="deal-ticket-error-message-price-limit">
|
||||
{priceError}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
);
|
||||
}
|
||||
|
||||
@ -43,7 +47,7 @@ export const DealTicketLimitAmount = ({
|
||||
<div className="mb-2">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="flex-1">
|
||||
<FormGroup
|
||||
<TradingFormGroup
|
||||
label={t('Size')}
|
||||
labelFor="input-order-size-limit"
|
||||
className="!mb-0"
|
||||
@ -59,8 +63,8 @@ export const DealTicketLimitAmount = ({
|
||||
},
|
||||
validate: validateAmount(sizeStep, 'Size'),
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
render={({ field, fieldState }) => (
|
||||
<TradingInput
|
||||
id="input-order-size-limit"
|
||||
className="w-full"
|
||||
type="number"
|
||||
@ -68,15 +72,16 @@ export const DealTicketLimitAmount = ({
|
||||
min={sizeStep}
|
||||
data-testid="order-size"
|
||||
onWheel={(e) => e.currentTarget.blur()}
|
||||
hasError={!!fieldState.error}
|
||||
{...field}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormGroup>
|
||||
</TradingFormGroup>
|
||||
</div>
|
||||
<div className="pt-7 leading-10">@</div>
|
||||
<div className="pt-5 leading-10">@</div>
|
||||
<div className="flex-1">
|
||||
<FormGroup
|
||||
<TradingFormGroup
|
||||
labelFor="input-price-quote"
|
||||
label={t(`Price (${quoteName})`)}
|
||||
labelAlign="right"
|
||||
@ -93,19 +98,20 @@ export const DealTicketLimitAmount = ({
|
||||
},
|
||||
validate: validateAmount(priceStep, 'Price'),
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
render={({ field, fieldState }) => (
|
||||
<TradingInput
|
||||
id="input-price-quote"
|
||||
className="w-full"
|
||||
type="number"
|
||||
step={priceStep}
|
||||
data-testid="order-price"
|
||||
onWheel={(e) => e.currentTarget.blur()}
|
||||
hasError={!!fieldState.error}
|
||||
{...field}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormGroup>
|
||||
</TradingFormGroup>
|
||||
</div>
|
||||
</div>
|
||||
{renderError()}
|
||||
|
@ -4,7 +4,11 @@ import {
|
||||
validateAmount,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Input, InputError, Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
TradingInput,
|
||||
TradingInputError,
|
||||
Tooltip,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { isMarketInAuction } from '@vegaprotocol/markets';
|
||||
import type { DealTicketAmountProps } from './deal-ticket-amount';
|
||||
import { Controller } from 'react-hook-form';
|
||||
@ -33,7 +37,7 @@ export const DealTicketMarketAmount = ({
|
||||
<div className="mb-2">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="flex-1">
|
||||
<div className="mb-2 text-sm">{t('Size')}</div>
|
||||
<div className="mb-2 text-xs">{t('Size')}</div>
|
||||
<Controller
|
||||
name="size"
|
||||
control={control}
|
||||
@ -45,8 +49,8 @@ export const DealTicketMarketAmount = ({
|
||||
},
|
||||
validate: validateAmount(sizeStep, 'Size'),
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
render={({ field, fieldState }) => (
|
||||
<TradingInput
|
||||
id="input-order-size-market"
|
||||
className="w-full"
|
||||
type="number"
|
||||
@ -54,12 +58,13 @@ export const DealTicketMarketAmount = ({
|
||||
min={sizeStep}
|
||||
onWheel={(e) => e.currentTarget.blur()}
|
||||
data-testid="order-size"
|
||||
hasError={!!fieldState.error}
|
||||
{...field}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="pt-7 leading-10">@</div>
|
||||
<div className="pt-5 leading-10">@</div>
|
||||
<div className="flex-1 text-sm text-right">
|
||||
{inAuction && (
|
||||
<Tooltip
|
||||
@ -72,7 +77,7 @@ export const DealTicketMarketAmount = ({
|
||||
)}
|
||||
<div
|
||||
data-testid="last-price"
|
||||
className={classNames('leading-10', { 'pt-7': !inAuction })}
|
||||
className={classNames('leading-10', { 'pt-5': !inAuction })}
|
||||
>
|
||||
{priceFormatted && quoteName ? (
|
||||
<>
|
||||
@ -85,12 +90,12 @@ export const DealTicketMarketAmount = ({
|
||||
</div>
|
||||
</div>
|
||||
{sizeError && (
|
||||
<InputError
|
||||
<TradingInputError
|
||||
intent="danger"
|
||||
testId="deal-ticket-error-message-size-market"
|
||||
>
|
||||
{sizeError}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
@ -4,9 +4,9 @@ import type { OrderFormValues } from '../../hooks/use-form-values';
|
||||
import { toDecimal, validateAmount } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import {
|
||||
FormGroup,
|
||||
Input,
|
||||
InputError,
|
||||
TradingFormGroup,
|
||||
TradingInput,
|
||||
TradingInputError,
|
||||
Tooltip,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
@ -32,9 +32,9 @@ export const DealTicketSizeIceberg = ({
|
||||
const renderPeakSizeError = () => {
|
||||
if (peakSizeError) {
|
||||
return (
|
||||
<InputError testId="deal-ticket-peak-error-message-size-limit">
|
||||
<TradingInputError testId="deal-ticket-peak-error-message-size-limit">
|
||||
{peakSizeError}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
);
|
||||
}
|
||||
|
||||
@ -44,9 +44,9 @@ export const DealTicketSizeIceberg = ({
|
||||
const renderMinimumSizeError = () => {
|
||||
if (minimumVisibleSizeError) {
|
||||
return (
|
||||
<InputError testId="deal-ticket-minimum-error-message-size-limit">
|
||||
<TradingInputError testId="deal-ticket-minimum-error-message-size-limit">
|
||||
{minimumVisibleSizeError}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
);
|
||||
}
|
||||
|
||||
@ -57,7 +57,7 @@ export const DealTicketSizeIceberg = ({
|
||||
<div className="mb-2">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex-1">
|
||||
<FormGroup
|
||||
<TradingFormGroup
|
||||
label={
|
||||
<Tooltip
|
||||
description={
|
||||
@ -93,7 +93,7 @@ export const DealTicketSizeIceberg = ({
|
||||
validate: validateAmount(sizeStep, 'peakSize'),
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
<TradingInput
|
||||
id="input-order-peak-size"
|
||||
className="w-full"
|
||||
type="number"
|
||||
@ -106,14 +106,14 @@ export const DealTicketSizeIceberg = ({
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormGroup>
|
||||
</TradingFormGroup>
|
||||
</div>
|
||||
<div className="flex-0 items-center">
|
||||
<div className="flex"></div>
|
||||
<div className="flex"></div>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<FormGroup
|
||||
<TradingFormGroup
|
||||
label={
|
||||
<Tooltip
|
||||
description={
|
||||
@ -151,7 +151,7 @@ export const DealTicketSizeIceberg = ({
|
||||
validate: validateAmount(sizeStep, 'minimumVisibleSize'),
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
<TradingInput
|
||||
id="input-order-minimum-size"
|
||||
className="w-full"
|
||||
type="number"
|
||||
@ -164,7 +164,7 @@ export const DealTicketSizeIceberg = ({
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormGroup>
|
||||
</TradingFormGroup>
|
||||
</div>
|
||||
</div>
|
||||
{renderPeakSizeError()}
|
||||
|
@ -10,13 +10,13 @@ import {
|
||||
import { useForm, Controller, useController } from 'react-hook-form';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import {
|
||||
Radio,
|
||||
RadioGroup,
|
||||
Input,
|
||||
Checkbox,
|
||||
FormGroup,
|
||||
InputError,
|
||||
Select,
|
||||
TradingRadio,
|
||||
TradingRadioGroup,
|
||||
TradingInput,
|
||||
TradingCheckbox,
|
||||
TradingFormGroup,
|
||||
TradingInputError,
|
||||
TradingSelect,
|
||||
Tooltip,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { getDerivedPrice, type Market } from '@vegaprotocol/markets';
|
||||
@ -187,9 +187,9 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
}}
|
||||
/>
|
||||
{errors.type && (
|
||||
<InputError testId="stop-order-error-message-type">
|
||||
<TradingInputError testId="stop-order-error-message-type">
|
||||
{errors.type.message}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
)}
|
||||
|
||||
<Controller
|
||||
@ -199,21 +199,21 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
<SideSelector value={field.value} onValueChange={field.onChange} />
|
||||
)}
|
||||
/>
|
||||
<FormGroup label={t('Trigger')} compact={true} labelFor="">
|
||||
<TradingFormGroup label={t('Trigger')} compact={true} labelFor="">
|
||||
<Controller
|
||||
name="triggerDirection"
|
||||
control={control}
|
||||
render={({ field }) => {
|
||||
const { onChange, value } = field;
|
||||
return (
|
||||
<RadioGroup
|
||||
<TradingRadioGroup
|
||||
name="triggerDirection"
|
||||
onChange={onChange}
|
||||
value={value}
|
||||
orientation="horizontal"
|
||||
className="mb-2"
|
||||
>
|
||||
<Radio
|
||||
<TradingRadio
|
||||
value={
|
||||
Schema.StopOrderTriggerDirection
|
||||
.TRIGGER_DIRECTION_RISES_ABOVE
|
||||
@ -221,7 +221,7 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
id="triggerDirection-risesAbove"
|
||||
label={'Rises above'}
|
||||
/>
|
||||
<Radio
|
||||
<TradingRadio
|
||||
value={
|
||||
Schema.StopOrderTriggerDirection
|
||||
.TRIGGER_DIRECTION_FALLS_BELOW
|
||||
@ -229,7 +229,7 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
id="triggerDirection-fallsBelow"
|
||||
label={'Falls below'}
|
||||
/>
|
||||
</RadioGroup>
|
||||
</TradingRadioGroup>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
@ -246,16 +246,17 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
validate: validateAmount(priceStep, 'Price'),
|
||||
}}
|
||||
control={control}
|
||||
render={({ field }) => {
|
||||
render={({ field, fieldState }) => {
|
||||
const { value, ...props } = field;
|
||||
return (
|
||||
<div className="mb-2">
|
||||
<Input
|
||||
<TradingInput
|
||||
data-testid="triggerPrice"
|
||||
type="number"
|
||||
step={priceStep}
|
||||
appendElement={asset.symbol}
|
||||
value={value || ''}
|
||||
hasError={!!fieldState.error}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
@ -263,9 +264,9 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
}}
|
||||
/>
|
||||
{errors.triggerPrice && (
|
||||
<InputError testId="stop-order-error-message-trigger-price">
|
||||
<TradingInputError testId="stop-order-error-message-trigger-price">
|
||||
{errors.triggerPrice.message}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
@ -294,16 +295,17 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
'Trailing percentage offset'
|
||||
),
|
||||
}}
|
||||
render={({ field }) => {
|
||||
render={({ field, fieldState }) => {
|
||||
const { value, ...props } = field;
|
||||
return (
|
||||
<div className="mb-2">
|
||||
<Input
|
||||
<TradingInput
|
||||
type="number"
|
||||
step={trailingPercentOffsetStep}
|
||||
appendElement="%"
|
||||
data-testid="triggerTrailingPercentOffset"
|
||||
value={value || ''}
|
||||
hasError={!!fieldState.error}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
@ -311,9 +313,9 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
}}
|
||||
/>
|
||||
{errors.triggerTrailingPercentOffset && (
|
||||
<InputError testId="stop-order-error-message-trigger-trailing-percent-offset">
|
||||
<TradingInputError testId="stop-order-error-message-trigger-trailing-percent-offset">
|
||||
{errors.triggerTrailingPercentOffset.message}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
@ -324,25 +326,29 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
render={({ field }) => {
|
||||
const { onChange, value } = field;
|
||||
return (
|
||||
<RadioGroup
|
||||
<TradingRadioGroup
|
||||
onChange={onChange}
|
||||
value={value}
|
||||
orientation="horizontal"
|
||||
>
|
||||
<Radio value="price" id="triggerType-price" label={'Price'} />
|
||||
<Radio
|
||||
<TradingRadio
|
||||
value="price"
|
||||
id="triggerType-price"
|
||||
label={'Price'}
|
||||
/>
|
||||
<TradingRadio
|
||||
value="trailingPercentOffset"
|
||||
id="triggerType-trailingPercentOffset"
|
||||
label={'Trailing Percent Offset'}
|
||||
/>
|
||||
</RadioGroup>
|
||||
</TradingRadioGroup>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
</TradingFormGroup>
|
||||
<div className="mb-2">
|
||||
<div className="flex items-start gap-4">
|
||||
<FormGroup
|
||||
<TradingFormGroup
|
||||
labelFor="input-price-quote"
|
||||
label={t(`Size`)}
|
||||
className="!mb-0 flex-1"
|
||||
@ -358,10 +364,10 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
},
|
||||
validate: validateAmount(sizeStep, 'Size'),
|
||||
}}
|
||||
render={({ field }) => {
|
||||
render={({ field, fieldState }) => {
|
||||
const { value, ...props } = field;
|
||||
return (
|
||||
<Input
|
||||
<TradingInput
|
||||
id="order-size"
|
||||
className="w-full"
|
||||
type="number"
|
||||
@ -370,16 +376,17 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
onWheel={(e) => e.currentTarget.blur()}
|
||||
data-testid="order-size"
|
||||
value={value || ''}
|
||||
hasError={!!fieldState.error}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
<div className="pt-7 leading-10">@</div>
|
||||
</TradingFormGroup>
|
||||
<div className="pt-5 leading-10">@</div>
|
||||
<div className="flex-1">
|
||||
{type === Schema.OrderType.TYPE_LIMIT ? (
|
||||
<FormGroup
|
||||
<TradingFormGroup
|
||||
labelFor="input-price-quote"
|
||||
label={t(`Price (${quoteName})`)}
|
||||
labelAlign="right"
|
||||
@ -397,10 +404,10 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
},
|
||||
validate: validateAmount(priceStep, 'Price'),
|
||||
}}
|
||||
render={({ field }) => {
|
||||
render={({ field, fieldState }) => {
|
||||
const { value, ...props } = field;
|
||||
return (
|
||||
<Input
|
||||
<TradingInput
|
||||
id="input-price-quote"
|
||||
className="w-full"
|
||||
type="number"
|
||||
@ -408,15 +415,16 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
data-testid="order-price"
|
||||
onWheel={(e) => e.currentTarget.blur()}
|
||||
value={value || ''}
|
||||
hasError={!!fieldState.error}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
</TradingFormGroup>
|
||||
) : (
|
||||
<div
|
||||
className="text-sm text-right pt-7 leading-10"
|
||||
className="text-sm text-right pt-5 leading-10"
|
||||
data-testid="price"
|
||||
>
|
||||
{priceFormatted && quoteName
|
||||
@ -427,21 +435,21 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
</div>
|
||||
</div>
|
||||
{errors.size && (
|
||||
<InputError testId="stop-order-error-message-size">
|
||||
<TradingInputError testId="stop-order-error-message-size">
|
||||
{errors.size.message}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
)}
|
||||
|
||||
{!errors.size &&
|
||||
errors.price &&
|
||||
type === Schema.OrderType.TYPE_LIMIT && (
|
||||
<InputError testId="stop-order-error-message-price">
|
||||
<TradingInputError testId="stop-order-error-message-price">
|
||||
{errors.price.message}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
)}
|
||||
</div>
|
||||
<div className="mb-2">
|
||||
<FormGroup
|
||||
<TradingFormGroup
|
||||
label={t('Time in force')}
|
||||
labelFor="select-time-in-force"
|
||||
compact={true}
|
||||
@ -449,11 +457,12 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
<Controller
|
||||
name="timeInForce"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Select
|
||||
render={({ field, fieldState }) => (
|
||||
<TradingSelect
|
||||
id="select-time-in-force"
|
||||
className="w-full"
|
||||
data-testid="order-tif"
|
||||
hasError={!!fieldState.error}
|
||||
{...field}
|
||||
>
|
||||
<option
|
||||
@ -468,14 +477,14 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
>
|
||||
{timeInForceLabel(Schema.OrderTimeInForce.TIME_IN_FORCE_FOK)}
|
||||
</option>
|
||||
</Select>
|
||||
</TradingSelect>
|
||||
)}
|
||||
/>
|
||||
</FormGroup>
|
||||
</TradingFormGroup>
|
||||
{errors.timeInForce && (
|
||||
<InputError testId="stop-error-message-tif">
|
||||
<TradingInputError testId="stop-error-message-tif">
|
||||
{errors.timeInForce.message}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex gap-2 pb-2 justify-between">
|
||||
@ -485,29 +494,29 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
render={({ field }) => {
|
||||
const { onChange: onCheckedChange, value } = field;
|
||||
return (
|
||||
<Checkbox
|
||||
<TradingCheckbox
|
||||
onCheckedChange={onCheckedChange}
|
||||
checked={value}
|
||||
name="expire"
|
||||
label={<span className="text-xs">{t('Expire')}</span>}
|
||||
label={t('Expire')}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Checkbox
|
||||
<TradingCheckbox
|
||||
name="reduce-only"
|
||||
checked={true}
|
||||
disabled={true}
|
||||
label={
|
||||
<Tooltip description={<span>{t(REDUCE_ONLY_TOOLTIP)}</span>}>
|
||||
<span className="text-xs">{t('Reduce only')}</span>
|
||||
<>{t('Reduce only')}</>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
{expire && (
|
||||
<>
|
||||
<FormGroup
|
||||
<TradingFormGroup
|
||||
label={t('Strategy')}
|
||||
labelFor="expiryStrategy"
|
||||
compact={true}
|
||||
@ -517,26 +526,26 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
control={control}
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<RadioGroup orientation="horizontal" {...field}>
|
||||
<Radio
|
||||
<TradingRadioGroup orientation="horizontal" {...field}>
|
||||
<TradingRadio
|
||||
value={
|
||||
Schema.StopOrderExpiryStrategy.EXPIRY_STRATEGY_SUBMIT
|
||||
}
|
||||
id="expiryStrategy-submit"
|
||||
label={'Submit'}
|
||||
/>
|
||||
<Radio
|
||||
<TradingRadio
|
||||
value={
|
||||
Schema.StopOrderExpiryStrategy.EXPIRY_STRATEGY_CANCELS
|
||||
}
|
||||
id="expiryStrategy-cancel"
|
||||
label={'Cancel'}
|
||||
/>
|
||||
</RadioGroup>
|
||||
</TradingRadioGroup>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
</TradingFormGroup>
|
||||
<div className="mb-2">
|
||||
<Controller
|
||||
name="expiresAt"
|
||||
|
@ -17,8 +17,8 @@ import type { OrderSubmission } from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { mapFormValuesToOrderSubmission } from '../../utils/map-form-values-to-submission';
|
||||
import {
|
||||
Checkbox,
|
||||
InputError,
|
||||
TradingCheckbox,
|
||||
TradingInputError,
|
||||
Intent,
|
||||
Notification,
|
||||
Tooltip,
|
||||
@ -417,7 +417,7 @@ export const DealTicket = ({
|
||||
name="postOnly"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Checkbox
|
||||
<TradingCheckbox
|
||||
name="post-only"
|
||||
checked={!disablePostOnlyCheckbox && field.value}
|
||||
disabled={disablePostOnlyCheckbox}
|
||||
@ -449,7 +449,7 @@ export const DealTicket = ({
|
||||
name="reduceOnly"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Checkbox
|
||||
<TradingCheckbox
|
||||
name="reduce-only"
|
||||
checked={!disableReduceOnlyCheckbox && field.value}
|
||||
disabled={disableReduceOnlyCheckbox}
|
||||
@ -483,7 +483,7 @@ export const DealTicket = ({
|
||||
name="iceberg"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Checkbox
|
||||
<TradingCheckbox
|
||||
name="iceberg"
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
@ -572,11 +572,11 @@ export const NoWalletWarning = ({
|
||||
if (isReadOnly) {
|
||||
return (
|
||||
<div className="mb-2">
|
||||
<InputError testId="deal-ticket-error-message-summary">
|
||||
<TradingInputError testId="deal-ticket-error-message-summary">
|
||||
{
|
||||
'You need to connect your own wallet to start trading on this market'
|
||||
}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -613,9 +613,9 @@ const SummaryMessage = memo(
|
||||
if (error?.message) {
|
||||
return (
|
||||
<div className="mb-2">
|
||||
<InputError testId="deal-ticket-error-message-summary">
|
||||
<TradingInputError testId="deal-ticket-error-message-summary">
|
||||
{error?.message}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,4 +1,8 @@
|
||||
import { FormGroup, Input, InputError } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
TradingFormGroup,
|
||||
TradingInput,
|
||||
TradingInputError,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { formatForInput } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useRef } from 'react';
|
||||
@ -19,24 +23,25 @@ export const ExpirySelector = ({
|
||||
const dateFormatted = formatForInput(date);
|
||||
const minDate = formatForInput(date);
|
||||
return (
|
||||
<FormGroup
|
||||
<TradingFormGroup
|
||||
label={t('Expiry time/date')}
|
||||
labelFor="expiration"
|
||||
compact={true}
|
||||
>
|
||||
<Input
|
||||
<TradingInput
|
||||
data-testid="date-picker-field"
|
||||
id="expiration"
|
||||
type="datetime-local"
|
||||
value={dateFormatted}
|
||||
onChange={(e) => onSelect(e.target.value)}
|
||||
min={minDate}
|
||||
hasError={!!errorMessage}
|
||||
/>
|
||||
{errorMessage && (
|
||||
<InputError testId="deal-ticket-error-message-expiry">
|
||||
<TradingInputError testId="deal-ticket-error-message-expiry">
|
||||
{errorMessage}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
</TradingFormGroup>
|
||||
);
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {
|
||||
FormGroup,
|
||||
InputError,
|
||||
Select,
|
||||
TradingFormGroup,
|
||||
TradingInputError,
|
||||
TradingSelect,
|
||||
Tooltip,
|
||||
SimpleGrid,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
@ -90,12 +90,12 @@ export const TimeInForceSelector = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<FormGroup
|
||||
<TradingFormGroup
|
||||
label={t('Time in force')}
|
||||
labelFor="select-time-in-force"
|
||||
compact={true}
|
||||
>
|
||||
<Select
|
||||
<TradingSelect
|
||||
id="select-time-in-force"
|
||||
value={value}
|
||||
onChange={(e) => {
|
||||
@ -103,18 +103,19 @@ export const TimeInForceSelector = ({
|
||||
}}
|
||||
className="w-full"
|
||||
data-testid="order-tif"
|
||||
hasError={!!errorMessage}
|
||||
>
|
||||
{options.map(([key, value]) => (
|
||||
<option key={key} value={value}>
|
||||
{timeInForceLabel(value)}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
</TradingSelect>
|
||||
{errorMessage && (
|
||||
<InputError testId="deal-ticket-error-message-tif">
|
||||
<TradingInputError testId="deal-ticket-error-message-tif">
|
||||
{renderError(errorMessage)}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
</TradingFormGroup>
|
||||
);
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {
|
||||
InputError,
|
||||
TradingInputError,
|
||||
SimpleGrid,
|
||||
Tooltip,
|
||||
TradingDropdown,
|
||||
@ -178,9 +178,9 @@ export const TypeSelector = ({
|
||||
value={value}
|
||||
/>
|
||||
{errorMessage && (
|
||||
<InputError testId="deal-ticket-error-message-type">
|
||||
<TradingInputError testId="deal-ticket-error-message-type">
|
||||
{renderError(errorMessage as MarketModeValidationType)}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
@ -14,14 +14,14 @@ import { t } from '@vegaprotocol/i18n';
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
Button,
|
||||
FormGroup,
|
||||
Input,
|
||||
InputError,
|
||||
RichSelect,
|
||||
TradingFormGroup,
|
||||
TradingInput,
|
||||
TradingInputError,
|
||||
TradingRichSelect,
|
||||
Notification,
|
||||
Intent,
|
||||
ButtonLink,
|
||||
Select,
|
||||
TradingSelect,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useWeb3React } from '@web3-react/core';
|
||||
@ -151,7 +151,7 @@ export const DepositForm = ({
|
||||
noValidate={true}
|
||||
data-testid="deposit-form"
|
||||
>
|
||||
<FormGroup
|
||||
<TradingFormGroup
|
||||
label={t('From (Ethereum address)')}
|
||||
labelFor="ethereum-address"
|
||||
>
|
||||
@ -197,15 +197,17 @@ export const DepositForm = ({
|
||||
}}
|
||||
/>
|
||||
{errors.from?.message && (
|
||||
<InputError intent="danger">{errors.from.message}</InputError>
|
||||
<TradingInputError intent="danger">
|
||||
{errors.from.message}
|
||||
</TradingInputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
<FormGroup label={t('To (Vega key)')} labelFor="to">
|
||||
</TradingFormGroup>
|
||||
<TradingFormGroup label={t('To (Vega key)')} labelFor="to">
|
||||
<AddressField
|
||||
pubKeys={pubKeys}
|
||||
onChange={() => setValue('to', '')}
|
||||
select={
|
||||
<Select {...register('to')} id="to" defaultValue="">
|
||||
<TradingSelect {...register('to')} id="to" defaultValue="">
|
||||
<option value="" disabled>
|
||||
{t('Please select')}
|
||||
</option>
|
||||
@ -215,10 +217,10 @@ export const DepositForm = ({
|
||||
{pk}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
</TradingSelect>
|
||||
}
|
||||
input={
|
||||
<Input
|
||||
<TradingInput
|
||||
// eslint-disable-next-line jsx-a11y/no-autofocus
|
||||
autoFocus={true} // focus input immediately after is shown
|
||||
id="to"
|
||||
@ -233,12 +235,12 @@ export const DepositForm = ({
|
||||
}
|
||||
/>
|
||||
{errors.to?.message && (
|
||||
<InputError intent="danger" forInput="to">
|
||||
<TradingInputError intent="danger" forInput="to">
|
||||
{errors.to.message}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
<FormGroup label={t('Asset')} labelFor="asset">
|
||||
</TradingFormGroup>
|
||||
<TradingFormGroup label={t('Asset')} labelFor="asset">
|
||||
<Controller
|
||||
control={control}
|
||||
name="asset"
|
||||
@ -248,7 +250,7 @@ export const DepositForm = ({
|
||||
},
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<RichSelect
|
||||
<TradingRichSelect
|
||||
data-testid="select-asset"
|
||||
id={field.name}
|
||||
name={field.name}
|
||||
@ -271,13 +273,13 @@ export const DepositForm = ({
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</RichSelect>
|
||||
</TradingRichSelect>
|
||||
)}
|
||||
/>
|
||||
{errors.asset?.message && (
|
||||
<InputError intent="danger" forInput="asset">
|
||||
<TradingInputError intent="danger" forInput="asset">
|
||||
{errors.asset.message}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
)}
|
||||
{isActive && isFaucetable && selectedAsset && (
|
||||
<UseButton onClick={submitFaucet}>
|
||||
@ -296,7 +298,7 @@ export const DepositForm = ({
|
||||
{t('View asset details')}
|
||||
</button>
|
||||
)}
|
||||
</FormGroup>
|
||||
</TradingFormGroup>
|
||||
<FaucetNotification
|
||||
isActive={isActive}
|
||||
selectedAsset={selectedAsset}
|
||||
@ -308,8 +310,8 @@ export const DepositForm = ({
|
||||
</div>
|
||||
)}
|
||||
{approved && (
|
||||
<FormGroup label={t('Amount')} labelFor="amount">
|
||||
<Input
|
||||
<TradingFormGroup label={t('Amount')} labelFor="amount">
|
||||
<TradingInput
|
||||
type="number"
|
||||
autoComplete="off"
|
||||
id="amount"
|
||||
@ -374,9 +376,9 @@ export const DepositForm = ({
|
||||
})}
|
||||
/>
|
||||
{errors.amount?.message && (
|
||||
<InputError intent="danger" forInput="amount">
|
||||
<TradingInputError intent="danger" forInput="amount">
|
||||
{errors.amount.message}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
)}
|
||||
{selectedAsset && balances && (
|
||||
<UseButton
|
||||
@ -390,7 +392,7 @@ export const DepositForm = ({
|
||||
{t('Use maximum')}
|
||||
</UseButton>
|
||||
)}
|
||||
</FormGroup>
|
||||
</TradingFormGroup>
|
||||
)}
|
||||
<ApproveNotification
|
||||
isActive={isActive}
|
||||
|
@ -4,10 +4,10 @@ import { t } from '@vegaprotocol/i18n';
|
||||
import {
|
||||
Button,
|
||||
ButtonLink,
|
||||
Input,
|
||||
TradingInput,
|
||||
Loader,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
TradingRadio,
|
||||
TradingRadioGroup,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useEnvironment } from '../../hooks';
|
||||
import { CUSTOM_NODE_KEY } from '../../types';
|
||||
@ -76,7 +76,7 @@ export const NodeSwitcher = ({ closeDialog }: { closeDialog: () => void }) => {
|
||||
`This app will only work on ${VEGA_ENV}. Select a node to connect to.`
|
||||
)}
|
||||
</p>
|
||||
<RadioGroup
|
||||
<TradingRadioGroup
|
||||
value={nodeRadio}
|
||||
onChange={(value) => setNodeRadio(value)}
|
||||
>
|
||||
@ -112,7 +112,7 @@ export const NodeSwitcher = ({ closeDialog }: { closeDialog: () => void }) => {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</TradingRadioGroup>
|
||||
<div className="mt-4">
|
||||
<Button
|
||||
fill={true}
|
||||
@ -161,7 +161,7 @@ const CustomRowWrapper = ({
|
||||
<LayoutRow dataTestId="custom-row">
|
||||
<div className="flex w-full mb-2">
|
||||
{nodes.length > 0 && (
|
||||
<Radio
|
||||
<TradingRadio
|
||||
id="node-url-custom"
|
||||
value={CUSTOM_NODE_KEY}
|
||||
label={nodeRadio === CUSTOM_NODE_KEY ? '' : t('Other')}
|
||||
@ -172,7 +172,7 @@ const CustomRowWrapper = ({
|
||||
data-testid="custom-node"
|
||||
className="flex items-center w-full gap-2"
|
||||
>
|
||||
<Input
|
||||
<TradingInput
|
||||
placeholder="https://"
|
||||
value={inputText}
|
||||
hasError={Boolean(error)}
|
||||
|
@ -2,7 +2,7 @@ import type { ApolloError } from '@apollo/client';
|
||||
import { useHeaderStore } from '@vegaprotocol/apollo-client';
|
||||
import { isValidUrl } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Radio } from '@vegaprotocol/ui-toolkit';
|
||||
import { TradingRadio } from '@vegaprotocol/ui-toolkit';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { CUSTOM_NODE_KEY } from '../../types';
|
||||
import {
|
||||
@ -138,7 +138,7 @@ export const RowData = ({
|
||||
<>
|
||||
{id !== CUSTOM_NODE_KEY && (
|
||||
<div className="break-all" data-testid="node">
|
||||
<Radio id={`node-url-${id}`} value={url} label={url} />
|
||||
<TradingRadio id={`node-url-${id}`} value={url} label={url} />
|
||||
</div>
|
||||
)}
|
||||
<LayoutCell
|
||||
|
@ -9,9 +9,9 @@ import { t } from '@vegaprotocol/i18n';
|
||||
import { Size } from '@vegaprotocol/datagrid';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import {
|
||||
FormGroup,
|
||||
Input,
|
||||
InputError,
|
||||
TradingFormGroup,
|
||||
TradingInput,
|
||||
TradingInputError,
|
||||
Button,
|
||||
Dialog,
|
||||
Icon,
|
||||
@ -102,8 +102,12 @@ export const OrderEditDialog = ({
|
||||
noValidate
|
||||
>
|
||||
<div className="flex flex-col md:flex-row gap-4">
|
||||
<FormGroup label={t('Price')} labelFor="limitPrice" className="grow">
|
||||
<Input
|
||||
<TradingFormGroup
|
||||
label={t('Price')}
|
||||
labelFor="limitPrice"
|
||||
className="grow"
|
||||
>
|
||||
<TradingInput
|
||||
type="number"
|
||||
step={step}
|
||||
{...register('limitPrice', {
|
||||
@ -119,13 +123,13 @@ export const OrderEditDialog = ({
|
||||
id="limitPrice"
|
||||
/>
|
||||
{errors.limitPrice?.message && (
|
||||
<InputError intent="danger">
|
||||
<TradingInputError intent="danger">
|
||||
{errors.limitPrice.message}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
<FormGroup label={t('Size')} labelFor="size" className="grow">
|
||||
<Input
|
||||
</TradingFormGroup>
|
||||
<TradingFormGroup label={t('Size')} labelFor="size" className="grow">
|
||||
<TradingInput
|
||||
type="number"
|
||||
step={stepSize}
|
||||
{...register('size', {
|
||||
@ -139,9 +143,11 @@ export const OrderEditDialog = ({
|
||||
id="size"
|
||||
/>
|
||||
{errors.size?.message && (
|
||||
<InputError intent="danger">{errors.size.message}</InputError>
|
||||
<TradingInputError intent="danger">
|
||||
{errors.size.message}
|
||||
</TradingInputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
</TradingFormGroup>
|
||||
</div>
|
||||
<Button variant="primary" size="md" type="submit">
|
||||
{t('Update')}
|
||||
|
@ -172,7 +172,7 @@ module.exports = {
|
||||
900: '#F9FAFA',
|
||||
},
|
||||
},
|
||||
danger: '#FF077F',
|
||||
danger: '#EC003C',
|
||||
warning: '#FF8700',
|
||||
success: '#00F780',
|
||||
},
|
||||
|
@ -16,8 +16,8 @@ export * from './form-group';
|
||||
export * from './healthbar';
|
||||
export * from './icon';
|
||||
export * from './indicator';
|
||||
export * from './input-error';
|
||||
export * from './input';
|
||||
export * from './input-error';
|
||||
export * from './key-value-table';
|
||||
export * from './link';
|
||||
export * from './loader';
|
||||
@ -48,10 +48,18 @@ export * from './tiny-scroll';
|
||||
export * from './toast';
|
||||
export * from './toggle';
|
||||
export * from './tooltip';
|
||||
export * from './trading-button';
|
||||
export * from './trading-dropdown';
|
||||
export * from './traffic-light';
|
||||
export * from './vega-icons';
|
||||
export * from './vega-logo';
|
||||
export * from './viewing-as-user';
|
||||
export * from './pill';
|
||||
|
||||
// Trading specific components
|
||||
export * from './trading-button';
|
||||
export * from './trading-checkbox';
|
||||
export * from './trading-dropdown';
|
||||
export * from './trading-form-group';
|
||||
export * from './trading-input-error';
|
||||
export * from './trading-input';
|
||||
export * from './trading-radio-group';
|
||||
export * from './trading-select';
|
||||
|
@ -0,0 +1,40 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import { TradingCheckbox } from './checkbox';
|
||||
|
||||
describe('Checkbox', () => {
|
||||
it('should render checkbox with label successfully', () => {
|
||||
render(<TradingCheckbox label="test" />);
|
||||
expect(screen.getByText('test')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render a checked checkbox if specified in state', () => {
|
||||
render(<TradingCheckbox label="label" checked={true} />);
|
||||
expect(screen.getByTestId(/icon-/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render an unchecked checkbox if specified in state', () => {
|
||||
render(<TradingCheckbox label="unchecked" checked={false} />);
|
||||
expect(screen.queryByTestId(/icon-/)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render an indeterminate checkbox if specified in state', () => {
|
||||
render(<TradingCheckbox label="indeterminate" checked="indeterminate" />);
|
||||
expect(screen.getByTestId('indeterminate-icon')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('fires callback on change if provided', () => {
|
||||
const callback = jest.fn();
|
||||
|
||||
render(
|
||||
<TradingCheckbox
|
||||
name="test"
|
||||
label="onchange"
|
||||
onCheckedChange={callback}
|
||||
/>
|
||||
);
|
||||
|
||||
const checkbox = screen.getByText('onchange');
|
||||
fireEvent.click(checkbox);
|
||||
expect(callback).toHaveBeenCalled();
|
||||
});
|
||||
});
|
@ -0,0 +1,38 @@
|
||||
import type { Meta, StoryFn } from '@storybook/react';
|
||||
import type { TradingCheckboxProps } from './checkbox';
|
||||
import { TradingCheckbox } from './checkbox';
|
||||
|
||||
export default {
|
||||
component: TradingCheckbox,
|
||||
title: 'Checkbox',
|
||||
} as Meta<typeof TradingCheckbox>;
|
||||
|
||||
const Template: StoryFn<TradingCheckboxProps> = (args) => (
|
||||
<TradingCheckbox {...args} />
|
||||
);
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {
|
||||
name: 'default',
|
||||
label: 'Regular checkbox',
|
||||
};
|
||||
|
||||
export const Overflow = Template.bind({});
|
||||
Overflow.args = {
|
||||
name: 'overflow',
|
||||
label:
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
|
||||
};
|
||||
|
||||
export const Disabled = Template.bind({});
|
||||
Disabled.args = {
|
||||
disabled: true,
|
||||
label: 'Disabled',
|
||||
};
|
||||
|
||||
export const Indeterminate = Template.bind({});
|
||||
Indeterminate.args = {
|
||||
name: 'default',
|
||||
checked: 'indeterminate',
|
||||
label: 'Indeterminate checkbox',
|
||||
};
|
63
libs/ui-toolkit/src/components/trading-checkbox/checkbox.tsx
Normal file
63
libs/ui-toolkit/src/components/trading-checkbox/checkbox.tsx
Normal file
@ -0,0 +1,63 @@
|
||||
import { VegaIcon, VegaIconNames } from '../icon';
|
||||
import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
|
||||
import classNames from 'classnames';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
type CheckedState = boolean | 'indeterminate';
|
||||
export interface TradingCheckboxProps {
|
||||
checked?: CheckedState;
|
||||
label?: ReactNode;
|
||||
name?: string;
|
||||
onCheckedChange?: (checked: CheckedState) => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export const TradingCheckbox = ({
|
||||
checked,
|
||||
label,
|
||||
name,
|
||||
onCheckedChange,
|
||||
disabled = false,
|
||||
}: TradingCheckboxProps) => {
|
||||
const rootClasses = classNames(
|
||||
'relative flex justify-center items-center w-3 h-3',
|
||||
'border rounded-sm overflow-hidden',
|
||||
'border-vega-clight-500 dark:border-vega-cdark-500',
|
||||
'aria-checked:border-vega-clight-400 dark:aria-checked:border-vega-cdark-400',
|
||||
'disabled:border-vega-clight-600 dark:disabled:border-vega-cdark-600',
|
||||
'bg-vega-clight-700 dark:bg-vega-cdark-700'
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex gap-1.5 items-center">
|
||||
<CheckboxPrimitive.Root
|
||||
name={name}
|
||||
id={name}
|
||||
className={rootClasses}
|
||||
checked={checked}
|
||||
onCheckedChange={onCheckedChange}
|
||||
disabled={disabled}
|
||||
data-testid={name}
|
||||
>
|
||||
<CheckboxPrimitive.CheckboxIndicator className="flex justify-center items-center w-3 h-3">
|
||||
{checked === 'indeterminate' ? (
|
||||
<span
|
||||
data-testid="indeterminate-icon"
|
||||
className="absolute w-[8px] h-[2px] bg-vega-clight-50 dark:bg-vega-cdark-50"
|
||||
/>
|
||||
) : (
|
||||
<VegaIcon name={VegaIconNames.TICK} size={10} />
|
||||
)}
|
||||
</CheckboxPrimitive.CheckboxIndicator>
|
||||
</CheckboxPrimitive.Root>
|
||||
<label
|
||||
htmlFor={name}
|
||||
className={classNames('text-xs flex-1', {
|
||||
'text-vega-clight-200 dark:text-vega-cdark-200': disabled,
|
||||
})}
|
||||
>
|
||||
{label}
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
};
|
1
libs/ui-toolkit/src/components/trading-checkbox/index.ts
Normal file
1
libs/ui-toolkit/src/components/trading-checkbox/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './checkbox';
|
@ -0,0 +1,23 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
|
||||
import { TradingFormGroup } from './form-group';
|
||||
|
||||
describe('FormGroup', () => {
|
||||
it('should render label if given a label', () => {
|
||||
render(
|
||||
<TradingFormGroup label="label" labelFor="test">
|
||||
<input id="test"></input>
|
||||
</TradingFormGroup>
|
||||
);
|
||||
expect(screen.getByLabelText('label')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render children', () => {
|
||||
render(
|
||||
<TradingFormGroup label="label" labelFor="test">
|
||||
<input data-testid="foo" id="test"></input>
|
||||
</TradingFormGroup>
|
||||
);
|
||||
expect(screen.getByTestId('foo')).toBeInTheDocument();
|
||||
});
|
||||
});
|
@ -0,0 +1,53 @@
|
||||
import classNames from 'classnames';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
export interface TradingFormGroupProps {
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
label: string | ReactNode; // For accessibility reasons this must always be set for screen readers. If you want it to not show, then use the hideLabel prop"
|
||||
labelFor: string; // Same as above
|
||||
hideLabel?: boolean;
|
||||
disabled?: boolean;
|
||||
labelDescription?: string;
|
||||
labelAlign?: 'left' | 'right';
|
||||
compact?: boolean;
|
||||
}
|
||||
|
||||
export const TradingFormGroup = ({
|
||||
children,
|
||||
className,
|
||||
label,
|
||||
labelFor,
|
||||
labelDescription,
|
||||
labelAlign = 'left',
|
||||
hideLabel = false,
|
||||
compact = false,
|
||||
disabled = false,
|
||||
}: TradingFormGroupProps) => {
|
||||
const wrapperClasses = classNames(
|
||||
'relative',
|
||||
{
|
||||
'mb-2': compact,
|
||||
'mb-4': !compact,
|
||||
},
|
||||
className
|
||||
);
|
||||
const labelClasses = classNames('block mb-2 text-xs', {
|
||||
'text-right': labelAlign === 'right',
|
||||
'sr-only': hideLabel,
|
||||
'text-muted': disabled,
|
||||
});
|
||||
return (
|
||||
<div data-testid="form-group" className={wrapperClasses}>
|
||||
{label && (
|
||||
<label htmlFor={labelFor} className={labelClasses}>
|
||||
{label}
|
||||
{labelDescription && (
|
||||
<div className="font-light mt-1">{labelDescription}</div>
|
||||
)}
|
||||
</label>
|
||||
)}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,47 @@
|
||||
import type { StoryFn, Meta } from '@storybook/react';
|
||||
import { TradingInput } from '../trading-input';
|
||||
import type { TradingFormGroupProps } from './form-group';
|
||||
import { TradingFormGroup } from './form-group';
|
||||
export default {
|
||||
component: TradingFormGroup,
|
||||
title: 'FormGroup',
|
||||
argTypes: {
|
||||
label: {
|
||||
type: 'string',
|
||||
},
|
||||
labelFor: {
|
||||
type: 'string',
|
||||
},
|
||||
labelDescription: {
|
||||
type: 'string',
|
||||
},
|
||||
className: {
|
||||
type: 'string',
|
||||
},
|
||||
hasError: {
|
||||
type: 'boolean',
|
||||
},
|
||||
disabled: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
} as Meta;
|
||||
|
||||
const Template: StoryFn<TradingFormGroupProps> = (args) => (
|
||||
<TradingFormGroup {...args}>
|
||||
<TradingInput id="labelFor" />
|
||||
</TradingFormGroup>
|
||||
);
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {
|
||||
label: 'Label',
|
||||
labelFor: 'labelFor',
|
||||
};
|
||||
|
||||
export const WithLabelDescription = Template.bind({});
|
||||
WithLabelDescription.args = {
|
||||
label: 'Label',
|
||||
labelFor: 'labelFor',
|
||||
labelDescription: 'Description text',
|
||||
};
|
@ -0,0 +1 @@
|
||||
export * from './form-group';
|
@ -0,0 +1 @@
|
||||
export * from './input-error';
|
@ -0,0 +1,10 @@
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import { TradingInputError } from './input-error';
|
||||
|
||||
describe('InputError', () => {
|
||||
it('should render successfully', () => {
|
||||
const { baseElement } = render(<TradingInputError />);
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,20 @@
|
||||
import type { StoryFn, Meta } from '@storybook/react';
|
||||
import { TradingInputError } from './input-error';
|
||||
|
||||
export default {
|
||||
component: TradingInputError,
|
||||
title: 'InputError',
|
||||
} as Meta;
|
||||
|
||||
const Template: StoryFn = (args) => <TradingInputError {...args} />;
|
||||
|
||||
export const Danger = Template.bind({});
|
||||
Danger.args = {
|
||||
children: 'An error that might have happened',
|
||||
};
|
||||
|
||||
export const Warning = Template.bind({});
|
||||
Warning.args = {
|
||||
intent: 'warning',
|
||||
children: 'Something that might be an issue',
|
||||
};
|
@ -0,0 +1,42 @@
|
||||
import classNames from 'classnames';
|
||||
import type { HTMLAttributes } from 'react';
|
||||
|
||||
interface TradingInputErrorProps extends HTMLAttributes<HTMLDivElement> {
|
||||
children?: React.ReactNode;
|
||||
intent?: 'danger' | 'warning';
|
||||
forInput?: string;
|
||||
testId?: string;
|
||||
}
|
||||
|
||||
export const TradingInputError = ({
|
||||
intent = 'danger',
|
||||
children,
|
||||
forInput,
|
||||
testId,
|
||||
className,
|
||||
...props
|
||||
}: TradingInputErrorProps) => {
|
||||
const effectiveClassName = classNames(
|
||||
'text-xs flex items-center first-letter:uppercase',
|
||||
'mt-2',
|
||||
{
|
||||
'border-danger': intent === 'danger',
|
||||
'border-warning': intent === 'warning',
|
||||
},
|
||||
{
|
||||
'text-warning': intent === 'warning',
|
||||
'text-danger': intent === 'danger',
|
||||
}
|
||||
);
|
||||
return (
|
||||
<div
|
||||
data-testid={testId || 'input-error-text'}
|
||||
aria-describedby={forInput}
|
||||
className={classNames(effectiveClassName, className)}
|
||||
{...props}
|
||||
role="alert"
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
1
libs/ui-toolkit/src/components/trading-input/index.ts
Normal file
1
libs/ui-toolkit/src/components/trading-input/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './input';
|
10
libs/ui-toolkit/src/components/trading-input/input.spec.tsx
Normal file
10
libs/ui-toolkit/src/components/trading-input/input.spec.tsx
Normal file
@ -0,0 +1,10 @@
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import { TradingInput } from './input';
|
||||
|
||||
describe('Input', () => {
|
||||
it('should render successfully', () => {
|
||||
const { baseElement } = render(<TradingInput />);
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,83 @@
|
||||
import type { StoryFn, Meta } from '@storybook/react';
|
||||
import { TradingInput } from './input';
|
||||
import { FormGroup } from '../form-group';
|
||||
export default {
|
||||
component: TradingInput,
|
||||
title: 'Input',
|
||||
} as Meta;
|
||||
|
||||
const Template: StoryFn = (args) => (
|
||||
<FormGroup label="Hello" labelFor={args.id}>
|
||||
<TradingInput value="I type words" {...args} />
|
||||
</FormGroup>
|
||||
);
|
||||
|
||||
const customElementPlaceholder = (
|
||||
<span
|
||||
style={{
|
||||
fontFamily: 'monospace',
|
||||
backgroundColor: 'grey',
|
||||
padding: '4px',
|
||||
}}
|
||||
>
|
||||
Ω
|
||||
</span>
|
||||
);
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {
|
||||
id: 'input-default',
|
||||
};
|
||||
|
||||
export const WithError = Template.bind({});
|
||||
WithError.args = {
|
||||
hasError: true,
|
||||
id: 'input-has-error',
|
||||
};
|
||||
|
||||
export const Disabled = Template.bind({});
|
||||
Disabled.args = {
|
||||
disabled: true,
|
||||
id: 'input-disabled',
|
||||
};
|
||||
|
||||
export const TypeDate = Template.bind({});
|
||||
TypeDate.args = {
|
||||
type: 'date',
|
||||
id: 'input-date',
|
||||
};
|
||||
|
||||
export const TypeDateTime = Template.bind({});
|
||||
TypeDateTime.args = {
|
||||
type: 'datetime-local',
|
||||
id: 'input-datetime-local',
|
||||
min: '2022-09-05T11:29:17',
|
||||
max: '2023-09-05T10:29:49',
|
||||
};
|
||||
|
||||
export const IconPrepend = Template.bind({});
|
||||
IconPrepend.args = {
|
||||
prependIconName: 'search',
|
||||
id: 'input-icon-prepend',
|
||||
};
|
||||
|
||||
export const IconAppend = Template.bind({});
|
||||
IconAppend.args = {
|
||||
value: 'I type words and even more words',
|
||||
appendIconName: 'search',
|
||||
id: 'input-icon-append',
|
||||
};
|
||||
|
||||
export const ElementPrepend = Template.bind({});
|
||||
ElementPrepend.args = {
|
||||
value: '<- custom element',
|
||||
prependElement: customElementPlaceholder,
|
||||
id: 'input-element-prepend',
|
||||
};
|
||||
|
||||
export const ElementAppend = Template.bind({});
|
||||
ElementAppend.args = {
|
||||
value: 'custom element ->',
|
||||
appendElement: customElementPlaceholder,
|
||||
id: 'input-element-append',
|
||||
};
|
174
libs/ui-toolkit/src/components/trading-input/input.tsx
Normal file
174
libs/ui-toolkit/src/components/trading-input/input.tsx
Normal file
@ -0,0 +1,174 @@
|
||||
import type { InputHTMLAttributes, ReactNode } from 'react';
|
||||
import { forwardRef } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import type { IconName } from '../icon';
|
||||
import { Icon } from '../icon';
|
||||
import { defaultFormElement } from '../../utils/shared';
|
||||
|
||||
type InputRootProps = InputHTMLAttributes<HTMLInputElement> & {
|
||||
hasError?: boolean;
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
type NoPrepend = {
|
||||
prependIconName?: never;
|
||||
prependIconDescription?: string;
|
||||
prependElement?: never;
|
||||
};
|
||||
|
||||
type NoAppend = {
|
||||
appendIconName?: never;
|
||||
appendIconDescription?: string;
|
||||
appendElement?: never;
|
||||
};
|
||||
|
||||
type InputPrepend = NoAppend &
|
||||
(
|
||||
| NoPrepend
|
||||
| {
|
||||
prependIconName: IconName;
|
||||
prependIconDescription?: string;
|
||||
prependElement?: never;
|
||||
}
|
||||
| {
|
||||
prependIconName?: never;
|
||||
prependIconDescription?: never;
|
||||
prependElement: ReactNode;
|
||||
}
|
||||
);
|
||||
|
||||
type InputAppend = NoPrepend &
|
||||
(
|
||||
| NoAppend
|
||||
| {
|
||||
appendIconName: IconName;
|
||||
appendIconDescription?: string;
|
||||
appendElement?: never;
|
||||
}
|
||||
| {
|
||||
appendIconName?: never;
|
||||
appendIconDescription?: never;
|
||||
appendElement: ReactNode;
|
||||
}
|
||||
);
|
||||
|
||||
type AffixProps = InputPrepend | InputAppend;
|
||||
|
||||
export type TradingInputProps = InputRootProps & AffixProps;
|
||||
|
||||
export const tradingInputStyle = ({
|
||||
style,
|
||||
disabled,
|
||||
}: {
|
||||
style?: React.CSSProperties;
|
||||
disabled?: boolean;
|
||||
}) =>
|
||||
disabled
|
||||
? {
|
||||
...style,
|
||||
backgroundImage:
|
||||
'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGCAYAAADgzO9IAAAAAXNSR0IArs4c6QAAACNJREFUGFdjtLS0/M8ABcePH2eEsRlJl4BpBdHIuuFmEi0BABqjEQVjx/LTAAAAAElFTkSuQmCC)',
|
||||
}
|
||||
: style;
|
||||
|
||||
const getAffixElement = ({
|
||||
prependElement,
|
||||
prependIconName,
|
||||
prependIconDescription,
|
||||
appendElement,
|
||||
appendIconName,
|
||||
appendIconDescription,
|
||||
}: Pick<TradingInputProps, keyof AffixProps>) => {
|
||||
const position = prependIconName || prependElement ? 'pre' : 'post';
|
||||
|
||||
const className = classNames(
|
||||
['fill-black dark:fill-white', 'absolute', 'z-10'],
|
||||
{
|
||||
'left-3': position === 'pre',
|
||||
'right-3': position === 'post',
|
||||
}
|
||||
);
|
||||
|
||||
const element = prependElement || appendElement;
|
||||
const iconName = prependIconName || appendIconName;
|
||||
const iconDescription = prependIconDescription || appendIconDescription;
|
||||
|
||||
if (element) {
|
||||
return <div className={className}>{element}</div>;
|
||||
}
|
||||
|
||||
if (iconName) {
|
||||
return (
|
||||
<Icon
|
||||
name={iconName}
|
||||
className={className}
|
||||
aria-label={iconDescription}
|
||||
aria-hidden={!iconDescription}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const TradingInput = forwardRef<HTMLInputElement, TradingInputProps>(
|
||||
(
|
||||
{
|
||||
prependIconName,
|
||||
prependIconDescription,
|
||||
appendIconName,
|
||||
appendIconDescription,
|
||||
prependElement,
|
||||
appendElement,
|
||||
className,
|
||||
hasError,
|
||||
...props
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const hasPrepended = !!(prependIconName || prependElement);
|
||||
const hasAppended = !!(appendIconName || appendElement);
|
||||
|
||||
const inputClassName = classNames(
|
||||
'appearance-none dark:color-scheme-dark px-3 h-8',
|
||||
className,
|
||||
{
|
||||
'pl-9': hasPrepended,
|
||||
'pr-9': hasAppended,
|
||||
}
|
||||
);
|
||||
|
||||
const input = (
|
||||
<input
|
||||
{...props}
|
||||
ref={ref}
|
||||
className={classNames(
|
||||
defaultFormElement(hasError, props.disabled),
|
||||
inputClassName
|
||||
)}
|
||||
/>
|
||||
);
|
||||
|
||||
const element = getAffixElement({
|
||||
prependIconName,
|
||||
prependIconDescription,
|
||||
appendIconName,
|
||||
appendIconDescription,
|
||||
prependElement,
|
||||
appendElement,
|
||||
});
|
||||
|
||||
if (element) {
|
||||
return (
|
||||
<div className="flex items-center relative">
|
||||
{hasPrepended && element}
|
||||
{input}
|
||||
{hasAppended && element}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
);
|
@ -0,0 +1 @@
|
||||
export * from './radio-group';
|
@ -0,0 +1,22 @@
|
||||
import type { StoryFn, Meta } from '@storybook/react';
|
||||
import type { TradingRadioGroupProps } from './radio-group';
|
||||
import { TradingRadioGroup, TradingRadio } from './radio-group';
|
||||
|
||||
export default {
|
||||
component: TradingRadioGroup,
|
||||
title: 'RadioGroup',
|
||||
} as Meta;
|
||||
|
||||
const Template: StoryFn<TradingRadioGroupProps> = (args) => (
|
||||
<TradingRadioGroup {...args}>
|
||||
<TradingRadio id="item-1" value="1" label="Item 1" />
|
||||
<TradingRadio id="item-2" value="2" label="Item 2" />
|
||||
<TradingRadio id="item-3" value="3" label="Disabled item" disabled={true} />
|
||||
</TradingRadioGroup>
|
||||
);
|
||||
|
||||
export const Vertical = Template.bind({});
|
||||
export const Horizontal = Template.bind({});
|
||||
Horizontal.args = {
|
||||
orientation: 'horizontal',
|
||||
};
|
@ -0,0 +1,99 @@
|
||||
import { forwardRef } from 'react';
|
||||
import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
|
||||
import classNames from 'classnames';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
export interface TradingRadioGroupProps {
|
||||
name?: string;
|
||||
children: ReactNode;
|
||||
defaultValue?: string;
|
||||
value?: string;
|
||||
orientation?: 'horizontal' | 'vertical';
|
||||
onChange?: (value: string) => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const TradingRadioGroup = forwardRef<
|
||||
HTMLDivElement,
|
||||
TradingRadioGroupProps
|
||||
>(
|
||||
(
|
||||
{
|
||||
children,
|
||||
name,
|
||||
value,
|
||||
orientation = 'vertical',
|
||||
onChange,
|
||||
className,
|
||||
}: TradingRadioGroupProps,
|
||||
ref
|
||||
) => {
|
||||
const groupClasses = classNames(
|
||||
'flex text-sm',
|
||||
{
|
||||
'flex-col gap-2': orientation === 'vertical',
|
||||
'flex-row gap-4': orientation === 'horizontal',
|
||||
},
|
||||
className
|
||||
);
|
||||
return (
|
||||
<RadioGroupPrimitive.Root
|
||||
ref={ref}
|
||||
name={name}
|
||||
value={value}
|
||||
onValueChange={onChange}
|
||||
orientation={orientation}
|
||||
className={groupClasses}
|
||||
>
|
||||
{children}
|
||||
</RadioGroupPrimitive.Root>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
interface RadioProps {
|
||||
id: string;
|
||||
value: string;
|
||||
label: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export const TradingRadio = ({ id, value, label, disabled }: RadioProps) => {
|
||||
const wrapperClasses = classNames('flex items-center gap-1.5 text-xs');
|
||||
const itemClasses = classNames(
|
||||
'flex justify-center items-center',
|
||||
'w-3 h-3 rounded-full border',
|
||||
'border-vega-clight-500 dark:border-vega-cdark-500',
|
||||
'aria-checked:border-vega-clight-400 dark:aria-checked:border-vega-cdark-400',
|
||||
'disabled:border-vega-clight-600 dark:disabled:border-vega-cdark-600',
|
||||
'bg-vega-clight-700 dark:bg-vega-cdark-700'
|
||||
);
|
||||
const indicatorClasses = classNames(
|
||||
'block w-2.5 h-2.5 border-2 rounded-full',
|
||||
'bg-vega-clight-50 dark:bg-vega-cdark-50',
|
||||
'border-vega-clight-700 dark:border-vega-cdark-700'
|
||||
);
|
||||
return (
|
||||
<div className={wrapperClasses}>
|
||||
<RadioGroupPrimitive.Item
|
||||
value={value}
|
||||
className={itemClasses}
|
||||
id={id}
|
||||
data-testid={id}
|
||||
disabled={disabled}
|
||||
>
|
||||
<RadioGroupPrimitive.Indicator className={indicatorClasses} />
|
||||
</RadioGroupPrimitive.Item>
|
||||
<label
|
||||
htmlFor={id}
|
||||
className={
|
||||
disabled
|
||||
? 'text-vega-clight-200 dark:text-vega-cdark-200'
|
||||
: 'cursor-pointer'
|
||||
}
|
||||
>
|
||||
{label}
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
};
|
1
libs/ui-toolkit/src/components/trading-select/index.ts
Normal file
1
libs/ui-toolkit/src/components/trading-select/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './select';
|
@ -0,0 +1,37 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import { TradingRichSelect, TradingSelect, TradingOption } from './select';
|
||||
|
||||
describe('Select', () => {
|
||||
it('should render successfully', () => {
|
||||
const { baseElement } = render(<TradingSelect />);
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('RichSelect', () => {
|
||||
it('should render select element with placeholder when no value is pre-selected', async () => {
|
||||
const { findByTestId } = render(
|
||||
<TradingRichSelect placeholder={'Select'}>
|
||||
<TradingOption value={'1'}>1</TradingOption>
|
||||
<TradingOption value={'2'}>2</TradingOption>
|
||||
</TradingRichSelect>
|
||||
);
|
||||
const btn = (await findByTestId(
|
||||
'rich-select-trigger'
|
||||
)) as HTMLButtonElement;
|
||||
expect(btn.textContent).toEqual('Select');
|
||||
});
|
||||
|
||||
it('should render select element with pre-selected value', async () => {
|
||||
const { findByTestId } = render(
|
||||
<TradingRichSelect placeholder={'Select'} value={'1'}>
|
||||
<TradingOption value={'1'}>1</TradingOption>
|
||||
<TradingOption value={'2'}>2</TradingOption>
|
||||
</TradingRichSelect>
|
||||
);
|
||||
const btn = (await findByTestId(
|
||||
'rich-select-trigger'
|
||||
)) as HTMLButtonElement;
|
||||
expect(btn.textContent).toEqual('1');
|
||||
});
|
||||
});
|
@ -0,0 +1,92 @@
|
||||
import type { StoryFn, Meta } from '@storybook/react';
|
||||
import { TradingOption, TradingSelect, TradingRichSelect } from './select';
|
||||
import { FormGroup } from '../form-group';
|
||||
|
||||
export default {
|
||||
component: TradingSelect,
|
||||
title: 'Select',
|
||||
} as Meta;
|
||||
|
||||
const Template: StoryFn = (args) => (
|
||||
<FormGroup label="Select an option" labelFor={args.id}>
|
||||
<TradingSelect {...args}>
|
||||
<option value="Option 1">Option 1</option>
|
||||
<option value="Option 2">Option 2</option>
|
||||
<option value="Option 3">Option 3</option>
|
||||
</TradingSelect>
|
||||
</FormGroup>
|
||||
);
|
||||
|
||||
const RichSelectTemplate: StoryFn = ({ placeholder, ...props }) => (
|
||||
<FormGroup label="Select an option" labelFor={props.id}>
|
||||
<TradingRichSelect placeholder={placeholder} {...props} />
|
||||
</FormGroup>
|
||||
);
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {
|
||||
id: 'select-default',
|
||||
};
|
||||
|
||||
export const WithError = Template.bind({});
|
||||
WithError.args = {
|
||||
id: 'select-has-error',
|
||||
hasError: true,
|
||||
};
|
||||
|
||||
export const Disabled = Template.bind({});
|
||||
Disabled.args = {
|
||||
id: 'select-disabled',
|
||||
disabled: true,
|
||||
};
|
||||
|
||||
export const RichDefaultSelect = RichSelectTemplate.bind({});
|
||||
RichDefaultSelect.args = {
|
||||
id: 'rich',
|
||||
name: 'rich',
|
||||
placeholder: 'Select an option',
|
||||
onValueChange: (v: string) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(v);
|
||||
},
|
||||
children: (
|
||||
<>
|
||||
<TradingOption value="1">
|
||||
<div className="flex flex-col justify-start items-start">
|
||||
<span>Option One</span>
|
||||
<span className="text-xs">First option</span>
|
||||
</div>
|
||||
</TradingOption>
|
||||
<TradingOption value="2">
|
||||
<div className="flex flex-col justify-start items-start">
|
||||
<span>Option Two</span>
|
||||
<span className="text-xs">Second option</span>
|
||||
</div>
|
||||
</TradingOption>
|
||||
<TradingOption value="3">
|
||||
<div className="flex flex-col justify-start items-start">
|
||||
<span>Option Three</span>
|
||||
<span className="text-xs">Third option</span>
|
||||
</div>
|
||||
</TradingOption>
|
||||
<TradingOption value="4">
|
||||
<div className="flex flex-col justify-start items-start">
|
||||
<span>Option Four</span>
|
||||
<span className="text-xs">Fourth option</span>
|
||||
</div>
|
||||
</TradingOption>
|
||||
<TradingOption value="5">
|
||||
<div className="flex flex-col justify-start items-start">
|
||||
<span>Option Five</span>
|
||||
<span className="text-xs">Fifth option</span>
|
||||
</div>
|
||||
</TradingOption>
|
||||
<TradingOption value="6">
|
||||
<div className="flex flex-col justify-start items-start">
|
||||
<span>Option Six</span>
|
||||
<span className="text-xs">Sixth option</span>
|
||||
</div>
|
||||
</TradingOption>
|
||||
</>
|
||||
),
|
||||
};
|
129
libs/ui-toolkit/src/components/trading-select/select.tsx
Normal file
129
libs/ui-toolkit/src/components/trading-select/select.tsx
Normal file
@ -0,0 +1,129 @@
|
||||
import type { Ref, SelectHTMLAttributes } from 'react';
|
||||
import { useRef } from 'react';
|
||||
import { forwardRef } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Icon } from '..';
|
||||
import { defaultSelectElement } from '../../utils/shared';
|
||||
import * as SelectPrimitive from '@radix-ui/react-select';
|
||||
|
||||
export interface TradingSelectProps
|
||||
extends SelectHTMLAttributes<HTMLSelectElement> {
|
||||
hasError?: boolean;
|
||||
className?: string;
|
||||
value?: string | number;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export const TradingSelect = forwardRef<HTMLSelectElement, TradingSelectProps>(
|
||||
({ className, hasError, ...props }, ref) => (
|
||||
<div className="flex items-center relative">
|
||||
<select
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={classNames(
|
||||
defaultSelectElement(hasError, props.disabled),
|
||||
className,
|
||||
'appearance-none rounded-md'
|
||||
)}
|
||||
/>
|
||||
<Icon
|
||||
name="chevron-down"
|
||||
className="absolute right-4 z-10 pointer-events-none"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
|
||||
export type TradingRichSelectProps = React.ComponentProps<
|
||||
typeof SelectPrimitive.Root
|
||||
> & {
|
||||
placeholder: string;
|
||||
hasError?: boolean;
|
||||
id?: string;
|
||||
'data-testid'?: string;
|
||||
};
|
||||
export const TradingRichSelect = forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
||||
TradingRichSelectProps
|
||||
>(({ id, children, placeholder, hasError, ...props }, forwardedRef) => {
|
||||
const containerRef = useRef<HTMLDivElement>();
|
||||
const contentRef = useRef<HTMLDivElement>();
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={containerRef as Ref<HTMLDivElement>}
|
||||
className="flex items-center relative"
|
||||
>
|
||||
<SelectPrimitive.Root {...props} defaultOpen={false}>
|
||||
<SelectPrimitive.Trigger
|
||||
data-testid={props['data-testid'] || 'rich-select-trigger'}
|
||||
className={classNames(
|
||||
defaultSelectElement(hasError, props.disabled),
|
||||
'rounded-md pl-2 pr-11',
|
||||
'max-w-full overflow-hidden break-all'
|
||||
)}
|
||||
id={id}
|
||||
ref={forwardedRef}
|
||||
>
|
||||
<SelectPrimitive.Value placeholder={placeholder} />
|
||||
<SelectPrimitive.Icon className={classNames('absolute right-4')}>
|
||||
<Icon name="chevron-down" />
|
||||
</SelectPrimitive.Icon>
|
||||
</SelectPrimitive.Trigger>
|
||||
<SelectPrimitive.Portal container={containerRef.current}>
|
||||
<SelectPrimitive.Content
|
||||
ref={contentRef as Ref<HTMLDivElement>}
|
||||
className={classNames(
|
||||
'relative',
|
||||
'z-20',
|
||||
'bg-white dark:bg-black',
|
||||
'border border-neutral-500 focus:border-black dark:focus:border-white rounded',
|
||||
'overflow-hidden',
|
||||
'shadow-lg'
|
||||
)}
|
||||
position={'item-aligned'}
|
||||
side={'bottom'}
|
||||
align={'center'}
|
||||
>
|
||||
<SelectPrimitive.ScrollUpButton className="flex items-center justify-center py-1 absolute w-full h-6 z-20 bg-gradient-to-t from-transparent to-neutral-50 dark:to-neutral-900">
|
||||
<Icon name="chevron-up" />
|
||||
</SelectPrimitive.ScrollUpButton>
|
||||
<SelectPrimitive.Viewport>{children}</SelectPrimitive.Viewport>
|
||||
<SelectPrimitive.ScrollDownButton className="flex items-center justify-center py-1 absolute bottom-0 w-full h-6 z-20 bg-gradient-to-b from-transparent to-neutral-50 dark:to-neutral-900">
|
||||
<Icon name="chevron-down" />
|
||||
</SelectPrimitive.ScrollDownButton>
|
||||
</SelectPrimitive.Content>
|
||||
</SelectPrimitive.Portal>
|
||||
</SelectPrimitive.Root>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export const TradingOption = forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Item>,
|
||||
React.ComponentProps<typeof SelectPrimitive.Item>
|
||||
>(({ children, className, ...props }, forwardedRef) => (
|
||||
<SelectPrimitive.Item
|
||||
data-testid="rich-select-option"
|
||||
className={classNames(
|
||||
'relative',
|
||||
'text-black dark:text-white',
|
||||
'cursor-pointer outline-none',
|
||||
'hover:bg-neutral-100 dark:hover:bg-neutral-800',
|
||||
'focus:bg-neutral-100 dark:focus:bg-neutral-800',
|
||||
'pl-2 py-2',
|
||||
'pr-12',
|
||||
'w-full',
|
||||
'text-sm',
|
||||
'data-selected:bg-vega-yellow dark:data-selected:text-black dark:data-selected:bg-vega-yellow',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
ref={forwardedRef}
|
||||
>
|
||||
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
||||
<SelectPrimitive.ItemIndicator className="absolute right-4 top-[50%] translate-y-[-50%]">
|
||||
<Icon name="tick" />
|
||||
</SelectPrimitive.ItemIndicator>
|
||||
</SelectPrimitive.Item>
|
||||
));
|
@ -1,18 +1,20 @@
|
||||
import classnames from 'classnames';
|
||||
|
||||
export const defaultSelectElement = (hasError?: boolean) =>
|
||||
classnames(defaultFormElement(hasError), 'pr-10 dark:bg-black');
|
||||
export const defaultSelectElement = (hasError?: boolean, disabled?: boolean) =>
|
||||
classnames(defaultFormElement(hasError, disabled), 'pr-10 min-h-8 py-1');
|
||||
|
||||
export const defaultFormElement = (hasError?: boolean) =>
|
||||
export const defaultFormElement = (hasError?: boolean, disabled?: boolean) =>
|
||||
classnames(
|
||||
'flex items-center w-full text-sm',
|
||||
'p-2 rounded whitespace-nowrap text-ellipsis overflow-hidden',
|
||||
'bg-transparent',
|
||||
'border',
|
||||
'focus:border-vega-light-300 dark:focus:border-vega-dark-300',
|
||||
'disabled:opacity-60',
|
||||
'focus:border-vega-clight-400 dark:focus:border-vega-cdark-400',
|
||||
{
|
||||
'border-vega-pink text-vega-pink': hasError,
|
||||
'border-vega-light-200 dark:border-vega-dark-200': !hasError,
|
||||
'bg-vega-clight-700 dark:bg-vega-cdark-700': !disabled && !hasError,
|
||||
'bg-transparent': disabled || hasError,
|
||||
'border-vega-clight-600 dark:border-vega-cdark-600': disabled,
|
||||
'border-vega-red-500': !disabled && hasError,
|
||||
'border-vega-clight-500 dark:border-vega-cdark-500':
|
||||
!disabled && !hasError,
|
||||
}
|
||||
);
|
||||
|
@ -2,11 +2,11 @@ import classNames from 'classnames';
|
||||
import { create } from 'zustand';
|
||||
import {
|
||||
Dialog,
|
||||
FormGroup,
|
||||
Input,
|
||||
Intent,
|
||||
Pill,
|
||||
TradingButton,
|
||||
TradingFormGroup,
|
||||
TradingInput,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
@ -421,17 +421,17 @@ const CustomUrlInput = ({
|
||||
<VegaIcon name={VegaIconNames.ARROW_LEFT} /> {t('Go back')}
|
||||
</button>
|
||||
</div>
|
||||
<FormGroup
|
||||
<TradingFormGroup
|
||||
labelFor="wallet-url"
|
||||
label={t('Custom wallet location')}
|
||||
hideLabel
|
||||
>
|
||||
<Input
|
||||
<TradingInput
|
||||
value={walletUrl}
|
||||
onChange={(e) => setWalletUrl(e.target.value)}
|
||||
name="wallet-url"
|
||||
/>
|
||||
</FormGroup>
|
||||
</TradingFormGroup>
|
||||
<ConnectionOption
|
||||
disabled={!isDesktopWalletRunning}
|
||||
type="jsonRpc"
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import {
|
||||
FormGroup,
|
||||
Input,
|
||||
InputError,
|
||||
TradingFormGroup,
|
||||
TradingInput,
|
||||
TradingInputError,
|
||||
Intent,
|
||||
TradingButton,
|
||||
VegaIcon,
|
||||
@ -60,8 +60,8 @@ export function ViewConnectorForm({
|
||||
'Browse from the perspective of another Vega user in read-only mode.'
|
||||
)}
|
||||
</p>
|
||||
<FormGroup label={t('Vega Pubkey')} labelFor="address">
|
||||
<Input
|
||||
<TradingFormGroup label={t('Vega Pubkey')} labelFor="address">
|
||||
<TradingInput
|
||||
{...register('address', {
|
||||
required: t('Required'),
|
||||
validate: validatePubkey,
|
||||
@ -71,9 +71,11 @@ export function ViewConnectorForm({
|
||||
type="text"
|
||||
/>
|
||||
{errors.address?.message && (
|
||||
<InputError intent="danger">{errors.address.message}</InputError>
|
||||
<TradingInputError intent="danger">
|
||||
{errors.address.message}
|
||||
</TradingInputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
</TradingFormGroup>
|
||||
<TradingButton
|
||||
data-testid="connect"
|
||||
intent={Intent.Info}
|
||||
|
@ -12,11 +12,11 @@ import { t } from '@vegaprotocol/i18n';
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
Button,
|
||||
FormGroup,
|
||||
Input,
|
||||
InputError,
|
||||
TradingFormGroup,
|
||||
TradingInput,
|
||||
TradingInputError,
|
||||
Notification,
|
||||
RichSelect,
|
||||
TradingRichSelect,
|
||||
ExternalLink,
|
||||
Intent,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
@ -150,7 +150,7 @@ export const WithdrawForm = ({
|
||||
field: ControllerRenderProps<FormFields, 'asset'>;
|
||||
}) => {
|
||||
return (
|
||||
<RichSelect
|
||||
<TradingRichSelect
|
||||
data-testid="select-asset"
|
||||
id="asset"
|
||||
name="asset"
|
||||
@ -170,7 +170,7 @@ export const WithdrawForm = ({
|
||||
balance={<AssetBalance asset={a} />}
|
||||
/>
|
||||
))}
|
||||
</RichSelect>
|
||||
</TradingRichSelect>
|
||||
);
|
||||
};
|
||||
|
||||
@ -193,7 +193,7 @@ export const WithdrawForm = ({
|
||||
noValidate={true}
|
||||
data-testid="withdraw-form"
|
||||
>
|
||||
<FormGroup label={t('Asset')} labelFor="asset">
|
||||
<TradingFormGroup label={t('Asset')} labelFor="asset">
|
||||
<Controller
|
||||
control={control}
|
||||
name="asset"
|
||||
@ -205,10 +205,12 @@ export const WithdrawForm = ({
|
||||
render={renderAssetsSelector}
|
||||
/>
|
||||
{errors.asset?.message && (
|
||||
<InputError intent="danger">{errors.asset.message}</InputError>
|
||||
<TradingInputError intent="danger">
|
||||
{errors.asset.message}
|
||||
</TradingInputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
</TradingFormGroup>
|
||||
<TradingFormGroup
|
||||
label={t('To (Ethereum address)')}
|
||||
labelFor="ethereum-address"
|
||||
>
|
||||
@ -218,15 +220,17 @@ export const WithdrawForm = ({
|
||||
clearErrors('to');
|
||||
}}
|
||||
/>
|
||||
<Input
|
||||
<TradingInput
|
||||
id="ethereum-address"
|
||||
data-testid="eth-address-input"
|
||||
{...register('to', { validate: { required, ethereumAddress } })}
|
||||
/>
|
||||
{errors.to?.message && (
|
||||
<InputError intent="danger">{errors.to.message}</InputError>
|
||||
<TradingInputError intent="danger">
|
||||
{errors.to.message}
|
||||
</TradingInputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
</TradingFormGroup>
|
||||
{selectedAsset && threshold && (
|
||||
<div className="mb-4">
|
||||
<WithdrawLimits
|
||||
@ -238,8 +242,8 @@ export const WithdrawForm = ({
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<FormGroup label={t('Amount')} labelFor="amount">
|
||||
<Input
|
||||
<TradingFormGroup label={t('Amount')} labelFor="amount">
|
||||
<TradingInput
|
||||
data-testid="amount-input"
|
||||
type="number"
|
||||
autoComplete="off"
|
||||
@ -259,7 +263,9 @@ export const WithdrawForm = ({
|
||||
})}
|
||||
/>
|
||||
{errors.amount?.message && (
|
||||
<InputError intent="danger">{errors.amount.message}</InputError>
|
||||
<TradingInputError intent="danger">
|
||||
{errors.amount.message}
|
||||
</TradingInputError>
|
||||
)}
|
||||
{selectedAsset && (
|
||||
<UseButton
|
||||
@ -282,7 +288,7 @@ export const WithdrawForm = ({
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</FormGroup>
|
||||
</TradingFormGroup>
|
||||
<Button
|
||||
data-testid="submit-withdrawal"
|
||||
type="submit"
|
||||
|
Loading…
Reference in New Issue
Block a user