Skip to content

Commit

Permalink
Merge pull request #79 from matthew-t-smith/dev/mthomas
Browse files Browse the repository at this point in the history
Front-end bug fixes, GraphView tweaks
  • Loading branch information
reelmatt authored May 2, 2020
2 parents c794f7a + ee522ab commit 9867670
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 95 deletions.
8 changes: 8 additions & 0 deletions front-end/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions front-end/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"react-bootstrap": "^1.0.0-beta.17",
"react-dom": "^16.13.0",
"react-scripts": "3.4.1",
"react-spinners-css": "^1.1.7",
"react-window": "^1.8.5",
"regenerator-runtime": "^0.13.5",
"resize-observer-polyfill": "^1.5.1"
Expand Down
15 changes: 12 additions & 3 deletions front-end/src/API.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,18 @@ export async function uploadWorkflow(formData) {
async function handleEdge(link, method) {
const sourceId = link.getSourcePort().getNode().options.id;
const targetId = link.getTargetPort().getNode().options.id;
return fetchWrapper(
`/node/edge/${sourceId}/${targetId}`,
{method: method});

let endpoint;

if (link.getSourcePort().options.in) {
// If edge goes from IN port -> OUT port, reverse the ports
endpoint = `/node/edge/${targetId}/${sourceId}`;
} else {
// Otherwise, keep source -> target edge
endpoint = `/node/edge/${sourceId}/${targetId}`;
}

return fetchWrapper(endpoint, {method: method});
}


Expand Down
18 changes: 14 additions & 4 deletions front-end/src/components/CustomNode/CustomNodeWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,19 +59,29 @@ export default class CustomNodeWidget extends React.Component {
</PortWidget>
);
}

let graphView;
let width = 40;
if (this.props.node.options.node_type !== "flow_control") {
graphView = (
<div className="custom-node-tabular" onClick={this.toggleGraph}>
<img src="tabular-icon.png" alt="Tabular" style={{width:25, height:25}}/>
</div>
);
width = 80;
}

return (
<div className="custom-node-wrapper">
<div className="custom-node-name">{this.props.node.options.name}</div>
<div className="custom-node" style={{ borderColor: this.props.node.options.color }}>
<div className="custom-node" style={{ borderColor: this.props.node.options.color, width: width }}>
<div className="custom-node-configure" onClick={this.toggleConfig}>{String.fromCharCode(this.icon)}</div>
<NodeConfig node={this.props.node}
show={this.state.showConfig}
toggleShow={this.toggleConfig}
onDelete={this.handleDelete}
onSubmit={this.acceptConfiguration} />
<div className="custom-node-tabular" onClick={this.toggleGraph}>
<img src="tabular-icon.png" alt="Tabular" style={{width:25, height:25}}></img>
</div>
{graphView}
<GraphView node={this.props.node}
show={this.state.showGraph}
toggleShow={this.toggleGraph}
Expand Down
195 changes: 107 additions & 88 deletions front-end/src/components/CustomNode/GraphView.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import { Roller } from 'react-spinners-css';
import { Modal, Button } from 'react-bootstrap';
import propTypes from 'prop-types';
import { VariableSizeGrid as Grid } from 'react-window';
Expand All @@ -13,16 +14,16 @@ export default class GraphView extends React.Component {
this.key_id = props.node.getNodeId();
this.state = {
loading: false,
rowCount: 0,
columnCount: 0,
data: [],
keys: [],
rows: [],
columns: [],
maxWidth: 0,
gridRef: React.createRef()};
}
};

columnWidths = (index) => {
return 10 * this.state.widths[index];
}
};

rowHeights = () => new Array(765)
.fill(true)
Expand All @@ -32,109 +33,127 @@ export default class GraphView extends React.Component {
this.props.toggleShow();
};

computeWidths = (columnCount, rowCount, json) => {
const widths = new Array(columnCount);
const keys = Object.keys(json);
for (let index = 0; index < columnCount; index++) {
const key = keys[index];
widths[index] = key.length;
for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
const value = json[key][rowIndex.toString()];
if (value != null && value.length > widths[index]) {
widths[index] = value.length;
}
/**
* Compute width of grid columns.
*
* Width is based on the maximum-length cell contained within the JSON data.
*
* @param {Object} columns The column information from the data
* @param {int} rowCount Number of rows in the data
* @param {Object} data The raw data from Node execution
* @returns {any[]}
*/
computeWidths = (columns, rowCount, data) => {
const columnCount = columns.length;
const widths = new Array(columnCount);
let maxWidth = this.state.maxWidth;

for (let index = 0; index < columnCount; index++) {
const column = columns[index];
widths[index] = column.length;

for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
const row = data[column][rowIndex.toString()];

if (row != null && row.length > widths[index]) {
widths[index] = row.length;
}

maxWidth += widths[index] * 10;
}
}
}

return widths;
}
this.setState({maxWidth: maxWidth});
return widths;
};

load = async () => {
this.setState({loading: true})
API.retrieveData(this.key_id)
this.setState({loading: true});

API.retrieveData(this.key_id)
.then(json => {
const keys = Object.keys(json);
const columnCount = keys.length;
const rows = Object.keys(json[keys[0]]);
const rowCount = rows.length;
const widths = this.computeWidths(columnCount, rowCount, json);
this.setState({ data: json,
keys: keys,
columnCount: columnCount,
rowCount: rowCount,
loading: false,
widths: widths});
}).catch(err => console.log(err));
}
const columns = Object.keys(json);
const rows = Object.keys(json[columns[0]]);
const widths = this.computeWidths(columns, rows.length, json);

this.setState({
data: json,
columns: columns,
rows: rows,
loading: false,
widths: widths
});
})
.catch(err => console.error(err));
};

Cell = ({ columnIndex, rowIndex, style }) => {
const className = columnIndex % 2
? rowIndex % 2 === 0
? 'GridItemOdd'
: 'GridItemEven'
: rowIndex % 2
? 'GridItemOdd'
: 'GridItemEven';

if (rowIndex === 0) {
return (
<div className={className} style={style}>
{this.state.keys[columnIndex]}
</div>
);
}
const className = (rowIndex % 2 === 0) ? 'GridItemEven' : 'GridItemOdd';
const column = this.state.columns[columnIndex];

return (
<div className={className} style={style}>
{this.state.data[this.state.keys[columnIndex]][rowIndex.toString()] }
{(rowIndex === 0) ? column : this.state.data[column][rowIndex.toString()]}
</div>
);
}
};


render() {
let body;
let footer;

if (this.state.loading) {
return (<div>Loading data...</div>);
// Print loading spinner
body = (<Roller color="black" />);
} else if (this.state.data.length < 1) {
// Print instructions about loading
body = "Loading the data might take a while depending on how big the data is.";
footer = (
<Modal.Footer>
<Button variant="secondary" onClick={this.onClose}>Cancel</Button>
<Button variant="secondary"
disabled={this.props.node.options.status !== "complete"}
onClick={this.load}>Load
</Button>
</Modal.Footer>
);
} else {
// Display the grid
let displayHeight = this.state.rows.length * 20;
let displayWidth = this.state.maxWidth;

body = (
<Grid
ref={this.state.gridRef}
className="Grid"
columnCount={this.state.columns.length}
columnWidth={index => this.columnWidths(index)}
height={displayHeight < 600 ? displayHeight + 5 : 600}
rowCount={this.state.rows.length}
rowHeight={index => 20}
width={displayWidth < 900 ? displayWidth : 900}
>
{this.Cell}
</Grid>
);
}

if (this.state.columnCount < 1) {
return (
<Modal show={this.props.show} onHide={this.props.toggleShow} centered
onWheel={e => e.stopPropagation()}>
<Modal.Header>
<Modal.Title><b>{this.props.node.options.name}</b> View</Modal.Title>
return (
<Modal
show={this.props.show}
onHide={this.props.toggleShow}
centered
dialogClassName={"GraphView"}
onWheel={e => e.stopPropagation()}
>
<Modal.Header closeButton>
<Modal.Title><b>{this.props.node.options.name}</b> View</Modal.Title>
</Modal.Header>
<Modal.Body>
Loading the data might take a while depending on how big the data is.
{body}
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={this.onClose}>Cancel</Button>
<Button variant="secondary" onClick={this.load}>Load</Button>
</Modal.Footer>
</Modal>
);
}

return (
<Modal show={this.props.show} onHide={this.props.toggleShow} centered
onWheel={e => e.stopPropagation()}>
<Modal.Header closeButton>
<Modal.Title><b>{this.props.node.options.name}</b> View</Modal.Title>
</Modal.Header>
<Modal.Body>
<Grid
ref={this.state.gridRef}
className="Grid"
columnCount={this.state.columnCount}
columnWidth={index => this.columnWidths(index)}
height={150}
rowCount={this.state.rowCount}
rowHeight={index => 20}
width={480}
>
{this.Cell}
</Grid>
</Modal.Body>
{footer}
</Modal>
);
}
Expand All @@ -145,4 +164,4 @@ GraphView.propTypes = {
show: propTypes.bool,
toggleShow: propTypes.func,
onClose: propTypes.func,
}
};
7 changes: 7 additions & 0 deletions front-end/src/styles/GraphView.css
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
.Grid {
border: 1px solid #d9dddd;
width: auto;
margin: auto;
}

.GridItemEven, .GridItemOdd {
display: flex;
align-items: center;
justify-content: center;
border-right: 1px solid grey;
border-bottom: 1px solid grey;
}

.GridItemEven {
background-color: #f8f8f0;
}

.GraphView {
max-width: 925px;
}

0 comments on commit 9867670

Please sign in to comment.