Skip to content

Commit

Permalink
Merge pull request #153 from georgetown-cset/142-add-partial-year-sha…
Browse files Browse the repository at this point in the history
…ding

Add partial year shading
  • Loading branch information
jmelot authored Oct 30, 2023
2 parents 0f7c8d9 + 9cfb63c commit 86ffaae
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 114 deletions.
47 changes: 0 additions & 47 deletions web/gui-v2/src/components/DetailViewChart.jsx

This file was deleted.

25 changes: 10 additions & 15 deletions web/gui-v2/src/components/DetailViewPatents.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import { css } from '@emotion/react';

import { Autocomplete, Dropdown } from '@eto/eto-ui-components';
import { Autocomplete } from '@eto/eto-ui-components';

import HeaderWithLink from './HeaderWithLink';
import StatGrid from './StatGrid';
Expand All @@ -11,7 +11,6 @@ import TrendsChart from './TrendsChart';
import overall from '../static_data/overall_data.json';
import { patentMap } from '../static_data/table_columns';
import { commas } from '../util';
import { assemblePlotlyParams } from '../util/plotly-helpers';

const styles = {
section: css`
Expand Down Expand Up @@ -123,18 +122,6 @@ const DetailViewPatents = ({
.map(k => ({ text: patentMap[k].replace(/ patents/i, ''), val: k }))
.sort((a, b) => a.text.localeCompare(b.text, 'en', { sensitivity: 'base' }));

const aiSubfieldChartData = assemblePlotlyParams(
"Trends in research....",
overall.years,
[
[
aiSubfieldOptions.find(e => e.val === aiSubfield)?.text,
data.patents[aiSubfield].counts
],
],
chartLayoutChanges,
);

return (
<>
<HeaderWithLink title="Patents" />
Expand Down Expand Up @@ -164,8 +151,15 @@ const DetailViewPatents = ({

<TrendsChart
css={styles.section}
{...aiSubfieldChartData}
data={[
[
aiSubfieldOptions.find(e => e.val === aiSubfield)?.text,
data.patents[aiSubfield].counts
],
]}
id="ai-subfield-patents"
layoutChanges={chartLayoutChanges}
partialStartIndex={endIx}
title={
<>
Trends in {data.name}'s patenting in
Expand All @@ -179,6 +173,7 @@ const DetailViewPatents = ({
/>
</>
}
years={overall.years}
/>
</>
);
Expand Down
47 changes: 20 additions & 27 deletions web/gui-v2/src/components/DetailViewPublications.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { css } from '@emotion/react';

import { Dropdown } from '@eto/eto-ui-components';

import Chart from './DetailViewChart';
import HeaderWithLink from './HeaderWithLink';
import StatGrid from './StatGrid';
import TableSection from './TableSection';
Expand All @@ -12,7 +11,6 @@ import TrendsChart from './TrendsChart';
import overall from '../static_data/overall_data.json';
import { articleMap } from '../static_data/table_columns';
import { commas } from '../util';
import { assemblePlotlyParams } from '../util/plotly-helpers';

const styles = {
noTopMargin: css`
Expand Down Expand Up @@ -119,27 +117,6 @@ const DetailViewPublications = ({
{ text: "Robotics", val: "robotics_pubs" },
];

const aiSubfieldChartData = assemblePlotlyParams(
"Trends in research....",
overall.years,
[
[
aiSubfieldOptions.find(e => e.val === aiSubfield)?.text,
data.articles[aiSubfield].counts
],
],
chartLayoutChanges,
);

const topConfs = assemblePlotlyParams(
<>{data.name}'s top AI conference publications</>,
overall.years,
[
["AI top conference publications", data.articles.ai_pubs_top_conf.counts],
],
chartLayoutChanges,
);

return (
<>
<HeaderWithLink css={styles.noTopMargin} title="Publications" />
Expand All @@ -161,8 +138,15 @@ const DetailViewPublications = ({

<TrendsChart
css={styles.section}
{...aiSubfieldChartData}
data={[
[
aiSubfieldOptions.find(e => e.val === aiSubfield)?.text,
data.articles[aiSubfield].counts
],
]}
id="ai-subfield-research"
layoutChanges={chartLayoutChanges}
partialStartIndex={endIx}
title={
<>
Trends in {data.name}'s research in
Expand All @@ -176,11 +160,20 @@ const DetailViewPublications = ({
/>
</>
}
years={overall.years}
/>

<div css={styles.section}>
<Chart {...topConfs} id="ai-top-conference-pubs" />
</div>
<TrendsChart
css={styles.section}
data={[
["AI top conference publications", data.articles.ai_pubs_top_conf.counts],
]}
id="ai-top-conference-pubs-2"
layoutChanges={chartLayoutChanges}
partialStartIndex={endIx}
title={<>{data.name}'s top AI conference publications</>}
years={overall.years}
/>
</>
);
};
Expand Down
4 changes: 2 additions & 2 deletions web/gui-v2/src/components/ListView.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,14 @@ describe("ListView", () => {
await user.click(removedCheckbox);
expect(removedCheckbox.checked).toEqual(false);
await user.click(getByRole(dialog, 'button', { name: 'Apply' }));
await waitForElementToBeRemoved(dialog);
await waitForElementToBeRemoved(() => screen.getByRole('dialog'));
expect(screen.queryByRole('heading', { name: 'Add/remove columns'})).not.toBeInTheDocument();

// Verify that the changes took effect
for ( const column of INITIAL_COLUMNS.filter(e => e !== REMOVED_COLUMN) ) {
expect(screen.getByRole('columnheader', { name: new RegExp(column, 'i') }));
}
}, 60000);
}, 90000);
});

describe('groups', () => {
Expand Down
55 changes: 47 additions & 8 deletions web/gui-v2/src/components/TrendsChart.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import React from 'react';
import React, { Suspense, lazy } from 'react';
import { css } from '@emotion/react';

import Chart from './DetailViewChart';
import SectionHeading from './SectionHeading';
import { fallback } from '../styles/common-styles';
import { assemblePlotlyParams } from '../util/plotly-helpers';

const Plot = lazy(() => import('react-plotly.js'));

const isSSR = typeof window === "undefined";

const styles = {
chartWrapper: css`
Expand All @@ -19,26 +25,59 @@ const styles = {
}
}
`,
chartContainer: css`
/* aspect-ratio: 4 / 3; */
aspect-ratio: 16 / 9;
display: flex;
flex-direction: column;
margin: 0.5rem auto 0;
max-width: 1000px;
`,
};

/**
* Chart and heading showing trends over time.
*
* @param {object} props
* @param {Array<[string, Array<number>]>} props.data
* @param {object} props.layoutChanges
* @param {boolean|undefined} props.partialStartIndex
* @param {string} props.title
* @param {Array<number>} 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 (
<div
className={appliedClassName}
css={[styles.sectionMargin, styles.sectionWithHeading, styles.chartWrapper, appliedCss]}
id={appliedId}
>
<Chart
{...otherProps}
id={appliedId}
title={title}
/>
{!isSSR &&
<Suspense fallback={<div css={fallback}>Loading graph...</div>}>
<SectionHeading id={appliedId}>
{title}
</SectionHeading>
<div css={styles.chartContainer}>
<Plot
data={data}
layout={layout}
config={config}
/>
</div>
</Suspense>
}
</div>
);
};
Expand Down
62 changes: 47 additions & 15 deletions web/gui-v2/src/util/plotly-helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -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<number>} years Array of years corresponding to `data`
* @param {Array<[string, Array<number>]>} 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]))
Expand All @@ -41,7 +75,5 @@ export const assemblePlotlyParams = (title, years, data, layoutChanges) => {
config,
data: preparedData,
layout,
title,
};
};

Loading

0 comments on commit 86ffaae

Please sign in to comment.