Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add depth to chart #338

Merged
merged 14 commits into from
Oct 26, 2020
12 changes: 7 additions & 5 deletions packages/website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"version": "0.1.0",
"private": true,
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"start": "cross-env EXTEND_ESLINT=true react-scripts start",
"build": "cross-env EXTEND_ESLINT=true react-scripts build",
"test": "react-scripts --expose-gc test --logHeapUsage --maxWorkers=2",
"eject": "react-scripts eject",
"lint": "eslint --ext .js,.jsx,.ts,.tsx ./src --ignore-path .gitignore",
Expand Down Expand Up @@ -83,9 +83,9 @@
"node": "12.x"
},
"devDependencies": {
"@types/immutable": "^3.8.7",
"@types/validator": "^13.1.0",
"@types/chart.js": "^2.9.27",
"@types/classnames": "^2.2.10",
"@types/immutable": "^3.8.7",
"@types/jest": "^24.0.0",
"@types/lodash": "^4.14.149",
"@types/node": "^12.0.0",
Expand All @@ -97,6 +97,8 @@
"@types/react-slick": "^0.23.4",
"@types/react-swipeable-views": "^0.13.0",
"@types/redux-mock-store": "^1.0.2",
"canvas": "^2.6.1"
"@types/validator": "^13.1.0",
"canvas": "^2.6.1",
"cross-env": "^7.0.2"
}
}
13 changes: 8 additions & 5 deletions packages/website/src/common/Chart/ChartWithTooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import React, {
useState,
} from "react";
import { Line } from "react-chartjs-2";
import Chart, { ChartProps } from "./index";
import type { ChartTooltipModel } from "chart.js";
import Chart, { ChartProps } from ".";
import Tooltip, { TooltipData } from "./Tooltip";
import { useProcessedChartData } from "./utils";

interface ChartWithTooltipProps extends ChartProps {
export interface ChartWithTooltipProps extends ChartProps {
depth: number | null;
className?: string;
style?: CSSProperties;
Expand Down Expand Up @@ -43,9 +44,11 @@ function ChartWithTooltip({
});
const [showTooltip, setShowTooltip] = useState<boolean>(false);

const customTooltip = (ref: React.RefObject<Line>) => (tooltipModel: any) => {
const customTooltip = (ref: React.RefObject<Line>) => (
tooltipModel: ChartTooltipModel
) => {
const chart = ref.current;
if (!chart) {
if (!chart?.chartInstance.canvas) {
return;
}
const position = chart.chartInstance.canvas.getBoundingClientRect();
Expand All @@ -54,7 +57,7 @@ function ChartWithTooltip({
const date = tooltipModel.dataPoints?.[0]?.xLabel;
const index = date && chartLabels.findIndex((item) => item === date);

if (index > -1 && index !== 0) {
if (index && index > -1 && typeof date === "string") {
setTooltipPosition({ top, left });
setTooltipData({
date,
Expand Down
88 changes: 41 additions & 47 deletions packages/website/src/common/Chart/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ import "./plugins/backgroundPlugin";
import "./plugins/fillPlugin";
import "./plugins/slicePlugin";
import "chartjs-plugin-annotation";
import { createChartData } from "../../helpers/createChartData";
import { useProcessedChartData } from "./utils";
import { createChartData, useProcessedChartData } from "./utils";
import { SurveyListItem } from "../../store/Survey/types";

export interface ChartProps {
Expand All @@ -28,6 +27,30 @@ export interface ChartProps {

const SMALL_WINDOW = 400;

const makeAnnotation = (
name: string,
value: number | null,
borderColor: string,
backgroundColor = "rgb(169,169,169, 0.7)"
) => ({
type: "line",
mode: "horizontal",
scaleID: "y-axis-0",
value,
borderColor,
borderWidth: 2,
borderDash: [5, 5],
label: {
enabled: true,
backgroundColor,
yPadding: 3,
xPadding: 3,
position: "left",
xAdjust: 10,
content: name,
},
});

function Chart({
dailyData,
surveys,
Expand All @@ -50,22 +73,24 @@ function Chart({

const [xPeriod, setXPeriod] = useState<"week" | "month">("week");

const stepSize = 5;
const yStepSize = 5;

const {
xAxisMax,
xAxisMin,
yAxisMax,
yAxisMin,
surfaceTemperatureData,
bottomTemperatureData,
tempWithSurvey,
chartLabels,
} = useProcessedChartData(dailyData, surveys, temperatureThreshold);

const changeXTickShiftAndPeriod = () => {
const { current } = chartRef;
if (current) {
const xScale = current.chartInstance.scales["x-axis-0"];
// not sure why 'scales' doesn't have a type. Possibly from a plugin?
const xScale = (current.chartInstance as any).scales["x-axis-0"];
const ticksPositions = xScale.ticks.map((_: any, index: number) =>
xScale.getPixelForTick(index)
);
Expand All @@ -79,9 +104,7 @@ function Chart({
}
};

/*
Catch the "window done resizing" event as suggested by https://css-tricks.com/snippets/jquery/done-resizing-event/
*/
// Catch the "window done resizing" event as suggested by https://css-tricks.com/snippets/jquery/done-resizing-event/
const onResize = useCallback(() => {
setUpdateChart(true);
setTimeout(() => {
Expand Down Expand Up @@ -130,42 +153,12 @@ function Chart({

annotation: {
annotations: [
{
type: "line",
mode: "horizontal",
scaleID: "y-axis-0",
value: maxMonthlyMean,
borderColor: "rgb(75, 192, 192)",
borderWidth: 2,
borderDash: [5, 5],
label: {
enabled: true,
backgroundColor: "rgb(169,169,169, 0.7)",
yPadding: 3,
xPadding: 3,
position: "left",
xAdjust: 10,
content: "Historical Max",
},
},
{
type: "line",
mode: "horizontal",
scaleID: "y-axis-0",
value: temperatureThreshold,
borderColor: "#ff8d00",
borderWidth: 2,
borderDash: [5, 5],
label: {
enabled: true,
backgroundColor: "rgb(169,169,169, 0.7)",
yPadding: 3,
xPadding: 3,
position: "left",
xAdjust: 10,
content: "Bleaching Threshold",
},
},
makeAnnotation("Historical Max", maxMonthlyMean, "rgb(75, 192, 192)"),
makeAnnotation(
"Bleaching Threshold",
temperatureThreshold,
"#ff8d00"
),
],
},
scales: {
Expand Down Expand Up @@ -200,11 +193,11 @@ function Chart({
display: true,
ticks: {
min: yAxisMin,
stepSize,
stepSize: yStepSize,
max: yAxisMax,
callback: (value: number) => {
if (![1, stepSize - 1].includes(value % stepSize)) {
return `${value}\u00B0 `;
if (![1, yStepSize - 1].includes(value % yStepSize)) {
return `${value}° `;
}
return "";
},
Expand All @@ -230,7 +223,8 @@ function Chart({
chartLabels,
tempWithSurvey,
surfaceTemperatureData,
Boolean(temperatureThreshold)
bottomTemperatureData,
!!temperatureThreshold
)}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ const plugin = {
const chartWidth = chartArea.right - chartArea.left;
const day = 1;
ctx.save();
// eslint-disable-next-line fp/no-mutation
ctx.fillStyle = options.color;
if (options.xTicksFontWeight) {
// eslint-disable-next-line no-param-reassign
// eslint-disable-next-line no-param-reassign,fp/no-mutation
chart.scales["x-axis-0"].options.ticks.fontSize =
(chartWidth * options.xTicksFontWeight) / 100;
}
// eslint-disable-next-line fp/no-mutation
for (let i = 0; i < ticksPositions.length; i += 2 * day) {
const start = ticksPositions[i];
const end = ticksPositions[i + day];
Expand Down
2 changes: 1 addition & 1 deletion packages/website/src/common/Chart/plugins/fillPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const fillBetweenLinesPlugin = {
!chart.data.datasets[options.datasetIndex].backgroundColor ||
options.updateChart
) {
// eslint-disable-next-line no-param-reassign
// eslint-disable-next-line no-param-reassign,fp/no-mutation
chart.data.datasets[options.datasetIndex].backgroundColor = gradient;
chart.update();
}
Expand Down
5 changes: 3 additions & 2 deletions packages/website/src/common/Chart/plugins/slicePlugin.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const { Chart } = require("react-chartjs-2");

export const sliceDrawPLugin = {
export const sliceDrawPlugin = {
id: "sliceDrawPlugin",
afterDatasetsDraw: (chart: any, _easingValue: any, options: any) => {
const yScale = chart.scales["y-axis-0"];
Expand All @@ -13,11 +13,12 @@ export const sliceDrawPLugin = {
if (options.sliceAtLabel) {
ctx.beginPath();
ctx.moveTo(xCoord, top);
// eslint-disable-next-line fp/no-mutation
ctx.strokeStyle = "#777777";
ctx.lineTo(xCoord, bottom);
ctx.stroke();
}
},
};

Chart.pluginService.register(sliceDrawPLugin);
Chart.pluginService.register(sliceDrawPlugin);
75 changes: 72 additions & 3 deletions packages/website/src/common/Chart/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ChartComponentProps } from "react-chartjs-2";
import type { ChartProps } from ".";
import { sortByDate } from "../../helpers/sortDailyData";
import type { DailyData } from "../../store/Reefs/types";
Expand Down Expand Up @@ -30,12 +31,13 @@ export const createDatasets = (
.map((item) => {
const date = new Date(item.date).setHours(0, 0, 0, 0);
if (surveyDates.includes(date)) {
return item.satelliteTemperature;
return item.avgBottomTemperature || item.satelliteTemperature;
}
return null;
});

return {
// repeat first value, so chart start point isn't instantaneous.
tempWithSurvey: [tempWithSurvey[0], ...tempWithSurvey],
bottomTemperatureData: [bottomTemperature[0], ...bottomTemperature],
surfaceTemperatureData: [surfaceTemperature[0], ...surfaceTemperature],
Expand Down Expand Up @@ -65,9 +67,15 @@ export const calculateAxisLimits = (
// Add an extra date one day after the final daily data date
const chartLabels = [xAxisMin, ...dates];

const { surfaceTemperatureData } = createDatasets(dailyData, surveys);
const { surfaceTemperatureData, bottomTemperatureData } = createDatasets(
dailyData,
surveys
);

const temperatureData = [...surfaceTemperatureData].filter((value) => value);
const temperatureData = [
...surfaceTemperatureData,
...bottomTemperatureData,
].filter((value) => value);

const yAxisMinTemp = Math.min(...temperatureData) - ySpacing;

Expand Down Expand Up @@ -111,3 +119,64 @@ export function useProcessedChartData(
);
return { sortedDailyData, ...axisLimits, ...datasets };
}

export const createChartData = (
labels: string[],
tempWithSurvey: (number | null)[],
surfaceTemps: number[],
bottomTemps: number[],
fill: boolean
) => {
const data: ChartComponentProps["data"] = {
labels,
datasets: [
{
type: "scatter",
label: "SURVEYS",
data: tempWithSurvey,
pointRadius: 5,
backgroundColor: "#ffffff",
pointBackgroundColor: "#ffff",
borderWidth: 1.5,
borderColor: "#128cc0",
},
{
label: "SURFACE TEMP",
data: surfaceTemps,
backgroundColor: "rgb(107,193,225,0.2)",
borderColor: "#6bc1e1",
borderWidth: 2,
pointBackgroundColor: "#ffffff",
pointBorderWidth: 1.5,
pointRadius: 0,
cubicInterpolationMode: "monotone",
},
{
label: "TEMP AT DEPTH",
data: bottomTemps,
borderColor: "#46a5cf",
borderWidth: 2,
pointBackgroundColor: "#ffffff",
pointBorderWidth: 1.5,
pointRadius: 0,
cubicInterpolationMode: "monotone",
},
],
};

if (fill) {
// eslint-disable-next-line fp/no-mutating-methods
data.datasets!.splice(1, 0, {
label: "BLEACHING THRESHOLD",
data: surfaceTemps,
fill,
borderColor: "#6bc1e1",
borderWidth: 2,
pointBackgroundColor: "#ffffff",
pointBorderWidth: 1.5,
pointRadius: 0,
cubicInterpolationMode: "monotone",
});
}
return data;
};
1 change: 1 addition & 0 deletions packages/website/src/common/Search/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { getReefNameAndRegion } from "../../store/Reefs/helpers";
const Search = ({ classes }: SearchProps) => {
const [searchedReef, setSearchedReef] = useState<Reef | null>(null);
const dispatch = useDispatch();
// eslint-disable-next-line fp/no-mutating-methods
const reefs = useSelector(reefsListSelector)
.filter((reef) => getReefNameAndRegion(reef).name)
// Sort by formatted name
Expand Down
Loading