Skip to content

Commit 5cafc70

Browse files
authored
Merge pull request #2206 from broadinstitute/ew-explore-pathways
Search and visualize biological pathways (SCP-5982)
2 parents d74fe21 + 8a98465 commit 5cafc70

27 files changed

Lines changed: 1422 additions & 255 deletions

app/javascript/components/explore/ExploreDisplayPanelManager.jsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { faLink, faEye, faTimes, faUndo } from '@fortawesome/free-solid-svg-icon
55

66
import ClusterSelector from '~/components/visualization/controls/ClusterSelector'
77
import AnnotationSelector from '~/components/visualization/controls/AnnotationSelector'
8+
import LabelSelector from '~/components/visualization/controls/LabelSelector'
89
import SubsampleSelector from '~/components/visualization/controls/SubsampleSelector'
910
import { ExploreConsensusSelector } from '~/components/visualization/controls/ConsensusSelector'
1011
import SpatialSelector from '~/components/visualization/controls/SpatialSelector'
@@ -283,7 +284,7 @@ export default function ExploreDisplayPanelManager({
283284
// the wrong tabs
284285
const updateParams = { geneList: '', ideogramFileId: '' }
285286

286-
const clusterParamNames = ['cluster', 'annotation', 'subsample', 'spatialGroups']
287+
const clusterParamNames = ['cluster', 'annotation', 'subsample', 'label', 'spatialGroups']
287288
clusterParamNames.forEach(param => {
288289
updateParams[param] = param in newParams ? newParams[param] : exploreParamsWithDefaults[param]
289290
})
@@ -411,6 +412,13 @@ export default function ExploreDisplayPanelManager({
411412
cluster={exploreParamsWithDefaults.cluster}
412413
shownAnnotation={shownAnnotation}
413414
updateClusterParams={updateClusterParams}/>
415+
{ shownTab === 'pathway' &&
416+
<LabelSelector
417+
exploreParamsWithDefaults={exploreParamsWithDefaults}
418+
exploreInfo={exploreInfo}
419+
updateClusterParams={updateClusterParams}
420+
/>
421+
}
414422
{ shownTab === 'scatter' && <CreateAnnotation
415423
isSelecting={isCellSelecting}
416424
setIsSelecting={setIsCellSelecting}

app/javascript/components/explore/ExploreDisplayTabs.jsx

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ import ScatterPlot from '~/components/visualization/ScatterPlot'
1313
import StudyViolinPlot from '~/components/visualization/StudyViolinPlot'
1414
import DotPlot from '~/components/visualization/DotPlot'
1515
import Heatmap from '~/components/visualization/Heatmap'
16+
import Pathway from '~/components/visualization/Pathway'
1617
import GeneListHeatmap from '~/components/visualization/GeneListHeatmap'
1718
import GenomeView from './GenomeView'
18-
import { getAnnotationValues, getShownAnnotation } from '~/lib/cluster-utils'
19+
import { getAnnotationValues, getEligibleLabels } from '~/lib/cluster-utils'
1920
import RelatedGenesIdeogram from '~/components/visualization/RelatedGenesIdeogram'
2021
import InferCNVIdeogram from '~/components/visualization/InferCNVIdeogram'
2122
import useResizeEffect from '~/hooks/useResizeEffect'
@@ -29,6 +30,7 @@ import PlotTabs from './PlotTabs'
2930
import {
3031
initCellFaceting, filterCells, getFacetsParam, parseFacetsParam
3132
} from '~/lib/cell-faceting'
33+
import { getIsPathway } from '~/lib/search-utils'
3234

3335
/** Get the selected clustering and annotation, or their defaults */
3436
export function getSelectedClusterAndAnnot(exploreInfo, exploreParams) {
@@ -286,7 +288,7 @@ export default function ExploreDisplayTabs({
286288
const [filterErrorText, setFilterErrorText] = useState(null)
287289

288290
const {
289-
enabledTabs, disabledTabs, isGeneList, isGene, isMultiGene, hasIdeogramOutputs
291+
enabledTabs, disabledTabs, isGeneList, isGene, isPathway, isMultiGene, hasIdeogramOutputs
290292
} = getEnabledTabs(exploreInfo, exploreParamsWithDefaults, cellFaceting)
291293

292294
// exploreParams object without genes specified, to pass to cluster comparison plots
@@ -299,13 +301,27 @@ export default function ExploreDisplayTabs({
299301
window.SCP.exploreInfo = exploreInfo
300302

301303
/** helper function so that StudyGeneField doesn't have to see the full exploreParams object */
302-
function searchGenes(genes) {
304+
function queryFn(queries) {
305+
const isPathway = getIsPathway(queries[0])
303306
// also unset any selected gene lists or ideogram files
304-
const newParams = { genes, geneList: '', ideogramFileId: '' }
305-
if (genes.length < 2) {
306-
// and unset the consensus if there are no longer 2+ genes
307-
newParams.consensus = ''
307+
const newParams = { geneList: '', ideogramFileId: '' }
308+
if (isPathway) {
309+
newParams.pathway = queries[0]
310+
newParams['genes'] = []
311+
} else {
312+
newParams.genes = queries
313+
newParams['pathway'] = ''
314+
315+
if (queries.length < 2) {
316+
// and unset the consensus if there are no longer 2+ genes
317+
newParams.consensus = ''
318+
}
319+
}
320+
321+
if (exploreParams?.label !== '') {
322+
newParams.label = exploreParams.label
308323
}
324+
309325
updateExploreParams(newParams)
310326
}
311327

@@ -320,7 +336,7 @@ export default function ExploreDisplayTabs({
320336
exploreInfo &&
321337
exploreInfo.taxonNames.length === 1 &&
322338
exploreParams.genes.length === 1 &&
323-
!isGeneList
339+
!isGeneList && !isPathway
324340
) {
325341
showRelatedGenesIdeogram = true
326342
currentTaxon = exploreInfo.taxonNames[0]
@@ -516,25 +532,33 @@ export default function ExploreDisplayTabs({
516532
return { main, side }
517533
}
518534

535+
let queries
536+
if (exploreParams.pathway !== '') {
537+
queries = [exploreParams.pathway]
538+
} else {
539+
queries = exploreParams.genes
540+
}
541+
519542
return (
520543
<>
521544
{/* Render top content for Explore view, i.e. gene search box and plot tabs */}
522545
<div className="row position-forward">
523546
<div className="col-md-5">
524547
<div className="flexbox">
525-
<StudyGeneField genes={exploreParams.genes}
526-
searchGenes={searchGenes}
548+
<StudyGeneField
549+
queries={queries}
550+
queryFn={queryFn}
527551
allGenes={exploreInfo ? exploreInfo.uniqueGenes : []}
528552
isLoading={!exploreInfo}
529553
speciesList={exploreInfo ? exploreInfo.taxonNames : []}/>
530554
{ // show if this is gene search || gene list
531-
(isGene || isGeneList || hasIdeogramOutputs) &&
555+
(isGene || isGeneList || hasIdeogramOutputs || isPathway) &&
532556
<OverlayTrigger placement="top" overlay={
533557
<Tooltip id="back-to-cluster-view">{'Return to cluster view'}</Tooltip>
534558
}>
535559
<button className="action fa-lg"
536560
aria-label="Back arrow"
537-
onClick={() => searchGenes([])}>
561+
onClick={() => queryFn([])}>
538562
<FontAwesomeIcon icon={faArrowLeft}/>
539563
</button>
540564
</OverlayTrigger>
@@ -559,7 +583,7 @@ export default function ExploreDisplayTabs({
559583
taxon={currentTaxon}
560584
target={`.${plotContainerClass}`}
561585
genesInScope={exploreInfo.uniqueGenes}
562-
searchGenes={searchGenes}
586+
queryFn={queryFn}
563587
speciesList={exploreInfo.taxonNames}
564588

565589
studyAccession={studyAccession}
@@ -666,6 +690,17 @@ export default function ExploreDisplayTabs({
666690
/>
667691
</div>
668692
}
693+
{ enabledTabs.includes('pathway') &&
694+
<div className={shownTab === 'pathway' ? '' : 'hidden'}>
695+
<Pathway
696+
studyAccession={studyAccession}
697+
{... exploreParamsWithDefaults}
698+
labels={getEligibleLabels(exploreParamsWithDefaults, exploreInfo)}
699+
dimensions={getPlotDimensions({ showViewOptionsControls, showDifferentialExpressionTable })}
700+
queryFn={queryFn}
701+
/>
702+
</div>
703+
}
669704
{ enabledTabs.includes('geneListHeatmap') &&
670705
<div className={shownTab === 'geneListHeatmap' ? '' : 'hidden'}>
671706
<GeneListHeatmap
@@ -726,7 +761,7 @@ export default function ExploreDisplayTabs({
726761
clearExploreParams={clearExploreParams}
727762
exploreParamsWithDefaults={exploreParamsWithDefaults}
728763
routerLocation={routerLocation}
729-
searchGenes={searchGenes}
764+
queryFn={queryFn}
730765
countsByLabelForDe={countsByLabelForDe}
731766
setShowUpstreamDifferentialExpressionPanel={setShowUpstreamDifferentialExpressionPanel}
732767
showDifferentialExpressionPanel={showDifferentialExpressionPanel}
@@ -765,6 +800,7 @@ export function getEnabledTabs(exploreInfo, exploreParams, cellFaceting) {
765800
const numGenes = exploreParams?.genes?.length
766801
const isMultiGene = numGenes > 1
767802
const isGene = exploreParams?.genes?.length > 0
803+
const isPathway = exploreParams?.pathway && exploreParams.pathway !== ''
768804
const isConsensus = !!exploreParams.consensus
769805
const hasClusters = exploreInfo && exploreInfo.clusterGroupNames.length > 0
770806
const hasSpatialGroups = exploreParams.spatialGroups?.length > 0
@@ -783,6 +819,8 @@ export function getEnabledTabs(exploreInfo, exploreParams, cellFaceting) {
783819

784820
if (isGeneList) {
785821
enabledTabs = ['geneListHeatmap']
822+
} else if (isPathway) {
823+
enabledTabs = ['pathway']
786824
} else if (isGene) {
787825
if (isMultiGene) {
788826
if (isConsensus) {
@@ -838,5 +876,5 @@ export function getEnabledTabs(exploreInfo, exploreParams, cellFaceting) {
838876
disabledTabs = []
839877
}
840878

841-
return { enabledTabs, disabledTabs, isGeneList, isGene, isMultiGene, hasIdeogramOutputs }
879+
return { enabledTabs, disabledTabs, isGeneList, isGene, isPathway, isMultiGene, hasIdeogramOutputs }
842880
}

app/javascript/components/explore/ExploreTabRouter.jsx

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,15 @@ function updateExploreParams(newOptions, wasUserSpecified=true) {
4343
const search = location.search
4444
const currentParams = buildExploreParamsFromQuery(search)
4545
const mergedOpts = Object.assign({}, currentParams, newOptions)
46+
47+
if (newOptions.pathway && newOptions.pathway !== '') {
48+
mergedOpts.genes = []
49+
}
50+
51+
if (newOptions.genes?.length > 0) {
52+
mergedOpts.pathway = ''
53+
}
54+
4655
if (wasUserSpecified) {
4756
// this is just default params being fetched from the server, so don't change the url
4857
Object.keys(newOptions).forEach(key => {
@@ -62,7 +71,7 @@ function updateExploreParams(newOptions, wasUserSpecified=true) {
6271
// view options settings should not add history entries
6372
// e.g. when a user hits 'back', it shouldn't undo their cluster selection,
6473
// it should take them to the page they were on before they came to the explore tab
65-
navigate(`${query}#study-visualize`, { replace: true })
74+
navigate(query, { replace: true })
6675
}
6776

6877
/** converts query string parameters into the dataParams object */
@@ -90,6 +99,7 @@ function buildExploreParamsFromQuery(query) {
9099
})
91100
exploreParams.cluster = queryParams.cluster ? queryParams.cluster : ''
92101
exploreParams.annotation = annotation
102+
exploreParams.label = queryParams.label ? queryParams.label : ''
93103
exploreParams.subsample = queryParams.subsample ? queryParams.subsample : ''
94104
exploreParams.consensus = queryParams.consensus ? queryParams.consensus : null
95105
if (queryParams.spatialGroups === SPATIAL_GROUPS_EMPTY) {
@@ -98,6 +108,8 @@ function buildExploreParamsFromQuery(query) {
98108
exploreParams.spatialGroups = queryParams.spatialGroups ? queryParams.spatialGroups.split(',') : []
99109
}
100110
exploreParams.genes = geneParamToArray(queryParams.genes)
111+
exploreParams.pathway = queryParams.pathway ? queryParams.pathway : ''
112+
101113
exploreParams.geneList = queryParams.geneList ? queryParams.geneList : ''
102114
exploreParams.heatmapRowCentering = queryParams.heatmapRowCentering ?
103115
queryParams.heatmapRowCentering :
@@ -117,7 +129,7 @@ function buildExploreParamsFromQuery(query) {
117129
exploreParams.expressionFilter = filterArray
118130
}
119131
}
120-
exploreParams.expressionSort = queryParams.expressionSort
132+
exploreParams.expressionSort = queryParams.expressionSort ? queryParams.expressionSort : ''
121133
exploreParams.hiddenTraces = queryParams.hiddenTraces ? queryParams.hiddenTraces.split(',') : []
122134
exploreParams.isSplitLabelArrays = queryParams.isSplitLabelArrays === 'true' ? true : null
123135

@@ -131,8 +143,10 @@ function buildQueryFromParams(exploreParams) {
131143
const querySafeOptions = {
132144
cluster: exploreParams.cluster,
133145
annotation: getIdentifierForAnnotation(exploreParams.annotation),
146+
label: exploreParams.label,
134147
subsample: exploreParams.subsample,
135148
genes: geneArrayToParam(exploreParams.genes),
149+
pathway: exploreParams.pathway,
136150
consensus: exploreParams.consensus,
137151
geneList: exploreParams.geneList,
138152
spatialGroups: exploreParams.spatialGroups.join(','),
@@ -165,7 +179,7 @@ function buildQueryFromParams(exploreParams) {
165179

166180
/** controls list in which query string params are rendered into URL bar */
167181
const PARAM_LIST_ORDER = [
168-
'geneList', 'genes', 'cluster', 'spatialGroups', 'annotation', 'subsample', 'consensus',
182+
'geneList', 'genes', 'pathway', 'cluster', 'spatialGroups', 'annotation', 'subsample', 'consensus',
169183
'tab', 'scatterColor', 'distributionPlot', 'distributionPoints',
170184
'heatmapFit', 'heatmapRowCentering', 'trackFileName', 'ideogramFileId', 'expressionFilter', 'expressionSort',
171185
'isSplitLabelArrays', 'hiddenTraces', 'facets'

app/javascript/components/explore/PlotTabs.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const tabList = [
88
{ key: 'distribution', label: 'Distribution' },
99
{ key: 'dotplot', label: 'Dot plot' },
1010
{ key: 'heatmap', label: 'Heatmap' },
11+
{ key: 'pathway', label: 'Pathway' },
1112
{ key: 'geneListHeatmap', label: 'Precomputed heatmap' },
1213
{ key: 'spatial', label: 'Spatial' },
1314
{ key: 'genome', label: 'Genome' },

0 commit comments

Comments
 (0)