@jbrowse/plugin-data-management 1.4.1 → 1.5.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 (37) hide show
  1. package/dist/AddTrackWidget/components/TrackSourceSelect.d.ts +2 -1
  2. package/dist/AddTrackWidget/model.d.ts +1 -363
  3. package/dist/PluginStoreWidget/components/CustomPluginForm.d.ts +1 -1
  4. package/dist/PluginStoreWidget/components/PluginCard.d.ts +2 -2
  5. package/dist/SetDefaultSession/SetDefaultSession.d.ts +4 -6
  6. package/dist/index.d.ts +8 -12
  7. package/dist/plugin-data-management.cjs.development.js +627 -501
  8. package/dist/plugin-data-management.cjs.development.js.map +1 -1
  9. package/dist/plugin-data-management.cjs.production.min.js +1 -1
  10. package/dist/plugin-data-management.cjs.production.min.js.map +1 -1
  11. package/dist/plugin-data-management.esm.js +614 -507
  12. package/dist/plugin-data-management.esm.js.map +1 -1
  13. package/package.json +3 -2
  14. package/src/AddConnectionWidget/components/AddConnectionWidget.test.js +3 -8
  15. package/src/AddTrackWidget/components/AddTrackWidget.test.js +2 -3
  16. package/src/AddTrackWidget/components/AddTrackWidget.tsx +4 -2
  17. package/src/AddTrackWidget/components/ConfirmTrack.tsx +160 -88
  18. package/src/AddTrackWidget/components/TrackSourceSelect.tsx +30 -23
  19. package/src/AddTrackWidget/components/__snapshots__/AddTrackWidget.test.js.snap +157 -124
  20. package/src/AddTrackWidget/index.test.jsx +78 -26
  21. package/src/AddTrackWidget/model.ts +5 -14
  22. package/src/AssemblyManager/AssemblyAddForm.tsx +7 -6
  23. package/src/AssemblyManager/AssemblyManager.test.tsx +1 -0
  24. package/src/HierarchicalTrackSelectorWidget/components/HierarchicalTrackSelector.js +27 -17
  25. package/src/HierarchicalTrackSelectorWidget/model.js +3 -2
  26. package/src/PluginStoreWidget/components/CustomPluginForm.tsx +164 -56
  27. package/src/PluginStoreWidget/components/InstalledPlugin.tsx +10 -2
  28. package/src/PluginStoreWidget/components/PluginCard.tsx +7 -9
  29. package/src/PluginStoreWidget/components/PluginStoreWidget.test.js +9 -10
  30. package/src/PluginStoreWidget/components/PluginStoreWidget.tsx +36 -26
  31. package/src/PluginStoreWidget/components/__snapshots__/PluginStoreWidget.test.js.snap +89 -51
  32. package/src/SetDefaultSession/SetDefaultSession.test.tsx +7 -81
  33. package/src/SetDefaultSession/SetDefaultSession.tsx +51 -162
  34. package/src/index.ts +1 -51
  35. package/src/ucsc-trackhub/configSchema.js +4 -1
  36. package/src/ucsc-trackhub/model.js +31 -31
  37. package/src/ucsc-trackhub/ucscTrackHub.js +40 -12
@@ -1,181 +1,70 @@
1
- import React, { useState } from 'react'
1
+ import React from 'react'
2
2
  import { observer } from 'mobx-react'
3
- import Dialog from '@material-ui/core/Dialog'
4
- import { makeStyles } from '@material-ui/core/styles'
5
- import DialogTitle from '@material-ui/core/DialogTitle'
6
- import DialogContent from '@material-ui/core/DialogContent'
7
- import DialogActions from '@material-ui/core/DialogActions'
8
- import Button from '@material-ui/core/Button'
9
- import List from '@material-ui/core/List'
10
- import ListItem from '@material-ui/core/ListItem'
11
- import ListItemIcon from '@material-ui/core/ListItemIcon'
12
- import ListItemText from '@material-ui/core/ListItemText'
13
- import ListSubheader from '@material-ui/core/ListSubheader'
14
- import Paper from '@material-ui/core/Paper'
15
- import Typography from '@material-ui/core/Typography'
16
- import Radio from '@material-ui/core/Radio'
17
- import pluralize from 'pluralize'
18
- import { Grid } from '@material-ui/core'
3
+ import {
4
+ Dialog,
5
+ DialogTitle,
6
+ DialogContent,
7
+ DialogActions,
8
+ Button,
9
+ Typography,
10
+ } from '@material-ui/core'
19
11
 
20
- const useStyles = makeStyles(theme => ({
21
- root: {
22
- margin: theme.spacing(1),
23
- },
24
- message: {
25
- padding: theme.spacing(3),
26
- },
27
- titleBox: {
28
- color: '#fff',
29
- backgroundColor: theme.palette.primary.main,
30
- textAlign: 'center',
31
- },
32
- dialogContent: {
33
- width: 600,
34
- },
35
- resetButton: {
36
- justifyContent: 'center',
37
- marginBottom: '6px',
38
- },
39
- }))
40
-
41
- const CurrentSession = observer(
42
- ({
43
- session,
44
- selectedDefault,
45
- handleRadio,
46
- }: {
47
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
48
- session: any
49
- selectedDefault: string
50
- handleRadio: Function
51
- }) => {
52
- const classes = useStyles()
53
-
54
- return (
55
- <Paper className={classes.root}>
56
- <List subheader={<ListSubheader>Currently open session</ListSubheader>}>
57
- <ListItem>
58
- <ListItemIcon>
59
- <Radio
60
- checked={session.name === selectedDefault}
61
- onChange={() => handleRadio(session)}
62
- />
63
- </ListItemIcon>
64
- <ListItemText primary={session.name} />
65
- </ListItem>
66
- </List>
67
- </Paper>
68
- )
69
- },
70
- )
12
+ function canSetDefaultSession(obj: unknown): obj is {
13
+ jbrowse: { setDefaultSessionConf: Function }
14
+ session: unknown
15
+ } {
16
+ return typeof obj === 'object' && !!obj && 'jbrowse' in obj
17
+ }
71
18
 
72
19
  const SetDefaultSession = observer(
73
- ({
74
- rootModel,
75
- open,
76
- onClose,
77
- currentDefault,
78
- }: {
79
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
80
- rootModel: any
81
- open: boolean
82
- onClose: Function
83
- currentDefault: string
84
- }) => {
85
- const classes = useStyles()
86
- const { session } = rootModel
87
- const [selectedDefault, setSelectedDefault] = useState(currentDefault)
88
-
89
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
90
- function handleRadio(sessionSnapshot: any) {
91
- setSelectedDefault(sessionSnapshot.name)
92
- rootModel.jbrowse.setDefaultSessionConf(sessionSnapshot)
93
- session.notify(
94
- `Set default session to ${sessionSnapshot.name}`,
95
- 'success',
96
- )
20
+ ({ rootModel, onClose }: { rootModel?: unknown; onClose: () => void }) => {
21
+ if (!rootModel) {
22
+ return null
97
23
  }
24
+ if (!canSetDefaultSession(rootModel)) {
25
+ console.error('Incorrect rootmodel')
26
+ return null
27
+ }
28
+ const { jbrowse, session } = rootModel
98
29
 
99
30
  return (
100
- <Dialog open={open}>
101
- <DialogTitle className={classes.titleBox}>
102
- Set Default Session
103
- </DialogTitle>
31
+ <Dialog open onClose={onClose}>
32
+ <DialogTitle>Set default session</DialogTitle>
104
33
  <DialogContent>
105
- <Grid className={classes.resetButton} container>
106
- <Grid item>
107
- <Button
108
- color="secondary"
109
- variant="contained"
110
- onClick={() => {
111
- setSelectedDefault('New session')
112
- rootModel.jbrowse.setDefaultSessionConf({
113
- name: `New session`,
114
- })
115
- session.notify('Reset default session', 'success')
116
- }}
117
- >
118
- Clear default session
119
- </Button>
120
- </Grid>
121
- </Grid>
122
-
123
- <CurrentSession
124
- session={session}
125
- selectedDefault={selectedDefault}
126
- handleRadio={handleRadio}
127
- />
128
- <Paper className={classes.root}>
129
- <List subheader={<ListSubheader>Saved sessions</ListSubheader>}>
130
- {session.savedSessions.length ? (
131
- session.savedSessions.map(
132
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
133
- (sessionSnapshot: any) => {
134
- const { views = [] } = sessionSnapshot
135
- const totalTracks = views
136
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
137
- .map((view: any) => view.tracks.length)
138
- .reduce((a: number, b: number) => a + b, 0)
139
- if (sessionSnapshot.name !== session.name) {
140
- return (
141
- <ListItem key={sessionSnapshot.name}>
142
- <ListItemIcon>
143
- <Radio
144
- checked={sessionSnapshot.name === selectedDefault}
145
- onChange={() => handleRadio(sessionSnapshot)}
146
- />
147
- </ListItemIcon>
148
- <ListItemText
149
- primary={sessionSnapshot.name}
150
- secondary={`${views.length} ${pluralize(
151
- 'view',
152
- views.length,
153
- )}; ${totalTracks}
154
- open ${pluralize('track', totalTracks)}`}
155
- />
156
- </ListItem>
157
- )
158
- }
159
- return null
160
- },
161
- )
162
- ) : (
163
- <Typography className={classes.message}>
164
- No saved sessions found
165
- </Typography>
166
- )}
167
- </List>
168
- </Paper>
34
+ <Typography>
35
+ Select "Set current session as default" to make your current session
36
+ saved to the config file. You can also hit "Clear default session",
37
+ which would remove the default session from the config.
38
+ </Typography>
169
39
  </DialogContent>
170
40
  <DialogActions>
41
+ <Button
42
+ variant="contained"
43
+ onClick={() => {
44
+ jbrowse.setDefaultSessionConf({
45
+ name: `New session`,
46
+ })
47
+ onClose()
48
+ }}
49
+ >
50
+ Clear default session
51
+ </Button>
171
52
  <Button
172
53
  color="secondary"
173
54
  variant="contained"
55
+ onClick={() => onClose()}
56
+ >
57
+ Cancel
58
+ </Button>
59
+ <Button
60
+ color="primary"
61
+ variant="contained"
174
62
  onClick={() => {
175
- onClose(false)
63
+ jbrowse.setDefaultSessionConf(session)
64
+ onClose()
176
65
  }}
177
66
  >
178
- Return
67
+ Set current session as default
179
68
  </Button>
180
69
  </DialogActions>
181
70
  </Dialog>
package/src/index.ts CHANGED
@@ -3,10 +3,6 @@ import ConnectionType from '@jbrowse/core/pluggableElementTypes/ConnectionType'
3
3
  import WidgetType from '@jbrowse/core/pluggableElementTypes/WidgetType'
4
4
  import Plugin from '@jbrowse/core/Plugin'
5
5
  import PluginManager from '@jbrowse/core/PluginManager'
6
- import { SessionWithWidgets, isAbstractMenuManager } from '@jbrowse/core/util'
7
- import NoteAddIcon from '@material-ui/icons/NoteAdd'
8
- import InputIcon from '@material-ui/icons/Input'
9
- import ExtensionIcon from '@material-ui/icons/Extension'
10
6
  import {
11
7
  configSchema as ucscConfigSchema,
12
8
  modelFactory as ucscModelFactory,
@@ -105,53 +101,7 @@ export default class extends Plugin {
105
101
  })
106
102
  }
107
103
 
108
- configure(pluginManager: PluginManager) {
109
- if (isAbstractMenuManager(pluginManager.rootModel)) {
110
- pluginManager.rootModel.appendToMenu('File', {
111
- label: 'Open track',
112
- icon: NoteAddIcon,
113
- onClick: (session: SessionWithWidgets) => {
114
- if (session.views.length === 0) {
115
- session.notify('Please open a view to add a track first')
116
- } else if (session.views.length >= 1) {
117
- const widget = session.addWidget(
118
- 'AddTrackWidget',
119
- 'addTrackWidget',
120
- { view: session.views[0].id },
121
- )
122
- session.showWidget(widget)
123
- if (session.views.length > 1) {
124
- session.notify(
125
- `This will add a track to the first view. Note: if you want to open a track in a specific view open the track selector for that view and use the add track (plus icon) in the bottom right`,
126
- )
127
- }
128
- }
129
- },
130
- })
131
- pluginManager.rootModel.appendToMenu('File', {
132
- label: 'Open connection',
133
- icon: InputIcon,
134
- onClick: (session: SessionWithWidgets) => {
135
- const widget = session.addWidget(
136
- 'AddConnectionWidget',
137
- 'addConnectionWidget',
138
- )
139
- session.showWidget(widget)
140
- },
141
- })
142
- pluginManager.rootModel.appendToMenu('File', {
143
- label: 'Plugin store',
144
- icon: ExtensionIcon,
145
- onClick: (session: SessionWithWidgets) => {
146
- const widget = session.addWidget(
147
- 'PluginStoreWidget',
148
- 'pluginStoreWidget',
149
- )
150
- session.showWidget(widget)
151
- },
152
- })
153
- }
154
- }
104
+ configure(pluginManager: PluginManager) {}
155
105
  }
156
106
 
157
107
  export { AssemblyManager, SetDefaultSession }
@@ -6,7 +6,10 @@ export default ConfigurationSchema(
6
6
  {
7
7
  hubTxtLocation: {
8
8
  type: 'fileLocation',
9
- defaultValue: { uri: 'http://mysite.com/path/to/hub.txt' },
9
+ defaultValue: {
10
+ uri: 'http://mysite.com/path/to/hub.txt',
11
+ locationType: 'UriLocation',
12
+ },
10
13
  description: 'location of the hub file (usually called hub.txt)',
11
14
  },
12
15
  assemblyNames: {
@@ -2,6 +2,7 @@ import { BaseConnectionModelFactory } from '@jbrowse/core/pluggableElementTypes/
2
2
  import {
3
3
  ConfigurationReference,
4
4
  readConfObject,
5
+ getConf,
5
6
  } from '@jbrowse/core/configuration'
6
7
  import { getSession } from '@jbrowse/core/util'
7
8
  import { types } from 'mobx-state-tree'
@@ -24,23 +25,23 @@ export default function UCSCTrackHubConnection(pluginManager) {
24
25
  })
25
26
  .actions(self => ({
26
27
  connect() {
27
- const connectionName = readConfObject(self.configuration, 'name')
28
- const hubFileLocation = readConfObject(
29
- self.configuration,
30
- 'hubTxtLocation',
31
- )
28
+ const connectionName = getConf(self, 'name')
29
+ const hubFileLocation = getConf(self, 'hubTxtLocation')
32
30
  const session = getSession(self)
33
31
  fetchHubFile(hubFileLocation)
34
32
  .then(hubFile => {
35
- let genomesFileLocation
36
- if (hubFileLocation.uri) {
37
- genomesFileLocation = {
38
- uri: new URL(hubFile.get('genomesFile'), hubFileLocation.uri)
39
- .href,
40
- }
41
- } else {
42
- genomesFileLocation = { localPath: hubFile.get('genomesFile') }
43
- }
33
+ const genomesFileLocation = hubFileLocation.uri
34
+ ? {
35
+ uri: new URL(
36
+ hubFile.get('genomesFile'),
37
+ hubFileLocation.uri,
38
+ ).href,
39
+ locationType: 'UriLocation',
40
+ }
41
+ : {
42
+ localPath: hubFile.get('genomesFile'),
43
+ locationType: 'LocalPathLocation',
44
+ }
44
45
  return Promise.all([
45
46
  hubFile,
46
47
  fetchGenomesFile(genomesFileLocation),
@@ -49,10 +50,7 @@ export default function UCSCTrackHubConnection(pluginManager) {
49
50
  .then(([hubFile, genomesFile]) => {
50
51
  const trackDbData = []
51
52
  for (const [genomeName, genome] of genomesFile) {
52
- const assemblyNames = readConfObject(
53
- self.configuration,
54
- 'assemblyNames',
55
- )
53
+ const assemblyNames = getConf(self, 'assemblyNames')
56
54
  if (
57
55
  assemblyNames.length > 0 &&
58
56
  !assemblyNames.includes(genomeName)
@@ -67,19 +65,21 @@ export default function UCSCTrackHubConnection(pluginManager) {
67
65
  `Cannot find assembly for "${genomeName}" from the genomes file for connection "${connectionName}"`,
68
66
  )
69
67
  }
70
- let trackDbFileLocation
71
- if (hubFileLocation.uri) {
72
- trackDbFileLocation = {
73
- uri: new URL(
74
- genome.get('trackDb'),
75
- new URL(hubFile.get('genomesFile'), hubFileLocation.uri),
76
- ).href,
77
- }
78
- } else {
79
- trackDbFileLocation = {
80
- localPath: genome.get('trackDb'),
81
- }
82
- }
68
+ const trackDbFileLocation = hubFileLocation.uri
69
+ ? {
70
+ uri: new URL(
71
+ genome.get('trackDb'),
72
+ new URL(
73
+ hubFile.get('genomesFile'),
74
+ hubFileLocation.uri,
75
+ ),
76
+ ).href,
77
+ locationType: 'UriLocation',
78
+ }
79
+ : {
80
+ localPath: genome.get('trackDb'),
81
+ locationType: 'LocalPathLocation',
82
+ }
83
83
  trackDbData.push(
84
84
  Promise.all([
85
85
  trackDbFileLocation,
@@ -1,5 +1,4 @@
1
1
  import { objectHash } from '@jbrowse/core/util'
2
- import { GenomesFile, HubFile, TrackDbFile } from '@gmod/ucsc-hub'
3
2
  import { openLocation } from '@jbrowse/core/util/io'
4
3
  import {
5
4
  generateUnsupportedTrackConf,
@@ -12,6 +11,7 @@ export { ucscAssemblies }
12
11
  export async function fetchHubFile(hubFileLocation) {
13
12
  try {
14
13
  const hubFileText = await openLocation(hubFileLocation).readFile('utf8')
14
+ const { HubFile } = await import('@gmod/ucsc-hub')
15
15
  return new HubFile(hubFileText)
16
16
  } catch (error) {
17
17
  throw new Error(`Not a valid hub.txt file, got error: '${error}'`)
@@ -22,14 +22,14 @@ export async function fetchGenomesFile(genomesFileLocation) {
22
22
  const genomesFileText = await openLocation(genomesFileLocation).readFile(
23
23
  'utf8',
24
24
  )
25
+ const { GenomesFile } = await import('@gmod/ucsc-hub')
25
26
  return new GenomesFile(genomesFileText)
26
27
  }
27
28
 
28
29
  export async function fetchTrackDbFile(trackDbFileLocation) {
29
- const trackDbFileText = await openLocation(trackDbFileLocation).readFile(
30
- 'utf8',
31
- )
32
- return new TrackDbFile(trackDbFileText)
30
+ const text = await openLocation(trackDbFileLocation).readFile('utf8')
31
+ const { TrackDbFile } = await import('@gmod/ucsc-hub')
32
+ return new TrackDbFile(text)
33
33
  }
34
34
 
35
35
  export function generateTracks(
@@ -101,9 +101,13 @@ function makeTrackConfig(
101
101
  if (trackDbFileLocation.uri) {
102
102
  bigDataLocation = {
103
103
  uri: new URL(track.get('bigDataUrl'), trackDbFileLocation.uri).href,
104
+ locationType: 'UriLocation',
104
105
  }
105
106
  } else {
106
- bigDataLocation = { localPath: track.get('bigDataUrl') }
107
+ bigDataLocation = {
108
+ localPath: track.get('bigDataUrl'),
109
+ locationType: 'LocalPathLocation',
110
+ }
107
111
  }
108
112
  let bigDataIndexLocation
109
113
 
@@ -114,17 +118,25 @@ function makeTrackConfig(
114
118
  ? {
115
119
  uri: new URL(track.get('bigDataIndex'), trackDbFileLocation.uri)
116
120
  .href,
121
+ locationType: 'UriLocation',
117
122
  }
118
123
  : {
119
124
  uri: new URL(
120
125
  `${track.get('bigDataUrl')}.bai`,
121
126
  trackDbFileLocation.uri,
122
127
  ).href,
128
+ locationType: 'UriLocation',
123
129
  }
124
130
  } else {
125
131
  bigDataIndexLocation = track.get('bigDataIndex')
126
- ? { localPath: track.get('bigDataIndex') }
127
- : { localPath: `${track.get('bigDataUrl')}.bai` }
132
+ ? {
133
+ localPath: track.get('bigDataIndex'),
134
+ locationType: 'LocalPathLocation',
135
+ }
136
+ : {
137
+ localPath: `${track.get('bigDataUrl')}.bai`,
138
+ locationType: 'LocalPathLocation',
139
+ }
128
140
  }
129
141
  return {
130
142
  type: 'AlignmentsTrack',
@@ -284,17 +296,25 @@ function makeTrackConfig(
284
296
  ? {
285
297
  uri: new URL(track.get('bigDataIndex'), trackDbFileLocation.uri)
286
298
  .href,
299
+ locationType: 'UriLocation',
287
300
  }
288
301
  : {
289
302
  uri: new URL(
290
303
  `${track.get('bigDataUrl')}.crai`,
291
304
  trackDbFileLocation.uri,
292
305
  ).href,
306
+ locationType: 'UriLocation',
293
307
  }
294
308
  } else {
295
309
  bigDataIndexLocation = track.get('bigDataIndex')
296
- ? { localPath: track.get('bigDataIndex') }
297
- : { localPath: `${track.get('bigDataUrl')}.crai` }
310
+ ? {
311
+ localPath: track.get('bigDataIndex'),
312
+ locationType: 'LocalPathLocation',
313
+ }
314
+ : {
315
+ localPath: `${track.get('bigDataUrl')}.crai`,
316
+ locationType: 'LocalPathLocation',
317
+ }
298
318
  }
299
319
  return {
300
320
  type: 'AlignmentsTrack',
@@ -349,17 +369,25 @@ function makeTrackConfig(
349
369
  ? {
350
370
  uri: new URL(track.get('bigDataIndex'), trackDbFileLocation.uri)
351
371
  .href,
372
+ locationType: 'UriLocation',
352
373
  }
353
374
  : {
354
375
  uri: new URL(
355
376
  `${track.get('bigDataUrl')}.tbi`,
356
377
  trackDbFileLocation.uri,
357
378
  ).href,
379
+ locationType: 'UriLocation',
358
380
  }
359
381
  } else {
360
382
  bigDataIndexLocation = track.get('bigDataIndex')
361
- ? { localPath: track.get('bigDataIndex') }
362
- : { localPath: `${track.get('bigDataUrl')}.tbi` }
383
+ ? {
384
+ localPath: track.get('bigDataIndex'),
385
+ locationType: 'LocalPathLocation',
386
+ }
387
+ : {
388
+ localPath: `${track.get('bigDataUrl')}.tbi`,
389
+ locationType: 'LocalPathLocation',
390
+ }
363
391
  }
364
392
  return {
365
393
  type: 'VariantTrack',