Merge pull request #2904 from ethereum/custom-tooltips-rdtxns

Custom Tooltips for Run and Deploy
pull/3032/head
Joseph Izang 2 years ago committed by GitHub
commit c9ea23105b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 13
      apps/remix-ide-e2e/src/commands/addAtAddressInstance.ts
  2. 6
      apps/remix-ide-e2e/src/commands/clickFunction.ts
  3. 8
      apps/remix-ide-e2e/src/commands/testConstantFunction.ts
  4. 44
      apps/remix-ide-e2e/src/tests/debugger.test.ts
  5. 21
      libs/remix-ui/run-tab/src/lib/components/account.tsx
  6. 70
      libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx
  7. 301
      libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx
  8. 17
      libs/remix-ui/run-tab/src/lib/components/deployButton.tsx
  9. 28
      libs/remix-ui/run-tab/src/lib/components/deployInput.tsx
  10. 12
      libs/remix-ui/run-tab/src/lib/components/environment.tsx
  11. 9
      libs/remix-ui/run-tab/src/lib/components/gasPrice.tsx
  12. 37
      libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx
  13. 12
      libs/remix-ui/run-tab/src/lib/components/multiDeployInput.tsx
  14. 32
      libs/remix-ui/run-tab/src/lib/components/recorderCardUI.tsx
  15. 217
      libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx
  16. 8
      libs/remix-ui/run-tab/src/lib/components/value.tsx

@ -21,15 +21,22 @@ function addInstance (browser: NightwatchBrowser, address: string, isValidFormat
.setValue('.ataddressinput', address, function () { .setValue('.ataddressinput', address, function () {
if (!isValidFormat || !isValidChecksum) browser.assert.elementPresent('button[id^="runAndDeployAtAdressButton"]:disabled') if (!isValidFormat || !isValidChecksum) browser.assert.elementPresent('button[id^="runAndDeployAtAdressButton"]:disabled')
else if (isAbi) { else if (isAbi) {
browser.click('button[id^="runAndDeployAtAdressButton"]') browser
.waitForElementPresent('[data-id="udappNotify-modal-footer-ok-react"]') .click({
selector: '//*[@id="runAndDeployAtAdressButtonContainer"]',
locateStrategy: 'xpath'
})
.waitForElementPresent('[data-id="udappNotify-modal-footer-ok-react"]', 5000)
.execute(function () { .execute(function () {
const modal = document.querySelector('[data-id="udappNotify-modal-footer-ok-react"]') as any const modal = document.querySelector('[data-id="udappNotify-modal-footer-ok-react"]') as any
modal.click() modal.click()
}) })
} else { } else {
browser.click('button[id^="runAndDeployAtAdressButton"]') browser.click({
selector: '//*[@id="runAndDeployAtAdressButtonContainer"]',
locateStrategy: 'xpath'
})
} }
callback() callback()
}) })

@ -3,18 +3,18 @@ import EventEmitter from 'events'
class ClickFunction extends EventEmitter { class ClickFunction extends EventEmitter {
command (this: NightwatchBrowser, fnFullName: string, expectedInput?: NightwatchClickFunctionExpectedInput): NightwatchBrowser { 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) { .perform(function (client, done) {
client.execute(function () { client.execute(function () {
document.querySelector('#runTabView').scrollTop = document.querySelector('#runTabView').scrollHeight document.querySelector('#runTabView').scrollTop = document.querySelector('#runTabView').scrollHeight
}, [], function () { }, [], function () {
if (expectedInput) { if (expectedInput) {
client.setValue('#runTabView input[title="' + expectedInput.types + '"]', expectedInput.values, _ => _) client.setValue('#runTabView input[data-title="' + expectedInput.types + '"]', expectedInput.values, _ => _)
} }
done() done()
}) })
}) })
.scrollAndClick('.instance button[title="' + fnFullName + '"]') .scrollAndClick('.instance button[data-title="' + fnFullName + '"]')
.pause(2000) .pause(2000)
.perform(() => { .perform(() => {
this.emit('complete') this.emit('complete')

@ -15,18 +15,18 @@ class TestConstantFunction extends EventEmitter {
} }
function testConstantFunction (browser: NightwatchBrowser, address: string, fnFullName: string, expectedInput: NightwatchTestConstantFunctionExpectedInput, expectedOutput: string, cb: VoidFunction) { 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 () { client.execute(function () {
document.querySelector('#runTabView').scrollTop = document.querySelector('#runTabView').scrollHeight document.querySelector('#runTabView').scrollTop = document.querySelector('#runTabView').scrollHeight
}, [], function () { }, [], function () {
if (expectedInput) { if (expectedInput) {
client.waitForElementPresent('#runTabView input[title="' + expectedInput.types + '"]') client.waitForElementPresent('#runTabView input[data-title="' + expectedInput.types + '"]')
.setValue('#runTabView input[title="' + expectedInput.types + '"]', expectedInput.values) .setValue('#runTabView input[data-title="' + expectedInput.types + '"]', expectedInput.values)
} }
done() done()
}) })
}) })
.click(`#instance${address} button[title="${fnFullName}"]`) .click(`#instance${address} button[data-title="${fnFullName}"]`)
.pause(1000) .pause(1000)
.waitForElementPresent('#instance' + address + ' .udapp_contractActionsContainer .udapp_value') .waitForElementPresent('#instance' + address + ' .udapp_contractActionsContainer .udapp_value')
.scrollInto('#instance' + address + ' .udapp_contractActionsContainer .udapp_value') .scrollInto('#instance' + address + ' .udapp_contractActionsContainer .udapp_value')

@ -19,8 +19,8 @@ module.exports = {
.clickLaunchIcon('solidity').click('*[data-id="compilerContainerCompileBtn"]') .clickLaunchIcon('solidity').click('*[data-id="compilerContainerCompileBtn"]')
.pause(4000) .pause(4000)
.clickLaunchIcon('udapp') .clickLaunchIcon('udapp')
.waitForElementPresent('*[title="Deploy - transact (not payable)"]', 60000) .waitForElementPresent('*[data-title="Deploy - transact (not payable)"]', 60000)
.click('*[title="Deploy - transact (not payable)"]') .click('*[data-title="Deploy - transact (not payable)"]')
.debugTransaction(0) .debugTransaction(0)
.waitForElementContainsText('*[data-id="sidePanelSwapitTitle"]', 'DEBUGGER', 60000) .waitForElementContainsText('*[data-id="sidePanelSwapitTitle"]', 'DEBUGGER', 60000)
.clearConsole() .clearConsole()
@ -30,8 +30,8 @@ module.exports = {
browser.waitForElementVisible('*[data-id="verticalIconsKindudapp"]') browser.waitForElementVisible('*[data-id="verticalIconsKindudapp"]')
.clickLaunchIcon('udapp') .clickLaunchIcon('udapp')
.clickInstance(0) .clickInstance(0)
.scrollAndClick('*[title="string name, uint256 goal"]') .scrollAndClick('*[data-title="string name, uint256 goal"]')
.setValue('*[title="string name, uint256 goal"]', '"toast", 999') .setValue('*[data-title="string name, uint256 goal"]', '"toast", 999')
.click('*[data-id="createProject - transact (not payable)"]') .click('*[data-id="createProject - transact (not payable)"]')
.debugTransaction(0) .debugTransaction(0)
.pause(2000) .pause(2000)
@ -88,7 +88,7 @@ module.exports = {
.clickLaunchIcon('solidity') .clickLaunchIcon('solidity')
.testContracts('externalImport.sol', sources[1]['externalImport.sol'], ['ERC20']) .testContracts('externalImport.sol', sources[1]['externalImport.sol'], ['ERC20'])
.clickLaunchIcon('udapp') .clickLaunchIcon('udapp')
.waitForElementPresent('*[title="Deploy - transact (not payable)"]', 35000) .waitForElementPresent('*[data-title="Deploy - transact (not payable)"]', 35000)
.selectContract('ERC20') .selectContract('ERC20')
.createContract('"tokenName", "symbol"') .createContract('"tokenName", "symbol"')
.debugTransaction(0) .debugTransaction(0)
@ -159,7 +159,7 @@ module.exports = {
.clickLaunchIcon('solidity') .clickLaunchIcon('solidity')
.testContracts('locals.sol', sources[3]['locals.sol'], ['testLocals']) .testContracts('locals.sol', sources[3]['locals.sol'], ['testLocals'])
.clickLaunchIcon('udapp') .clickLaunchIcon('udapp')
.waitForElementPresent('*[title="Deploy - transact (not payable)"]', 40000) .waitForElementPresent('*[data-title="Deploy - transact (not payable)"]', 40000)
.createContract('') .createContract('')
.pause(2000) .pause(2000)
.clearConsole() .clearConsole()
@ -266,7 +266,7 @@ const sources = [
'blah.sol': { 'blah.sol': {
content: ` content: `
pragma solidity >=0.7.0 <0.9.0; pragma solidity >=0.7.0 <0.9.0;
contract Kickstarter { contract Kickstarter {
enum State { Started, Completed } enum State { Started, Completed }
@ -276,9 +276,9 @@ const sources = [
string name; string name;
uint goal; uint goal;
State state; State state;
} }
Project[] public projects; Project[] public projects;
constructor() { constructor() {
@ -291,7 +291,7 @@ const sources = [
project.state = State.Started; project.state = State.Started;
project.goal = goal; project.goal = goal;
} }
} }
` `
} }
}, },
@ -309,12 +309,12 @@ const sources = [
function test1 (bytes calldata userData) external returns (bytes memory, bytes32, bytes32, uint) { function test1 (bytes calldata userData) external returns (bytes memory, bytes32, bytes32, uint) {
bytes32 idAsk = abi.decode(userData[:33], (bytes32)); bytes32 idAsk = abi.decode(userData[:33], (bytes32));
bytes32 idOffer = abi.decode(userData[32:64], (bytes32)); bytes32 idOffer = abi.decode(userData[32:64], (bytes32));
bytes memory ro = abi.encodePacked(msg.sender, msg.sender, idAsk, idOffer); bytes memory ro = abi.encodePacked(msg.sender, msg.sender, idAsk, idOffer);
return (ro, idAsk, idOffer, userData.length); return (ro, idAsk, idOffer, userData.length);
} }
function testgp (bytes calldata userData) external returns (bytes4) { function testgp (bytes calldata userData) external returns (bytes4) {
return abi.decode(userData[:4], (bytes4)); return abi.decode(userData[:4], (bytes4));
} }
@ -341,9 +341,9 @@ const sources = [
'withGeneratedSources.sol': { 'withGeneratedSources.sol': {
content: ` content: `
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
contract A { contract A {
function f(uint[] memory) public returns (uint256) { } function f(uint[] memory) public returns (uint256) { }
} }
` `
} }
@ -372,7 +372,7 @@ const sources = [
} }
/** /**
* @dev Return value * @dev Return value
* @return value of 'number' * @return value of 'number'
*/ */
function retrieve() public view returns (uint256){ function retrieve() public view returns (uint256){
@ -393,14 +393,14 @@ const sources = [
function callA() public { function callA() public {
p = 123; p = 123;
try b.callB() { try b.callB() {
} }
catch (bytes memory reason) { catch (bytes memory reason) {
} }
} }
} }
contract B { contract B {
C c; C c;
uint p; uint p;
@ -413,7 +413,7 @@ const sources = [
c.callC(); c.callC();
} }
} }
contract C { contract C {
uint p; uint p;
function callC() public { function callC() public {
@ -498,7 +498,7 @@ const jsGetTrace = `(async () => {
} }
})()` })()`
const jsDebug = `(async () => { const jsDebug = `(async () => {
try { try {
const result = await remix.call('debugger', 'debug', '0x65f0813753462414f9a91f0aabea946188327995f54b893b63a8d7ff186cfca3') const result = await remix.call('debugger', 'debug', '0x65f0813753462414f9a91f0aabea946188327995f54b893b63a8d7ff186cfca3')
console.log('result ', result) console.log('result ', result)

@ -3,6 +3,7 @@ import React, { useEffect, useState, useRef } from 'react'
import { CopyToClipboard } from '@remix-ui/clipboard' import { CopyToClipboard } from '@remix-ui/clipboard'
import { AccountProps } from '../types' import { AccountProps } from '../types'
import { PassphrasePrompt } from './passphrase' import { PassphrasePrompt } from './passphrase'
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
export function AccountUI (props: AccountProps) { export function AccountUI (props: AccountProps) {
const { selectedAccount, loadedAccounts } = props.accounts const { selectedAccount, loadedAccounts } = props.accounts
@ -150,9 +151,15 @@ export function AccountUI (props: AccountProps) {
<div className="udapp_crow"> <div className="udapp_crow">
<label className="udapp_settingsLabel"> <label className="udapp_settingsLabel">
Account Account
<span id="remixRunPlusWraper" title={plusOpt.title}> <OverlayTrigger placement={'top-start'} overlay={
<i id="remixRunPlus" className={`fas fa-plus-circle udapp_icon ${plusOpt.classList}`} aria-hidden="true" onClick={newAccount}></i> <Tooltip className="text-wrap" id="remixPlusWrapperTooltip">
</span> <span>{plusOpt.title}</span>
</Tooltip>
}>
<span id="remixRunPlusWraper">
<i id="remixRunPlus" className={`fas fa-plus-circle udapp_icon ${plusOpt.classList}`} aria-hidden="true" onClick={newAccount}></i>
</span>
</OverlayTrigger>
</label> </label>
<div className="udapp_account"> <div className="udapp_account">
<select id="txorigin" data-id="runTabSelectAccount" name="txorigin" className="form-control udapp_select custom-select pr-4" value={selectedAccount} onChange={(e) => { props.setAccount(e.target.value) }}> <select id="txorigin" data-id="runTabSelectAccount" name="txorigin" className="form-control udapp_select custom-select pr-4" value={selectedAccount} onChange={(e) => { props.setAccount(e.target.value) }}>
@ -161,7 +168,13 @@ export function AccountUI (props: AccountProps) {
} }
</select> </select>
<div style={{ marginLeft: -5 }}><CopyToClipboard tip='Copy account to clipboard' content={selectedAccount} direction='top' /></div> <div style={{ marginLeft: -5 }}><CopyToClipboard tip='Copy account to clipboard' content={selectedAccount} direction='top' /></div>
<i id="remixRunSignMsg" data-id="settingsRemixRunSignMsg" className="mx-1 fas fa-edit udapp_icon" aria-hidden="true" onClick={signMessage} title="Sign a message using this account"></i> <OverlayTrigger placement={'top-start'} overlay={
<Tooltip className="text-nowrap" id="remixSignMsgTooltip">
<span>{"Sign a message using this account"}</span>
</Tooltip>
}>
<i id="remixRunSignMsg" data-id="settingsRemixRunSignMsg" className="mx-1 fas fa-edit udapp_icon" aria-hidden="true" onClick={signMessage}></i>
</OverlayTrigger>
</div> </div>
</div> </div>
) )

@ -2,10 +2,10 @@
import React, { useEffect, useRef, useState } from 'react' import React, { useEffect, useRef, useState } from 'react'
import { ContractDropdownProps, DeployMode } from '../types' import { ContractDropdownProps, DeployMode } from '../types'
import { ContractData, FuncABI } from '@remix-project/core-plugin' import { ContractData, FuncABI } from '@remix-project/core-plugin'
import { OverlayTrigger, Tooltip } from 'react-bootstrap' // eslint-disable-line
import * as ethJSUtil from 'ethereumjs-util' import * as ethJSUtil from 'ethereumjs-util'
import { ContractGUI } from './contractGUI' import { ContractGUI } from './contractGUI'
import { deployWithProxyMsg, upgradeWithProxyMsg } from '@remix-ui/helper' import { deployWithProxyMsg, upgradeWithProxyMsg } from '@remix-ui/helper'
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
const _paq = window._paq = window._paq || [] const _paq = window._paq = window._paq || []
export function ContractDropdownUI(props: ContractDropdownProps) { export function ContractDropdownUI(props: ContractDropdownProps) {
@ -115,7 +115,6 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
const initSelectedContract = () => { const initSelectedContract = () => {
const contracts = contractList[currentFile] const contracts = contractList[currentFile]
if (contracts && contracts.length > 0) { if (contracts && contracts.length > 0) {
const contract = contracts.find(contract => contract.alias === currentContract) const contract = contracts.find(contract => contract.alias === currentContract)
@ -276,13 +275,25 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
: null} : null}
</div> </div>
<div className="udapp_subcontainer"> <div className="udapp_subcontainer">
<select ref={contractsRef} value={currentContract} onChange={handleContractChange} className="udapp_contractNames custom-select" disabled={contractOptions.disabled} title={contractOptions.title} style={{ display: loadType === 'abi' && !isContractFile(currentFile) ? 'none' : 'block' }}> <OverlayTrigger
placement={"right"}
overlay={
<Tooltip
className="text-nowrap"
id="remixUdappContractNamesTooltip"
>
<span>{contractOptions.title}</span>
</Tooltip>
}
>
<select ref={contractsRef} value={currentContract} onChange={handleContractChange} className="udapp_contractNames custom-select" disabled={contractOptions.disabled} style={{ display: loadType === 'abi' && !isContractFile(currentFile) ? 'none' : 'block' }}>
{(contractList[currentFile] || []).map((contract, index) => { {(contractList[currentFile] || []).map((contract, index) => {
return <option key={index} value={contract.alias}> return <option key={index} value={contract.alias}>
{contract.alias} - {contract.file} {contract.alias} - {contract.file}
</option> </option>
})} })}
</select> </select>
</OverlayTrigger>
<span className="py-1" style={{ display: abiLabel.display }}>{abiLabel.content}</span> <span className="py-1" style={{ display: abiLabel.display }}>{abiLabel.content}</span>
</div> </div>
<div> <div>
@ -311,28 +322,47 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
onChange={handleCheckedIPFS} onChange={handleCheckedIPFS}
checked={props.ipfsCheckedState} checked={props.ipfsCheckedState}
/> />
<label <OverlayTrigger placement={'right'} overlay={
htmlFor="deployAndRunPublishToIPFS" <Tooltip className="text-wrap" id="remixIpfsUdappTooltip">
data-id="contractDropdownIpfsCheckboxLabel" <span>Publishing the source code and metadata to IPFS facilitates source code verification <br />using Sourcify and will greatly foster contract adoption (auditing, debugging, calling it, etc...)</span>
className="m-0 form-check-label custom-control-label udapp_checkboxAlign" </Tooltip>
title="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...)" }>
> <label
Publish to IPFS htmlFor="deployAndRunPublishToIPFS"
</label> data-id="contractDropdownIpfsCheckboxLabel"
className="m-0 form-check-label custom-control-label udapp_checkboxAlign"
>
Publish to IPFS
</label>
</OverlayTrigger>
</div> </div>
</div> : '' </div> : ''
} }
</div> </div>
<div className="udapp_orLabel mt-2" style={{ display: loadType === 'abi' && !isContractFile(currentFile) ? 'none' : 'block' }}>or</div> <div className="udapp_orLabel mt-2" style={{ display: loadType === 'abi' && !isContractFile(currentFile) ? 'none' : 'block' }}>or</div>
<div className="udapp_button udapp_atAddressSect"> <div className="udapp_button udapp_atAddressSect ">
<button className="udapp_atAddress btn btn-sm btn-info" id="runAndDeployAtAdressButton" disabled={atAddressOptions.disabled} title={atAddressOptions.title} onClick={loadFromAddress}>At Address</button> <OverlayTrigger placement={'top-end'} overlay={
<input <Tooltip className="text-wrap" id="runAndDeployAddresstooltip">
ref={atAddressValue} <span>{atAddressOptions.title}</span>
className="udapp_input udapp_ataddressinput ataddressinput form-control" </Tooltip>
placeholder="Load contract from Address" }>
title="address of contract" <div id="runAndDeployAtAdressButtonContainer" onClick={loadFromAddress} data-title={atAddressOptions.title}>
onChange={atAddressChanged} <button className="udapp_atAddress btn btn-sm btn-info" id="runAndDeployAtAdressButton" disabled={atAddressOptions.disabled} style={{ pointerEvents: 'none' }} onClick={loadFromAddress} data-title={atAddressOptions.title}
/> >At Address</button>
</div>
</OverlayTrigger>
<OverlayTrigger placement={'top-end'} overlay={
<Tooltip className="text-wrap" id="runAndDeployAddressInputtooltip">
<span>{"address of contract"}</span>
</Tooltip>
}>
<input
ref={atAddressValue}
className="udapp_input udapp_ataddressinput ataddressinput form-control"
placeholder="Load contract from Address"
onChange={atAddressChanged}
/>
</OverlayTrigger>
</div> </div>
</div> </div>
</div> </div>

@ -3,6 +3,7 @@ import React, { useEffect, useRef, useState } from 'react'
import * as remixLib from '@remix-project/remix-lib' import * as remixLib from '@remix-project/remix-lib'
import { ContractGUIProps } from '../types' import { ContractGUIProps } from '../types'
import { CopyToClipboard } from '@remix-ui/clipboard' import { CopyToClipboard } from '@remix-ui/clipboard'
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
const txFormat = remixLib.execution.txFormat const txFormat = remixLib.execution.txFormat
const txHelper = remixLib.execution.txHelper const txHelper = remixLib.execution.txHelper
@ -233,66 +234,187 @@ export function ContractGUI (props: ContractGUIProps) {
} }
return ( return (
<div className={`udapp_contractProperty ${(props.funcABI.inputs && props.funcABI.inputs.length > 0) || (props.funcABI.type === 'fallback') || (props.funcABI.type === 'receive') ? 'udapp_hasArgs' : ''}`}> <div
<div className="udapp_contractActionsContainerSingle pt-2" style={{ display: toggleContainer ? 'none' : 'flex' }}> className={`udapp_contractProperty ${
<button onClick={handleActionClick} title={buttonOptions.title} className={`udapp_instanceButton ${props.widthClass} btn btn-sm ${buttonOptions.classList}`} data-id={buttonOptions.dataId}>{title}</button> (props.funcABI.inputs && props.funcABI.inputs.length > 0) ||
<input props.funcABI.type === "fallback" ||
className="form-control" props.funcABI.type === "receive"
data-id={props.funcABI.type === 'fallback' || props.funcABI.type === 'receive' ? `'(${props.funcABI.type}')` : 'multiParamManagerBasicInputField'} ? "udapp_hasArgs"
placeholder={props.inputs} : ""
title={props.funcABI.type === 'fallback' || props.funcABI.type === 'receive' ? `'(${props.funcABI.type}')` : props.inputs} }`}
onChange={handleBasicInput} >
ref={basicInputRef} <div
style={{ visibility: !((props.funcABI.inputs && props.funcABI.inputs.length > 0) || (props.funcABI.type === 'fallback') || (props.funcABI.type === 'receive')) ? 'hidden' : 'visible' }} /> className="udapp_contractActionsContainerSingle pt-2"
style={{ display: toggleContainer ? "none" : "flex" }}
>
<OverlayTrigger
placement={"right-start"}
overlay={
<Tooltip
className="text-wrap"
id="remixUdappInstanceButtonTooltip"
>
<span>{buttonOptions.title}</span>
</Tooltip>
}
>
<button
onClick={handleActionClick}
className={`udapp_instanceButton ${props.widthClass} btn btn-sm ${buttonOptions.classList}`}
data-id={buttonOptions.dataId}
data-title={buttonOptions.title}
>
{title}
</button>
</OverlayTrigger>
<OverlayTrigger
placement={"right"}
overlay={
<Tooltip className="text-nowrap" id="remixContractGuiTooltip">
<span>
{props.funcABI.type === "fallback" ||
props.funcABI.type === "receive"
? `'(${props.funcABI.type}')`
: props.inputs}
</span>
</Tooltip>
}
>
<input
className="form-control"
data-id={
props.funcABI.type === "fallback" ||
props.funcABI.type === "receive"
? `'(${props.funcABI.type}')`
: "multiParamManagerBasicInputField"
}
placeholder={props.inputs}
onChange={handleBasicInput}
data-title={
props.funcABI.type === "fallback" ||
props.funcABI.type === "receive"
? `'(${props.funcABI.type}')`
: props.inputs
}
ref={basicInputRef}
style={{
visibility: !(
(props.funcABI.inputs && props.funcABI.inputs.length > 0) ||
props.funcABI.type === "fallback" ||
props.funcABI.type === "receive"
)
? "hidden"
: "visible",
}}
/>
</OverlayTrigger>
<i <i
className="fas fa-angle-down udapp_methCaret" className="fas fa-angle-down udapp_methCaret"
onClick={switchMethodViewOn} onClick={switchMethodViewOn}
title={title} title={title}
style={{ visibility: !(props.funcABI.inputs && props.funcABI.inputs.length > 0) ? 'hidden' : 'visible' }}></i> style={{
visibility: !(
props.funcABI.inputs && props.funcABI.inputs.length > 0
)
? "hidden"
: "visible",
}}
></i>
</div> </div>
<div className="udapp_contractActionsContainerMulti" style={{ display: toggleContainer ? 'flex' : 'none' }}> <div
className="udapp_contractActionsContainerMulti"
style={{ display: toggleContainer ? "flex" : "none" }}
>
<div className="udapp_contractActionsContainerMultiInner text-dark"> <div className="udapp_contractActionsContainerMultiInner text-dark">
<div onClick={switchMethodViewOff} className="udapp_multiHeader"> <div onClick={switchMethodViewOff} className="udapp_multiHeader">
<div className="udapp_multiTitle run-instance-multi-title">{title}</div> <div className="udapp_multiTitle run-instance-multi-title">
<i className='fas fa-angle-up udapp_methCaret'></i> {title}
</div>
<i className="fas fa-angle-up udapp_methCaret"></i>
</div> </div>
<div> <div>
{props.funcABI.inputs.map((inp, index) => { {props.funcABI.inputs.map((inp, index) => {
return ( return (
<div className="udapp_multiArg" key={index}> <div className="udapp_multiArg" key={index}>
<label htmlFor={inp.name}> {inp.name}: </label> <label htmlFor={inp.name}> {inp.name}: </label>
<input ref={el => { multiFields.current[index] = el }} className="form-control" placeholder={inp.type} title={inp.name} data-id={`multiParamManagerInput${inp.name}`} /> <OverlayTrigger
</div>) placement="left-end"
overlay={
<Tooltip id="udappContractActionsTooltip" className="text-nowrap">
<span>{inp.name}</span>
</Tooltip>
}
>
<input
ref={(el) => {
multiFields.current[index] = el;
}}
className="form-control"
placeholder={inp.type}
data-id={`multiParamManagerInput${inp.name}`}
/>
</OverlayTrigger>
</div>
);
})} })}
</div> </div>
<div className="d-flex udapp_group udapp_multiArg"> <div className="d-flex udapp_group udapp_multiArg">
<CopyToClipboard tip='Copy calldata to clipboard' icon='fa-clipboard' direction={'bottom'} getContent={getEncodedCall} > <CopyToClipboard
tip="Copy calldata to clipboard"
icon="fa-clipboard"
direction={"bottom"}
getContent={getEncodedCall}
>
<button className="btn remixui_copyButton"> <button className="btn remixui_copyButton">
<i id="copyCalldata" className="m-0 remixui_copyIcon far fa-copy" aria-hidden="true"></i> <i
id="copyCalldata"
className="m-0 remixui_copyIcon far fa-copy"
aria-hidden="true"
></i>
<label htmlFor="copyCalldata">Calldata</label> <label htmlFor="copyCalldata">Calldata</label>
</button> </button>
</CopyToClipboard> </CopyToClipboard>
<CopyToClipboard tip='Copy encoded input parameters to clipboard' icon='fa-clipboard' direction={'bottom'} getContent={getEncodedParams} > <CopyToClipboard
tip="Copy encoded input parameters to clipboard"
icon="fa-clipboard"
direction={"bottom"}
getContent={getEncodedParams}
>
<button className="btn remixui_copyButton"> <button className="btn remixui_copyButton">
<i id="copyParameters" className="m-0 remixui_copyIcon far fa-copy" aria-hidden="true"></i> <i
id="copyParameters"
className="m-0 remixui_copyIcon far fa-copy"
aria-hidden="true"
></i>
<label htmlFor="copyParameters">Parameters</label> <label htmlFor="copyParameters">Parameters</label>
</button> </button>
</CopyToClipboard> </CopyToClipboard>
<button <OverlayTrigger
type="button" placement={"right"}
onClick={handleExpandMultiClick} overlay={
title={buttonOptions.title} <Tooltip
data-id={buttonOptions.dataId} className="text-nowrap"
className={`udapp_instanceButton ${buttonOptions.classList}`} id="remixUdappInstanceButtonTooltip"
>
<span>{buttonOptions.title}</span>
</Tooltip>
}
> >
{ buttonOptions.content } <button
</button> type="button"
onClick={handleExpandMultiClick}
data-id={buttonOptions.dataId}
className={`udapp_instanceButton ${buttonOptions.classList}`}
>
{buttonOptions.content}
</button>
</OverlayTrigger>
</div> </div>
</div> </div>
</div> </div>
{ props.deployOption && (props.deployOption || []).length > 0 ? {props.deployOption && (props.deployOption || []).length > 0 ? (
<> <>
<div className='d-flex justify-content-between'> <div className="d-flex justify-content-between">
<div className="d-flex py-1 align-items-center custom-control custom-checkbox"> <div className="d-flex py-1 align-items-center custom-control custom-checkbox">
<input <input
id="deployWithProxy" id="deployWithProxy"
@ -312,30 +434,55 @@ export function ContractGUI (props: ContractGUIProps) {
</label> </label>
</div> </div>
<div> <div>
{ {props.initializerOptions &&
props.initializerOptions && props.initializerOptions.initializeInputs ? props.initializerOptions.initializeInputs ? (
<span onClick={handleToggleDeployProxy}> <span onClick={handleToggleDeployProxy}>
<i className={!toggleDeployProxy ? 'fas fa-angle-right pt-2' : 'fas fa-angle-down'} aria-hidden="true"></i> <i
</span> : null className={
} !toggleDeployProxy
? "fas fa-angle-right pt-2"
: "fas fa-angle-down"
}
aria-hidden="true"
></i>
</span>
) : null}
</div> </div>
</div> </div>
{ {props.initializerOptions &&
props.initializerOptions && props.initializerOptions.initializeInputs ? props.initializerOptions.initializeInputs ? (
<div className={`pl-4 flex-column ${toggleDeployProxy ? "d-flex" : "d-none"}`}> <div
<div className={`flex-column 'd-flex'}`}>{ className={`pl-4 flex-column ${
props.initializerOptions.inputs.inputs.map((inp, index) => { toggleDeployProxy ? "d-flex" : "d-none"
return ( }`}
<div className="mb-2" key={index}> >
<label className='mt-2 text-left d-block' htmlFor={inp.name}> {inp.name}: </label> <div className={`flex-column 'd-flex'}`}>
<input ref={el => { initializeFields.current[index] = el }} style={{ height: 32 }} className="form-control udapp_input" placeholder={inp.type} title={inp.name} /> {props.initializerOptions.inputs.inputs.map((inp, index) => {
</div> return (
)}) <div className="mb-2" key={index}>
} <label
</div> className="mt-2 text-left d-block"
</div> : null htmlFor={inp.name}
} >
<div className='d-flex justify-content-between'> {" "}
{inp.name}:{" "}
</label>
<input
ref={(el) => {
initializeFields.current[index] = el;
}}
style={{ height: 32 }}
className="form-control udapp_input"
placeholder={inp.type}
title={inp.name}
/>
</div>
);
})}
</div>
</div>
) : null}
<div className="d-flex justify-content-between">
<div className="d-flex py-1 align-items-center custom-control custom-checkbox"> <div className="d-flex py-1 align-items-center custom-control custom-checkbox">
<input <input
id="upgradeImplementation" id="upgradeImplementation"
@ -355,10 +502,21 @@ export function ContractGUI (props: ContractGUIProps) {
</label> </label>
</div> </div>
<span onClick={handleToggleUpgradeImp}> <span onClick={handleToggleUpgradeImp}>
<i className={!toggleUpgradeImp ? 'fas fa-angle-right pt-2' : 'fas fa-angle-down'} aria-hidden="true"></i> <i
className={
!toggleUpgradeImp
? "fas fa-angle-right pt-2"
: "fas fa-angle-down"
}
aria-hidden="true"
></i>
</span> </span>
</div> </div>
<div className={`pl-4 flex-column ${toggleUpgradeImp ? "d-flex" : "d-none"}`}> <div
className={`pl-4 flex-column ${
toggleUpgradeImp ? "d-flex" : "d-none"
}`}
>
<div className={`flex-column 'd-flex'}`}> <div className={`flex-column 'd-flex'}`}>
<div className="d-flex py-1 align-items-center custom-control custom-checkbox"> <div className="d-flex py-1 align-items-center custom-control custom-checkbox">
<input <input
@ -379,18 +537,33 @@ export function ContractGUI (props: ContractGUIProps) {
Use last deployed ERC1967 contract Use last deployed ERC1967 contract
</label> </label>
</div> </div>
{ {!useLastProxy ? (
!useLastProxy ?
<div className="mb-2"> <div className="mb-2">
<label className='mt-2 text-left d-block'>Proxy Address: </label> <label className="mt-2 text-left d-block">
<input style={{ height: 32 }} className="form-control udapp_input" data-id="ERC1967AddressInput" placeholder='proxy address' title='Enter previously deployed proxy address on the selected network' onChange={handleSetProxyAddress} /> Proxy Address:{" "}
</div> : </label>
<span className='text-capitalize' data-id="lastDeployedERC1967Address" style={{ fontSize: '.8em' }}>{ proxyAddress || 'No proxy address available' }</span> <input
} style={{ height: 32 }}
className="form-control udapp_input"
data-id="ERC1967AddressInput"
placeholder="proxy address"
title="Enter previously deployed proxy address on the selected network"
onChange={handleSetProxyAddress}
/>
</div>
) : (
<span
className="text-capitalize"
data-id="lastDeployedERC1967Address"
style={{ fontSize: ".8em" }}
>
{proxyAddress || "No proxy address available"}
</span>
)}
</div> </div>
</div> </div>
</> : null </>
} ) : null}
</div> </div>
) );
} }

@ -1,6 +1,6 @@
import React, { useState } from 'react' import React, { useState } from 'react'
import { DeployButtonProps } from '../types' import { DeployButtonProps } from '../types'
import { ButtonGroup, Dropdown } from 'react-bootstrap' import { ButtonGroup, Dropdown, OverlayTrigger, Tooltip } from 'react-bootstrap'
export function DeployButton (props: DeployButtonProps) { export function DeployButton (props: DeployButtonProps) {
const [showOptions, setShowOptions] = useState<boolean>(false) const [showOptions, setShowOptions] = useState<boolean>(false)
@ -24,9 +24,18 @@ export function DeployButton (props: DeployButtonProps) {
} }
</Dropdown.Menu> </Dropdown.Menu>
</Dropdown> : </Dropdown> :
<button onClick={props.handleActionClick} title={props.buttonOptions.title} className={`udapp_instanceButton ${props.buttonOptions.widthClass} btn btn-sm ${props.buttonOptions.classList}`} data-id={props.buttonOptions.dataId}> <OverlayTrigger
Deploy placement="right-start"
</button> overlay={
<Tooltip id="deployButtonTooltip" className="text-nowrap">
<span>{props.buttonOptions.title}</span>
</Tooltip>
}
>
<button onClick={props.handleActionClick} className={`udapp_instanceButton ${props.buttonOptions.widthClass} btn btn-sm ${props.buttonOptions.classList}`} data-id={props.buttonOptions.dataId}>
Deploy
</button>
</OverlayTrigger>
} }
</> </>
) )

@ -1,4 +1,5 @@
import React from 'react' import React from 'react'
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
import { DeployInputProps } from '../types' import { DeployInputProps } from '../types'
import { DeployButton } from './deployButton' import { DeployButton } from './deployButton'
@ -6,15 +7,24 @@ export function DeployInput (props: DeployInputProps) {
return ( return (
<div className="udapp_contractActionsContainerSingle pt-2" style={{ display: 'flex' }}> <div className="udapp_contractActionsContainerSingle pt-2" style={{ display: 'flex' }}>
<DeployButton buttonOptions={props.buttonOptions} selectedIndex={props.selectedIndex} setSelectedIndex={props.setSelectedIndex} handleActionClick={props.handleActionClick} deployOptions={props.deployOptions} /> <DeployButton buttonOptions={props.buttonOptions} selectedIndex={props.selectedIndex} setSelectedIndex={props.setSelectedIndex} handleActionClick={props.handleActionClick} deployOptions={props.deployOptions} />
<input <OverlayTrigger
className="form-control" placement="right-start"
data-id={props.funcABI.type === 'fallback' || props.funcABI.type === 'receive' ? `'(${props.funcABI.type}')` : 'multiParamManagerBasicInputField'} overlay={
placeholder={props.inputs} <Tooltip id="deployInputTooltip" className="text-nowrap">
title={props.funcABI.type === 'fallback' || props.funcABI.type === 'receive' ? `'(${props.funcABI.type}')` : props.inputs} <span>{props.funcABI.type === 'fallback' || props.funcABI.type === 'receive' ? `'(${props.funcABI.type}')` : props.inputs}</span>
onChange={props.handleBasicInput} </Tooltip>
ref={props.basicInputRef} }
style={{ visibility: !props.inputs ? 'hidden' : 'visible' }} >
/> <input
className="form-control"
data-id={props.funcABI.type === 'fallback' || props.funcABI.type === 'receive' ? `'(${props.funcABI.type}')` : 'multiParamManagerBasicInputField'}
placeholder={props.inputs}
title={props.funcABI.type === 'fallback' || props.funcABI.type === 'receive' ? `'(${props.funcABI.type}')` : props.inputs}
onChange={props.handleBasicInput}
ref={props.basicInputRef}
style={{ visibility: !props.inputs ? 'hidden' : 'visible' }}
/>
</OverlayTrigger>
</div> </div>
) )
} }

@ -6,7 +6,7 @@ import { CustomMenu, CustomToggle } from '@remix-ui/helper'
import { OverlayTrigger, Tooltip } from 'react-bootstrap' // eslint-disable-line import { OverlayTrigger, Tooltip } from 'react-bootstrap' // eslint-disable-line
export function EnvironmentUI (props: EnvironmentProps) { export function EnvironmentUI (props: EnvironmentProps) {
const handleChangeExEnv = (env: string) => { const handleChangeExEnv = (env: string) => {
const provider = props.providers.providerList.find(exEnv => exEnv.value === env) const provider = props.providers.providerList.find(exEnv => exEnv.value === env)
@ -49,7 +49,7 @@ export function EnvironmentUI (props: EnvironmentProps) {
</Tooltip> </Tooltip>
}> }>
<i style={{ fontSize: 'medium' }} className={'ml-2 fal fa-plug'} aria-hidden="true" onClick={() => { window.open(bridges[currentProvider.value], '_blank') }}></i> <i style={{ fontSize: 'medium' }} className={'ml-2 fal fa-plug'} aria-hidden="true" onClick={() => { window.open(bridges[currentProvider.value], '_blank') }}></i>
</OverlayTrigger>} </OverlayTrigger>}
</Dropdown.Toggle> </Dropdown.Toggle>
<Dropdown.Menu as={CustomMenu} className='w-100 custom-dropdown-items' data-id="custom-dropdown-items" > <Dropdown.Menu as={CustomMenu} className='w-100 custom-dropdown-items' data-id="custom-dropdown-items" >
{ {
@ -67,7 +67,13 @@ export function EnvironmentUI (props: EnvironmentProps) {
} }
</Dropdown.Menu> </Dropdown.Menu>
</Dropdown> </Dropdown>
<a href="https://remix-ide.readthedocs.io/en/latest/run.html#environment" target="_blank" rel="noreferrer"><i className="udapp_infoDeployAction ml-2 fas fa-info" title="Click for docs about Environment"></i></a> <OverlayTrigger placement={'bottom-start'} overlay={
<Tooltip className="text-wrap" id="runAndDeployAddresstooltip">
<span>{"Click for docs about Environment"}</span>
</Tooltip>
}>
<a href="https://remix-ide.readthedocs.io/en/latest/run.html#environment" target="_blank" rel="noreferrer"><i className="udapp_infoDeployAction ml-2 fas fa-info"></i></a>
</OverlayTrigger>
</div> </div>
</div> </div>
) )

@ -1,5 +1,6 @@
// eslint-disable-next-line no-use-before-define // eslint-disable-next-line no-use-before-define
import React from 'react' import React from 'react'
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
import { GasPriceProps } from '../types' import { GasPriceProps } from '../types'
export function GasPriceUI (props: GasPriceProps) { export function GasPriceUI (props: GasPriceProps) {
@ -10,7 +11,13 @@ export function GasPriceUI (props: GasPriceProps) {
return ( return (
<div className="udapp_crow"> <div className="udapp_crow">
<label className="udapp_settingsLabel">Gas limit</label> <label className="udapp_settingsLabel">Gas limit</label>
<input type="number" className="form-control udapp_gasNval udapp_col2" title="The default gas limit is 3M. Adjust as needed." id="gasLimit" value={props.gasLimit} onChange={handleGasLimit} /> <OverlayTrigger placement={'right-end'} overlay={
<Tooltip className="text-nowrap" id="remixGasPriceTooltip">
<span>{"The default gas limit is 3M. Adjust as needed."}</span>
</Tooltip>
}>
<input type="number" className="form-control udapp_gasNval udapp_col2" id="gasLimit" value={props.gasLimit} onChange={handleGasLimit} />
</OverlayTrigger>
</div> </div>
) )
} }

@ -1,5 +1,6 @@
// eslint-disable-next-line no-use-before-define // eslint-disable-next-line no-use-before-define
import React from 'react' import React from 'react'
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
import { InstanceContainerProps } from '../types' import { InstanceContainerProps } from '../types'
import { UniversalDappUI } from './universalDappUI' import { UniversalDappUI } from './universalDappUI'
@ -12,15 +13,35 @@ export function InstanceContainerUI (props: InstanceContainerProps) {
return ( return (
<div className="udapp_instanceContainer mt-3 border-0 list-group-item"> <div className="udapp_instanceContainer mt-3 border-0 list-group-item">
<label className="udapp_deployedContracts d-flex justify-content-between align-items-center pl-2 mb-2" <div className="d-flex justify-content-between align-items-center pl-2 mb-2">
title="Autogenerated generic user interfaces for interaction with deployed contracts"> <OverlayTrigger
Deployed Contracts placement="top-start"
overlay={
<Tooltip className="text-nowrap" id="deployAndRunClearInstancesTooltip">
<span>{"Autogenerated generic user interfaces for interaction with deployed contracts"}</span>
</Tooltip>
}
>
<label className="udapp_deployedContracts">
Deployed Contracts
</label>
</OverlayTrigger>
{ instanceList.length > 0 { instanceList.length > 0
? <i className="mr-2 udapp_icon far fa-trash-alt" data-id="deployAndRunClearInstances" onClick={clearInstance} ? (
title="Clear instances list and reset recorder" aria-hidden="true"> <OverlayTrigger
</i> : null placement="right"
} overlay={
</label> <Tooltip className="text-nowrap" id="deployAndRunClearInstancesTooltip">
<span>{"Clear instances list and reset recorder"}</span>
</Tooltip>
}
>
<i className="mr-2 udapp_icon far fa-trash-alt" data-id="deployAndRunClearInstances" onClick={clearInstance} aria-hidden="true">
</i>
</OverlayTrigger>
) : null
}
</div>
{ instanceList.length > 0 { instanceList.length > 0
? <div> { props.instances.instanceList.map((instance, index) => { ? <div> { props.instances.instanceList.map((instance, index) => {
return <UniversalDappUI return <UniversalDappUI

@ -1,4 +1,5 @@
import React, { useRef, useState } from 'react' import React, { useRef, useState } from 'react'
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
import { MultiDeployInputProps } from '../types' import { MultiDeployInputProps } from '../types'
import { DeployButton } from './deployButton' import { DeployButton } from './deployButton'
@ -16,7 +17,16 @@ export function MultiDeployInput(props: MultiDeployInputProps) {
return ( return (
<div className="udapp_multiArg" key={index}> <div className="udapp_multiArg" key={index}>
<label htmlFor={inp.name}> {inp.name}: </label> <label htmlFor={inp.name}> {inp.name}: </label>
<input ref={el => { multiFields.current[index] = el }} className="form-control" placeholder={inp.type} title={inp.name} data-id={`multiParamManagerInput${inp.name}`} /> <OverlayTrigger
placement="left-end"
overlay={
<Tooltip id="udappMultiArgTooltip" className="text-nowrap">
<span>{inp.name}</span>
</Tooltip>
}
>
<input ref={el => { multiFields.current[index] = el }} className="form-control" placeholder={inp.type} data-id={`multiParamManagerInput${inp.name}`} />
</OverlayTrigger>
</div>) </div>)
})} })}
</div> </div>

@ -31,7 +31,13 @@ export function RecorderUI (props: RecorderProps) {
<div className="udapp_recorderSection d-flex justify-content-between" onClick={toggleClass}> <div className="udapp_recorderSection d-flex justify-content-between" onClick={toggleClass}>
<div className="d-flex justify-content-center align-items-center"> <div className="d-flex justify-content-center align-items-center">
<label className="mt-1 udapp_recorderSectionLabel">Transactions recorded</label> <label className="mt-1 udapp_recorderSectionLabel">Transactions recorded</label>
<div className="ml-2 badge badge-pill badge-primary text-center" title="The number of recorded transactions">{props.count}</div> <OverlayTrigger placement={'right'} overlay={
<Tooltip className="text-nowrap" id="recordedTransactionsCounttooltip">
<span>{'The number of recorded transactions'}</span>
</Tooltip>
}>
<div className="ml-2 badge badge-pill badge-primary text-center" data-title="The number of recorded transactions">{props.count}</div>
</OverlayTrigger>
<OverlayTrigger placement={'right'} overlay={ <OverlayTrigger placement={'right'} overlay={
<Tooltip className="text-nowrap" id="info-recorder"> <Tooltip className="text-nowrap" id="info-recorder">
<span>Save transactions (deployed contracts and function executions) and replay them in another environment. <br/> e.g Transactions created in Remix VM can be replayed in the Injected Provider. <span>Save transactions (deployed contracts and function executions) and replay them in another environment. <br/> e.g Transactions created in Remix VM can be replayed in the Injected Provider.
@ -59,19 +65,33 @@ export function RecorderUI (props: RecorderProps) {
</OverlayTrigger> </OverlayTrigger>
</div> </div>
<div className="mb-1 mt-1 udapp_transactionActions"> <div className="mb-1 mt-1 udapp_transactionActions">
<OverlayTrigger placement={'right'} overlay={ <OverlayTrigger placement={'bottom-start'} overlay={
<Tooltip className="text-nowrap" id="tooltip-save-recorder"> <Tooltip className="text-nowrap" id="remixUdappTransactionSavetooltip">
<span>Save {props.count} transaction{props.count === 1 ? '' : 's'} as scenario file</span> <span>
{
props.count === 0 ? 'No transactions to save'
: props.count === 1 ? `Save ${props.count} transaction as scenario file`
: `Save ${props.count} transactions as scenario file`
}
</span>
</Tooltip> </Tooltip>
}> }>
<button className="btn btn-sm btn-info savetransaction udapp_recorder" title={props.count === 0 ? 'No transactions to save' : ''} disabled={props.count === 0 ? true: false} onClick={triggerRecordButton}>Save</button> <span>
<button className="btn btn-sm btn-info savetransaction udapp_recorder" disabled={props.count === 0 ? true: false} onClick={triggerRecordButton} style={{ pointerEvents: props.count === 0 ? 'none' : 'auto' }}>
Save
</button>
</span>
</OverlayTrigger> </OverlayTrigger>
<OverlayTrigger placement={'right'} overlay={ <OverlayTrigger placement={'right'} overlay={
<Tooltip className="text-nowrap" id="tooltip-run-recorder"> <Tooltip className="text-nowrap" id="tooltip-run-recorder">
<span>Run transaction(s) from the current scenario file</span> <span>Run transaction(s) from the current scenario file</span>
</Tooltip> </Tooltip>
}> }>
<button className="btn btn-sm btn-info runtransaction udapp_runTxs" data-id="runtransaction" title={enableRunButton ? 'No scenario file selected' : ''} disabled={enableRunButton} onClick={handleClickRunButton}>Run</button> <span>
<button className="btn btn-sm btn-info runtransaction udapp_runTxs" data-id="runtransaction" disabled={enableRunButton} onClick={handleClickRunButton} style={{ pointerEvents: enableRunButton ? 'none' : 'auto' }}>
Run
</button>
</span>
</OverlayTrigger> </OverlayTrigger>
</div> </div>
</div> </div>

@ -7,6 +7,7 @@ import * as remixLib from '@remix-project/remix-lib'
import * as ethJSUtil from 'ethereumjs-util' import * as ethJSUtil from 'ethereumjs-util'
import { ContractGUI } from './contractGUI' import { ContractGUI } from './contractGUI'
import { TreeView, TreeViewItem } from '@remix-ui/tree-view' import { TreeView, TreeViewItem } from '@remix-ui/tree-view'
import { OverlayTrigger, Tooltip } from 'react-bootstrap' // eslint-disable-line
import { BN } from 'ethereumjs-util' import { BN } from 'ethereumjs-util'
import { is0XPrefixed, isHexadecimal, isNumeric, shortenAddress } from '@remix-ui/helper' import { is0XPrefixed, isHexadecimal, isNumeric, shortenAddress } from '@remix-ui/helper'
@ -210,104 +211,190 @@ export function UniversalDappUI (props: UdappProps) {
} }
return ( return (
<div className={`instance udapp_instance udapp_run-instance border-dark ${toggleExpander ? 'udapp_hidesub' : 'bg-light'}`} id={`instance${address}`} data-shared="universalDappUiInstance"> <div
className={`instance udapp_instance udapp_run-instance border-dark ${
toggleExpander ? "udapp_hidesub" : "bg-light"
}`}
id={`instance${address}`}
data-shared="universalDappUiInstance"
>
<div className="udapp_title pb-0 alert alert-secondary"> <div className="udapp_title pb-0 alert alert-secondary">
<span data-id={`universalDappUiTitleExpander${props.index}`} className="btn udapp_titleExpander" onClick={toggleClass}> <span
<i className={`fas ${toggleExpander ? 'fa-angle-right' : 'fa-angle-down'}`} aria-hidden="true"></i> data-id={`universalDappUiTitleExpander${props.index}`}
className="btn udapp_titleExpander"
onClick={toggleClass}
>
<i
className={`fas ${
toggleExpander ? "fa-angle-right" : "fa-angle-down"
}`}
aria-hidden="true"
></i>
</span> </span>
<div className="input-group udapp_nameNbuts"> <div className="input-group udapp_nameNbuts">
<div className="udapp_titleText input-group-prepend"> <div className="udapp_titleText input-group-prepend">
<span className="input-group-text udapp_spanTitleText"> <span className="input-group-text udapp_spanTitleText">
{props.instance.name} at {shortenAddress(address)} ({props.context}) {props.instance.name} at {shortenAddress(address)} (
{props.context})
</span> </span>
</div> </div>
<div className="btn-group"> <div className="btn-group">
<button className="btn p-1 btn-secondary"><CopyToClipboard content={address} direction={'top'} /></button> <button className="btn p-1 btn-secondary">
<CopyToClipboard content={address} direction={"top"} />
</button>
</div> </div>
</div> </div>
<button <OverlayTrigger
className="udapp_udappClose mr-1 p-1 btn btn-secondary align-items-center" placement="right"
data-id="universalDappUiUdappClose" overlay={
onClick={remove} <Tooltip className="text-nowrap" id="udapp_udappCloseTooltip">
title="Remove from the list" <span>{'Remove from the list'}</span>
</Tooltip>
}
> >
<i className="udapp_closeIcon fas fa-times" aria-hidden="true"></i> <button
</button> className="udapp_udappClose mr-1 p-1 btn btn-secondary align-items-center"
data-id="universalDappUiUdappClose"
onClick={remove}
>
<i className="udapp_closeIcon fas fa-times" aria-hidden="true"></i>
</button>
</OverlayTrigger>
</div> </div>
<div className="udapp_cActionsWrapper" data-id="universalDappUiContractActionWrapper"> <div
className="udapp_cActionsWrapper"
data-id="universalDappUiContractActionWrapper"
>
<div className="udapp_contractActionsContainer"> <div className="udapp_contractActionsContainer">
<div className="d-flex" data-id="instanceContractBal"> <div className="d-flex" data-id="instanceContractBal">
<label>Balance: {instanceBalance} ETH</label> <label>Balance: {instanceBalance} ETH</label>
</div> </div>
{ {contractABI &&
contractABI && contractABI.map((funcABI, index) => { contractABI.map((funcABI, index) => {
if (funcABI.type !== 'function') return null if (funcABI.type !== "function") return null;
const isConstant = funcABI.constant !== undefined ? funcABI.constant : false const isConstant =
const lookupOnly = funcABI.stateMutability === 'view' || funcABI.stateMutability === 'pure' || isConstant funcABI.constant !== undefined ? funcABI.constant : false;
const inputs = props.getFuncABIInputs(funcABI) const lookupOnly =
funcABI.stateMutability === "view" ||
funcABI.stateMutability === "pure" ||
isConstant;
const inputs = props.getFuncABIInputs(funcABI);
return <div key={index}> return (
<ContractGUI <div key={index}>
funcABI={funcABI} <ContractGUI
clickCallBack={(valArray: { name: string, type: string }[], inputsValues: string) => { funcABI={funcABI}
runTransaction(lookupOnly, funcABI, valArray, inputsValues, index) clickCallBack={(
}} valArray: { name: string; type: string }[],
inputs={inputs} inputsValues: string
evmBC={evmBC} ) => {
lookupOnly={lookupOnly} runTransaction(
key={index} lookupOnly,
/> funcABI,
<div className="udapp_value" data-id="udapp_value"> valArray,
<TreeView id="treeView"> inputsValues,
{ index
Object.keys(props.instance.decodedResponse || {}).map((key) => { );
const funcIndex = index.toString() }}
const response = props.instance.decodedResponse[key] inputs={inputs}
evmBC={evmBC}
lookupOnly={lookupOnly}
key={index}
/>
<div className="udapp_value" data-id="udapp_value">
<TreeView id="treeView">
{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 key === funcIndex
return renderData(props.instance.decodedResponse[key][innerkey], response, innerkey, innerkey) ? Object.keys(response || {}).map(
}) : null (innerkey, index) => {
}) return renderData(
} props.instance.decodedResponse[key][
</TreeView> innerkey
],
response,
innerkey,
innerkey
);
}
)
: null;
}
)}
</TreeView>
</div>
</div> </div>
</div> );
}) })}
}
</div> </div>
<div className="d-flex flex-column"> <div className="d-flex flex-column">
<div className="d-flex flex-row justify-content-between mt-2"> <div className="d-flex flex-row justify-content-between mt-2">
<div className="py-2 border-top d-flex justify-content-start flex-grow-1"> <div className="py-2 border-top d-flex justify-content-start flex-grow-1">
Low level interactions Low level interactions
</div> </div>
<a <OverlayTrigger
href="https://solidity.readthedocs.io/en/v0.6.2/contracts.html#receive-ether-function" placement={"bottom-end"}
title="check out docs for using 'receive'/'fallback'" overlay={
target="_blank" rel="noreferrer" <Tooltip className="text-wrap" id="receiveEthDocstoolTip">
<span>{"check out docs for using 'receive'/'fallback'"}</span>
</Tooltip>
}
> >
<i aria-hidden="true" className="fas fa-info my-2 mr-1"></i> <a
</a> href="https://solidity.readthedocs.io/en/v0.6.2/contracts.html#receive-ether-function"
target="_blank"
rel="noreferrer"
>
<i aria-hidden="true" className="fas fa-info my-2 mr-1"></i>
</a>
</OverlayTrigger>
</div> </div>
<div className="d-flex flex-column align-items-start"> <div className="d-flex flex-column align-items-start">
<label className="">CALLDATA</label> <label className="">CALLDATA</label>
<div className="d-flex justify-content-end w-100 align-items-center"> <div className="d-flex justify-content-end w-100 align-items-center">
<input id="deployAndRunLLTxCalldata" onChange={handleCalldataChange} className="udapp_calldataInput form-control" title="The Calldata to send to fallback function of the contract." /> <OverlayTrigger
<button placement="bottom"
id="deployAndRunLLTxSendTransaction" overlay={
data-id="pluginManagerSettingsDeployAndRunLLTxSendTransaction" <Tooltip className="text-nowrap" id="deployAndRunLLTxCalldataInputTooltip">
className="btn udapp_instanceButton p-0 w-50 border-warning text-warning" <span>{"The Calldata to send to fallback function of the contract."}</span>
title="Send data to contract." </Tooltip>
onClick={sendData} }
>
<input
id="deployAndRunLLTxCalldata"
onChange={handleCalldataChange}
className="udapp_calldataInput form-control"
/>
</OverlayTrigger>
<OverlayTrigger
placement="right"
overlay={
<Tooltip className="text-nowrap" id="deployAndRunLLTxCalldataTooltip">
<span>Send data to contract.</span>
</Tooltip>
}
> >
Transact <button
</button> id="deployAndRunLLTxSendTransaction"
data-id="pluginManagerSettingsDeployAndRunLLTxSendTransaction"
className="btn udapp_instanceButton p-0 w-50 border-warning text-warning"
onClick={sendData}
>
Transact
</button>
</OverlayTrigger>
</div> </div>
</div> </div>
<div> <div>
<label id="deployAndRunLLTxError" className="text-danger my-2">{ llIError }</label> <label id="deployAndRunLLTxError" className="text-danger my-2">
{llIError}
</label>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
) );
} }

@ -3,6 +3,7 @@ import React, { useEffect, useRef, useState } from 'react'
import { BN } from 'ethereumjs-util' import { BN } from 'ethereumjs-util'
import { isNumeric } from '@remix-ui/helper' import { isNumeric } from '@remix-ui/helper'
import { ValueProps } from '../types' import { ValueProps } from '../types'
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
export function ValueUI (props: ValueProps) { export function ValueUI (props: ValueProps) {
const [sendValue, setSendValue] = useState<string>(props.sendValue) const [sendValue, setSendValue] = useState<string>(props.sendValue)
@ -49,6 +50,11 @@ export function ValueUI (props: ValueProps) {
<div className="udapp_crow"> <div className="udapp_crow">
<label className="udapp_settingsLabel" data-id="remixDRValueLabel">Value</label> <label className="udapp_settingsLabel" data-id="remixDRValueLabel">Value</label>
<div className="udapp_gasValueContainer"> <div className="udapp_gasValueContainer">
<OverlayTrigger placement={'top-start'} overlay={
<Tooltip className="text-nowrap" id="remixValueTooltip">
<span>{"Enter an amount and choose its unit"}</span>
</Tooltip>
}>
<input <input
ref={inputValue} ref={inputValue}
type="number" type="number"
@ -58,11 +64,11 @@ export function ValueUI (props: ValueProps) {
className="form-control udapp_gasNval udapp_col2" className="form-control udapp_gasNval udapp_col2"
id="value" id="value"
data-id="dandrValue" data-id="dandrValue"
title="Enter an amount and choose its unit"
onKeyPress={validateInputKey} onKeyPress={validateInputKey}
onChange={validateValue} onChange={validateValue}
value={props.sendValue} value={props.sendValue}
/> />
</OverlayTrigger>
<select name="unit" value={props.sendUnit} className="form-control p-1 udapp_gasNvalUnit udapp_col2_2 custom-select" id="unit" onChange={(e) => { props.setUnit((e.target.value) as 'ether' | 'finney' | 'gwei' | 'wei') }}> <select name="unit" value={props.sendUnit} className="form-control p-1 udapp_gasNvalUnit udapp_col2_2 custom-select" id="unit" onChange={(e) => { props.setUnit((e.target.value) as 'ether' | 'finney' | 'gwei' | 'wei') }}>
<option data-unit="wei" value='wei'>Wei</option> <option data-unit="wei" value='wei'>Wei</option>
<option data-unit="gwei" value="gwei">Gwei</option> <option data-unit="gwei" value="gwei">Gwei</option>

Loading…
Cancel
Save