jbrowse-plugin-msaview 3.7.0 → 3.7.1
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/README.md +44 -30
- package/dist/LaunchMsaViewExtensionPoint/index.js +25 -2
- package/dist/LaunchMsaViewExtensionPoint/index.test.js +45 -0
- package/dist/MsaViewPanel/model.d.ts +10 -11
- package/dist/MsaViewPanel/model.js +0 -17
- package/dist/jbrowse-plugin-msaview.umd.production.min.js +35 -33
- package/dist/jbrowse-plugin-msaview.umd.production.min.js.map +4 -4
- package/dist/utils/unirefHomologs.js +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -4
- package/src/LaunchMsaViewExtensionPoint/index.test.ts +48 -0
- package/src/LaunchMsaViewExtensionPoint/index.ts +44 -8
- package/src/MsaViewPanel/model.ts +0 -22
- package/src/utils/unirefHomologs.ts +1 -1
- package/src/version.ts +1 -1
- package/dist/MsaViewPanel/applyRegion.d.ts +0 -23
- package/dist/MsaViewPanel/applyRegion.js +0 -19
- package/dist/MsaViewPanel/applyRegion.test.d.ts +0 -1
- package/dist/MsaViewPanel/applyRegion.test.js +0 -45
- package/src/MsaViewPanel/applyRegion.test.ts +0 -55
- package/src/MsaViewPanel/applyRegion.ts +0 -33
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
// pantherOrthologs.ts produce, so the launch, the labels and the aligner do not
|
|
20
20
|
// know which source ran. `protein` is the UniProt accession, which is what the
|
|
21
21
|
// CDD overlay reads through efetch (Swiss-Prot rows only; see the PANTHER note
|
|
22
|
-
// in
|
|
22
|
+
// in docs/alignments-from-a-gene.md).
|
|
23
23
|
import { handleFetch, jsonfetch } from './fetch';
|
|
24
24
|
import { cleanGeneCandidate, dedupeLabels, defaultMaxSpecies, } from './ncbiOrthologs';
|
|
25
25
|
const UNIPROT = 'https://rest.uniprot.org';
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "3.7.
|
|
1
|
+
export declare const version = "3.7.1";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '3.7.
|
|
1
|
+
export const version = '3.7.1';
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "3.7.
|
|
2
|
+
"version": "3.7.1",
|
|
3
3
|
"license": "MIT",
|
|
4
4
|
"name": "jbrowse-plugin-msaview",
|
|
5
5
|
"repository": {
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"jsdom": "^30.1.0",
|
|
47
47
|
"mobx": "^7.0.3",
|
|
48
48
|
"mobx-react": "^10.0.2",
|
|
49
|
-
"msa-parsers": "^8.
|
|
49
|
+
"msa-parsers": "^8.2.0",
|
|
50
50
|
"oxfmt": "^0.68.0",
|
|
51
51
|
"oxlint": "^1.83.0",
|
|
52
52
|
"oxlint-tsgolint": "^7.0.2001",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"puppeteer": "^25.11.0",
|
|
57
57
|
"react": "^19.3.0",
|
|
58
58
|
"react-dom": "^19.3.0",
|
|
59
|
-
"react-msaview": "^8.
|
|
59
|
+
"react-msaview": "^8.2.0",
|
|
60
60
|
"rimraf": "^6.1.3",
|
|
61
61
|
"rxjs": "^7.8.2",
|
|
62
62
|
"serve": "^14.2.6",
|
|
@@ -87,10 +87,11 @@
|
|
|
87
87
|
"test:setup:version": "node scripts/test-versions.mjs setup",
|
|
88
88
|
"test:versions": "node scripts/test-versions.mjs run",
|
|
89
89
|
"test:version": "node scripts/test-versions.mjs run",
|
|
90
|
+
"readme-figure": "node scripts/readme-figure.mjs",
|
|
90
91
|
"host-compat": "node scripts/host-compat-probe.mjs --bundle dist/jbrowse-plugin-msaview.umd.production.min.js",
|
|
91
92
|
"check-ci": "node scripts/require-green-ci.mjs",
|
|
92
93
|
"preversion": "pnpm check-ci && pnpm check-host-dep-pins && pnpm lint && pnpm build && pnpm check-host-externals && pnpm host-compat",
|
|
93
|
-
"version": "node -e \"console.log('export const version = \\'' + require('./package.json').version + '\\'')\" > src/version.ts && git-cliff --tag v$npm_package_version --unreleased --prepend CHANGELOG.md && git add src/version.ts CHANGELOG.md",
|
|
94
|
+
"version": "node -e \"console.log('export const version = \\'' + require('./package.json').version + '\\'')\" > src/version.ts && git-cliff --tag v$npm_package_version --unreleased --prepend CHANGELOG.md && (pnpm readme-figure || echo 'WARNING: README figure not refreshed, see above') && git add src/version.ts CHANGELOG.md img/1.png",
|
|
94
95
|
"postversion": "git push --follow-tags"
|
|
95
96
|
}
|
|
96
97
|
}
|
|
@@ -88,3 +88,51 @@ test('one field set is enough to need init', () => {
|
|
|
88
88
|
querySeqName: 'QUERY',
|
|
89
89
|
})
|
|
90
90
|
})
|
|
91
|
+
|
|
92
|
+
test('msa and tree urls reach the plugin sources, query the query row', () => {
|
|
93
|
+
const snapshot = launch({
|
|
94
|
+
msa: 'https://example.com/p53.afa',
|
|
95
|
+
tree: 'https://example.com/p53.nh',
|
|
96
|
+
query: 'Human',
|
|
97
|
+
})
|
|
98
|
+
expect(snapshot.init).toMatchObject({
|
|
99
|
+
msaUrl: 'https://example.com/p53.afa',
|
|
100
|
+
querySeqName: 'Human',
|
|
101
|
+
})
|
|
102
|
+
expect(snapshot.treeFilehandle).toEqual({
|
|
103
|
+
uri: 'https://example.com/p53.nh',
|
|
104
|
+
locationType: 'UriLocation',
|
|
105
|
+
})
|
|
106
|
+
expect(snapshot.relativeTo).toBe('Human')
|
|
107
|
+
expect('msa' in snapshot || 'query' in snapshot).toBe(false)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
test('inline msa and newick text become inline data', () => {
|
|
111
|
+
const snapshot = launch({ msa: '>a\nMEEP\n>b\nMEEP', tree: '(a,b);' })
|
|
112
|
+
expect(snapshot.data).toEqual({ msa: '>a\nMEEP\n>b\nMEEP', tree: '(a,b);' })
|
|
113
|
+
expect('init' in snapshot).toBe(false)
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
test('highlights, region and column tracks expand onto the query row', () => {
|
|
117
|
+
const snapshot = launch({
|
|
118
|
+
msa: 'https://example.com/p53.afa',
|
|
119
|
+
query: 'Human',
|
|
120
|
+
highlights: ['102-292 DNA-binding', 175],
|
|
121
|
+
region: '170-290',
|
|
122
|
+
columnTracks: [{ name: 'ClinVar', start: 3, values: [2, 1] }],
|
|
123
|
+
})
|
|
124
|
+
expect(snapshot.highlights).toEqual([
|
|
125
|
+
{ row: 'Human', start: 102, end: 292, label: 'DNA-binding' },
|
|
126
|
+
{ row: 'Human', start: 175, end: 175, label: '{residue}{position}' },
|
|
127
|
+
])
|
|
128
|
+
expect(snapshot.region).toEqual({ row: 'Human', start: 170, end: 290 })
|
|
129
|
+
expect(snapshot.columnTracks).toEqual([
|
|
130
|
+
{
|
|
131
|
+
id: 'clinvar',
|
|
132
|
+
name: 'ClinVar',
|
|
133
|
+
kind: 'bar',
|
|
134
|
+
row: 'Human',
|
|
135
|
+
values: [0, 0, 2, 1],
|
|
136
|
+
},
|
|
137
|
+
])
|
|
138
|
+
})
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { expandSpec } from 'react-msaview'
|
|
2
|
+
|
|
1
3
|
import { launchMsaView } from '../utils/launchMsaView'
|
|
2
4
|
|
|
3
5
|
import type { BlastParams, OrthologParams } from '../MsaViewPanel/model'
|
|
@@ -6,16 +8,25 @@ import type PluginManager from '@jbrowse/core/PluginManager'
|
|
|
6
8
|
import type { AbstractSessionModel } from '@jbrowse/core/util'
|
|
7
9
|
import type {
|
|
8
10
|
Clade,
|
|
9
|
-
|
|
11
|
+
ColumnTrackShorthand,
|
|
10
12
|
Encoding,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
HighlightShorthand,
|
|
14
|
+
RegionShorthand,
|
|
13
15
|
ResidueMapping,
|
|
14
16
|
RowPanelSpec,
|
|
15
17
|
} from 'react-msaview'
|
|
16
18
|
|
|
17
19
|
interface LaunchMsaViewArgs {
|
|
18
20
|
session: AbstractSessionModel
|
|
21
|
+
/**
|
|
22
|
+
* Shorthand, see react-msaview's docs/layers.md: `msa` and `tree` take a
|
|
23
|
+
* url or the text itself, and `query` names the row the spec is about, the
|
|
24
|
+
* `querySeqName` and the `row` of every highlight, region and column track
|
|
25
|
+
* that names none
|
|
26
|
+
*/
|
|
27
|
+
msa?: string
|
|
28
|
+
tree?: string
|
|
29
|
+
query?: string
|
|
19
30
|
data?: { msa: string; tree?: string }
|
|
20
31
|
msaFileLocation?: { uri: string }
|
|
21
32
|
msaIndexedLocation?: { uri: string }
|
|
@@ -54,15 +65,15 @@ interface LaunchMsaViewArgs {
|
|
|
54
65
|
* for whole rows. Prefer this over `highlightColumns`: a residue range stays
|
|
55
66
|
* put when `allowedGappyness` changes the visible columns.
|
|
56
67
|
*/
|
|
57
|
-
highlights?:
|
|
68
|
+
highlights?: HighlightShorthand[]
|
|
58
69
|
/** per-column tracks supplied as data, see react-msaview's docs/layers.md */
|
|
59
|
-
columnTracks?:
|
|
70
|
+
columnTracks?: ColumnTrackShorthand[]
|
|
60
71
|
/**
|
|
61
72
|
* Where the view opens, in `highlights` coordinates: `{row, start, end}`
|
|
62
73
|
* zooms onto residues of that row, `{start, end}` onto alignment columns.
|
|
63
74
|
* Applied once the alignment has loaded, then dropped from the session.
|
|
64
75
|
*/
|
|
65
|
-
region?:
|
|
76
|
+
region?: RegionShorthand
|
|
66
77
|
/**
|
|
67
78
|
* Passed through untouched as react-msaview snapshot properties; see its
|
|
68
79
|
* docs/layers.md. A bare `{ uri }` is enough for the two filehandles.
|
|
@@ -121,6 +132,30 @@ interface LaunchMsaViewArgs {
|
|
|
121
132
|
placement?: MsaViewPlacement
|
|
122
133
|
}
|
|
123
134
|
|
|
135
|
+
/**
|
|
136
|
+
* `msa` and `tree` onto the plugin's own sources, so a url still goes through
|
|
137
|
+
* `init.msaUrl` and its format sniffing, and `query` onto `querySeqName`
|
|
138
|
+
*/
|
|
139
|
+
function withLongSources({ msa, tree, ...args }: LaunchMsaViewArgs) {
|
|
140
|
+
const inlineMsa = msa?.includes('\n') ? msa : undefined
|
|
141
|
+
const inlineTree = tree?.trimStart().startsWith('(') ? tree : undefined
|
|
142
|
+
const data =
|
|
143
|
+
inlineMsa || inlineTree
|
|
144
|
+
? {
|
|
145
|
+
...args.data,
|
|
146
|
+
...(inlineMsa ? { msa: inlineMsa } : {}),
|
|
147
|
+
...(inlineTree ? { tree: inlineTree } : {}),
|
|
148
|
+
}
|
|
149
|
+
: args.data
|
|
150
|
+
return {
|
|
151
|
+
...args,
|
|
152
|
+
data: data as LaunchMsaViewArgs['data'],
|
|
153
|
+
...(msa && !inlineMsa ? { msaFileLocation: { uri: msa } } : {}),
|
|
154
|
+
...(tree && !inlineTree ? { treeFileLocation: { uri: tree } } : {}),
|
|
155
|
+
querySeqName: args.querySeqName ?? args.query,
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
124
159
|
export default function LaunchMsaViewExtensionPointF(
|
|
125
160
|
pluginManager: PluginManager,
|
|
126
161
|
) {
|
|
@@ -129,6 +164,7 @@ export default function LaunchMsaViewExtensionPointF(
|
|
|
129
164
|
(args: LaunchMsaViewArgs) => {
|
|
130
165
|
const {
|
|
131
166
|
session,
|
|
167
|
+
query,
|
|
132
168
|
data,
|
|
133
169
|
msaFileLocation,
|
|
134
170
|
msaIndexedLocation,
|
|
@@ -137,7 +173,7 @@ export default function LaunchMsaViewExtensionPointF(
|
|
|
137
173
|
querySeqName,
|
|
138
174
|
searchParams,
|
|
139
175
|
...rest
|
|
140
|
-
} = args
|
|
176
|
+
} = withLongSources(args)
|
|
141
177
|
|
|
142
178
|
// `orthologParams` and `searchParams` name no alignment at all — the
|
|
143
179
|
// view builds one at launch, which is the dialog's Orthologs and BLAST
|
|
@@ -169,7 +205,7 @@ export default function LaunchMsaViewExtensionPointF(
|
|
|
169
205
|
querySeqName,
|
|
170
206
|
}
|
|
171
207
|
launchMsaView(session, {
|
|
172
|
-
...rest,
|
|
208
|
+
...expandSpec({ ...rest, query }),
|
|
173
209
|
...(searchParams ? { blastParams: searchParams } : {}),
|
|
174
210
|
data,
|
|
175
211
|
...(treeFileLocation
|
|
@@ -20,7 +20,6 @@ import {
|
|
|
20
20
|
storeDataToIndexedDB,
|
|
21
21
|
syncGenomeHoverToMsaColumn,
|
|
22
22
|
} from './afterCreateAutoruns'
|
|
23
|
-
import { applyRegion } from './applyRegion'
|
|
24
23
|
import {
|
|
25
24
|
msaCoordToGenomeCoord,
|
|
26
25
|
msaCoordToGenomeRegions,
|
|
@@ -40,7 +39,6 @@ import type { TranscriptRef } from './util'
|
|
|
40
39
|
import type { MenuItem } from '@jbrowse/core/ui'
|
|
41
40
|
import type { Instance } from '@jbrowse/mobx-state-tree'
|
|
42
41
|
import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
|
|
43
|
-
import type { Region } from 'react-msaview'
|
|
44
42
|
|
|
45
43
|
type LGV = LinearGenomeViewModel
|
|
46
44
|
|
|
@@ -246,14 +244,6 @@ export default function stateModelFactory() {
|
|
|
246
244
|
* #property
|
|
247
245
|
*/
|
|
248
246
|
mafRegion: types.frozen<MafRegion | undefined>(),
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* #property
|
|
252
|
-
* where the view opens: 1-based residues of `row`, or alignment
|
|
253
|
-
* columns without one. Zoomed onto once and then cleared, so a
|
|
254
|
-
* reloaded session keeps the reader's own scroll.
|
|
255
|
-
*/
|
|
256
|
-
region: types.frozen<Region | undefined>(),
|
|
257
247
|
}),
|
|
258
248
|
)
|
|
259
249
|
|
|
@@ -463,12 +453,6 @@ export default function stateModelFactory() {
|
|
|
463
453
|
setMafRegion(arg?: MafRegion) {
|
|
464
454
|
self.mafRegion = arg
|
|
465
455
|
},
|
|
466
|
-
/**
|
|
467
|
-
* #action
|
|
468
|
-
*/
|
|
469
|
-
setRegion(arg?: Region) {
|
|
470
|
-
self.region = arg
|
|
471
|
-
},
|
|
472
456
|
/**
|
|
473
457
|
* #action
|
|
474
458
|
*/
|
|
@@ -671,12 +655,6 @@ export default function stateModelFactory() {
|
|
|
671
655
|
// so they're factories returning the autorun body rather than plain fns
|
|
672
656
|
addDisposer(self, autorun(syncGenomeHoverToMsaColumn(self)))
|
|
673
657
|
addDisposer(self, autorun(observeProteinHighlights(self)))
|
|
674
|
-
addDisposer(
|
|
675
|
-
self,
|
|
676
|
-
autorun(() => {
|
|
677
|
-
applyRegion(self)
|
|
678
|
-
}),
|
|
679
|
-
)
|
|
680
658
|
},
|
|
681
659
|
}))
|
|
682
660
|
}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
// pantherOrthologs.ts produce, so the launch, the labels and the aligner do not
|
|
20
20
|
// know which source ran. `protein` is the UniProt accession, which is what the
|
|
21
21
|
// CDD overlay reads through efetch (Swiss-Prot rows only; see the PANTHER note
|
|
22
|
-
// in
|
|
22
|
+
// in docs/alignments-from-a-gene.md).
|
|
23
23
|
|
|
24
24
|
import { handleFetch, jsonfetch } from './fetch'
|
|
25
25
|
import {
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '3.7.
|
|
1
|
+
export const version = '3.7.1'
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { Region } from 'react-msaview';
|
|
2
|
-
interface RegionModel {
|
|
3
|
-
region?: Region;
|
|
4
|
-
viewInitialized: boolean;
|
|
5
|
-
numColumns: number;
|
|
6
|
-
treeFilehandle?: unknown;
|
|
7
|
-
data: {
|
|
8
|
-
tree?: string;
|
|
9
|
-
};
|
|
10
|
-
zoomToRegion: (region: Region) => void;
|
|
11
|
-
setRegion: (region?: Region) => void;
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Zoom onto the launch's `region` once, then forget it, so a reloaded session
|
|
15
|
-
* opens where the reader left it rather than back on the region.
|
|
16
|
-
*
|
|
17
|
-
* zoomToRegion is a no-op until the view has a width, and it resolves residues
|
|
18
|
-
* through the visible columns, so it waits for the alignment and for a tree
|
|
19
|
-
* file too: a clade the tree collapses hides columns and would move the
|
|
20
|
-
* target after the zoom.
|
|
21
|
-
*/
|
|
22
|
-
export declare function applyRegion(self: RegionModel): void;
|
|
23
|
-
export {};
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Zoom onto the launch's `region` once, then forget it, so a reloaded session
|
|
3
|
-
* opens where the reader left it rather than back on the region.
|
|
4
|
-
*
|
|
5
|
-
* zoomToRegion is a no-op until the view has a width, and it resolves residues
|
|
6
|
-
* through the visible columns, so it waits for the alignment and for a tree
|
|
7
|
-
* file too: a clade the tree collapses hides columns and would move the
|
|
8
|
-
* target after the zoom.
|
|
9
|
-
*/
|
|
10
|
-
export function applyRegion(self) {
|
|
11
|
-
const { region } = self;
|
|
12
|
-
if (region &&
|
|
13
|
-
self.viewInitialized &&
|
|
14
|
-
self.numColumns > 0 &&
|
|
15
|
-
!(self.treeFilehandle && !self.data.tree)) {
|
|
16
|
-
self.zoomToRegion(region);
|
|
17
|
-
self.setRegion(undefined);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { expect, test, vi } from 'vitest';
|
|
2
|
-
import { applyRegion } from './applyRegion';
|
|
3
|
-
const REGION = { row: 'Human', start: 245, end: 249 };
|
|
4
|
-
function makeModel(over = {}) {
|
|
5
|
-
const model = {
|
|
6
|
-
region: REGION,
|
|
7
|
-
viewInitialized: true,
|
|
8
|
-
numColumns: 400,
|
|
9
|
-
treeFilehandle: undefined,
|
|
10
|
-
data: {},
|
|
11
|
-
zoomToRegion: vi.fn(),
|
|
12
|
-
setRegion: vi.fn((arg) => {
|
|
13
|
-
model.region = arg;
|
|
14
|
-
}),
|
|
15
|
-
...over,
|
|
16
|
-
};
|
|
17
|
-
return model;
|
|
18
|
-
}
|
|
19
|
-
test('zooms onto the region once, then forgets it', () => {
|
|
20
|
-
const model = makeModel();
|
|
21
|
-
applyRegion(model);
|
|
22
|
-
applyRegion(model);
|
|
23
|
-
expect(model.zoomToRegion).toHaveBeenCalledTimes(1);
|
|
24
|
-
expect(model.zoomToRegion).toHaveBeenCalledWith(REGION);
|
|
25
|
-
expect(model.region).toBeUndefined();
|
|
26
|
-
});
|
|
27
|
-
test('waits for the view to have a width', () => {
|
|
28
|
-
const model = makeModel({ viewInitialized: false });
|
|
29
|
-
applyRegion(model);
|
|
30
|
-
expect(model.zoomToRegion).not.toHaveBeenCalled();
|
|
31
|
-
expect(model.region).toEqual(REGION);
|
|
32
|
-
});
|
|
33
|
-
test('waits for the alignment', () => {
|
|
34
|
-
const model = makeModel({ numColumns: 0 });
|
|
35
|
-
applyRegion(model);
|
|
36
|
-
expect(model.zoomToRegion).not.toHaveBeenCalled();
|
|
37
|
-
});
|
|
38
|
-
test('waits for a tree file, whose collapsed clades move the columns', () => {
|
|
39
|
-
const model = makeModel({ treeFilehandle: { uri: 'x.nh' } });
|
|
40
|
-
applyRegion(model);
|
|
41
|
-
expect(model.zoomToRegion).not.toHaveBeenCalled();
|
|
42
|
-
model.data.tree = '(a,b);';
|
|
43
|
-
applyRegion(model);
|
|
44
|
-
expect(model.zoomToRegion).toHaveBeenCalledTimes(1);
|
|
45
|
-
});
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { expect, test, vi } from 'vitest'
|
|
2
|
-
|
|
3
|
-
import { applyRegion } from './applyRegion'
|
|
4
|
-
|
|
5
|
-
import type { Region } from 'react-msaview'
|
|
6
|
-
|
|
7
|
-
const REGION: Region = { row: 'Human', start: 245, end: 249 }
|
|
8
|
-
|
|
9
|
-
function makeModel(over: Record<string, unknown> = {}) {
|
|
10
|
-
const model = {
|
|
11
|
-
region: REGION as Region | undefined,
|
|
12
|
-
viewInitialized: true,
|
|
13
|
-
numColumns: 400,
|
|
14
|
-
treeFilehandle: undefined as unknown,
|
|
15
|
-
data: {} as { tree?: string },
|
|
16
|
-
zoomToRegion: vi.fn(),
|
|
17
|
-
setRegion: vi.fn((arg?: Region) => {
|
|
18
|
-
model.region = arg
|
|
19
|
-
}),
|
|
20
|
-
...over,
|
|
21
|
-
}
|
|
22
|
-
return model
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
test('zooms onto the region once, then forgets it', () => {
|
|
26
|
-
const model = makeModel()
|
|
27
|
-
applyRegion(model)
|
|
28
|
-
applyRegion(model)
|
|
29
|
-
expect(model.zoomToRegion).toHaveBeenCalledTimes(1)
|
|
30
|
-
expect(model.zoomToRegion).toHaveBeenCalledWith(REGION)
|
|
31
|
-
expect(model.region).toBeUndefined()
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
test('waits for the view to have a width', () => {
|
|
35
|
-
const model = makeModel({ viewInitialized: false })
|
|
36
|
-
applyRegion(model)
|
|
37
|
-
expect(model.zoomToRegion).not.toHaveBeenCalled()
|
|
38
|
-
expect(model.region).toEqual(REGION)
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
test('waits for the alignment', () => {
|
|
42
|
-
const model = makeModel({ numColumns: 0 })
|
|
43
|
-
applyRegion(model)
|
|
44
|
-
expect(model.zoomToRegion).not.toHaveBeenCalled()
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
test('waits for a tree file, whose collapsed clades move the columns', () => {
|
|
48
|
-
const model = makeModel({ treeFilehandle: { uri: 'x.nh' } })
|
|
49
|
-
applyRegion(model)
|
|
50
|
-
expect(model.zoomToRegion).not.toHaveBeenCalled()
|
|
51
|
-
|
|
52
|
-
model.data.tree = '(a,b);'
|
|
53
|
-
applyRegion(model)
|
|
54
|
-
expect(model.zoomToRegion).toHaveBeenCalledTimes(1)
|
|
55
|
-
})
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import type { Region } from 'react-msaview'
|
|
2
|
-
|
|
3
|
-
interface RegionModel {
|
|
4
|
-
region?: Region
|
|
5
|
-
viewInitialized: boolean
|
|
6
|
-
numColumns: number
|
|
7
|
-
treeFilehandle?: unknown
|
|
8
|
-
data: { tree?: string }
|
|
9
|
-
zoomToRegion: (region: Region) => void
|
|
10
|
-
setRegion: (region?: Region) => void
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Zoom onto the launch's `region` once, then forget it, so a reloaded session
|
|
15
|
-
* opens where the reader left it rather than back on the region.
|
|
16
|
-
*
|
|
17
|
-
* zoomToRegion is a no-op until the view has a width, and it resolves residues
|
|
18
|
-
* through the visible columns, so it waits for the alignment and for a tree
|
|
19
|
-
* file too: a clade the tree collapses hides columns and would move the
|
|
20
|
-
* target after the zoom.
|
|
21
|
-
*/
|
|
22
|
-
export function applyRegion(self: RegionModel) {
|
|
23
|
-
const { region } = self
|
|
24
|
-
if (
|
|
25
|
-
region &&
|
|
26
|
-
self.viewInitialized &&
|
|
27
|
-
self.numColumns > 0 &&
|
|
28
|
-
!(self.treeFilehandle && !self.data.tree)
|
|
29
|
-
) {
|
|
30
|
-
self.zoomToRegion(region)
|
|
31
|
-
self.setRegion(undefined)
|
|
32
|
-
}
|
|
33
|
-
}
|