pacificclimate/climate-explorer-frontend

View on GitHub
src/components/data-controllers/DualDataController/DualDataController.js

Summary

Maintainability
A
2 hrs
Test Coverage
/*********************************************************************
 * DualDataController.js - controller component for numerical display
 * of two variables at once, supporting comparison between them.
 *
 * Receives a model, experiment, and two variables from its parent,
 * [Dual|Precip]AppController. Provides widgets for users to select a specific
 * slice of the data (timespan or run). Queries the API to fetch data on
 * both variables for the DataGraph viewers it manages, Annual Cycle Graph,
 * Long Term Average Graph, and Timeseries Graph, to show comparisons of
 * the two variables.
 *
 * If a user selects two variables that come from multi year mean datasets,
 * an Annual Graph and a Long Term Average Graph will be displayed. If a
 * user selects two variables that are not multi year means, the less
 * structured Timeseries Graph will be displayed.
 *
 * In either case, a Variable Response scatterplot is displayed, with the
 * secondary variable along the x axis (explanatory variable) and the
 * primary variable along the y axis (response variable).
 *
 * Selecting one multi year mean dataset and one nominal time dataset
 * displays an error message, as comparing these is not as simple as
 * plotting them on the same graph.
 *
 * The main variable is internally referred to as "variable" the
 * variable being compared to it is the "comparand." Available data
 * is based on the main variable; it's possible to display a dataset with
 * the main variable when the comparand is lacking matching data,
 * but not vice versa.
 *
 * Also allows downloading of the data displayed in the graphs.
 *
 * Children: DualAnnualCycleGraph, DualLongTermAveragesGraph
 *           DualVariableResponseGraph, DualTimeSeriesGraph
 *********************************************************************/

import PropTypes from "prop-types";

import React from "react";
import { Panel, Row, Col } from "react-bootstrap";
import _ from "lodash";

import DualAnnualCycleGraph from "../../graphs/DualAnnualCycleGraph";
import DualLongTermAveragesGraph from "../../graphs/DualLongTermAveragesGraph";
import DualTimeSeriesGraph from "../../graphs/DualTimeSeriesGraph";
import DualVariableResponseGraph from "../../graphs/DualVariableResponseGraph";
import {
  singleLtaTabLabel,
  timeSeriesTabLabel,
  variableResponseTabLabel,
  dualAnnualCycleTabLabel,
  graphsPanelLabel,
} from "../../guidance-content/info/InformationItems";

import styles from "../DataController.module.css";
import { MEVSummary } from "../../data-presentation/MEVSummary";
import GraphTabs from "../GraphTabs";

export default class DualDataController extends React.Component {
  static propTypes = {
    ensemble_name: PropTypes.string, // TODO: Why is this declared? Remove?
    model_id: PropTypes.string,
    variable_id: PropTypes.string,
    comparand_id: PropTypes.string,
    experiment: PropTypes.string,
    area: PropTypes.string,
    meta: PropTypes.array,
    comparandMeta: PropTypes.array,
  };

  shouldComponentUpdate(nextProps, nextState) {
    // This guards against re-rendering before calls to the data sever alter the
    // state
    // TODO: Consider making shallow comparisons. Deep ones are expensive.
    // If immutable data objects are used (or functionally equivalently,
    // new data objects each time), then shallow comparison works.
    return !(
      _.isEqual(nextProps.meta, this.props.meta) &&
      _.isEqual(nextProps.comparandMeta, this.props.comparandMeta) &&
      _.isEqual(nextProps.area, this.props.area)
    );
  }

  // Spec for generating tabs containing various graphs.
  // Property names indicate whether the dataset is a multi-year mean or not.
  // TODO: Pull this out into new component CompareVariablesGraphs
  static graphTabsSpecs = {
    mym: [
      { title: dualAnnualCycleTabLabel, graph: DualAnnualCycleGraph },
      { title: singleLtaTabLabel, graph: DualLongTermAveragesGraph },
      { title: variableResponseTabLabel, graph: DualVariableResponseGraph },
    ],
    notMym: [
      { title: timeSeriesTabLabel, graph: DualTimeSeriesGraph },
      { title: variableResponseTabLabel, graph: DualVariableResponseGraph },
    ],
  };

  render() {
    return (
      // TODO: https://github.com/pacificclimate/climate-explorer-frontend/issues/246
      <Panel>
        <Panel.Heading>
          <Panel.Title>
            <Row>
              <Col lg={4}>{graphsPanelLabel}</Col>
              <Col lg={8}>
                <MEVSummary
                  className={styles.mevSummary}
                  {...this.props}
                  dual
                />
              </Col>
            </Row>
          </Panel.Title>
        </Panel.Heading>
        <Panel.Body className={styles.data_panel}>
          <GraphTabs
            {...this.props}
            specs={DualDataController.graphTabsSpecs}
          />
        </Panel.Body>
      </Panel>
    );
  }
}