From 625ccbd2e6c9cfd8c4d840d33ee4af060d9754d5 Mon Sep 17 00:00:00 2001 From: Mcat12 Date: Sun, 8 Jul 2018 18:05:54 -0400 Subject: [PATCH] Combine forward destinations and query types charts into one component Named GenericDoughnutChart. Signed-off-by: Mcat12 --- src/components/ForwardDestinationsChart.js | 160 ++------------------ src/components/GenericDoughnutChart.js | 167 +++++++++++++++++++++ src/components/QueryTypesChart.js | 155 +------------------ 3 files changed, 184 insertions(+), 298 deletions(-) create mode 100644 src/components/GenericDoughnutChart.js diff --git a/src/components/ForwardDestinationsChart.js b/src/components/ForwardDestinationsChart.js index d4351f3..f1f9198 100644 --- a/src/components/ForwardDestinationsChart.js +++ b/src/components/ForwardDestinationsChart.js @@ -8,157 +8,15 @@ * This file is copyright under the latest version of the EUPL. * Please see LICENSE file for your rights under this license. */ -import React, { Component } from 'react'; -import { Doughnut } from 'react-chartjs-2'; -import { translate } from 'react-i18next'; -import { makeCancelable, api, ignoreCancel } from "../utils"; +import React from "react"; +import { translate } from "react-i18next"; +import { api } from "../utils"; +import GenericDoughnutChart from "./GenericDoughnutChart"; -class ForwardDestinationsChart extends Component { - state = { - loading: true, - data: [], - colors: [], - labels: [] - }; - - constructor(props) { - super(props); - this.chartRef = React.createRef(); - } - - updateChart = () => { - this.updateHandler = makeCancelable( - api.getForwardDestinations(), - { repeat: this.updateChart, interval: 10 * 60 * 1000 } - ); - this.updateHandler.promise.then(forwardDestinations => { - const colors = [ - "#20a8d8", - "#f86c6b", - "#4dbd74", - "#f8cb00", - "#263238", - "#63c2de", - "#b0bec5" - ]; - const data = []; - const labels = []; - const usedColors = []; - - // Fill in dataset metadata - let i = 0; - for(let destination of forwardDestinations) { - data.push(destination.percent); - labels.push(destination.name.length !== 0 ? destination.name : destination.ip); - usedColors.push( - // If we ran out of colors, make a random one - i < colors.length - ? colors[i] - : '#' + parseInt("" + Math.random() * 0xffffff, 10).toString(16).padStart(6, "0") - ); - - i++; - } - - this.setState({ - loading: false, - data, - colors: usedColors, - labels - }) - }).catch(ignoreCancel); - }; - - handleClick = (e, index) => { - // Hide the destination by clicking on the internal legend item - const chart = this.chartRef.current.chartInstance; - chart.legend.options.onClick.call(chart, e, chart.legend.legendItems[index]); - - // Cause an update so the external legend gets updated - this.forceUpdate(); - }; - - componentDidMount() { - this.updateChart(); - } - - componentWillUnmount() { - this.updateHandler.cancel(); - } - - render() { - const { t } = this.props; - - const options = { - legend: { display: false }, - tooltips: { - enabled: true, - callbacks: { - title: () => t("Queries Answered By Destination"), - label: (tooltipItems, data) => { - const dataset = data.datasets[tooltipItems.datasetIndex]; - const label = data.labels[tooltipItems.index]; - return label + ": " + dataset.data[tooltipItems.index].toFixed(1) + "%"; - } - } - }, - maintainAspectRatio: false - }; - - // Get the metadata for the items, so we know if they are hidden or not. - // If the chart has not been created yet, make some fake metadata. - const meta = this.chartRef.current !== null - ? this.chartRef.current.chartInstance.getDatasetMeta(0).data - : this.state.data.map(() => ({ hidden: false })); - - return ( -
-
- {t("Queries Answered By")} -
-
-
- -
-
-
    - { - this.state.labels - // Zip label and color together - .map((label, i) => [label, this.state.colors[i]]) - // Create the list items - .map(([label, color], i) => ( -
  • this.handleClick(e, i)}> - - {label} -
  • - )) - } -
-
-
- { - this.state.loading - ? -
- -
- : - null - } -
- ); - } -} +const ForwardDestinationsChart = ({ t }) => ( + +); export default translate("dashboard")(ForwardDestinationsChart); diff --git a/src/components/GenericDoughnutChart.js b/src/components/GenericDoughnutChart.js new file mode 100644 index 0000000..e18b4de --- /dev/null +++ b/src/components/GenericDoughnutChart.js @@ -0,0 +1,167 @@ +/* Pi-hole: A black hole for Internet advertisements +* (c) 2017 Pi-hole, LLC (https://pi-hole.net) +* Network-wide ad blocking via your own hardware. +* +* Web Interface +* Generic Doughnut Chart component +* +* This file is copyright under the latest version of the EUPL. +* Please see LICENSE file for your rights under this license. */ + +import React, { Component } from "react"; +import { Doughnut } from "react-chartjs-2"; +import PropTypes from "prop-types"; +import { makeCancelable, ignoreCancel } from "../utils"; + +class GenericDoughnutChart extends Component { + state = { + loading: true, + data: [], + colors: [], + labels: [] + }; + + constructor(props) { + super(props); + this.chartRef = React.createRef(); + } + + updateChart = () => { + this.updateHandler = makeCancelable( + this.props.apiCall(), + { repeat: this.updateChart, interval: 10 * 60 * 1000 } + ); + this.updateHandler.promise.then(apiData => { + const colors = [ + "#20a8d8", + "#f86c6b", + "#4dbd74", + "#f8cb00", + "#263238", + "#63c2de", + "#b0bec5" + ]; + const data = []; + const labels = []; + const usedColors = []; + + // Fill in dataset metadata + let i = 0; + for(let entry of apiData) { + data.push(entry.percent); + labels.push(entry.name.length !== 0 ? entry.name : entry.ip); + usedColors.push( + // If we ran out of colors, make a random one + i < colors.length + ? colors[i] + : "#" + parseInt("" + Math.random() * 0xffffff, 10).toString(16).padStart(6, "0") + ); + + i++; + } + + this.setState({ + loading: false, + data, + colors: usedColors, + labels + }); + }).catch(ignoreCancel); + }; + + handleClick = (e, index) => { + // Hide the entry by clicking on the internal legend item + const chart = this.chartRef.current.chartInstance; + chart.legend.options.onClick.call(chart, e, chart.legend.legendItems[index]); + + // Cause an update so the external legend gets updated + this.forceUpdate(); + }; + + componentDidMount() { + this.updateChart(); + } + + componentWillUnmount() { + this.updateHandler.cancel(); + } + + render() { + const options = { + legend: { display: false }, + tooltips: { + enabled: true, + callbacks: { + title: () => this.props.title, + label: (tooltipItems, data) => { + const dataset = data.datasets[tooltipItems.datasetIndex]; + const label = data.labels[tooltipItems.index]; + return label + ": " + dataset.data[tooltipItems.index].toFixed(1) + "%"; + } + } + }, + maintainAspectRatio: false + }; + + // Get the metadata for the items, so we know if they are hidden or not. + // If the chart has not been created yet, make some fake metadata. + const meta = this.chartRef.current !== null + ? this.chartRef.current.chartInstance.getDatasetMeta(0).data + : this.state.data.map(() => ({ hidden: false })); + + return ( +
+
+ {this.props.title} +
+
+
+ +
+
+
    + { + this.state.labels + // Zip label and color together + .map((label, i) => [label, this.state.colors[i]]) + // Create the list items + .map(([label, color], i) => ( +
  • this.handleClick(e, i)}> + + {label} +
  • + )) + } +
+
+
+ { + this.state.loading + ? +
+ +
+ : + null + } +
+ ); + } +} + +GenericDoughnutChart.propTypes = { + title: PropTypes.string.isRequired, + apiCall: PropTypes.func.isRequired +}; + +export default GenericDoughnutChart; \ No newline at end of file diff --git a/src/components/QueryTypesChart.js b/src/components/QueryTypesChart.js index c1016ec..7a41536 100644 --- a/src/components/QueryTypesChart.js +++ b/src/components/QueryTypesChart.js @@ -8,154 +8,15 @@ * This file is copyright under the latest version of the EUPL. * Please see LICENSE file for your rights under this license. */ -import React, { Component } from 'react'; -import { Doughnut } from 'react-chartjs-2'; +import React from 'react'; import { translate } from 'react-i18next'; -import { makeCancelable, api, ignoreCancel } from "../utils"; +import { api } from "../utils"; +import GenericDoughnutChart from "./GenericDoughnutChart"; -class QueryTypesChart extends Component { - state = { - loading: true, - data: [], - colors: [], - labels: [] - }; - - constructor(props) { - super(props); - this.chartRef = React.createRef(); - } - - updateChart = () => { - this.updateHandler = makeCancelable(api.getQueryTypes(), { repeat: this.updateChart, interval: 10 * 60 * 1000 }); - this.updateHandler.promise.then(queryTypes => { - const colors = [ - "#20a8d8", - "#f86c6b", - "#4dbd74", - "#f8cb00", - "#263238", - "#63c2de", - "#b0bec5" - ]; - const data = []; - const labels = []; - const usedColors = []; - - // Fill in dataset metadata - let i = 0; - for(let queryType of queryTypes) { - data.push(queryType.percent); - labels.push(queryType.name); - usedColors.push( - // If we ran out of colors, make a random one - i < colors.length - ? colors[i] - : '#' + parseInt("" + Math.random() * 0xffffff, 10).toString(16) - ); - - i++; - } - - this.setState({ - loading: false, - data, - colors: usedColors, - labels - }) - }).catch(ignoreCancel); - }; - - handleClick = (e, index) => { - // Hide the destination by clicking on the internal legend item - const chart = this.chartRef.current.chartInstance; - chart.legend.options.onClick.call(chart, e, chart.legend.legendItems[index]); - - // Cause an update so the external legend gets updated - this.forceUpdate(); - }; - - componentDidMount() { - this.updateChart(); - } - - componentWillUnmount() { - this.updateHandler.cancel(); - } - - render() { - const { t } = this.props; - - const options = { - legend: { display: false }, - tooltips: { - enabled: true, - callbacks: { - title: () => t("Query Types"), - label: (tooltipItems, data) => { - const dataset = data.datasets[tooltipItems.datasetIndex]; - const label = data.labels[tooltipItems.index]; - return label + ": " + dataset.data[tooltipItems.index].toFixed(1) + "%"; - } - } - }, - maintainAspectRatio: false - }; - - // Get the metadata for the items, so we know if they are hidden or not. - // If the chart has not been created yet, make some fake metadata. - const meta = this.chartRef.current !== null - ? this.chartRef.current.chartInstance.getDatasetMeta(0).data - : this.state.data.map(() => ({ hidden: false })); - - return ( -
-
- {t("Query Types")} -
-
-
- -
-
-
    - { - this.state.labels - // Zip label and color together - .map((label, i) => [label, this.state.colors[i]]) - // Create the list items - .map(([label, color], i) => ( -
  • this.handleClick(e, i)}> - - {label} -
  • - )) - } -
-
-
- { - this.state.loading - ? -
- -
- : - null - } -
- ); - } -} +const QueryTypesChart = ({ t }) => ( + +); export default translate("dashboard")(QueryTypesChart);