feat(wallet): Edit Spend Limit Popup (#30690)
This commit is contained in:
@@ -955,6 +955,8 @@ inline constexpr webui::LocalizedString kLocalizedStrings[] = {
|
||||
IDS_BRAVE_WALLET_EDIT_PERMISSIONS_PROPOSED_ALLOWANCE},
|
||||
{"braveWalletEditPermissionsCustomAllowance",
|
||||
IDS_BRAVE_WALLET_EDIT_PERMISSIONS_CUSTOM_ALLOWANCE},
|
||||
{"braveWalletProposedSpendLimit", IDS_BRAVE_WALLET_PROPOSED_SPEND_LIMIT},
|
||||
{"braveWalletCustomSpendLimit", IDS_BRAVE_WALLET_CUSTOM_SPEND_LIMIT},
|
||||
{"braveWalletNotValidEthAddress", IDS_BRAVE_WALLET_NOT_VALID_ETH_ADDRESS},
|
||||
{"braveWalletNotValidSolAddress", IDS_BRAVE_WALLET_NOT_VALID_SOL_ADDRESS},
|
||||
{"braveWalletNotValidAddress", IDS_BRAVE_WALLET_NOT_VALID_ADDRESS},
|
||||
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
// Copyright (c) 2025 The Brave Authors. All rights reserved.
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
import * as React from 'react'
|
||||
|
||||
// Utils
|
||||
import { getLocale } from '../../../../common/locale'
|
||||
|
||||
// Components
|
||||
import {
|
||||
EditSpendLimit, //
|
||||
} from './edit_spend_limit'
|
||||
import { BottomSheet } from '../../shared/bottom_sheet/bottom_sheet'
|
||||
import {
|
||||
WalletPanelStory, //
|
||||
} from '../../../stories/wrappers/wallet-panel-story-wrapper'
|
||||
|
||||
export const _EditSpendLimit = {
|
||||
render: () => {
|
||||
return (
|
||||
<BottomSheet
|
||||
isOpen={true}
|
||||
title={getLocale('braveWalletEditPermissionsTitle')}
|
||||
onClose={() => alert('Close Clicked')}
|
||||
>
|
||||
<EditSpendLimit
|
||||
onCancel={() => alert('Cancel Clicked')}
|
||||
onSave={(limit: string) => alert(`Spend limit updated to ${limit}`)}
|
||||
approvalTarget='0x Exchange Proxy'
|
||||
isApprovalUnlimited={false}
|
||||
proposedAllowance='100'
|
||||
symbol='ETH'
|
||||
/>
|
||||
</BottomSheet>
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
export default {
|
||||
title: 'Wallet/Panel/Components/Edit Spend Limit',
|
||||
component: EditSpendLimit,
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
},
|
||||
decorators: [
|
||||
(Story: any) => (
|
||||
<WalletPanelStory>
|
||||
<Story />
|
||||
</WalletPanelStory>
|
||||
),
|
||||
],
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) 2025 The Brave Authors. All rights reserved.
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
import styled from 'styled-components'
|
||||
import * as leo from '@brave/leo/tokens/css/variables'
|
||||
|
||||
// Shared Styles
|
||||
import { Column, Text } from '../../shared/style'
|
||||
|
||||
export const StyledWrapper = styled(Column)`
|
||||
overflow: hidden;
|
||||
`
|
||||
|
||||
export const LabelText = styled(Text)`
|
||||
font: ${leo.font.default.regular};
|
||||
letter-spacing: ${leo.typography.letterSpacing.default};
|
||||
`
|
||||
|
||||
export const AmountText = styled(Text)`
|
||||
font: ${leo.font.large.semibold};
|
||||
letter-spacing: ${leo.typography.letterSpacing.default};
|
||||
`
|
||||
|
||||
export const Input = styled.input<{
|
||||
hasError?: boolean
|
||||
}>`
|
||||
font: ${leo.font.small.regular};
|
||||
letter-spacing: ${leo.typography.letterSpacing.small};
|
||||
background-color: ${leo.color.container.highlight};
|
||||
color: ${leo.color.text.primary};
|
||||
border: none;
|
||||
width: 100%;
|
||||
padding: 0px;
|
||||
outline: 1px solid ${leo.color.container.highlight};
|
||||
transition:
|
||||
outline 0.1s ease-in-out,
|
||||
box-shadow 0.1s ease-in-out;
|
||||
border-radius: ${leo.radius.m};
|
||||
padding: 10px 12px;
|
||||
::placeholder {
|
||||
font: ${leo.font.small.regular};
|
||||
letter-spacing: ${leo.typography.letterSpacing.small};
|
||||
color: ${leo.color.text.tertiary};
|
||||
}
|
||||
:hover {
|
||||
outline: 1px solid ${leo.color.divider.strong};
|
||||
box-shadow: ${leo.effect.elevation['02']};
|
||||
}
|
||||
:focus {
|
||||
outline: 2px solid ${leo.color.primary[40]};
|
||||
}
|
||||
::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
::-webkit-outer-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
`
|
||||
|
||||
export const ButtonText = styled.span`
|
||||
display: flex;
|
||||
width: 80px;
|
||||
justify-content: center;
|
||||
`
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
// Copyright (c) 2025 The Brave Authors. All rights reserved.
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
import * as React from 'react'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { Provider } from 'react-redux'
|
||||
|
||||
// Utils
|
||||
import {
|
||||
// eslint-disable-next-line import/no-named-default
|
||||
default as BraveCoreThemeProvider,
|
||||
} from '../../../../common/BraveCoreThemeProvider'
|
||||
import { createMockStore } from '../../../utils/test-utils'
|
||||
|
||||
// Components
|
||||
import { EditSpendLimit } from './edit_spend_limit'
|
||||
|
||||
describe('EditSpendLimit', () => {
|
||||
const renderComponent = () => {
|
||||
const store = createMockStore({})
|
||||
return render(
|
||||
<Provider store={store}>
|
||||
<BraveCoreThemeProvider>
|
||||
<EditSpendLimit
|
||||
onCancel={jest.fn()}
|
||||
onSave={jest.fn()}
|
||||
proposedAllowance='100'
|
||||
symbol='ETH'
|
||||
approvalTarget='Uniswap V3'
|
||||
isApprovalUnlimited={false}
|
||||
/>
|
||||
</BraveCoreThemeProvider>
|
||||
</Provider>,
|
||||
)
|
||||
}
|
||||
|
||||
it('should render the component', () => {
|
||||
renderComponent()
|
||||
|
||||
expect(
|
||||
screen.getByText('braveWalletEditPermissionsDescription'),
|
||||
).toBeInTheDocument()
|
||||
expect(
|
||||
screen.getByText('braveWalletProposedSpendLimit'),
|
||||
).toBeInTheDocument()
|
||||
expect(screen.getByText('100 ETH')).toBeInTheDocument()
|
||||
expect(screen.getByText('braveWalletCustomSpendLimit')).toBeInTheDocument()
|
||||
expect(screen.getByText('braveWalletButtonCancel')).toBeInTheDocument()
|
||||
expect(
|
||||
screen.getByText('braveWalletAccountSettingsSave'),
|
||||
).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
// Copyright (c) 2025 The Brave Authors. All rights reserved.
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
import * as React from 'react'
|
||||
import Button from '@brave/leo/react/button'
|
||||
import RadioButton from '@brave/leo/react/radioButton'
|
||||
|
||||
// Utils
|
||||
import { getLocale } from '../../../../common/locale'
|
||||
|
||||
// Styled Components
|
||||
import {
|
||||
StyledWrapper,
|
||||
LabelText,
|
||||
AmountText,
|
||||
Input,
|
||||
ButtonText,
|
||||
} from './edit_spend_limit.style'
|
||||
import { Column, Row } from '../../shared/style'
|
||||
|
||||
type AllowanceTypes = 'proposed' | 'custom'
|
||||
|
||||
export interface Props {
|
||||
onCancel: () => void
|
||||
onSave: (limit: string) => void
|
||||
proposedAllowance: string
|
||||
symbol: string
|
||||
approvalTarget: string
|
||||
isApprovalUnlimited: boolean
|
||||
}
|
||||
|
||||
export const EditSpendLimit = (props: Props) => {
|
||||
const {
|
||||
onCancel,
|
||||
onSave,
|
||||
approvalTarget,
|
||||
isApprovalUnlimited,
|
||||
proposedAllowance,
|
||||
symbol,
|
||||
} = props
|
||||
|
||||
// State
|
||||
const [allowanceType, setAllowanceType] =
|
||||
React.useState<AllowanceTypes>('proposed')
|
||||
const [customAllowance, setCustomAllowance] = React.useState<string>('')
|
||||
|
||||
// Refs
|
||||
const customAllowanceInputRef = React.useRef<HTMLInputElement>(null)
|
||||
|
||||
// Methods
|
||||
const toggleAllowanceRadio = (key: AllowanceTypes) => {
|
||||
if (key === 'custom') {
|
||||
customAllowanceInputRef.current?.focus()
|
||||
}
|
||||
setAllowanceType(key)
|
||||
}
|
||||
|
||||
const onChangeCustomAllowance = (
|
||||
event: React.ChangeEvent<HTMLInputElement>,
|
||||
) => {
|
||||
setCustomAllowance(event.target.value)
|
||||
}
|
||||
|
||||
const onClickSave = React.useCallback(() => {
|
||||
onSave(allowanceType === 'custom' ? customAllowance : proposedAllowance)
|
||||
onCancel()
|
||||
}, [allowanceType, customAllowance, proposedAllowance, onSave, onCancel])
|
||||
|
||||
// Memos
|
||||
const isSaveButtonDisabled = React.useMemo(() => {
|
||||
return allowanceType === 'custom' && customAllowance === ''
|
||||
}, [allowanceType, customAllowance])
|
||||
|
||||
const formattedProposedAllowance = React.useMemo(() => {
|
||||
return isApprovalUnlimited
|
||||
? getLocale('braveWalletTransactionApproveUnlimited')
|
||||
: proposedAllowance
|
||||
}, [proposedAllowance, isApprovalUnlimited])
|
||||
|
||||
return (
|
||||
<StyledWrapper
|
||||
gap='32px'
|
||||
padding='16px'
|
||||
width='100%'
|
||||
>
|
||||
<LabelText
|
||||
textColor='secondary'
|
||||
textAlign='left'
|
||||
>
|
||||
{getLocale('braveWalletEditPermissionsDescription').replace(
|
||||
'$1',
|
||||
approvalTarget,
|
||||
)}
|
||||
</LabelText>
|
||||
<Column
|
||||
gap='16px'
|
||||
width='100%'
|
||||
alignItems='flex-start'
|
||||
justifyContent='flex-start'
|
||||
>
|
||||
<RadioButton
|
||||
name='proposed'
|
||||
value='proposed'
|
||||
currentValue={allowanceType}
|
||||
onChange={() => toggleAllowanceRadio('proposed')}
|
||||
>
|
||||
<Column
|
||||
alignItems='flex-start'
|
||||
justifyContent='flex-start'
|
||||
>
|
||||
<LabelText textColor='primary'>
|
||||
{getLocale('braveWalletProposedSpendLimit')}
|
||||
</LabelText>
|
||||
<AmountText textColor='primary'>
|
||||
{formattedProposedAllowance} {symbol}
|
||||
</AmountText>
|
||||
</Column>
|
||||
</RadioButton>
|
||||
<RadioButton
|
||||
name='custom'
|
||||
value='custom'
|
||||
currentValue={allowanceType}
|
||||
onChange={() => toggleAllowanceRadio('custom')}
|
||||
>
|
||||
<Column
|
||||
alignItems='flex-start'
|
||||
justifyContent='flex-start'
|
||||
gap='4px'
|
||||
>
|
||||
<LabelText textColor='primary'>
|
||||
{getLocale('braveWalletCustomSpendLimit')}
|
||||
</LabelText>
|
||||
<Input
|
||||
ref={customAllowanceInputRef}
|
||||
placeholder={proposedAllowance}
|
||||
type='number'
|
||||
value={customAllowance}
|
||||
onChange={onChangeCustomAllowance}
|
||||
data-testid='custom-allowance-input'
|
||||
/>
|
||||
</Column>
|
||||
</RadioButton>
|
||||
</Column>
|
||||
<Row gap='16px'>
|
||||
<Button
|
||||
kind='outline'
|
||||
size='medium'
|
||||
onClick={onCancel}
|
||||
>
|
||||
<ButtonText>{getLocale('braveWalletButtonCancel')}</ButtonText>
|
||||
</Button>
|
||||
<Button
|
||||
size='medium'
|
||||
onClick={onClickSave}
|
||||
isDisabled={isSaveButtonDisabled}
|
||||
>
|
||||
<ButtonText>{getLocale('braveWalletAccountSettingsSave')}</ButtonText>
|
||||
</Button>
|
||||
</Row>
|
||||
</StyledWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
export default EditSpendLimit
|
||||
@@ -1080,6 +1080,8 @@ provideStrings({
|
||||
braveWalletEditPermissionsButton: 'Edit permissions',
|
||||
braveWalletEditPermissionsProposedAllowance: 'Proposed allowance',
|
||||
braveWalletEditPermissionsCustomAllowance: 'Custom allowance',
|
||||
braveWalletProposedSpendLimit: 'Proposed spend limit',
|
||||
braveWalletCustomSpendLimit: 'Custom spend limit',
|
||||
|
||||
// Send Input Errors
|
||||
braveWalletNotValidFilAddress: 'Not a valid FIL address',
|
||||
|
||||
@@ -514,6 +514,8 @@
|
||||
<message name="IDS_BRAVE_WALLET_EDIT_PERMISSIONS_BUTTON" desc="Edit allowance panel button">Edit permissions</message>
|
||||
<message name="IDS_BRAVE_WALLET_EDIT_PERMISSIONS_PROPOSED_ALLOWANCE" desc="Edit allowance panel button">Proposed allowance</message>
|
||||
<message name="IDS_BRAVE_WALLET_EDIT_PERMISSIONS_CUSTOM_ALLOWANCE" desc="Edit allowance panel button">Custom allowance</message>
|
||||
<message name="IDS_BRAVE_WALLET_PROPOSED_SPEND_LIMIT" desc="Edit allowance proposed spend limit label">Proposed spend limit</message>
|
||||
<message name="IDS_BRAVE_WALLET_CUSTOM_SPEND_LIMIT" desc="Edit allowance custom spend limit label">Custom spend limit</message>
|
||||
<message name="IDS_BRAVE_WALLET_NOT_VALID_ETH_ADDRESS" desc="Send input not valid ETH address error">Not a valid ETH address</message>
|
||||
<message name="IDS_BRAVE_WALLET_NOT_VALID_SOL_ADDRESS" desc="Send input not valid SOL address error">Not a valid SOL address</message>
|
||||
<message name="IDS_BRAVE_WALLET_NOT_VALID_ADDRESS" desc="Send input not valid address error">Not a valid <ph name="COIN_TYPE_SYMBOL"><ex>ETH</ex>$1</ph> address.</message>
|
||||
|
||||
Reference in New Issue
Block a user