Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

create react-variant-view package #3459

Merged
merged 3 commits into from
Oct 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/react-variant-view/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# react-variant-view

## License

© [cBioPortal](https://github.com/cBioPortal)
70 changes: 70 additions & 0 deletions packages/react-variant-view/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
{
"name": "react-variant-view",
"version": "0.1.0-beta.0",
"description": "cBioPortal Variant Viewer",
"main": "dist/index.js",
"module": "dist/index.es.js",
"jsnext:main": "dist/index.es.js",
"typings": "dist/index.d.ts",
"styles": "dist/styles.css",
"engines": {
"node": ">=8.12.0",
"yarn": ">=1.22.4"
},
"files": [
"dist"
],
"author": "cBioPortal",
"license": "AGPL-3.0-or-later",
"repository": "cBioPortal/cbioportal-frontend",
"scripts": {
"build": "yarn run tcm && cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=2048 yarn run rollup",
"start": "yarn run watch",
"watch": "concurrently \"yarn run tcm:watch\" \"yarn run rollup:watch\"",
"watchSSL": "yarn run watch",
"rollup": "rollup -c rollup.config.ts",
"rollup:watch": "yarn run rollup --watch",
"tcm": "tcm -p src/**/*.module.scss",
"tcm:watch": "yarn run tcm --watch",
"prepare": "yarn run build",
"test": "cross-env CI=1 react-scripts-ts test --env=jsdom",
"test:watch": "yarn run test --watch"
},
"peerDependencies": {
"mobx": "^3.0.0 || ^4.0.0 || ^5.0.0",
"mobx-react": "^4.0.0 || ^5.0.0",
"prop-types": "^15.5.4",
"react": "^15.0.0 || ^16.0.0",
"react-dom": "^15.0.0 || ^16.0.0",
"bootstrap": "^3.0.0 || ^4.0.0"
},
"dependencies": {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to make sure that these dependencies are complete. This package may still work inside the monorepo even if some dependencies are not listed here. But when we include the package in another project we may get missing dependency errors. For now we can test it with signal to see if it works fine (after publishing the beta version to npm).

"autobind-decorator": "^2.1.0",
"cbioportal-frontend-commons": "^0.3.9",
"cbioportal-utils": "^0.2.7",
"classnames": "^2.2.5",
"font-awesome": "^4.7.0",
"genome-nexus-ts-api-client": "^1.1.7",
"jquery": "^3.2.1",
"lodash": "^4.17.11",
"mobxpromise": "github:cbioportal/mobxpromise#v1.0.2",
"oncokb-styles": "~0.1.2",
"oncokb-ts-api-client": "^1.1.2",
"react-bootstrap": "^0.31.5",
"react-collapse": "4.0.3",
"react-if": "^2.1.0",
"react-motion": "^0.5.2",
"react-mutation-mapper": "^0.6.10",
"react-rangeslider": "^2.2.0",
"react-select": "^3.0.4",
"react-spinkit": "^3.0.0",
"react-table": "^6.10.0",
"seamless-immutable": "^7.1.4",
"superagent": "^3.8.3",
"typescript": "4.0.3"
},
"devDependencies": {
"@testing-library/react": "^9.3.2",
"react-scripts-ts": "^3.1.0"
}
}
9 changes: 9 additions & 0 deletions packages/react-variant-view/rollup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import getRollupOptions from '../config/rollup.config';
import pkg from './package.json';

export default getRollupOptions(
'src/index.tsx',
pkg.main,
pkg.module,
pkg.styles
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { observer } from 'mobx-react';
import * as React from 'react';

import Oncokb from './Oncokb';
import { IndicatorQueryResp } from 'oncokb-ts-api-client';

interface IBiologicalFunctionProps {
oncokb: IndicatorQueryResp | undefined;
isCanonicalTranscriptSelected: boolean;
}

@observer
class BiologicalFunction extends React.Component<IBiologicalFunctionProps> {
public render() {
return (
<Oncokb
oncokb={this.props.oncokb}
isCanonicalTranscriptSelected={
this.props.isCanonicalTranscriptSelected
}
/>
);
}
}

export default BiologicalFunction;
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import { observer } from 'mobx-react';
import * as React from 'react';

import { DefaultTooltip } from 'cbioportal-frontend-commons';
import { IndicatorQueryResp } from 'oncokb-ts-api-client';
import featureTableStyle from '../featureTable/FeatureTable.module.scss';

interface IOncokbProps {
oncokb: IndicatorQueryResp | undefined;
isCanonicalTranscriptSelected: boolean;
}

export enum ONCOGENICITY {
ONCOGENIC = 'Oncogenic',
LIKELY_ONCOGENIC = 'Likely Oncogenic',
PREDICTED_ONCOGENIC = 'Predicted Oncogenic',
NEUTRAL = 'Neutral',
LIKELY_NEUTRAL = 'Likely Neutral',
INCONCLUSIVE = 'Inconclusive',
VUS = 'vus',
UNKNOWN = 'Unknown',
}

export const ONCOGENICITY_CLASS_NAMES: { [oncogenic: string]: string } = {
[ONCOGENICITY.ONCOGENIC]: 'oncogenic',
[ONCOGENICITY.LIKELY_ONCOGENIC]: 'oncogenic',
[ONCOGENICITY.PREDICTED_ONCOGENIC]: 'oncogenic',
[ONCOGENICITY.NEUTRAL]: 'neutral',
[ONCOGENICITY.LIKELY_NEUTRAL]: 'neutral',
[ONCOGENICITY.INCONCLUSIVE]: 'inconclusive',
[ONCOGENICITY.VUS]: 'vus',
[ONCOGENICITY.UNKNOWN]: 'unknown',
};

export enum MUTATION_EFFECT {
GAIN_OF_FUNCTION = 'Gain-of-function',
LIKELY_GAIN_OF_FUNCTION = 'Likely Gain-of-function',
LOSS_OF_FUNCTION = 'Loss-of-function',
LIKELY_LOSS_OF_FUNCTION = 'Likely Loss-of-function',
SWITCH_OF_FUNCTION = 'Switch-of-function',
LIKELY_SWITCH_OF_FUNCTION = 'Likely Switch-of-function',
NEUTRAL = 'Neutral',
LIKELY_NEUTRAL = 'Likely Neutral',
INCONCLUSIVE = 'Inconclusive',
UNKNOWN = 'Unknown',
}

export const MUTATION_EFFECT_CLASS_NAMES: {
[mutationEffect: string]: string;
} = {
[MUTATION_EFFECT.GAIN_OF_FUNCTION]: 'gain',
[MUTATION_EFFECT.LIKELY_GAIN_OF_FUNCTION]: 'gain',
[MUTATION_EFFECT.LOSS_OF_FUNCTION]: 'loss',
[MUTATION_EFFECT.LIKELY_LOSS_OF_FUNCTION]: 'loss',
[MUTATION_EFFECT.SWITCH_OF_FUNCTION]: 'switch',
[MUTATION_EFFECT.LIKELY_SWITCH_OF_FUNCTION]: 'switch',
[MUTATION_EFFECT.NEUTRAL]: 'neutral',
[MUTATION_EFFECT.LIKELY_NEUTRAL]: 'neutral',
[MUTATION_EFFECT.INCONCLUSIVE]: 'inconclusive',
[MUTATION_EFFECT.UNKNOWN]: 'unknown',
};

export const ONCOKB_URL = 'https://www.oncokb.org';

@observer
export default class Oncokb extends React.Component<IOncokbProps> {
public oncogenicity(oncokb: IndicatorQueryResp) {
if (oncokb.oncogenic && oncokb.oncogenic !== '') {
return oncokb.oncogenic;
} else {
return null;
}
}
public oncokbTooltip(oncokbUrl: string) {
return (
<DefaultTooltip
placement="top"
overlay={
<span>
<a
href={oncokbUrl}
target="_blank"
rel="noopener noreferrer"
>
OncoKB
</a>{' '}
is a precision oncology knowledge base and contains
<br />
information about the effects and treatment implications
<br />
of specific cancer gene alterations. <br />
</span>
}
>
<a href={oncokbUrl} target="_blank" rel="noopener noreferrer">
OncoKB&nbsp;
<i className="fas fa-external-link-alt" />
{!this.props.isCanonicalTranscriptSelected && (
<span> *</span>
)}
</a>
</DefaultTooltip>
);
}

public mutationEffect(oncokb: IndicatorQueryResp) {
if (oncokb.mutationEffect && oncokb.mutationEffect.knownEffect !== '') {
return oncokb.mutationEffect.knownEffect;
} else {
return null;
}
}

public render() {
const oncokbUrl = generateOncokbLink(ONCOKB_URL, this.props.oncokb);
return this.props.oncokb ? (
<div className={featureTableStyle['functional-group']}>
<div className={featureTableStyle['data-source']}>
{this.oncokbTooltip(oncokbUrl)}
</div>
<div className={featureTableStyle['data-with-link']}>
{this.biologicalFunctionData(
this.mutationEffect(this.props.oncokb),
this.oncogenicity(this.props.oncokb),
oncokbUrl
)}
</div>
</div>
) : (
<div className={featureTableStyle['functional-group']}>
<div className={featureTableStyle['data-source']}>
{this.oncokbTooltip(oncokbUrl)}
</div>
<div>N/A</div>
</div>
);
}

private biologicalFunctionData(
mutationEffect: string | null,
oncogenicity: string | null,
oncokbUrl: string
) {
let biologicalFunctionData: string | null = null;
if (mutationEffect && oncogenicity) {
biologicalFunctionData = `${mutationEffect}, ${oncogenicity}`;
} else if (mutationEffect) {
biologicalFunctionData = oncogenicity;
} else if (oncogenicity) {
biologicalFunctionData = mutationEffect;
} else {
biologicalFunctionData = 'N/A';
}
return (
<a href={oncokbUrl} target="_blank" rel="noopener noreferrer">
<p>{biologicalFunctionData}</p>
</a>
);
}
}

export function generateOncokbLink(
link: string,
oncokb: IndicatorQueryResp | undefined
): string {
let url = link;
const hugoSymbol = oncokb && oncokb.query && oncokb.query.hugoSymbol;
const alteration = oncokb && oncokb.query && oncokb.query.alteration;
if (hugoSymbol && alteration) {
url = `${url}/gene/${hugoSymbol}/${alteration}`;
}
return url;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
.group-name {
background-color: #f2f2f2;
font-size: 0.875rem;
font-weight: bold;
margin-top: 3px;
margin-bottom: 3px;
padding-bottom: 4px;
padding-top: 4px;
max-width: 27%;
display: flex;
align-items: center;
}

.group-content {
display: flex;
align-items: center;
}

.data-content {
margin-top: 3px;
margin-bottom: 3px;
padding-bottom: 4px;
padding-top: 4px;
}

.data-group-gap {
display: block;
}

.no-data {
font-size: 1rem;
margin-left: 15px;
color: black;
}

/* gnomad */
.gnomad {
font-size: 1rem;
font-weight: bold;
}

/* dbsnp */
.dbsnp {
font-size: 1rem;
font-weight: bold;
}

/* functional prediction */
.functional-prediction-data {
display: flex;
align-items: center;
font-size: 1rem;
}

.functional-prediction-no-data {
font-size: 1rem;
font-weight: bold;
}

// oncokb
.oncokb {
font-size: 1rem;
font-weight: bold;
}

// redesign from here. remove some of classes above after complete
.feature-table {
padding-top: 10px;

p {
margin-bottom: 5px;
}
p:last-child {
margin-bottom: 0;
}
}

.data-source {
min-width: 200px;
}

.functional-group {
display: flex;
}

.data-with-link {
a {
color: black;
&:hover,
&:focus,
&:active,
&:visited {
color: black;
}
}
}