@jbrowse/plugin-variants 2.3.1 → 2.3.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.
Files changed (51) hide show
  1. package/dist/VcfAdapter/VcfAdapter.d.ts +1 -2
  2. package/dist/VcfAdapter/VcfAdapter.js +2 -2
  3. package/dist/VcfAdapter/VcfAdapter.js.map +1 -1
  4. package/dist/{VcfTabixAdapter/VcfFeature.d.ts → VcfFeature/index.d.ts} +3 -15
  5. package/dist/VcfFeature/index.js +59 -0
  6. package/dist/VcfFeature/index.js.map +1 -0
  7. package/dist/VcfFeature/util.d.ts +7 -0
  8. package/dist/VcfFeature/util.js +129 -0
  9. package/dist/VcfFeature/util.js.map +1 -0
  10. package/dist/VcfTabixAdapter/VcfTabixAdapter.d.ts +2 -19
  11. package/dist/VcfTabixAdapter/VcfTabixAdapter.js +1 -35
  12. package/dist/VcfTabixAdapter/VcfTabixAdapter.js.map +1 -1
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.js +1 -1
  15. package/dist/index.js.map +1 -1
  16. package/esm/VcfAdapter/VcfAdapter.d.ts +1 -2
  17. package/esm/VcfAdapter/VcfAdapter.js +2 -2
  18. package/esm/VcfAdapter/VcfAdapter.js.map +1 -1
  19. package/esm/{VcfTabixAdapter/VcfFeature.d.ts → VcfFeature/index.d.ts} +3 -15
  20. package/esm/VcfFeature/index.js +56 -0
  21. package/esm/VcfFeature/index.js.map +1 -0
  22. package/esm/VcfFeature/util.d.ts +7 -0
  23. package/esm/VcfFeature/util.js +123 -0
  24. package/esm/VcfFeature/util.js.map +1 -0
  25. package/esm/VcfTabixAdapter/VcfTabixAdapter.d.ts +2 -19
  26. package/esm/VcfTabixAdapter/VcfTabixAdapter.js +1 -35
  27. package/esm/VcfTabixAdapter/VcfTabixAdapter.js.map +1 -1
  28. package/esm/index.d.ts +1 -1
  29. package/esm/index.js +1 -1
  30. package/esm/index.js.map +1 -1
  31. package/package.json +3 -3
  32. package/src/VariantFeatureWidget/VariantFeatureWidget.test.tsx +42 -0
  33. package/src/VariantFeatureWidget/__snapshots__/{VariantFeatureWidget.test.js.snap → VariantFeatureWidget.test.tsx.snap} +1 -1
  34. package/src/VcfAdapter/VcfAdapter.test.ts +4 -5
  35. package/src/VcfAdapter/VcfAdapter.ts +3 -4
  36. package/src/VcfFeature/index.test.ts +132 -0
  37. package/src/VcfFeature/index.ts +104 -0
  38. package/src/VcfFeature/util.ts +140 -0
  39. package/src/VcfTabixAdapter/VcfTabixAdapter.test.ts +9 -10
  40. package/src/VcfTabixAdapter/VcfTabixAdapter.ts +3 -51
  41. package/src/__snapshots__/{index.test.js.snap → index.test.ts.snap} +0 -0
  42. package/src/{index.test.js → index.test.ts} +0 -1
  43. package/src/index.ts +1 -1
  44. package/dist/VcfTabixAdapter/VcfFeature.js +0 -187
  45. package/dist/VcfTabixAdapter/VcfFeature.js.map +0 -1
  46. package/esm/VcfTabixAdapter/VcfFeature.js +0 -184
  47. package/esm/VcfTabixAdapter/VcfFeature.js.map +0 -1
  48. package/src/LinearVariantDisplay/configSchema.test.js +0 -55
  49. package/src/VariantFeatureWidget/VariantFeatureWidget.test.js +0 -41
  50. package/src/VcfTabixAdapter/VcfFeature.test.ts +0 -118
  51. package/src/VcfTabixAdapter/VcfFeature.ts +0 -250
@@ -1,55 +0,0 @@
1
- import BoxRendererType from '@jbrowse/core/pluggableElementTypes/renderers/BoxRendererType'
2
- import Plugin from '@jbrowse/core/Plugin'
3
- import PluginManager from '@jbrowse/core/PluginManager'
4
- import PileupRenderer from '@jbrowse/plugin-alignments/src/PileupRenderer'
5
- import {
6
- configSchema as svgFeatureRendererConfigSchema,
7
- ReactComponent as SvgFeatureRendererReactComponent,
8
- } from '@jbrowse/plugin-svg/src/SvgFeatureRenderer'
9
- import configSchemaFactory from './configSchema'
10
-
11
- // mock warnings to avoid unnecessary outputs
12
- beforeEach(() => {
13
- jest.spyOn(console, 'warn').mockImplementation(() => {})
14
- })
15
-
16
- afterEach(() => {
17
- console.warn.mockRestore()
18
- })
19
-
20
- class PileupRendererPlugin extends Plugin {
21
- install(pluginManager) {
22
- PileupRenderer(pluginManager)
23
- }
24
- }
25
-
26
- class SvgFeatureRendererPlugin extends Plugin {
27
- install(pluginManager) {
28
- pluginManager.addRendererType(
29
- () =>
30
- new BoxRendererType({
31
- name: 'SvgFeatureRenderer',
32
- ReactComponent: SvgFeatureRendererReactComponent,
33
- configSchema: svgFeatureRendererConfigSchema,
34
- pluginManager,
35
- }),
36
- )
37
- }
38
- }
39
-
40
- test('has a viewType attr', () => {
41
- const configSchema = configSchemaFactory(
42
- new PluginManager([
43
- new PileupRendererPlugin(),
44
- new SvgFeatureRendererPlugin(),
45
- ])
46
- .createPluggableElements()
47
- .configure(),
48
- )
49
- const config = configSchema.create({
50
- type: 'LinearVariantDisplay',
51
- displayId: 'displayId0',
52
- name: 'Zonker Display',
53
- })
54
- expect(config.type).toEqual('LinearVariantDisplay')
55
- })
@@ -1,41 +0,0 @@
1
- import React from 'react'
2
- import { render } from '@testing-library/react'
3
- import { types } from 'mobx-state-tree'
4
- import { ConfigurationSchema } from '@jbrowse/core/configuration'
5
- import PluginManager from '@jbrowse/core/PluginManager'
6
- import { stateModelFactory } from '.'
7
-
8
- import VariantFeatureDetails from './VariantFeatureWidget'
9
-
10
- describe('VariantTrack widget', () => {
11
- it('renders with just the required model elements', () => {
12
- console.warn = jest.fn()
13
- const pluginManager = new PluginManager([])
14
- const Session = types.model({
15
- rpcManager: types.optional(types.frozen(), {}),
16
- configuration: ConfigurationSchema('test', {}),
17
- widget: stateModelFactory(pluginManager),
18
- })
19
- const model = Session.create(
20
- {
21
- widget: { type: 'VariantFeatureWidget' },
22
- },
23
- { pluginManager },
24
- )
25
- model.widget.setFeatureData({
26
- refName: 'ctgA',
27
- start: 176,
28
- end: 177,
29
- name: 'rs123',
30
- REF: 'A',
31
- ALT: ['<TRA>'],
32
- QUAL: 10.4,
33
- INFO: {
34
- MQ: 5,
35
- },
36
- })
37
-
38
- const { container } = render(<VariantFeatureDetails model={model.widget} />)
39
- expect(container.firstChild).toMatchSnapshot()
40
- })
41
- })
@@ -1,118 +0,0 @@
1
- import VcfParser from '@gmod/vcf'
2
- import VcfFeature from './VcfFeature'
3
-
4
- test('test usage of the VcfFeature', () => {
5
- const parser = new VcfParser({
6
- header: `#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\tBAMs/caudaus.sorted.sam`,
7
- })
8
- const line = `lcl|Scaffald_1\t80465\trs118266897\tR\tA\t29\tPASS\tNS=3;0,14;AF=0.5;DB;112;PG2.1`
9
-
10
- const variant = parser.parseLine(line)
11
-
12
- const f = new VcfFeature({
13
- parser,
14
- variant,
15
- id: 'myuniqueid',
16
- })
17
- expect(f.id()).toEqual('myuniqueid')
18
- expect(f.get('name')).toEqual('rs118266897')
19
- })
20
-
21
- test('try INS feature with END less than start', () => {
22
- const parser = new VcfParser({
23
- header: `#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\tBAMs/caudaus.sorted.sam`,
24
- })
25
- const line = `chr1\t100\trs123\tR\tA\t29\tPASS\tEND=1;SVTYPE=INS`
26
-
27
- const variant = parser.parseLine(line)
28
-
29
- const f = new VcfFeature({
30
- parser,
31
- variant,
32
- id: 'myuniqueid',
33
- })
34
- expect(f.id()).toEqual('myuniqueid')
35
- expect(f.get('start')).toEqual(99)
36
- expect(f.get('end')).toEqual(100)
37
- })
38
-
39
- test('try DEL feature with END info field valid', () => {
40
- const parser = new VcfParser({
41
- header: `#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\tBAMs/caudaus.sorted.sam`,
42
- })
43
- const line = `chr1\t100\trs123\tR\t<DEL>\t29\tPASS\tEND=1000;SVTYPE=DEL`
44
-
45
- const variant = parser.parseLine(line)
46
-
47
- const f = new VcfFeature({
48
- parser,
49
- variant,
50
- id: 'myuniqueid',
51
- })
52
- expect(f.id()).toEqual('myuniqueid')
53
- expect(f.get('start')).toEqual(99)
54
- expect(f.get('end')).toEqual(1000)
55
- })
56
-
57
- describe('test SV description', () => {
58
- it('multiple SVs', () => {
59
- const parser = new VcfParser({
60
- header: `#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\tBAMs/caudaus.sorted.sam`,
61
- })
62
- const line = `chr1\t100\trs123\tR\t<INVDUP>,<INV>\t29\tPASS\tEND=1000;SVTYPE=DEL`
63
-
64
- const variant = parser.parseLine(line)
65
-
66
- const f = new VcfFeature({
67
- parser,
68
- variant,
69
- id: 'myuniqueid',
70
- })
71
- expect(f.get('description')).toEqual('<INVDUP>,<INV>')
72
- })
73
- it('BND', () => {
74
- const parser = new VcfParser({
75
- header: `#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\tBAMs/caudaus.sorted.sam`,
76
- })
77
- const line = `chr1\t100\trs123\tR\tG[ctgA:34200[\t29\tPASS\tEND=1000;SVTYPE=BND`
78
-
79
- const variant = parser.parseLine(line)
80
-
81
- const f = new VcfFeature({
82
- parser,
83
- variant,
84
- id: 'myuniqueid',
85
- })
86
- expect(f.get('description')).toEqual('G[ctgA:34200[')
87
- })
88
- it('multiple BND', () => {
89
- const parser = new VcfParser({
90
- header: `#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\tBAMs/caudaus.sorted.sam`,
91
- })
92
- const line = `chr1\t100\trs123\tR\tG[ctgA:34200[,G[ctgA:44200[\t29\tPASS\tEND=1000;SVTYPE=BND`
93
-
94
- const variant = parser.parseLine(line)
95
-
96
- const f = new VcfFeature({
97
- parser,
98
- variant,
99
- id: 'myuniqueid',
100
- })
101
- expect(f.get('description')).toEqual('G[ctgA:34200[,G[ctgA:44200[')
102
- })
103
- it('multiple SNV', () => {
104
- const parser = new VcfParser({
105
- header: `#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT\tBAMs/caudaus.sorted.sam`,
106
- })
107
- const line = `chr1\t100\trs123\tG\tA,C\t29\tPASS\tHELLO=world`
108
-
109
- const variant = parser.parseLine(line)
110
-
111
- const f = new VcfFeature({
112
- parser,
113
- variant,
114
- id: 'myuniqueid',
115
- })
116
- expect(f.get('description')).toEqual('SNV G -> A,C')
117
- })
118
- })
@@ -1,250 +0,0 @@
1
- import { Feature } from '@jbrowse/core/util/simpleFeature'
2
- import { parseBreakend } from '@gmod/vcf'
3
-
4
- /* eslint-disable no-underscore-dangle */
5
-
6
- /**
7
- * VCF Feature creation with lazy genotype evaluation.
8
- */
9
- interface Samples {
10
- [key: string]: {
11
- [key: string]: { values: string[] | number[] | null }
12
- }
13
- }
14
-
15
- interface FeatureData {
16
- [key: string]: unknown
17
- refName: string
18
- start: number
19
- end: number
20
- description?: string
21
- type?: string
22
- name?: string
23
- aliases?: string[]
24
- samples?: Samples
25
- }
26
-
27
- export default class VCFFeature implements Feature {
28
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
29
- private variant: any
30
-
31
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
32
- private parser: any
33
-
34
- private data: FeatureData
35
-
36
- private _id: string
37
-
38
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
39
- constructor(args: { variant: any; parser: any; id: string }) {
40
- this.variant = args.variant
41
- this.parser = args.parser
42
- this.data = this.dataFromVariant(this.variant)
43
- this._id = args.id
44
- }
45
-
46
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
47
- get(field: string): any {
48
- return field === 'samples'
49
- ? this.variant.SAMPLES
50
- : this.data[field] || this.variant[field]
51
- }
52
-
53
- set(): void {}
54
-
55
- parent(): undefined {
56
- return undefined
57
- }
58
-
59
- children(): undefined {
60
- return undefined
61
- }
62
-
63
- tags(): string[] {
64
- const t = [
65
- ...Object.keys(this.data),
66
- ...Object.keys(this.variant),
67
- 'samples',
68
- ]
69
- return t
70
- }
71
-
72
- id(): string {
73
- return this._id
74
- }
75
-
76
- dataFromVariant(variant: {
77
- REF: string
78
- POS: number
79
- ALT: string[]
80
- CHROM: string
81
- INFO: any // eslint-disable-line @typescript-eslint/no-explicit-any
82
- ID: string[]
83
- }): FeatureData {
84
- const { REF, ALT, POS, CHROM, INFO, ID } = variant
85
- const start = POS - 1
86
- const [SO_term, description] = this._getSOTermAndDescription(REF, ALT)
87
- const isTRA = ALT?.some(f => f === '<TRA>')
88
- const isSymbolic = ALT?.some(f => f.indexOf('<') !== -1)
89
-
90
- return {
91
- refName: CHROM,
92
- start,
93
- end: isSymbolic && INFO.END && !isTRA ? +INFO.END[0] : start + REF.length,
94
- description,
95
- type: SO_term,
96
- name: ID?.join(','),
97
- aliases: ID && ID.length > 1 ? variant.ID.slice(1) : undefined,
98
- }
99
- }
100
-
101
- /**
102
- * Get a sequence ontology (SO) term that describes the variant type
103
- */
104
- _getSOTermAndDescription(
105
- ref: string,
106
- alt: string[],
107
- ): [string, string] | [undefined, undefined] {
108
- // it's just a remark if there are no alternate alleles
109
- if (!alt || alt.length === 0) {
110
- return ['remark', 'no alternative alleles']
111
- }
112
-
113
- const soTerms = new Set<string>()
114
- let descriptions = new Set<string>()
115
- alt.forEach(a => {
116
- let [soTerm, description] = this._getSOAndDescFromAltDefs(ref, a)
117
-
118
- if (!soTerm) {
119
- ;[soTerm, description] = this._getSOAndDescByExamination(ref, a)
120
- }
121
- if (soTerm && description) {
122
- soTerms.add(soTerm)
123
- descriptions.add(description)
124
- }
125
- })
126
-
127
- // Combine descriptions like ["SNV G -> A", "SNV G -> T"] to ["SNV G -> A,T"]
128
- if (descriptions.size > 1) {
129
- const prefixes = new Set(
130
- [...descriptions].map(desc => {
131
- const prefix = desc.split('->')
132
- return prefix[1] ? prefix[0] : desc
133
- }),
134
- )
135
-
136
- const new_descs = [...prefixes].map(prefix => {
137
- const suffixes = [...descriptions]
138
- .map(desc => {
139
- const pref = desc.split('-> ')
140
- return pref[1] && pref[0] === prefix ? pref[1] : ''
141
- })
142
- .filter(f => !!f)
143
-
144
- return suffixes.length
145
- ? prefix + '-> ' + suffixes.join(',')
146
- : [...descriptions].join(',')
147
- })
148
-
149
- descriptions = new Set(new_descs)
150
- }
151
- if (soTerms.size) {
152
- return [[...soTerms].join(','), [...descriptions].join(',')]
153
- }
154
- return [undefined, undefined]
155
- }
156
-
157
- static _altTypeToSO: { [key: string]: string | undefined } = {
158
- DEL: 'deletion',
159
- INS: 'insertion',
160
- DUP: 'duplication',
161
- INV: 'inversion',
162
- INVDUP: 'inverted duplication',
163
- CNV: 'copy_number_variation',
164
- TRA: 'translocation',
165
- 'DUP:TANDEM': 'tandem_duplication',
166
- NON_REF: 'sequence_variant',
167
- '*': 'sequence_variant',
168
- }
169
-
170
- _getSOAndDescFromAltDefs(
171
- ref: string,
172
- alt: string,
173
- ): [string, string] | [undefined, undefined] {
174
- if (typeof alt === 'string' && !alt.startsWith('<')) {
175
- return [undefined, undefined]
176
- }
177
-
178
- // look for a definition with an SO type for this
179
- let soTerm = VCFFeature._altTypeToSO[alt]
180
- // if no SO term but ALT is in metadata, assume sequence_variant
181
- if (!soTerm && this.parser.getMetadata('ALT', alt)) {
182
- soTerm = 'sequence_variant'
183
- }
184
- if (soTerm) {
185
- return [soTerm, alt]
186
- }
187
-
188
- // try to look for a definition for a parent term if we can
189
- const modAlt = alt.split(':')
190
- if (modAlt.length > 1) {
191
- return this._getSOAndDescFromAltDefs(
192
- ref,
193
- `<${modAlt.slice(0, modAlt.length - 1).join(':')}>`,
194
- )
195
- }
196
-
197
- // no parent
198
- return [undefined, undefined]
199
- }
200
-
201
- // note: term SNV is used instead of SNP because SO definition of SNP says
202
- // abundance must be at least 1% in population, and can't be sure we meet
203
- // that
204
- _getSOAndDescByExamination(ref: string, alt: string): [string, string] {
205
- const bnd = parseBreakend(alt)
206
- if (bnd) {
207
- return ['breakend', alt]
208
- } else if (ref.length === 1 && alt.length === 1) {
209
- return ['SNV', this._makeDescriptionString('SNV', ref, alt)]
210
- } else if (alt === '<INS>') {
211
- return ['insertion', alt]
212
- } else if (alt === '<DEL>') {
213
- return ['deletion', alt]
214
- } else if (alt === '<INV>') {
215
- return ['deletion', alt]
216
- } else if (alt === '<TRA>') {
217
- return ['translocation', alt]
218
- } else if (alt.includes('<')) {
219
- return ['sv', alt]
220
- } else if (ref.length === alt.length) {
221
- if (ref.split('').reverse().join('') === alt) {
222
- return ['inversion', this._makeDescriptionString('inversion', ref, alt)]
223
- }
224
- return [
225
- 'substitution',
226
- this._makeDescriptionString('substitution', ref, alt),
227
- ]
228
- } else if (ref.length <= alt.length) {
229
- return ['insertion', this._makeDescriptionString('insertion', ref, alt)]
230
- } else if (ref.length > alt.length) {
231
- return ['deletion', this._makeDescriptionString('deletion', ref, alt)]
232
- }
233
-
234
- return ['indel', this._makeDescriptionString('indel', ref, alt)]
235
- }
236
-
237
- _makeDescriptionString(soTerm: string, ref: string, alt: string): string {
238
- return `${soTerm} ${ref} -> ${alt}`
239
- }
240
-
241
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
242
- toJSON(): any {
243
- return {
244
- uniqueId: this._id,
245
- ...this.variant,
246
- ...this.data,
247
- samples: this.variant.SAMPLES,
248
- }
249
- }
250
- }