Commit c7187946 authored by Lijiao's avatar Lijiao Committed by GitHub
Browse files

Use office-fabric-ui components (#1964)

parent fdfff50d
......@@ -14,8 +14,9 @@ process.on('unhandledRejection', err => {
// Ensure environment variables are read.
require('../config/env');
const fs = require('fs');
const chalk = require('chalk');
const chalk = require('react-dev-utils/chalk');
const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const clearConsole = require('react-dev-utils/clearConsole');
......@@ -28,7 +29,7 @@ const {
} = require('react-dev-utils/WebpackDevServerUtils');
const openBrowser = require('react-dev-utils/openBrowser');
const paths = require('../config/paths');
const config = require('../config/webpack.config.dev');
const configFactory = require('../config/webpack.config');
const createDevServerConfig = require('../config/webpackDevServer.config');
const useYarn = fs.existsSync(paths.yarnLockFile);
......@@ -54,35 +55,50 @@ if (process.env.HOST) {
console.log(
`If this was unintentional, check that you haven't mistakenly set it in your shell.`
);
console.log(`Learn more here: ${chalk.yellow('http://bit.ly/2mwWSwH')}`);
console.log();
console.log(
`Learn more here: ${chalk.yellow('https://bit.ly/CRA-advanced-config')}`
);
}
// We attempt to use the default port but if it is busy, we offer the user to
// run on a different port. `choosePort()` Promise resolves to the next free port.
choosePort(HOST, DEFAULT_PORT)
// We require that you explicitly set browsers and do not fall back to
// browserslist defaults.
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
checkBrowsers(paths.appPath, isInteractive)
.then(() => {
// We attempt to use the default port but if it is busy, we offer the user to
// run on a different port. `choosePort()` Promise resolves to the next free port.
return choosePort(HOST, DEFAULT_PORT);
})
.then(port => {
if (port == null) {
// We have not found a port.
return;
}
const config = configFactory('development');
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
const appName = require(paths.appPackageJson).name;
const useTypeScript = fs.existsSync(paths.appTsConfig);
const urls = prepareUrls(protocol, HOST, port);
const devSocket = {
warnings: warnings =>
devServer.sockWrite(devServer.sockets, 'warnings', warnings),
errors: errors =>
devServer.sockWrite(devServer.sockets, 'errors', errors),
};
// Create a webpack compiler that is configured with custom messages.
const compiler = createCompiler({
appName,
config,
//devSocket: null,
urls,
useYarn,
//useTypeScript: true,
webpack
appName,
config,
devSocket,
urls,
useYarn,
useTypeScript,
webpack,
});
// Load proxy config
const proxySetting = require(paths.appPackageJson).proxy;
const proxyConfig = prepareProxy(proxySetting, paths.appPublic);
// Serve webpack assets generated by the compiler over a web sever.
// Serve webpack assets generated by the compiler over a web server.
const serverConfig = createDevServerConfig(
proxyConfig,
urls.lanUrlForConfig
......@@ -96,6 +112,18 @@ choosePort(HOST, DEFAULT_PORT)
if (isInteractive) {
clearConsole();
}
// We used to support resolving modules according to `NODE_PATH`.
// This now has been deprecated in favor of jsconfig/tsconfig.json
// This lets you use absolute paths in imports inside large monorepos:
if (process.env.NODE_PATH) {
console.log(
chalk.yellow(
'Setting NODE_PATH to resolve modules absolutely has been deprecated in favor of setting baseUrl in jsconfig.json (or tsconfig.json if you are using TypeScript) and will be removed in a future major release of create-react-app.'
)
);
}
console.log(chalk.cyan('Starting the development server...\n'));
openBrowser(urls.localUrlForBrowser);
});
......@@ -110,7 +138,6 @@ choosePort(HOST, DEFAULT_PORT)
.catch(err => {
if (err && err.message) {
console.log(err.message);
console.log(err.stack);
}
process.exit(1);
});
'use strict';
// Do this as the first thing so that any code reading it knows the right env.
process.env.BABEL_ENV = 'test';
process.env.NODE_ENV = 'test';
process.env.PUBLIC_URL = '';
// Makes the script crash on unhandled rejections instead of silently
// ignoring them. In the future, promise rejections that are not handled will
// terminate the Node.js process with a non-zero exit code.
process.on('unhandledRejection', err => {
throw err;
});
// Ensure environment variables are read.
require('../config/env');
const jest = require('jest');
const execSync = require('child_process').execSync;
let argv = process.argv.slice(2);
function isInGitRepository() {
try {
execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' });
return true;
} catch (e) {
return false;
}
}
function isInMercurialRepository() {
try {
execSync('hg --cwd . root', { stdio: 'ignore' });
return true;
} catch (e) {
return false;
}
}
// Watch unless on CI or explicitly running all tests
if (
!process.env.CI &&
argv.indexOf('--watchAll') === -1
) {
// https://github.com/facebook/create-react-app/issues/5210
const hasSourceControl = isInGitRepository() || isInMercurialRepository();
argv.push(hasSourceControl ? '--watch' : '--watchAll');
}
jest.run(argv);
......@@ -16,14 +16,30 @@
z-index: 1000;
}
.headerCon{
width: 90%;
margin: 0 auto;
}
.contentBox{
width: 100%;
}
.content{
width: 86%;
width: 89%;
min-width: 1024px;
margin: 0 auto;
margin-top: 74px;
margin-bottom: 30px;
background: #fff;
}
/* office-fabric-ui */
.ms-Pivot-linkContent{
height: 44px;
}
.ms-Callout-main{
p{
font-weight: 500;
color: #333;
}
}
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App />, div);
ReactDOM.unmountComponentAtNode(div);
});
import * as React from 'react';
import { Row, Col } from 'antd';
import { Stack } from 'office-ui-fabric-react';
import { COLUMN } from './static/const';
import { EXPERIMENT, TRIALS } from './static/datamodel';
import './App.css';
import SlideBar from './components/SlideBar';
import NavCon from './components/NavCon';
import './App.scss';
interface AppState {
interval: number;
columnList: Array<string>;
columnList: string[];
experimentUpdateBroadcast: number;
trialsUpdateBroadcast: number;
metricGraphMode: 'max' | 'min'; // tuner's optimize_mode filed
}
class App extends React.Component<{}, AppState> {
private timerId: number | null;
private timerId!: number | null;
constructor(props: {}) {
super(props);
......@@ -28,7 +28,7 @@ class App extends React.Component<{}, AppState> {
}
async componentDidMount(): Promise<void> {
await Promise.all([ EXPERIMENT.init(), TRIALS.init() ]);
await Promise.all([EXPERIMENT.init(), TRIALS.init()]);
this.setState(state => ({ experimentUpdateBroadcast: state.experimentUpdateBroadcast + 1 }));
this.setState(state => ({ trialsUpdateBroadcast: state.trialsUpdateBroadcast + 1 }));
this.timerId = window.setTimeout(this.refresh, this.state.interval * 1000);
......@@ -45,7 +45,7 @@ class App extends React.Component<{}, AppState> {
}
// TODO: use local storage
changeColumn = (columnList: Array<string>): void => {
changeColumn = (columnList: string[]): void => {
this.setState({ columnList: columnList });
}
......@@ -53,7 +53,7 @@ class App extends React.Component<{}, AppState> {
this.setState({ metricGraphMode: val });
}
render(): React.ReactNode{
render(): React.ReactNode {
const { interval, columnList, experimentUpdateBroadcast, trialsUpdateBroadcast, metricGraphMode } = this.state;
if (experimentUpdateBroadcast === 0 || trialsUpdateBroadcast === 0) {
return null; // TODO: render a loading page
......@@ -66,28 +66,27 @@ class App extends React.Component<{}, AppState> {
experimentUpdateBroadcast,
trialsUpdateBroadcast,
metricGraphMode, changeMetricGraphMode: this.changeMetricGraphMode
})
})
);
return (
<Row className="nni" style={{ minHeight: window.innerHeight }}>
<Row className="header">
<Col span={1} />
<Col className="headerCon" span={22}>
<SlideBar changeInterval={this.changeInterval} />
</Col>
<Col span={1} />
</Row>
<Row className="contentBox">
<Row className="content">
<Stack className="nni" style={{ minHeight: window.innerHeight }}>
<div className="header">
<div className="headerCon">
<NavCon changeInterval={this.changeInterval} refreshFunction={this.lastRefresh}/>
</div>
</div>
<Stack className="contentBox">
<Stack className="content">
{reactPropsChildren}
</Row>
</Row>
</Row>
</Stack>
</Stack>
</Stack>
);
}
private refresh = async (): Promise<void> => {
const [ experimentUpdated, trialsUpdated ] = await Promise.all([ EXPERIMENT.update(), TRIALS.update() ]);
const [experimentUpdated, trialsUpdated] = await Promise.all([EXPERIMENT.update(), TRIALS.update()]);
if (experimentUpdated) {
this.setState(state => ({ experimentUpdateBroadcast: state.experimentUpdateBroadcast + 1 }));
}
......@@ -95,7 +94,7 @@ class App extends React.Component<{}, AppState> {
this.setState(state => ({ trialsUpdateBroadcast: state.trialsUpdateBroadcast + 1 }));
}
if ([ 'DONE', 'ERROR', 'STOPPED' ].includes(EXPERIMENT.status)) {
if (['DONE', 'ERROR', 'STOPPED'].includes(EXPERIMENT.status)) {
// experiment finished, refresh once more to ensure consistency
if (this.state.interval > 0) {
this.setState({ interval: 0 });
......@@ -107,7 +106,7 @@ class App extends React.Component<{}, AppState> {
}
}
private async lastRefresh(): Promise<void> {
public async lastRefresh(): Promise<void> {
await EXPERIMENT.update();
await TRIALS.update(true);
this.setState(state => ({ experimentUpdateBroadcast: state.experimentUpdateBroadcast + 1 }));
......@@ -116,3 +115,5 @@ class App extends React.Component<{}, AppState> {
}
export default App;
import * as React from 'react';
import { Icon, initializeIcons } from 'office-ui-fabric-react';
initializeIcons();
const infoIcon = <Icon iconName='info' />;
const warining = <Icon iconName='Warning' />;
const errorBadge = <Icon iconName='ErrorBadge' />;
const completed = <Icon iconName='Completed' />;
const blocked = <Icon iconName='StatusCircleBlock' />;
const copy = <Icon iconName='Copy' />;
const tableListIcon = <Icon iconName='BulletedList' />;
const downLoadIcon = { iconName: 'Download' };
const infoIconAbout = { iconName: 'info' };
const timeIcon = { iconName: 'ReminderTime' };
const disableUpdates = { iconName: 'DisableUpdates' };
const requency = { iconName: 'Timer' };
const closeTimer = { iconName: 'Blocked2' };
const LineChart = <Icon iconName='LineChart' />;
export { infoIcon, warining, errorBadge, completed, blocked,
infoIconAbout, copy, tableListIcon, downLoadIcon, timeIcon,
disableUpdates, requency, closeTimer, LineChart
};
import { getTheme, mergeStyleSets, FontWeights, FontSizes } from 'office-ui-fabric-react';
// Themed styles for the example.
const theme = getTheme();
const contentStyles = mergeStyleSets({
container: {
display: 'flex',
flexFlow: 'column nowrap',
alignItems: 'stretch'
},
header: [
theme.fonts.xLargePlus,
{
flex: '1 1 auto',
borderTop: `4px solid ${theme.palette.themePrimary}`,
color: theme.palette.neutralPrimary,
display: 'flex',
fontSize: FontSizes.xLarge,
alignItems: 'center',
fontWeight: FontWeights.semibold,
padding: '12px 12px 14px 24px'
}
],
body: {
flex: '4 4 auto',
padding: '0 24px 24px 24px',
overflowY: 'hidden',
selectors: {
p: {
margin: '14px 0'
},
'p:first-child': {
marginTop: 0
},
'p:last-child': {
marginBottom: 0
}
}
}
});
const iconButtonStyles = mergeStyleSets({
root: {
color: theme.palette.neutralPrimary,
marginLeft: 'auto',
marginTop: '4px',
marginRight: '2px'
},
rootHovered: {
color: theme.palette.neutralDark
}
});
export { contentStyles, iconButtonStyles };
\ No newline at end of file
import * as React from 'react';
import { Dialog, DialogType, DialogFooter, Checkbox, PrimaryButton, DefaultButton } from 'office-ui-fabric-react';
import { OPERATION } from '../../static/const';
interface ChangeColumnState {
userSelectColumnList: string[];
originSelectColumnList: string[];
}
interface ChangeColumnProps {
isHideDialog: boolean;
showColumn: string[]; // all column List
selectedColumn: string[]; // user selected column list
changeColumn: (val: string[]) => void;
hideShowColumnDialog: () => void;
}
interface CheckBoxItems {
label: string;
checked: boolean;
onChange: () => void;
}
class ChangeColumnComponent extends React.Component<ChangeColumnProps, ChangeColumnState> {
constructor(props: ChangeColumnProps) {
super(props);
this.state = { userSelectColumnList: this.props.selectedColumn, originSelectColumnList: this.props.selectedColumn };
}
makeChangeHandler = (label: string): any => {
return (ev: any, checked: boolean): void => this.onCheckboxChange(ev, label, checked);
}
onCheckboxChange = (ev: React.FormEvent<HTMLElement | HTMLInputElement> | undefined, label: string, val?: boolean, ): void => {
const source: string[] = JSON.parse(JSON.stringify(this.state.userSelectColumnList));
if (val === true) {
if (!source.includes(label)) {
source.push(label);
this.setState(() => ({ userSelectColumnList: source }));
}
} else {
if (source.includes(label)) {
// remove from source
const result = source.filter((item) => item !== label);
this.setState(() => ({ userSelectColumnList: result }));
}
}
};
saveUserSelectColumn = (): void => {
const { userSelectColumnList } = this.state;
const { showColumn } = this.props;
// sort by Trial No. | ID | Duration | Start Time | End Time | ...
const sortColumn: string[] = [];
/**
*
* TODO: use this function to refactor sort column
* search space might orderless
showColumn.map(item => {
userSelectColumnList.map(key => {
if (item === key || key.includes('search space')) {
if (!sortColumn.includes(key)) {
sortColumn.push(key);
}
}
});
});
*/
// push ![Operation] ![search space] column
showColumn.map(item => {
userSelectColumnList.map(key => {
if (item === key && item !== OPERATION) {
sortColumn.push(key);
}
});
});
// push search space key
userSelectColumnList.map(index => {
if (index.includes('search space')) {
if (!sortColumn.includes(index)) {
sortColumn.push(index);
}
}
});
// push Operation
if (userSelectColumnList.includes(OPERATION)) {
sortColumn.push(OPERATION);
}
this.props.changeColumn(sortColumn);
this.hideDialog(); // hide dialog
}
hideDialog = (): void => {
this.props.hideShowColumnDialog();
}
// user exit dialog
cancelOption = (): void => {
// reset select column
const { originSelectColumnList } = this.state;
this.setState({ userSelectColumnList: originSelectColumnList }, () => {
this.hideDialog();
});
}
render(): React.ReactNode {
const { showColumn, isHideDialog } = this.props;
const { userSelectColumnList } = this.state;
const renderOptions: Array<CheckBoxItems> = [];
showColumn.map(item => {
if (userSelectColumnList.includes(item)) {
// selected column name
renderOptions.push({ label: item, checked: true, onChange: this.makeChangeHandler(item) });
} else {
renderOptions.push({ label: item, checked: false, onChange: this.makeChangeHandler(item) });
}
});
return (
<div>
<div>Hello</div>
<Dialog
hidden={isHideDialog} // required field!
dialogContentProps={{
type: DialogType.largeHeader,
title: 'Change table column',
subText: 'You can chose which columns you want to see in the table.'
}}
modalProps={{
isBlocking: false,
styles: { main: { maxWidth: 450 } }
}}
>
<div>
{renderOptions.map(item => {
return <Checkbox key={item.label} {...item} styles={{ root: { marginBottom: 8 } }} />
})}
</div>
<DialogFooter>
<PrimaryButton text="Save" onClick={this.saveUserSelectColumn} />
<DefaultButton text="Cancel" onClick={this.cancelOption} />
</DialogFooter>
</Dialog>
</div>
);
}
}
export default ChangeColumnComponent;
\ No newline at end of file
import * as React from 'react';
import { Row, Modal } from 'antd';
import { Stack, Modal, IconButton } from 'office-ui-fabric-react';
import ReactEcharts from 'echarts-for-react';
import IntermediateVal from '../public-child/IntermediateVal';
import { TRIALS } from '../../static/datamodel';
import { contentStyles, iconButtonStyles } from '../Buttons/ModalTheme';
import '../../static/style/compare.scss';
import { TableRecord, Intermedia, TooltipForIntermediate } from 'src/static/interface';
import { TableRecord, Intermedia, TooltipForIntermediate } from '../../static/interface'; // eslint-disable-line no-unused-vars
// the modal of trial compare
interface CompareProps {
compareRows: Array<TableRecord>;
visible: boolean;
compareStacks: Array<TableRecord>;
cancelFunc: () => void;
}
class Compare extends React.Component<CompareProps, {}> {
public _isCompareMount: boolean;
public _isCompareMount!: boolean;
constructor(props: CompareProps) {
super(props);
}
intermediate = (): any => {
const { compareRows } = this.props;
intermediate = (): React.ReactNode => {
const { compareStacks } = this.props;
const trialIntermediate: Array<Intermedia> = [];
const idsList: Array<string> = [];
Object.keys(compareRows).map(item => {
const temp = compareRows[item];
const trial = TRIALS.getTrial(temp.id);
const idsList: string[] = [];
compareStacks.forEach(element => {
const trial = TRIALS.getTrial(element.id);
trialIntermediate.push({
name: temp.id,
name: element.id,
data: trial.description.intermediate,
type: 'line',
hyperPara: trial.description.parameters
});
idsList.push(temp.id);
idsList.push(element.id);
});
// find max intermediate number
trialIntermediate.sort((a, b) => { return (b.data.length - a.data.length); });
const legend: Array<string> = [];
const legend: string[] = [];
// max length
const length = trialIntermediate[0] !== undefined ? trialIntermediate[0].data.length : 0;
const xAxis: number[] = [];
Object.keys(trialIntermediate).map(item => {
const temp = trialIntermediate[item];
legend.push(temp.name);
trialIntermediate.forEach(element => {
legend.push(element.name);
});
for (let i = 1; i <= length; i++) {
xAxis.push(i);
......@@ -59,7 +57,7 @@ class Compare extends React.Component<CompareProps, {}> {
return [point[0] - 300, 80];
}
},
formatter: function (data: TooltipForIntermediate): any {
formatter: function (data: TooltipForIntermediate): React.ReactNode {
const trialId = data.seriesName;
let obj = {};
const temp = trialIntermediate.find(key => key.name === trialId);
......@@ -108,19 +106,18 @@ class Compare extends React.Component<CompareProps, {}> {
// render table column ---
initColumn = (): React.ReactNode => {
const idList: Array<string> = [];
const idList: string[] = [];
const sequenceIdList: number[] = [];
const durationList: number[] = [];
const compareRows = this.props.compareRows.map(tableRecord => TRIALS.getTrial(tableRecord.id));
const compareStacks = this.props.compareStacks.map(tableRecord => TRIALS.getTrial(tableRecord.id));
const parameterList: Array<object> = [];
let parameterKeys: Array<string> = [];
if (compareRows.length !== 0) {
parameterKeys = Object.keys(compareRows[0].description.parameters);
let parameterKeys: string[] = [];
if (compareStacks.length !== 0) {
parameterKeys = Object.keys(compareStacks[0].description.parameters);
}
Object.keys(compareRows).map(item => {
const temp = compareRows[item];
compareStacks.forEach(temp => {
idList.push(temp.id);
sequenceIdList.push(temp.sequenceId);
durationList.push(temp.duration);
......@@ -132,7 +129,7 @@ class Compare extends React.Component<CompareProps, {}> {
? true : false;
}
return (
<table className="compare">
<table className="compare-modal-table">
<tbody>
<tr>
<td className="column">Id</td>
......@@ -152,8 +149,8 @@ class Compare extends React.Component<CompareProps, {}> {
</tr>
<tr>
<td className="column">Default metric</td>
{Object.keys(compareRows).map(index => {
const temp = compareRows[index];
{Object.keys(compareStacks).map(index => {
const temp = compareStacks[index];
return (
<td className="value" key={index}>
<IntermediateVal trialId={temp.id} />
......@@ -204,24 +201,31 @@ class Compare extends React.Component<CompareProps, {}> {
this._isCompareMount = false;
}
render(): React.ReactNode{
const { visible, cancelFunc } = this.props;
render(): React.ReactNode {
const { cancelFunc } = this.props;
return (
<Modal
title="Compare trials"
visible={visible}
onCancel={cancelFunc}
footer={null}
destroyOnClose={true}
maskClosable={false}
width="90%"
isOpen={true}
containerClassName={contentStyles.container}
className="compare-modal"
>
<Row className="compare-intermediate">
{this.intermediate()}
<Row className="compare-yAxis"># Intermediate result</Row>
</Row>
<Row>{this.initColumn()}</Row>
<div>
<div className={contentStyles.header}>
<span>Compare trials</span>
<IconButton
styles={iconButtonStyles}
iconProps={{ iconName: 'Cancel' }}
ariaLabel="Close popup modal"
onClick={cancelFunc}
/>
</div>
<Stack className="compare-modal-intermediate">
{this.intermediate()}
<Stack className="compare-yAxis"># Intermediate result</Stack>
</Stack>
<Stack>{this.initColumn()}</Stack>
</div>
</Modal>
);
}
......
import * as React from 'react';
import axios from 'axios';
import { Row, Col, Input, Modal, Form, Button, Icon } from 'antd';
import { Stack, StackItem, PrimaryButton, DefaultButton } from 'office-ui-fabric-react';
import { Dialog, DialogType, DialogFooter } from 'office-ui-fabric-react/lib/Dialog';
import { MANAGER_IP } from '../../static/const';
import { EXPERIMENT, TRIALS } from '../../static/datamodel';
import { FormComponentProps } from 'antd/lib/form';
const FormItem = Form.Item;
import { warining, errorBadge, completed } from '../Buttons/Icon';
import './customized.scss';
interface CustomizeProps extends FormComponentProps {
interface CustomizeProps {
visible: boolean;
copyTrialId: string;
closeCustomizeModal: () => void;
......@@ -21,6 +21,7 @@ interface CustomizeState {
copyTrialParameter: object; // user click the trial's parameters
customParameters: object; // customized trial, maybe user change trial's parameters
customID: number; // submit customized trial succeed, return the new customized trial id
changeMap: Map<string, string | number>; // store change key: value
}
class Customize extends React.Component<CustomizeProps, CustomizeState> {
......@@ -34,15 +35,29 @@ class Customize extends React.Component<CustomizeProps, CustomizeState> {
searchSpace: EXPERIMENT.searchSpace,
copyTrialParameter: {},
customParameters: {},
customID: NaN
customID: NaN,
changeMap: new Map
};
}
getFinalVal = (event: React.ChangeEvent<HTMLInputElement>): void => {
const { name, value } = event.target;
const { changeMap } = this.state;
this.setState({ changeMap: changeMap.set(name, value) });
}
// [submit click] user add a new trial [submit a trial]
addNewTrial = (): void => {
const { searchSpace, copyTrialParameter } = this.state;
const { searchSpace, copyTrialParameter, changeMap } = this.state;
// get user edited hyperParameter, ps: will change data type if you modify the input val
const customized = this.props.form.getFieldsValue();
const customized = JSON.parse(JSON.stringify(copyTrialParameter));
// changeMap: user changed keys: values
changeMap.forEach(function (value, key) {
customized[key] = value;
});
// true: parameters are wrong
let flag = false;
Object.keys(customized).map(item => {
......@@ -113,13 +128,13 @@ class Customize extends React.Component<CustomizeProps, CustomizeState> {
closeSucceedHint = (): void => {
// also close customized trial modal
this.setState(() => ({ isShowSubmitSucceed: false }));
this.setState(() => ({ isShowSubmitSucceed: false, changeMap: new Map() }));
this.props.closeCustomizeModal();
}
closeFailedHint = (): void => {
// also close customized trial modal
this.setState(() => ({ isShowSubmitFailed: false }));
this.setState(() => ({ isShowSubmitFailed: false, changeMap: new Map() }));
this.props.closeCustomizeModal();
}
......@@ -138,180 +153,120 @@ class Customize extends React.Component<CustomizeProps, CustomizeState> {
this.setState(() => ({ copyTrialParameter: originCopyTrialPara }));
}
}
render(): React.ReactNode {
const { closeCustomizeModal, visible } = this.props;
const { isShowSubmitSucceed, isShowSubmitFailed, isShowWarning, customID, copyTrialParameter } = this.state;
const {
form: { getFieldDecorator },
// form: { getFieldDecorator, getFieldValue },
} = this.props;
const warning = 'The parameters you set are not in our search space, this may cause the tuner to crash, Are'
+ ' you sure you want to continue submitting?';
return (
<Row>
{/* form: search space */}
<Modal
title="Customized trial setting"
visible={visible}
onCancel={closeCustomizeModal}
footer={null}
destroyOnClose={true}
maskClosable={false}
centered={true}
<Stack>
<Dialog
hidden={!visible} // required field!
dialogContentProps={{
type: DialogType.largeHeader,
title: 'Customized trial setting',
subText: 'You can submit a customized trial.'
}}
modalProps={{
isBlocking: false,
styles: { main: { maxWidth: 450 } }
}}
>
{/* search space form */}
<Row className="hyper-box">
<Form>
{
Object.keys(copyTrialParameter).map(item => (
<Row key={item} className="hyper-form">
<Col span={9} className="title">{item}</Col>
<Col span={15} className="inputs">
<FormItem key={item} style={{ marginBottom: 0 }}>
{getFieldDecorator(item, {
initialValue: copyTrialParameter[item],
})(
<Input />
)}
</FormItem>
</Col>
</Row>
)
)
}
<Row key="tag" className="hyper-form tag-input">
<Col span={9} className="title">Tag</Col>
<Col span={15} className="inputs">
<FormItem key="tag" style={{ marginBottom: 0 }}>
{getFieldDecorator('tag', {
initialValue: 'Customized',
})(
<Input />
)}
</FormItem>
</Col>
</Row>
</Form>
</Row>
<Row className="modal-button">
<Button
type="primary"
className="tableButton distance"
onClick={this.addNewTrial}
>
Submit
</Button>
<Button
className="tableButton cancelSty"
onClick={this.props.closeCustomizeModal}
>
Cancel
</Button>
</Row>
{/* control button */}
</Modal>
<form className="hyper-box">
{
Object.keys(copyTrialParameter).map(item => (
<Stack horizontal key={item} className="hyper-form">
<StackItem styles={{ root: { minWidth: 100 } }} className="title">{item}</StackItem>
<StackItem className="inputs">
<input
type="text"
name={item}
defaultValue={copyTrialParameter[item]}
onChange={this.getFinalVal}
/>
</StackItem>
</Stack>
)
)
}
{/* disable [tag] because we havn't support */}
{/* <Stack key="tag" horizontal className="hyper-form tag-input">
<StackItem grow={9} className="title">Tag</StackItem>
<StackItem grow={15} className="inputs">
<input type="text" value='Customized' />
</StackItem>
</Stack> */}
</form>
<DialogFooter>
<PrimaryButton text="Submit" onClick={this.addNewTrial} />
<DefaultButton text="Cancel" onClick={closeCustomizeModal} />
</DialogFooter>
</Dialog>
{/* clone: prompt succeed or failed */}
<Modal
visible={isShowSubmitSucceed}
footer={null}
destroyOnClose={true}
maskClosable={false}
closable={false}
centered={true}
<Dialog
hidden={!isShowSubmitSucceed}
onDismiss={this.closeSucceedHint}
dialogContentProps={{
type: DialogType.normal,
title: <div className="icon-color">{completed}<b>Submit successfully</b></div>,
closeButtonAriaLabel: 'Close',
subText: `You can find your customized trial by Trial No.${customID}`
}}
modalProps={{
isBlocking: false,
styles: { main: { minWidth: 500 } },
}}
>
<Row className="resubmit">
<Row>
<h2 className="title">
<span>
<Icon type="check-circle" className="color-succ" />
<b>Submit successfully</b>
</span>
</h2>
<div className="hint">
<span>You can find your customized trial by Trial No.{customID}</span>
</div>
</Row>
<Row className="modal-button">
<Button
className="tableButton cancelSty"
onClick={this.closeSucceedHint}
>
OK
</Button>
</Row>
</Row>
</Modal>
<Modal
visible={isShowSubmitFailed}
footer={null}
destroyOnClose={true}
maskClosable={false}
closable={false}
centered={true}
<DialogFooter>
<PrimaryButton onClick={this.closeSucceedHint} text="OK" />
</DialogFooter>
</Dialog>
<Dialog
hidden={!isShowSubmitFailed}
onDismiss={this.closeSucceedHint}
dialogContentProps={{
type: DialogType.normal,
title: <div className="icon-error">{errorBadge}Submit Failed</div>,
closeButtonAriaLabel: 'Close',
subText: 'Unknown error.'
}}
modalProps={{
isBlocking: false,
styles: { main: { minWidth: 500 } },
}}
>
<Row className="resubmit">
<Row>
<h2 className="title">
<span>
<Icon type="check-circle" className="color-error" />Submit Failed
</span>
</h2>
<div className="hint">
<span>Unknown error.</span>
</div>
</Row>
<Row className="modal-button">
<Button
className="tableButton cancelSty"
onClick={this.closeFailedHint}
>
OK
</Button>
</Row>
</Row>
</Modal>
<DialogFooter>
<PrimaryButton onClick={this.closeFailedHint} text="OK" />
</DialogFooter>
</Dialog>
{/* hyperParameter not match search space, warning modal */}
<Modal
visible={isShowWarning}
footer={null}
destroyOnClose={true}
maskClosable={false}
closable={false}
centered={true}
<Dialog
hidden={!isShowWarning}
onDismiss={this.closeSucceedHint}
dialogContentProps={{
type: DialogType.normal,
title: <div className="icon-error">{warining}Warning</div>,
closeButtonAriaLabel: 'Close',
subText: `${warning}`
}}
modalProps={{
isBlocking: false,
styles: { main: { minWidth: 500 } },
}}
>
<Row className="resubmit">
<Row>
<h2 className="title">
<span>
<Icon className="color-warn" type="warning" />Warning
</span>
</h2>
<div className="hint">
<span>{warning}</span>
</div>
</Row>
<Row className="modal-button center">
<Button
className="tableButton cancelSty distance"
onClick={this.warningConfirm}
>
Confirm
</Button>
<Button
className="tableButton cancelSty"
onClick={this.warningCancel}
>
Cancel
</Button>
</Row>
</Row>
</Modal>
</Row>
<DialogFooter>
<PrimaryButton onClick={this.warningConfirm} text="Confirm" />
<DefaultButton onClick={this.warningCancel} text="Cancel" />
</DialogFooter>
</Dialog>
</Stack>
);
}
}
export default Form.create<FormComponentProps>()(Customize);
\ No newline at end of file
export default Customize;
\ No newline at end of file
import * as React from 'react';
import axios from 'axios';
import { downFile } from '../../static/function';
import { Drawer, Tabs, Row, Col, Button } from 'antd';
import {
Stack, PrimaryButton, DefaultButton, Panel, StackItem, Pivot, PivotItem
} from 'office-ui-fabric-react';
import { MANAGER_IP, DRAWEROPTION } from '../../static/const';
import MonacoEditor from 'react-monaco-editor';
const { TabPane } = Tabs;
import '../../static/style/logDrawer.scss';
interface ExpDrawerProps {
......@@ -19,13 +20,13 @@ interface ExpDrawerState {
class ExperimentDrawer extends React.Component<ExpDrawerProps, ExpDrawerState> {
public _isCompareMount: boolean;
public _isCompareMount!: boolean;
constructor(props: ExpDrawerProps) {
super(props);
this.state = {
experiment: '',
expDrawerHeight: window.innerHeight - 48
expDrawerHeight: window.innerHeight
};
}
......@@ -72,7 +73,7 @@ class ExperimentDrawer extends React.Component<ExpDrawerProps, ExpDrawerState> {
}
onWindowResize = (): void => {
this.setState(() => ({expDrawerHeight: window.innerHeight - 48}));
this.setState(() => ({ expDrawerHeight: window.innerHeight }));
}
componentDidMount(): void {
......@@ -97,53 +98,42 @@ class ExperimentDrawer extends React.Component<ExpDrawerProps, ExpDrawerState> {
const { isVisble, closeExpDrawer } = this.props;
const { experiment, expDrawerHeight } = this.state;
return (
<Row className="logDrawer">
<Drawer
// title="Log Message"
placement="right"
closable={false}
destroyOnClose={true}
onClose={closeExpDrawer}
visible={isVisble}
width="54%"
height={expDrawerHeight}
<Stack className="logDrawer">
<Panel
isOpen={isVisble}
hasCloseButton={false}
styles={{ root: { height: expDrawerHeight, paddingTop: 15 } }}
>
{/* 104: tabHeight(40) + tabMarginBottom(16) + buttonHeight(32) + buttonMarginTop(16) */}
<div className="card-container log-tab-body">
<Tabs type="card" style={{ height: expDrawerHeight, minHeight: 190 }}>
<TabPane tab="Experiment Parameters" key="Experiment">
<div className="just-for-log">
<MonacoEditor
width="100%"
height={expDrawerHeight - 104}
language="json"
value={experiment}
options={DRAWEROPTION}
<Pivot style={{ minHeight: 190 }} className="log-tab-body">
<PivotItem headerText="Experiment Parameters">
<div className="just-for-log">
<MonacoEditor
width="100%"
// 92 + marginTop[16]
height={expDrawerHeight - 108}
language="json"
value={experiment}
options={DRAWEROPTION}
/>
</div>
<Stack horizontal className="buttons">
<StackItem grow={50} className="download">
<PrimaryButton
text="Download"
onClick={this.downExperimentParameters}
/>
</div>
<Row className="buttons">
<Col span={12} className="download">
<Button
type="primary"
onClick={this.downExperimentParameters}
>
Download
</Button>
</Col>
<Col span={12} className="close">
<Button
type="default"
onClick={closeExpDrawer}
>
Close
</Button>
</Col>
</Row>
</TabPane>
</Tabs>
</div>
</Drawer>
</Row>
</StackItem>
<StackItem grow={50} className="close">
<DefaultButton
text="Close"
onClick={closeExpDrawer}
/>
</StackItem>
</Stack>
</PivotItem>
</Pivot>
</Panel>
</Stack>
);
}
}
......
import * as React from 'react';
import {
Stack, FocusTrapCallout, DefaultButton,
FocusZone,
PrimaryButton, getTheme, mergeStyleSets, FontWeights
} from 'office-ui-fabric-react';
import { killJob } from '../../static/function';
import { blocked } from '../Buttons/Icon';
const theme = getTheme();
const styles = mergeStyleSets({
buttonArea: {
verticalAlign: 'top',
display: 'inline-block',
textAlign: 'center',
height: 32
},
callout: {
maxWidth: 300
},
header: {
padding: '18px 24px 12px'
},
title: [
theme.fonts.xLarge,
{
margin: 0,
color: theme.palette.neutralPrimary,
fontWeight: FontWeights.semilight
}
],
inner: {
height: '100%',
padding: '0 24px 20px'
},
actions: {
position: 'relative',
marginTop: 20,
width: '100%',
whiteSpace: 'nowrap'
},
buttons: {
display: 'flex',
justifyContent: 'flex-end',
padding: '0 24px 24px'
},
subtext: [
theme.fonts.small,
{
margin: 0,
color: theme.palette.neutralPrimary,
fontWeight: FontWeights.semilight
}
]
});
interface KillJobState {
isCalloutVisible: boolean;
}
interface KillJobProps {
trial: any;
}
class KillJob extends React.Component<KillJobProps, KillJobState> {
private menuButtonElement!: HTMLElement | null;
constructor(props: KillJobProps) {
super(props);
this.state = { isCalloutVisible: false };
}
onDismiss = (): void => {
this.setState(() => ({ isCalloutVisible: false }));
}
onKill = (): void => {
this.setState({ isCalloutVisible: false }, () => {
const { trial } = this.props;
killJob(trial.key, trial.id, trial.status);
});
}
openPromot = (event: React.SyntheticEvent<EventTarget>): void => {
event.preventDefault();
event.stopPropagation();
this.setState({ isCalloutVisible: true });
}
render(): React.ReactNode {
const { isCalloutVisible } = this.state;
const prompString = 'Are you sure to cancel this trial?';
return (
<div>
<div className={styles.buttonArea} ref={(menuButton): any => (this.menuButtonElement = menuButton)}>
<PrimaryButton className="detail-button-operation" onClick={this.openPromot} title="kill">{blocked}</PrimaryButton>
</div>
{isCalloutVisible ? (
<div>
<FocusTrapCallout
role="alertdialog"
className={styles.callout}
gapSpace={0}
target={this.menuButtonElement}
onDismiss={this.onDismiss}
setInitialFocus={true}
>
<div className={styles.header}>
<p className={styles.title}>Kill trial</p>
</div>
<div className={styles.inner}>
<div>
<p className={styles.subtext}>{prompString}</p>
</div>
</div>
<FocusZone>
<Stack className={styles.buttons} gap={8} horizontal>
<DefaultButton onClick={this.onDismiss}>No</DefaultButton>
<PrimaryButton onClick={this.onKill}>Yes</PrimaryButton>
</Stack>
</FocusZone>
</FocusTrapCallout>
</div>
) : null}
</div>
);
}
}
export default KillJob;
\ No newline at end of file
import * as React from 'react';
import axios from 'axios';
import { Drawer, Tabs, Row, Col, Button, Icon } from 'antd';
import {
Stack, StackItem, Panel, PrimaryButton, DefaultButton,
Pivot, PivotItem
} from 'office-ui-fabric-react';
import { infoIcon } from '../Buttons/Icon';
import { DOWNLOAD_IP } from '../../static/const';
import { downFile } from '../../static/function';
const { TabPane } = Tabs;
import MonacoHTML from '../public-child/MonacoEditor';
import '../../static/style/logDrawer.scss';
......@@ -45,22 +48,22 @@ class LogDrawer extends React.Component<LogDrawerProps, LogDrawerState> {
}
}
dispatcherHTML = (): any => {
dispatcherHTML = (): React.ReactNode => {
return (
<div>
<span>Dispatcher Log</span>
<span className="refresh" onClick={this.manualRefresh}>
<Icon type="sync" />
{infoIcon}
</span>
</div>
);
}
nnimanagerHTML = (): any => {
nnimanagerHTML = (): React.ReactNode => {
return (
<div>
<span>NNImanager Log</span>
<span className="refresh" onClick={this.manualRefresh}><Icon type="sync" /></span>
<span className="refresh" onClick={this.manualRefresh}>{infoIcon}</span>
</div>
);
}
......@@ -74,7 +77,7 @@ class LogDrawer extends React.Component<LogDrawerProps, LogDrawerState> {
window.addEventListener('resize', this.setLogDrawerHeight);
}
componentWillUnmount(): void {
componentWillUnmount(): void{
window.clearTimeout(this.timerId);
window.removeEventListener('resize', this.setLogDrawerHeight);
}
......@@ -82,85 +85,56 @@ class LogDrawer extends React.Component<LogDrawerProps, LogDrawerState> {
render(): React.ReactNode {
const { closeDrawer, activeTab } = this.props;
const { nniManagerLogStr, dispatcherLogStr, isLoading, logDrawerHeight } = this.state;
return (
<Row>
<Drawer
placement="right"
closable={false}
destroyOnClose={true}
onClose={closeDrawer}
visible={true}
width="76%"
height={logDrawerHeight}
// className="logDrawer"
<Stack>
<Panel
isOpen={true}
hasCloseButton={false}
isFooterAtBottom={true}
>
<div className="card-container log-tab-body">
<Tabs
type="card"
defaultActiveKey={activeTab}
style={{ height: logDrawerHeight, minHeight: 190 }}
<div className="log-tab-body">
<Pivot
selectedKey={activeTab}
style={{ minHeight: 190, paddingTop: '16px' }}
>
{/* <Tabs type="card" onTabClick={this.selectwhichLog} defaultActiveKey={activeTab}> */}
{/* <TabPane tab="Dispatcher Log" key="dispatcher"> */}
<TabPane tab={this.dispatcherHTML()} key="dispatcher">
<div>
<MonacoHTML
content={dispatcherLogStr || 'Loading...'}
loading={isLoading}
height={logDrawerHeight - 104}
/>
</div>
<Row className="buttons">
<Col span={12} className="download">
<Button
type="primary"
onClick={this.downloadDispatcher}
>
Download
</Button>
</Col>
<Col span={12} className="close">
<Button
type="default"
onClick={closeDrawer}
>
Close
</Button>
</Col>
</Row>
</TabPane>
<TabPane tab={this.nnimanagerHTML()} key="nnimanager">
{/* <PivotItem headerText={this.dispatcherHTML()} key="dispatcher" onLinkClick> */}
<PivotItem headerText="Dispatcher Log" key="dispatcher">
<MonacoHTML
content={dispatcherLogStr || 'Loading...'}
loading={isLoading}
// paddingTop[16] + tab[44] + button[32]
height={logDrawerHeight - 92}
/>
<Stack horizontal className="buttons">
<StackItem grow={12} className="download">
<PrimaryButton text="Download" onClick={this.downloadDispatcher} />
</StackItem>
<StackItem grow={12} className="close">
<DefaultButton text="Close" onClick={closeDrawer} />
</StackItem>
</Stack>
</PivotItem>
<PivotItem headerText="NNIManager Log" key="nnimanager">
{/* <TabPane tab="NNImanager Log" key="nnimanager"> */}
<div>
<MonacoHTML
content={nniManagerLogStr || 'Loading...'}
loading={isLoading}
height={logDrawerHeight - 104}
/>
</div>
<Row className="buttons">
<Col span={12} className="download">
<Button
type="primary"
onClick={this.downloadNNImanager}
>
Download
</Button>
</Col>
<Col span={12} className="close">
<Button
type="default"
onClick={closeDrawer}
>
Close
</Button>
</Col>
</Row>
</TabPane>
</Tabs>
<MonacoHTML
content={nniManagerLogStr || 'Loading...'}
loading={isLoading}
height={logDrawerHeight - 92}
/>
<Stack horizontal className="buttons">
<StackItem grow={12} className="download">
<PrimaryButton text="Download" onClick={this.downloadNNImanager} />
</StackItem>
<StackItem grow={12} className="close">
<DefaultButton text="Close" onClick={closeDrawer} />
</StackItem>
</Stack>
</PivotItem>
</Pivot>
</div>
</Drawer>
</Row>
</Panel>
</Stack>
);
}
......
import * as React from 'react';
import { MessageBar, MessageBarType } from 'office-ui-fabric-react';
interface MessageInfoProps {
info: string;
typeInfo: string;
className?: string;
}
class MessageInfo extends React.Component<MessageInfoProps, {}> {
constructor(props: MessageInfoProps) {
super(props);
}
render(): React.ReactNode {
const { info, typeInfo, className } = this.props;
return (
<MessageBar
messageBarType={MessageBarType[typeInfo]}
isMultiline={false}
className={className}
>
{info}
</MessageBar>
);
}
}
export default MessageInfo;
\ No newline at end of file
.ant-modal-body{
border-radius: none;
}
.ant-modal-title {
font-size: 18px;
}
/* resubmit confirm modal style */
.resubmit{
.title{
......@@ -29,17 +23,17 @@
padding: 16px 18px 16px 16px;
}
.hyper-form{
height: 32px;
margin-bottom: 8px;
margin-bottom: 10px;
.title{
font-size: 14px;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 32px;
line-height: 40px;
}
.inputs{
height: 32px;
}
input{
padding-left: 5px;
height: 32px;
}
}
......@@ -69,3 +63,24 @@
.center{
text-align: center;
}
.icon-color{
i{
color: green;
}
}
.icon-error{
i{
color: red;
}
}
.icon-color, .icon-error{
i{
margin-right: 10px;
position: relative;
top: 5px;
}
}
.ms-Dialog-subText{
color: #333;
}
\ No newline at end of file
import * as React from 'react';
import axios from 'axios';
import { WEBUIDOC, MANAGER_IP } from '../static/const';
import {
Stack, initializeIcons, StackItem, CommandBarButton,
IContextualMenuProps, IStackTokens, IStackStyles
} from 'office-ui-fabric-react';
import LogDrawer from './Modals/LogDrawer';
import ExperimentDrawer from './Modals/ExperimentDrawer';
import {
downLoadIcon, infoIconAbout,
timeIcon, disableUpdates, requency, closeTimer
} from './Buttons/Icon';
import { OVERVIEWTABS, DETAILTABS, NNILOGO } from './stateless-component/NNItabs';
import '../static/style/nav/nav.scss';
import '../static/style/icon.scss';
initializeIcons();
const stackTokens: IStackTokens = {
childrenGap: 15
};
const stackStyle: IStackStyles = {
root: {
minWidth: 400, height: 56, display: 'flex', verticalAlign: 'center'
}
};
interface NavState {
version: string;
menuVisible: boolean;
navBarVisible: boolean;
isdisabledFresh: boolean;
isvisibleLogDrawer: boolean;
isvisibleExperimentDrawer: boolean;
refreshText: string;
refreshFrequency: number | string;
}
interface NavProps {
changeInterval: (value: number) => void;
refreshFunction: () => void;
}
class NavCon extends React.Component<NavProps, NavState> {
constructor(props: NavProps) {
super(props);
this.state = {
version: '',
menuVisible: false,
navBarVisible: false,
isdisabledFresh: false,
isvisibleLogDrawer: false, // download button (nnimanager·dispatcher) click -> drawer
isvisibleExperimentDrawer: false,
refreshText: 'Auto refresh',
refreshFrequency: 10
};
}
// to see & download experiment parameters
showExpcontent = (): void => {
this.setState({ isvisibleExperimentDrawer: true });
}
// to see & download dispatcher | nnimanager log
showDispatcherLog = (): void => {
this.setState({ isvisibleLogDrawer: true });
}
// close log drawer (nnimanager.dispatcher)
closeLogDrawer = (): void => {
this.setState({ isvisibleLogDrawer: false });
}
// close download experiment parameters drawer
closeExpDrawer = (): void => {
this.setState({ isvisibleExperimentDrawer: false });
}
getNNIversion = (): void => {
axios(`${MANAGER_IP}/version`, {
method: 'GET'
})
.then(res => {
if (res.status === 200) {
this.setState({ version: res.data });
}
});
}
openGithub = (): void => {
const { version } = this.state;
const feed = `https://github.com/Microsoft/nni/issues/new?labels=${version}`;
window.open(feed);
}
openDocs = (): void => {
window.open(WEBUIDOC);
}
openGithubNNI = (): void => {
const {version} = this.state;
const nniLink = `https://github.com/Microsoft/nni/tree/${version}`;
window.open(nniLink);
}
getInterval = (num: number): void => {
this.props.changeInterval(num); // notice parent component
this.setState(() => ({
refreshFrequency: num === 0 ? '' : num,
refreshText: num === 0 ? 'Disable auto' : 'Auto refresh'
}));
}
componentDidMount(): void {
this.getNNIversion();
}
render(): React.ReactNode {
const { isvisibleLogDrawer, isvisibleExperimentDrawer, version,
refreshText, refreshFrequency } = this.state;
const aboutProps: IContextualMenuProps = {
items: [
{
key: 'feedback',
text: 'Feedback',
iconProps: { iconName: 'OfficeChat' },
onClick: this.openGithub
},
{
key: 'help',
text: 'Document',
iconProps: { iconName: 'TextDocument' },
onClick: this.openDocs
},
{
key: 'version',
text: `Version ${version}`,
iconProps: { iconName: 'VerifiedBrand' },
onClick: this.openGithubNNI
}
]
};
return (
<Stack horizontal className="nav">
<StackItem grow={30} styles={{ root: { minWidth: 300, display: 'flex', verticalAlign: 'center' } }}>
<span className="desktop-logo">{NNILOGO}</span>
<span className="left-right-margin">{OVERVIEWTABS}</span>
<span>{DETAILTABS}</span>
</StackItem>
<StackItem grow={70} className="navOptions">
<Stack horizontal horizontalAlign="end" tokens={stackTokens} styles={stackStyle}>
{/* refresh button danyi*/}
{/* TODO: fix bug */}
{/* <CommandBarButton
iconProps={{ iconName: 'sync' }}
text="Refresh"
onClick={this.props.refreshFunction}
/> */}
<div className="nav-refresh">
<CommandBarButton
iconProps={refreshFrequency === '' ? disableUpdates : timeIcon}
text={refreshText}
menuProps={this.refreshProps}
/>
<div className="nav-refresh-num">{refreshFrequency}</div>
</div>
<CommandBarButton
iconProps={downLoadIcon}
text="Download"
menuProps={this.menuProps}
/>
<CommandBarButton
iconProps={infoIconAbout}
text="about"
menuProps={aboutProps}
/>
</Stack>
</StackItem>
{/* the drawer for dispatcher & nnimanager log message */}
{isvisibleLogDrawer && <LogDrawer closeDrawer={this.closeLogDrawer} />}
<ExperimentDrawer isVisble={isvisibleExperimentDrawer} closeExpDrawer={this.closeExpDrawer} />
</Stack>
);
}
// view and download experiment [log & experiment result]
private menuProps: IContextualMenuProps = {
items: [
{
key: 'experiment',
text: 'Experiment Summary',
iconProps: { iconName: 'ShowResults' },
onClick: this.showExpcontent
},
{
key: 'logfiles',
text: 'Logfiles',
iconProps: { iconName: 'FilePDB' },
onClick: this.showDispatcherLog
}
],
directionalHintFixed: true
};
private refreshProps: IContextualMenuProps = {
items: [
{
key: 'disableRefresh',
text: 'Disable auto refresh',
iconProps: closeTimer,
onClick: this.getInterval.bind(this, 0)
},
{
key: 'refresh10',
text: 'Refresh every 10s',
iconProps: requency,
onClick: this.getInterval.bind(this, 10)
},
{
key: 'refresh20',
text: 'Refresh every 20s',
iconProps: requency,
onClick: this.getInterval.bind(this, 20)
},
{
key: 'refresh30',
text: 'Refresh every 30s',
iconProps: requency,
onClick: this.getInterval.bind(this, 30)
},
{
key: 'refresh60',
text: 'Refresh every 1min',
iconProps: requency,
onClick: this.getInterval.bind(this, 60)
},
]
};
}
export default NavCon;
import * as React from 'react';
import { Row, Col } from 'antd';
import { Stack, IStackTokens } from 'office-ui-fabric-react';
import { EXPERIMENT, TRIALS } from '../static/datamodel';
import { Trial } from '../static/model/trial';
import SuccessTable from './overview/SuccessTable';
import Title1 from './overview/Title1';
import SuccessTable from './overview/SuccessTable';
import Progressed from './overview/Progress';
import Accuracy from './overview/Accuracy';
import SearchSpace from './overview/SearchSpace';
import BasicInfo from './overview/BasicInfo';
import TrialInfo from './overview/TrialProfile';
require('../static/style/overview.scss');
require('../static/style/logPath.scss');
require('../static/style/accuracy.css');
require('../static/style/table.scss');
require('../static/style/overviewTitle.scss');
import '../static/style/overview.scss';
import '../static/style/logPath.scss';
interface OverviewProps {
experimentUpdateBroadcast: number;
......@@ -55,9 +51,7 @@ class Overview extends React.Component<OverviewProps, OverviewState> {
render(): React.ReactNode {
const { trialConcurrency } = this.state;
const { experimentUpdateBroadcast, metricGraphMode } = this.props;
const searchSpace = this.convertSearchSpace();
const bestTrials = this.findBestTrials();
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const bestAccuracy = bestTrials.length > 0 ? bestTrials[0].accuracy! : NaN;
......@@ -67,16 +61,20 @@ class Overview extends React.Component<OverviewProps, OverviewState> {
const titleMaxbgcolor = (metricGraphMode === 'max' ? '#999' : '#b3b3b3');
const titleMinbgcolor = (metricGraphMode === 'min' ? '#999' : '#b3b3b3');
const stackTokens: IStackTokens = {
childrenGap: 30,
};
return (
<div className="overview">
{/* status and experiment block */}
<Row>
<Stack>
<Title1 text="Experiment" icon="11.png" />
<BasicInfo experimentUpdateBroadcast={experimentUpdateBroadcast} />
</Row>
<Row className="overMessage">
{/* status graph */}
<Col span={9} className="prograph overviewBoder cc">
</Stack>
<Stack horizontal className="overMessage">
{/* status block */}
<Stack.Item styles={{ root: { maxWidth: 440 } }} className="prograph overviewBoder cc">
<Title1 text="Status" icon="5.png" />
<Progressed
bestAccuracy={bestAccuracy}
......@@ -84,17 +82,17 @@ class Overview extends React.Component<OverviewProps, OverviewState> {
changeConcurrency={this.changeConcurrency}
experimentUpdateBroadcast={experimentUpdateBroadcast}
/>
</Col>
</Stack.Item>
{/* experiment parameters search space tuner assessor... */}
<Col span={7} className="overviewBoder cc">
<Stack.Item styles={{root: {width: 400}}} className="overviewBoder">
<Title1 text="Search space" icon="10.png" />
<Row className="experiment">
<Stack className="experiment">
<SearchSpace searchSpace={searchSpace} />
</Row>
</Col>
<Col span={8} className="overviewBoder cc">
<Title1 text="Profile" icon="4.png" />
<Row className="experiment">
</Stack>
</Stack.Item>
<Stack.Item styles={{root: {width: 400}}}>
<Title1 text="Config" icon="4.png" />
<Stack className="experiment">
{/* the scroll bar all the trial profile in the searchSpace div*/}
<div className="experiment searchSpace">
<TrialInfo
......@@ -102,44 +100,38 @@ class Overview extends React.Component<OverviewProps, OverviewState> {
concurrency={trialConcurrency}
/>
</div>
</Row>
</Col>
</Row>
<Row className="overGraph">
<Row className="top10bg">
<Col span={4} className="top10Title">
<Title1 text="Top10 trials" icon="7.png" />
</Col>
<Col
span={2}
</Stack>
</Stack.Item>
</Stack>
<Stack>
<Stack horizontal className="top10bg">
<div
className="title"
onClick={this.clickMaxTop}
>
<Title1 text="Maximal" icon="max.png" bgcolor={titleMaxbgcolor} />
</Col>
<Col
span={2}
<Title1 text="Top10 Maximal trials" icon="max.png" bgcolor={titleMaxbgcolor} />
</div>
<div
className="title minTitle"
onClick={this.clickMinTop}
>
<Title1 text="Minimal" icon="min.png" bgcolor={titleMinbgcolor} />
</Col>
</Row>
<Row>
<Col span={8} className="overviewBoder">
<Row className="accuracy">
<Accuracy
accuracyData={accuracyGraphData}
accNodata={noDataMessage}
height={324}
/>
</Row>
</Col>
<Col span={16} id="succeTable">
<SuccessTable trialIds={bestTrials.map(trial => trial.info.id)}/>
</Col>
</Row>
</Row>
<Title1 text="Top10 Minimal trials" icon="min.png" bgcolor={titleMinbgcolor} />
</div>
</Stack>
<Stack horizontal tokens={stackTokens}>
<div style={{ width: '40%'}}>
<Accuracy
accuracyData={accuracyGraphData}
accNodata={noDataMessage}
height={404}
/>
</div>
<div style={{ width: '60%'}}>
<SuccessTable trialIds={bestTrials.map(trial => trial.info.id)} />
</div>
</Stack>
</Stack>
</div>
);
}
......
import * as React from 'react';
import { Link } from 'react-router';
import axios from 'axios';
import { MANAGER_IP } from '../static/const';
import MediaQuery from 'react-responsive';
import { Row, Col, Menu, Dropdown, Icon, Select, Button, Form } from 'antd';
import { FormComponentProps } from 'antd/lib/form';
import { OVERVIEWTABS, DETAILTABS, NNILOGO } from './stateless-component/NNItabs';
const { SubMenu } = Menu;
const { Option } = Select;
const FormItem = Form.Item;
import LogDrawer from './Modal/LogDrawer';
import ExperimentDrawer from './Modal/ExperimentDrawer';
import '../static/style/slideBar.scss';
import '../static/style/button.scss';
interface SliderState {
version: string;
menuVisible: boolean;
navBarVisible: boolean;
isdisabledFresh: boolean;
isvisibleLogDrawer: boolean;
isvisibleExperimentDrawer: boolean;
activeKey: string;
}
interface SliderProps extends FormComponentProps {
changeInterval: (value: number) => void;
}
interface EventPer {
key: string;
}
class SlideBar extends React.Component<SliderProps, SliderState> {
public divMenu: HTMLDivElement | null;
public selectHTML: Select | null;
constructor(props: SliderProps) {
super(props);
this.state = {
version: '',
menuVisible: false,
navBarVisible: false,
isdisabledFresh: false,
isvisibleLogDrawer: false, // download button (nnimanager·dispatcher) click -> drawer
isvisibleExperimentDrawer: false,
activeKey: 'dispatcher'
};
}
getNNIversion = (): void => {
axios(`${MANAGER_IP}/version`, {
method: 'GET'
})
.then(res => {
if (res.status === 200) {
this.setState({ version: res.data });
}
});
}
handleMenuClick = (e: EventPer): void => {
this.setState({ menuVisible: false });
switch (e.key) {
// to see & download experiment parameters
case '1':
this.setState({ isvisibleExperimentDrawer: true });
break;
// to see & download nnimanager log
case '2':
this.setState({ activeKey: 'nnimanager', isvisibleLogDrawer: true });
break;
// to see & download dispatcher log
case '3':
this.setState({ isvisibleLogDrawer: true, activeKey: 'dispatcher' });
break;
case 'close':
case '10':
case '20':
case '30':
case '60':
this.getInterval(e.key);
break;
default:
}
}
handleVisibleChange = (flag: boolean): void => {
this.setState({ menuVisible: flag });
}
getInterval = (value: string): void => {
if (value === 'close') {
this.props.changeInterval(0);
} else {
this.props.changeInterval(parseInt(value, 10));
}
}
menu = (): any => {
return (
<Menu onClick={this.handleMenuClick}>
<Menu.Item key="1">Experiment Parameters</Menu.Item>
<Menu.Item key="2">NNImanager Logfile</Menu.Item>
<Menu.Item key="3">Dispatcher Logfile</Menu.Item>
</Menu>
);
}
// nav bar
navigationBar = (): any => {
const { version } = this.state;
const feedBackLink = `https://github.com/Microsoft/nni/issues/new?labels=${version}`;
return (
<Menu onClick={this.handleMenuClick} className="menu-list" style={{ width: 216 }}>
{/* <Menu onClick={this.handleMenuClick} className="menu-list" style={{width: window.innerWidth}}> */}
<Menu.Item key="feedback">
<a href={feedBackLink} rel="noopener noreferrer" target="_blank">Feedback</a>
</Menu.Item>
<Menu.Item key="version">Version: {version}</Menu.Item>
<SubMenu
key="download"
onChange={this.handleVisibleChange}
title={
<span>
<span>View</span>
</span>
}
>
<Menu.Item key="1">Experiment Parameters</Menu.Item>
<Menu.Item key="2">NNImanager Logfile</Menu.Item>
<Menu.Item key="3">Dispatcher Logfile</Menu.Item>
</SubMenu>
</Menu>
);
}
mobileTabs = (): any => {
return (
// <Menu className="menuModal" style={{width: 880, position: 'fixed', left: 0, top: 56}}>
<Menu className="menuModal" style={{ padding: '0 10px' }}>
<Menu.Item key="overview"><Link to={'/oview'}>Overview</Link></Menu.Item>
<Menu.Item key="detail"><Link to={'/detail'}>Trials detail</Link></Menu.Item>
</Menu>
);
}
refreshInterval = (): any => {
const {
form: { getFieldDecorator },
// form: { getFieldDecorator, getFieldValue },
} = this.props;
return (
<Form>
<FormItem style={{ marginBottom: 0 }}>
{getFieldDecorator('interval', {
initialValue: 'Refresh every 10s',
})(
<Select onSelect={this.getInterval}>
<Option value="close">Disable Auto Refresh</Option>
<Option value="10">Refresh every 10s</Option>
<Option value="20">Refresh every 20s</Option>
<Option value="30">Refresh every 30s</Option>
<Option value="60">Refresh every 1min</Option>
</Select>,
)}
</FormItem>
</Form>
);
}
select = (): any => {
const { isdisabledFresh } = this.state;
return (
<div className="interval">
{this.refreshInterval()}
<Button
className="fresh"
onClick={this.fresh}
type="ghost"
disabled={isdisabledFresh}
>
<Icon type="sync" /><span>Refresh</span>
</Button>
</div>
);
}
fresh = (event: React.SyntheticEvent<EventTarget>): void => {
event.preventDefault();
event.stopPropagation();
this.setState({ isdisabledFresh: true }, () => {
setTimeout(() => { this.setState({ isdisabledFresh: false }); }, 1000);
});
}
desktopHTML = (): any => {
const { version, menuVisible } = this.state;
const feed = `https://github.com/Microsoft/nni/issues/new?labels=${version}`;
return (
<Row className="nav">
<Col span={8}>
<span className="desktop-logo">{NNILOGO}</span>
<span className="left-right-margin">{OVERVIEWTABS}</span>
<span>{DETAILTABS}</span>
</Col>
<Col span={16} className="desktop-right">
<span>
<Button
className="fresh"
type="ghost"
>
<a target="_blank" rel="noopener noreferrer" href="https://nni.readthedocs.io/en/latest/Tutorial/WebUI.html">
<img
src={require('../static/img/icon/ques.png')}
alt="question"
className="question"
/>
<span>Help</span>
</a>
</Button>
</span>
<span>{this.select()}</span>
<span>
<Dropdown
className="dropdown"
overlay={this.menu()}
onVisibleChange={this.handleVisibleChange}
visible={menuVisible}
trigger={['click']}
>
<a className="ant-dropdown-link" href="#">
<Icon type="download" className="down-icon" />
<span>View</span>
{
menuVisible
?
<Icon type="up" className="margin-icon" />
:
<Icon type="down" className="margin-icon" />
}
</a>
</Dropdown>
</span>
<span className="feedback">
<a href={feed} target="_blank" rel="noopener noreferrer">
<img
src={require('../static/img/icon/issue.png')}
alt="NNI github issue"
/>
Feedback
</a>
</span>
<span className="version">Version: {version}</span>
</Col>
</Row>
);
}
tabeltHTML = (): any => {
return (
<Row className="nav">
<Col className="tabelt-left" span={14}>
<span>
<Dropdown overlay={this.navigationBar()} trigger={['click']}>
<Icon type="unordered-list" className="more" />
</Dropdown>
</span>
<span className="left-right-margin tabelt-img">{NNILOGO}</span>
<span>{OVERVIEWTABS}</span>
<span className="left-margin">{DETAILTABS}</span>
</Col>
<Col className="tabelt-right" span={10}>
{this.select()}
</Col>
</Row>
);
}
mobileHTML = (): any => {
const { isdisabledFresh } = this.state;
return (
<Row className="nav">
<Col className="left" span={8}>
<span>
<Dropdown className="more-mobile" overlay={this.navigationBar()} trigger={['click']}>
<Icon type="unordered-list" className="more" />
</Dropdown>
</span>
<span>
<Dropdown overlay={this.mobileTabs()} trigger={['click']}>
<a className="ant-dropdown-link" href="#">
<span>NNI <Icon type="down" /></span>
</a>
</Dropdown>
</span>
</Col>
<Col className="center" span={8}>
<img
src={require('../static/img/logo2.png')}
alt="NNI logo"
/>
</Col>
{/* the class interval have other style ! */}
<Col className="right interval" span={8}>
<Button
className="fresh"
onClick={this.fresh}
type="ghost"
disabled={isdisabledFresh}
>
<Icon type="sync" /><span>Refresh</span>
</Button>
</Col>
</Row>
);
}
// close log drawer (nnimanager.dispatcher)
closeLogDrawer = (): void => {
this.setState({ isvisibleLogDrawer: false, activeKey: '' });
}
// close download experiment parameters drawer
closeExpDrawer = (): void => {
this.setState({ isvisibleExperimentDrawer: false });
}
componentDidMount(): void {
this.getNNIversion();
}
render(): React.ReactNode {
const mobile = (<MediaQuery maxWidth={884}>{this.mobileHTML()}</MediaQuery>);
const tablet = (<MediaQuery minWidth={885} maxWidth={1281}>{this.tabeltHTML()}</MediaQuery>);
const desktop = (<MediaQuery minWidth={1282}>{this.desktopHTML()}</MediaQuery>);
const { isvisibleLogDrawer, activeKey, isvisibleExperimentDrawer } = this.state;
return (
<div>
{mobile}
{tablet}
{desktop}
{/* the drawer for dispatcher & nnimanager log message */}
{isvisibleLogDrawer ? (
<LogDrawer
closeDrawer={this.closeLogDrawer}
activeTab={activeKey}
/>
) : null}
<ExperimentDrawer
isVisble={isvisibleExperimentDrawer}
closeExpDrawer={this.closeExpDrawer}
/>
</div>
);
}
}
export default Form.create<FormComponentProps>()(SlideBar);
import * as React from 'react';
import { Row, Col, Tabs, Select, Button, Icon } from 'antd';
const Option = Select.Option;
import {
Stack, StackItem, Pivot, PivotItem, Dropdown, IDropdownOption, DefaultButton
} from 'office-ui-fabric-react';
import { EXPERIMENT, TRIALS } from '../static/datamodel';
import { Trial } from '../static/model/trial';
import { tableListIcon } from './Buttons/Icon';
import DefaultPoint from './trial-detail/DefaultMetricPoint';
import Duration from './trial-detail/Duration';
import Title1 from './overview/Title1';
import Para from './trial-detail/Para';
import Intermediate from './trial-detail/Intermediate';
import TableList from './trial-detail/TableList';
const TabPane = Tabs.TabPane;
import '../static/style/trialsDetail.scss';
import '../static/style/search.scss';
......@@ -21,8 +21,8 @@ interface TrialDetailState {
}
interface TrialsDetailProps {
columnList: Array<string>;
changeColumn: (val: Array<string>) => void;
columnList: string[];
changeColumn: (val: string[]) => void;
experimentUpdateBroacast: number;
trialsUpdateBroadcast: number;
}
......@@ -32,44 +32,25 @@ class TrialsDetail extends React.Component<TrialsDetailProps, TrialDetailState>
public interAccuracy = 0;
public interAllTableList = 2;
public tableList: TableList | null;
public searchInput: HTMLInputElement | null;
private titleOfacc = (
<Title1 text="Default metric" icon="3.png" />
);
private titleOfhyper = (
<Title1 text="Hyper-parameter" icon="1.png" />
);
private titleOfDuration = (
<Title1 text="Trial duration" icon="2.png" />
);
private titleOfIntermediate = (
<div className="panelTitle">
<Icon type="line-chart" />
<span>Intermediate result</span>
</div>
);
public tableList!: TableList | null;
public searchInput!: HTMLInputElement | null;
constructor(props: TrialsDetailProps) {
super(props);
this.state = {
tablePageSize: 20,
whichGraph: '1',
searchType: 'id',
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type, @typescript-eslint/no-unused-vars
searchFilter: trial => true
searchType: 'Id',
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/explicit-function-return-type
searchFilter: trial => true
};
}
// search a trial by trial No. & trial id
// search a trial by trial No. | trial id | Parameters | Status
searchTrial = (event: React.ChangeEvent<HTMLInputElement>): void => {
const targetValue = event.target.value;
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type, @typescript-eslint/no-unused-vars
let filter = (trial: Trial) => true;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
let filter = (trial: Trial): boolean => true;
if (!targetValue.trim()) {
this.setState({ searchFilter: filter });
return;
......@@ -81,7 +62,7 @@ class TrialsDetail extends React.Component<TrialsDetailProps, TrialDetailState>
case 'Trial No.':
filter = (trial): boolean => trial.info.sequenceId.toString() === targetValue;
break;
case 'status':
case 'Status':
filter = (trial): boolean => trial.info.status.toUpperCase().includes(targetValue.toUpperCase());
break;
case 'parameters':
......@@ -94,112 +75,119 @@ class TrialsDetail extends React.Component<TrialsDetailProps, TrialDetailState>
this.setState({ searchFilter: filter });
}
handleTablePageSizeSelect = (value: string): void => {
this.setState({ tablePageSize: value === 'all' ? -1 : parseInt(value, 10) });
handleTablePageSizeSelect = (event: React.FormEvent<HTMLDivElement>, item: IDropdownOption | undefined): void => {
if (item !== undefined) {
this.setState({ tablePageSize: item.text === 'all' ? -1 : parseInt(item.text, 10) });
}
}
handleWhichTabs = (activeKey: string): void => {
this.setState({ whichGraph: activeKey });
}
updateSearchFilterType = (value: string): void => {
updateSearchFilterType = (event: React.FormEvent<HTMLDivElement>, item: IDropdownOption | undefined): void => {
// clear input value and re-render table
if (this.searchInput !== null) {
this.searchInput.value = '';
if (item !== undefined) {
if (this.searchInput !== null) {
this.searchInput.value = '';
}
this.setState(() => ({ searchType: item.text }));
}
this.setState({ searchType: value });
}
render(): React.ReactNode {
const { tablePageSize, whichGraph } = this.state;
const { tablePageSize, whichGraph, searchType } = this.state;
const { columnList, changeColumn } = this.props;
const source = TRIALS.filter(this.state.searchFilter);
const trialIds = TRIALS.filter(this.state.searchFilter).map(trial => trial.id);
const searchOptions = [
{ key: 'Id', text: 'Id' },
{ key: 'Trial No.', text: 'Trial No.' },
{ key: 'Status', text: 'Status' },
{ key: 'Parameters', text: 'Parameters' },
];
return (
<div>
<div className="trial" id="tabsty">
<Tabs type="card" onChange={this.handleWhichTabs}>
<TabPane tab={this.titleOfacc} key="1">
<Row className="graph">
<Pivot defaultSelectedKey={"0"} className="detial-title">
{/* <PivotItem tab={this.titleOfacc} key="1"> doesn't work*/}
<PivotItem headerText="Default metric" itemIcon="HomeGroup" key="1">
<Stack className="graph">
<DefaultPoint
trialIds={trialIds}
visible={whichGraph === '1'}
trialsUpdateBroadcast={this.props.trialsUpdateBroadcast}
/>
</Row>
</TabPane>
<TabPane tab={this.titleOfhyper} key="2">
<Row className="graph">
</Stack>
</PivotItem>
{/* <PivotItem tab={this.titleOfhyper} key="2"> */}
<PivotItem headerText="Hyper-parameter" itemIcon="Equalizer" key="2">
<Stack className="graph">
<Para
dataSource={source}
expSearchSpace={JSON.stringify(EXPERIMENT.searchSpace)}
whichGraph={whichGraph}
/>
</Row>
</TabPane>
<TabPane tab={this.titleOfDuration} key="3">
</Stack>
</PivotItem>
{/* <PivotItem tab={this.titleOfDuration} key="3"> */}
<PivotItem headerText="Duration" itemIcon="BarChartHorizontal" key="3">
<Duration source={source} whichGraph={whichGraph} />
</TabPane>
<TabPane tab={this.titleOfIntermediate} key="4">
</PivotItem>
{/* <PivotItem tab={this.titleOfIntermediate} key="4"> */}
<PivotItem headerText="Intermediate result" itemIcon="StackedLineChart" key="4">
{/* *why this graph has small footprint? */}
<Intermediate source={source} whichGraph={whichGraph} />
</TabPane>
</Tabs>
</PivotItem>
</Pivot>
</div>
{/* trial table list */}
<Title1 text="Trial jobs" icon="6.png" />
<Row className="allList">
<Col span={10}>
<span>Show</span>
<Select
className="entry"
onSelect={this.handleTablePageSizeSelect}
defaultValue="20"
>
<Option value="20">20</Option>
<Option value="50">50</Option>
<Option value="100">100</Option>
<Option value="all">All</Option>
</Select>
<span>entries</span>
</Col>
<Col span={14} className="right">
<Button
className="common"
onClick={(): void => { if (this.tableList) { this.tableList.addColumn(); }}}
>
Add column
</Button>
<Button
className="mediateBtn common"
<Stack horizontal className="panelTitle">
<span style={{ marginRight: 12 }}>{tableListIcon}</span>
<span>Trial jobs</span>
</Stack>
<Stack horizontal className="allList">
<StackItem grow={50}>
<DefaultButton
text="Compare"
className="allList-compare"
// use child-component tableList's function, the function is in child-component.
onClick={(): void => { if (this.tableList) { this.tableList.compareBtn(); }}}
>
Compare
</Button>
<Select defaultValue="id" className="filter" onSelect={this.updateSearchFilterType}>
<Option value="id">Id</Option>
<Option value="Trial No.">Trial No.</Option>
<Option value="status">Status</Option>
<Option value="parameters">Parameters</Option>
</Select>
<input
type="text"
className="search-input"
placeholder={`Search by ${this.state.searchType}`}
onChange={this.searchTrial}
style={{ width: 230 }}
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
ref={text => (this.searchInput) = text}
onClick={(): void => { if (this.tableList) { this.tableList.compareBtn(); } }}
/>
</Col>
</Row>
</StackItem>
<StackItem grow={50}>
<Stack horizontal horizontalAlign="end" className="allList">
<DefaultButton
className="allList-button-gap"
text="Add column"
onClick={(): void => { if (this.tableList) { this.tableList.addColumn(); } }}
/>
<Dropdown
selectedKey={searchType}
options={searchOptions}
onChange={this.updateSearchFilterType}
styles={{ root: { width: 150 } }}
/>
<input
type="text"
className="allList-search-input"
placeholder={`Search by ${this.state.searchType}`}
onChange={this.searchTrial}
style={{ width: 230 }}
ref={(text): any => (this.searchInput) = text}
/>
</Stack>
</StackItem>
</Stack>
<TableList
pageSize={tablePageSize}
tableSource={source.map(trial => trial.tableRecord)}
columnList={columnList}
changeColumn={changeColumn}
trialsUpdateBroadcast={this.props.trialsUpdateBroadcast}
ref={(tabList) => this.tableList = tabList} // eslint-disable-line @typescript-eslint/explicit-function-return-type
// TODO: change any to specific type
ref={(tabList): any => this.tableList = tabList}
/>
</div>
);
......
import * as React from 'react';
import ReactEcharts from 'echarts-for-react';
const echarts = require('echarts/lib/echarts');
import echarts from 'echarts/lib/echarts';
echarts.registerTheme('my_theme', {
color: '#3c8dbc'
});
require('echarts/lib/chart/scatter');
require('echarts/lib/component/tooltip');
require('echarts/lib/component/title');
import 'echarts/lib/chart/scatter';
import 'echarts/lib/component/tooltip';
import 'echarts/lib/component/title';
interface AccuracyProps {
accuracyData: object;
......@@ -28,7 +28,6 @@ class Accuracy extends React.Component<AccuracyProps, {}> {
<ReactEcharts
option={accuracyData}
style={{
width: '90%',
height: height,
margin: '0 auto',
}}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment