Skip to content

Commit

Permalink
feat: gene info cards (#298)
Browse files Browse the repository at this point in the history
* gene info button and starting api call, added tracking

* searching for schema

* add dispatches, implement api on hover

* wip

* created components for gene info and fetching ensembl ID from annoMatrix

* some typing & feature changes

* move ensembl id search from quickgene into gene component

* fixing rendering issue: moving ensembl ID searches to geneExpression and quickGene

* scatterplot v info card interaction

* added loading screen

* minimizing resets when gene info card is loading (on new click)

* typing for ts & lengthening right sidebar

* link to API endpoint, sds styling, additional typing

* hooked up api endpoint and unexpected result checking

* adding api base url for gene info

* info circle active state

* google search functionality

* interaction between scatterplot and gene info card

* removing log statements

* linting and removing hack.yaml

* fixing unit tests and more linting

* unit test fix?

* unit test actually fixed

* moving backend changes to new branch

* moving backend changes to new branch

* requested changes

* requested changes

* merge conflict

* removing merge conflict side effects

* feat: gene info card backend changes (#323)

* adding in backend changes

* unit tests and status codes

* requested change

* requested change

Co-authored-by: Siena Cizdziel <[email protected]>

* fixing styling issues

* chore: fix gene info to pass smoke tests (#319)

* feature id checks, reverting global rightsidebarwidth change, passing smoke tests

* chore: gene info icon and minor design changes (#320)

* icon changes

* requested design changes

Co-authored-by: Siena Cizdziel <[email protected]>

Co-authored-by: Siena Cizdziel <[email protected]>

* remove toast error

* fix: request gene info with name if no ensembl ID (#325)

* remove error checking for no feature id column

* functionality for datasets that don't have feature ids

* lint

* removing style changes

* hook up new api endpoint

* add gene param

Co-authored-by: Siena Cizdziel <[email protected]>

* remove http error

* requested changes

* requested changes

* min to minimized

* setstatus change

Co-authored-by: Siena Cizdziel <[email protected]>
Co-authored-by: Siena Cizdziel <[email protected]>
  • Loading branch information
3 people authored Jul 26, 2022
1 parent 9172e27 commit 8cf1341
Show file tree
Hide file tree
Showing 25 changed files with 2,863 additions and 6,879 deletions.
8,743 changes: 1,949 additions & 6,794 deletions client/package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion client/src/actions/geneset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ export const genesetAddGenes =
postUserErrorToast(
`${gene.geneSymbol} doesn't appear to be a valid gene name.`
);
} else acc.push(gene);
} else {
acc.push(gene);
}
return acc;
}, []);

Expand Down
24 changes: 23 additions & 1 deletion client/src/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
} from "../components/util/transientLocalStorage";
import { selectIsUserStateDirty } from "../selectors/global";
import AnnoMatrix from "../annoMatrix/annoMatrix";
import { LabelArray, LabelIndex } from "../util/dataframe";
import { DataframeValue, LabelArray, LabelIndex } from "../util/dataframe";
import { packDiffExPdu, DiffExMode, DiffExArguments } from "../util/diffexpdu";
import { track } from "../analytics";
import { EVENTS } from "../analytics/events";
Expand Down Expand Up @@ -139,6 +139,27 @@ async function genesetsFetchAndLoad(
}
}

interface GeneInfoAPI {
ncbi_url: string;
name: string;
synonyms: string[];
summary: string;
}
/**
* Fetch gene summary information
* @param geneID ensembl ID corresponding to gene to search
* @param gene human-readable name of gene
*/
async function fetchGeneInfo(
geneID: DataframeValue,
gene: string
): Promise<GeneInfoAPI | undefined> {
const response = await fetchJson<GeneInfoAPI>(
`geneinfo?geneID=${geneID}&gene=${gene}`
);
return response;
}

function prefetchEmbeddings(annoMatrix: AnnoMatrix) {
/*
prefetch requests for all embeddings
Expand Down Expand Up @@ -457,6 +478,7 @@ export default {
checkExplainNewTab,
navigateCheckUserState,
selectDataset,
fetchGeneInfo,
selectContinuousMetadataAction: selnActions.selectContinuousMetadataAction,
selectCategoricalMetadataAction: selnActions.selectCategoricalMetadataAction,
selectCategoricalAllMetadataAction:
Expand Down
1 change: 1 addition & 0 deletions client/src/analytics/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export enum EVENTS {
EXPLORER_MAXIMIZE_GENE_BUTTON_CLICKED = "EXPLORER_MAXIMIZE_GENE_BUTTON_CLICKED",
EXPLORER_CATEGORY_SELECT_BUTTON_CLICKED = "EXPLORER_CATEGORY_SELECT_BUTTON_CLICKED",
EXPLORER_CATEGORICAL_VALUE_SELECT_BUTTON_CLICKED = "EXPLORER_CATEGORICAL_VALUE_SELECT_BUTTON_CLICKED",
EXPLORER_GENE_INFO_BUTTON_CLICKED = "EXPLORER_GENE_INFO_BUTTON_CLICKED",
EXPLORER_ADD_GENE_AND_COLORBY = "EXPLORER_ADD_GENE_AND_COLORBY",
EXPLORER_ADD_GENE_AND_DISPLAY_SCATTERPLOT = "EXPLORER_ADD_GENE_AND_DISPLAY_SCATTERPLOT",
EXPLORER_DISPLAY_SCATTERPLOT = "EXPLORER_DISPLAY_SCATTERPLOT",
Expand Down
5 changes: 4 additions & 1 deletion client/src/annoMatrix/normalize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export function normalizeResponse(
field,
colLabel
) as AnnotationColumnSchema;

const isIndex = _isIndex(schema, field, colLabel);
const { type, writable } = colSchema;

Expand All @@ -68,7 +69,9 @@ export function normalizeResponse(
type === "string" ||
type === "categorical" ||
writable;
if (!isIndex && isEnumType) {
// If "feature_id" column exists, should not normalize
// (gene info cards require ensembl ID of any gene, and normalizing overwrites some of those IDs)
if (!isIndex && colLabel !== "feature_id" && isEnumType) {
response = normalizeCategorical(response, colLabel, colSchema);
}
}
Expand Down
132 changes: 83 additions & 49 deletions client/src/components/geneExpression/gene.tsx
Original file line number Diff line number Diff line change
@@ -1,68 +1,68 @@
import React from "react";
import { connect } from "react-redux";

import { Button, Icon } from "@blueprintjs/core";
import { connect } from "react-redux";
import { Icon as InfoCircle } from "czifui";
import Truncate from "../util/truncate";
import HistogramBrush from "../brushableHistogram";
import { RootState } from "../../reducers";

import actions from "../../actions";

import { track } from "../../analytics";
import { EVENTS } from "../../analytics/events";
import { DataframeValue } from "../../util/dataframe";

const MINI_HISTOGRAM_WIDTH = 110;

// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
type State = any;
type State = RootState;

interface Props {
gene: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- FIXME
quickGene: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- FIXME
removeGene: any;
geneId: DataframeValue;
}

// @ts-expect-error ts-migrate(1238) FIXME: Unable to resolve signature of class decorator whe... Remove this comment to see the full error message
@connect((state, ownProps) => {
// @ts-expect-error ts-migrate(2339) FIXME: Property 'gene' does not exist on type '{}'.
@connect((state: RootState, ownProps: Props) => {
const { gene } = ownProps;

return {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
isColorAccessor:
(state as any).colors.colorAccessor === gene &&
(state as any).colors.colorMode !== "color by categorical metadata",
isScatterplotXXaccessor:
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(state as any).controls.scatterplotXXaccessor === gene,
isScatterplotYYaccessor:
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(state as any).controls.scatterplotYYaccessor === gene,
state.colors.colorAccessor === gene &&
state.colors.colorMode !== "color by categorical metadata",
isScatterplotXXaccessor: state.controls.scatterplotXXaccessor === gene,
isScatterplotYYaccessor: state.controls.scatterplotYYaccessor === gene,
isGeneInfo: state.controls.gene === gene && state.controls.geneIsOpen,
};
})
// eslint-disable-next-line @typescript-eslint/ban-types --- FIXME: disabled temporarily on migrate to TS.
class Gene extends React.Component<{}, State> {
// eslint-disable-next-line @typescript-eslint/ban-types --- FIXME: disabled temporarily on migrate to TS.
constructor(props: {}) {
class Gene extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
geneIsExpanded: false,
};
}

// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
onColorChangeClick = () => {
onColorChangeClick = (): void => {
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dispatch' does not exist on type 'Readon... Remove this comment to see the full error message
const { dispatch, gene } = this.props;
track(EVENTS.EXPLORER_COLORBY_GENE_BUTTON_CLICKED);
dispatch(actions.requestSingleGeneExpressionCountsForColoringPOST(gene));
};

// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
handleGeneExpandClick = () => {
handleGeneExpandClick = (): void => {
track(EVENTS.EXPLORER_MAXIMIZE_GENE_BUTTON_CLICKED);

const { geneIsExpanded } = this.state;
this.setState({ geneIsExpanded: !geneIsExpanded });
};

// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
handleSetGeneAsScatterplotX = () => {
handleSetGeneAsScatterplotX = (): void => {
track(EVENTS.EXPLORER_PLOT_X_BUTTON_CLICKED);

// @ts-expect-error ts-migrate(2339) FIXME: Property 'dispatch' does not exist on type 'Readon... Remove this comment to see the full error message
const { dispatch, gene } = this.props;
dispatch({
Expand All @@ -71,10 +71,8 @@ class Gene extends React.Component<{}, State> {
});
};

// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
handleSetGeneAsScatterplotY = () => {
handleSetGeneAsScatterplotY = (): void => {
track(EVENTS.EXPLORER_PLOT_Y_BUTTON_CLICKED);

// @ts-expect-error ts-migrate(2339) FIXME: Property 'dispatch' does not exist on type 'Readon... Remove this comment to see the full error message
const { dispatch, gene } = this.props;
dispatch({
Expand All @@ -83,29 +81,59 @@ class Gene extends React.Component<{}, State> {
});
};

// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
handleDeleteGeneFromSet = () => {
handleDeleteGeneFromSet = (): void => {
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dispatch' does not exist on type 'Readon... Remove this comment to see the full error message
const { dispatch, gene, geneset } = this.props;
dispatch(actions.genesetDeleteGenes(geneset, [gene]));
};

// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
render() {
handleDisplayGeneInfo = async (): Promise<void> => {
track(EVENTS.EXPLORER_GENE_INFO_BUTTON_CLICKED);
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dispatch' does not exist on type 'Readon... Remove this comment to see the full error message
const { dispatch, gene, geneId } = this.props;
dispatch({
type: "load gene info",
gene,
});

const info = await actions.fetchGeneInfo(geneId, gene);
if (!info) {
dispatch({
type: "open gene info",
gene,
url: "",
name: "",
synonyms: [],
summary: "",
infoError: "fetch gene info failed",
});
return;
}
dispatch({
type: "open gene info",
gene,
url: info.ncbi_url,
name: info.name,
synonyms: info.synonyms,
summary: info.summary,
infoError: null,
});
};

render(): JSX.Element {
const {
// @ts-expect-error ts-migrate(2339) FIXME: Property 'gene' does not exist on type 'Readonly<{... Remove this comment to see the full error message
gene,
// @ts-expect-error ts-migrate(2339) FIXME: Property 'geneDescription' does not exist on type ... Remove this comment to see the full error message
geneDescription,
// @ts-expect-error ts-migrate(2339) FIXME: Property 'isColorAccessor' does not exist on type ... Remove this comment to see the full error message
isColorAccessor,
// @ts-expect-error ts-migrate(2339) FIXME: Property 'isScatterplotXXaccessor' does not exist ... Remove this comment to see the full error message
// @ts-expect-error ts-migrate(2339) FIXME: Property 'isScatterplotXXaccessor' does not exist on type ... Remove this comment to see the full error message
isScatterplotXXaccessor,
// @ts-expect-error ts-migrate(2339) FIXME: Property 'isScatterplotYYaccessor' does not exist ... Remove this comment to see the full error message
// @ts-expect-error ts-migrate(2339) FIXME: Property 'isScatterplotYYaccessor' does not exist on type ... Remove this comment to see the full error message
isScatterplotYYaccessor,
// @ts-expect-error ts-migrate(2339) FIXME: Property 'quickGene' does not exist on type 'Reado... Remove this comment to see the full error message
// @ts-expect-error ts-migrate(2339) FIXME: Property 'isGeneInfo' does not exist on type ... Remove this comment to see the full error message
isGeneInfo,
quickGene,
// @ts-expect-error ts-migrate(2339) FIXME: Property 'removeGene' does not exist on type 'Read... Remove this comment to see the full error message
removeGene,
} = this.props;
const { geneIsExpanded } = this.state;
Expand Down Expand Up @@ -138,18 +166,6 @@ class Gene extends React.Component<{}, State> {
}}
>
<div>
{!quickGene && (
<Icon
icon="drag-handle-horizontal"
iconSize={12}
style={{
marginRight: 7,
cursor: "grab",
position: "relative",
top: -1,
}}
/>
)}
<Truncate
tooltipAddendum={geneDescription && `: ${geneDescription}`}
>
Expand All @@ -164,6 +180,24 @@ class Gene extends React.Component<{}, State> {
</span>
</Truncate>
</div>
<div style={{ flexShrink: 0 }}>
<Button
minimal
small
data-testid={`get-info-${gene}`}
onClick={this.handleDisplayGeneInfo}
active={isGeneInfo}
intent={isGeneInfo ? "primary" : "none"}
>
<div style={{ filter: "grayscale(100%)" }}>
<InfoCircle
sdsIcon="infoCircle"
sdsSize="s"
sdsType="static"
/>
</div>
</Button>
</div>
{!geneIsExpanded ? (
<HistogramBrush
// @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call.
Expand Down
Loading

0 comments on commit 8cf1341

Please sign in to comment.