Unverified Commit 4244d85a authored by Lijiaoa's avatar Lijiaoa Committed by GitHub
Browse files

update overview layout (#3046)

parent 1f1a2c1e
......@@ -29,6 +29,8 @@
width: 87%;
margin: 0 auto;
min-width: 1200px;
/* nav bar: 56 + marginTop: 18 */
margin-top: 74px;
margin-bottom: 30px;
}
......
......@@ -13,8 +13,9 @@ import { TrialCount } from './overview/count/TrialCount';
import { Command1 } from './overview/command/Command1';
import { Command2 } from './overview/command/Command2';
import { TitleContext } from './overview/TitleContext';
import { itemStyle1, itemStyleSucceed, itemStyle2, entriesOption } from './overview/overviewConst';
import { itemStyleSucceed, entriesOption } from './overview/overviewConst';
import '../static/style/overview/overview.scss';
import '../static/style/overview/topTrial.scss';
import '../static/style/logPath.scss';
interface OverviewState {
......@@ -89,42 +90,40 @@ class Overview extends React.Component<{}, OverviewState> {
</BestMetricContext.Provider>
</div>
{/* duration & trial numbers */}
<div className='overviewProgress'>
<div className='duration'>
<TitleContext.Provider value={{ text: 'Duration', icon: 'Timer' }}>
<Title />
</TitleContext.Provider>
<ExpDurationContext.Provider
value={{
maxExecDuration,
execDuration,
updateOverviewPage,
maxDurationUnit,
changeMaxDurationUnit
}}
>
<ExpDuration />
</ExpDurationContext.Provider>
</div>
<div className='trialCount'>
<TitleContext.Provider value={{ text: 'Trial numbers', icon: 'NumberSymbol' }}>
<Title />
</TitleContext.Provider>
<ExpDurationContext.Provider
value={{
maxExecDuration,
execDuration,
updateOverviewPage,
maxDurationUnit,
changeMaxDurationUnit
}}
>
<TrialCount />
</ExpDurationContext.Provider>
</div>
<div className='duration'>
<TitleContext.Provider value={{ text: 'Duration', icon: 'Timer' }}>
<Title />
</TitleContext.Provider>
<ExpDurationContext.Provider
value={{
maxExecDuration,
execDuration,
updateOverviewPage,
maxDurationUnit,
changeMaxDurationUnit
}}
>
<ExpDuration />
</ExpDurationContext.Provider>
</div>
<div className='trialCount'>
<TitleContext.Provider value={{ text: 'Trial numbers', icon: 'NumberSymbol' }}>
<Title />
</TitleContext.Provider>
<ExpDurationContext.Provider
value={{
maxExecDuration,
execDuration,
updateOverviewPage,
maxDurationUnit,
changeMaxDurationUnit
}}
>
<TrialCount />
</ExpDurationContext.Provider>
</div>
{/* table */}
<div className='overviewTable'>
<div className='overviewBestMetric'>
<Stack horizontal>
<div style={itemStyleSucceed}>
<TitleContext.Provider value={{ text: 'Top trials', icon: 'BulletedList' }}>
......@@ -167,6 +166,9 @@ class Overview extends React.Component<{}, OverviewState> {
</Stack>
</div>
</Stack>
<div className='overviewChart'>
<Accuracy accuracyData={accuracyGraphData} accNodata={noDataMessage} />
</div>
<SuccessTable trialIds={bestTrials.map(trial => trial.info.trialJobId)} />
</div>
<div className='overviewCommand1'>
......@@ -175,24 +177,6 @@ class Overview extends React.Component<{}, OverviewState> {
<div className='overviewCommand2'>
<Command2 />
</div>
<div className='overviewChart'>
<Stack horizontal>
<div style={itemStyle1}>
<TitleContext.Provider
value={{ text: 'Trial metric chart', icon: 'HomeGroup' }}
>
<Title />
</TitleContext.Provider>
</div>
<div style={itemStyle2}>
<Stack className='maxmin' horizontal>
<div className='circle' />
<div>{`Top ${this.context.metricGraphMode}imal trials`}</div>
</Stack>
</div>
</Stack>
<Accuracy accuracyData={accuracyGraphData} accNodata={noDataMessage} height={380} />
</div>
</div>
</div>
);
......@@ -219,8 +203,8 @@ class Overview extends React.Component<{}, OverviewState> {
return {
// support max show 0.0000000
grid: {
left: 67,
right: 40
x: 60,
y: 40
},
tooltip: {
trigger: 'item'
......
......@@ -11,7 +11,6 @@ import 'echarts/lib/component/title';
interface AccuracyProps {
accuracyData: object;
accNodata: string;
height: number;
}
class Accuracy extends React.Component<AccuracyProps, {}> {
......@@ -20,17 +19,10 @@ class Accuracy extends React.Component<AccuracyProps, {}> {
}
render(): React.ReactNode {
const { accNodata, accuracyData, height } = this.props;
const { accNodata, accuracyData } = this.props;
return (
<div style={{ position: 'relative' }}>
<ReactEcharts
option={accuracyData}
style={{
height: height,
margin: '0 auto'
}}
theme='my_theme'
/>
<div className='defaultMetricContainer'>
<ReactEcharts option={accuracyData} theme='my_theme' />
<div className='showMess'>{accNodata}</div>
</div>
);
......
......@@ -6,7 +6,7 @@ import { convertDuration, convertTimeAsUnit } from '../../../static/function';
import { EditExperimentParam } from './EditExperimentParam';
import { ExpDurationContext } from './ExpDurationContext';
import { EditExpeParamContext } from './context';
import { durationItem1, durationItem2 } from './commonStyle';
import { leftProgress, durationItem2, progressHeight } from './commonStyle';
import '../../../static/style/overview/count.scss';
export const ExpDuration = (): any => (
......@@ -19,7 +19,7 @@ export const ExpDuration = (): any => (
const maxExecDurationStr = convertTimeAsUnit(maxDurationUnit, maxExecDuration).toString();
return (
<Stack horizontal className='ExpDuration'>
<div style={durationItem1}>
<div style={leftProgress}>
<TooltipHost
content={`${convertDuration(tooltip)} remaining`}
directionalHint={DirectionalHint.bottomCenter}
......@@ -33,7 +33,11 @@ export const ExpDuration = (): any => (
}
}}
>
<ProgressIndicator className={EXPERIMENT.status} percentComplete={percent} barHeight={15} />
<ProgressIndicator
className={EXPERIMENT.status}
percentComplete={percent}
barHeight={progressHeight}
/>
</TooltipHost>
{/* execDuration / maxDuration: 20min / 1h */}
<div className='exp-progress'>
......
......@@ -5,7 +5,7 @@ import { CONTROLTYPE, TOOLTIP_BACKGROUND_COLOR, MAX_TRIAL_NUMBERS } from '../../
import { EditExperimentParam } from './EditExperimentParam';
import { EditExpeParamContext } from './context';
import { ExpDurationContext } from './ExpDurationContext';
import { trialCountItem1, trialCountItem2 } from './commonStyle';
import { leftProgress, trialCountItem2, progressHeight } from './commonStyle';
export const TrialCount = (): any => {
const count = TRIALS.countStatus();
......@@ -23,9 +23,9 @@ export const TrialCount = (): any => {
return (
<React.Fragment>
<Stack horizontal horizontalAlign='space-between' className='ExpDuration'>
<div style={trialCountItem1}>
<div style={leftProgress}>
<TooltipHost
content={bar2.toString()}
content={`${bar2.toString()} trials`}
directionalHint={DirectionalHint.bottomCenter}
tooltipProps={{
calloutProps: {
......@@ -40,7 +40,7 @@ export const TrialCount = (): any => {
<ProgressIndicator
className={EXPERIMENT.status}
percentComplete={bar2Percent}
barHeight={15}
barHeight={progressHeight}
/>
</TooltipHost>
<div className='exp-progress'>
......@@ -81,7 +81,7 @@ export const TrialCount = (): any => {
</EditExpeParamContext.Provider>
</div>
</Stack>
<Stack horizontal horizontalAlign='space-between' className='mess'>
<Stack horizontal horizontalAlign='space-between' className='trialStatus'>
<div className='basic'>
<p>Running</p>
<div>{count.get('RUNNING')}</div>
......
const durationItem1: React.CSSProperties = {
width: '33%'
const leftProgress: React.CSSProperties = {
width: '33%',
position: 'relative',
top: 6
};
const durationItem2: React.CSSProperties = {
width: '52%',
width: '51.5%',
paddingLeft: '15%'
};
const trialCountItem1: React.CSSProperties = {
width: '33%'
};
const trialCountItem2: React.CSSProperties = {
width: '52%'
width: '51.5%'
};
export { durationItem1, durationItem2, trialCountItem1, trialCountItem2 };
const progressHeight = 8;
export { leftProgress, durationItem2, trialCountItem2, progressHeight };
......@@ -23,76 +23,74 @@ export const ReBasicInfo = (): any => {
return (
<div>
<div className='basic'>
<p>
ID: <span>{EXPERIMENT.profile.id}</span>
</p>
<div>{EXPERIMENT.profile.params.experimentName}</div>
</div>
<div className='basic'>
<p>Status</p>
<Stack horizontal className='status'>
<span className={`${EXPERIMENT.status} status-text`}>{EXPERIMENT.status}</span>
{EXPERIMENT.status === 'ERROR' ? (
<div>
<div className={styles.buttonArea} ref={ref}>
<IconButton
iconProps={{ iconName: 'info' }}
onClick={isCalloutVisible ? onDismiss : showCallout}
/>
</div>
{isCalloutVisible && (
<Callout
className={styles.callout}
ariaLabelledBy={labelId}
ariaDescribedBy={descriptionId}
role='alertdialog'
gapSpace={0}
target={ref}
onDismiss={onDismiss}
setInitialFocus={true}
>
<div className={styles.header}>
<p className={styles.title} id={labelId} style={{ color: '#333' }}>
Error
</p>
</div>
<div className={styles.inner}>
<p className={styles.subtext} id={descriptionId} style={{ color: '#333' }}>
{EXPERIMENT.error}
</p>
<div className={styles.actions}>
<Link className={styles.link} onClick={ShowLogDrawer}>
Learn about
</Link>
<Stack horizontal horizontalAlign='space-between' className='mess'>
<div className='basic'>
<p>Name</p>
<div className='nowrap'>{EXPERIMENT.profile.params.experimentName}</div>
<p className='margin'>ID</p>
<div className='nowrap'>{EXPERIMENT.profile.id}</div>
</div>
<div className='basic'>
<p>Status</p>
<Stack horizontal className='status'>
<span className={`${EXPERIMENT.status} status-text`}>{EXPERIMENT.status}</span>
{EXPERIMENT.status === 'ERROR' ? (
<div>
<div className={styles.buttonArea} ref={ref}>
<IconButton
iconProps={{ iconName: 'info' }}
onClick={isCalloutVisible ? onDismiss : showCallout}
/>
</div>
{isCalloutVisible && (
<Callout
className={styles.callout}
ariaLabelledBy={labelId}
ariaDescribedBy={descriptionId}
role='alertdialog'
gapSpace={0}
target={ref}
onDismiss={onDismiss}
setInitialFocus={true}
>
<div className={styles.header}>
<p className={`${styles.title} color`} id={labelId}>
Error
</p>
</div>
<div className={styles.inner}>
<p className={`${styles.subtext} color`} id={descriptionId}>
{EXPERIMENT.error}
</p>
<div className={styles.actions}>
<Link className={styles.link} onClick={ShowLogDrawer}>
Learn about
</Link>
</div>
</div>
</div>
</Callout>
)}
</div>
) : null}
</Stack>
</div>
<div className='basic'>
<BestMetricContext.Consumer>
{(value): React.ReactNode => (
<Stack className='bestMetric'>
<p>Best metric</p>
<div className={EXPERIMENT.status}>
{isNaN(value.bestAccuracy) ? 'N/A' : value.bestAccuracy.toFixed(6)}
</Callout>
)}
</div>
</Stack>
)}
</BestMetricContext.Consumer>
</div>
<div className='basic'>
<p>Start time</p>
<div className='nowrap'>{formatTimestamp(EXPERIMENT.profile.startTime)}</div>
</div>
<div className='basic'>
<p>End time</p>
<div className='nowrap'>{formatTimestamp(EXPERIMENT.profile.endTime)}</div>
</div>
) : null}
</Stack>
<BestMetricContext.Consumer>
{(value): React.ReactNode => (
<Stack className='bestMetric'>
<p className='margin'>Best metric</p>
<div className={EXPERIMENT.status}>
{isNaN(value.bestAccuracy) ? 'N/A' : value.bestAccuracy.toFixed(6)}
</div>
</Stack>
)}
</BestMetricContext.Consumer>
</div>
<div className='basic'>
<p>Start time</p>
<div className='nowrap'>{formatTimestamp(EXPERIMENT.profile.startTime)}</div>
<p className='margin'>End time</p>
<div className='nowrap'>{formatTimestamp(EXPERIMENT.profile.endTime)}</div>
</div>
</Stack>
{/* learn about click -> default active key is dispatcher. */}
{isShowLogDrawer ? <LogDrawer closeDrawer={closeLogDrawer} activeTab='dispatcher' /> : null}
</div>
......
import * as React from 'react';
import { DetailsList, IDetailsListProps, IColumn } from '@fluentui/react';
import {
DetailsList,
IDetailsListProps,
IColumn,
IRenderFunction,
IDetailsHeaderProps,
Sticky,
StickyPositionType,
ScrollablePane,
ScrollbarVisibility
} from '@fluentui/react';
import DefaultMetric from '../../public-child/DefaultMetric';
import Details from './Details';
import { convertDuration } from '../../../static/function';
......@@ -74,8 +84,8 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
name: 'Trial No.',
key: 'sequenceId',
fieldName: 'sequenceId', // required!
minWidth: 50,
maxWidth: 87,
minWidth: 65,
maxWidth: 119,
isResizable: true,
data: 'number',
onColumnClick: this.onColumnClick,
......@@ -85,8 +95,8 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
name: 'ID',
key: 'id',
fieldName: 'id',
minWidth: 50,
maxWidth: 87,
minWidth: 65,
maxWidth: 119,
isResizable: true,
className: 'tableHead leftTitle',
data: 'string',
......@@ -96,8 +106,8 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
{
name: 'Duration',
key: 'duration',
minWidth: 65,
maxWidth: 150,
minWidth: 90,
maxWidth: 166,
isResizable: true,
fieldName: 'duration',
data: 'number',
......@@ -111,8 +121,8 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
{
name: 'Status',
key: 'status',
minWidth: 80,
maxWidth: 150,
minWidth: 108,
maxWidth: 160,
isResizable: true,
fieldName: 'status',
onRender: (item: any): React.ReactNode => (
......@@ -123,8 +133,8 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
name: 'Default metric',
key: 'accuracy',
fieldName: 'accuracy',
minWidth: 100,
maxWidth: 160,
minWidth: 108,
maxWidth: 166,
isResizable: true,
data: 'number',
onColumnClick: this.onColumnClick,
......@@ -132,6 +142,20 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
}
];
onRenderDetailsHeader: IRenderFunction<IDetailsHeaderProps> = (props, defaultRender) => {
if (!props) {
return null;
}
return (
<Sticky stickyPosition={StickyPositionType.Header} isScrollSynced>
{// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
defaultRender!({
...props
})}
</Sticky>
);
};
setInnerWidth = (): void => {
this.setState(() => ({ innerWidth: window.innerWidth }));
};
......@@ -156,15 +180,18 @@ class SuccessTable extends React.Component<SuccessTableProps, SuccessTableState>
return (
<div id='succTable'>
<DetailsList
columns={columns}
items={source}
setKey='set'
compact={true}
onRenderRow={this.onRenderRow}
selectionMode={0} // close selector function
className='succTable'
/>
<ScrollablePane className='scrollPanel' scrollbarVisibility={ScrollbarVisibility.auto}>
<DetailsList
columns={columns}
items={source}
setKey='set'
compact={true}
onRenderRow={this.onRenderRow}
onRenderDetailsHeader={this.onRenderDetailsHeader}
selectionMode={0} // close selector function
className='succTable'
/>
</ScrollablePane>
{isNoneData && <div className='succTable-tooltip'>{this.tooltipStr}</div>}
</div>
);
......
$seriesIconMargin: 8px;
$seriesIconMargin: 10px;
.ExpDuration {
margin-top: 28px;
margin-top: 20px;
span:hover {
cursor: pointer;
......@@ -13,7 +13,7 @@ $seriesIconMargin: 8px;
}
.exp-progress {
margin-top: 10px;
margin-top: 16px;
.bold {
font-weight: 500;
......@@ -57,17 +57,17 @@ $seriesIconMargin: 8px;
}
&-dropdown {
width: 48px;
width: 65px;
display: inline-block;
position: relative;
top: 13px;
left: 4px;
margin-right: 3px;
}
}
.ExpDuration .series .confirm {
margin: 0 6px;
.ms-Dropdown-title {
padding-right: 0;
}
}
}
.series {
......@@ -114,4 +114,12 @@ $seriesIconMargin: 8px;
.basic p {
margin-top: 0;
}
p.margin {
margin-top: 20px;
}
}
.trialStatus {
margin-top: 8px;
}
......@@ -4,8 +4,8 @@ $boxGapPadding: 10px;
.wrapper {
display: grid;
grid-template-columns: repeat(9, 1fr);
grid-auto-rows: 97px;
grid-template-columns: repeat(8, 1fr);
grid-auto-rows: 93px;
> div {
background: #fff;
......@@ -14,61 +14,63 @@ $boxGapPadding: 10px;
box-sizing: border-box;
}
.overviewProgress {
grid-column: 2 / 6;
grid-row: 1 / 5;
display: grid;
grid-auto-rows: 70px;
margin: 0 $boxGapPadding;
padding: 0;
background: transparent;
.duration,
.trialCount {
background: #fff;
padding: $boxPadding;
border-radius: $boxBorderRadius;
box-sizing: border-box;
/* for alert message tooltip position */
position: relative;
}
.duration,
.trialCount {
grid-column: 1 / 5;
background: #fff;
padding: $boxPadding;
border-radius: $boxBorderRadius;
box-sizing: border-box;
margin-top: $boxGapPadding;
.duration {
// grid-row: 1 / 3;
height: 139px;
}
/* for alert message tooltip position */
position: relative;
}
.trialCount {
margin-top: 79px;
height: 239px;
}
.duration {
grid-row: 3 / 5;
height: 138px;
}
.trialCount {
grid-row: 4 / 6;
margin-top: 65px;
height: 239px;
}
.overviewCommand1,
.overviewCommand2 {
grid-row-start: 8;
margin-top: -59px;
margin-right: $boxGapPadding;
border-radius: 0;
}
.overviewCommand1 {
grid-column-start: 1;
grid-column: 1 / 5;
border-radius: $boxBorderRadius 0 0 $boxBorderRadius;
}
.overviewCommand2 {
grid-column: 2 / 6;
margin-right: 10px;
grid-column: 2 / 5;
margin-right: $boxGapPadding;
padding-left: 30px;
border-radius: 0 $boxBorderRadius $boxBorderRadius 0;
}
}
.overviewBasicInfo {
grid-column-start: 1;
grid-row: 1 / 5;
grid-column: 1 / 5;
grid-row: 1 / 3;
z-index: 2;
}
.overviewBasicInfo,
.duration,
.trialCount {
margin-right: $boxGapPadding;
}
.basic {
line-height: 21px;
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
......@@ -96,9 +98,10 @@ $boxGapPadding: 10px;
}
}
.overviewTable {
grid-column: 6 / 10;
grid-row: 1 / 11;
.overviewBestMetric {
grid-column: 5 / 9;
grid-row: 1 / 9;
max-height: 736px;
overflow: hidden;
.topTrialTitle {
......@@ -114,43 +117,33 @@ $boxGapPadding: 10px;
}
.mincenter {
margin: 0 13px 0 10px;
margin: 0 13px 0 $boxGapPadding;
}
.chooseEntry {
margin-right: 10px;
margin-right: $boxGapPadding;
line-height: 30px;
}
}
.overviewCommand1,
.overviewCommand2 {
grid-row: 7 / 9;
height: 144px;
overflow: hidden;
margin-top: 10px;
margin-top: $boxGapPadding;
}
$circle: 10px;
$bgblue: #0071bc;
.overviewChart {
grid-column: 1 / 6;
grid-row: 7 / 11;
margin-right: $boxGapPadding;
margin-top: -29px;
.circle {
width: $circle;
height: $circle;
border-radius: 50%;
background-color: $bgblue;
margin-top: 6px;
margin-right: 18px;
}
margin-top: 20px;
}
.showMess {
position: absolute;
top: 42%;
left: 48%;
.defaultMetricContainer {
position: relative;
.showMess {
position: absolute;
top: 42%;
left: 48%;
}
}
$iconPaddingVal: 20px;
$iconPaddingVal: 10px;
.panelTitle {
span {
......@@ -9,7 +9,7 @@ $iconPaddingVal: 20px;
i {
font-size: 24px;
margin-right: 20px;
margin-right: $iconPaddingVal;
color: #545454;
}
}
......@@ -18,22 +18,3 @@ $iconPaddingVal: 20px;
#tabsty .anticon {
margin-right: 0;
}
.top10bg {
.top10Title {
width: 160px;
}
.title {
border-left: 2px solid #fff;
}
.minTitle {
// margin-right: $iconPaddingVal;
border-right: 2px solid #fff;
}
.title:hover {
cursor: pointer;
}
}
$circle: 10px;
$bgblue: #0071bc;
.maxmin {
margin-top: 40px;
.circle {
width: $circle;
height: $circle;
border-radius: 50%;
background-color: $bgblue;
margin-top: 6px;
margin-right: 18px;
}
}
......@@ -6,7 +6,10 @@
.status-text {
display: inline-block;
line-height: 30px;
}
.color {
color: #333;
}
}
......@@ -60,7 +63,6 @@
/* office-fabric-ui progressIndicator */
.ms-ProgressIndicator-itemProgress {
padding: 0;
border: 2px solid #e6e6e6;
}
.cursor,
......
$tableHeight: 381px;
.scrollPanel {
height: $tableHeight;
}
#succTable {
min-height: 400px;
max-height: 1000px;
overflow-y: auto;
height: $tableHeight;
position: relative;
top: -10px;
.ms-DetailsHeader {
padding-top: 0;
}
.succTable-tooltip {
width: 90%;
......
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