Merge pull request #1212 from ethereum/multi_param

Multi params component
pull/1/head
yann300 7 years ago committed by GitHub
commit 0b802edc04
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      package.json
  2. 28
      src/app/tabs/run-tab.js
  3. 125
      src/multiParamManager.js
  4. 101
      src/universal-dapp-styles.js
  5. 59
      src/universal-dapp-ui.js
  6. 6
      test-browser/helpers/contracts.js
  7. 2
      test-browser/tests/ballot.js
  8. 6
      test-browser/tests/compiling.js
  9. 2
      test-browser/tests/units/testRecorder.js

@ -171,7 +171,7 @@
"sourcemap": "exorcist --root ../ build/app.js.map > build/app.js", "sourcemap": "exorcist --root ../ build/app.js.map > build/app.js",
"start": "npm-run-all -lpr serve watch onchange remixd", "start": "npm-run-all -lpr serve watch onchange remixd",
"test": "npm run csslint; standard && node test/index.js", "test": "npm run csslint; standard && node test/index.js",
"test-browser": "npm-run-all -lpr selenium downloadsolc make-mock-compiler serve browsertest", "test-browser": "npm-run-all -lpr selenium downloadsolc_root make-mock-compiler serve browsertest",
"watch": "watchify src/index.js -dv -p browserify-reload -o build/app.js" "watch": "watchify src/index.js -dv -p browserify-reload -o build/app.js"
} }
} }

@ -13,6 +13,7 @@ var Recorder = require('../../recorder')
var EventManager = remixLib.EventManager var EventManager = remixLib.EventManager
var addTooltip = require('../ui/tooltip') var addTooltip = require('../ui/tooltip')
var ethJSUtil = require('ethereumjs-util') var ethJSUtil = require('ethereumjs-util')
var MultiParamManager = require('../../multiParamManager')
var csjs = require('csjs-inject') var csjs = require('csjs-inject')
var css = require('./styles/run-tab-styles') var css = require('./styles/run-tab-styles')
@ -221,7 +222,7 @@ function contractDropdown (events, appAPI, appEvents, opts, instanceContainer) {
}) })
var atAddressButtonInput = yo`<input class="${css.input} ataddressinput" placeholder="Load contract from Address" title="atAddress" />` var atAddressButtonInput = yo`<input class="${css.input} ataddressinput" placeholder="Load contract from Address" title="atAddress" />`
var createButtonInput = yo`<input class="${css.input} create" placeholder="" title="Create" />`
var selectContractNames = yo`<select class="${css.contractNames}" disabled></select>` var selectContractNames = yo`<select class="${css.contractNames}" disabled></select>`
function getSelectedContract () { function getSelectedContract () {
@ -235,6 +236,7 @@ function contractDropdown (events, appAPI, appEvents, opts, instanceContainer) {
return null return null
} }
appAPI.getSelectedContract = getSelectedContract appAPI.getSelectedContract = getSelectedContract
var createPanel = yo`<div class="${css.button}"></div>`
var el = yo` var el = yo`
<div class="${css.container}"> <div class="${css.container}">
@ -242,10 +244,7 @@ function contractDropdown (events, appAPI, appEvents, opts, instanceContainer) {
${selectContractNames} ${compFails} ${selectContractNames} ${compFails}
</div> </div>
<div class="${css.buttons}"> <div class="${css.buttons}">
<div class="${css.button}"> ${createPanel}
${createButtonInput}
<div class="${css.create}" onclick=${function () { createInstance() }} >Create</div>
</div>
<div class="${css.button}"> <div class="${css.button}">
${atAddressButtonInput} ${atAddressButtonInput}
<div class="${css.atAddress}" onclick=${function () { loadFromAddress(appAPI) }}>At Address</div> <div class="${css.atAddress}" onclick=${function () { loadFromAddress(appAPI) }}>At Address</div>
@ -255,23 +254,23 @@ function contractDropdown (events, appAPI, appEvents, opts, instanceContainer) {
` `
function setInputParamsPlaceHolder () { function setInputParamsPlaceHolder () {
createButtonInput.value = '' createPanel.innerHTML = ''
if (opts.compiler.getContract && selectContractNames.selectedIndex >= 0 && selectContractNames.children.length > 0) { if (opts.compiler.getContract && selectContractNames.selectedIndex >= 0 && selectContractNames.children.length > 0) {
var ctrabi = txHelper.getConstructorInterface(getSelectedContract().contract.object.abi) var ctrabi = txHelper.getConstructorInterface(getSelectedContract().contract.object.abi)
if (ctrabi.inputs.length) { var createConstructorInstance = new MultiParamManager(0, ctrabi, (valArray, inputsValues) => {
createButtonInput.setAttribute('placeholder', txHelper.inputParametersDeclarationToString(ctrabi.inputs)) createInstance(inputsValues)
createButtonInput.removeAttribute('disabled') }, txHelper.inputParametersDeclarationToString(ctrabi.inputs), 'Deploy')
return createPanel.appendChild(createConstructorInstance.render())
} return
} else {
createPanel.innerHTML = 'No compiled contracts'
} }
createButtonInput.setAttribute('placeholder', '')
createButtonInput.setAttribute('disabled', true)
} }
selectContractNames.addEventListener('change', setInputParamsPlaceHolder) selectContractNames.addEventListener('change', setInputParamsPlaceHolder)
// ADD BUTTONS AT ADDRESS AND CREATE // ADD BUTTONS AT ADDRESS AND CREATE
function createInstance () { function createInstance (args) {
var selectedContract = getSelectedContract() var selectedContract = getSelectedContract()
if (selectedContract.contract.object.evm.bytecode.object.length === 0) { if (selectedContract.contract.object.evm.bytecode.object.length === 0) {
@ -280,7 +279,6 @@ function contractDropdown (events, appAPI, appEvents, opts, instanceContainer) {
} }
var constructor = txHelper.getConstructorInterface(selectedContract.contract.object.abi) var constructor = txHelper.getConstructorInterface(selectedContract.contract.object.abi)
var args = createButtonInput.value
txFormat.buildData(selectedContract.name, selectedContract.contract.object, opts.compiler.getContracts(), true, constructor, args, (error, data) => { txFormat.buildData(selectedContract.name, selectedContract.contract.object, opts.compiler.getContracts(), true, constructor, args, (error, data) => {
if (!error) { if (!error) {
appAPI.logMessage(`creation of ${selectedContract.name} pending...`) appAPI.logMessage(`creation of ${selectedContract.name} pending...`)

@ -0,0 +1,125 @@
'use strict'
var yo = require('yo-yo')
var css = require('./universal-dapp-styles')
class MultiParamManager {
/**
*
* @param {bool} lookupOnly
* @param {Object} funABI
* @param {Function} clickMultiCallBack
* @param {string} inputs
* @param {string} title
*
*/
constructor (lookupOnly, funABI, clickCallBack, inputs, title) {
this.lookupOnly = lookupOnly
this.funABI = funABI
this.clickCallBack = clickCallBack
this.inputs = inputs
this.title = title
}
switchMethodViewOn () {
this.contractActionsContainerSingle.style.display = 'none'
this.contractActionsContainerMulti.style.display = 'flex'
}
switchMethodViewOff () {
this.contractActionsContainerSingle.style.display = 'flex'
this.contractActionsContainerMulti.style.display = 'none'
}
createMultiFields () {
if (this.funABI.inputs) {
return yo`<div>
${this.funABI.inputs.map(function (inp) {
return yo`<div class="${css.multiArg}"><label for="${inp.name}"> ${inp.name}: </label><input placeholder="${inp.type}" id="${inp.name}" title="${inp.name}"></div>`
})}
</div>`
}
}
render () {
var title
if (this.title) {
title = this.title
} else if (this.funABI.name) {
title = this.funABI.name
} else {
title = '(fallback)'
}
var basicInputField = yo`<input></input>`
basicInputField.setAttribute('placeholder', this.inputs)
basicInputField.setAttribute('title', this.inputs)
var onClick = (domEl) => {
this.clickCallBack(this.funABI.inputs, basicInputField.value)
}
this.contractActionsContainerSingle = yo`<div class="${css.contractActionsContainerSingle}" >
<i class="fa fa-caret-right ${css.methCaret}" onclick=${() => { this.switchMethodViewOn() }} title=${title} ></i>
<button onclick=${() => { onClick() }} class="${css.instanceButton}">${title}</button>${basicInputField}
</div>`
var multiFields = this.createMultiFields()
var multiOnClick = () => {
var valArray = multiFields.querySelectorAll('input')
var ret = ''
for (var k = 0; k < valArray.length; k++) {
var el = valArray[k]
if (ret !== '') ret += ','
ret += el.value
}
this.clickCallBack(this.funABI.inputs, ret)
}
var button = yo`<button onclick=${() => { multiOnClick() }} class="${css.instanceButton}">Submit</button>`
this.contractActionsContainerMulti = yo`<div class="${css.contractActionsContainerMulti}" >
<div class="${css.contractActionsContainerMultiInner}" >
<div onclick=${() => { this.switchMethodViewOff() }} class="${css.multiHeader}">
<i class='fa fa-caret-down ${css.methCaret}'></i>
<div class="${css.multiTitle}">${title}</div>
</div>
${multiFields}
<div class="${css.group} ${css.multiArg}" >
${button}
</div>
</div>
</div>`
var contractProperty = yo`<div class="${css.contractProperty}">${this.contractActionsContainerSingle} ${this.contractActionsContainerMulti}</div>`
if (this.lookupOnly) {
contractProperty.classList.add(css.constant)
button.setAttribute('title', (title + ' - call'))
this.contractActionsContainerSingle.querySelector(`.${css.instanceButton}`).setAttribute('title', (title + ' - call'))
}
if (this.funABI.inputs && this.funABI.inputs.length > 0) {
contractProperty.classList.add(css.hasArgs)
} else {
this.contractActionsContainerSingle.querySelector('i').style.visibility = 'hidden'
basicInputField.style.display = 'none'
}
if (this.funABI.payable === true) {
contractProperty.classList.add(css.payable)
button.setAttribute('title', (title + ' - transact (payable)'))
this.contractActionsContainerSingle.querySelector('button').setAttribute('title', (title + ' - transact (payable)'))
}
if (!this.lookupOnly && this.funABI.payable === false) {
button.setAttribute('title', (title + ' - transact (not payable)'))
this.contractActionsContainerSingle.querySelector('button').setAttribute('title', (title + ' - transact (not payable)'))
}
return contractProperty
}
}
module.exports = MultiParamManager

@ -19,6 +19,7 @@ var css = csjs`
word-break: break-word; word-break: break-word;
line-height: initial; line-height: initial;
overflow: visible; overflow: visible;
margin-bottom: 10px;
} }
.titleLine { .titleLine {
display: flex; display: flex;
@ -37,6 +38,8 @@ var css = csjs`
${styles.rightPanel.runTab.box_Instance}; ${styles.rightPanel.runTab.box_Instance};
margin-bottom: 10px; margin-bottom: 10px;
padding: 10px 15px 15px 15px; padding: 10px 15px 15px 15px;
position: relative;
overflow: visible;
} }
.instance .title:before { .instance .title:before {
content: "\\25BE"; content: "\\25BE";
@ -55,13 +58,24 @@ var css = csjs`
.instance.hidesub .udappClose { .instance.hidesub .udappClose {
display: flex; display: flex;
} }
.methCaret {
margin-right: 5px;
cursor: pointer;
font-size: 12px;
padding-top: 5px;
vertical-align: top;
}
.group:after {
content: "";
display: table;
clear: both;
}
.buttonsContainer { .buttonsContainer {
margin-top: 2%; margin-top: 2%;
display: flex; display: flex;
overflow: hidden; overflow: hidden;
} }
.contractActions { .contractActions {
display: flex;
} }
.instanceButton {} .instanceButton {}
.closeIcon { .closeIcon {
@ -75,10 +89,13 @@ var css = csjs`
.contractProperty { .contractProperty {
overflow: auto; overflow: auto;
margin-bottom: 0.4em; margin-bottom: 0.4em;
width:100%;
} }
.contractProperty.hasArgs input { .contractProperty.hasArgs input {
width: 75%; min-width: 200px;
padding: .36em; padding: .36em;
border-radius: 5px;
width: 70%;
} }
.contractProperty button { .contractProperty button {
${styles.rightPanel.runTab.button_Create} ${styles.rightPanel.runTab.button_Create}
@ -101,10 +118,9 @@ var css = csjs`
margin:0; margin:0;
word-break: inherit; word-break: inherit;
outline: none; outline: none;
width: inherit;
} }
.contractProperty input { .contractProperty input {
display: none; width: 75%
} }
.contractProperty > .value { .contractProperty > .value {
box-sizing: border-box; box-sizing: border-box;
@ -113,6 +129,83 @@ var css = csjs`
color: ${styles.appProperties.mainText_Color}; color: ${styles.appProperties.mainText_Color};
margin-left: 4px; margin-left: 4px;
} }
.contractActionsContainer {
display: flex;
width: 98%;
}
.contractActionsContainerSingle {
display: flex;
width: 100%;
}
.contractActionsContainerMulti {
display:none;
width: 100%;
}
.contractActionsContainerMultiInner {
margin-bottom: 10px;
border-bottom: 1px solid ${styles.appProperties.solidBorderBox_BorderColor};
padding: 0px 5px 5px 0px;
background-color: ${styles.appProperties.primary_BackgroundColor};
width: 100%;
}
.multiHeader {
text-align: left;
font-size: 10px;
margin-bottom: 5px;
font-weight: bold;
}
.multiTitle {
${styles.rightPanel.runTab.button_Create}
border-radius: 3px;
display: inline-block;
width: 95%;
font-size: 10px;
height: 25px;
padding-left: 20px;
font-weight: normal;
line-height: 25px;
cursor: default;
}
.contractProperty.constant .multiTitle {
${styles.rightPanel.runTab.button_Constant}
border-radius: 3px;
display: inline-block;
width: 95%;
font-size: 10px;
height: 25px;
padding-left: 20px;
font-weight: normal;
line-height: 25px;
cursor: default;
}
.multiArg {
margin-bottom: 8px;
}
.multiArg input{
padding: 5px;
}
.multiArg label {
float: left;
margin-right: 6px;
font-size: 10px;
width: 20%;
}
.multiArg button {
border-radius: 3px;
float: right;
margin-right: 5%;
font-size: 10px;
border-width: 1px;
width: inherit;
}
.multiHeader button {
display: inline-block;
width: 94%;
}
.hasArgs .multiArg input {
border-left: 1px solid #dddddd;
}
.hasArgs input { .hasArgs input {
display: block; display: block;
border: 1px solid #dddddd; border: 1px solid #dddddd;

@ -6,6 +6,7 @@ var yo = require('yo-yo')
var helper = require('./lib/helper') var helper = require('./lib/helper')
var copyToClipboard = require('./app/ui/copy-to-clipboard') var copyToClipboard = require('./app/ui/copy-to-clipboard')
var css = require('./universal-dapp-styles') var css = require('./universal-dapp-styles')
var MultiParamManager = require('./multiParamManager')
/* /*
trigger debugRequested trigger debugRequested
@ -91,63 +92,23 @@ UniversalDAppUI.prototype.getCallButton = function (args) {
// args.contractName [constr only] // args.contractName [constr only]
var lookupOnly = args.funABI.constant var lookupOnly = args.funABI.constant
var inputs = self.udapp.getInputs(args.funABI) var outputOverride = yo`<div class=${css.value}></div>` // show return value
var inputField = yo`<input></input>`
inputField.setAttribute('placeholder', inputs)
inputField.setAttribute('title', inputs)
var outputOverride = yo`<div class=${css.value}></div>` function clickButton (valArr, inputsValues) {
self.udapp.call(true, args, inputsValues, lookupOnly, (decoded) => {
var title
if (args.funABI.name) {
title = args.funABI.name
} else {
title = '(fallback)'
}
var button = yo`<button onclick=${clickButton} class="${css.instanceButton}"></button>`
button.classList.add(css.call)
button.setAttribute('title', title)
button.innerHTML = title
function clickButton () {
self.udapp.call(true, args, inputField.value, lookupOnly, (decoded) => {
outputOverride.innerHTML = '' outputOverride.innerHTML = ''
outputOverride.appendChild(decoded) outputOverride.appendChild(decoded)
}) })
} }
var contractProperty = yo`<div class="${css.contractProperty} ${css.buttonsContainer}"></div>` var multiParamManager = new MultiParamManager(lookupOnly, args.funABI, (valArray, inputsValues, domEl) => {
var contractActions = yo`<div class="${css.contractActions}" ></div>` clickButton(valArray, inputsValues, domEl)
}, self.udapp.getInputs(args.funABI))
contractProperty.appendChild(contractActions)
contractActions.appendChild(button)
if (inputs.length) {
contractActions.appendChild(inputField)
}
if (lookupOnly) {
contractProperty.appendChild(outputOverride)
}
if (lookupOnly) {
contractProperty.classList.add(css.constant)
button.setAttribute('title', (title + ' - call'))
}
if (args.funABI.inputs && args.funABI.inputs.length > 0) {
contractProperty.classList.add(css.hasArgs)
}
if (args.funABI.payable === true) { var contractActionsContainer = yo`<div class="${css.contractActionsContainer}" >${multiParamManager.render()}</div>`
contractProperty.classList.add(css.payable) contractActionsContainer.appendChild(outputOverride)
button.setAttribute('title', (title + ' - transact (payable)'))
}
if (!lookupOnly && args.funABI.payable === false) {
button.setAttribute('title', (title + ' - transact (not payable)'))
}
return contractProperty return contractActionsContainer
} }
module.exports = UniversalDAppUI module.exports = UniversalDAppUI

@ -42,8 +42,8 @@ function getCompiledContracts (browser, compiled, callback) {
function createContract (browser, inputParams, callback) { function createContract (browser, inputParams, callback) {
browser.click('.runView') browser.click('.runView')
.setValue('input.create', inputParams, function () { .setValue('div[class^="contractActionsContainerSingle"] input', inputParams, function () {
browser.click('#runTabView div[class^="create"]').perform(function () { callback() }) browser.click('#runTabView button[class^="instanceButton"]').perform(function () { callback() })
}) })
} }
@ -100,7 +100,7 @@ function clickFunction (fnFullName, expectedInput) {
function verifyCallReturnValue (browser, address, checks, done) { function verifyCallReturnValue (browser, address, checks, done) {
browser.execute(function (address) { browser.execute(function (address) {
var nodes = document.querySelectorAll('#instance' + address + ' div[class^="contractProperty"] div[class^="value"]') var nodes = document.querySelectorAll('#instance' + address + ' div[class^="contractActionsContainer"] div[class^="value"]')
var ret = [] var ret = []
for (var k = 0; k < nodes.length; k++) { for (var k = 0; k < nodes.length; k++) {
var text = nodes[k].innerText ? nodes[k].innerText : nodes[k].textContent var text = nodes[k].innerText ? nodes[k].innerText : nodes[k].textContent

@ -35,7 +35,7 @@ function runTests (browser, testData) {
}) })
}).click('.runView') }).click('.runView')
.setValue('input[placeholder="uint8 _numProposals"]', '1') .setValue('input[placeholder="uint8 _numProposals"]', '1')
.click('#runTabView div[class^="create"]') .click('#runTabView button[class^="instanceButton"]')
.testFunction('delegate - transact (not payable)', '0x0571a2439ea58bd349dd130afb8aff62a33af14c06de0dbc3928519bdf13ce2e', .testFunction('delegate - transact (not payable)', '0x0571a2439ea58bd349dd130afb8aff62a33af14c06de0dbc3928519bdf13ce2e',
`[vm]\nfrom:0xca3...a733c\nto:Ballot.delegate(address) 0x692...77b3a\nvalue:0 wei\ndata:0x5c1...4d2db\nlogs:0\nhash:0x057...3ce2e`, `[vm]\nfrom:0xca3...a733c\nto:Ballot.delegate(address) 0x692...77b3a\nvalue:0 wei\ndata:0x5c1...4d2db\nlogs:0\nhash:0x057...3ce2e`,
{types: 'address to', values: '"0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db"'}, null, null) {types: 'address to', values: '"0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db"'}, null, null)

@ -38,7 +38,7 @@ function runTests (browser) {
function testSimpleContract (browser, callback) { function testSimpleContract (browser, callback) {
contractHelper.testContracts(browser, 'Untitled.sol', sources[0]['browser/Untitled.sol'], ['TestContract'], function () { contractHelper.testContracts(browser, 'Untitled.sol', sources[0]['browser/Untitled.sol'], ['TestContract'], function () {
browser.click('.runView') browser.click('.runView')
.click('#runTabView div[class^="create"]') .click('#runTabView button[class^="instanceButton"]')
.pause(500) .pause(500)
.click('#runTabView .instance div[class^="title"]') .click('#runTabView .instance div[class^="title"]')
.click('#runTabView .instance div[class^="title"]') .click('#runTabView .instance div[class^="title"]')
@ -68,7 +68,7 @@ function testSimpleContract (browser, callback) {
function testReturnValues (browser, callback) { function testReturnValues (browser, callback) {
contractHelper.testContracts(browser, 'returnValues.sol', sources[1]['browser/returnValues.sol'], ['testReturnValues'], function () { contractHelper.testContracts(browser, 'returnValues.sol', sources[1]['browser/returnValues.sol'], ['testReturnValues'], function () {
browser.click('.runView') browser.click('.runView')
.click('#runTabView div[class^="create"]') .click('#runTabView button[class^="instanceButton"]')
.pause(500) .pause(500)
.testFunction('retunValues1 - transact (not payable)', .testFunction('retunValues1 - transact (not payable)',
'0x79dc928d149d2ade02ab610a8ae290636222d034d4adce0bb08a68401e3d1f7f', '0x79dc928d149d2ade02ab610a8ae290636222d034d4adce0bb08a68401e3d1f7f',
@ -106,7 +106,7 @@ function testReturnValues (browser, callback) {
function testInputValues (browser, callback) { function testInputValues (browser, callback) {
contractHelper.testContracts(browser, 'inputValues.sol', sources[2]['browser/inputValues.sol'], ['test'], function () { contractHelper.testContracts(browser, 'inputValues.sol', sources[2]['browser/inputValues.sol'], ['test'], function () {
browser.click('.runView') browser.click('.runView')
.click('#runTabView div[class^="create"]') .click('#runTabView button[class^="instanceButton"]')
.pause(500) .pause(500)
.testFunction('inputValue1 - transact (not payable)', .testFunction('inputValue1 - transact (not payable)',
'0x917a873d27d105213eaf5461e14780387ccceb66fed574f8432d1963917832ae', '0x917a873d27d105213eaf5461e14780387ccceb66fed574f8432d1963917832ae',

@ -14,7 +14,7 @@ module.exports = {
.clickFunction('getInt - call') .clickFunction('getInt - call')
.clickFunction('getAddress - call') .clickFunction('getAddress - call')
.clickFunction('getFromLib - call') .clickFunction('getFromLib - call')
.waitForElementPresent('div[class^="contractProperty"] div[class^="value"]') .waitForElementPresent('div[class^="contractActionsContainer"] div[class^="value"] ul')
.perform((client, done) => { .perform((client, done) => {
contractHelper.verifyCallReturnValue(browser, '0x35ef07393b57464e93deb59175ff72e6499450cf', ['0: uint256: 1', '0: uint256: 3456', '0: address: 0x35ef07393b57464e93deb59175ff72e6499450cf'], () => { contractHelper.verifyCallReturnValue(browser, '0x35ef07393b57464e93deb59175ff72e6499450cf', ['0: uint256: 1', '0: uint256: 3456', '0: address: 0x35ef07393b57464e93deb59175ff72e6499450cf'], () => {
done() done()

Loading…
Cancel
Save