} props.years
+ * @returns {JSX.Element}
+ */
const TrendsChart = ({
className: appliedClassName,
css: appliedCss,
+ data: dataRaw,
id: appliedId,
+ layoutChanges,
+ partialStartIndex=undefined,
title,
- ...otherProps
+ years,
}) => {
+ const { config, data, layout } = assemblePlotlyParams(years, dataRaw, layoutChanges, { partialStartIndex });
+
return (
-
+ {!isSSR &&
+ Loading graph...
}>
+
+ {title}
+
+
+
+ }
);
};
diff --git a/web/gui-v2/src/util/plotly-helpers.js b/web/gui-v2/src/util/plotly-helpers.js
index b0d9d0cf..f42aaefa 100644
--- a/web/gui-v2/src/util/plotly-helpers.js
+++ b/web/gui-v2/src/util/plotly-helpers.js
@@ -2,34 +2,68 @@ import merge from 'lodash/merge';
import { PlotlyDefaults } from '@eto/eto-ui-components';
-const assembleChartData = (name, years, vals, otherParams) => {
- return {
+const assembleChartData = (name, years, vals, otherParams, options={}) => {
+ const { partialStartIndex } = options;
+
+ const common = {
hovertemplate: "%{y}",
- mode: 'lines+markers',
- type: 'scatter',
- ...otherParams,
+ legendgroup: name,
+ mode: "lines+markers",
name,
- x: years,
- y: vals,
- };
+ type: "scatter",
+ }
+
+ // TODO / QUESTION: How do we want to handle the line/shading style for the
+ // year specified in `year*End`? Think about and adjust if desired.
+ const endSolidIx = partialStartIndex ? (partialStartIndex + 1) : undefined;
+
+ const result = [
+ {
+ ...common,
+ ...otherParams,
+ x: years.slice(0, endSolidIx),
+ y: vals.slice(0, endSolidIx),
+ }
+ ];
+
+ if ( partialStartIndex !== undefined ) {
+ result.push({
+ ...common,
+ ...otherParams,
+ fill: "tozeroy",
+ line: { dash: "dash" },
+ marker: { color: "lightgray" },
+ showlegend: false,
+ x: years.slice(partialStartIndex),
+ y: vals.slice(partialStartIndex),
+ });
+ }
+
+ return result;
};
/**
* Generate the parameters for a given Plotly chart.
*
- * @param {string} title Overall title for the chart
* @param {Array} years Array of years corresponding to `data`
* @param {Array<[string, Array]>} data Array of tuples representing
* individual traces in the chart, each consisting of a string for the trace
* title and an array of values (which must be the same length as `years`).
* @param {object} layoutChanges Any changes that should be merged into the
* ETO-standard `layout` object provided by `PlotlyDefaults`.
+ * @param {object} options
+ * @param {number} options.partialStartIndex
* @returns {object} An object containing the parameters for this Plotly chart:
- * `{ config, data, layout, title }`
+ * `{ config, data, layout }`
*/
-export const assemblePlotlyParams = (title, years, data, layoutChanges) => {
- const preparedData = data.map(([traceTitle, traceData, otherParams={}]) => {
- return assembleChartData(traceTitle, years, traceData, otherParams);
+export const assemblePlotlyParams = (
+ years,
+ data,
+ layoutChanges={},
+ options={},
+) => {
+ const preparedData = data.flatMap(([traceTitle, traceData, otherParams={}]) => {
+ return assembleChartData(traceTitle, years, traceData, otherParams, options);
});
const maxY = Math.max(
...data.map(e => Math.max(...e[1]))
@@ -41,7 +75,5 @@ export const assemblePlotlyParams = (title, years, data, layoutChanges) => {
config,
data: preparedData,
layout,
- title,
};
};
-
diff --git a/web/gui-v2/src/util/plotly-helpers.test.js b/web/gui-v2/src/util/plotly-helpers.test.js
new file mode 100644
index 00000000..f892e80d
--- /dev/null
+++ b/web/gui-v2/src/util/plotly-helpers.test.js
@@ -0,0 +1,71 @@
+import { assemblePlotlyParams } from './plotly-helpers';
+
+
+const TITLE = "AI top conference publications";
+const YEARS = [ 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 ];
+const INPUT_DATA = [
+ [ TITLE, [ 850, 928, 845, 843, 838, 1040, 1357, 1551, 1507, 461, 2 ] ],
+];
+const LAYOUT_CHANGES = {
+ legend: { y: 1.15 },
+ margin: { b: 50, l: 50, pad: 4, r: 50, t: 0 },
+};
+const PARTIAL_START = YEARS.findIndex(e => e === 2022);
+
+describe("Plotly helpers", () => {
+ it("create the parameters as expected", () => {
+
+ const { config, data, layout } = assemblePlotlyParams(YEARS, INPUT_DATA, LAYOUT_CHANGES, { partialStartIndex: PARTIAL_START });
+
+ expect(config).toEqual({
+ displayModeBar: false,
+ responsive: true,
+ });
+ expect(data).toEqual([
+ {
+ hovertemplate: "%{y}",
+ legendgroup: TITLE,
+ mode: "lines+markers",
+ name: TITLE,
+ type: "scatter",
+ x: [ 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 ],
+ y: [ 850, 928, 845, 843, 838, 1040, 1357, 1551, 1507, 461 ],
+ },
+ {
+ fill: "tozeroy",
+ hovertemplate: "%{y}",
+ legendgroup: TITLE,
+ line: { dash: "dash" },
+ marker: { color: "lightgray" },
+ mode: "lines+markers",
+ name: TITLE,
+ showlegend: false,
+ type: "scatter",
+ x: [ 2022, 2023 ],
+ y: [ 461, 2 ],
+ },
+ ]);
+ expect(layout).toEqual({
+ autosize: true,
+ xaxis: {
+ fixedrange: true,
+ },
+ yaxis: {
+ fixedrange: true,
+ },
+ font: {
+ family: "GTZirkonRegular, Arial"
+ },
+ legend: {
+ orientation: "h",
+ yanchor: "top",
+ xanchor: "center",
+ y: 1.15,
+ x: 0.5
+ },
+ margin: { b: 50, l: 50, pad: 4, r: 50, t: 0 },
+ paper_bgcolor: "rgba(0,0,0,0)",
+ plot_bgcolor: "rgba(0,0,0,0)",
+ });
+ });
+});