Skip to content

Commit

Permalink
enlarge column tabs + add tooltip to clinical attribute header
Browse files Browse the repository at this point in the history
  • Loading branch information
Rajat-Sirohi committed Jun 24, 2021
1 parent 103d827 commit 014f9c5
Show file tree
Hide file tree
Showing 54 changed files with 719 additions and 57 deletions.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
44 changes: 44 additions & 0 deletions src/pages/resultsView/ResultsViewPageStore.ts
Expand Up @@ -69,6 +69,7 @@ import GenomeNexusCache from 'shared/cache/GenomeNexusCache';
import GenomeNexusMutationAssessorCache from 'shared/cache/GenomeNexusMutationAssessorCache';
import CancerTypeCache from 'shared/cache/CancerTypeCache';
import MutationCountCache from 'shared/cache/MutationCountCache';
import ClinicalAttributeCache from 'shared/cache/ClinicalAttributeCache';
import DiscreteCNACache from 'shared/cache/DiscreteCNACache';
import PdbHeaderCache from 'shared/cache/PdbHeaderCache';
import {
Expand Down Expand Up @@ -1143,6 +1144,26 @@ export class ResultsViewPageStore {
},
});

// TODO: Should include all clinical attributes, not just server attributes
readonly mutationsTabClinicalAttributes = remoteData<ClinicalAttribute[]>({
await: () => [this.studyIds],
invoke: async () => {
const clinicalAttributes = await client.fetchClinicalAttributesUsingPOST(
{
studyIds: this.studyIds.result!,
}
);
const excludeList = ['CANCER_TYPE_DETAILED', 'MUTATION_COUNT'];

return _.uniqBy(
clinicalAttributes.filter(
x => !excludeList.includes(x.clinicalAttributeId)
),
x => x.clinicalAttributeId
);
},
});

readonly clinicalAttributeIdToClinicalAttribute = remoteData({
await: () => [this.clinicalAttributes],
invoke: () =>
Expand Down Expand Up @@ -1255,6 +1276,22 @@ export class ResultsViewPageStore {
},
});

readonly clinicalAttributeIdToAvailableFrequency = remoteData({
await: () => [
this.clinicalAttributeIdToAvailableSampleCount,
this.samples,
],
invoke: () => {
const numSamples = this.samples.result!.length;
return Promise.resolve(
_.mapValues(
this.clinicalAttributeIdToAvailableSampleCount.result!,
count => (100 * count) / numSamples
)
);
},
});

readonly cnSegments = remoteData<CopyNumberSeg[]>(
{
await: () => [this.filteredSamples],
Expand Down Expand Up @@ -3428,6 +3465,7 @@ export class ResultsViewPageStore {
this.oncoKbCancerGenes,
() => this.mutationsByGene.result![gene.hugoGeneSymbol] || [],
() => this.mutationCountCache,
() => this.clinicalAttributeCache,
() => this.genomeNexusCache,
() => this.genomeNexusMutationAssessorCache,
() => this.discreteCNACache,
Expand All @@ -3442,6 +3480,8 @@ export class ResultsViewPageStore {
this.uniqueSampleKeyToTumorType.result!,
this.generateGenomeNexusHgvsgUrl,
this.clinicalDataGroupedBySampleMap,
this.mutationsTabClinicalAttributes,
this.clinicalAttributeIdToAvailableFrequency,
this.genomeNexusClient,
this.genomeNexusInternalClient,
() => this.urlWrapper.query.mutations_transcript_id
Expand Down Expand Up @@ -5325,6 +5365,10 @@ export class ResultsViewPageStore {
return new MutationCountCache();
}

@cached @computed get clinicalAttributeCache() {
return new ClinicalAttributeCache();
}

@cached @computed get pdbHeaderCache() {
return new PdbHeaderCache();
}
Expand Down
263 changes: 263 additions & 0 deletions src/pages/resultsView/mutation/AddColumns.tsx
@@ -0,0 +1,263 @@
import * as React from 'react';
import * as _ from 'lodash';
import MobxPromise from 'mobxpromise';
import { observer } from 'mobx-react';
import { action, computed, makeObservable, observable } from 'mobx';
import { Checkbox } from 'react-bootstrap';
import { remoteData, getTextWidth } from 'cbioportal-frontend-commons';
import { ClinicalAttribute } from 'cbioportal-ts-api-client';
import { IColumnVisibilityControlsProps } from 'shared/components/columnVisibilityControls/ColumnVisibilityControls';
import { MSKTab, MSKTabs } from 'shared/components/MSKTabs/MSKTabs';
import CustomDropdown from 'shared/components/oncoprint/controls/CustomDropdown';
import AddChartByType from '../../studyView/addChartButton/addChartByType/AddChartByType';
import { ChartDataCountSet } from '../../studyView/StudyViewUtils';

export interface IAddColumnsProps extends IColumnVisibilityControlsProps {
clinicalAttributes: ClinicalAttribute[];
clinicalAttributeIdToAvailableFrequency: MobxPromise<{
[clinicalAttributeId: string]: number;
}>;
}

enum Tab {
MUTATIONS = 'Mutations',
CLINICAL = 'Clinical',
}

type Option = {
key: string;
label: string;
selected: boolean;
};

const MIN_DROPDOWN_WIDTH = 400;
const CONTAINER_PADDING_WIDTH = 20;
const TAB_PADDING_WIDTH = 14;
const COUNT_PADDING_WIDTH = 17;
@observer
export default class AddColumns extends React.Component<IAddColumnsProps, {}> {
@observable tabId: Tab = Tab.MUTATIONS;

constructor(props: IAddColumnsProps) {
super(props);
makeObservable(this);
}

@action.bound
private updateTabId(newId: Tab) {
this.tabId = newId;
}

@action.bound
private addAll(ids: string[], options: Option[]) {
if (this.props.onColumnToggled && options.length > 0) {
for (let option of options) {
if (!option.selected && ids.includes(option.key)) {
this.props.onColumnToggled(option.key);
}
}
}
}

@action.bound
private clearAll(ids: string[], options: Option[]) {
if (this.props.onColumnToggled && options.length > 0) {
for (let option of options) {
if (option.selected && ids.includes(option.key)) {
this.props.onColumnToggled(option.key);
}
}
}
}

@action.bound
private toggle(id: string) {
if (this.props.onColumnToggled) {
this.props.onColumnToggled(id);
}
}

readonly emptyPromise = remoteData({
invoke: () =>
new Promise<ChartDataCountSet>(() => {
return;
}),
});

@computed get clinicalAttributeIds(): Set<string> {
let ids: Set<string> = new Set();
this.props.clinicalAttributes.forEach(x =>
ids.add(x.clinicalAttributeId)
);
return ids
.add('CANCER_STUDY')
.add('CANCER_TYPE_DETAILED')
.add('MUTATION_COUNT');
}

@computed get mutationsOptions(): Option[] {
if (!this.props.columnVisibility) {
return [];
}

return this.props.columnVisibility
.filter(col => !this.clinicalAttributeIds.has(col.id))
.map(col => ({
key: col.id,
label: col.name,
selected: col.visible,
}));
}

@computed get mutationsTabContent() {
return (
<AddChartByType
options={this.mutationsOptions}
freqPromise={this.emptyPromise}
onAddAll={(ids: string[]) =>
this.addAll(ids, this.mutationsOptions)
}
onClearAll={(ids: string[]) =>
this.clearAll(ids, this.mutationsOptions)
}
onToggleOption={this.toggle}
optionsGivenInSortedOrder={true}
width={this.dropdownWidth}
excludeFrequency={true}
/>
);
}

@computed get clinicalOptions(): Option[] {
if (!this.props.columnVisibility) {
return [];
}

return this.props.columnVisibility
.filter(col => this.clinicalAttributeIds.has(col.id))
.map(col => ({
key: col.id,
label: col.name,
selected: col.visible,
}));
}

@computed get clinicalTabContent() {
return (
<AddChartByType
options={this.clinicalOptions}
freqPromise={this.props.clinicalAttributeIdToAvailableFrequency}
onAddAll={(ids: string[]) =>
this.addAll(ids, this.clinicalOptions)
}
onClearAll={(ids: string[]) =>
this.clearAll(ids, this.clinicalOptions)
}
onToggleOption={this.toggle}
optionsGivenInSortedOrder={false}
width={this.dropdownWidth}
/>
);
}

@computed get mutationsTabText() {
return (
<span style={{ marginTop: '3px', marginBottom: '3px' }}>
{Tab.MUTATIONS}
<span style={{ paddingLeft: 5 }}>
<span className="oncoprintDropdownCount">
{this.mutationsOptions.length}
</span>
</span>
</span>
);
}

@computed get clinicalTabText() {
return (
<span style={{ marginTop: '3px', marginBottom: '3px' }}>
{Tab.CLINICAL}
<span style={{ paddingLeft: 5 }}>
<span className="oncoprintDropdownCount">
{this.clinicalOptions.length}
</span>
</span>
</span>
);
}

private getTextPixel(text: string, fontSize: string) {
return Math.floor(getTextWidth(text, 'Helvetica Neue', fontSize));
}

@computed get dropdownWidth() {
let width = 2 * CONTAINER_PADDING_WIDTH;
const HEADER_FONT_SIZE = '14px';
const COUNT_FONT_SIZE = '11px';

const textWidth =
this.getTextPixel(Tab.CLINICAL, HEADER_FONT_SIZE) +
TAB_PADDING_WIDTH;
const countTextWidth =
this.getTextPixel(
this.clinicalOptions.length.toString(),
COUNT_FONT_SIZE
) + COUNT_PADDING_WIDTH;
width += textWidth + countTextWidth;

return Math.max(width, MIN_DROPDOWN_WIDTH);
}

render() {
const haveMutationsOptions = this.mutationsOptions.length > 0;
const haveClinicalOptions = this.clinicalOptions.length > 0;

return (
<div style={{ float: 'right' }}>
<CustomDropdown
bsStyle="default"
title="Columns"
id="addColumnsDropdown"
className={this.props.className}
styles={{ minWidth: MIN_DROPDOWN_WIDTH, width: 'auto' }}
buttonClassName="btn btn-default btn-sm"
>
<div
style={{
display: 'flex',
flexDirection: 'column',
}}
>
{(haveMutationsOptions || haveClinicalOptions) && (
<MSKTabs
activeTabId={this.tabId}
onTabClick={this.updateTabId}
unmountOnHide={false}
className="mainTabs mutationsTabAddColumnsDropdown"
>
{haveMutationsOptions && (
<MSKTab
key={0}
id={Tab.MUTATIONS}
linkText={this.mutationsTabText}
>
{this.mutationsTabContent}
</MSKTab>
)}
{haveClinicalOptions && (
<MSKTab
key={1}
id={Tab.CLINICAL}
linkText={this.clinicalTabText}
>
{this.clinicalTabContent}
</MSKTab>
)}
</MSKTabs>
)}
</div>
</CustomDropdown>
</div>
);
}
}
3 changes: 3 additions & 0 deletions src/pages/resultsView/mutation/Mutations.tsx
Expand Up @@ -226,6 +226,9 @@ export default class Mutations extends React.Component<
pubMedCache={this.props.store.pubMedCache}
cancerTypeCache={this.props.store.cancerTypeCache}
mutationCountCache={this.props.store.mutationCountCache}
clinicalAttributeCache={
this.props.store.clinicalAttributeCache
}
genomeNexusCache={this.props.store.genomeNexusCache}
genomeNexusMutationAssessorCache={
this.props.store.genomeNexusMutationAssessorCache
Expand Down

0 comments on commit 014f9c5

Please sign in to comment.