Merge branch 'master' into intl

pull/5370/head
drafish 2 years ago
commit 7a85086944
  1. 2
      apps/remix-ide-e2e/src/commands/clickElementAtPosition.ts
  2. 7
      apps/remix-ide-e2e/src/tests/solidityUnittests.test.ts
  3. 46
      libs/remix-ui/checkbox/src/lib/remix-ui-checkbox.tsx
  4. 13
      libs/remix-ui/panel/src/lib/plugins/panel-header.tsx
  5. 66
      libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx
  6. 59
      libs/remix-ui/solidity-compiler/src/lib/contract-selection.tsx
  7. 156
      libs/remix-ui/solidity-unit-testing/src/lib/solidity-unit-testing.tsx
  8. 16
      libs/remix-ui/static-analyser/src/lib/Button/StaticAnalyserButton.tsx
  9. 2
      libs/remix-ui/static-analyser/src/lib/remix-ui-static-analyser.tsx
  10. 115
      libs/remix-ui/workspace/src/lib/components/file-explorer-menu.tsx
  11. 3
      libs/remix-ui/workspace/src/lib/types/index.ts

@ -14,7 +14,7 @@ class ClickElement extends EventEmitter {
} }
function _clickElement (browser: NightwatchBrowser, cssSelector: string, index: number, forceSelectIfUnselected: boolean, cb: VoidFunction) { function _clickElement (browser: NightwatchBrowser, cssSelector: string, index: number, forceSelectIfUnselected: boolean, cb: VoidFunction) {
browser.waitForElementPresent(cssSelector) browser.waitForElementPresent(cssSelector, 5000)
.execute(function (cssSelector: string, index: number, forceSelectIfUnselected: boolean) { .execute(function (cssSelector: string, index: number, forceSelectIfUnselected: boolean) {
const elem = document.querySelectorAll(cssSelector)[index] as HTMLElement const elem = document.querySelectorAll(cssSelector)[index] as HTMLElement
if (forceSelectIfUnselected) { if (forceSelectIfUnselected) {

@ -152,9 +152,12 @@ module.exports = {
.waitForElementPresent('*[data-id="verticalIconsKindfilePanel"]') .waitForElementPresent('*[data-id="verticalIconsKindfilePanel"]')
.addFile('myTests/simple_storage_test.sol', sources[0]['tests/simple_storage_test.sol']) .addFile('myTests/simple_storage_test.sol', sources[0]['tests/simple_storage_test.sol'])
.clickLaunchIcon('solidityUnitTesting') .clickLaunchIcon('solidityUnitTesting')
.clearValue('*[data-id="uiPathInput"]') .execute(() => {
.setValue('*[data-id="uiPathInput"]', 'myTests') const myQuery: any = document.getElementById('utPath')
myQuery.value = 'myTests'
})
.click('*[data-id="testTabGenerateTestFolder"]') .click('*[data-id="testTabGenerateTestFolder"]')
.saveScreenshot('./reports/screenshots/changeCurrentPathg3.png')
.clickElementAtPosition('.singleTest', 0, { forceSelectIfUnselected: true }) .clickElementAtPosition('.singleTest', 0, { forceSelectIfUnselected: true })
.scrollAndClick('*[data-id="testTabRunTestsTabRunAction"]') .scrollAndClick('*[data-id="testTabRunTestsTabRunAction"]')
.waitForElementPresent('*[data-id="testTabSolidityUnitTestsOutputheader"]', 60000) .waitForElementPresent('*[data-id="testTabSolidityUnitTestsOutputheader"]', 60000)

@ -1,5 +1,8 @@
import React, { CSSProperties } from 'react' //eslint-disable-line import React, { CSSProperties } from 'react' //eslint-disable-line
import { OverlayProps, OverlayTrigger, OverlayTriggerProps, Tooltip } from 'react-bootstrap'// eslint-disable-line
import './remix-ui-checkbox.css' import './remix-ui-checkbox.css'
type Placement = import('react-overlays/usePopper').Placement;
/* eslint-disable-next-line */ /* eslint-disable-next-line */
export interface RemixUiCheckboxProps { export interface RemixUiCheckboxProps {
@ -15,6 +18,7 @@ export interface RemixUiCheckboxProps {
title?: string title?: string
visibility?: string visibility?: string
display?: string display?: string
tooltipPlacement?: Placement
} }
export const RemixUiCheckbox = ({ export const RemixUiCheckbox = ({
@ -29,24 +33,34 @@ export const RemixUiCheckbox = ({
categoryId, categoryId,
title, title,
visibility, visibility,
display = 'flex' display = 'flex',
tooltipPlacement = 'right-start'
}: RemixUiCheckboxProps) => { }: RemixUiCheckboxProps) => {
return ( return (
<div className="listenOnNetwork_2A0YE0 custom-control custom-checkbox" title={title} style={{ display: display, alignItems: 'center', visibility: visibility } as CSSProperties } onClick={onClick}> <OverlayTrigger
<input placement={tooltipPlacement}
id={id} overlay={
type={inputType} <Tooltip id={`${name}Tooltip`}>
onChange={onChange} <span>{title}</span>
style={{ verticalAlign: 'bottom' }} </Tooltip>
name={name} }
className="custom-control-input" >
checked={checked} <div className="listenOnNetwork_2A0YE0 custom-control custom-checkbox" style={{ display: display, alignItems: 'center', visibility: visibility } as CSSProperties } onClick={onClick}>
/> <input
<label className="form-check-label custom-control-label" id={`heading${categoryId}`} style={{ paddingTop: '0.15rem' }}> id={id}
{name ? <div className="font-weight-bold">{itemName}</div> : ''} type={inputType}
{label} onChange={onChange}
</label> style={{ verticalAlign: 'bottom' }}
</div> name={name}
className="custom-control-input"
checked={checked}
/>
<label className="form-check-label custom-control-label" id={`heading${categoryId}`} style={{ paddingTop: '0.15rem' }}>
{name ? <div className="font-weight-bold">{itemName}</div> : ''}
{label}
</label>
</div>
</OverlayTrigger>
) )
} }

@ -3,6 +3,7 @@ import React, { useEffect, useRef, useState } from 'react' // eslint-disable-lin
import { FormattedMessage } from 'react-intl' import { FormattedMessage } from 'react-intl'
import { PluginRecord } from '../types' import { PluginRecord } from '../types'
import './panel.css' import './panel.css'
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
export interface RemixPanelProps { export interface RemixPanelProps {
plugins: Record<string, PluginRecord>; plugins: Record<string, PluginRecord>;
@ -35,9 +36,15 @@ const RemixUIPanelHeader = (props: RemixPanelProps) => {
<div className="d-flex flex-row"> <div className="d-flex flex-row">
{plugin?.profile?.maintainedBy?.toLowerCase() === "remix" && (<i aria-hidden="true" className="text-success mt-1 px-1 fas fa-check" title="Maintained by Remix"></i>)} {plugin?.profile?.maintainedBy?.toLowerCase() === "remix" && (<i aria-hidden="true" className="text-success mt-1 px-1 fas fa-check" title="Maintained by Remix"></i>)}
</div> </div>
<div className="swapitHeaderInfoSection d-flex justify-content-between" data-id='swapitHeaderInfoSectionId' onClick={toggleClass} title="Plugin info"> <OverlayTrigger overlay={
<i className={`px-2 ml-2 pt-1 pb-4 ${!toggleExpander ? 'fas fa-angle-right' : 'fas fa-angle-down bg-light'}`} aria-hidden="true"></i> <Tooltip className="text-nowrap" id="pluginInfoTooltip">
</div> <span>Plugin info</span>
</Tooltip>
} placement={'right-end'}>
<div className="swapitHeaderInfoSection d-flex justify-content-between" data-id='swapitHeaderInfoSectionId' onClick={toggleClass}>
<i className={`px-2 ml-2 pt-1 pb-4 ${!toggleExpander ? 'fas fa-angle-right' : 'fas fa-angle-down bg-light'}`} aria-hidden="true"></i>
</div>
</OverlayTrigger>
</div> </div>
</div> </div>
<div className={`bg-light mx-3 mb-2 p-3 pt-1 border-bottom flex-column ${toggleExpander ? "d-flex" : "d-none"}`}> <div className={`bg-light mx-3 mb-2 p-3 pt-1 border-bottom flex-column ${toggleExpander ? "d-flex" : "d-none"}`}>

@ -742,15 +742,26 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
<label className="remixui_compilerLabel form-check-label" htmlFor="versionSelector"> <label className="remixui_compilerLabel form-check-label" htmlFor="versionSelector">
<FormattedMessage id='solidity.compiler' defaultMessage='Compiler' /> <FormattedMessage id='solidity.compiler' defaultMessage='Compiler' />
</label> </label>
<span <OverlayTrigger
className="far fa-plus border-0 p-0 ml-3" placement="top"
onClick={() => promptCompiler()} overlay={
title={intl.formatMessage({ <Tooltip id="promptCompilerTooltip" className="text-nowrap">
id: 'solidity.addACustomCompilerWithURL', <span><FormattedMessage id='solidity.addACustomCompilerWithURL' defaultMessage='Add a custom compiler with URL' /></span>
defaultMessage: "Add a custom compiler with URL", </Tooltip>
})} }
></span> >
<span className="fa fa-file-text-o border-0 p-0 ml-2" onClick={() => showCompilerLicense()} title="See compiler license"></span> <span className="far fa-plus border-0 p-0 ml-3" onClick={() => promptCompiler()}></span>
</OverlayTrigger>
<OverlayTrigger
placement="top"
overlay={
<Tooltip id="showCompilerTooltip" className="text-nowrap">
<span>{"See compiler license"}</span>
</Tooltip>
}
>
<span className="fa fa-file-text-o border-0 p-0 ml-2" onClick={() => showCompilerLicense()}></span>
</OverlayTrigger>
<select value={state.selectedVersion || state.defaultVersion} onChange={(e) => handleLoadVersion(e.target.value)} className="custom-select" id="versionSelector" disabled={state.allversions.length <= 0}> <select value={state.selectedVersion || state.defaultVersion} onChange={(e) => handleLoadVersion(e.target.value)} className="custom-select" id="versionSelector" disabled={state.allversions.length <= 0}>
{state.allversions.length <= 0 && <option disabled data-id={state.selectedVersion === state.defaultVersion ? 'selected' : ''}>{state.defaultVersion}</option>} {state.allversions.length <= 0 && <option disabled data-id={state.selectedVersion === state.defaultVersion ? 'selected' : ''}>{state.defaultVersion}</option>}
{state.allversions.length <= 0 && <option disabled data-id={state.selectedVersion === 'builtin' ? 'selected' : ''}>builtin</option>} {state.allversions.length <= 0 && <option disabled data-id={state.selectedVersion === 'builtin' ? 'selected' : ''}>builtin</option>}
@ -846,10 +857,19 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
<label className="remixui_compilerLabel form-check-label" htmlFor="compilierLanguageSelector"> <label className="remixui_compilerLabel form-check-label" htmlFor="compilierLanguageSelector">
<FormattedMessage id='solidity.language' defaultMessage='Language' /> <FormattedMessage id='solidity.language' defaultMessage='Language' />
</label> </label>
<select onChange={(e) => handleLanguageChange(e.target.value)} disabled={state.useFileConfiguration} value={state.language} className="custom-select" id="compilierLanguageSelector" title="Language specification available from Compiler >= v0.5.7"> <OverlayTrigger
<option data-id={state.language === 'Solidity' ? 'selected' : ''} value='Solidity'>Solidity</option> placement="right-start"
<option data-id={state.language === 'Yul' ? 'selected' : ''} value='Yul'>Yul</option> overlay={
</select> <Tooltip id="compilerLabelTooltip" className="text-nowrap">
<span>{'Language specification available from Compiler >= v0.5.7'}</span>
</Tooltip>
}
>
<select onChange={(e) => handleLanguageChange(e.target.value)} disabled={state.useFileConfiguration} value={state.language} className="custom-select" id="compilierLanguageSelector">
<option data-id={state.language === 'Solidity' ? 'selected' : ''} value='Solidity'>Solidity</option>
<option data-id={state.language === 'Yul' ? 'selected' : ''} value='Yul'>Yul</option>
</select>
</OverlayTrigger>
</div> </div>
<div className="mb-2 ml-4"> <div className="mb-2 ml-4">
<label className="remixui_compilerLabel form-check-label" htmlFor="evmVersionSelector"> <label className="remixui_compilerLabel form-check-label" htmlFor="evmVersionSelector">
@ -886,11 +906,21 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
</label> </label>
</div> </div>
<div className={`pt-2 ml-4 ml-2 align-items-start justify-content-between d-flex`}> <div className={`pt-2 ml-4 ml-2 align-items-start justify-content-between d-flex`}>
{(!showFilePathInput && state.useFileConfiguration) && <span {(!showFilePathInput && state.useFileConfiguration) && <OverlayTrigger
title="Click to open the config file" placement="bottom"
onClick={configFilePath === '' ? () => { } : async () => { await openFile() }} overlay={
className="py-2 remixui_compilerConfigPath" <Tooltip id="configfileTooltip" className="text-nowrap">
>{configFilePath === '' ? 'No file selected.' : configFilePath}</span>} <span>
Click to open the config file
</span>
</Tooltip>
}
>
<span
onClick={configFilePath === '' ? () => { } : async () => { await openFile() }}
className="py-2 remixui_compilerConfigPath"
>{configFilePath === '' ? 'No file selected.' : configFilePath}</span>
</OverlayTrigger>}
{(!showFilePathInput && !state.useFileConfiguration) && <span className="py-2 text-secondary">{configFilePath}</span>} {(!showFilePathInput && !state.useFileConfiguration) && <span className="py-2 text-secondary">{configFilePath}</span>}
<input <input
ref={configFilePathInput} ref={configFilePathInput}

@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react' // eslint-disable-line import React, { useState, useEffect, Fragment } from 'react' // eslint-disable-line
import { FormattedMessage, useIntl } from 'react-intl' import { FormattedMessage, useIntl } from 'react-intl'
import { ContractSelectionProps } from './types' import { ContractSelectionProps } from './types'
import { PublishToStorage } from '@remix-ui/publish-to-storage' // eslint-disable-line import { PublishToStorage } from '@remix-ui/publish-to-storage' // eslint-disable-line
@ -6,6 +6,7 @@ import { TreeView, TreeViewItem } from '@remix-ui/tree-view' // eslint-disable-l
import { CopyToClipboard } from '@remix-ui/clipboard' // eslint-disable-line import { CopyToClipboard } from '@remix-ui/clipboard' // eslint-disable-line
import './css/style.css' import './css/style.css'
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
export const ContractSelection = (props: ContractSelectionProps) => { export const ContractSelection = (props: ContractSelectionProps) => {
const { api, compiledFileName, contractsDetails, contractList, modal } = props const { api, compiledFileName, contractsDetails, contractList, modal } = props
@ -198,16 +199,56 @@ export const ContractSelection = (props: ContractSelectionProps) => {
</select> </select>
</div> </div>
<article className="mt-2 pb-0"> <article className="mt-2 pb-0">
<button id="publishOnIpfs" className="btn btn-secondary btn-block" title="Publish on Ipfs" onClick={() => { handlePublishToStorage('ipfs') }}> <button id="publishOnIpfs" className="btn btn-secondary btn-block" onClick={() => { handlePublishToStorage('ipfs') }}>
<span><FormattedMessage id='solidity.publishOn' defaultMessage='Publish on' /> Ipfs</span> <OverlayTrigger
<img id="ipfsLogo" className="remixui_storageLogo ml-2" src="assets/img/ipfs.webp" /> placement="right-start"
overlay={
<Tooltip
id="publishOnIpfsTooltip"
className="text-nowrap"
>
<span><FormattedMessage id='solidity.publishOn' defaultMessage='Publish on' /> Ipfs</span>
</Tooltip>
}
>
<span>
<span><FormattedMessage id='solidity.publishOn' defaultMessage='Publish on' /> Ipfs</span>
<img id="ipfsLogo" className="remixui_storageLogo ml-2" src="assets/img/ipfs.webp" />
</span>
</OverlayTrigger>
</button> </button>
<button id="publishOnSwarm" className="btn btn-secondary btn-block" title="Publish on Swarm" onClick={() => { handlePublishToStorage('swarm') }}> <button id="publishOnSwarm" className="btn btn-secondary btn-block" onClick={() => { handlePublishToStorage('swarm') }}>
<span><FormattedMessage id='solidity.publishOn' defaultMessage='Publish on' /> Swarm</span> <OverlayTrigger
<img id="swarmLogo" className="remixui_storageLogo ml-2" src="assets/img/swarm.webp" /> placement="right-start"
overlay={
<Tooltip
id="publishOnSwarmTooltip"
className="text-nowrap"
>
<span><FormattedMessage id='solidity.publishOn' defaultMessage='Publish on' /> Swarm</span>
</Tooltip>
}
>
<span>
<span><FormattedMessage id='solidity.publishOn' defaultMessage='Publish on' /> Swarm</span>
<img id="swarmLogo" className="remixui_storageLogo ml-2" src="assets/img/swarm.webp" />
</span>
</OverlayTrigger>
</button> </button>
<button data-id="compilation-details" className="btn btn-secondary btn-block" title="Display Contract Details" onClick={() => { details() }}> <button data-id="compilation-details" className="btn btn-secondary btn-block" onClick={() => { details() }}>
<FormattedMessage id='solidity.compilationDetails' defaultMessage='Compilation Details' /> <OverlayTrigger
placement="right-start"
overlay={
<Tooltip
id="CompilationDetailsTooltip"
className="text-nowrap"
>
<span>Display Contract Details</span>
</Tooltip>
}
>
<span><FormattedMessage id='solidity.compilationDetails' defaultMessage='Compilation Details' /></span>
</OverlayTrigger>
</button> </button>
{/* Copy to Clipboard */} {/* Copy to Clipboard */}
<div className="remixui_contractHelperButtons"> <div className="remixui_contractHelperButtons">

@ -2,6 +2,7 @@ import React, { useState, useRef, useEffect, ReactElement } from 'react' // esli
import * as semver from 'semver' import * as semver from 'semver'
import { eachOfSeries } from 'async' // eslint-disable-line import { eachOfSeries } from 'async' // eslint-disable-line
import type Web3 from 'web3' import type Web3 from 'web3'
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
import { canUseWorker, urlFromVersion } from '@remix-project/remix-solidity' import { canUseWorker, urlFromVersion } from '@remix-project/remix-solidity'
import { Renderer } from '@remix-ui/renderer' // eslint-disable-line import { Renderer } from '@remix-ui/renderer' // eslint-disable-line
import { Toaster } from '@remix-ui/toaster' // eslint-disable-line import { Toaster } from '@remix-ui/toaster' // eslint-disable-line
@ -296,20 +297,26 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
let label let label
if (index > -1) { if (index > -1) {
const className = "alert-danger d-inline-block mb-1 mr-1 p-1 failed_" + runningTestFileName const className = "alert-danger d-inline-block mb-1 mr-1 p-1 failed_" + runningTestFileName
label = (<div label = (<OverlayTrigger placement={'right'} overlay={
<Tooltip className="text-nowrap" id="info-recorder">
<span>At least one contract test failed</span>
</Tooltip>
}><div
className={className} className={className}
title="At least one contract test failed"
> >
FAIL FAIL
</div>) </div></OverlayTrigger>)
} else { } else {
const className = "alert-success d-inline-block mb-1 mr-1 p-1 passed_" + runningTestFileName const className = "alert-success d-inline-block mb-1 mr-1 p-1 passed_" + runningTestFileName
label = (<div label = (<OverlayTrigger placement={'top-end'} overlay={
<Tooltip className="text-nowrap" id="info-recorder">
<span>All contract tests passed</span>
</Tooltip>
}><div
className={className} className={className}
title="All contract tests passed"
> >
PASS PASS
</div>) </div></OverlayTrigger>)
} }
// show contract and file name with label // show contract and file name with label
const ContractCard: ReactElement = ( const ContractCard: ReactElement = (
@ -335,8 +342,12 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
if (test.debugTxHash) { if (test.debugTxHash) {
const { web3, debugTxHash } = test const { web3, debugTxHash } = test
debugBtn = ( debugBtn = (
<div id={test.value.replaceAll(' ', '_')} className="btn border btn btn-sm ml-1" style={{ cursor: 'pointer' }} title="Start debugging" onClick={() => startDebug(debugTxHash, web3)}> <div id={test.value.replaceAll(' ', '_')} className="btn border btn btn-sm ml-1" style={{ cursor: 'pointer' }} onClick={() => startDebug(debugTxHash, web3)}>
<i className="fas fa-bug"></i> <OverlayTrigger placement={'top-start'} overlay={
<Tooltip className="text-nowrap" id="info-recorder">
<span>Start debugging</span>
</Tooltip>
}><i className="fas fa-bug"></i></OverlayTrigger>
</div> </div>
) )
} }
@ -663,57 +674,102 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
}) })
} }
</datalist> </datalist>
<input <OverlayTrigger
list="utPathList" placement="top-end"
className="inputFolder custom-select" overlay={
id="utPath" <Tooltip className="text-nowrap" id="uiPathInputtooltip">
data-id="uiPathInput" <span>{"Press 'Enter' to change the path for test files."}</span>
name="utPath" </Tooltip>
value={inputPathValue} }
title="Press 'Enter' to change the path for test files."
style={{ backgroundImage: "var(--primary)" }}
onKeyDown={() => { if (inputPathValue === '/') setInputPathValue('')} }
onChange={handleTestDirInput}
onClick = {() => { if (inputPathValue === '/') setInputPathValue('')} }
/>
<button
className="btn border ml-2"
data-id="testTabGenerateTestFolder"
title="Create a test folder"
disabled={disableCreateButton}
onClick={handleCreateFolder}
> >
Create <input
</button> list="utPathList"
className="inputFolder custom-select"
id="utPath"
data-id="uiPathInput"
name="utPath"
value={inputPathValue}
style={{ backgroundImage: "var(--primary)" }}
onKeyDown={() => { if (inputPathValue === '/') setInputPathValue('')} }
onChange={handleTestDirInput}
onClick = {() => { if (inputPathValue === '/') setInputPathValue('')} }
/>
</OverlayTrigger>
<OverlayTrigger
placement="top-end"
overlay={
<Tooltip className="text-nowrap" id="uiPathInputButtontooltip">
<span>Create a test folder</span>
</Tooltip>
}
>
<button
className="btn border ml-2"
data-id="testTabGenerateTestFolder"
disabled={disableCreateButton}
onClick={handleCreateFolder}
>
Create
</button>
</OverlayTrigger>
</div> </div>
</div> </div>
</div> </div>
<div> <div>
<div className="d-flex p-2"> <div className="d-flex p-2">
<button <OverlayTrigger overlay={
className="btn border w-50" <Tooltip id="generateTestsButtontooltip" className="text-nowrap">
data-id="testTabGenerateTestFile" <span>Generate a sample test file</span>
title="Generate a sample test file" </Tooltip>
disabled={disableGenerateButton} } placement={'bottom-start'}>
onClick={async () => { <button
await testTabLogic.generateTestFile((err:any) => { if (err) setToasterMsg(err)}) // eslint-disable-line @typescript-eslint/no-explicit-any className="btn border w-50"
await updateForNewCurrent() data-id="testTabGenerateTestFile"
}} disabled={disableGenerateButton}
> onClick={async () => {
Generate await testTabLogic.generateTestFile((err:any) => { if (err) setToasterMsg(err)}) // eslint-disable-line @typescript-eslint/no-explicit-any
</button> await updateForNewCurrent()
<a className="btn border text-decoration-none pr-0 d-flex w-50 ml-2" title="Check out documentation." target="__blank" href="https://remix-ide.readthedocs.io/en/latest/unittesting.html#test-directory"> }}
>
Generate
</button>
</OverlayTrigger>
<OverlayTrigger overlay={
<Tooltip id="generateTestsLinktooltip" className="text-nowrap">
<span>Check out documentation.</span>
</Tooltip>
} placement={'bottom-start'}>
<a className="btn border text-decoration-none pr-0 d-flex w-50 ml-2" target="__blank" href="https://remix-ide.readthedocs.io/en/latest/unittesting.html#test-directory">
<label className="btn p-1 ml-2 m-0">How to use...</label> <label className="btn p-1 ml-2 m-0">How to use...</label>
</a> </a>
</OverlayTrigger>
</div> </div>
<div className="d-flex p-2"> <div className="d-flex p-2">
<button id="runTestsTabRunAction" title={runButtonTitle} data-id="testTabRunTestsTabRunAction" className="w-50 btn btn-primary" disabled={disableRunButton} onClick={runTests}> <OverlayTrigger placement={'top-start'} overlay={
<span className="fas fa-play ml-2"></span> <Tooltip className="text-nowrap" id="info-recorder">
<label className="labelOnBtn btn btn-primary p-1 ml-2 m-0">Run</label> <span>
</button> {runButtonTitle}
<button id="runTestsTabStopAction" data-id="testTabRunTestsTabStopAction" className="w-50 pl-2 ml-2 btn btn-secondary" disabled={disableStopButton} title="Stop running tests" onClick={stopTests}> </span>
<span className="fas fa-stop ml-2"></span> </Tooltip>
<label className="labelOnBtn btn btn-secondary p-1 ml-2 m-0" id="runTestsTabStopActionLabel">{stopButtonLabel}</label> }>
<button id="runTestsTabRunAction"data-id="testTabRunTestsTabRunAction" className="w-50 btn btn-primary" disabled={disableRunButton} onClick={runTests}>
<span className="fas fa-play ml-2"></span>
<label className="labelOnBtn btn btn-primary p-1 ml-2 m-0">Run</label>
</button>
</OverlayTrigger>
<button id="runTestsTabStopAction" data-id="testTabRunTestsTabStopAction" className="w-50 pl-2 ml-2 btn btn-secondary" disabled={disableStopButton} onClick={stopTests}>
<OverlayTrigger placement={'top-start'} overlay={
<Tooltip className="text-nowrap" id="info-recorder">
<span>
Stop running tests
</span>
</Tooltip>
}>
<span>
<span className="fas fa-stop ml-2"></span>
<label className="labelOnBtn btn btn-secondary p-1 ml-2 m-0" id="runTestsTabStopActionLabel">{stopButtonLabel}</label>
</span>
</OverlayTrigger>
</button> </button>
</div> </div>
<div className="d-flex align-items-center mx-3 pb-2 mt-2 border-bottom"> <div className="d-flex align-items-center mx-3 pb-2 mt-2 border-bottom">
@ -730,7 +786,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
const elemId = `singleTest${testFileObj.fileName}` const elemId = `singleTest${testFileObj.fileName}`
return ( return (
<div className="d-flex align-items-center py-1" key={index}> <div className="d-flex align-items-center py-1" key={index}>
<input className="singleTest" id={elemId} onChange={(e) => toggleCheckbox(e.target.checked, index)} type="checkbox" checked={testFileObj.checked} /> <input data-id="singleTest" className="singleTest" id={elemId} onChange={(e) => toggleCheckbox(e.target.checked, index)} type="checkbox" checked={testFileObj.checked} />
<label className="singleTestLabel text-nowrap pl-2 mb-0" htmlFor={elemId}>{testFileObj.fileName}</label> <label className="singleTestLabel text-nowrap pl-2 mb-0" htmlFor={elemId}>{testFileObj.fileName}</label>
</div> </div>
) )

@ -1,4 +1,5 @@
import React from 'react' //eslint-disable-line import React from 'react' //eslint-disable-line
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
interface StaticAnalyserButtonProps { interface StaticAnalyserButtonProps {
onClick: (event) => void onClick: (event) => void
@ -16,8 +17,19 @@ const StaticAnalyserButton = ({
let classList = "btn btn-sm w-25 btn-primary" let classList = "btn btn-sm w-25 btn-primary"
classList += disabled ? " disabled" : "" classList += disabled ? " disabled" : ""
return ( return (
<button className={classList} disabled={disabled} title={title} onClick={onClick}> <button className={classList} disabled={disabled} onClick={onClick}>
{buttonText} <OverlayTrigger
placement="bottom-start"
overlay={
<Tooltip id="ssaRunButtonTooltip" className="text-nowrap">
<span>{title}</span>
</Tooltip>
}
>
<span>
{buttonText}
</span>
</OverlayTrigger>
</button> </button>
) )
} }

@ -490,6 +490,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
label="Select all" label="Select all"
onClick={() => handleCheckAllModules(groupedModules)} onClick={() => handleCheckAllModules(groupedModules)}
onChange={() => {}} onChange={() => {}}
tooltipPlacement={'top-start'}
/> />
<RemixUiCheckbox <RemixUiCheckbox
id="autorunstaticanalysis" id="autorunstaticanalysis"
@ -499,6 +500,7 @@ export const RemixUiStaticAnalyser = (props: RemixUiStaticAnalyserProps) => {
checked={autoRun} checked={autoRun}
label="Autorun" label="Autorun"
onChange={() => {}} onChange={() => {}}
tooltipPlacement={'bottom-start'}
/> />
<Button <Button
buttonText="Run" buttonText="Run"

@ -1,36 +1,42 @@
import React, { useState, useEffect } from 'react' //eslint-disable-line import React, { useState, useEffect } from 'react' //eslint-disable-line
import { useIntl } from 'react-intl' import { FormattedMessage } from 'react-intl'
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
import { Placement } from 'react-bootstrap/esm/Overlay'
import { FileExplorerMenuProps } from '../types' import { FileExplorerMenuProps } from '../types'
const _paq = window._paq = window._paq || [] const _paq = window._paq = window._paq || []
export const FileExplorerMenu = (props: FileExplorerMenuProps) => { export const FileExplorerMenu = (props: FileExplorerMenuProps) => {
const intl = useIntl()
const [state, setState] = useState({ const [state, setState] = useState({
menuItems: [ menuItems: [
{ {
action: 'createNewFile', action: 'createNewFile',
title: 'Create New File', title: 'Create New File',
icon: 'far fa-file' icon: 'far fa-file',
placement: 'top-start'
}, },
{ {
action: 'createNewFolder', action: 'createNewFolder',
title: 'Create New Folder', title: 'Create New Folder',
icon: 'far fa-folder' icon: 'far fa-folder',
placement: 'top-end'
}, },
{ {
action: 'publishToGist', action: 'publishToGist',
title: 'Publish all the current workspace files (only root) to a github gist', title: 'Publish all the current workspace files (only root) to a github gist',
icon: 'fab fa-github' icon: 'fab fa-github',
placement: 'top-start'
}, },
{ {
action: 'uploadFile', action: 'uploadFile',
title: 'Load a local file into current workspace', title: 'Load a local file into current workspace',
icon: 'fa fa-upload' icon: 'fa fa-upload',
placement: 'right'
}, },
{ {
action: 'updateGist', action: 'updateGist',
title: 'Update the current [gist] explorer', title: 'Update the current [gist] explorer',
icon: 'fab fa-github' icon: 'fab fa-github',
placement: 'right-start'
} }
].filter(item => props.menuItems && props.menuItems.find((name) => { return name === item.action })), ].filter(item => props.menuItems && props.menuItems.find((name) => { return name === item.action })),
actions: {} actions: {}
@ -48,49 +54,74 @@ export const FileExplorerMenu = (props: FileExplorerMenuProps) => {
return ( return (
<> <>
<span className='remixui_label' title={props.title} data-path={props.title} style={{ fontWeight: 'bold' }}>{ props.title }</span> <OverlayTrigger
placement="top-start"
overlay={
<Tooltip id="remixuilabelTooltip" className="text-nowrap">
<span>{props.title}</span>
</Tooltip>
}
>
<span className='remixui_label' data-path={props.title} style={{ fontWeight: 'bold' }}>{ props.title }</span>
</OverlayTrigger>
<span className="pl-2">{ <span className="pl-2">{
state.menuItems.map(({ action, title, icon }, index) => { state.menuItems.map(({ action, title, icon, placement }, index) => {
if (action === 'uploadFile') { if (action === 'uploadFile') {
return ( return (
<label <OverlayTrigger
id={action} placement="right"
data-id={'fileExplorerUploadFile' + action } overlay={
className={icon + ' mb-0 remixui_newFile'} <Tooltip id="uploadFileTooltip" className="text-nowrap">
title={intl.formatMessage({id: `filePanel.${action}`, defaultMessage: title})} <span><FormattedMessage id={`filePanel.${action}`} defaultMessage={title} /></span>
key={index} </Tooltip>
}
> >
<input id="fileUpload" data-id="fileExplorerFileUpload" type="file" onChange={(e) => { <label
e.stopPropagation() id={action}
props.uploadFile(e.target) data-id={'fileExplorerUploadFile' + action }
e.target.value = null className={icon + ' mb-0 remixui_newFile'}
}} key={index}
multiple /> >
</label> <input id="fileUpload" data-id="fileExplorerFileUpload" type="file" onChange={(e) => {
e.stopPropagation()
props.uploadFile(e.target)
e.target.value = null
}}
multiple />
</label>
</OverlayTrigger>
) )
} else { } else {
return ( return (
<span <OverlayTrigger
id={action} placement={placement as Placement}
data-id={'fileExplorerNewFile' + action} overlay={
onClick={(e) => { <Tooltip id={`${action}-${title}-${icon}-${index}`} className="text-nowrap">
e.stopPropagation() <span><FormattedMessage id={`filePanel.${action}`} defaultMessage={title} /></span>
_paq.push(['trackEvent', 'fileExplorer', 'fileAction', action]) </Tooltip>
if (action === 'createNewFile') { }
props.createNewFile()
} else if (action === 'createNewFolder') {
props.createNewFolder()
} else if (action === 'publishToGist') {
props.publishToGist()
} else {
state.actions[action]()
}
}}
className={'newFile ' + icon + ' remixui_newFile'}
title={intl.formatMessage({id: `filePanel.${action}`, defaultMessage: title})}
key={index}
> >
</span> <span
id={action}
data-id={'fileExplorerNewFile' + action}
onClick={(e) => {
e.stopPropagation()
_paq.push(['trackEvent', 'fileExplorer', 'fileAction', action])
if (action === 'createNewFile') {
props.createNewFile()
} else if (action === 'createNewFolder') {
props.createNewFolder()
} else if (action === 'publishToGist') {
props.publishToGist()
} else {
state.actions[action]()
}
}}
className={'newFile ' + icon + ' remixui_newFile'}
key={`${action}-${title}-${index}`}
>
</span>
</OverlayTrigger>
) )
} }
})} })}

@ -103,7 +103,7 @@ export interface FileExplorerProps {
dispatchMoveFile: (src: string, dest: string) => Promise<void>, dispatchMoveFile: (src: string, dest: string) => Promise<void>,
dispatchMoveFolder: (src: string, dest: string) => Promise<void> dispatchMoveFolder: (src: string, dest: string) => Promise<void>
} }
type Placement = import('react-overlays/usePopper').Placement
export interface FileExplorerMenuProps { export interface FileExplorerMenuProps {
title: string, title: string,
menuItems: string[], menuItems: string[],
@ -111,6 +111,7 @@ export interface FileExplorerMenuProps {
createNewFolder: (parentFolder?: string) => void, createNewFolder: (parentFolder?: string) => void,
publishToGist: (path?: string) => void, publishToGist: (path?: string) => void,
uploadFile: (target: EventTarget & HTMLInputElement) => void uploadFile: (target: EventTarget & HTMLInputElement) => void
tooltipPlacement?: Placement
} }
export interface FileExplorerContextMenuProps { export interface FileExplorerContextMenuProps {
actions: action[], actions: action[],

Loading…
Cancel
Save