@apollo-annotation/jbrowse-plugin-apollo 0.3.1 → 0.3.2

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 (47) hide show
  1. package/dist/index.esm.js +2072 -1496
  2. package/dist/index.esm.js.map +1 -1
  3. package/dist/jbrowse-plugin-apollo.cjs.development.js +2069 -1493
  4. package/dist/jbrowse-plugin-apollo.cjs.development.js.map +1 -1
  5. package/dist/jbrowse-plugin-apollo.cjs.production.min.js +1 -1
  6. package/dist/jbrowse-plugin-apollo.cjs.production.min.js.map +1 -1
  7. package/dist/jbrowse-plugin-apollo.umd.development.js +2256 -1533
  8. package/dist/jbrowse-plugin-apollo.umd.development.js.map +1 -1
  9. package/dist/jbrowse-plugin-apollo.umd.production.min.js +1 -1
  10. package/dist/jbrowse-plugin-apollo.umd.production.min.js.map +1 -1
  11. package/package.json +13 -11
  12. package/src/ApolloSequenceAdapter/ApolloSequenceAdapter.ts +7 -10
  13. package/src/FeatureDetailsWidget/ApolloFeatureDetailsWidget.tsx +3 -0
  14. package/src/FeatureDetailsWidget/Attributes.tsx +27 -27
  15. package/src/FeatureDetailsWidget/FeatureDetailsNavigation.tsx +65 -0
  16. package/src/FeatureDetailsWidget/TranscriptBasic.tsx +6 -1
  17. package/src/FeatureDetailsWidget/TranscriptSequence.tsx +25 -2
  18. package/src/LinearApolloDisplay/components/LinearApolloDisplay.tsx +0 -1
  19. package/src/LinearApolloDisplay/glyphs/BoxGlyph.ts +8 -1
  20. package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +88 -40
  21. package/src/LinearApolloDisplay/glyphs/Glyph.ts +8 -1
  22. package/src/LinearApolloDisplay/stateModel/base.ts +28 -2
  23. package/src/LinearApolloDisplay/stateModel/layouts.ts +65 -11
  24. package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +25 -6
  25. package/src/LinearApolloDisplay/stateModel/rendering.ts +1 -2
  26. package/src/OntologyManager/OntologyStore/index.ts +6 -2
  27. package/src/OntologyManager/OntologyStore/indexeddb-storage.ts +41 -13
  28. package/src/OntologyManager/index.ts +35 -0
  29. package/src/SixFrameFeatureDisplay/stateModel.ts +11 -2
  30. package/src/TabularEditor/HybridGrid/Feature.tsx +1 -2
  31. package/src/TabularEditor/HybridGrid/HybridGrid.tsx +0 -1
  32. package/src/TabularEditor/HybridGrid/featureContextMenuItems.ts +8 -1
  33. package/src/components/AddRefSeqAliases.tsx +7 -8
  34. package/src/components/CopyFeature.tsx +1 -1
  35. package/src/components/CreateApolloAnnotation.tsx +304 -0
  36. package/src/components/DownloadGFF3.tsx +5 -1
  37. package/src/components/FilterFeatures.tsx +120 -0
  38. package/src/components/ModifyFeatureAttribute.tsx +27 -27
  39. package/src/components/OntologyTermMultiSelect.tsx +5 -5
  40. package/src/extensions/annotationFromJBrowseFeature.test.ts +119 -0
  41. package/src/extensions/annotationFromJBrowseFeature.ts +171 -0
  42. package/src/extensions/annotationFromPileup.ts +1 -1
  43. package/src/extensions/index.ts +1 -0
  44. package/src/index.ts +8 -2
  45. package/src/session/ClientDataStore.ts +29 -0
  46. package/src/session/session.ts +2 -5
  47. package/src/LinearApolloDisplay/stateModel/getGlyph.ts +0 -40
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apollo-annotation/jbrowse-plugin-apollo",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Apollo plugin for JBrowse 2",
5
5
  "keywords": [
6
6
  "jbrowse",
@@ -48,14 +48,14 @@
48
48
  }
49
49
  },
50
50
  "dependencies": {
51
- "@apollo-annotation/common": "^0.3.1",
52
- "@apollo-annotation/mst": "^0.3.1",
53
- "@apollo-annotation/shared": "^0.3.1",
51
+ "@apollo-annotation/common": "^0.3.2",
52
+ "@apollo-annotation/mst": "^0.3.2",
53
+ "@apollo-annotation/shared": "^0.3.2",
54
54
  "@emotion/react": "^11.10.6",
55
55
  "@emotion/styled": "^11.10.6",
56
56
  "@gmod/gff": "1.2.0",
57
- "@jbrowse/plugin-authentication": "^2.0.1",
58
- "@jbrowse/plugin-linear-genome-view": "^2.0.1",
57
+ "@jbrowse/plugin-authentication": "^3.0.1",
58
+ "@jbrowse/plugin-linear-genome-view": "^3.0.1",
59
59
  "@mui/icons-material": "^5.8.4",
60
60
  "@types/jsonpath": "^0.2.0",
61
61
  "autosuggest-highlight": "^3.3.4",
@@ -71,11 +71,11 @@
71
71
  "tslib": "^2.3.1"
72
72
  },
73
73
  "devDependencies": {
74
- "@jbrowse/cli": "^2.6.2",
75
- "@jbrowse/core": "^2.13.1",
74
+ "@jbrowse/cli": "^3.0.1",
75
+ "@jbrowse/core": "^3.0.1",
76
76
  "@jbrowse/development-tools": "^2.1.1",
77
77
  "@jest/globals": "^29.0.3",
78
- "@mui/material": "^5.11.14",
78
+ "@mui/material": "^6.0.0",
79
79
  "@mui/x-data-grid": "^7.0.0",
80
80
  "@types/autosuggest-highlight": "^3",
81
81
  "@types/file-saver": "^2",
@@ -84,6 +84,8 @@
84
84
  "@types/react": "^18.3.4",
85
85
  "@types/react-dom": "^18",
86
86
  "cypress": "12.17.3",
87
+ "cypress-image-diff-html-report": "^2.2.0",
88
+ "cypress-image-diff-js": "^2.3.0",
87
89
  "cypress-mongodb": "^6.2.0",
88
90
  "fake-indexeddb": "^4.0.2",
89
91
  "jest": "^29.6.2",
@@ -108,8 +110,8 @@
108
110
  "typescript": "^5.5.3"
109
111
  },
110
112
  "peerDependencies": {
111
- "@jbrowse/core": "^2.13.1",
112
- "@mui/material": "^5.11.14",
113
+ "@jbrowse/core": "^3.0.1",
114
+ "@mui/material": "^6.0.0",
113
115
  "mobx": "^6.6.1",
114
116
  "mobx-react": "^7.2.1",
115
117
  "mobx-state-tree": "^5.4.0",
@@ -4,10 +4,7 @@
4
4
  /* eslint-disable @typescript-eslint/no-unsafe-member-access */
5
5
  /* eslint-disable @typescript-eslint/no-unsafe-call */
6
6
  import { readConfObject } from '@jbrowse/core/configuration'
7
- import {
8
- BaseOptions,
9
- BaseSequenceAdapter,
10
- } from '@jbrowse/core/data_adapters/BaseAdapter'
7
+ import { BaseSequenceAdapter } from '@jbrowse/core/data_adapters/BaseAdapter'
11
8
  import { ObservableCreate } from '@jbrowse/core/util/rxjs'
12
9
  import SimpleFeature, { Feature } from '@jbrowse/core/util/simpleFeature'
13
10
  import { NoAssemblyRegion, Region } from '@jbrowse/core/util/types'
@@ -48,12 +45,12 @@ const isInWebWorker = typeof sessionStorage === 'undefined'
48
45
  export class ApolloSequenceAdapter extends BaseSequenceAdapter {
49
46
  private regions: NoAssemblyRegion[] | undefined
50
47
 
51
- public async getRefNames(opts?: BaseOptions) {
52
- const regions = await this.getRegions(opts)
48
+ public async getRefNames() {
49
+ const regions = await this.getRegions()
53
50
  return regions.map((regions) => regions.refName)
54
51
  }
55
52
 
56
- public async getRegions(opts?: BaseOptions): Promise<NoAssemblyRegion[]> {
53
+ public async getRegions(): Promise<NoAssemblyRegion[]> {
57
54
  if (this.regions) {
58
55
  return this.regions
59
56
  }
@@ -93,7 +90,7 @@ export class ApolloSequenceAdapter extends BaseSequenceAdapter {
93
90
  removeEventListener('message', messageListener)
94
91
  resolve(data.regions)
95
92
  }
96
- addEventListener('message', messageListener, opts)
93
+ addEventListener('message', messageListener)
97
94
  // @ts-expect-error waiting for types to be published
98
95
  globalThis.rpcServer.emit('apollo', {
99
96
  apollo: true,
@@ -112,7 +109,7 @@ export class ApolloSequenceAdapter extends BaseSequenceAdapter {
112
109
  * @param param -
113
110
  * @returns Observable of Feature objects in the region
114
111
  */
115
- public getFeatures(region: Region, opts?: BaseOptions) {
112
+ public getFeatures(region: Region) {
116
113
  const { end, refName, start } = region
117
114
  const assemblyId = readConfObject(this.config, 'assemblyId')
118
115
  const regionWithAssemblyName = { ...region, assemblyName: assemblyId }
@@ -161,7 +158,7 @@ export class ApolloSequenceAdapter extends BaseSequenceAdapter {
161
158
  removeEventListener('message', messageListener)
162
159
  resolve(data.sequence)
163
160
  }
164
- addEventListener('message', messageListener, opts)
161
+ addEventListener('message', messageListener)
165
162
  // @ts-expect-error waiting for types to be published
166
163
  globalThis.rpcServer.emit('apollo', {
167
164
  apollo: true,
@@ -8,6 +8,7 @@ import { Attributes } from './Attributes'
8
8
  import { BasicInformation } from './BasicInformation'
9
9
  import { ApolloFeatureDetailsWidget as ApolloFeatureDetails } from './model'
10
10
  import { Sequence } from './Sequence'
11
+ import { FeatureDetailsNavigation } from './FeatureDetailsNavigation'
11
12
 
12
13
  const useStyles = makeStyles()((theme) => ({
13
14
  root: {
@@ -59,6 +60,8 @@ export const ApolloFeatureDetailsWidget = observer(
59
60
  assembly={currentAssembly._id}
60
61
  refName={refName}
61
62
  />
63
+ <hr />
64
+ <FeatureDetailsNavigation model={model} feature={feature} />
62
65
  </div>
63
66
  )
64
67
  },
@@ -11,7 +11,7 @@ import {
11
11
  FormControl,
12
12
  FormControlLabel,
13
13
  FormLabel,
14
- Grid,
14
+ Grid2,
15
15
  IconButton,
16
16
  Paper,
17
17
  Radio,
@@ -243,7 +243,7 @@ export const Attributes = observer(function Attributes({
243
243
  return (
244
244
  <>
245
245
  <Typography variant="h5">Attributes</Typography>
246
- <Grid container direction="column" spacing={1}>
246
+ <Grid2 container direction="column" spacing={1}>
247
247
  {Object.entries(attributes).map(([key, value]) => {
248
248
  if (key === '') {
249
249
  return null
@@ -251,20 +251,20 @@ export const Attributes = observer(function Attributes({
251
251
  const EditorComponent =
252
252
  reservedKeys.get(key) ?? CustomAttributeValueEditor
253
253
  return (
254
- <Grid container item spacing={3} alignItems="center" key={key}>
255
- <Grid item xs="auto">
254
+ <Grid2 container spacing={3} alignItems="center" key={key}>
255
+ <Grid2>
256
256
  <Paper variant="outlined" className={classes.attributeName}>
257
257
  <Typography>{key}</Typography>
258
258
  </Paper>
259
- </Grid>
260
- <Grid item flexGrow={1}>
259
+ </Grid2>
260
+ <Grid2 flexGrow={1}>
261
261
  <EditorComponent
262
262
  session={session}
263
263
  value={value}
264
264
  onChange={(newValue) => onChangeCommitted(key, newValue)}
265
265
  />
266
- </Grid>
267
- <Grid item xs={1}>
266
+ </Grid2>
267
+ <Grid2>
268
268
  <IconButton
269
269
  aria-label="delete"
270
270
  size="medium"
@@ -273,11 +273,11 @@ export const Attributes = observer(function Attributes({
273
273
  >
274
274
  <DeleteIcon fontSize="medium" key={key} />
275
275
  </IconButton>
276
- </Grid>
277
- </Grid>
276
+ </Grid2>
277
+ </Grid2>
278
278
  )
279
279
  })}
280
- <Grid item>
280
+ <Grid2>
281
281
  <Button
282
282
  color="primary"
283
283
  variant="contained"
@@ -288,12 +288,12 @@ export const Attributes = observer(function Attributes({
288
288
  >
289
289
  Add new
290
290
  </Button>
291
- </Grid>
291
+ </Grid2>
292
292
  {showAddNewForm ? (
293
- <Grid item>
293
+ <Grid2>
294
294
  <Paper elevation={8} className={classes.newAttributePaper}>
295
- <Grid container direction="column">
296
- <Grid item>
295
+ <Grid2 container direction="column">
296
+ <Grid2>
297
297
  <FormControl>
298
298
  <FormLabel id="attribute-radio-button-group">
299
299
  Select attribute type
@@ -309,11 +309,11 @@ export const Attributes = observer(function Attributes({
309
309
  control={<Radio />}
310
310
  disableTypography
311
311
  label={
312
- <Grid container spacing={1} alignItems="center">
313
- <Grid item>
312
+ <Grid2 container spacing={1} alignItems="center">
313
+ <Grid2>
314
314
  <Typography>Custom</Typography>
315
- </Grid>
316
- <Grid item>
315
+ </Grid2>
316
+ <Grid2>
317
317
  <TextField
318
318
  label="Custom attribute key"
319
319
  variant="outlined"
@@ -327,8 +327,8 @@ export const Attributes = observer(function Attributes({
327
327
  setNewAttributeKey(event.target.value)
328
328
  }}
329
329
  />
330
- </Grid>
331
- </Grid>
330
+ </Grid2>
331
+ </Grid2>
332
332
  }
333
333
  />
334
334
  {[...reservedKeys.keys()].map((key) => (
@@ -341,8 +341,8 @@ export const Attributes = observer(function Attributes({
341
341
  ))}
342
342
  </RadioGroup>
343
343
  </FormControl>
344
- </Grid>
345
- <Grid item>
344
+ </Grid2>
345
+ <Grid2>
346
346
  <DialogActions>
347
347
  <Button
348
348
  key="addButton"
@@ -366,12 +366,12 @@ export const Attributes = observer(function Attributes({
366
366
  Cancel
367
367
  </Button>
368
368
  </DialogActions>
369
- </Grid>
370
- </Grid>
369
+ </Grid2>
370
+ </Grid2>
371
371
  </Paper>
372
- </Grid>
372
+ </Grid2>
373
373
  ) : null}
374
- </Grid>
374
+ </Grid2>
375
375
  {errorMessage ? (
376
376
  <Typography color="error">{errorMessage}</Typography>
377
377
  ) : null}
@@ -0,0 +1,65 @@
1
+ import React from 'react'
2
+
3
+ import { Button, Typography } from '@mui/material'
4
+ import { observer } from 'mobx-react'
5
+
6
+ import { AnnotationFeature } from '@apollo-annotation/mst'
7
+ import { ApolloFeatureDetailsWidget as ApolloFeatureDetails } from './model'
8
+
9
+ export const FeatureDetailsNavigation = observer(
10
+ function FeatureDetailsNavigation(props: {
11
+ model: ApolloFeatureDetails
12
+ feature: AnnotationFeature
13
+ }) {
14
+ const { feature, model } = props
15
+ const { children, parent } = feature
16
+ const childFeatures = []
17
+ if (children) {
18
+ for (const [, child] of children) {
19
+ childFeatures.push(child)
20
+ }
21
+ }
22
+
23
+ if (!(parent ?? childFeatures.length > 0)) {
24
+ return null
25
+ }
26
+
27
+ return (
28
+ <div>
29
+ <Typography variant="h5">Go to related feature</Typography>
30
+ {parent && (
31
+ <div>
32
+ <Typography variant="h6">Parent:</Typography>
33
+ <Button
34
+ variant="contained"
35
+ onClick={() => {
36
+ model.setFeature(parent)
37
+ }}
38
+ >
39
+ {parent.type} ({parent.min}..{parent.max})
40
+ </Button>
41
+ </div>
42
+ )}
43
+ {childFeatures.length > 0 && (
44
+ <div>
45
+ <Typography variant="h6">
46
+ {childFeatures.length === 1 ? 'Child' : 'Children'}:
47
+ </Typography>
48
+ {childFeatures.map((child) => (
49
+ <div key={child._id} style={{ marginBottom: 5 }}>
50
+ <Button
51
+ variant="contained"
52
+ onClick={() => {
53
+ model.setFeature(child)
54
+ }}
55
+ >
56
+ {child.type} ({child.min}..{child.max})
57
+ </Button>
58
+ </div>
59
+ ))}
60
+ </div>
61
+ )}
62
+ </div>
63
+ )
64
+ },
65
+ )
@@ -83,7 +83,12 @@ export const TranscriptBasicInformation = observer(
83
83
  return null
84
84
  }
85
85
 
86
- const { strand, transcriptParts } = feature
86
+ let strand, transcriptParts
87
+ try {
88
+ ;({ strand, transcriptParts } = feature)
89
+ } catch {
90
+ return null
91
+ }
87
92
  const [firstLocation] = transcriptParts
88
93
 
89
94
  const locationData = firstLocation
@@ -1,6 +1,6 @@
1
1
  import { AnnotationFeature } from '@apollo-annotation/mst'
2
2
  import { splitStringIntoChunks } from '@apollo-annotation/shared'
3
- import { revcom } from '@jbrowse/core/util'
3
+ import { defaultCodonTable, revcom } from '@jbrowse/core/util'
4
4
  import {
5
5
  Button,
6
6
  MenuItem,
@@ -18,7 +18,7 @@ import { ApolloSessionModel } from '../session'
18
18
  const SEQUENCE_WRAP_LENGTH = 60
19
19
 
20
20
  type SegmentType = 'upOrDownstream' | 'UTR' | 'CDS' | 'intron' | 'protein'
21
- type SegmentListType = 'CDS' | 'cDNA' | 'genomic'
21
+ type SegmentListType = 'CDS' | 'cDNA' | 'genomic' | 'protein'
22
22
 
23
23
  interface SequenceSegment {
24
24
  type: SegmentType
@@ -121,6 +121,28 @@ function getSequenceSegments(
121
121
  segments.push({ type: 'CDS', sequenceLines, locs })
122
122
  return segments
123
123
  }
124
+ case 'protein': {
125
+ let wholeSequence = ''
126
+ const [firstLocation] = cdsLocations
127
+ const locs: { min: number; max: number }[] = []
128
+ for (const loc of firstLocation) {
129
+ let sequence = getSequence(loc.min, loc.max)
130
+ if (strand === -1) {
131
+ sequence = revcom(sequence)
132
+ }
133
+ wholeSequence += sequence
134
+ locs.push({ min: loc.min, max: loc.max })
135
+ }
136
+ let protein = ''
137
+ for (let i = 0; i < wholeSequence.length; i += 3) {
138
+ const codonSeq: string = wholeSequence.slice(i, i + 3).toUpperCase()
139
+ protein +=
140
+ defaultCodonTable[codonSeq as keyof typeof defaultCodonTable] || '&'
141
+ }
142
+ const sequenceLines = splitStringIntoChunks(protein, SEQUENCE_WRAP_LENGTH)
143
+ segments.push({ type: 'protein', sequenceLines, locs })
144
+ return segments
145
+ }
124
146
  }
125
147
  }
126
148
 
@@ -238,6 +260,7 @@ export const TranscriptSequence = observer(function TranscriptSequence({
238
260
  <MenuItem value="CDS">CDS</MenuItem>
239
261
  <MenuItem value="cDNA">cDNA</MenuItem>
240
262
  <MenuItem value="genomic">Genomic</MenuItem>
263
+ <MenuItem value="protein">Protein</MenuItem>
241
264
  </Select>
242
265
  <Paper
243
266
  style={{
@@ -1,4 +1,3 @@
1
- /* eslint-disable @typescript-eslint/no-unsafe-call */
2
1
  /* eslint-disable @typescript-eslint/unbound-method */
3
2
  /* eslint-disable @typescript-eslint/no-misused-promises */
4
3
  /* eslint-disable @typescript-eslint/no-unnecessary-condition */
@@ -389,7 +389,14 @@ function getContextMenuItems(
389
389
  },
390
390
  },
391
391
  )
392
- if (sourceFeature.type === 'mRNA' && isSessionModelWithWidgets(session)) {
392
+ const { featureTypeOntology } = session.apolloDataStore.ontologyManager
393
+ if (!featureTypeOntology) {
394
+ throw new Error('featureTypeOntology is undefined')
395
+ }
396
+ if (
397
+ featureTypeOntology.isTypeOf(sourceFeature.type, 'transcript') &&
398
+ isSessionModelWithWidgets(session)
399
+ ) {
393
400
  menuItems.push({
394
401
  label: 'Edit transcript details',
395
402
  onClick: () => {