diff --git a/apps/remix-ide-e2e/src/commands/addAtAddressInstance.ts b/apps/remix-ide-e2e/src/commands/addAtAddressInstance.ts index 86624a900d..48270c6b9b 100644 --- a/apps/remix-ide-e2e/src/commands/addAtAddressInstance.ts +++ b/apps/remix-ide-e2e/src/commands/addAtAddressInstance.ts @@ -21,15 +21,22 @@ function addInstance (browser: NightwatchBrowser, address: string, isValidFormat .setValue('.ataddressinput', address, function () { if (!isValidFormat || !isValidChecksum) browser.assert.elementPresent('button[id^="runAndDeployAtAdressButton"]:disabled') else if (isAbi) { - browser.click('button[id^="runAndDeployAtAdressButton"]') - .waitForElementPresent('[data-id="udappNotify-modal-footer-ok-react"]') + browser + .click({ + selector: '//*[@id="runAndDeployAtAdressButtonContainer"]', + locateStrategy: 'xpath' + }) + .waitForElementPresent('[data-id="udappNotify-modal-footer-ok-react"]', 5000) .execute(function () { const modal = document.querySelector('[data-id="udappNotify-modal-footer-ok-react"]') as any modal.click() }) } else { - browser.click('button[id^="runAndDeployAtAdressButton"]') + browser.click({ + selector: '//*[@id="runAndDeployAtAdressButtonContainer"]', + locateStrategy: 'xpath' + }) } callback() }) diff --git a/apps/remix-ide-e2e/src/commands/clickFunction.ts b/apps/remix-ide-e2e/src/commands/clickFunction.ts index e84be6a0d0..9ae4b3c330 100644 --- a/apps/remix-ide-e2e/src/commands/clickFunction.ts +++ b/apps/remix-ide-e2e/src/commands/clickFunction.ts @@ -3,18 +3,18 @@ import EventEmitter from 'events' class ClickFunction extends EventEmitter { command (this: NightwatchBrowser, fnFullName: string, expectedInput?: NightwatchClickFunctionExpectedInput): NightwatchBrowser { - this.api.waitForElementPresent('.instance button[title="' + fnFullName + '"]') + this.api.waitForElementPresent('.instance button[data-title="' + fnFullName + '"]') .perform(function (client, done) { client.execute(function () { document.querySelector('#runTabView').scrollTop = document.querySelector('#runTabView').scrollHeight }, [], function () { if (expectedInput) { - client.setValue('#runTabView input[title="' + expectedInput.types + '"]', expectedInput.values, _ => _) + client.setValue('#runTabView input[data-title="' + expectedInput.types + '"]', expectedInput.values, _ => _) } done() }) }) - .scrollAndClick('.instance button[title="' + fnFullName + '"]') + .scrollAndClick('.instance button[data-title="' + fnFullName + '"]') .pause(2000) .perform(() => { this.emit('complete') diff --git a/apps/remix-ide-e2e/src/commands/testConstantFunction.ts b/apps/remix-ide-e2e/src/commands/testConstantFunction.ts index 00f9512814..3216bc39ed 100644 --- a/apps/remix-ide-e2e/src/commands/testConstantFunction.ts +++ b/apps/remix-ide-e2e/src/commands/testConstantFunction.ts @@ -15,18 +15,18 @@ class TestConstantFunction extends EventEmitter { } function testConstantFunction (browser: NightwatchBrowser, address: string, fnFullName: string, expectedInput: NightwatchTestConstantFunctionExpectedInput, expectedOutput: string, cb: VoidFunction) { - browser.waitForElementPresent('.instance button[title="' + fnFullName + '"]').perform(function (client, done) { + browser.waitForElementPresent('.instance button[data-title="' + fnFullName + '"]').perform(function (client, done) { client.execute(function () { document.querySelector('#runTabView').scrollTop = document.querySelector('#runTabView').scrollHeight }, [], function () { if (expectedInput) { - client.waitForElementPresent('#runTabView input[title="' + expectedInput.types + '"]') - .setValue('#runTabView input[title="' + expectedInput.types + '"]', expectedInput.values) + client.waitForElementPresent('#runTabView input[data-title="' + expectedInput.types + '"]') + .setValue('#runTabView input[data-title="' + expectedInput.types + '"]', expectedInput.values) } done() }) }) - .click(`#instance${address} button[title="${fnFullName}"]`) + .click(`#instance${address} button[data-title="${fnFullName}"]`) .pause(1000) .waitForElementPresent('#instance' + address + ' .udapp_contractActionsContainer .udapp_value') .scrollInto('#instance' + address + ' .udapp_contractActionsContainer .udapp_value') diff --git a/apps/remix-ide-e2e/src/tests/debugger.test.ts b/apps/remix-ide-e2e/src/tests/debugger.test.ts index b9602b4b57..56965e31a9 100644 --- a/apps/remix-ide-e2e/src/tests/debugger.test.ts +++ b/apps/remix-ide-e2e/src/tests/debugger.test.ts @@ -19,8 +19,8 @@ module.exports = { .clickLaunchIcon('solidity').click('*[data-id="compilerContainerCompileBtn"]') .pause(4000) .clickLaunchIcon('udapp') - .waitForElementPresent('*[title="Deploy - transact (not payable)"]', 60000) - .click('*[title="Deploy - transact (not payable)"]') + .waitForElementPresent('*[data-title="Deploy - transact (not payable)"]', 60000) + .click('*[data-title="Deploy - transact (not payable)"]') .debugTransaction(0) .waitForElementContainsText('*[data-id="sidePanelSwapitTitle"]', 'DEBUGGER', 60000) .clearConsole() @@ -30,8 +30,8 @@ module.exports = { browser.waitForElementVisible('*[data-id="verticalIconsKindudapp"]') .clickLaunchIcon('udapp') .clickInstance(0) - .scrollAndClick('*[title="string name, uint256 goal"]') - .setValue('*[title="string name, uint256 goal"]', '"toast", 999') + .scrollAndClick('*[data-title="string name, uint256 goal"]') + .setValue('*[data-title="string name, uint256 goal"]', '"toast", 999') .click('*[data-id="createProject - transact (not payable)"]') .debugTransaction(0) .pause(2000) @@ -88,7 +88,7 @@ module.exports = { .clickLaunchIcon('solidity') .testContracts('externalImport.sol', sources[1]['externalImport.sol'], ['ERC20']) .clickLaunchIcon('udapp') - .waitForElementPresent('*[title="Deploy - transact (not payable)"]', 35000) + .waitForElementPresent('*[data-title="Deploy - transact (not payable)"]', 35000) .selectContract('ERC20') .createContract('"tokenName", "symbol"') .debugTransaction(0) @@ -159,7 +159,7 @@ module.exports = { .clickLaunchIcon('solidity') .testContracts('locals.sol', sources[3]['locals.sol'], ['testLocals']) .clickLaunchIcon('udapp') - .waitForElementPresent('*[title="Deploy - transact (not payable)"]', 40000) + .waitForElementPresent('*[data-title="Deploy - transact (not payable)"]', 40000) .createContract('') .pause(2000) .clearConsole() @@ -266,7 +266,7 @@ const sources = [ 'blah.sol': { content: ` pragma solidity >=0.7.0 <0.9.0; - + contract Kickstarter { enum State { Started, Completed } @@ -276,9 +276,9 @@ const sources = [ string name; uint goal; State state; - } + } - Project[] public projects; + Project[] public projects; constructor() { @@ -291,7 +291,7 @@ const sources = [ project.state = State.Started; project.goal = goal; } - } + } ` } }, @@ -309,12 +309,12 @@ const sources = [ function test1 (bytes calldata userData) external returns (bytes memory, bytes32, bytes32, uint) { bytes32 idAsk = abi.decode(userData[:33], (bytes32)); bytes32 idOffer = abi.decode(userData[32:64], (bytes32)); - + bytes memory ro = abi.encodePacked(msg.sender, msg.sender, idAsk, idOffer); return (ro, idAsk, idOffer, userData.length); } - - + + function testgp (bytes calldata userData) external returns (bytes4) { return abi.decode(userData[:4], (bytes4)); } @@ -341,9 +341,9 @@ const sources = [ 'withGeneratedSources.sol': { content: ` // SPDX-License-Identifier: GPL-3.0 - pragma experimental ABIEncoderV2; - contract A { - function f(uint[] memory) public returns (uint256) { } + pragma experimental ABIEncoderV2; + contract A { + function f(uint[] memory) public returns (uint256) { } } ` } @@ -372,7 +372,7 @@ const sources = [ } /** - * @dev Return value + * @dev Return value * @return value of 'number' */ function retrieve() public view returns (uint256){ @@ -393,14 +393,14 @@ const sources = [ function callA() public { p = 123; try b.callB() { - + } catch (bytes memory reason) { - + } } } - + contract B { C c; uint p; @@ -413,7 +413,7 @@ const sources = [ c.callC(); } } - + contract C { uint p; function callC() public { @@ -498,7 +498,7 @@ const jsGetTrace = `(async () => { } })()` -const jsDebug = `(async () => { +const jsDebug = `(async () => { try { const result = await remix.call('debugger', 'debug', '0x65f0813753462414f9a91f0aabea946188327995f54b893b63a8d7ff186cfca3') console.log('result ', result) diff --git a/libs/remix-ui/run-tab/src/lib/components/account.tsx b/libs/remix-ui/run-tab/src/lib/components/account.tsx index 1429d8d33a..0dd64facc9 100644 --- a/libs/remix-ui/run-tab/src/lib/components/account.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/account.tsx @@ -3,6 +3,7 @@ import React, { useEffect, useState, useRef } from 'react' import { CopyToClipboard } from '@remix-ui/clipboard' import { AccountProps } from '../types' import { PassphrasePrompt } from './passphrase' +import { OverlayTrigger, Tooltip } from 'react-bootstrap' export function AccountUI (props: AccountProps) { const { selectedAccount, loadedAccounts } = props.accounts @@ -150,9 +151,15 @@ export function AccountUI (props: AccountProps) {
- + + {"Sign a message using this account"} + + }> + +
) diff --git a/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx b/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx index b9eb9fd7c7..0d5ad43701 100644 --- a/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx @@ -2,10 +2,10 @@ import React, { useEffect, useRef, useState } from 'react' import { ContractDropdownProps, DeployMode } from '../types' import { ContractData, FuncABI } from '@remix-project/core-plugin' +import { OverlayTrigger, Tooltip } from 'react-bootstrap' // eslint-disable-line import * as ethJSUtil from 'ethereumjs-util' import { ContractGUI } from './contractGUI' import { deployWithProxyMsg, upgradeWithProxyMsg } from '@remix-ui/helper' -import { OverlayTrigger, Tooltip } from 'react-bootstrap' const _paq = window._paq = window._paq || [] export function ContractDropdownUI(props: ContractDropdownProps) { @@ -115,7 +115,6 @@ export function ContractDropdownUI(props: ContractDropdownProps) { const initSelectedContract = () => { const contracts = contractList[currentFile] - if (contracts && contracts.length > 0) { const contract = contracts.find(contract => contract.alias === currentContract) @@ -276,13 +275,25 @@ export function ContractDropdownUI(props: ContractDropdownProps) { : null}
- {(contractList[currentFile] || []).map((contract, index) => { return })} + {abiLabel.content}
@@ -311,28 +322,47 @@ export function ContractDropdownUI(props: ContractDropdownProps) { onChange={handleCheckedIPFS} checked={props.ipfsCheckedState} /> - + + Publishing the source code and metadata to IPFS facilitates source code verification
using Sourcify and will greatly foster contract adoption (auditing, debugging, calling it, etc...)
+ + }> + +
: '' }
or
-
- - +
+ + {atAddressOptions.title} + + }> +
+ +
+
+ + {"address of contract"} + + }> + +
diff --git a/libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx b/libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx index ad5716f45c..cccbbbbb2e 100644 --- a/libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx @@ -3,6 +3,7 @@ import React, { useEffect, useRef, useState } from 'react' import * as remixLib from '@remix-project/remix-lib' import { ContractGUIProps } from '../types' import { CopyToClipboard } from '@remix-ui/clipboard' +import { OverlayTrigger, Tooltip } from 'react-bootstrap' const txFormat = remixLib.execution.txFormat const txHelper = remixLib.execution.txHelper @@ -233,66 +234,187 @@ export function ContractGUI (props: ContractGUIProps) { } return ( -
0) || (props.funcABI.type === 'fallback') || (props.funcABI.type === 'receive') ? 'udapp_hasArgs' : ''}`}> -
- - 0) || (props.funcABI.type === 'fallback') || (props.funcABI.type === 'receive')) ? 'hidden' : 'visible' }} /> +
0) || + props.funcABI.type === "fallback" || + props.funcABI.type === "receive" + ? "udapp_hasArgs" + : "" + }`} + > +
+ + {buttonOptions.title} + + } + > + + + + + {props.funcABI.type === "fallback" || + props.funcABI.type === "receive" + ? `'(${props.funcABI.type}')` + : props.inputs} + + + } + > + 0) || + props.funcABI.type === "fallback" || + props.funcABI.type === "receive" + ) + ? "hidden" + : "visible", + }} + /> + 0) ? 'hidden' : 'visible' }}> + style={{ + visibility: !( + props.funcABI.inputs && props.funcABI.inputs.length > 0 + ) + ? "hidden" + : "visible", + }} + >
-
+
-
{title}
- +
+ {title} +
+
{props.funcABI.inputs.map((inp, index) => { return (
- { multiFields.current[index] = el }} className="form-control" placeholder={inp.type} title={inp.name} data-id={`multiParamManagerInput${inp.name}`} /> -
) + + {inp.name} + + } + > + { + multiFields.current[index] = el; + }} + className="form-control" + placeholder={inp.type} + data-id={`multiParamManagerInput${inp.name}`} + /> + +
+ ); })}
- + - + - + +
- { props.deployOption && (props.deployOption || []).length > 0 ? + {props.deployOption && (props.deployOption || []).length > 0 ? ( <> -
+
- { - props.initializerOptions && props.initializerOptions.initializeInputs ? + {props.initializerOptions && + props.initializerOptions.initializeInputs ? ( - - : null - } + + + ) : null}
- { - props.initializerOptions && props.initializerOptions.initializeInputs ? -
-
{ - props.initializerOptions.inputs.inputs.map((inp, index) => { - return ( -
- - { initializeFields.current[index] = el }} style={{ height: 32 }} className="form-control udapp_input" placeholder={inp.type} title={inp.name} /> -
- )}) - } -
-
: null - } -
+ {props.initializerOptions && + props.initializerOptions.initializeInputs ? ( +
+
+ {props.initializerOptions.inputs.inputs.map((inp, index) => { + return ( +
+ + { + initializeFields.current[index] = el; + }} + style={{ height: 32 }} + className="form-control udapp_input" + placeholder={inp.type} + title={inp.name} + /> +
+ ); + })} +
+
+ ) : null} +
- +
-
+
- { - !useLastProxy ? + {!useLastProxy ? (
- - -
: - { proxyAddress || 'No proxy address available' } - } + + +
+ ) : ( + + {proxyAddress || "No proxy address available"} + + )}
- : null - } + + ) : null}
- ) + ); } diff --git a/libs/remix-ui/run-tab/src/lib/components/deployButton.tsx b/libs/remix-ui/run-tab/src/lib/components/deployButton.tsx index ced72344d5..08fa2843c7 100644 --- a/libs/remix-ui/run-tab/src/lib/components/deployButton.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/deployButton.tsx @@ -1,6 +1,6 @@ import React, { useState } from 'react' import { DeployButtonProps } from '../types' -import { ButtonGroup, Dropdown } from 'react-bootstrap' +import { ButtonGroup, Dropdown, OverlayTrigger, Tooltip } from 'react-bootstrap' export function DeployButton (props: DeployButtonProps) { const [showOptions, setShowOptions] = useState(false) @@ -24,9 +24,18 @@ export function DeployButton (props: DeployButtonProps) { } : - + + {props.buttonOptions.title} + + } + > + + } ) diff --git a/libs/remix-ui/run-tab/src/lib/components/deployInput.tsx b/libs/remix-ui/run-tab/src/lib/components/deployInput.tsx index 26fcf2a164..c5dc153e42 100644 --- a/libs/remix-ui/run-tab/src/lib/components/deployInput.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/deployInput.tsx @@ -1,4 +1,5 @@ import React from 'react' +import { OverlayTrigger, Tooltip } from 'react-bootstrap' import { DeployInputProps } from '../types' import { DeployButton } from './deployButton' @@ -6,15 +7,24 @@ export function DeployInput (props: DeployInputProps) { return (
- + + {props.funcABI.type === 'fallback' || props.funcABI.type === 'receive' ? `'(${props.funcABI.type}')` : props.inputs} + + } + > + +
) } diff --git a/libs/remix-ui/run-tab/src/lib/components/environment.tsx b/libs/remix-ui/run-tab/src/lib/components/environment.tsx index 3e8b020613..b4a875b1b5 100644 --- a/libs/remix-ui/run-tab/src/lib/components/environment.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/environment.tsx @@ -6,7 +6,7 @@ import { CustomMenu, CustomToggle } from '@remix-ui/helper' import { OverlayTrigger, Tooltip } from 'react-bootstrap' // eslint-disable-line export function EnvironmentUI (props: EnvironmentProps) { - + const handleChangeExEnv = (env: string) => { const provider = props.providers.providerList.find(exEnv => exEnv.value === env) @@ -49,7 +49,7 @@ export function EnvironmentUI (props: EnvironmentProps) { }> - } + } { @@ -67,7 +67,13 @@ export function EnvironmentUI (props: EnvironmentProps) { } - + + {"Click for docs about Environment"} + + }> + +
) diff --git a/libs/remix-ui/run-tab/src/lib/components/gasPrice.tsx b/libs/remix-ui/run-tab/src/lib/components/gasPrice.tsx index a7c5335abd..1dcd3fb57f 100644 --- a/libs/remix-ui/run-tab/src/lib/components/gasPrice.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/gasPrice.tsx @@ -1,5 +1,6 @@ // eslint-disable-next-line no-use-before-define import React from 'react' +import { OverlayTrigger, Tooltip } from 'react-bootstrap' import { GasPriceProps } from '../types' export function GasPriceUI (props: GasPriceProps) { @@ -10,7 +11,13 @@ export function GasPriceUI (props: GasPriceProps) { return (
- + + {"The default gas limit is 3M. Adjust as needed."} + + }> + +
) } diff --git a/libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx b/libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx index c6a838bf12..94cb03deba 100644 --- a/libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx @@ -1,5 +1,6 @@ // eslint-disable-next-line no-use-before-define import React from 'react' +import { OverlayTrigger, Tooltip } from 'react-bootstrap' import { InstanceContainerProps } from '../types' import { UniversalDappUI } from './universalDappUI' @@ -12,15 +13,35 @@ export function InstanceContainerUI (props: InstanceContainerProps) { return (
-
diff --git a/libs/remix-ui/run-tab/src/lib/components/recorderCardUI.tsx b/libs/remix-ui/run-tab/src/lib/components/recorderCardUI.tsx index 6108a7f1b8..c413e2ec90 100644 --- a/libs/remix-ui/run-tab/src/lib/components/recorderCardUI.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/recorderCardUI.tsx @@ -31,7 +31,13 @@ export function RecorderUI (props: RecorderProps) {
-
{props.count}
+ + {'The number of recorded transactions'} + + }> +
{props.count}
+
Save transactions (deployed contracts and function executions) and replay them in another environment.
e.g Transactions created in Remix VM can be replayed in the Injected Provider. @@ -59,19 +65,33 @@ export function RecorderUI (props: RecorderProps) {
- - Save {props.count} transaction{props.count === 1 ? '' : 's'} as scenario file + + + { + props.count === 0 ? 'No transactions to save' + : props.count === 1 ? `Save ${props.count} transaction as scenario file` + : `Save ${props.count} transactions as scenario file` + } + }> - + + + Run transaction(s) from the current scenario file }> - + + +
diff --git a/libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx b/libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx index 4c2deaebcb..b9bdfa82ed 100644 --- a/libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx @@ -7,6 +7,7 @@ import * as remixLib from '@remix-project/remix-lib' import * as ethJSUtil from 'ethereumjs-util' import { ContractGUI } from './contractGUI' import { TreeView, TreeViewItem } from '@remix-ui/tree-view' +import { OverlayTrigger, Tooltip } from 'react-bootstrap' // eslint-disable-line import { BN } from 'ethereumjs-util' import { is0XPrefixed, isHexadecimal, isNumeric, shortenAddress } from '@remix-ui/helper' @@ -210,104 +211,190 @@ export function UniversalDappUI (props: UdappProps) { } return ( -
+
- - + +
- {props.instance.name} at {shortenAddress(address)} ({props.context}) + {props.instance.name} at {shortenAddress(address)} ( + {props.context})
- +
- + +
-
+
-
- -
- { - contractABI && contractABI.map((funcABI, index) => { - if (funcABI.type !== 'function') return null - const isConstant = funcABI.constant !== undefined ? funcABI.constant : false - const lookupOnly = funcABI.stateMutability === 'view' || funcABI.stateMutability === 'pure' || isConstant - const inputs = props.getFuncABIInputs(funcABI) +
+ +
+ {contractABI && + contractABI.map((funcABI, index) => { + if (funcABI.type !== "function") return null; + const isConstant = + funcABI.constant !== undefined ? funcABI.constant : false; + const lookupOnly = + funcABI.stateMutability === "view" || + funcABI.stateMutability === "pure" || + isConstant; + const inputs = props.getFuncABIInputs(funcABI); - return
- { - runTransaction(lookupOnly, funcABI, valArray, inputsValues, index) - }} - inputs={inputs} - evmBC={evmBC} - lookupOnly={lookupOnly} - key={index} - /> -
- - { - Object.keys(props.instance.decodedResponse || {}).map((key) => { - const funcIndex = index.toString() - const response = props.instance.decodedResponse[key] + return ( +
+ { + runTransaction( + lookupOnly, + funcABI, + valArray, + inputsValues, + index + ); + }} + inputs={inputs} + evmBC={evmBC} + lookupOnly={lookupOnly} + key={index} + /> +
+ + {Object.keys(props.instance.decodedResponse || {}).map( + (key) => { + const funcIndex = index.toString(); + const response = props.instance.decodedResponse[key]; - return key === funcIndex ? Object.keys(response || {}).map((innerkey, index) => { - return renderData(props.instance.decodedResponse[key][innerkey], response, innerkey, innerkey) - }) : null - }) - } - + return key === funcIndex + ? Object.keys(response || {}).map( + (innerkey, index) => { + return renderData( + props.instance.decodedResponse[key][ + innerkey + ], + response, + innerkey, + innerkey + ); + } + ) + : null; + } + )} + +
-
- }) - } + ); + })}
- - + +
- +
- ) + ); } diff --git a/libs/remix-ui/run-tab/src/lib/components/value.tsx b/libs/remix-ui/run-tab/src/lib/components/value.tsx index 6728639a45..3e23867aee 100644 --- a/libs/remix-ui/run-tab/src/lib/components/value.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/value.tsx @@ -3,6 +3,7 @@ import React, { useEffect, useRef, useState } from 'react' import { BN } from 'ethereumjs-util' import { isNumeric } from '@remix-ui/helper' import { ValueProps } from '../types' +import { OverlayTrigger, Tooltip } from 'react-bootstrap' export function ValueUI (props: ValueProps) { const [sendValue, setSendValue] = useState(props.sendValue) @@ -49,6 +50,11 @@ export function ValueUI (props: ValueProps) {
+ + {"Enter an amount and choose its unit"} + + }> +