Skip to content
Snippets Groups Projects
Commit 2e2179b8 authored by Reinoud Bokhorst's avatar Reinoud Bokhorst
Browse files

OSB-35: fix table height and re-render

parent b7104087
No related branches found
No related tags found
2 merge requests!89Monitoring maintenance Epic branch merge,!1Resolve OSB-13 "Monitoringmaintenance "
Showing with 230 additions and 68 deletions
...@@ -1850,6 +1850,7 @@ LCU/Maintenance/MDB_WebView/maintenancedb_view/src/App.test.js -text ...@@ -1850,6 +1850,7 @@ LCU/Maintenance/MDB_WebView/maintenancedb_view/src/App.test.js -text
LCU/Maintenance/MDB_WebView/maintenancedb_view/src/api_configuration.js -text LCU/Maintenance/MDB_WebView/maintenancedb_view/src/api_configuration.js -text
LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/AntennaErrorDetails.js -text LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/AntennaErrorDetails.js -text
LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/AntennaView.js -text LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/AntennaView.js -text
LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/FillHeight.js -text
LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/LatestObservations.css -text LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/LatestObservations.css -text
LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/LatestObservations.js -text LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/LatestObservations.js -text
LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/LatestObservations.scss -text LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/LatestObservations.scss -text
......
import React from "react";
/*
* FillHeight: Creates a div that fills the remaining height of the viewport.
* Window resize is monitored to scale the div accordingly.
*
* This component uses the 'render prop' pattern where the 'children' prop is used
* as the render function. The height of the div (in an object) is passed to
* the function for use by subcomponents. E.g.:
*
* <FillHeight>
* { (props) => <MyComponent hgt={props.height} /> }
* </FillHeight>
*/
class FillHeight extends React.Component {
static defaultProps = {
className: 'fill-height-container', // classname for the container div
gutterBottom: 10, // px, leave some space at the bottom
minHeight: 200 // px, minimum height
};
state = {
height: 500 // px, height of the container div
};
node = null; // internal ref to the DOM node (div)
mounted = false; // bool
resizeTimeout = null; // throttling of render during resize
componentDidMount() {
this.mounted = true;
window.addEventListener("resize", () => {
window.clearTimeout(this.resizeTimeout);
this.resizeTimeout = window.setTimeout(this.onWindowResize, 200);
});
// force a render now that it has been mounted
// (the offset is known at this point)
this.onWindowResize();
}
componentWillUnmount() {
this.mounted = false;
window.removeEventListener("resize", this.onWindowResize);
}
onWindowResize = () => {
if (!this.mounted || !this.node) {
return;
}
let offset = this.node.getBoundingClientRect().top;
let height = window.innerHeight - offset - this.props.gutterBottom;
if (height < this.props.minHeight) {
height = this.props.minHeight;
}
this.setState({
height
});
};
getNodeRef = (node) => {
this.node = node;
}
render() {
// Create a simple div for measuring the viewport offset when it mounts,
// then re-render.
if (!this.mounted) {
return (
<div ref={this.getNodeRef} />
);
}
// Render prop pattern; pass height to child render function
return (
<div ref={this.getNodeRef} className={this.props.className} style={{height: this.state.height+'px'}}>
{ this.props.children({height: this.state.height}) }
</div>
);
}
};
export default FillHeight;
...@@ -21,6 +21,8 @@ import ReactTableContainer from "react-table-container"; ...@@ -21,6 +21,8 @@ import ReactTableContainer from "react-table-container";
import moment from 'moment'; import moment from 'moment';
import {withRouter} from "react-router"; import {withRouter} from "react-router";
import classNames from "classnames"; import classNames from "classnames";
import FillHeight from '../components/FillHeight.js';
// CSS // CSS
import '../themes/lofar-styles.css'; import '../themes/lofar-styles.css';
...@@ -243,6 +245,7 @@ class RTSMLines extends Component { ...@@ -243,6 +245,7 @@ class RTSMLines extends Component {
this.setState({ this.setState({
displaySingleTests: !this.state.displaySingleTests displaySingleTests: !this.state.displaySingleTests
}); });
this.props.callback();
}; };
render() { render() {
...@@ -310,7 +313,8 @@ class ComponentClass extends Component { ...@@ -310,7 +313,8 @@ class ComponentClass extends Component {
component_type={this.props.type} component_type={this.props.type}
station_name={this.props.station_name} station_name={this.props.station_name}
station_type={this.props.station_type} station_type={this.props.station_type}
data={data}/>) data={data}
callback={this.rerender} />)
} }
renderTestLines(data, component_ids) { renderTestLines(data, component_ids) {
...@@ -340,11 +344,17 @@ class ComponentClass extends Component { ...@@ -340,11 +344,17 @@ class ComponentClass extends Component {
return rows return rows
} }
rerender = () => {
this.setState({ state: this.state });
}
render() { render() {
const comp_ids = this.computeComponentIDList(this.props.type); const comp_ids = this.computeComponentIDList(this.props.type);
//<ReactTableContainer width="100%" height="79vh">
this.i++;
return ( return (
<ReactTableContainer width="100%" height="79vh"> <ReactTableContainer width="100%" height={this.props.height+'px'} id={this.i}>
<table className="stv-table table-sm table-hover table-bordered"> <table className="stv-table table-sm table-hover table-bordered">
<thead className="stv-tableheader"> <thead className="stv-tableheader">
<tr> <tr>
...@@ -365,6 +375,21 @@ class ComponentClass extends Component { ...@@ -365,6 +375,21 @@ class ComponentClass extends Component {
} }
} }
/*
* Render a Tab item
*/
function Tab({label, onClick, isActive}) {
const cls = isActive ? 'clickable-tab-active' : 'clickable-tab-unactive';
return (
<NavItem className="clickable-tab">
<NavLink className={cls} onClick={onClick}>
{label}
</NavLink>
</NavItem>
);
}
/** /**
* StationTestView class. * StationTestView class.
*/ */
...@@ -374,15 +399,6 @@ class StationTestViewC extends Component { ...@@ -374,15 +399,6 @@ class StationTestViewC extends Component {
activeTab: undefined activeTab: undefined
} }
toggleTab = (e) => {
let tab = e.currentTarget.innerHTML;
if (this.state.activeTab !== tab) {
this.setState({
activeTab: tab
});
}
}
// Set the activeTab to the first component if it wasn't set yet or when // Set the activeTab to the first component if it wasn't set yet or when
// the station changed and doesn't have the active component // the station changed and doesn't have the active component
static getDerivedStateFromProps(props, state) { static getDerivedStateFromProps(props, state) {
...@@ -401,43 +417,54 @@ class StationTestViewC extends Component { ...@@ -401,43 +417,54 @@ class StationTestViewC extends Component {
return null; return null;
} }
tabClass = (componentType) => {
const className = (this.state.activeTab === componentType ? 'active' : 'unactive');
return 'clickable-tab-'+className
}
// Do not (re)render when data is loading (performance improvement) // Do not (re)render when data is loading (performance improvement)
shouldComponentUpdate(nextProps, nextState, nextContext) { shouldComponentUpdate(nextProps, nextState, nextContext) {
return nextProps.isLoading ? false : true; return nextProps.isLoading ? false : true;
} }
toggleTab = (e) => {
let tab = e.currentTarget.innerHTML;
if (this.state.activeTab !== tab) {
this.setState({
activeTab: tab
});
}
}
render() { render() {
const stationType = LOFARDefinitions.stationTypeFromName(this.props.selectedStation); const stationType = LOFARDefinitions.stationTypeFromName(this.props.selectedStation);
const componentTypes = Object.keys(this.props.data).sort(); const componentTypes = Object.keys(this.props.data).sort();
if (this.props.isLoading) {
return null;
}
return ( return (
<div> <div>
<Nav tabs className="component-type-selector"> <Nav tabs className="component-type-selector">
{ componentTypes.map((componentType, key) => {
<NavItem key={key} className="clickable-tab"> componentTypes.map((componentType, key) =>
<NavLink className={this.tabClass(componentType)} <Tab key={key} onClick={this.toggleTab} isActive={this.state.activeTab===componentType} label={componentType} />
onClick={this.toggleTab}> )
{componentType}
</NavLink>
</NavItem>)
} }
</Nav> </Nav>
<TabContent activeTab={this.state.activeTab}> <FillHeight className="border-right">
{ componentTypes.map((componentType, key) => { ({height}) => (
<TabPane key={key} tabId={componentType}> <TabContent activeTab={this.state.activeTab}>
<ComponentClass key={componentType} {componentTypes.map((componentType, key) =>
station_type={stationType} <TabPane key={key} tabId={componentType}>
type={componentType} <ComponentClass key={componentType}
station_name={this.props.selectedStation} station_type={stationType}
data={this.props.data[componentType]} /> type={componentType}
</TabPane>) station_name={this.props.selectedStation}
} data={this.props.data[componentType]}
</TabContent> height={height}
/>
</TabPane>
)}
</TabContent>
)}
</FillHeight>
</div>); </div>);
} }
......
...@@ -21,6 +21,7 @@ import MultiSelectDropdown from '../components/MultiSelectDropdown.js' ...@@ -21,6 +21,7 @@ import MultiSelectDropdown from '../components/MultiSelectDropdown.js'
import {AntennaIdsPerTypeStationType} from '../utils/LOFARDefinitions.js' import {AntennaIdsPerTypeStationType} from '../utils/LOFARDefinitions.js'
// CSS // CSS
import './Toolbar.css' import './Toolbar.css'
import 'react-datepicker/dist/react-datepicker.css';
/** /**
......
...@@ -9,47 +9,81 @@ import StationTestView from '../components/StationTestView'; ...@@ -9,47 +9,81 @@ import StationTestView from '../components/StationTestView';
import StationTestChildView from '../components/StationTestChildView'; import StationTestChildView from '../components/StationTestChildView';
import { composeQueryString } from '../utils/utils.js'; import { composeQueryString } from '../utils/utils.js';
import { Toolbar, DateRangeSelector, TestTypeSelector, ErrorTypesSelector } from '../components/Toolbar' import { Toolbar, DateRangeSelector, TestTypeSelector, ErrorTypesSelector } from '../components/Toolbar'
import FillHeight from '../components/FillHeight.js';
// CSS
import 'react-datepicker/dist/react-datepicker.css';
/*
* Display an Alert
*/
function ErrorAlert({doDisplay, message}){
if (! doDisplay) {
return null;
}
// The 10px is the margin that ResponsiveGridLayout uses
return (
<Alert style={{margin: '10px'}} color="warning">
{message}
</Alert>
);
}
/*
* Display the page body (below the header)
*/
function PageBody({doDisplay, url}) {
if (! doDisplay) {
return null;
}
return (
<Container fluid={true}>
<Row>
<Col md="8" className="col-padding">
<StationTestView url={url} />
</Col>
<Col md="4" className="col-padding">
<StationTestChildView />
</Col>
</Row>
</Container>
);
}
class StationOverviewPageC extends Component { class StationOverviewPageC extends Component {
getStationSummaryURL(){ getStationSummaryURL() {
const parameters = {};
if (this.isParameterMissing()) { if (this.isParameterMissing()) {
return ""; return '';
} }
parameters.station_name = this.props.selectedStation; const parameters = {
parameters.test_type = this.props.testType; format: 'json',
parameters.from_date = moment(this.props.startDate).format('YYYY-MM-DD'); station_name: this.props.selectedStation,
parameters.to_date = moment(this.props.endDate).format('YYYY-MM-DD'); test_type: this.props.testType,
parameters.error_types = this.props.selectedErrorTypes from_date: moment(this.props.startDate).format('YYYY-MM-DD'),
const baseURL = '/api/view/ctrl_station_component_errors?format=json'; to_date: moment(this.props.endDate).format('YYYY-MM-DD'),
error_types: this.props.selectedErrorTypes
};
const baseURL = '/api/view/ctrl_station_component_errors';
const queryString = composeQueryString(parameters); const queryString = composeQueryString(parameters);
return `${baseURL}&${queryString}`
return `${baseURL}?${queryString}`
} }
isParameterMissing(){ isParameterMissing(){
const stationName = this.props.selectedStation; const stationName = this.props.selectedStation;
return stationName === undefined || return stationName === undefined || stationName === "";
stationName === ""
}
renderErrorIfParameterMissing(){
if (this.isParameterMissing()){
// The 10px is the margin that ResponsiveGridLayout uses
return <Alert style={{margin: '10px'}} color="warning">Please select a station</Alert>
}else {
return ""
}
} }
render() { render() {
const parmMissing = this.isParameterMissing();
return ( return (
<React.Fragment> <React.Fragment>
<Header active_page={this.props.location} /> <Header active_page={this.props.location} />
...@@ -59,15 +93,8 @@ class StationOverviewPageC extends Component { ...@@ -59,15 +93,8 @@ class StationOverviewPageC extends Component {
<DateRangeSelector /> <DateRangeSelector />
<ErrorTypesSelector /> <ErrorTypesSelector />
</Toolbar> </Toolbar>
{this.renderErrorIfParameterMissing()} <ErrorAlert doDisplay={parmMissing} message="Please select a station" />
{ this.isParameterMissing() ? "" : <PageBody doDisplay={!parmMissing} url={this.getStationSummaryURL()} />
<Container fluid={true} style={{padding: '10px'}}>
<Row>
<Col md="8"><StationTestView url={this.getStationSummaryURL()}/></Col>
<Col md="4"><StationTestChildView /></Col>
</Row>
</Container>
}
</React.Fragment> </React.Fragment>
); );
} }
......
...@@ -61,8 +61,12 @@ class TilesPageC extends Component { ...@@ -61,8 +61,12 @@ class TilesPageC extends Component {
{ parmMissing ? null : { parmMissing ? null :
<Container fluid={true} style={{padding: '10px'}}> <Container fluid={true} style={{padding: '10px'}}>
<Row> <Row>
<Col md="8"><AntennaView url={url}/></Col> <Col md="8" className="col-padding">
<Col md="4"><AntennaErrorDetails/></Col> <AntennaView url={url}/>
</Col>
<Col md="4" className="col-padding">
<AntennaErrorDetails/>
</Col>
</Row> </Row>
</Container> </Container>
} }
......
...@@ -89,3 +89,7 @@ img { ...@@ -89,3 +89,7 @@ img {
.form-input button:hover { .form-input button:hover {
background-color: #e8e8ec !important; } background-color: #e8e8ec !important; }
/* Padding on column in bootstrap grid */
.row .col-padding {
padding-top: 15px; }
...@@ -106,3 +106,9 @@ $griditem-color: $secondary-dark; ...@@ -106,3 +106,9 @@ $griditem-color: $secondary-dark;
.form-input button:hover { .form-input button:hover {
background-color: $secondary-light !important; background-color: $secondary-light !important;
} }
/* Padding on column in bootstrap grid */
.row .col-padding {
padding-top: 15px;
}
...@@ -6408,3 +6408,7 @@ img { ...@@ -6408,3 +6408,7 @@ img {
.form-input button:hover { .form-input button:hover {
background-color: #e8e8ec !important; } background-color: #e8e8ec !important; }
/* Padding on column in bootstrap grid */
.row .col-padding {
padding-top: 15px; }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment