Skip to content

Commit

Permalink
Merge branch 'master' into feat/limited-pfb-file-export
Browse files Browse the repository at this point in the history
  • Loading branch information
em-ingram authored Sep 4, 2020
2 parents 7b8c61c + 0ce11bb commit fb8dd05
Show file tree
Hide file tree
Showing 37 changed files with 7,802 additions and 1,734 deletions.
8 changes: 7 additions & 1 deletion dev.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'self' blob:; connect-src 'self' blob: localhost https://localhost:9443 wss://localhost:9443 https://*.s3.amazonaws.com; img-src 'self' data: https://*.s3.amazonaws.com https://www.google-analytics.com; script-src 'self' 'unsafe-eval' https://www.google-analytics.com localhost https://localhost:9443; worker-src 'self' blob:; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com localhost https://localhost:9443; object-src 'none'; font-src 'self' data: https://fonts.googleapis.com https://fonts.gstatic.com; frame-src 'self';">
<!-- Content-Security-Policy connect-src:
- https://*.mapbox.com for COVID-19 Dashboard maps
- https://opendata.datacommons.io (Prod) and https://static.planx-pla.net (QA) to fetch COVID-19 Dashboard data
frame-src:
- https://auspice.planx-pla.net and https://auspice.pandemicresponsecommons.org for COVID-19 Auspice app in iframe
-->
<meta http-equiv="Content-Security-Policy" content="default-src 'self' https://login.bionimbus.org https://wayf.incommonfederation.org; child-src blob:; connect-src 'self' blob: localhost https://localhost:9443 wss://localhost:9443 https://*.s3.amazonaws.com https://*.mapbox.com https://opendata.datacommons.io https://static.planx-pla.net ; img-src 'self' https://opendata.datacommons.io https://static.planx-pla.net data: https://*.s3.amazonaws.com https://www.google-analytics.com; script-src 'self' 'unsafe-eval' https://www.google-analytics.com localhost https://localhost:9443; worker-src 'self' blob:; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com localhost https://localhost:9443; object-src 'none'; font-src 'self' data: https://fonts.googleapis.com https://fonts.gstatic.com; frame-src 'self' https://auspice.planx-pla.net https://auspice.pandemicresponsecommons.org;">
<meta name="viewport" content="width=device-width" />
<link href="https://fonts.googleapis.com/icon?family=Source+Sans+Pro" rel="stylesheet">
<link rel="stylesheet/less" type="text/css" media="all" href="/src/css/base.less">
Expand Down
3,681 changes: 1,975 additions & 1,706 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"d3-array": "^1.2.0",
"d3-ease": "^1.0.5",
"d3-force": "^1.1.0",
"d3-request": "^1.0.6",
"d3-scale": "^1.0.0",
"d3-selection": "^1.1.0",
"d3-transition": "^1.1.3",
Expand All @@ -39,10 +40,12 @@
"less": "^3.11.1",
"less-loader": "^4.1.0",
"lodash": "^4.17.11",
"mapbox-gl": "^1.9.1",
"moment": "^2.22.2",
"node-fetch": "^1.7.3",
"npm": "^6.14.7",
"nunjucks": "^3.0.1",
"papaparse": "^5.2.0",
"pluralize": "^8.0.0",
"prop-types": "^15.6.0",
"query-string": "^6.3.0",
Expand All @@ -52,6 +55,7 @@
"react-dom": "16.8.6",
"react-ga": "^2.5.3",
"react-helmet": "^5.2.0",
"react-map-gl": "^5.2.3",
"react-redux": "^5.0.7",
"react-relay": "^1.6.0",
"react-responsive": "^6.1.1",
Expand All @@ -61,6 +65,7 @@
"react-slick": "^0.25.2",
"react-svg-loader": "^3.0.3",
"react-table": "^6.9.2",
"react-tabs": "^3.1.0",
"react-virtualized": "^9.21.0",
"recharts": "^1.0.0-beta.10",
"redux": "^3.7.0",
Expand Down
1 change: 1 addition & 0 deletions runWebpack.sh
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ if [[ "$NODE_ENV" == "dev" || "$NODE_ENV" == "auto" ]]; then
echo ./node_modules/.bin/webpack-dev-server
./node_modules/.bin/webpack-dev-server
else
export NODE_OPTIONS='--max-old-space-size=4096'
export NODE_ENV="production"
echo ./node_modules/.bin/webpack --bail
./node_modules/.bin/webpack --bail
Expand Down
44 changes: 44 additions & 0 deletions src/Covid19Dashboard/ChartCarousel/ChartCarousel.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
.chart-carousel__container {
width: 550px;
padding: 10px 30px 30px 30px; // space for arrows and dots
margin-left: 10px;
margin-bottom: 10px;
}

.chart-carousel__container-border {
box-shadow: 0 2px 4px rgba(0,0,0,0.3);
}

.chart-carousel__popup-chart {
height: 50vh;
}

.chart-carousel__left-column {
float: left;
}

.chart-carousel__description {
text-align: justify;
overflow-y: auto;
height: 60vh;
padding: 0 10px 0 20px;
}

.chart-carousel__image {
max-width: 100%;
max-height: 350px;
margin: auto;
}

.chart-carousel__hover {
position: absolute;
left: 0;
right: 0;
margin: auto;
background-color: white;
box-shadow: 0 2px 4px rgba(0,0,0,0.3);
max-width: 500px;
padding: 10px;
white-space: pre-wrap;
z-index: 10; // place it over the "mapbox" link...
}
158 changes: 158 additions & 0 deletions src/Covid19Dashboard/ChartCarousel/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import Slider from 'react-slick';
import 'slick-carousel/slick/slick.css';
import 'slick-carousel/slick/slick-theme.css';

import { covid19DashboardConfig } from '../../localconf';
import Spinner from '../../components/Spinner';
import PlotChart from '../PlotChart';
import './ChartCarousel.less';


class ChartCarousel extends PureComponent {
constructor(props) {
super(props);
this.containerRef = React.createRef();
this.state = {
hoveredChartId: null,
hoverYPosition: 0,
};
}

onChartHover(hoveredChartId) {
// match top of carousel component + 10 pixels
const boundsRect = this.containerRef.current.getBoundingClientRect();
const topY = boundsRect.top + window.scrollY + 10;
this.setState({
hoveredChartId, // if null, will not display hover info
hoverYPosition: topY,
});
}

render() {
if (!this.props.chartsConfig.length) {
return null;
}

const sliderSettings = {
dots: true,
infinite: false,
speed: 500,
slidesToShow: 1,
slidesToScroll: 1,
arrows: true,
};

const charts = [];
this.props.chartsConfig.forEach((chartConfig, i) => {
const hasImagePath = chartConfig.type === 'image' && chartConfig.path;

// make sure the chart data is available as prop
if (!(chartConfig.prop in this.props) && !hasImagePath && !chartConfig.guppyConfig) {
console.error(`ChartCarousel is missing '${chartConfig.prop}' prop found in configuration`); // eslint-disable-line no-console
return;
}

// generate the chart
let chart = null;
const plotChartConfig = { ...chartConfig, plots: this.props[chartConfig.prop] };
switch (chartConfig.type) {
case 'component':
chart = this.props[chartConfig.prop];
break;
case 'image':
chart = (<img
className='chart-carousel__image'
src={covid19DashboardConfig.dataUrl +
(hasImagePath ? chartConfig.path : this.props[chartConfig.prop])}
alt={`Chart${chartConfig.title ? ` for ${chartConfig.title}` : ''}`}
/>);
break;
case 'lineChart':
case 'barChart':
chart = <PlotChart {...plotChartConfig} />;
break;
default:
console.error(`ChartCarousel cannot handle '${chartConfig.type}' chart type found in configuration`); // eslint-disable-line no-console
}

// if the chart data is not loaded yet, do not display an empty chart
if (!chart) {
return;
}

// TODO: description scrollbar
const showDescriptionColumn = this.props.isInPopup && (
chartConfig.title || chartConfig.description
);
const chartContainer = (<div
key={0}
onMouseOver={() => this.onChartHover(i)}
onMouseOut={() => this.onChartHover(null)}
>
<div
className={`${this.props.isInPopup ? 'chart-carousel__popup-chart' : null} ${showDescriptionColumn ? 'chart-carousel__left-column' : ''}`}
>
{chart}
</div>
{ showDescriptionColumn &&
<div className='chart-carousel__description'>
<h3>
{chartConfig.title}
</h3>
<p>
{chartConfig.description}
</p>
</div>
}
</div>);
charts.push(chartContainer);
});

const showDescriptionHover = !this.props.isInPopup &&
this.state.hoveredChartId !== null &&
// do not show the hover if there is a title without
// description - the title is already displayed
this.props.chartsConfig[this.state.hoveredChartId].description;

return (
charts.length > 0 ?
<div>
<div
className={`chart-carousel__container ${this.props.isInPopup ? '' : 'chart-carousel__container-border'}`}
ref={this.containerRef}
// match the popup width...
style={this.props.isInPopup ? { width: '70vw' } : {}}
>
<Slider {...sliderSettings}>
{charts}
</Slider>
</div>

{ showDescriptionHover &&
<div className='chart-carousel__hover' style={{ top: this.state.hoverYPosition }} >
<h3>
{this.props.chartsConfig[this.state.hoveredChartId].title}
</h3>
<p>
{this.props.chartsConfig[this.state.hoveredChartId].description}
</p>
</div>
}
</div>
: <Spinner />
);
}
}

ChartCarousel.propTypes = {
chartsConfig: PropTypes.array.isRequired,
isInPopup: PropTypes.bool,
};

ChartCarousel.defaultProps = {
isInPopup: false,
};

export default ChartCarousel;
14 changes: 14 additions & 0 deletions src/Covid19Dashboard/ControlPanel/ControlPanel.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.control-panel {
position: absolute;
z-index: 2;
padding: 8px;
margin: 10px;
max-width: 200px;
height: fit-content;
background: #fff;
box-shadow: 0 2px 4px rgba(0,0,0,0.3);
}

.control-panel > * {
margin-bottom: 0;
}
11 changes: 11 additions & 0 deletions src/Covid19Dashboard/ControlPanel/LegendPanel/LegendPanel.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.legend-panel__item {
margin-bottom: 0;
}

.legend-panel__item span {
display: inline-block;
width: 10px;
height: 10px;
margin-right: 5px;
border: 1px solid black;
}
61 changes: 61 additions & 0 deletions src/Covid19Dashboard/ControlPanel/LegendPanel/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';

import { numberWithCommas } from '../../dataUtils.js';

import './LegendPanel.less';

function dictToLegendList(colors) {
// input: {0: '#FFF', 10: '#888', 100: '#000'}
// output: [[ '0-9', '#FFF' ], [ '10-99', '#888' ], [ '100+', '#000' ]]
return Object.entries(colors).map((value, i) => {
const color = value[1];
let label = Number(value[0]);
if (i === Object.keys(colors).length - 1) {
label = `${label}+`;
} else {
const nextLabel = Number(Object.keys(colors)[i + 1]);
if (nextLabel - 1 !== label) {
label = `${label} - ${nextLabel - 1}`;
}
}
return [numberWithCommas(label), color];
});
}

class LegendPanel extends PureComponent {
render() {
const legendData = dictToLegendList(this.props.colors);
return (
<div className='legend-panel'>
<h3>Legend</h3>
<div>
{
legendData.map(
value =>
(<p
key={value[1]}
className='legend-panel__item'
>
<span
style={{ backgroundColor: value[1] }}
/>
{value[0]}
</p>),
)
}
</div>
</div>
);
}
}

LegendPanel.propTypes = {
colors: PropTypes.object,
};

LegendPanel.defaultProps = {
colors: {},
};

export default LegendPanel;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.map-style-panel__radio {
margin-right: 5px;
}
Loading

0 comments on commit fb8dd05

Please sign in to comment.