Skip to content

Commit

Permalink
Merge pull request #407 from pacificclimate/watershed-table
Browse files Browse the repository at this point in the history
Add watershed information table
  • Loading branch information
corviday authored May 19, 2021
2 parents 7a9c7d1 + b12ef73 commit 15453e2
Show file tree
Hide file tree
Showing 14 changed files with 539 additions and 27 deletions.
52 changes: 52 additions & 0 deletions src/components/AttributeValueTable/AttributeValueTable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Very simple table intended to display some number of attribute/value pairs
// about a single object, rather than offering comparisons of multiple objects
// as the DataTable does.
// Data shouuld have an attribute, a value, and units.

import PropTypes from 'prop-types';
import React from 'react';
import { BootstrapTable, TableHeaderColumn } from 'react-bootstrap-table';

class AttributeValueTable extends React.Component {
static propTypes = {
data: PropTypes.array,
options: PropTypes.object,
};

render() {
return (
<div id={'table'}>
<BootstrapTable
data={this.props.data}
options={this.props.options}
striped
hover
>
<TableHeaderColumn
dataField='attribute'
dataAlign='center'
width='20%'
>
Attribute
</TableHeaderColumn>
<TableHeaderColumn
dataField='value' isKey
dataAlign='center'
width='20%'
>
Value
</TableHeaderColumn>
<TableHeaderColumn
dataField='units'
dataAlign='center'
width='20%'
>
Units
</TableHeaderColumn>
</BootstrapTable>
</div>
);
}
}

export default AttributeValueTable;
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Smoke test for the Attribute Value Table
import React from 'react';
import TestUtils from 'react-dom/test-utils';
import AttributeValueTable from '../AttributeValueTable';

jest.dontMock('../AttributeValueTable');
jest.dontMock('../../../core/util');

describe('AttributeValueTable', function () {
it('renders', function () {
var table = TestUtils.renderIntoDocument(
<AttributeValueTable data={[]} />
);
expect(TestUtils.isCompositeComponent(table)).toBeTruthy();
});
});
157 changes: 157 additions & 0 deletions src/components/WatershedSummaryTable/WatershedSummaryTable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
// Watershed Summary Table: Panel containing a Attribute Value table that
// shows physical information (elevation, area, etc) about the watershed the user
// has selected.

// TODO: Use HOC `withAsyncData` to manage fetching data

import PropTypes from 'prop-types';
import React from 'react';
import { Row, Col, Panel } from 'react-bootstrap';

import _ from 'lodash';

import AttributeValueTable from '../AttributeValueTable/AttributeValueTable';
import { watershedTableLabel } from
'../guidance-content/info/InformationItems';
import { MEVSummary } from '../data-presentation/MEVSummary';

import { getWatershed } from '../../data-services/ce-backend';
import {
parseWatershedTableData,
validateWatershedData,
} from '../../core/util';
import { errorMessage } from '../graphs/graph-helpers';

import styles from './WatershedSummaryTable.module.css';


// TODO: Use `withAsyncData` to factor out common data-fetching code here
export default class WatershedSummaryTable extends React.Component {
static propTypes = {
ensemble_name: PropTypes.string,
area: PropTypes.string,
};

// Lifecycle hooks
// Follows React 16+ lifecycle API and recommendations.
// See https://reactjs.org/blog/2018/03/29/react-v-16-3.html
// See https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html
// See https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html

constructor(props) {
super(props);

// See src/components/graphs/README for an explanation of the content and
// usage of state values. This is important for understanding how this
// component works.

this.state = {
prevArea: null,
fetchingData: false,
data: null,
dataError: null,
};
}

static getDerivedStateFromProps(props, state) {
if (props.area !== state.prevArea) {
return {
prevArea: props.area,
fetchingData: false, // not quite yet
data: null, // Signal that data fetch is required
dataError: null,
};
}

// No state update necessary.
return null;
}

componentDidMount() {
this.fetchData();
}

componentDidUpdate(prevProps, prevState) {
if (!this.state.fetchingData && this.state.data === null) {
this.fetchData();
}
}

// Data fetching

getAndValidateWatershed(parameters) {
return (
getWatershed(parameters)
.then(validateWatershedData)
.then(response => response.data)
);
}

fetchData() {
if (!this.props.area) {
// Don't fetch data when user hasn't selected a watershed
return;
}
this.setState({ fetchingData: true });
const metadata = {
..._.pick(this.props, 'ensemble_name', 'area')
};
this.getAndValidateWatershed(metadata)
.then(data => {
this.setState({
fetchingData: false,
data: parseWatershedTableData(
data, this.props.area),
dataError: null,
});
}).catch(dataError => {
this.setState({
// Set data non-null here to prevent infinite update loop.
data: undefined,
fetchingData: false,
dataError,
});
});
}

// render helpers

watershedTableOptions() {
// Return a data table options object appropriate to the current state.

// An error occurred
if (this.state.dataError) {
return { noDataText: errorMessage(this.state.dataError) };
}

// Waiting for data
if (this.state.fetchingData || this.state.data === null) {
return { noDataText: 'Select a point on the map with the circle marker tool to see watershed information' };
}

// We can haz data
return { noDataText: 'We have data and this message should not show' };
}

render() {
return (
<Panel>
<Panel.Heading>
<Panel.Title>
<Row>
<Col lg={4}>
{watershedTableLabel}
</Col>
</Row>
</Panel.Title>
</Panel.Heading>
<Panel.Body className={styles.data_panel}>
<AttributeValueTable
data={this.state.data || []}
options={this.watershedTableOptions()}
/>
</Panel.Body>
</Panel>
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.mevSummary {
text-align: right;
}

.selector_label {
margin-right: 1em;
}

.selector {
display: inline-block;
width: 50%;
}
18 changes: 18 additions & 0 deletions src/components/WatershedSummaryTable/__tests__/smoke.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//Smoke test for the WatershedSummaryTable panel
import React from 'react';
import ReactDOM from 'react-dom';
import WatershedSummaryTable from '../WatershedSummaryTable';
import { watershed_wkt} from '../../../test_support/data';

jest.mock('../../../data-services/ce-backend');

it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(
<WatershedSummaryTable
area={ watershed_wkt }
ensemble_name={"upper_fraser"}
/>,
div
);
});
6 changes: 6 additions & 0 deletions src/components/WatershedSummaryTable/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "WatershedSummaryTable",
"version": "0.0.0",
"private": true,
"main": "./WatershedSummaryTable.js"
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import styles from '../DataController.module.css';
import { MEVSummary } from '../../data-presentation/MEVSummary';
import FlowArrow from '../../data-presentation/FlowArrow';
import GraphTabs from '../GraphTabs';
import WatershedSummaryTable from '../../WatershedSummaryTable'

export default class FloodDataController extends React.Component {
static propTypes = {
Expand Down Expand Up @@ -107,6 +108,8 @@ export default class FloodDataController extends React.Component {
</Panel.Body>
</Panel>

<WatershedSummaryTable {...this.props} />

</div>
);
}
Expand Down
20 changes: 20 additions & 0 deletions src/components/guidance-content/info/InformationItems.js
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,26 @@ export const exportStatsTableDataLabel = (
</LabelWithInfo>
);

///////////////////////////////
// Watershed table
///////////////////////////////
export const watershedTableLabel = (
<LabelWithInfo label='Watershed Upstream Of Selected Point'>
<p>
This table presents information about the selected grid and its upstream area
or watershed.
</p>
<p>
The Outlet Latitude and Longitude describe the point selected on the map,
which defines the outlet of the watershed. The Source Elevation and Outlet
Elevation are the minimum and maximum elevation of the watershed. The Area
is the drainage area of the watershed, upstream of the selected point.
The Melton Ratio is defined as the relief (maximum minus minimum elevation),
over the watershed, divided by the square root of its drainage area.
</p>
</LabelWithInfo>
);

// export const infoTemplate = (
// <Information>
// <p>
Expand Down
Loading

0 comments on commit 15453e2

Please sign in to comment.