Speed up dataset drawer rendering (#1926)

This commit is contained in:
maniarathi
2020-10-12 11:20:58 -07:00
committed by GitHub
parent beb46bf3df
commit 5325495123
2 changed files with 53 additions and 133 deletions
+21 -77
View File
@@ -1,13 +1,9 @@
import React, { PureComponent } from "react"; import React, { PureComponent } from "react";
import { connect, shallowEqual } from "react-redux"; import { connect } from "react-redux";
import { Drawer } from "@blueprintjs/core"; import { Drawer } from "@blueprintjs/core";
import Async from "react-async";
import InfoFormat from "./infoFormat"; import InfoFormat from "./infoFormat";
import { import { selectableCategoryNames } from "../../util/stateManager/controlsHelpers";
selectableCategoryNames,
createCategorySummaryFromDfCol,
} from "../../util/stateManager/controlsHelpers";
@connect((state) => { @connect((state) => {
return { return {
@@ -20,44 +16,6 @@ import {
}; };
}) })
class InfoDrawer extends PureComponent { class InfoDrawer extends PureComponent {
static watchAsync(props, prevProps) {
return !shallowEqual(props.watchProps, prevProps.watchProps);
}
fetchAsyncProps = async (props) => {
const { schema } = props.watchProps;
const { annoMatrix } = this.props;
const allCategoryNames = selectableCategoryNames(schema).sort();
const nonUserAnnoCategories = allCategoryNames.map((catName) => {
const isUserAnno = schema?.annotations?.obsByName[catName]?.writable;
if (!isUserAnno) return annoMatrix.fetch("obs", catName);
return null;
});
const singleValueCategories = (
await Promise.all(nonUserAnnoCategories)
).reduce((acc, categoryData, i) => {
// Actually check to see if it is null(user anno)
if (!categoryData) return acc;
const catName = allCategoryNames[i];
const column = categoryData.icol(0);
const colSchema = schema.annotations.obsByName[catName];
const categorySummary = createCategorySummaryFromDfCol(column, colSchema);
const { numCategoryValues } = categorySummary;
// Add to the array if the category has only one value
if (numCategoryValues === 1) {
acc.set(catName, categorySummary.allCategoryValues[0]);
}
return acc;
}, new Map());
return { singleValueCategories };
};
handleClose = () => { handleClose = () => {
const { dispatch } = this.props; const { dispatch } = this.props;
@@ -74,45 +32,31 @@ class InfoDrawer extends PureComponent {
dataPortalProps, dataPortalProps,
} = this.props; } = this.props;
const allCategoryNames = selectableCategoryNames(schema).sort();
const singleValueCategories = new Map();
allCategoryNames.forEach((catName) => {
const isUserAnno = schema?.annotations?.obsByName[catName]?.writable;
const colSchema = schema.annotations.obsByName[catName];
if (!isUserAnno && colSchema.categories?.length === 1) {
singleValueCategories.set(catName, colSchema.categories[0]);
}
});
return ( return (
<Drawer <Drawer
title="Dataset Overview" title="Dataset Overview"
onClose={this.handleClose} onClose={this.handleClose}
{...{ isOpen, position }} {...{ isOpen, position }}
> >
<Async <InfoFormat
watchFn={InfoDrawer.watchAsync} {...{
promiseFn={this.fetchAsyncProps} datasetTitle,
watchProps={{ schema }} aboutURL,
> singleValueCategories,
<Async.Pending> dataPortalProps,
<InfoFormat }}
skeleton />
{...{ datasetTitle, aboutURL, dataPortalProps }}
/>
</Async.Pending>
<Async.Rejected>
{(error) => {
console.error(error);
return <span>Failed to load info</span>;
}}
</Async.Rejected>
<Async.Fulfilled>
{(asyncProps) => {
const { singleValueCategories } = asyncProps;
return (
<InfoFormat
{...{
datasetTitle,
aboutURL,
singleValueCategories,
dataPortalProps,
}}
/>
);
}}
</Async.Fulfilled>
</Async>
</Drawer> </Drawer>
); );
} }
+32 -56
View File
@@ -1,15 +1,15 @@
import { H3, H1, UL, Classes } from "@blueprintjs/core"; import { H3, H1, UL } from "@blueprintjs/core";
import React from "react"; import React from "react";
import Truncate from "../util/truncate"; import Truncate from "../util/truncate";
const renderContributors = (contributors, affiliations, skeleton) => { const renderContributors = (contributors, affiliations) => {
// eslint-disable-next-line no-constant-condition -- Temp removed contributor section to avoid publishing PII // eslint-disable-next-line no-constant-condition -- Temp removed contributor section to avoid publishing PII
if (!contributors || contributors.length === 0 || true) return null; if (!contributors || contributors.length === 0 || true) return null;
return ( return (
<> <>
<H3 className={skeleton ? Classes.SKELETON : null}>Contributors</H3> <H3>Contributors</H3>
<p className={skeleton ? Classes.SKELETON : null}> <p>
{contributors.map((contributor) => { {contributors.map((contributor) => {
const { email, name, institution } = contributor; const { email, name, institution } = contributor;
@@ -22,7 +22,7 @@ const renderContributors = (contributors, affiliations, skeleton) => {
); );
})} })}
</p> </p>
{renderAffiliations(affiliations, skeleton)} {renderAffiliations(affiliations)}
</> </>
); );
}; };
@@ -39,14 +39,14 @@ const buildAffiliations = (contributors = []) => {
return affiliations; return affiliations;
}; };
const renderAffiliations = (affiliations, skeleton) => { const renderAffiliations = (affiliations) => {
if (affiliations.length === 0) return null; if (affiliations.length === 0) return null;
return ( return (
<> <>
<H3 className={skeleton ? Classes.SKELETON : null}>Affiliations</H3> <H3>Affiliations</H3>
<UL> <UL>
{affiliations.map((item, index) => ( {affiliations.map((item, index) => (
<div key={item} className={skeleton ? Classes.SKELETON : null}> <div key={item}>
<sup>{index + 1}</sup> <sup>{index + 1}</sup>
{" "} {" "}
{item} {item}
@@ -57,12 +57,12 @@ const renderAffiliations = (affiliations, skeleton) => {
); );
}; };
const renderDOILink = (type, doi, skeleton) => { const renderDOILink = (type, doi) => {
if (!doi) return null; if (!doi) return null;
return ( return (
<> <>
<H3 className={skeleton ? Classes.SKELETON : null}>{type}</H3> <H3>{type}</H3>
<p className={skeleton ? Classes.SKELETON : null}> <p>
<a href={doi} target="_blank" rel="noopener"> <a href={doi} target="_blank" rel="noopener">
{doi} {doi}
</a> </a>
@@ -71,12 +71,12 @@ const renderDOILink = (type, doi, skeleton) => {
); );
}; };
const renderOrganism = (organism, skeleton) => { const renderOrganism = (organism) => {
if (!organism) return null; if (!organism) return null;
return ( return (
<> <>
<H3 className={skeleton ? Classes.SKELETON : null}>Organism</H3> <H3>Organism</H3>
<p className={skeleton ? Classes.SKELETON : null}>{organism}</p> <p>{organism}</p>
</> </>
); );
}; };
@@ -85,11 +85,11 @@ const ONTOLOGY_KEY = "ontology_term_id";
const CAT_WIDTH = "30%"; const CAT_WIDTH = "30%";
const VAL_WIDTH = "35%"; const VAL_WIDTH = "35%";
// Render list of metadata attributes found in categorical field // Render list of metadata attributes found in categorical field
const renderSingleValueCategories = (singleValueCategories, skeleton) => { const renderSingleValueCategories = (singleValueCategories) => {
if (singleValueCategories.size === 0) return null; if (singleValueCategories.size === 0) return null;
return ( return (
<> <>
<H3 className={skeleton ? Classes.SKELETON : null}>Dataset Metadata</H3> <H3>Dataset Metadata</H3>
<UL> <UL>
{Array.from(singleValueCategories).reduce((elems, pair) => { {Array.from(singleValueCategories).reduce((elems, pair) => {
const [category, value] = pair; const [category, value] = pair;
@@ -115,11 +115,7 @@ const renderSingleValueCategories = (singleValueCategories, skeleton) => {
} else { } else {
// Create the list item // Create the list item
elems.push( elems.push(
<li <li key={category} style={{ width: "100%" }}>
className={skeleton ? Classes.SKELETON : null}
key={category}
style={{ width: "100%" }}
>
<Truncate> <Truncate>
<span style={{ width: CAT_WIDTH }}>{`${category}:`}</span> <span style={{ width: CAT_WIDTH }}>{`${category}:`}</span>
</Truncate> </Truncate>
@@ -138,20 +134,17 @@ const renderSingleValueCategories = (singleValueCategories, skeleton) => {
// Renders any links found in the config where link_type is not "SUMMARY" // Renders any links found in the config where link_type is not "SUMMARY"
// If there are no links in the config, render the aboutURL // If there are no links in the config, render the aboutURL
const renderLinks = (projectLinks, aboutURL, skeleton) => { const renderLinks = (projectLinks, aboutURL) => {
if (!projectLinks && !aboutURL) return null; if (!projectLinks && !aboutURL) return null;
if (projectLinks) if (projectLinks)
return ( return (
<> <>
<H3 className={skeleton ? Classes.SKELETON : null}>Project Links</H3> <H3>Project Links</H3>
<UL> <UL>
{projectLinks.map((link) => { {projectLinks.map((link) => {
if (link.link_type === "SUMMARY") return null; if (link.link_type === "SUMMARY") return null;
return ( return (
<li <li key={link.link_name}>
key={link.link_name}
className={skeleton ? Classes.SKELETON : null}
>
<a href={link.link_url} target="_blank" rel="noopener"> <a href={link.link_url} target="_blank" rel="noopener">
{link.link_name} {link.link_name}
</a> </a>
@@ -164,14 +157,9 @@ const renderLinks = (projectLinks, aboutURL, skeleton) => {
return ( return (
<> <>
<H3 className={skeleton ? Classes.SKELETON : null}>More Info</H3> <H3>More Info</H3>
<p> <p>
<a <a href={aboutURL} target="_blank" rel="noopener">
className={skeleton ? Classes.SKELETON : null}
href={aboutURL}
target="_blank"
rel="noopener"
>
{aboutURL} {aboutURL}
</a> </a>
</p> </p>
@@ -179,24 +167,14 @@ const renderLinks = (projectLinks, aboutURL, skeleton) => {
); );
}; };
const NUM_CATEGORIES = 8;
// Generates arbitrary placeholder array for singleValueCategories skeleton shape
const singleValueCategoriesPlaceholder = Array.from(Array(NUM_CATEGORIES)).map(
(_, index) => {
return [index, index];
}
);
const InfoFormat = React.memo( const InfoFormat = React.memo(
({ ({
datasetTitle, datasetTitle,
singleValueCategories = new Map(singleValueCategoriesPlaceholder), singleValueCategories,
aboutURL = "thisisabouthtelengthofaurl", aboutURL,
dataPortalProps = {}, dataPortalProps = {},
skeleton = false,
}) => { }) => {
if (dataPortalProps.corpora_schema_version === "1.0.0") { if (dataPortalProps.version?.["corpora_schema_version"] !== "1.0.0") {
dataPortalProps = {}; dataPortalProps = {};
} }
const { const {
@@ -212,15 +190,13 @@ const InfoFormat = React.memo(
return ( return (
<div style={{ margin: 24, overflow: "auto" }}> <div style={{ margin: 24, overflow: "auto" }}>
<H1 className={skeleton ? Classes.SKELETON : null}> <H1>{title ?? datasetTitle}</H1>
{title ?? datasetTitle} {renderContributors(contributors, affiliations)}
</H1> {renderDOILink("DOI", doi)}
{renderContributors(contributors, affiliations, skeleton)} {renderDOILink("Preprint DOI", preprintDOI)}
{renderDOILink("DOI", doi, skeleton)} {renderOrganism(organism)}
{renderDOILink("Preprint DOI", preprintDOI, skeleton)} {renderSingleValueCategories(singleValueCategories)}
{renderOrganism(organism, skeleton)} {renderLinks(projectLinks, aboutURL)}
{renderSingleValueCategories(singleValueCategories, skeleton)}
{renderLinks(projectLinks, aboutURL, skeleton)}
</div> </div>
); );
} }