From c3551be3a97af2978e2d5b35e4ed4931cdcf7fdb Mon Sep 17 00:00:00 2001 From: Bryan Lai Date: Fri, 18 Aug 2023 16:18:24 -0400 Subject: [PATCH] add color config for all alterations --- src/pages/resultsView/ResultsViewPageStore.ts | 85 +++++++++----- .../oncoprint/ResultsViewOncoprint.tsx | 81 +++++++------ .../oncoprint/controls/OncoprintColors.tsx | 110 +++++++++++------- .../oncoprint/controls/OncoprintControls.tsx | 91 +++++---------- src/shared/lib/Colors.ts | 17 +++ 5 files changed, 214 insertions(+), 170 deletions(-) diff --git a/src/pages/resultsView/ResultsViewPageStore.ts b/src/pages/resultsView/ResultsViewPageStore.ts index d0d1892ff80..6d2ec13c187 100644 --- a/src/pages/resultsView/ResultsViewPageStore.ts +++ b/src/pages/resultsView/ResultsViewPageStore.ts @@ -304,7 +304,7 @@ import { ONCOKB_DEFAULT_INFO, USE_DEFAULT_PUBLIC_INSTANCE_FOR_ONCOKB, } from 'react-mutation-mapper'; -import { RGBAColor } from 'oncoprintjs'; +import { IGeneticAlterationRuleSetParams, RGBAColor } from 'oncoprintjs'; type Optional = | { isApplicable: true; value: T } @@ -575,7 +575,9 @@ export class ResultsViewPageStore extends AnalysisStore @observable queryFormVisible: boolean = false; @observable userAlterationColors: { - [alteration: string]: string | undefined; + [alteration: string]: { + [type: string]: RGBAColor | undefined; + }; } = {}; private _selectedComparisonGroupsWarningSigns = observable.map< @@ -599,45 +601,64 @@ export class ResultsViewPageStore extends AnalysisStore @action.bound public onAlterationColorChange( alteration: string, - color: string | undefined + type: string, + color: RGBAColor ) { - if (color == undefined && this.userAlterationColors[alteration]) { - delete this.userAlterationColors[alteration]; - } else this.userAlterationColors[alteration] = color; + this.userAlterationColors[alteration][type] = color; } - @action public showAlterationWarningSign( - alteration: string, - markedValue: boolean - ) { - this._selectedComparisonGroupsWarningSigns.set(alteration, markedValue); - } - - public flagDuplicateColorsForAlterations( - alteration: string, - color: string | undefined + @action.bound + public setDefaultUserAlterationColors( + rule: IGeneticAlterationRuleSetParams ) { - let colors: { [color: string]: number } = {}; - - Object.keys(this.userAlterationColors).forEach( - (a: string, i: number) => { - let alterationColor = - a === alteration ? color : this.userAlterationColors[a]; - if ( - alterationColor == undefined || - colors[alterationColor] == undefined - ) { - if (alterationColor != undefined) - colors[alterationColor] = 1; - this.showAlterationWarningSign(alteration, false); + for (let alteration in rule.rule_params.conditional) { + this.userAlterationColors[alteration] = {}; + for (let type in rule.rule_params.conditional[alteration]) { + if (alteration === 'disp_mrna') { + this.userAlterationColors[alteration][type] = rule + .rule_params.conditional[alteration][type].shapes[0] + .stroke as RGBAColor; } else { - colors[alterationColor] = colors[alterationColor] + 1; - this.showAlterationWarningSign(alteration, true); + this.userAlterationColors[alteration][type] = rule + .rule_params.conditional[alteration][type].shapes[0] + .fill as RGBAColor; } } - ); + } } + // @action public showAlterationWarningSign( + // alteration: string, + // markedValue: boolean + // ) { + // this._selectedComparisonGroupsWarningSigns.set(alteration, markedValue); + // } + + // public flagDuplicateColorsForAlterations( + // alteration: string, + // color: string | undefined + // ) { + // let colors: { [color: string]: number } = {}; + + // Object.keys(this.userAlterationColors).forEach( + // (a: string, i: number) => { + // let alterationColor = + // a === alteration ? color : this.userAlterationColors[a]; + // if ( + // alterationColor == undefined || + // colors[alterationColor] == undefined + // ) { + // if (alterationColor != undefined) + // colors[alterationColor] = 1; + // this.showAlterationWarningSign(alteration, false); + // } else { + // colors[alterationColor] = colors[alterationColor] + 1; + // this.showAlterationWarningSign(alteration, true); + // } + // } + // ); + // } + public isAlterationMarkedWithWarningSign(alteration: string): boolean { return !!this._selectedComparisonGroupsWarningSigns.get(alteration); } diff --git a/src/shared/components/oncoprint/ResultsViewOncoprint.tsx b/src/shared/components/oncoprint/ResultsViewOncoprint.tsx index f24f4f33bb1..e43cbffbb70 100644 --- a/src/shared/components/oncoprint/ResultsViewOncoprint.tsx +++ b/src/shared/components/oncoprint/ResultsViewOncoprint.tsx @@ -96,7 +96,6 @@ import '../../../globalStyles/oncoprintStyles.scss'; import { GenericAssayTrackInfo } from 'pages/studyView/addChartButton/genericAssaySelection/GenericAssaySelection'; import { toDirectionString } from './SortUtils'; import { RestoreClinicalTracksMenu } from 'pages/resultsView/oncoprint/RestoreClinicalTracksMenu'; -import { hexToRGBA } from 'shared/lib/Colors'; interface IResultsViewOncoprintProps { divId: string; @@ -423,6 +422,7 @@ export default class ResultsViewOncoprint extends React.Component< super(props); makeObservable(this); + this.props.store.setDefaultUserAlterationColors(this.rule); this.showOqlInLabels = props.store.queryContainsOql; (window as any).resultsViewOncoprint = this; @@ -1823,50 +1823,65 @@ export default class ResultsViewOncoprint extends React.Component< } @action.bound - public setRules(alteration: string, color: RGBAColor | undefined) { + public setRules( + alteration: string, + type: string, + color: RGBAColor | undefined + ) { if (color == undefined) { this.rule = getGeneticTrackRuleSetParams( this.distinguishMutationType, this.distinguishDrivers, this.distinguishGermlineMutations ); - for (alteration in this.props.store.userAlterationColors) { - if ( - this.props.store.userAlterationColors[alteration] !== - undefined - ) { - this.rule.rule_params.conditional.disp_mut[ - alteration - ].shapes[0].fill = hexToRGBA( - this.props.store.userAlterationColors[alteration]! - ); + for (let a in this.props.store.userAlterationColors) { + for (let t in this.props.store.userAlterationColors[a]) { + if (a === 'disp_mrna') { + if (a !== alteration && t !== type) { + this.rule.rule_params.conditional[a][ + t + ].shapes[0].stroke = this.props.store.userAlterationColors[ + a + ][t]; + } else { + this.props.store.onAlterationColorChange( + alteration, + type, + this.rule.rule_params.conditional[a][t] + .shapes[0].stroke as RGBAColor + ); + } + } else { + if (a !== alteration || t !== type) { + this.rule.rule_params.conditional[a][ + t + ].shapes[0].fill = this.props.store.userAlterationColors[ + a + ][t]; + } else { + this.props.store.onAlterationColorChange( + alteration, + type, + this.rule.rule_params.conditional[a][t] + .shapes[0].fill as RGBAColor + ); + } + } } } } else { - // const rules = getGeneticTrackRuleSetParams(this.distinguishMutationType, - // this.distinguishDrivers, - // this.distinguishGermlineMutations - // ); - // if (rules.rule_params.conditional.disp_mut.missense && this.test) { - // rules.rule_params.conditional.disp_mut.missense.shapes[0].fill = [0, 128, 0, 1] - // } - // else if (rules.rule_params.conditional.disp_mut.missense && !this.test) { - // rules.rule_params.conditional.disp_mut.missense.shapes[0].fill = [83, 212, 0, 1] - // } - if (this.rule.rule_params.conditional.disp_mut[alteration]) { - this.rule.rule_params.conditional.disp_mut[ - alteration + if (alteration === 'disp_mrna') { + this.rule.rule_params.conditional[alteration][ + type + ].shapes[0].stroke = color; + } else { + this.rule.rule_params.conditional[alteration][ + type ].shapes[0].fill = color; } - // else if (rules.rule_params.conditional.disp_mut['splice,missense,inframe,trunc,promoter,other'] && this.test) { - // rules.rule_params.conditional.disp_mut['splice,missense,inframe,trunc,promoter,other'].shapes[0].fill = [0, 128, 0, 1] - // } - // else if (rules.rule_params.conditional.disp_mut['splice,missense,inframe,trunc,promoter,other'] && !this.test) { - // rules.rule_params.conditional.disp_mut['splice,missense,inframe,trunc,promoter,other'].shapes[0].fill = [83, 212, 0, 1] - // } - // this.rule = rules; + this.props.store.onAlterationColorChange(alteration, type, color); } - console.log(this.rule); + // console.log(this.rule); } public render() { diff --git a/src/shared/components/oncoprint/controls/OncoprintColors.tsx b/src/shared/components/oncoprint/controls/OncoprintColors.tsx index 8cd23a2c4eb..0d0a5bd1ec6 100644 --- a/src/shared/components/oncoprint/controls/OncoprintColors.tsx +++ b/src/shared/components/oncoprint/controls/OncoprintColors.tsx @@ -12,6 +12,7 @@ import { CLI_NO_COLOR, CLI_YES_COLOR, DARK_GREY, + rgbaToHex, } from 'shared/lib/Colors'; import { COLORS } from 'pages/studyView/StudyViewUtils'; import { ResultsViewPageStore } from 'pages/resultsView/ResultsViewPageStore'; @@ -23,29 +24,65 @@ import { RGBAColor } from 'oncoprintjs'; export interface IGroupCheckboxProps { alteration: string; + type: string; store?: ResultsViewPageStore; handlers: IOncoprintControlsHandlers; state: IOncoprintControlsState; - setRules?: (alteration: string, color: RGBAColor | undefined) => void; - color?: string; + setRules?: ( + alteration: string, + type: string, + color: RGBAColor | undefined + ) => void; + color: RGBAColor; markedWithWarningSign: boolean; } const COLOR_UNDEFINED = '#FFFFFF'; -const alterationToLabel: { [alteration: string]: string } = { - missense: 'Missense Mutation (unknown significance)', - missense_rec: 'Missense Mutation (putative driver)', - trunc: 'Truncating Mutation (unknown significance)', - trunc_rec: 'Truncating Mutation (putative driver)', - inframe: 'Inframe Mutation (unknown significance)', - inframe_rec: 'Inframe Mutation (putative driver)', - splice: 'Splice Mutation (unknown significance)', - splice_rec: 'Splice Mutation (putative driver)', - promoter: 'Promoter Mutation (unknown significance)', - promoter_rec: 'Promoter Mutation (putative driver)', - other: 'Other Mutation (unknown significance)', - other_rec: 'Other Mutation (putative driver)', +export const alterationToTypeToLabel: { + [alteration: string]: { [type: string]: string }; +} = { + // disp_cna + disp_cna: { + 'amp_rec,amp': 'Amplification', + 'gain_rec,gain': 'Gain', + 'hetloss_rec,hetloss': 'Shallow Deletion', + 'homdel_rec,homdel': 'Deep Deletion', + }, + // disp_germ + disp_germ: { + true: 'Germline Mutation', + }, + // disp_mrna + disp_mrna: { + high: 'mRNA High', + low: 'mRNA Low', + }, + // disp_mut + disp_mut: { + missense: 'Missense Mutation (unknown significance)', + missense_rec: 'Missense Mutation (putative driver)', + trunc: 'Truncating Mutation (unknown significance)', + trunc_rec: 'Truncating Mutation (putative driver)', + inframe: 'Inframe Mutation (unknown significance)', + inframe_rec: 'Inframe Mutation (putative driver)', + splice: 'Splice Mutation (unknown significance)', + splice_rec: 'Splice Mutation (putative driver)', + promoter: 'Promoter Mutation (unknown significance)', + promoter_rec: 'Promoter Mutation (putative driver)', + other: 'Other Mutation (unknown significance)', + other_rec: 'Other Mutation (putative driver)', + }, + // disp_prot + disp_prot: { + high: 'Protein High', + low: 'Protein Low', + }, + // disp_structuralVariant + disp_structuralVariant: { + sv: 'Structural Variant (unknown significance)', + sv_rec: 'Structural Variant (putative driver)', + }, }; @observer @@ -61,38 +98,24 @@ export default class OncoprintColors extends React.Component< @action.bound handleChangeComplete = (color: any, event: any) => { // if same color is select, unselect it (go back to no color) - if (color.hex === this.props.color) { - this.props.store?.onAlterationColorChange( - this.props.alteration, - undefined - ); + if (color.hex === rgbaToHex(this.props.color)) { this.props.setRules && - this.props.setRules(this.props.alteration, undefined); - this.props.store!.flagDuplicateColorsForAlterations( - this.props.alteration, - undefined - ); + this.props.setRules( + this.props.alteration, + this.props.type, + undefined + ); } else { - this.props.store?.onAlterationColorChange( - this.props.alteration, - color.hex - ); this.props.setRules && - this.props.setRules(this.props.alteration, [ + this.props.setRules(this.props.alteration, this.props.type, [ color.rgb.r, color.rgb.g, color.rgb.b, color.rgb.a, ]); - this.props.store!.flagDuplicateColorsForAlterations( - this.props.alteration, - color.hex - ); } this.props.handlers.onSelectTest && this.props.handlers.onSelectTest(!this.props.state.test!); - this.props.handlers.onSetRule && - this.props.handlers.onSetRule(this.props.state.rule!); }; @computed get colorList() { @@ -119,7 +142,7 @@ export default class OncoprintColors extends React.Component< circleSize={20} circleSpacing={3} onChangeComplete={this.handleChangeComplete} - color={this.props.color} + color={rgbaToHex(this.props.color)} width="140px" /> @@ -129,7 +152,11 @@ export default class OncoprintColors extends React.Component< render() { return (
- {alterationToLabel[this.props.alteration]} + { + alterationToTypeToLabel[this.props.alteration][ + this.props.type + ] + } diff --git a/src/shared/components/oncoprint/controls/OncoprintControls.tsx b/src/shared/components/oncoprint/controls/OncoprintControls.tsx index d31bb002a55..8703a996a4e 100644 --- a/src/shared/components/oncoprint/controls/OncoprintControls.tsx +++ b/src/shared/components/oncoprint/controls/OncoprintControls.tsx @@ -47,7 +47,7 @@ import { } from 'shared/components/oncoprint/Oncoprint'; import { getServerConfig } from 'config/config'; import { IGeneticAlterationRuleSetParams, RGBAColor } from 'oncoprintjs'; -import OncoprintColors from './OncoprintColors'; +import OncoprintColors, { alterationToTypeToLabel } from './OncoprintColors'; export interface IOncoprintControlsHandlers extends IDriverAnnotationControlsHandlers { @@ -144,7 +144,11 @@ export interface IOncoprintControlsProps { selectedGenericAssayEntitiesGroupedByGenericAssayTypeFromUrl?: { [genericAssayType: string]: string[]; }; - setRules?: (alteration: string, color: RGBAColor | undefined) => void; + setRules?: ( + alteration: string, + type: string, + color: RGBAColor | undefined + ) => void; } export interface ISelectOption { @@ -330,23 +334,7 @@ export default class OncoprintControls extends React.Component< this.props.handlers.onSelectDistinguishMutationType( !this.props.state.distinguishMutationType ); - // this.props.test && this.props.test(); - // this.props.handlers.onSetRule && - // this.props.handlers.onSetRule( - // this.props.state.rule - // ); break; - // case EVENT_KEY.rule: - // this.props.handlers.onSelectTest && - // this.props.handlers.onSelectTest( - // !this.props.state.test - // ); - // this.props.test && this.props.test(); - // this.props.handlers.onSetRule && - // this.props.handlers.onSetRule( - // this.props.state.rule - // ); - // break; case EVENT_KEY.distinguishGermlineMutations: this.props.handlers.onSelectDistinguishGermlineMutations( !this.props.state.distinguishGermlineMutations @@ -869,53 +857,28 @@ export default class OncoprintControls extends React.Component< className="oncoprint__controls__color_menu" data-test="oncoprintColorDropdownMenu" > - {[ - 'missense', - 'missense_rec', - 'trunc', - 'trunc_rec', - 'inframe', - 'inframe_rec', - 'splice', - 'splice_rec', - 'promoter', - 'promoter_rec', - 'other', - 'other_rec', - ].map(alteration => ( - + Object.keys( + alterationToTypeToLabel[alteration] + ).map(type => ( + - ))} - {/*
Color by Alteration
-
-
- -
-
*/} + )} + /> + )) + )}
); diff --git a/src/shared/lib/Colors.ts b/src/shared/lib/Colors.ts index 9546100ae7d..4913e3dce63 100644 --- a/src/shared/lib/Colors.ts +++ b/src/shared/lib/Colors.ts @@ -13,6 +13,7 @@ import { STRUCTURAL_VARIANT_COLOR, } from 'cbioportal-frontend-commons'; import { MUT_PROFILE_COUNT_NOT_MUTATED } from 'pages/resultsView/plots/PlotsTabUtils'; +import { RGBAColor } from 'oncoprintjs'; // Default grey export const BLACK = '#000000'; export const LIGHT_GREY = '#D3D3D3'; @@ -178,3 +179,19 @@ export function hexToRGBA(str: string): [number, number, number, number] { const b = parseInt(str[5] + str[6], 16); return [r, g, b, 1]; } + +export function rgbaToHex(rgba: RGBAColor): string { + let hexR = rgba[0].toString(16); + let hexG = rgba[1].toString(16); + let hexB = rgba[2].toString(16); + if (hexR.length === 1) { + hexR = '0' + hexR; + } + if (hexG.length === 1) { + hexG = '0' + hexG; + } + if (hexB.length === 1) { + hexB = '0' + hexB; + } + return `#${hexR}${hexG}${hexB}`; +}