Skip to content

Commit 16b8481

Browse files
swetshawDhaiwat10
andauthored
AddEtherInput component (#90)
* component ConvertTokenInput added * component `ConvertTokenInput` now accepts a prop `value` * using web3js to convert Ether <-> Wei * component uses ethers.utils instead of web3.utils for ether <-> wei conversion * added InpuGroupProps to InputGroup * renamed component name to EtherInput * Removed convert functionality from the EtherInput component * Deleted ConvertTokenInput Component * Modify EtherInput Co-authored-by: Dhaiwat Pandya <[email protected]>
1 parent f481a36 commit 16b8481

File tree

7 files changed

+174
-0
lines changed

7 files changed

+174
-0
lines changed

.changeset/sixty-boats-sip.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@web3-ui/components': minor
3+
---
4+
5+
Added a EtherInput component that supports input in wei or ether and automatically converts it to wei for you!

packages/components/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ This is the list of components the package currently provides:
3636
- [NFTGallery](#nftgallery)
3737
- [Provider](#provider)
3838
- [TokenGate](#tokengate)
39+
- [EtherInput](#etherinput)
3940

4041
---
4142

@@ -109,3 +110,15 @@ The TokenGate component lets you conditionally render some content depending on
109110
<p>This will be rendered if walletBalance is greater than or equal to 10.</p>
110111
</TokenGate>
111112
```
113+
114+
---
115+
116+
### EtherInput
117+
118+
EtherInput is an input field that accepts values in _wei_ or _ether_. It always returns the value to you in _wei_ so that you can easily use it to interact with contracts without any conversions.
119+
120+
```tsx
121+
const [value, setValue] = useState();
122+
123+
<EtherInput value={value} setValue={setValue} unit="ether" />; // value will always be in wei. eg. if someone enters 1 in the input, value will be 10^18
124+
```
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { Text } from '@chakra-ui/react';
2+
import React from 'react';
3+
import { EtherInput } from '.';
4+
5+
export default {
6+
title: 'Components/EtherInput',
7+
component: EtherInput
8+
};
9+
10+
const Component = ({ ...props }) => {
11+
const [value, setValue] = React.useState('');
12+
13+
return (
14+
<>
15+
<EtherInput
16+
value={value}
17+
onChange={val => setValue(val)}
18+
{...props}
19+
width="300px"
20+
/>
21+
<Text>Value: {value} wei</Text>
22+
</>
23+
);
24+
};
25+
26+
export const Ether = () => <Component unit="ether" />;
27+
export const Wei = () => <Component unit="wei" />;
28+
export const Label = () => <Component label="Enter value in wei" />;
29+
export const Error = () => <Component error="This is an error" />;
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import React from 'react';
2+
import { render, screen, fireEvent } from '@testing-library/react';
3+
import { EtherInput } from '.';
4+
import { ethers } from 'ethers';
5+
6+
const WrappedEtherInput = props => {
7+
const [value, setValue] = React.useState();
8+
9+
return (
10+
<>
11+
<EtherInput value={value} onChange={val => setValue(val)} {...props} />
12+
<p data-testid="output">{value}</p>
13+
</>
14+
);
15+
};
16+
17+
describe('EtherInput component', () => {
18+
it('should render without throwing', async () => {
19+
render(<WrappedEtherInput />);
20+
const input = await screen.findByPlaceholderText('Enter value in ether');
21+
expect(input).toBeInTheDocument();
22+
});
23+
24+
it('should handle ether inputs properly', async () => {
25+
render(<WrappedEtherInput unit="ether" />);
26+
const input = await screen.findByPlaceholderText('Enter value in ether');
27+
const output = screen.getByTestId('output');
28+
fireEvent.change(input, { target: { value: '1' } });
29+
expect(output.textContent).toBe(ethers.utils.parseEther('1').toString());
30+
});
31+
32+
it('should handle wei inputs properly', async () => {
33+
render(<WrappedEtherInput unit="wei" />);
34+
const input = await screen.findByPlaceholderText('Enter value in wei');
35+
const output = screen.getByTestId('output');
36+
fireEvent.change(input, { target: { value: '1' } });
37+
expect(output.textContent).toBe('1');
38+
});
39+
});
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import {
2+
Input,
3+
InputGroup,
4+
InputRightAddon,
5+
FormErrorMessage,
6+
InputGroupProps,
7+
FormLabel,
8+
FormControl
9+
} from '@chakra-ui/react';
10+
import React from 'react';
11+
import { ethers } from 'ethers';
12+
13+
export interface EtherInputProps {
14+
/**
15+
* Specifies the value of the EtherInput
16+
*/
17+
value: string;
18+
19+
/**
20+
* Input unit of the value entered in EtherInput
21+
*/
22+
unit?: 'wei' | 'ether';
23+
24+
/**
25+
* The label for EtherInput
26+
*/
27+
label?: string;
28+
29+
/**
30+
* Accepts a function that would be triggered when the value inside EtherInput changes.
31+
*/
32+
onChange: (value: string) => void;
33+
34+
/**
35+
* Whether you want to display the unit on the right side of the input field or not.
36+
* @default true
37+
*/
38+
showUnit?: boolean;
39+
40+
placeholder?: string;
41+
42+
/**
43+
* In case of an error, this prop would be used to display the error message. The component does not validate the input for you.
44+
*/
45+
error?: string;
46+
}
47+
/**
48+
* An input field that accepts ether values. Always returns the value in wei so that you can directly use it in transactions without any conversion.
49+
*/
50+
export const EtherInput: React.FC<EtherInputProps & InputGroupProps> = ({
51+
value,
52+
unit = 'ether',
53+
label,
54+
onChange,
55+
showUnit = true,
56+
placeholder,
57+
error,
58+
...props
59+
}) => {
60+
const [plainValue, setPlainValue] = React.useState<string>();
61+
const _placeholder = placeholder || `Enter value in ${unit}`;
62+
63+
return (
64+
<FormControl isInvalid={!!error}>
65+
{label ? <FormLabel mb="8px">{label}: </FormLabel> : ''}
66+
<InputGroup {...props}>
67+
<Input
68+
placeholder={_placeholder}
69+
value={plainValue}
70+
onChange={e => {
71+
setPlainValue(e.target.value);
72+
if (unit === 'ether') {
73+
onChange(ethers.utils.parseEther(e.target.value).toString());
74+
} else {
75+
onChange(e.target.value);
76+
}
77+
}}
78+
/>
79+
{showUnit && (
80+
<InputRightAddon>{unit === 'ether' ? 'Ξ' : 'wei'}</InputRightAddon>
81+
)}
82+
</InputGroup>
83+
{error && <FormErrorMessage>{error}</FormErrorMessage>}
84+
</FormControl>
85+
);
86+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './EtherInput';

packages/components/src/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ export * from './NFTGallery';
66
export * from './NFT';
77
export * from './AddressInput';
88
export * from './TokenGate';
9+
export * from './EtherInput';

0 commit comments

Comments
 (0)