jbrowse-plugin-graphgenomeviewer 1.0.0 → 3.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -0
- package/README.md +202 -24
- package/dist/chunks/AddTrackWorkflow-Y3OBHM33.js +2 -0
- package/dist/chunks/AddTrackWorkflow-Y3OBHM33.js.map +7 -0
- package/dist/chunks/GbzBaseSyntenyAdapter-EFIOI2SN.js +8 -0
- package/dist/chunks/GbzBaseSyntenyAdapter-EFIOI2SN.js.map +7 -0
- package/dist/chunks/GraphGenomeView-UD5WCV3Q.js +14 -0
- package/dist/chunks/GraphGenomeView-UD5WCV3Q.js.map +7 -0
- package/dist/chunks/MinigraphBubbleAdapter-B75VQK7T.js +2 -0
- package/dist/chunks/MinigraphBubbleAdapter-B75VQK7T.js.map +7 -0
- package/dist/chunks/RgfaTabixAdapter-IT7I5RR3.js +3 -0
- package/dist/chunks/RgfaTabixAdapter-IT7I5RR3.js.map +7 -0
- package/dist/chunks/bandage-layout-FIOZ7B3E.js +1660 -0
- package/dist/chunks/bandage-layout-FIOZ7B3E.js.map +7 -0
- package/dist/chunks/chunk-D7XFR2IV.js +2 -0
- package/dist/chunks/chunk-D7XFR2IV.js.map +7 -0
- package/dist/chunks/chunk-F4C3BEPV.js +2 -0
- package/dist/chunks/chunk-F4C3BEPV.js.map +7 -0
- package/dist/chunks/chunk-FCWXYIFC.js +2 -0
- package/dist/chunks/chunk-FCWXYIFC.js.map +7 -0
- package/dist/chunks/chunk-FY3BTKY4.js +2 -0
- package/dist/chunks/chunk-FY3BTKY4.js.map +7 -0
- package/dist/chunks/chunk-J4OLMLS5.js +2 -0
- package/dist/chunks/chunk-J4OLMLS5.js.map +7 -0
- package/dist/chunks/chunk-JCG7RLMB.js +2 -0
- package/dist/chunks/chunk-JCG7RLMB.js.map +7 -0
- package/dist/chunks/chunk-KWAXLUPQ.js +2 -0
- package/dist/chunks/chunk-KWAXLUPQ.js.map +7 -0
- package/dist/chunks/chunk-LGKM2PZH.js +2 -0
- package/dist/chunks/chunk-LGKM2PZH.js.map +7 -0
- package/dist/chunks/chunk-TN6FK5UD.js +2 -0
- package/dist/chunks/chunk-TN6FK5UD.js.map +7 -0
- package/dist/chunks/chunk-X3X74OGB.js +2 -0
- package/dist/chunks/chunk-X3X74OGB.js.map +7 -0
- package/dist/chunks/chunk-XEJ47DLD.js +2 -0
- package/dist/chunks/chunk-XEJ47DLD.js.map +7 -0
- package/dist/chunks/chunk-ZNGDWZ6Z.js +2 -0
- package/dist/chunks/chunk-ZNGDWZ6Z.js.map +7 -0
- package/dist/chunks/executeDiagonalize-XMPEDHVZ.js +2 -0
- package/dist/chunks/executeDiagonalize-XMPEDHVZ.js.map +7 -0
- package/dist/jbrowse-plugin-graphgenomeviewer.esm.js +3 -0
- package/dist/jbrowse-plugin-graphgenomeviewer.esm.js.map +7 -0
- package/package.json +94 -49
- package/src/GbzBaseSyntenyAdapter/GbzBaseSyntenyAdapter.test.ts +427 -0
- package/src/GbzBaseSyntenyAdapter/GbzBaseSyntenyAdapter.ts +461 -0
- package/src/GbzBaseSyntenyAdapter/configSchema.ts +181 -0
- package/src/GbzBaseSyntenyAdapter/index.ts +27 -0
- package/src/GbzBaseSyntenyAdapter/test_data/micb-kir3dl1.gbz.db +0 -0
- package/src/GbzBaseSyntenyAdapter/test_data/micb-kir3dl1.haplotype-index.db +0 -0
- package/src/GetSubgraph.test.ts +193 -0
- package/src/GetSubgraph.ts +93 -0
- package/src/GraphAddTrackWorkflow/AddTrackWorkflow.tsx +167 -0
- package/src/GraphAddTrackWorkflow/buildTrackConfig.test.ts +150 -0
- package/src/GraphAddTrackWorkflow/buildTrackConfig.ts +114 -0
- package/src/GraphAddTrackWorkflow/index.ts +20 -0
- package/src/GraphComputeLayout.ts +46 -0
- package/src/GraphGenomeView/anchoredNodes.ts +101 -0
- package/src/GraphGenomeView/bubbleSpreads.ts +102 -0
- package/src/GraphGenomeView/bubbles/bubbleHalos.test.ts +58 -0
- package/src/GraphGenomeView/bubbles/bubbleHalos.ts +149 -0
- package/src/GraphGenomeView/bubbles/bubbleRoutes.test.ts +141 -0
- package/src/GraphGenomeView/bubbles/bubblesFromGraph.test.ts +239 -0
- package/src/GraphGenomeView/bubbles/bubblesFromGraph.ts +210 -0
- package/src/GraphGenomeView/bubbles/classifyBubble.test.ts +172 -0
- package/src/GraphGenomeView/bubbles/classifyBubble.ts +138 -0
- package/src/GraphGenomeView/bubbles/popBubble.test.ts +73 -0
- package/src/GraphGenomeView/bubbles/popBubble.ts +26 -0
- package/src/GraphGenomeView/colorSchemes.ts +36 -0
- package/src/GraphGenomeView/components/BubbleHalos.tsx +162 -0
- package/src/GraphGenomeView/components/BubbleOverlay.tsx +181 -0
- package/src/GraphGenomeView/components/ColorSchemeSelect.tsx +41 -0
- package/src/GraphGenomeView/components/GenePins.tsx +107 -0
- package/src/GraphGenomeView/components/GraphCanvas.tsx +759 -0
- package/src/GraphGenomeView/components/GraphGenomeView.test.tsx +77 -0
- package/src/GraphGenomeView/components/GraphGenomeView.tsx +18 -17
- package/src/GraphGenomeView/components/GraphLoadStatus.tsx +84 -0
- package/src/GraphGenomeView/components/GraphSettingsDialog.tsx +327 -0
- package/src/GraphGenomeView/components/GraphStats.tsx +82 -0
- package/src/GraphGenomeView/components/GraphToolbar.tsx +84 -0
- package/src/GraphGenomeView/components/ImportForm.tsx +124 -0
- package/src/GraphGenomeView/components/LabelChip.tsx +65 -0
- package/src/GraphGenomeView/components/LayoutSelect.tsx +56 -0
- package/src/GraphGenomeView/components/RepeatSelect.tsx +51 -0
- package/src/GraphGenomeView/components/SettingsMenu.tsx +82 -0
- package/src/GraphGenomeView/components/SubgraphContextSelect.tsx +75 -0
- package/src/GraphGenomeView/components/SubgraphHaplotypesField.tsx +86 -0
- package/src/GraphGenomeView/components/WalkRowsOverlay.tsx +252 -0
- package/src/GraphGenomeView/components/WalkSelect.tsx +51 -0
- package/src/GraphGenomeView/components/overlayLabels.test.ts +56 -0
- package/src/GraphGenomeView/components/overlayLabels.ts +75 -0
- package/src/GraphGenomeView/deletionEdges.test.ts +198 -0
- package/src/GraphGenomeView/deletionEdges.ts +217 -0
- package/src/GraphGenomeView/genes/geneFeatures.ts +107 -0
- package/src/GraphGenomeView/genes/genePins.ts +130 -0
- package/src/GraphGenomeView/genes/genes.test.ts +153 -0
- package/src/GraphGenomeView/gfa/gfaConverter.test.ts +394 -0
- package/src/GraphGenomeView/gfa/gfaConverter.ts +235 -0
- package/src/GraphGenomeView/graphLabels.budget.test.ts +36 -0
- package/src/GraphGenomeView/graphLabels.test.ts +548 -0
- package/src/GraphGenomeView/graphLabels.ts +640 -0
- package/src/GraphGenomeView/index.ts +29 -7
- package/src/GraphGenomeView/laneRamp.test.ts +97 -0
- package/src/GraphGenomeView/laneRamp.ts +87 -0
- package/src/GraphGenomeView/launchKeys.test.ts +63 -0
- package/src/GraphGenomeView/layout/anchoredLayout.test.ts +213 -0
- package/src/GraphGenomeView/layout/anchoredLayout.ts +80 -0
- package/src/GraphGenomeView/layout/bubbleCrossing.test.ts +190 -0
- package/src/GraphGenomeView/layout/drawnScale.ts +199 -0
- package/src/GraphGenomeView/layout/mergeRuns.test.ts +100 -0
- package/src/GraphGenomeView/layout/mergeRuns.ts +203 -0
- package/src/GraphGenomeView/layout/orderedLayout.test.ts +212 -0
- package/src/GraphGenomeView/layout/orderedLayout.ts +396 -0
- package/src/GraphGenomeView/layout/orientToReference.test.ts +110 -0
- package/src/GraphGenomeView/layout/orientToReference.ts +79 -0
- package/src/GraphGenomeView/layout/placeOffReference.ts +147 -0
- package/src/GraphGenomeView/layout/referenceSeeds.test.ts +79 -0
- package/src/GraphGenomeView/layout/referenceSeeds.ts +89 -0
- package/src/GraphGenomeView/layout/rowSpacing.ts +5 -0
- package/src/GraphGenomeView/layout/sampleRowLayout.test.ts +318 -0
- package/src/GraphGenomeView/layout/sampleRowLayout.ts +134 -0
- package/src/GraphGenomeView/layout/variantMapLayout.test.ts +59 -0
- package/src/GraphGenomeView/layout/variantMapLayout.ts +20 -0
- package/src/GraphGenomeView/layout/walkRowLayout.ts +54 -0
- package/src/GraphGenomeView/layout/walkRows.test.ts +178 -0
- package/src/GraphGenomeView/layout/walkRows.ts +177 -0
- package/src/GraphGenomeView/layoutModes.ts +131 -0
- package/src/GraphGenomeView/model.test.ts +2696 -0
- package/src/GraphGenomeView/model.ts +2672 -94
- package/src/GraphGenomeView/nodeWidths.ts +67 -0
- package/src/GraphGenomeView/pathAnchoring.test.ts +252 -0
- package/src/GraphGenomeView/pathAnchoring.ts +164 -0
- package/src/GraphGenomeView/pathColors.test.ts +83 -0
- package/src/GraphGenomeView/pathColors.ts +122 -0
- package/src/GraphGenomeView/referenceSpan.test.ts +84 -0
- package/src/GraphGenomeView/referenceSpan.ts +133 -0
- package/src/GraphGenomeView/renderPasses.bench.ts +172 -0
- package/src/GraphGenomeView/renderPipeline.test.ts +226 -0
- package/src/GraphGenomeView/renderer/Canvas2DRenderer.test.ts +205 -0
- package/src/GraphGenomeView/renderer/Canvas2DRenderer.ts +246 -0
- package/src/GraphGenomeView/renderer/GeometryBuilder.test.ts +723 -0
- package/src/GraphGenomeView/renderer/GeometryBuilder.ts +882 -0
- package/src/GraphGenomeView/renderer/GraphRenderer.ts +13 -0
- package/src/GraphGenomeView/renderer/colorBits.ts +43 -0
- package/src/GraphGenomeView/renderer/recordingCanvas.ts +64 -0
- package/src/GraphGenomeView/renderer/types.ts +84 -0
- package/src/GraphGenomeView/renderer/walkFade.test.ts +100 -0
- package/src/GraphGenomeView/repeats/repeatFeatures.test.ts +114 -0
- package/src/GraphGenomeView/repeats/repeatFeatures.ts +202 -0
- package/src/GraphGenomeView/repeats/test_data/abca7_trgt_walks.json +575 -0
- package/src/GraphGenomeView/repeats/walkCalls.test.ts +127 -0
- package/src/GraphGenomeView/repeats/walkCalls.ts +132 -0
- package/src/GraphGenomeView/stress.test.ts +214 -0
- package/src/GraphGenomeView/subgraphLoad.test.ts +245 -0
- package/src/GraphGenomeView/types.ts +141 -0
- package/src/GraphGenomeView/util/SpatialIndex.test.ts +73 -0
- package/src/GraphGenomeView/util/SpatialIndex.ts +312 -0
- package/src/GraphGenomeView/util/edgeCurves.test.ts +93 -0
- package/src/GraphGenomeView/util/edgeCurves.ts +94 -0
- package/src/GraphGenomeView/util/geometry.test.ts +447 -0
- package/src/GraphGenomeView/util/geometry.ts +656 -0
- package/src/GraphGenomeView/util/hitDetection.test.ts +307 -0
- package/src/GraphGenomeView/util/hitDetection.ts +330 -0
- package/src/GraphGenomeView/util/wheelZoom.test.ts +40 -0
- package/src/GraphGenomeView/util/wheelZoom.ts +26 -0
- package/src/GraphGenomeView/walkHighlight.test.ts +54 -0
- package/src/GraphGenomeView/walkHighlight.ts +56 -0
- package/src/LaunchGraphGenomeView.ts +46 -0
- package/src/MinigraphBubbleAdapter/MinigraphBubbleAdapter.test.ts +139 -0
- package/src/MinigraphBubbleAdapter/MinigraphBubbleAdapter.ts +86 -0
- package/src/MinigraphBubbleAdapter/bubbleLine.ts +92 -0
- package/src/MinigraphBubbleAdapter/configSchema.ts +109 -0
- package/src/MinigraphBubbleAdapter/index.ts +22 -0
- package/src/MinigraphBubbleAdapter/test_data/hprc_bubbles_mhc.bed.gz +0 -0
- package/src/MinigraphBubbleAdapter/test_data/hprc_bubbles_mhc.bed.gz.tbi +0 -0
- package/src/RgfaTabixAdapter/RgfaTabixAdapter.test.ts +285 -0
- package/src/RgfaTabixAdapter/RgfaTabixAdapter.ts +194 -0
- package/src/RgfaTabixAdapter/bubbleTier.test.ts +84 -0
- package/src/RgfaTabixAdapter/configSchema.ts +142 -0
- package/src/RgfaTabixAdapter/index.ts +24 -0
- package/src/RgfaTabixAdapter/rgfaBed.test.ts +158 -0
- package/src/RgfaTabixAdapter/rgfaBed.ts +156 -0
- package/src/RgfaTabixAdapter/test_data/bubble_tier_chrY.links.bed.gz +0 -0
- package/src/RgfaTabixAdapter/test_data/bubble_tier_chrY.links.bed.gz.tbi +0 -0
- package/src/RgfaTabixAdapter/test_data/bubble_tier_chrY.segs.bed.gz +0 -0
- package/src/RgfaTabixAdapter/test_data/bubble_tier_chrY.segs.bed.gz.tbi +0 -0
- package/src/RgfaTabixAdapter/test_data/pggb_ecoli.links.bed.gz +0 -0
- package/src/RgfaTabixAdapter/test_data/pggb_ecoli.links.bed.gz.tbi +0 -0
- package/src/RgfaTabixAdapter/test_data/pggb_ecoli.segs.bed.gz +0 -0
- package/src/RgfaTabixAdapter/test_data/pggb_ecoli.segs.bed.gz.tbi +0 -0
- package/src/RgfaTabixAdapter/test_data/rgfa_ecoli.links.bed.gz +0 -0
- package/src/RgfaTabixAdapter/test_data/rgfa_ecoli.links.bed.gz.tbi +0 -0
- package/src/RgfaTabixAdapter/test_data/rgfa_ecoli.segs.bed.gz +0 -0
- package/src/RgfaTabixAdapter/test_data/rgfa_ecoli.segs.bed.gz.tbi +0 -0
- package/src/alleleProjection/projectAlleles.test.ts +75 -0
- package/src/alleleProjection/projectAlleles.ts +190 -0
- package/src/bandage/README.md +121 -0
- package/src/bandage/bandage-layout.d.ts +14 -0
- package/src/bandage/bandage-layout.js +0 -0
- package/src/bandage/native/CMakeLists.txt +45 -0
- package/src/bandage/native/include/graph.h +188 -0
- package/src/bandage/native/include/graphlayout.h +74 -0
- package/src/bandage/native/include/settings.h +86 -0
- package/src/bandage/native/include/types.h +56 -0
- package/src/bandage/native/src/bindings.cpp +164 -0
- package/src/bandage/native/src/graphlayout.cpp +621 -0
- package/src/bandage/resizableDecode.test.ts +58 -0
- package/src/gfa-core/gfaParser.bench.ts +55 -0
- package/src/gfa-core/gfaParser.test.ts +168 -0
- package/src/gfa-core/gfaParser.ts +237 -0
- package/src/gfa-core/index.ts +10 -0
- package/src/hoverSync/GraphNodeHighlight.tsx +62 -0
- package/src/hoverSync/graphViewHighlights.test.ts +158 -0
- package/src/hoverSync/graphViewHighlights.ts +119 -0
- package/src/hoverSync/index.test.tsx +36 -0
- package/src/hoverSync/index.tsx +19 -0
- package/src/hoverSync/lgvHover.test.ts +202 -0
- package/src/hoverSync/lgvHover.ts +136 -0
- package/src/index.ts +46 -15
- package/src/launchFromGraph/contributors.test.ts +197 -0
- package/src/launchFromGraph/contributors.ts +0 -0
- package/src/launchFromGraph/graphMenuItems.ts +211 -0
- package/src/launchFromGraph/launchFromGraph.test.ts +489 -0
- package/src/launchFromGraph/launchFromGraph.ts +265 -0
- package/src/launchFromGraph/launchTracks.test.ts +72 -0
- package/src/launchFromGraph/launchTracks.ts +50 -0
- package/src/launchFromGraph/linearViewTarget.test.ts +82 -0
- package/src/launchFromGraph/linearViewTarget.ts +75 -0
- package/src/launchFromGraph/syntenyTracks.ts +47 -0
- package/src/launchSubgraph/index.ts +152 -0
- package/src/launchSubgraph/launchSubgraph.test.ts +264 -0
- package/src/launchSubgraph/launchSubgraphView.ts +128 -0
- package/src/launchSubgraph/linearViewMenuItems.test.ts +176 -0
- package/src/launchSubgraph/linearViewMenuItems.ts +107 -0
- package/src/launchSubgraph/multiWayLaunch.test.ts +289 -0
- package/src/launchSubgraph/subgraphMenuItems.ts +73 -0
- package/src/launchSubgraph/subgraphTracks.test.ts +165 -0
- package/src/launchSubgraph/subgraphTracks.ts +151 -0
- package/src/launchSubgraph/testEnv.ts +258 -0
- package/src/loadBandage.ts +22 -0
- package/src/panSNTabix.test.ts +72 -0
- package/src/panSNTabix.ts +149 -0
- package/src/pansn.ts +106 -0
- package/src/pluginName.ts +3 -0
- package/src/synteny/ComparativeAdapterBase.ts +77 -0
- package/src/synteny/SyntenyFeature.ts +116 -0
- package/src/synteny/clipFeatureToRegion.test.ts +329 -0
- package/src/synteny/clipFeatureToRegion.ts +167 -0
- package/src/version.ts +1 -0
- package/dist/GraphGenomeView/components/FeatureDialog.d.ts +0 -5
- package/dist/GraphGenomeView/components/FeatureDialog.js +0 -18
- package/dist/GraphGenomeView/components/FeatureDialog.js.map +0 -1
- package/dist/GraphGenomeView/components/GraphGenomeView.d.ts +0 -6
- package/dist/GraphGenomeView/components/GraphGenomeView.js +0 -17
- package/dist/GraphGenomeView/components/GraphGenomeView.js.map +0 -1
- package/dist/GraphGenomeView/components/GraphPanel.d.ts +0 -6
- package/dist/GraphGenomeView/components/GraphPanel.js +0 -17
- package/dist/GraphGenomeView/components/GraphPanel.js.map +0 -1
- package/dist/GraphGenomeView/components/Header.d.ts +0 -6
- package/dist/GraphGenomeView/components/Header.js +0 -59
- package/dist/GraphGenomeView/components/Header.js.map +0 -1
- package/dist/GraphGenomeView/components/SettingsDialog.d.ts +0 -7
- package/dist/GraphGenomeView/components/SettingsDialog.js +0 -24
- package/dist/GraphGenomeView/components/SettingsDialog.js.map +0 -1
- package/dist/GraphGenomeView/index.d.ts +0 -2
- package/dist/GraphGenomeView/index.js +0 -15
- package/dist/GraphGenomeView/index.js.map +0 -1
- package/dist/GraphGenomeView/model.d.ts +0 -80
- package/dist/GraphGenomeView/model.js +0 -96
- package/dist/GraphGenomeView/model.js.map +0 -1
- package/dist/GraphGenomeView/util.d.ts +0 -1
- package/dist/GraphGenomeView/util.js +0 -8
- package/dist/GraphGenomeView/util.js.map +0 -1
- package/dist/index.d.ts +0 -8
- package/dist/index.js +0 -24
- package/dist/index.js.map +0 -1
- package/dist/jbrowse-plugin-graphgenomeviewer.umd.production.min.js +0 -67
- package/dist/jbrowse-plugin-graphgenomeviewer.umd.production.min.js.map +0 -7
- package/src/GraphGenomeView/components/FeatureDialog.tsx +0 -51
- package/src/GraphGenomeView/components/GraphPanel.tsx +0 -37
- package/src/GraphGenomeView/components/Header.tsx +0 -90
- package/src/GraphGenomeView/components/SettingsDialog.tsx +0 -71
- package/src/GraphGenomeView/util.ts +0 -7
- package/src/declare.d.ts +0 -1
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// LAUNCH_LABEL rather than the literal: the host renamed this submenu from
|
|
2
|
+
// "Launch view" to "Launch" (packages/core/src/ui/launchViewMenu.ts says why),
|
|
3
|
+
// and a hardcoded copy here fails as "the item was never added" rather than as
|
|
4
|
+
// "the submenu is called something else".
|
|
5
|
+
import { LAUNCH_LABEL } from '@jbrowse/core/ui'
|
|
6
|
+
|
|
7
|
+
import { createTestEnvironment } from './testEnv'
|
|
8
|
+
import { MAX_GRAPH_REGION_BP } from '../GraphGenomeView/model'
|
|
9
|
+
|
|
10
|
+
import type { MenuItem } from '@jbrowse/core/ui'
|
|
11
|
+
|
|
12
|
+
const LABEL_VISIBLE = 'Graph genome view (this region)'
|
|
13
|
+
const LABEL_SELECTION = 'Graph genome view (this selection)'
|
|
14
|
+
const LABEL_FEATURE = 'Graph genome view (this feature)'
|
|
15
|
+
|
|
16
|
+
function launchItems(items: MenuItem[]) {
|
|
17
|
+
const item = items.find(i => 'label' in i && i.label === LAUNCH_LABEL)
|
|
18
|
+
return item && 'subMenu' in item ? item.subMenu : []
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function find(items: MenuItem[], label: string) {
|
|
22
|
+
return items.find(i => 'label' in i && i.label === label)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function click(item: MenuItem | undefined) {
|
|
26
|
+
if (item && 'onClick' in item) {
|
|
27
|
+
item.onClick(undefined)
|
|
28
|
+
} else {
|
|
29
|
+
throw new Error('menu item has no onClick')
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// A rubberband selection is the entry point that works at the zoom people
|
|
34
|
+
// actually browse at: the visible region is routinely wider than the cap, and
|
|
35
|
+
// selecting a range needs no navigating first.
|
|
36
|
+
test('the rubberband menu launches the selected range', () => {
|
|
37
|
+
const { createDisplay } = createTestEnvironment()
|
|
38
|
+
const { session, view } = createDisplay()
|
|
39
|
+
view.setOffsets(
|
|
40
|
+
{ refName: 'ctgA', index: 0, offset: 1000, start: 0, end: 50_000 },
|
|
41
|
+
{ refName: 'ctgA', index: 0, offset: 2000, start: 0, end: 50_000 },
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
const item = find(view.rubberBandMenuItems(), LABEL_SELECTION)
|
|
45
|
+
expect(item).toBeDefined()
|
|
46
|
+
click(item)
|
|
47
|
+
|
|
48
|
+
const [type, snapshot] = session.addedViews[0]!
|
|
49
|
+
expect(type).toBe('GraphGenomeView')
|
|
50
|
+
expect(snapshot.loadedTrackId).toBe('graph_track')
|
|
51
|
+
expect(snapshot.connectedViewId).toBe(view.id)
|
|
52
|
+
expect(snapshot.loadedRegion).toMatchObject({ refName: 'ctgA' })
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test('the rubberband menu keeps the items it already had', () => {
|
|
56
|
+
const { createDisplay } = createTestEnvironment()
|
|
57
|
+
const { view } = createDisplay()
|
|
58
|
+
view.setOffsets(
|
|
59
|
+
{ refName: 'ctgA', index: 0, offset: 1000, start: 0, end: 50_000 },
|
|
60
|
+
{ refName: 'ctgA', index: 0, offset: 2000, start: 0, end: 50_000 },
|
|
61
|
+
)
|
|
62
|
+
const labels = view
|
|
63
|
+
.rubberBandMenuItems()
|
|
64
|
+
.flatMap((i: MenuItem) => ('label' in i ? [i.label] : []))
|
|
65
|
+
expect(labels).toContain('Zoom to region')
|
|
66
|
+
expect(labels).toContain(LABEL_SELECTION)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
// Nothing selected is not a launchable region, so the item stays out of the menu
|
|
70
|
+
// rather than appearing dead.
|
|
71
|
+
test('no rubberband selection adds no item', () => {
|
|
72
|
+
const { createDisplay } = createTestEnvironment()
|
|
73
|
+
const { view } = createDisplay()
|
|
74
|
+
expect(find(view.rubberBandMenuItems(), LABEL_SELECTION)).toBeUndefined()
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
// The point of the view-level item: the graph track need not be the one whose
|
|
78
|
+
// menu you opened, or be open at all.
|
|
79
|
+
test('the view menu launches the visible region', () => {
|
|
80
|
+
const { createDisplay } = createTestEnvironment()
|
|
81
|
+
const { session, view } = createDisplay({ trackId: 'bubble_track' })
|
|
82
|
+
|
|
83
|
+
const item = find(launchItems(view.menuItems()), LABEL_VISIBLE)
|
|
84
|
+
expect(item).toBeDefined()
|
|
85
|
+
click(item)
|
|
86
|
+
|
|
87
|
+
expect(session.addedViews[0]![1].loadedTrackId).toBe('graph_track')
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
// A vanishing item teaches nothing; a greyed-out one with the size in its
|
|
91
|
+
// tooltip says what to do about it.
|
|
92
|
+
test('an over-cap region disables the item instead of hiding it', () => {
|
|
93
|
+
const { createDisplay } = createTestEnvironment()
|
|
94
|
+
const { session, view } = createDisplay()
|
|
95
|
+
view.setDisplayedRegions([
|
|
96
|
+
{
|
|
97
|
+
assemblyName: 'volvox',
|
|
98
|
+
refName: 'ctgA',
|
|
99
|
+
start: 0,
|
|
100
|
+
end: MAX_GRAPH_REGION_BP * 3,
|
|
101
|
+
},
|
|
102
|
+
])
|
|
103
|
+
// the *visible* span is what's cut, so it has to be zoomed out past the cap,
|
|
104
|
+
// not merely have a large region loaded
|
|
105
|
+
view.showAllRegions()
|
|
106
|
+
expect(view.dynamicBlocks.totalBp).toBeGreaterThan(MAX_GRAPH_REGION_BP)
|
|
107
|
+
|
|
108
|
+
const item = find(launchItems(view.menuItems()), LABEL_VISIBLE)
|
|
109
|
+
expect(item).toMatchObject({ disabled: true })
|
|
110
|
+
expect(item && 'disabledHelpText' in item && item.disabledHelpText).toMatch(
|
|
111
|
+
/zoom in or select a smaller range/i,
|
|
112
|
+
)
|
|
113
|
+
expect(session.addedViews).toHaveLength(0)
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
// A graph is cut on its reference, the first assembly its track names, so a
|
|
117
|
+
// view of another one gets the reason rather than a cut in the wrong frame.
|
|
118
|
+
test('a view of a non-reference assembly disables the item and names the reference', () => {
|
|
119
|
+
const { createDisplay } = createTestEnvironment({
|
|
120
|
+
graphAssemblyNames: ['hg38', 'volvox'],
|
|
121
|
+
})
|
|
122
|
+
const { session, view } = createDisplay()
|
|
123
|
+
const item = find(launchItems(view.menuItems()), LABEL_VISIBLE)
|
|
124
|
+
expect(item).toMatchObject({ disabled: true })
|
|
125
|
+
expect(item && 'disabledHelpText' in item && item.disabledHelpText).toMatch(
|
|
126
|
+
/cut on its reference, hg38/,
|
|
127
|
+
)
|
|
128
|
+
expect(session.addedViews).toHaveLength(0)
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
// The bubble track marks exactly where haplotypes diverge, which is the most
|
|
132
|
+
// natural thing to right-click — but its adapter reads a summary index and
|
|
133
|
+
// cannot cut a graph, so the launch has to come from the graph track instead.
|
|
134
|
+
test('right-clicking a feature on a non-graph track cuts from the graph track', () => {
|
|
135
|
+
const { createDisplay } = createTestEnvironment()
|
|
136
|
+
const { session, display, view } = createDisplay({ trackId: 'bubble_track' })
|
|
137
|
+
display.openContextMenu({
|
|
138
|
+
item: { featureId: 'bubble1', startBp: 2000, endBp: 2400, name: 'bubble1' },
|
|
139
|
+
displayedRegionIndex: 0,
|
|
140
|
+
clientX: 0,
|
|
141
|
+
clientY: 0,
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
const item = find(launchItems(display.contextMenuItems()), LABEL_FEATURE)
|
|
145
|
+
expect(item).toBeDefined()
|
|
146
|
+
click(item)
|
|
147
|
+
|
|
148
|
+
const [, snapshot] = session.addedViews[0]!
|
|
149
|
+
expect(snapshot.loadedTrackId).toBe('graph_track')
|
|
150
|
+
expect(snapshot.connectedViewId).toBe(view.id)
|
|
151
|
+
// padded by half the feature's length on each side
|
|
152
|
+
expect(snapshot.loadedRegion).toEqual({
|
|
153
|
+
refName: 'ctgA',
|
|
154
|
+
assemblyName: 'volvox',
|
|
155
|
+
start: 1800,
|
|
156
|
+
end: 2600,
|
|
157
|
+
})
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
// No graph data in the session means no menu clutter anywhere.
|
|
161
|
+
test('no graph track in the session adds no items', () => {
|
|
162
|
+
const { createDisplay } = createTestEnvironment({ subgraphCapable: false })
|
|
163
|
+
const { view, display } = createDisplay({ trackId: 'bubble_track' })
|
|
164
|
+
display.openContextMenu({
|
|
165
|
+
item: { featureId: 'bubble1', startBp: 2000, endBp: 2400, name: 'bubble1' },
|
|
166
|
+
displayedRegionIndex: 0,
|
|
167
|
+
clientX: 0,
|
|
168
|
+
clientY: 0,
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
expect(find(launchItems(view.menuItems()), LABEL_VISIBLE)).toBeUndefined()
|
|
172
|
+
expect(find(view.rubberBandMenuItems(), LABEL_SELECTION)).toBeUndefined()
|
|
173
|
+
expect(
|
|
174
|
+
find(launchItems(display.contextMenuItems()), LABEL_FEATURE),
|
|
175
|
+
).toBeUndefined()
|
|
176
|
+
})
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { pushLaunchViewMenuItem } from '@jbrowse/core/ui'
|
|
2
|
+
import { getSession } from '@jbrowse/core/util'
|
|
3
|
+
|
|
4
|
+
import { SUBGRAPH_REGION_LABEL, regionFromViewport } from './launchSubgraphView'
|
|
5
|
+
import { subgraphMenuItems } from './subgraphMenuItems'
|
|
6
|
+
import { subgraphTracks } from './subgraphTracks'
|
|
7
|
+
|
|
8
|
+
import type PluginManager from '@jbrowse/core/PluginManager'
|
|
9
|
+
import type { PluggableElementType } from '@jbrowse/core/pluggableElementTypes'
|
|
10
|
+
import type ViewType from '@jbrowse/core/pluggableElementTypes/ViewType'
|
|
11
|
+
import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
|
|
12
|
+
|
|
13
|
+
const SELECTION_LABEL = 'Graph genome view (this selection)'
|
|
14
|
+
|
|
15
|
+
function isLinearGenomeView(elt: { name: string }): elt is ViewType {
|
|
16
|
+
return elt.name === 'LinearGenomeView'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Launch entry points on the linear view itself, rather than on a graph track's
|
|
20
|
+
// menu. Two reasons this is where they belong:
|
|
21
|
+
//
|
|
22
|
+
// - The graph track need not be in the view, or even turned on. Scanning the
|
|
23
|
+
// session for a subgraph-capable track gives a user browsing genes a way in;
|
|
24
|
+
// before, the only entry point was the menu of a track they might never have
|
|
25
|
+
// opened.
|
|
26
|
+
// - A rubberband selection picks a window directly, with no navigating first,
|
|
27
|
+
// and is the only way to ask about a range narrower than what is on screen.
|
|
28
|
+
export default function LinearViewMenuItemsF(pluginManager: PluginManager) {
|
|
29
|
+
pluginManager.addToExtensionPoint(
|
|
30
|
+
'Core-extendPluggableElement',
|
|
31
|
+
(pluggableElement: PluggableElementType) => {
|
|
32
|
+
if (isLinearGenomeView(pluggableElement)) {
|
|
33
|
+
// extendStateModel, NOT `stateModel = stateModel.extend(...)`: see
|
|
34
|
+
// launchSubgraph/index.ts for what a synchronous read costs now that the
|
|
35
|
+
// host registers state models as lazy loaders.
|
|
36
|
+
pluggableElement.extendStateModel(stateModel =>
|
|
37
|
+
stateModel.extend(
|
|
38
|
+
// Annotated rather than cast: the extension point hands over an
|
|
39
|
+
// IAnyModelType, and stating the model shape here is what types
|
|
40
|
+
// `getSelectedRegions`/`dynamicBlocks` below without an `as`.
|
|
41
|
+
(self: LinearGenomeViewModel) => {
|
|
42
|
+
const superMenuItems = self.menuItems
|
|
43
|
+
const superRubberBandMenuItems = self.rubberBandMenuItems
|
|
44
|
+
|
|
45
|
+
// Graph tracks for this view's assembly. Empty means every item
|
|
46
|
+
// below is empty too, so a session with no graph data gains no menu
|
|
47
|
+
// clutter.
|
|
48
|
+
function tracks() {
|
|
49
|
+
const assemblyName = self.assemblyNames[0]
|
|
50
|
+
return assemblyName
|
|
51
|
+
? subgraphTracks(
|
|
52
|
+
pluginManager,
|
|
53
|
+
getSession(self),
|
|
54
|
+
assemblyName,
|
|
55
|
+
self,
|
|
56
|
+
)
|
|
57
|
+
: []
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
views: {
|
|
62
|
+
menuItems() {
|
|
63
|
+
const items = superMenuItems()
|
|
64
|
+
for (const item of subgraphMenuItems({
|
|
65
|
+
label: SUBGRAPH_REGION_LABEL,
|
|
66
|
+
region: regionFromViewport(
|
|
67
|
+
self.dynamicBlocks.contentBlocks,
|
|
68
|
+
),
|
|
69
|
+
tracks: tracks(),
|
|
70
|
+
session: getSession(self),
|
|
71
|
+
connectedViewId: self.id,
|
|
72
|
+
})) {
|
|
73
|
+
pushLaunchViewMenuItem(items, item)
|
|
74
|
+
}
|
|
75
|
+
return items
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
// The rubberband menu is short and contextual, so these go in
|
|
79
|
+
// flat rather than under a "Launch view" submenu — that grouping
|
|
80
|
+
// earns its keep in the long track and view menus, not here.
|
|
81
|
+
rubberBandMenuItems() {
|
|
82
|
+
return [
|
|
83
|
+
...superRubberBandMenuItems(),
|
|
84
|
+
...subgraphMenuItems({
|
|
85
|
+
label: SELECTION_LABEL,
|
|
86
|
+
region: regionFromViewport(
|
|
87
|
+
self.getSelectedRegions(
|
|
88
|
+
self.leftOffset,
|
|
89
|
+
self.rightOffset,
|
|
90
|
+
),
|
|
91
|
+
),
|
|
92
|
+
tracks: tracks(),
|
|
93
|
+
session: getSession(self),
|
|
94
|
+
connectedViewId: self.id,
|
|
95
|
+
}),
|
|
96
|
+
]
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
),
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
return pluggableElement
|
|
105
|
+
},
|
|
106
|
+
)
|
|
107
|
+
}
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import PluginManager from '@jbrowse/core/PluginManager'
|
|
2
|
+
import {
|
|
3
|
+
ConfigurationReference,
|
|
4
|
+
ConfigurationSchema,
|
|
5
|
+
getConf,
|
|
6
|
+
} from '@jbrowse/core/configuration'
|
|
7
|
+
import AdapterType from '@jbrowse/core/pluggableElementTypes/AdapterType'
|
|
8
|
+
import DisplayType from '@jbrowse/core/pluggableElementTypes/DisplayType'
|
|
9
|
+
import TrackType from '@jbrowse/core/pluggableElementTypes/TrackType'
|
|
10
|
+
import ViewType from '@jbrowse/core/pluggableElementTypes/ViewType'
|
|
11
|
+
import {
|
|
12
|
+
BaseDisplay,
|
|
13
|
+
createBaseTrackConfig,
|
|
14
|
+
createBaseTrackModel,
|
|
15
|
+
} from '@jbrowse/core/pluggableElementTypes/models'
|
|
16
|
+
import { LAUNCH_LABEL } from '@jbrowse/core/ui'
|
|
17
|
+
import { getContainingTrack } from '@jbrowse/core/util'
|
|
18
|
+
import { types } from '@jbrowse/mobx-state-tree'
|
|
19
|
+
import { linearGenomeViewStateModelFactory } from '@jbrowse/plugin-linear-genome-view'
|
|
20
|
+
|
|
21
|
+
import LaunchSubgraphMenuItemF from './index'
|
|
22
|
+
import LinearViewMenuItemsF from './linearViewMenuItems'
|
|
23
|
+
|
|
24
|
+
import type { MenuItem } from '@jbrowse/core/ui'
|
|
25
|
+
import type { Instance } from '@jbrowse/mobx-state-tree'
|
|
26
|
+
|
|
27
|
+
const LABEL = 'Graph genome view (this region)'
|
|
28
|
+
|
|
29
|
+
// A stand-in for core's MultiWaySyntenyDisplay with the four members the
|
|
30
|
+
// launch reads by duck type (subgraphTracks.ts): the lanes in force are the
|
|
31
|
+
// picker's choice, else the track's own lanes, and a hidden lane stays in
|
|
32
|
+
// force.
|
|
33
|
+
function laneDisplayType() {
|
|
34
|
+
const configSchema = ConfigurationSchema(
|
|
35
|
+
'MultiWaySyntenyDisplay',
|
|
36
|
+
{},
|
|
37
|
+
{ explicitIdentifier: 'displayId', explicitlyTyped: true },
|
|
38
|
+
)
|
|
39
|
+
const stateModel = types
|
|
40
|
+
.compose(
|
|
41
|
+
'MultiWaySyntenyDisplay',
|
|
42
|
+
BaseDisplay,
|
|
43
|
+
types.model({
|
|
44
|
+
type: types.literal('MultiWaySyntenyDisplay'),
|
|
45
|
+
configuration: ConfigurationReference(configSchema),
|
|
46
|
+
}),
|
|
47
|
+
)
|
|
48
|
+
.volatile(() => ({
|
|
49
|
+
picked: undefined as string[] | undefined,
|
|
50
|
+
hidden: [] as string[],
|
|
51
|
+
}))
|
|
52
|
+
.views(self => ({
|
|
53
|
+
get laneSelection(): readonly string[] | undefined {
|
|
54
|
+
const configured = getConf(
|
|
55
|
+
getContainingTrack(self),
|
|
56
|
+
'assemblyNames',
|
|
57
|
+
).slice(1)
|
|
58
|
+
return self.picked ?? (configured.length ? configured : undefined)
|
|
59
|
+
},
|
|
60
|
+
get hiddenLanes(): readonly string[] {
|
|
61
|
+
return self.hidden
|
|
62
|
+
},
|
|
63
|
+
trackMenuItems(): MenuItem[] {
|
|
64
|
+
return []
|
|
65
|
+
},
|
|
66
|
+
}))
|
|
67
|
+
.actions(self => ({
|
|
68
|
+
setSelectedLanes(names: string[] | undefined) {
|
|
69
|
+
self.picked = names
|
|
70
|
+
},
|
|
71
|
+
hideLane(name: string) {
|
|
72
|
+
self.hidden = [...self.hidden, name]
|
|
73
|
+
},
|
|
74
|
+
}))
|
|
75
|
+
return new DisplayType({
|
|
76
|
+
name: 'MultiWaySyntenyDisplay',
|
|
77
|
+
configSchema,
|
|
78
|
+
stateModel,
|
|
79
|
+
trackType: 'SyntenyTrack',
|
|
80
|
+
viewType: 'LinearGenomeView',
|
|
81
|
+
ReactComponent: () => null,
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function createEnv(trackLanes: string[] = []) {
|
|
86
|
+
console.warn = vi.fn()
|
|
87
|
+
console.error = vi.fn()
|
|
88
|
+
const pluginManager = new PluginManager()
|
|
89
|
+
pluginManager.addAdapterType(
|
|
90
|
+
() =>
|
|
91
|
+
new AdapterType({
|
|
92
|
+
name: 'GbzBaseSyntenyAdapter',
|
|
93
|
+
configSchema: ConfigurationSchema(
|
|
94
|
+
'GbzBaseSyntenyAdapter',
|
|
95
|
+
{},
|
|
96
|
+
{ explicitlyTyped: true },
|
|
97
|
+
),
|
|
98
|
+
adapterCapabilities: ['getSubgraph', 'headerLanes'],
|
|
99
|
+
getAdapterClass: () => {
|
|
100
|
+
throw new Error('config-only')
|
|
101
|
+
},
|
|
102
|
+
}),
|
|
103
|
+
)
|
|
104
|
+
pluginManager.addTrackType(() => {
|
|
105
|
+
const schema = ConfigurationSchema(
|
|
106
|
+
'SyntenyTrack',
|
|
107
|
+
{},
|
|
108
|
+
{
|
|
109
|
+
baseConfiguration: createBaseTrackConfig(pluginManager),
|
|
110
|
+
explicitIdentifier: 'trackId',
|
|
111
|
+
},
|
|
112
|
+
)
|
|
113
|
+
return new TrackType({
|
|
114
|
+
name: 'SyntenyTrack',
|
|
115
|
+
configSchema: schema,
|
|
116
|
+
stateModel: createBaseTrackModel(pluginManager, 'SyntenyTrack', schema),
|
|
117
|
+
})
|
|
118
|
+
})
|
|
119
|
+
pluginManager.addDisplayType(() => laneDisplayType())
|
|
120
|
+
pluginManager.addViewType(
|
|
121
|
+
() =>
|
|
122
|
+
new ViewType({
|
|
123
|
+
name: 'LinearGenomeView',
|
|
124
|
+
stateModel: linearGenomeViewStateModelFactory(pluginManager),
|
|
125
|
+
ReactComponent: () => null,
|
|
126
|
+
}),
|
|
127
|
+
)
|
|
128
|
+
LaunchSubgraphMenuItemF(pluginManager)
|
|
129
|
+
LinearViewMenuItemsF(pluginManager)
|
|
130
|
+
pluginManager.createPluggableElements()
|
|
131
|
+
pluginManager.configure()
|
|
132
|
+
|
|
133
|
+
const track = pluginManager.pluggableConfigSchemaType('track').create(
|
|
134
|
+
{
|
|
135
|
+
type: 'SyntenyTrack',
|
|
136
|
+
trackId: 'gbz_lanes',
|
|
137
|
+
name: 'GBZ lanes',
|
|
138
|
+
assemblyNames: ['volvox', ...trackLanes],
|
|
139
|
+
adapter: { type: 'GbzBaseSyntenyAdapter' },
|
|
140
|
+
displays: [
|
|
141
|
+
{
|
|
142
|
+
type: 'MultiWaySyntenyDisplay',
|
|
143
|
+
displayId: 'gbz_lanes-MultiWaySyntenyDisplay',
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
},
|
|
147
|
+
{ pluginManager },
|
|
148
|
+
)
|
|
149
|
+
const assemblyRegions = [
|
|
150
|
+
{ refName: 'ctgA', start: 0, end: 50_000, assemblyName: 'volvox' },
|
|
151
|
+
]
|
|
152
|
+
const assembly = {
|
|
153
|
+
initialized: true,
|
|
154
|
+
regions: assemblyRegions,
|
|
155
|
+
getCanonicalRefName: (refName: string) => refName,
|
|
156
|
+
getGeneticCodeId: () => undefined,
|
|
157
|
+
getRegionForRefName: (refName: string) =>
|
|
158
|
+
assemblyRegions.find(r => r.refName === refName),
|
|
159
|
+
configuration: { sequence: undefined },
|
|
160
|
+
}
|
|
161
|
+
const LGV = pluginManager.getViewType('LinearGenomeView').stateModel
|
|
162
|
+
const Session = types
|
|
163
|
+
.model({
|
|
164
|
+
name: 'testSession',
|
|
165
|
+
view: types.maybe(LGV),
|
|
166
|
+
configuration: types.map(types.frozen()),
|
|
167
|
+
})
|
|
168
|
+
.volatile(() => ({
|
|
169
|
+
tracks: [track],
|
|
170
|
+
connectionInstances: [],
|
|
171
|
+
addedViews: [] as [string, Record<string, unknown>][],
|
|
172
|
+
rpcManager: { call: vi.fn() },
|
|
173
|
+
assemblyManager: {
|
|
174
|
+
get: (name: string) => (name === 'volvox' ? assembly : undefined),
|
|
175
|
+
waitForAssembly: () => Promise.resolve(assembly),
|
|
176
|
+
isValidRefName: (refName: string) => refName === 'ctgA',
|
|
177
|
+
},
|
|
178
|
+
}))
|
|
179
|
+
.views(self => ({
|
|
180
|
+
getTrackById: (id: string) => (id === 'gbz_lanes' ? track : undefined),
|
|
181
|
+
get assemblies() {
|
|
182
|
+
return []
|
|
183
|
+
},
|
|
184
|
+
get views() {
|
|
185
|
+
return self.view ? [self.view] : []
|
|
186
|
+
},
|
|
187
|
+
getDisplayTypeDefault() {
|
|
188
|
+
return undefined
|
|
189
|
+
},
|
|
190
|
+
}))
|
|
191
|
+
.actions(self => ({
|
|
192
|
+
setView(view: Instance<typeof LGV>) {
|
|
193
|
+
self.view = view
|
|
194
|
+
return view
|
|
195
|
+
},
|
|
196
|
+
addView(type: string, snapshot: Record<string, unknown>) {
|
|
197
|
+
self.addedViews.push([type, snapshot])
|
|
198
|
+
return snapshot
|
|
199
|
+
},
|
|
200
|
+
notify() {},
|
|
201
|
+
notifyError() {},
|
|
202
|
+
}))
|
|
203
|
+
const session = Session.create({ configuration: {} }, { pluginManager })
|
|
204
|
+
const view = session.setView(
|
|
205
|
+
LGV.create({
|
|
206
|
+
type: 'LinearGenomeView',
|
|
207
|
+
tracks: [
|
|
208
|
+
{
|
|
209
|
+
type: 'SyntenyTrack',
|
|
210
|
+
configuration: 'gbz_lanes',
|
|
211
|
+
displays: [
|
|
212
|
+
{
|
|
213
|
+
type: 'MultiWaySyntenyDisplay',
|
|
214
|
+
configuration: 'gbz_lanes-MultiWaySyntenyDisplay',
|
|
215
|
+
},
|
|
216
|
+
],
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
}),
|
|
220
|
+
)
|
|
221
|
+
view.setWidth(800)
|
|
222
|
+
view.setDisplayedRegions([
|
|
223
|
+
{ refName: 'ctgA', start: 0, end: 1000, assemblyName: 'volvox' },
|
|
224
|
+
])
|
|
225
|
+
const display = view.tracks[0]!.displays[0]!
|
|
226
|
+
return { session, view, display }
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function allLabels(items: MenuItem[]): string[] {
|
|
230
|
+
return items.flatMap(item => [
|
|
231
|
+
...('label' in item ? [item.label] : []),
|
|
232
|
+
...('subMenu' in item && Array.isArray(item.subMenu)
|
|
233
|
+
? allLabels(item.subMenu)
|
|
234
|
+
: []),
|
|
235
|
+
])
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function launchRegion(view: { menuItems: () => MenuItem[] }) {
|
|
239
|
+
const launch = view
|
|
240
|
+
.menuItems()
|
|
241
|
+
.find(i => 'label' in i && i.label === LAUNCH_LABEL)
|
|
242
|
+
const item =
|
|
243
|
+
launch && 'subMenu' in launch
|
|
244
|
+
? launch.subMenu.find(i => 'label' in i && i.label === LABEL)
|
|
245
|
+
: undefined
|
|
246
|
+
if (item && 'onClick' in item) {
|
|
247
|
+
item.onClick(undefined)
|
|
248
|
+
} else {
|
|
249
|
+
throw new Error(`no ${LABEL} in the view menu`)
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// pangenome_hprc_part3 sends readers to the linear view's menu for this, since
|
|
254
|
+
// the lane track's own menu is core's and carries no graph launch.
|
|
255
|
+
test("the GBZ cut is on the linear view's menu, not the lane track's", () => {
|
|
256
|
+
const { view, display } = createEnv()
|
|
257
|
+
expect(allLabels(view.menuItems())).toContain(LABEL)
|
|
258
|
+
expect(allLabels(display.trackMenuItems())).not.toContain(LABEL)
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
test("a launch cuts for the lanes the reader picked over the track's lanes", () => {
|
|
262
|
+
const { session, view, display } = createEnv(['HG00097.1', 'HG00099.1'])
|
|
263
|
+
display.setSelectedLanes(['HG00128.1'])
|
|
264
|
+
launchRegion(view)
|
|
265
|
+
expect(session.addedViews[0]![1].subgraphHaplotypes).toEqual(['HG00128.1'])
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
test('a launch cuts for the pick where the track names no lanes', () => {
|
|
269
|
+
const { session, view, display } = createEnv()
|
|
270
|
+
display.setSelectedLanes(['HG00128.1'])
|
|
271
|
+
launchRegion(view)
|
|
272
|
+
expect(session.addedViews[0]![1].subgraphHaplotypes).toEqual(['HG00128.1'])
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
test('a lane hidden on the track is left out of the cut', () => {
|
|
276
|
+
const { session, view, display } = createEnv(['HG00097.1', 'HG00099.1'])
|
|
277
|
+
display.hideLane('HG00097.1')
|
|
278
|
+
launchRegion(view)
|
|
279
|
+
expect(session.addedViews[0]![1].subgraphHaplotypes).toEqual(['HG00099.1'])
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
test("with no pick a launch cuts for the track's lanes", () => {
|
|
283
|
+
const { session, view } = createEnv(['HG00097.1', 'HG00099.1'])
|
|
284
|
+
launchRegion(view)
|
|
285
|
+
expect(session.addedViews[0]![1].subgraphHaplotypes).toEqual([
|
|
286
|
+
'HG00097.1',
|
|
287
|
+
'HG00099.1',
|
|
288
|
+
])
|
|
289
|
+
})
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import BubbleChartIcon from '@mui/icons-material/BubbleChart'
|
|
2
|
+
|
|
3
|
+
import { launchSubgraphView, subgraphRegionProblem } from './launchSubgraphView'
|
|
4
|
+
import { offReferenceProblem } from './subgraphTracks'
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
SubgraphLaunchSession,
|
|
8
|
+
SubgraphRegion,
|
|
9
|
+
} from './launchSubgraphView'
|
|
10
|
+
import type { SubgraphTrack } from './subgraphTracks'
|
|
11
|
+
import type { MenuItem } from '@jbrowse/core/ui'
|
|
12
|
+
|
|
13
|
+
// Menu items that cut `region` out of each graph track that can supply it.
|
|
14
|
+
//
|
|
15
|
+
// One capable track is the common case and gets a single flat item — a submenu
|
|
16
|
+
// of one is a needless extra click. Several become a submenu naming each track,
|
|
17
|
+
// since which graph the subgraph comes from is then a real choice.
|
|
18
|
+
//
|
|
19
|
+
// A region a track can't be cut at, past the cap or off the graph's reference,
|
|
20
|
+
// yields a *disabled* item rather than none: an item that vanishes teaches the
|
|
21
|
+
// user nothing, while one greyed out with the reason in its tooltip says what
|
|
22
|
+
// to do about it.
|
|
23
|
+
export function subgraphMenuItems({
|
|
24
|
+
label,
|
|
25
|
+
region,
|
|
26
|
+
tracks,
|
|
27
|
+
session,
|
|
28
|
+
connectedViewId,
|
|
29
|
+
}: {
|
|
30
|
+
label: string
|
|
31
|
+
region: SubgraphRegion | undefined
|
|
32
|
+
tracks: SubgraphTrack[]
|
|
33
|
+
session: SubgraphLaunchSession
|
|
34
|
+
connectedViewId?: string
|
|
35
|
+
}): MenuItem[] {
|
|
36
|
+
let items: MenuItem[] = []
|
|
37
|
+
if (region && tracks.length > 0) {
|
|
38
|
+
const sizeProblem = subgraphRegionProblem(region)
|
|
39
|
+
const entry = (track: SubgraphTrack) => {
|
|
40
|
+
const problem =
|
|
41
|
+
offReferenceProblem(track.referenceAssembly, region.assemblyName) ??
|
|
42
|
+
sizeProblem
|
|
43
|
+
return {
|
|
44
|
+
disabled: problem !== undefined,
|
|
45
|
+
disabledHelpText: problem,
|
|
46
|
+
onClick: () => {
|
|
47
|
+
launchSubgraphView({
|
|
48
|
+
session,
|
|
49
|
+
region,
|
|
50
|
+
trackId: track.trackId,
|
|
51
|
+
connectedViewId,
|
|
52
|
+
haplotypes: track.haplotypes,
|
|
53
|
+
})
|
|
54
|
+
},
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
items =
|
|
58
|
+
tracks.length === 1
|
|
59
|
+
? [{ label, icon: BubbleChartIcon, ...entry(tracks[0]!) }]
|
|
60
|
+
: [
|
|
61
|
+
{
|
|
62
|
+
label,
|
|
63
|
+
icon: BubbleChartIcon,
|
|
64
|
+
type: 'subMenu',
|
|
65
|
+
subMenu: tracks.map(track => ({
|
|
66
|
+
label: track.name,
|
|
67
|
+
...entry(track),
|
|
68
|
+
})),
|
|
69
|
+
},
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
return items
|
|
73
|
+
}
|