forked from mlflow/mlflow
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add new compact experiment view (mlflow#546)
* WIP splitting into two table views * WIP refactor * WIP; metrics & params appear as block elements * WIP got toggle to work * More WIP * Add docs * Fix lint & other obvious things before more major refactor * WIP * UI tweaks * Don't pass unnecessary props to table components * Fix react warnings * Fix toggle (broke it by fixing react warnings) * Add highlighting of sorted cells + toggle on header cell * WIP; added truncation to table cells & headers, will try to simplify CSS some * Fix warnings & linter, simplify some CSS * Fix metric filtering for compact view * Small refactoring to eliminate some duplicate code, store inline styles in constants * Address comments * Address some offline review comments * Sort doesn’t need to have the name, or could say “sort by” * Toggle should just be two icons (e.g. grid and column) * Let metric & param values flow next to each other * Try some suggestions, like highlighting names on hover * Remove changing hover on exit * WIP addressing some comments * More changes (updates to highlighting style etc). TODO: get tab stop alignment to work * Small refactor: share styles across containers for metric/param cell content. Also reuse background color for selected experiment for hovered metrics/params * Fix hover titles for toggle between compact/grid view * Fix width of param/metric key-value pairs * Address comments: * Use consistent icon for sorting * Change background color on highlight to yellow * Add padding to highlight boxes * Increase font size of caret + fix vertical alignment * Simplify CSS / remove dead code * WIP addressing comments * Undo unnecessary package-lock changes * Remove unused styles, refactor styles from ExperimentViewUtil into a static variable so they can be shared with the multi-column table, fix lint
- Loading branch information
1 parent
53de566
commit f758154
Showing
9 changed files
with
3,995 additions
and
3,510 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
20 changes: 20 additions & 0 deletions
20
mlflow/server/js/src/components/ExperimentRunsSortToggle.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import React from "react"; | ||
import PropTypes from "prop-types"; | ||
|
||
export default class ExperimentRunsSortToggle extends React.Component { | ||
static propTypes = { | ||
children: PropTypes.node, | ||
bsRole: PropTypes.string, | ||
bsClass: PropTypes.string, | ||
}; | ||
|
||
render() { | ||
// eslint-disable-next-line no-unused-vars | ||
const {bsRole, bsClass, ...otherProps} = this.props; | ||
return ( | ||
<span {...otherProps}> | ||
{this.props.children} | ||
</span> | ||
); | ||
} | ||
} |
89 changes: 89 additions & 0 deletions
89
mlflow/server/js/src/components/ExperimentRunsTableCompact.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import React, { Component } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import Table from 'react-bootstrap/es/Table'; | ||
import ExperimentViewUtil from "./ExperimentViewUtil"; | ||
|
||
const styles = { | ||
sortArrow: { | ||
marginLeft: "2px", | ||
}, | ||
sortContainer: { | ||
minHeight: "18px", | ||
}, | ||
sortToggle: { | ||
cursor: "pointer", | ||
}, | ||
sortKeyName: { | ||
display: "inline-block" | ||
}, | ||
}; | ||
|
||
/** | ||
* Compact table view for displaying runs associated with an experiment. Renders metrics/params in | ||
* a single table cell per run (as opposed to one cell per metric/param). | ||
*/ | ||
class ExperimentRunsTableCompact extends Component { | ||
static propTypes = { | ||
rows: PropTypes.arrayOf(PropTypes.object), | ||
onCheckAll: PropTypes.func.isRequired, | ||
isAllChecked: PropTypes.func.isRequired, | ||
onSortBy: PropTypes.func.isRequired, | ||
sortState: PropTypes.object.isRequired, | ||
}; | ||
|
||
getSortInfo(isMetric, isParam) { | ||
const { sortState, onSortBy } = this.props; | ||
const sortIcon = sortState.ascending ? | ||
<i className="fas fa-caret-up" style={styles.sortArrow}/> : | ||
<i className="fas fa-caret-down" style={styles.sortArrow}/>; | ||
if (sortState.isMetric === isMetric && sortState.isParam === isParam) { | ||
return ( | ||
<span | ||
style={styles.sortToggle} | ||
onClick={() => onSortBy(isMetric, isParam, sortState.key)} | ||
> | ||
<span style={styles.sortKeyName} className="run-table-container"> | ||
(sort: {sortState.key} | ||
</span> | ||
{sortIcon} | ||
<span>)</span> | ||
</span>); | ||
} | ||
return undefined; | ||
} | ||
|
||
render() { | ||
const { rows, onCheckAll, isAllChecked, onSortBy, sortState } = this.props; | ||
const headerCells = [ExperimentViewUtil.getSelectAllCheckbox(onCheckAll, isAllChecked())]; | ||
ExperimentViewUtil.getRunMetadataHeaderCells(onSortBy, sortState) | ||
.forEach((headerCell) => headerCells.push(headerCell)); | ||
return ( | ||
<Table hover> | ||
<colgroup span="7"/> | ||
<colgroup span="1"/> | ||
<colgroup span="1"/> | ||
<tbody> | ||
<tr> | ||
{headerCells} | ||
<th className="top-row left-border" scope="colgroup" | ||
colSpan="1"> | ||
<div>Parameters</div> | ||
<div style={styles.sortContainer} className="unselectable"> | ||
{this.getSortInfo(false, true)} | ||
</div> | ||
</th> | ||
<th className="top-row left-border" scope="colgroup" | ||
colSpan="1"> | ||
<div>Metrics</div> | ||
<div style={styles.sortContainer} className="unselectable"> | ||
{this.getSortInfo(true, false)} | ||
</div> | ||
</th> | ||
</tr> | ||
{rows.map(row => <tr key={row.key}>{row.contents}</tr>)} | ||
</tbody> | ||
</Table>); | ||
} | ||
} | ||
|
||
export default ExperimentRunsTableCompact; |
109 changes: 109 additions & 0 deletions
109
mlflow/server/js/src/components/ExperimentRunsTableMultiColumn.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
import React, { Component } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import Table from 'react-bootstrap/es/Table'; | ||
import ExperimentViewUtil from './ExperimentViewUtil'; | ||
import classNames from 'classnames'; | ||
|
||
/** | ||
* Table view for displaying runs associated with an experiment. Renders each metric and param | ||
* value associated with a run in its own column. | ||
*/ | ||
class ExperimentRunsTableMultiColumn extends Component { | ||
static propTypes = { | ||
rows: PropTypes.arrayOf(PropTypes.object), | ||
paramKeyList: PropTypes.arrayOf(PropTypes.string), | ||
metricKeyList: PropTypes.arrayOf(PropTypes.string), | ||
onCheckAll: PropTypes.func.isRequired, | ||
isAllChecked: PropTypes.func.isRequired, | ||
onSortBy: PropTypes.func.isRequired, | ||
sortState: PropTypes.object.isRequired, | ||
}; | ||
|
||
render() { | ||
const { paramKeyList, metricKeyList, rows, onCheckAll, isAllChecked, onSortBy, | ||
sortState } = this.props; | ||
const columns = [ExperimentViewUtil.getSelectAllCheckbox(onCheckAll, isAllChecked())]; | ||
ExperimentViewUtil.getRunMetadataHeaderCells(onSortBy, sortState) | ||
.forEach((cell) => columns.push(cell)); | ||
this.getMetricParamHeaderCells(paramKeyList, metricKeyList, onSortBy, sortState) | ||
.forEach((cell) => columns.push(cell)); | ||
return (<Table hover> | ||
<colgroup span="7"/> | ||
<colgroup span={paramKeyList.length}/> | ||
<colgroup span={metricKeyList.length}/> | ||
<tbody> | ||
<tr> | ||
<th className="top-row" scope="colgroup" colSpan="5"></th> | ||
<th | ||
className="top-row left-border" | ||
scope="colgroup" | ||
colSpan={paramKeyList.length} | ||
> | ||
Parameters | ||
</th> | ||
<th className="top-row left-border" scope="colgroup" | ||
colSpan={metricKeyList.length}>Metrics | ||
</th> | ||
</tr> | ||
<tr> | ||
{columns} | ||
</tr> | ||
{rows.map(row => <tr key={row.key}>{row.contents}</tr>)} | ||
</tbody> | ||
</Table>); | ||
} | ||
|
||
getMetricParamHeaderCells( | ||
paramKeyList, | ||
metricKeyList, | ||
onSortBy, | ||
sortState) { | ||
const numParams = paramKeyList.length; | ||
const numMetrics = metricKeyList.length; | ||
const columns = []; | ||
|
||
const getHeaderCell = (isParam, key, i) => { | ||
const isMetric = !isParam; | ||
const sortIcon = ExperimentViewUtil.getSortIcon(sortState, isMetric, isParam, key); | ||
const className = classNames("bottom-row", "sortable", { "left-border": i === 0 }); | ||
const elemKey = (isParam ? "param-" : "metric-") + key; | ||
return ( | ||
<th | ||
key={elemKey} className={className} | ||
onClick={() => onSortBy(isMetric, isParam, key)} | ||
> | ||
<span | ||
style={styles.metricParamNameContainer} | ||
className="run-table-container" | ||
> | ||
{key} | ||
</span> | ||
<span style={ExperimentViewUtil.styles.sortIconContainer}>{sortIcon}</span> | ||
</th>); | ||
}; | ||
|
||
paramKeyList.forEach((paramKey, i) => { | ||
columns.push(getHeaderCell(true, paramKey, i)); | ||
}); | ||
if (numParams === 0) { | ||
columns.push(<th key="meta-param-empty" className="bottom-row left-border">(n/a)</th>); | ||
} | ||
|
||
metricKeyList.forEach((metricKey, i) => { | ||
columns.push(getHeaderCell(false, metricKey, i)); | ||
}); | ||
if (numMetrics === 0) { | ||
columns.push(<th key="meta-metric-empty" className="bottom-row left-border">(n/a)</th>); | ||
} | ||
return columns; | ||
} | ||
} | ||
|
||
const styles = { | ||
metricParamNameContainer: { | ||
verticalAlign: "middle", | ||
display: "inline-block", | ||
}, | ||
}; | ||
|
||
export default ExperimentRunsTableMultiColumn; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.