react-msaview 6.4.2 → 7.0.0

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.
Files changed (212) hide show
  1. package/README.md +6 -5
  2. package/bundle/index.js +100 -101
  3. package/bundle/index.js.map +4 -4
  4. package/dist/components/Loading.js +2 -2
  5. package/dist/components/Loading.js.map +1 -1
  6. package/dist/components/MSAViewer.d.ts +10 -2
  7. package/dist/components/MSAViewer.js +90 -2
  8. package/dist/components/MSAViewer.js.map +1 -1
  9. package/dist/components/ResizeHandles.d.ts +3 -3
  10. package/dist/components/ResizeHandles.js +28 -21
  11. package/dist/components/ResizeHandles.js.map +1 -1
  12. package/dist/components/dialogs/{InterProScanDialog.d.ts → AnnotationFileDialog.d.ts} +2 -2
  13. package/dist/components/dialogs/AnnotationFileDialog.js +92 -0
  14. package/dist/components/dialogs/AnnotationFileDialog.js.map +1 -0
  15. package/dist/components/dialogs/FeatureDialog.js +13 -9
  16. package/dist/components/dialogs/FeatureDialog.js.map +1 -1
  17. package/dist/components/header/Header.js +4 -0
  18. package/dist/components/header/Header.js.map +1 -1
  19. package/dist/components/header/HeaderStatusArea.js +0 -2
  20. package/dist/components/header/HeaderStatusArea.js.map +1 -1
  21. package/dist/components/header/LoadWarnings.d.ts +6 -0
  22. package/dist/components/header/LoadWarnings.js +36 -0
  23. package/dist/components/header/LoadWarnings.js.map +1 -0
  24. package/dist/components/header/MultiAlignmentSelector.js +0 -0
  25. package/dist/components/header/MultiAlignmentSelector.js.map +1 -1
  26. package/dist/components/header/TreeSettingsMenu.js +9 -1
  27. package/dist/components/header/TreeSettingsMenu.js.map +1 -1
  28. package/dist/components/header/UnshareableDataWarning.d.ts +6 -0
  29. package/dist/components/header/UnshareableDataWarning.js +42 -0
  30. package/dist/components/header/UnshareableDataWarning.js.map +1 -0
  31. package/dist/components/header/getAnnotationsMenu.js +14 -14
  32. package/dist/components/header/getAnnotationsMenu.js.map +1 -1
  33. package/dist/components/import/ImportForm.js +1 -1
  34. package/dist/components/import/ImportForm.js.map +1 -1
  35. package/dist/components/import/ImportFormExamples.js +2 -6
  36. package/dist/components/import/ImportFormExamples.js.map +1 -1
  37. package/dist/components/import/util.js +3 -0
  38. package/dist/components/import/util.js.map +1 -1
  39. package/dist/components/msa/MSACanvasBlock.js +4 -5
  40. package/dist/components/msa/MSACanvasBlock.js.map +1 -1
  41. package/dist/components/msa/msaRaster.d.ts +13 -3
  42. package/dist/components/msa/msaRaster.js +173 -61
  43. package/dist/components/msa/msaRaster.js.map +1 -1
  44. package/dist/components/msa/renderHighlights.d.ts +39 -1
  45. package/dist/components/msa/renderHighlights.js +71 -6
  46. package/dist/components/msa/renderHighlights.js.map +1 -1
  47. package/dist/components/msa/renderMSABlock.d.ts +1 -2
  48. package/dist/components/msa/renderMSABlock.js +24 -28
  49. package/dist/components/msa/renderMSABlock.js.map +1 -1
  50. package/dist/components/msa/renderMSAMouseover.js +9 -30
  51. package/dist/components/msa/renderMSAMouseover.js.map +1 -1
  52. package/dist/components/renderCtx.d.ts +1 -1
  53. package/dist/components/tracks/TrackBlocks.js +10 -11
  54. package/dist/components/tracks/TrackBlocks.js.map +1 -1
  55. package/dist/components/tracks/drawTracks.d.ts +26 -8
  56. package/dist/components/tracks/drawTracks.js +123 -13
  57. package/dist/components/tracks/drawTracks.js.map +1 -1
  58. package/dist/components/tree/TreeCanvas.js +2 -3
  59. package/dist/components/tree/TreeCanvas.js.map +1 -1
  60. package/dist/components/tree/TreeCanvasBlock.js +4 -2
  61. package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
  62. package/dist/components/tree/TreeRuler.js +13 -3
  63. package/dist/components/tree/TreeRuler.js.map +1 -1
  64. package/dist/components/tree/renderTreeCanvas.d.ts +0 -1
  65. package/dist/components/tree/renderTreeCanvas.js +70 -30
  66. package/dist/components/tree/renderTreeCanvas.js.map +1 -1
  67. package/dist/components/tree/scaleBar.d.ts +10 -0
  68. package/dist/components/tree/scaleBar.js +24 -0
  69. package/dist/components/tree/scaleBar.js.map +1 -0
  70. package/dist/constants.d.ts +3 -1
  71. package/dist/constants.js +18 -1
  72. package/dist/constants.js.map +1 -1
  73. package/dist/fetchUtils.d.ts +7 -7
  74. package/dist/fetchUtils.js +4 -32
  75. package/dist/fetchUtils.js.map +1 -1
  76. package/dist/hierarchy.d.ts +2 -1
  77. package/dist/hierarchy.js +16 -10
  78. package/dist/hierarchy.js.map +1 -1
  79. package/dist/index.d.ts +2 -1
  80. package/dist/index.js +8 -2
  81. package/dist/index.js.map +1 -1
  82. package/dist/model/DataModel.js +5 -2
  83. package/dist/model/DataModel.js.map +1 -1
  84. package/dist/model.d.ts +454 -177
  85. package/dist/model.js +1180 -511
  86. package/dist/model.js.map +1 -1
  87. package/dist/renderToStaticMarkup.d.ts +11 -0
  88. package/dist/renderToStaticMarkup.js +30 -7
  89. package/dist/renderToStaticMarkup.js.map +1 -1
  90. package/dist/renderToSvg.js +25 -19
  91. package/dist/renderToSvg.js.map +1 -1
  92. package/dist/rowCoordinateCalculations.d.ts +25 -25
  93. package/dist/rowCoordinateCalculations.js +12 -36
  94. package/dist/rowCoordinateCalculations.js.map +1 -1
  95. package/dist/seqPosToGlobalCol.js +3 -1
  96. package/dist/seqPosToGlobalCol.js.map +1 -1
  97. package/dist/svgTestUtil.d.ts +144 -82
  98. package/dist/types.d.ts +97 -3
  99. package/dist/util.d.ts +11 -4
  100. package/dist/util.js +30 -10
  101. package/dist/util.js.map +1 -1
  102. package/dist/version.d.ts +1 -1
  103. package/dist/version.js +1 -1
  104. package/dist/wuss.d.ts +14 -0
  105. package/dist/wuss.js +55 -0
  106. package/dist/wuss.js.map +1 -0
  107. package/package.json +10 -8
  108. package/src/components/Loading.tsx +12 -2
  109. package/src/components/MSAViewer.tsx +106 -2
  110. package/src/components/ResizeHandles.tsx +31 -31
  111. package/src/components/dialogs/AnnotationFileDialog.tsx +162 -0
  112. package/src/components/dialogs/FeatureDialog.tsx +13 -9
  113. package/src/components/header/Header.tsx +4 -0
  114. package/src/components/header/HeaderStatusArea.tsx +0 -5
  115. package/src/components/header/LoadWarnings.tsx +49 -0
  116. package/src/components/header/MultiAlignmentSelector.tsx +0 -0
  117. package/src/components/header/TreeSettingsMenu.tsx +9 -1
  118. package/src/components/header/UnshareableDataWarning.tsx +58 -0
  119. package/src/components/header/getAnnotationsMenu.ts +16 -17
  120. package/src/components/import/ImportForm.tsx +2 -2
  121. package/src/components/import/ImportFormExamples.tsx +2 -6
  122. package/src/components/import/util.ts +3 -0
  123. package/src/components/msa/MSACanvasBlock.tsx +4 -5
  124. package/src/components/msa/msaRaster.ts +216 -59
  125. package/src/components/msa/renderHighlights.ts +103 -5
  126. package/src/components/msa/renderMSABlock.ts +25 -36
  127. package/src/components/msa/renderMSAMouseover.ts +9 -39
  128. package/src/components/renderCtx.ts +1 -0
  129. package/src/components/tracks/TrackBlocks.tsx +10 -13
  130. package/src/components/tracks/drawTracks.ts +165 -17
  131. package/src/components/tree/TreeCanvas.tsx +2 -3
  132. package/src/components/tree/TreeCanvasBlock.tsx +4 -2
  133. package/src/components/tree/TreeRuler.tsx +38 -3
  134. package/src/components/tree/renderTreeCanvas.ts +79 -35
  135. package/src/components/tree/scaleBar.ts +23 -0
  136. package/src/constants.ts +20 -1
  137. package/src/fetchUtils.ts +7 -40
  138. package/src/hierarchy.ts +20 -11
  139. package/src/index.ts +18 -2
  140. package/src/model/DataModel.ts +6 -2
  141. package/src/model.ts +1352 -616
  142. package/src/rawImports.d.ts +9 -0
  143. package/src/renderToStaticMarkup.ts +34 -7
  144. package/src/renderToSvg.tsx +33 -27
  145. package/src/rowCoordinateCalculations.ts +15 -59
  146. package/src/seqPosToGlobalCol.ts +3 -1
  147. package/src/types.ts +131 -4
  148. package/src/util.ts +31 -14
  149. package/src/version.ts +1 -1
  150. package/src/wuss.ts +62 -0
  151. package/dist/components/dialogs/InterProScanDialog.js +0 -137
  152. package/dist/components/dialogs/InterProScanDialog.js.map +0 -1
  153. package/dist/components/dialogs/InterProScanFileDialog.d.ts +0 -7
  154. package/dist/components/dialogs/InterProScanFileDialog.js +0 -64
  155. package/dist/components/dialogs/InterProScanFileDialog.js.map +0 -1
  156. package/dist/launchInterProScan.d.ts +0 -13
  157. package/dist/launchInterProScan.js +0 -110
  158. package/dist/launchInterProScan.js.map +0 -1
  159. package/dist/useColorContrast.d.ts +0 -10
  160. package/dist/useColorContrast.js +0 -13
  161. package/dist/useColorContrast.js.map +0 -1
  162. package/src/clustalX.test.ts +0 -74
  163. package/src/colSpace.test.ts +0 -67
  164. package/src/collapseLogic.test.ts +0 -115
  165. package/src/colorSchemes.docs.test.ts +0 -57
  166. package/src/columnCounts.test.ts +0 -73
  167. package/src/columnStats.test.ts +0 -94
  168. package/src/columnTracks.test.ts +0 -97
  169. package/src/components/dialogs/InterProScanDialog.tsx +0 -214
  170. package/src/components/dialogs/InterProScanFileDialog.tsx +0 -138
  171. package/src/components/minimap/minimapLayout.test.ts +0 -71
  172. package/src/components/msa/msaRaster.test.ts +0 -145
  173. package/src/components/msa/renderBoxFeatureCanvasBlock.test.ts +0 -51
  174. package/src/components/tree/renderTreeCanvas.test.ts +0 -296
  175. package/src/dataInitialized.test.ts +0 -42
  176. package/src/domainBands.test.ts +0 -122
  177. package/src/domainLetterColor.test.ts +0 -80
  178. package/src/exportFileName.test.ts +0 -40
  179. package/src/fetchUtils.test.ts +0 -83
  180. package/src/flatToTree.test.ts +0 -51
  181. package/src/hierarchy.test.ts +0 -228
  182. package/src/highlights.test.ts +0 -70
  183. package/src/impgRender.test.tsx +0 -68
  184. package/src/largeTree.test.ts +0 -67
  185. package/src/launchInterProScan.ts +0 -167
  186. package/src/loadErrorDisplay.test.tsx +0 -102
  187. package/src/modelFilehandleLoaders.test.ts +0 -137
  188. package/src/modelReset.test.ts +0 -120
  189. package/src/neighborJoining.test.ts +0 -164
  190. package/src/parseAsn1.test.ts +0 -36
  191. package/src/propertyConservation.test.ts +0 -36
  192. package/src/renderColumnTracksSvg.test.tsx +0 -108
  193. package/src/renderDomainsSvg.test.tsx +0 -118
  194. package/src/renderHighlightsSvg.test.tsx +0 -85
  195. package/src/renderMinimapSvg.test.tsx +0 -62
  196. package/src/renderRasterSvg.test.tsx +0 -144
  197. package/src/renderSequenceLogoSvg.test.tsx +0 -165
  198. package/src/renderToSvg.test.tsx +0 -65
  199. package/src/reset.test.ts +0 -61
  200. package/src/rowCoordinateCalculations.test.ts +0 -211
  201. package/src/rowSpace.test.ts +0 -46
  202. package/src/segmentOverlay.test.ts +0 -70
  203. package/src/seqPosToGlobalCol.test.ts +0 -59
  204. package/src/sequenceLogo.test.ts +0 -88
  205. package/src/setFontSize.test.ts +0 -33
  206. package/src/stripDefault.test.ts +0 -48
  207. package/src/stripDefaultSnapshot.test.ts +0 -87
  208. package/src/treeMetadata.test.ts +0 -39
  209. package/src/useCanvasAutorun.test.tsx +0 -98
  210. package/src/useColorContrast.ts +0 -18
  211. package/src/viewChrome.test.tsx +0 -134
  212. package/src/wheelZoom.test.ts +0 -46
@@ -1,57 +0,0 @@
1
- import { readFileSync } from 'node:fs'
2
-
3
- import { expect, test } from 'vitest'
4
-
5
- import colorSchemes from './colorSchemes.ts'
6
-
7
- // Guard against doc drift: every color-scheme name listed in the JS embedding
8
- // docs (USAGE.md) must be a real registered scheme, so a rename or removal in
9
- // colorSchemes.ts can't silently leave a dead name in the docs that users copy.
10
- // (Subset check only — a registered scheme may intentionally go undocumented.)
11
- test('color schemes listed in USAGE.md all exist', () => {
12
- const registered = new Set(Object.keys(colorSchemes))
13
- const usage = readFileSync(
14
- new URL('../../../USAGE.md', import.meta.url),
15
- 'utf8',
16
- )
17
-
18
- const section = /## Color schemes\n([\s\S]*?)\n## /.exec(usage)?.[1]
19
- expect(section, 'could not find the "## Color schemes" section').toBeTruthy()
20
-
21
- // drop the bold labels (**Protein:**, **Dynamic (per-column):**, …), leaving
22
- // just the comma-separated scheme names
23
- const names = section!.replace(/\*\*[^*]+\*\*/g, '').match(/[a-z][a-z0-9_]+/g)
24
- expect(names?.length).toBeGreaterThan(0)
25
-
26
- const unknown = names!.filter(name => !registered.has(name))
27
- expect(
28
- unknown,
29
- `documented but not registered: ${unknown.join(', ')}`,
30
- ).toEqual([])
31
- })
32
-
33
- // Same guard for the R package docs, whose color_scheme names pass straight
34
- // through as colorSchemeName with no remapping — a name that isn't registered
35
- // silently renders with no coloring.
36
- test('color schemes listed in the R README all exist', () => {
37
- const registered = new Set(Object.keys(colorSchemes))
38
- const readme = readFileSync(
39
- new URL('../../r-msaview/README.md', import.meta.url),
40
- 'utf8',
41
- )
42
-
43
- const section = /## Color schemes\n([\s\S]*?)\n## /.exec(readme)?.[1]
44
- expect(section, 'could not find the "## Color schemes" section').toBeTruthy()
45
-
46
- // names are backtick-quoted, e.g. `jalview_taylor`
47
- const names = [...section!.matchAll(/`([a-z][a-z0-9_]+)`/g)].flatMap(m =>
48
- m[1] ? [m[1]] : [],
49
- )
50
- expect(names.length).toBeGreaterThan(0)
51
-
52
- const unknown = names.filter(name => !registered.has(name))
53
- expect(
54
- unknown,
55
- `documented but not registered: ${unknown.join(', ')}`,
56
- ).toEqual([])
57
- })
@@ -1,73 +0,0 @@
1
- import { describe, expect, test } from 'vitest'
2
-
3
- import {
4
- columnCountsFromColumns,
5
- columnCountsFromRows,
6
- letterOfResidueSlot,
7
- } from './columnCounts.ts'
8
-
9
- describe('columnCounts', () => {
10
- test('tallies residues and gaps per column', () => {
11
- const counts = columnCountsFromRows(['AC-', 'AG.', 'AC-'])
12
- expect(counts.numColumns).toBe(3)
13
- expect(counts.count(0, 'A')).toBe(3)
14
- expect(counts.count(1, 'C')).toBe(2)
15
- expect(counts.count(1, 'G')).toBe(1)
16
- expect(counts.total(2)).toBe(3)
17
- expect(counts.gapCount(2)).toBe(3)
18
- expect(counts.gapCount(0)).toBe(0)
19
- })
20
-
21
- test('counts both gap characters, and neither as a residue', () => {
22
- const counts = columnCountsFromColumns(['-.A'])
23
- expect(counts.count(0, '-')).toBe(1)
24
- expect(counts.count(0, '.')).toBe(1)
25
- expect(counts.gapCount(0)).toBe(2)
26
- expect(counts.residueEntries(0)).toEqual([['A', 1]])
27
- expect(counts.lettersPresent).toEqual(new Set(['A']))
28
- })
29
-
30
- test('folds case, so lowercase (a3m insertions) counts as its residue', () => {
31
- const counts = columnCountsFromColumns(['aA'])
32
- expect(counts.count(0, 'A')).toBe(2)
33
- })
34
-
35
- test('digits and punctuation do not land on letter slots', () => {
36
- // a plain `code & 31` would fold '0' onto 'P' and '!' onto 'A'
37
- const counts = columnCountsFromColumns(['0!'])
38
- expect(counts.count(0, 'P')).toBe(0)
39
- expect(counts.count(0, 'A')).toBe(0)
40
- expect(counts.residueEntries(0)).toEqual([['?', 2]])
41
- })
42
-
43
- test('ragged rows: the widest row sets the column count', () => {
44
- const counts = columnCountsFromRows(['AAAA', 'AA'])
45
- expect(counts.numColumns).toBe(4)
46
- expect(counts.total(0)).toBe(2)
47
- expect(counts.total(3)).toBe(1)
48
- })
49
-
50
- test('entropy is taken over the non-gap residues only', () => {
51
- const counts = columnCountsFromColumns(['AAAA', 'AACC', 'ACGT', 'AC--'])
52
- // fully conserved: no choice left to make
53
- expect(counts.entropy(0)).toBeCloseTo(0, 10)
54
- // even two-way and four-way splits
55
- expect(counts.entropy(1)).toBeCloseTo(1, 10)
56
- expect(counts.entropy(2)).toBeCloseTo(2, 10)
57
- // the two gaps do not turn a 1-bit choice into a 2-bit one
58
- expect(counts.entropy(3)).toBeCloseTo(1, 10)
59
- })
60
-
61
- test('an all-gap column has no entropy rather than NaN', () => {
62
- expect(columnCountsFromColumns(['----']).entropy(0)).toBe(0)
63
- })
64
-
65
- test('residue slots round-trip to their letters', () => {
66
- const counts = columnCountsFromColumns(['WY'])
67
- const slots: number[] = []
68
- counts.forEachResidue(0, slot => {
69
- slots.push(slot)
70
- })
71
- expect(slots.map(letterOfResidueSlot)).toEqual(['W', 'Y'])
72
- })
73
- })
@@ -1,94 +0,0 @@
1
- // @vitest-environment jsdom
2
- import { beforeAll, describe, expect, test } from 'vitest'
3
-
4
- import MSAModelF from './model.ts'
5
-
6
- beforeAll(() => {
7
- HTMLCanvasElement.prototype.getContext = function () {
8
- let font = '10px sans-serif'
9
- return {
10
- get font() {
11
- return font
12
- },
13
- set font(v: string) {
14
- font = v
15
- },
16
- measureText(t: string) {
17
- const size = Number.parseFloat(font) || 10
18
- return { width: t.length * size * 0.6 } as TextMetrics
19
- },
20
- } as unknown as CanvasRenderingContext2D
21
- } as unknown as typeof HTMLCanvasElement.prototype.getContext
22
- })
23
-
24
- // col0 is invariant M; col3 varies V/V/A (all hydrophobic) so property
25
- // conservation stays high while identity conservation drops
26
- const protein = `>a
27
- MKLVIL
28
- >b
29
- MRLVIL
30
- >c
31
- MKLAIL`
32
-
33
- const dna = `>a
34
- ACGTAC
35
- >b
36
- ACGTAG
37
- >c
38
- ACGAAC`
39
-
40
- function make(msa: string, id: string) {
41
- return MSAModelF().create({
42
- id,
43
- type: 'MsaView',
44
- msaFormat: 'fasta',
45
- data: { msa },
46
- })
47
- }
48
-
49
- describe('property conservation track', () => {
50
- test('present for protein alignments', () => {
51
- const ids = make(protein, 'p').tracks.map(t => t.model.id)
52
- expect(ids).toContain('conservation')
53
- expect(ids).toContain('property-conservation')
54
- })
55
-
56
- test('omitted for nucleotide alignments', () => {
57
- const ids = make(dna, 'd').tracks.map(t => t.model.id)
58
- expect(ids).toContain('conservation')
59
- expect(ids).not.toContain('property-conservation')
60
- })
61
-
62
- test('conservative-substitution column: property beats identity', () => {
63
- const model = make(protein, 'pc')
64
- // col3 = V/V/A: same property class, different identity
65
- expect(model.propertyConservation[3]!).toBeGreaterThan(
66
- model.conservation[3]!,
67
- )
68
- })
69
- })
70
-
71
- describe('mouseOverColumnStats', () => {
72
- test('undefined when nothing hovered', () => {
73
- expect(make(protein, 'm0').mouseOverColumnStats).toBeUndefined()
74
- })
75
-
76
- test('reports consensus, gaps and distribution for hovered column', () => {
77
- const model = make(protein, 'm1')
78
- model.setMousePos(0, 0)
79
- const s = model.mouseOverColumnStats
80
- expect(s?.consensusLetter).toBe('M')
81
- expect(s?.consensusFraction).toBe(1)
82
- expect(s?.gapFraction).toBe(0)
83
- expect(s?.total).toBe(3)
84
- })
85
-
86
- test('mixed column reports the majority residue as consensus', () => {
87
- const model = make(protein, 'm2')
88
- // col1 = K/R/K -> consensus K at 2/3
89
- model.setMousePos(1, 0)
90
- const s = model.mouseOverColumnStats
91
- expect(s?.consensusLetter).toBe('K')
92
- expect(s?.consensusCount).toBe(2)
93
- })
94
- })
@@ -1,97 +0,0 @@
1
- import { getSnapshot } from '@jbrowse/mobx-state-tree'
2
- import { expect, test } from 'vitest'
3
-
4
- import stateModelFactory from './model.ts'
5
-
6
- import type { ColumnTrackSpec } from './types.ts'
7
-
8
- const MsaView = stateModelFactory()
9
-
10
- // s1 has a gap at column 1 and s2 a gap at column 3, and every row is gapped at
11
- // column 5, so hiding gappy columns drops that one
12
- const msa = '>s1\nA-CDE-F\n>s2\nAB-DE-F\n>s3\nABCDE-F\n'
13
-
14
- function makeModel(columnTracks: ColumnTrackSpec[]) {
15
- return MsaView.create({
16
- type: 'MsaView',
17
- msaFormat: 'fasta',
18
- data: { msa },
19
- columnTracks,
20
- })
21
- }
22
-
23
- test('column-indexed values normalize by max and clamp to 0..1', () => {
24
- const model = makeModel([
25
- { id: 't', name: 'T', kind: 'bar', values: [1, 2, 4, -1], max: 2 },
26
- ])
27
- expect(model.columnTrackContent.get('t')?.values).toEqual([0.5, 1, 1, 0])
28
- })
29
-
30
- test('row-indexed values land on the residues of that row', () => {
31
- const model = makeModel([
32
- { id: 't', name: 'T', kind: 'bar', values: [0.1, 0.2, 0.3], row: 's1' },
33
- ])
34
- // residues 1..3 of s1 are A, C, D at global columns 0, 2, 3
35
- expect(model.columnTrackContent.get('t')?.values).toEqual([
36
- 0.1, 0, 0.2, 0.3, 0, 0, 0,
37
- ])
38
- })
39
-
40
- test('a row-indexed text track spreads its letters the same way', () => {
41
- const model = makeModel([
42
- { id: 'f', name: 'Frame', kind: 'text', data: '123', row: 's2' },
43
- ])
44
- // residues of s2 sit at columns 0, 1, 3
45
- expect(model.columnTrackContent.get('f')?.data).toBe('12 3 ')
46
- })
47
-
48
- test('hiding gappy columns drops them from the values and the text', () => {
49
- const model = makeModel([
50
- { id: 't', name: 'T', kind: 'bar', values: [1, 2, 3, 4, 5, 6, 7], max: 7 },
51
- { id: 'f', name: 'F', kind: 'text', data: 'abcdefg' },
52
- ])
53
- model.setHideGaps(true)
54
- model.setAllowedGappyness(99)
55
- expect(model.blanks).toEqual([5])
56
- expect(model.columnTrackContent.get('t')?.values).toHaveLength(6)
57
- expect(model.columnTrackContent.get('f')?.data).toBe('abcdeg')
58
- })
59
-
60
- test('data tracks join the track list with their own color and height', () => {
61
- const model = makeModel([
62
- { id: 't', name: 'dN/dS', kind: 'bar', values: [1], color: 'red' },
63
- { id: 'f', name: 'Frame', kind: 'text', data: '1', colors: { 1: 'blue' } },
64
- ])
65
- const ids = model.tracks.map(t => t.model.id)
66
- expect(ids).toContain('t')
67
- expect(ids).toContain('f')
68
- const bar = model.tracks.find(t => t.model.id === 't')!.model
69
- expect(bar.barColor).toBe('red')
70
- expect(bar.height).toBe(model.conservationTrackHeight)
71
- const text = model.tracks.find(t => t.model.id === 'f')!.model
72
- expect(text.customColorScheme).toEqual({ 1: 'blue' })
73
- expect(text.height).toBe(model.rowHeight)
74
- expect(text.data).toBe('1')
75
-
76
- expect(model.turnedOnTracks.map(t => t.model.id)).toContain('t')
77
- model.toggleTrack('t')
78
- expect(model.turnedOnTracks.map(t => t.model.id)).not.toContain('t')
79
- })
80
-
81
- test('setColumnTracks replaces the set and the snapshot carries it', () => {
82
- const model = makeModel([])
83
- expect(getSnapshot(model)).not.toHaveProperty('columnTracks')
84
- model.setColumnTracks([{ id: 't', name: 'T', kind: 'bar', values: [0.5] }])
85
- expect(getSnapshot(model).columnTracks).toEqual([
86
- { id: 't', name: 'T', kind: 'bar', values: [0.5] },
87
- ])
88
- })
89
-
90
- test('a track too large to share leaves the snapshot but keeps drawing', () => {
91
- const model = makeModel([
92
- { id: 'big', name: 'Big', kind: 'bar', values: Array(20_000).fill(0.5) },
93
- { id: 'small', name: 'Small', kind: 'bar', values: [0.5] },
94
- ])
95
- expect(model.columnTrackContent.get('big')?.values).toHaveLength(20_000)
96
- expect(getSnapshot(model).columnTracks?.map(t => t.id)).toEqual(['small'])
97
- })
@@ -1,214 +0,0 @@
1
- import React, { useState } from 'react'
2
-
3
- import { Dialog } from '@jbrowse/core/ui'
4
- import { getSession } from '@jbrowse/core/util'
5
- import { Button, DialogActions, DialogContent, Typography } from '@mui/material'
6
- import { observer } from 'mobx-react'
7
- import { getUngappedSequence } from 'msa-parsers'
8
-
9
- import { launchInterProScan } from '../../launchInterProScan.ts'
10
-
11
- import type { MsaViewModel } from '../../model.ts'
12
-
13
- const FAMILIES = 'Families, domains, sites & repeats'
14
- const FEATURES = 'Other sequence features'
15
- const STRUCTURAL = 'Structural domains'
16
- const OTHER = 'Other category'
17
-
18
- // The analyses the EBI iprscan5 endpoint accepts, all on by default. Static, so
19
- // it lives out here rather than being rebuilt on every render of the dialog.
20
- const interProScanPrograms = [
21
- {
22
- name: 'NCBIfam',
23
- category: FAMILIES,
24
- description: 'NCBI RefSeq FAMs including TIGRFAMs',
25
- },
26
- {
27
- name: 'SFLD',
28
- category: FAMILIES,
29
- description: 'Structure function linkage database',
30
- },
31
- {
32
- name: 'Phobius',
33
- category: FEATURES,
34
- description:
35
- 'A combined transmembrane topology and signal peptide predictor',
36
- },
37
- { name: 'SignalP', category: FEATURES },
38
- { name: 'SignalP_EUK', category: OTHER },
39
- { name: 'SignalP_GRAM_POSITIVE', category: OTHER },
40
- { name: 'SignalP_GRAM_NEGATIVE', category: OTHER },
41
- { name: 'SuperFamily', category: STRUCTURAL },
42
- { name: 'Panther', category: FAMILIES },
43
- { name: 'Gene3d', category: STRUCTURAL },
44
- { name: 'HAMAP', category: FAMILIES },
45
- { name: 'PrositeProfiles', category: FAMILIES },
46
- { name: 'PrositePatterns', category: FAMILIES },
47
- { name: 'Coils', category: FEATURES },
48
- { name: 'SMART', category: FAMILIES },
49
- {
50
- name: 'CDD',
51
- category: FAMILIES,
52
- description: 'Conserved Domains Database',
53
- },
54
- { name: 'PRINTS', category: FAMILIES },
55
- { name: 'PfamA', category: FAMILIES },
56
- { name: 'MobiDBLite', category: FEATURES },
57
- { name: 'PIRSF', category: OTHER },
58
- { name: 'TMHMM', category: FEATURES },
59
- { name: 'AntiFam', category: OTHER },
60
- { name: 'FunFam', category: OTHER },
61
- { name: 'PIRSR', category: FAMILIES },
62
- ].map(program => ({ ...program, checked: true }))
63
-
64
- const InterProScanDialog = observer(function ({
65
- handleClose,
66
- model,
67
- }: {
68
- handleClose: () => void
69
- model: MsaViewModel
70
- }) {
71
- const [vals, setVals] = useState(interProScanPrograms)
72
-
73
- const programs = vals.filter(e => e.checked).map(e => e.name)
74
- const [show, setShow] = useState(false)
75
-
76
- return (
77
- <Dialog
78
- maxWidth="xl"
79
- title="Query InterProScan API for domains"
80
- onClose={() => {
81
- handleClose()
82
- }}
83
- open
84
- >
85
- <DialogContent>
86
- <Typography>
87
- This will run InterProScan via the InterProScan API on all rows of the
88
- current MSA
89
- </Typography>
90
- <Button
91
- onClick={() => {
92
- setShow(!show)
93
- }}
94
- >
95
- {show ? 'Hide' : 'Show'} advanced options
96
- </Button>
97
- {show ? (
98
- <div>
99
- <Typography>Select algorithms for InterProScan to run</Typography>
100
- <div>
101
- <Button
102
- variant="contained"
103
- color="secondary"
104
- onClick={() => {
105
- setVals(vals.map(v => ({ ...v, checked: false })))
106
- }}
107
- >
108
- Select none
109
- </Button>
110
- <Button
111
- variant="contained"
112
- color="primary"
113
- onClick={() => {
114
- setVals(vals.map(v => ({ ...v, checked: true })))
115
- }}
116
- >
117
- Select all
118
- </Button>
119
- </div>
120
- <table>
121
- <tbody>
122
- {vals
123
- .toSorted((a, b) => a.category.localeCompare(b.category))
124
- .map(({ name, checked, category, description }) => (
125
- <tr key={name}>
126
- <td>
127
- <input
128
- type="checkbox"
129
- checked={checked}
130
- onChange={() => {
131
- setVals(
132
- vals.map(e =>
133
- e.name === name
134
- ? { ...e, checked: !e.checked }
135
- : e,
136
- ),
137
- )
138
- }}
139
- />
140
- </td>
141
- <td title={description}>{name}</td>
142
- <td>{category}</td>
143
- </tr>
144
- ))}
145
- </tbody>
146
- </table>
147
- </div>
148
- ) : null}
149
- </DialogContent>
150
- <DialogActions>
151
- <Button
152
- variant="contained"
153
- color="secondary"
154
- onClick={() => {
155
- handleClose()
156
- }}
157
- >
158
- Cancel
159
- </Button>
160
- <Button
161
- variant="contained"
162
- color="primary"
163
- onClick={() => {
164
- const controller = new AbortController()
165
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
166
- ;(async () => {
167
- try {
168
- const { rows } = model
169
- if (rows.length > 140) {
170
- throw new Error(
171
- 'Too many sequences, please run InterProScan offline',
172
- )
173
- }
174
- await launchInterProScan({
175
- algorithm: 'interproscan',
176
- programs: programs,
177
- seq: rows
178
- .map(row => [row[0], getUngappedSequence(row[1])])
179
- .filter(f => !!f[1])
180
- .map(row => `>${row[0]}\n${row[1]}`)
181
- .join('\n'),
182
- onProgress: arg => {
183
- model.setStatus(
184
- arg
185
- ? {
186
- ...arg,
187
- onCancel: () => {
188
- controller.abort()
189
- },
190
- }
191
- : undefined,
192
- )
193
- },
194
- model,
195
- signal: controller.signal,
196
- })
197
- } catch (e) {
198
- console.error(e)
199
- getSession(model).notifyError(`${e}`, e)
200
- } finally {
201
- model.setStatus()
202
- }
203
- })()
204
- handleClose()
205
- }}
206
- >
207
- Send sequences to InterProScan
208
- </Button>
209
- </DialogActions>
210
- </Dialog>
211
- )
212
- })
213
-
214
- export default InterProScanDialog
@@ -1,138 +0,0 @@
1
- import React, { useState } from 'react'
2
-
3
- import { Dialog } from '@jbrowse/core/ui'
4
- import { getSession } from '@jbrowse/core/util'
5
- import {
6
- Button,
7
- DialogActions,
8
- DialogContent,
9
- FormControl,
10
- FormControlLabel,
11
- Radio,
12
- RadioGroup,
13
- TextField,
14
- Typography,
15
- } from '@mui/material'
16
- import { observer } from 'mobx-react'
17
- import { interProScanResponseToAnnotations } from 'msa-parsers'
18
-
19
- import { jsonfetch } from '../../fetchUtils.ts'
20
-
21
- import type { MsaViewModel } from '../../model.ts'
22
- import type { InterProScanResponse } from 'msa-parsers'
23
-
24
- const InterProScanFileDialog = observer(function ({
25
- handleClose,
26
- model,
27
- }: {
28
- handleClose: () => void
29
- model: MsaViewModel
30
- }) {
31
- const [file, setFile] = useState<File>()
32
- const [choice, setChoice] = useState('file')
33
- const [interProURL, setInterProURL] = useState('')
34
-
35
- return (
36
- <Dialog
37
- maxWidth="xl"
38
- title="Open InterProScan results"
39
- onClose={() => {
40
- handleClose()
41
- }}
42
- open
43
- >
44
- <DialogContent>
45
- <div>
46
- <Typography>
47
- Open a JSON file of InterProScan results that you run remotely on
48
- EBI servers or locally
49
- </Typography>
50
-
51
- <div style={{ display: 'flex', margin: 30 }}>
52
- <FormControl component="fieldset">
53
- <RadioGroup
54
- value={choice}
55
- onChange={event => {
56
- setChoice(event.target.value)
57
- }}
58
- >
59
- <FormControlLabel value="url" control={<Radio />} label="URL" />
60
- <FormControlLabel
61
- value="file"
62
- control={<Radio />}
63
- label="File"
64
- />
65
- </RadioGroup>
66
- </FormControl>
67
- {choice === 'url' ? (
68
- <div>
69
- <Typography>
70
- Open a InterProScan JSON file remote URL
71
- </Typography>
72
- <TextField
73
- label="URL"
74
- value={interProURL}
75
- onChange={event => {
76
- setInterProURL(event.target.value)
77
- }}
78
- />
79
- </div>
80
- ) : null}
81
- {choice === 'file' ? (
82
- <div style={{ paddingTop: 20 }}>
83
- <Typography>
84
- Open a InterProScan JSON file file from your local drive
85
- </Typography>
86
- <Button variant="outlined" component="label">
87
- Choose File
88
- <input
89
- type="file"
90
- hidden
91
- onChange={({ target }) => {
92
- const file = target.files?.[0]
93
- if (file) {
94
- setFile(file)
95
- }
96
- }}
97
- />
98
- </Button>
99
- </div>
100
- ) : null}
101
- </div>
102
- </div>
103
- </DialogContent>
104
- <DialogActions>
105
- <Button
106
- variant="contained"
107
- color="primary"
108
- onClick={() => {
109
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
110
- ;(async () => {
111
- try {
112
- const ret: InterProScanResponse = file
113
- ? JSON.parse(await file.text())
114
- : await jsonfetch(interProURL)
115
-
116
- model.setAnnotations(interProScanResponseToAnnotations(ret))
117
- getSession(model).notify(
118
- 'Loaded interproscan results',
119
- 'success',
120
- )
121
- } catch (e) {
122
- console.error(e)
123
- getSession(model).notifyError(`${e}`, e)
124
- } finally {
125
- model.setStatus()
126
- }
127
- })()
128
- handleClose()
129
- }}
130
- >
131
- Open results
132
- </Button>
133
- </DialogActions>
134
- </Dialog>
135
- )
136
- })
137
-
138
- export default InterProScanFileDialog