@jbrowse/plugin-data-management 1.5.0 → 1.5.4

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 (30) hide show
  1. package/dist/AddTrackWidget/model.d.ts +1 -434
  2. package/dist/AssemblyManager/AssemblyTable.d.ts +8 -2
  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 +387 -324
  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 +389 -326
  12. package/dist/plugin-data-management.esm.js.map +1 -1
  13. package/package.json +3 -2
  14. package/src/AddTrackWidget/components/AddTrackWidget.test.js +1 -1
  15. package/src/AddTrackWidget/components/AddTrackWidget.tsx +3 -1
  16. package/src/AddTrackWidget/components/ConfirmTrack.tsx +101 -32
  17. package/src/AddTrackWidget/components/TrackSourceSelect.tsx +2 -3
  18. package/src/AddTrackWidget/index.test.jsx +34 -15
  19. package/src/AddTrackWidget/model.ts +5 -14
  20. package/src/AssemblyManager/AssemblyManager.tsx +3 -1
  21. package/src/AssemblyManager/AssemblyTable.tsx +40 -39
  22. package/src/HierarchicalTrackSelectorWidget/model.js +3 -2
  23. package/src/PluginStoreWidget/components/CustomPluginForm.tsx +164 -56
  24. package/src/PluginStoreWidget/components/InstalledPlugin.tsx +16 -11
  25. package/src/PluginStoreWidget/components/PluginCard.tsx +7 -9
  26. package/src/PluginStoreWidget/components/PluginStoreWidget.test.js +8 -7
  27. package/src/PluginStoreWidget/components/PluginStoreWidget.tsx +34 -25
  28. package/src/PluginStoreWidget/components/__snapshots__/PluginStoreWidget.test.js.snap +69 -52
  29. package/src/SetDefaultSession/SetDefaultSession.test.tsx +6 -81
  30. package/src/SetDefaultSession/SetDefaultSession.tsx +51 -162
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jbrowse/plugin-data-management",
3
- "version": "1.5.0",
3
+ "version": "1.5.4",
4
4
  "description": "JBrowse 2 linear genome view",
5
5
  "keywords": [
6
6
  "jbrowse",
@@ -37,6 +37,7 @@
37
37
  "dependencies": {
38
38
  "@gmod/ucsc-hub": "^0.1.3",
39
39
  "@material-ui/icons": "^4.9.1",
40
+ "clsx": "^1.1.0",
40
41
  "pluralize": "^8.0.0",
41
42
  "react-virtualized-auto-sizer": "^1.0.2",
42
43
  "react-vtree": "^3.0.0-beta.1",
@@ -55,5 +56,5 @@
55
56
  "publishConfig": {
56
57
  "access": "public"
57
58
  },
58
- "gitHead": "542025578a39bd170c8a166f2568ee7edbd54072"
59
+ "gitHead": "0c398214590969168694b4ed8e20b595178b9efd"
59
60
  }
@@ -125,7 +125,7 @@ describe('<AddTrackWidget />', () => {
125
125
  expect(session.sessionTracks.length).toBe(2)
126
126
  })
127
127
 
128
- it('fails to add a track', async () => {
128
+ xit('fails to add a track', async () => {
129
129
  const { getByTestId, getAllByTestId, findByText } = render(
130
130
  <AddTrackWidget model={model} />,
131
131
  )
@@ -11,10 +11,12 @@ import {
11
11
  import { getSession } from '@jbrowse/core/util'
12
12
  import { getConf } from '@jbrowse/core/configuration'
13
13
  import { observer } from 'mobx-react'
14
+ import { Alert } from '@material-ui/lab'
15
+
16
+ // locals
14
17
  import ConfirmTrack from './ConfirmTrack'
15
18
  import TrackSourceSelect from './TrackSourceSelect'
16
19
  import { AddTrackModel } from '../model'
17
- import { Alert } from '@material-ui/lab'
18
20
 
19
21
  const useStyles = makeStyles(theme => ({
20
22
  root: {
@@ -1,15 +1,22 @@
1
+ import React from 'react'
1
2
  import { readConfObject } from '@jbrowse/core/configuration'
2
3
  import { getSession } from '@jbrowse/core/util'
3
- import Link from '@material-ui/core/Link'
4
- import MenuItem from '@material-ui/core/MenuItem'
5
- import { makeStyles } from '@material-ui/core/styles'
6
- import TextField from '@material-ui/core/TextField'
7
- import Typography from '@material-ui/core/Typography'
4
+ import {
5
+ Link,
6
+ MenuItem,
7
+ TextField,
8
+ ListSubheader,
9
+ Typography,
10
+ makeStyles,
11
+ } from '@material-ui/core'
12
+ import PluginManager from '@jbrowse/core/PluginManager'
8
13
  import { observer } from 'mobx-react'
9
14
  import { getEnv } from 'mobx-state-tree'
10
- import React from 'react'
11
15
  import { UNKNOWN } from '@jbrowse/core/util/tracks'
16
+
17
+ // locals
12
18
  import { AddTrackModel } from '../model'
19
+ import { AdapterMetadata } from '@jbrowse/core/pluggableElementTypes/AdapterType'
13
20
 
14
21
  const useStyles = makeStyles(theme => ({
15
22
  spacing: {
@@ -41,17 +48,68 @@ function StatusMessage({
41
48
  )
42
49
  }
43
50
 
51
+ /**
52
+ * categorizeAdapters takes a list of adapters and sorts their menu item elements under an appropriate ListSubheader
53
+ * element. In this way, adapters that are from external plugins can have headers that differentiate them from the
54
+ * out-of-the-box plugins.
55
+ * @param adaptersList - a list of adapters found in the PluginManager
56
+ * @returns a series of JSX elements that are ListSubheaders followed by the adapters
57
+ * found under that subheader
58
+ */
59
+ function categorizeAdapters(
60
+ adaptersList: { name: string; adapterMetadata: AdapterMetadata }[],
61
+ ) {
62
+ let currentCategory = ''
63
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
64
+ const items: any = []
65
+ adaptersList.forEach(adapter => {
66
+ if (adapter.adapterMetadata?.category) {
67
+ if (currentCategory !== adapter.adapterMetadata?.category) {
68
+ currentCategory = adapter.adapterMetadata?.category
69
+ items.push(
70
+ <ListSubheader
71
+ key={adapter.adapterMetadata?.category}
72
+ value={adapter.adapterMetadata?.category}
73
+ >
74
+ {adapter.adapterMetadata?.category}
75
+ </ListSubheader>,
76
+ )
77
+ }
78
+ items.push(
79
+ <MenuItem key={adapter.name} value={adapter.name}>
80
+ {adapter.adapterMetadata?.displayName
81
+ ? adapter.adapterMetadata?.displayName
82
+ : adapter.name}
83
+ </MenuItem>,
84
+ )
85
+ }
86
+ })
87
+ return items
88
+ }
89
+
90
+ function getAdapterTypes(pluginManager: PluginManager) {
91
+ return pluginManager.getElementTypesInGroup('adapter') as {
92
+ name: string
93
+ adapterMetadata: AdapterMetadata
94
+ }[]
95
+ }
96
+
97
+ function getTrackTypes(pluginManager: PluginManager) {
98
+ return pluginManager.getElementTypesInGroup('track') as { name: string }[]
99
+ }
100
+
44
101
  const TrackAdapterSelector = observer(({ model }: { model: AddTrackModel }) => {
45
102
  const classes = useStyles()
46
103
  const session = getSession(model)
47
104
  const { trackAdapter } = model
48
-
105
+ // prettier-ignore
106
+ const adapters = getAdapterTypes(getEnv(session).pluginManager)
49
107
  return (
50
108
  <TextField
51
109
  className={classes.spacing}
52
- value={trackAdapter?.type}
110
+ value={trackAdapter?.type !== 'UNKNOWN' ? trackAdapter?.type : ''}
53
111
  label="adapterType"
54
- helperText="An adapter type"
112
+ helperText="Select an adapter type"
55
113
  select
56
114
  fullWidth
57
115
  onChange={event => model.setAdapterHint(event.target.value)}
@@ -60,15 +118,24 @@ const TrackAdapterSelector = observer(({ model }: { model: AddTrackModel }) => {
60
118
  SelectDisplayProps: { 'data-testid': 'adapterTypeSelect' },
61
119
  }}
62
120
  >
63
- {getEnv(session)
64
- .pluginManager.getElementTypesInGroup('adapter')
65
- // Exclude SNPCoverageAdapter from primary adapter user selection
66
- .filter((elt: { name: string }) => elt.name !== 'SNPCoverageAdapter')
67
- .map((elt: { name: string }) => (
121
+ {adapters
122
+ // Excludes any adapter with the 'adapterMetadata.hiddenFromGUI' property, and anything with the 'adapterMetadata.category' property
123
+ .filter(
124
+ elt =>
125
+ !elt.adapterMetadata?.hiddenFromGUI &&
126
+ !elt.adapterMetadata?.category,
127
+ )
128
+ .map(elt => (
68
129
  <MenuItem key={elt.name} value={elt.name}>
69
- {elt.name}
130
+ {elt.adapterMetadata?.displayName
131
+ ? elt.adapterMetadata?.displayName
132
+ : elt.name}
70
133
  </MenuItem>
71
134
  ))}
135
+ {/* adapters with the 'adapterMetadata.category' property are categorized by the value of the property here */}
136
+ {categorizeAdapters(
137
+ adapters.filter(elt => !elt.adapterMetadata?.hiddenFromGUI),
138
+ )}
72
139
  </TextField>
73
140
  )
74
141
  })
@@ -78,8 +145,8 @@ function UnknownAdapterPrompt({ model }: { model: AddTrackModel }) {
78
145
  return (
79
146
  <>
80
147
  <Typography className={classes.spacing}>
81
- Was not able to guess the adapter type for this data, but it may be in
82
- the list below. If not, you can{' '}
148
+ JBrowse was not able to guess the adapter type for this data, but it may
149
+ be in the list below. If not, you can{' '}
83
150
  <Link
84
151
  href="https://github.com/GMOD/jbrowse-components/releases"
85
152
  target="_blank"
@@ -106,13 +173,14 @@ const TrackTypeSelector = observer(({ model }: { model: AddTrackModel }) => {
106
173
  const classes = useStyles()
107
174
  const session = getSession(model)
108
175
  const { trackType } = model
176
+ const trackTypes = getTrackTypes(getEnv(session).pluginManager)
109
177
 
110
178
  return (
111
179
  <TextField
112
180
  className={classes.spacing}
113
181
  value={trackType}
114
182
  label="trackType"
115
- helperText="A track type"
183
+ helperText="Select a track type"
116
184
  select
117
185
  fullWidth
118
186
  onChange={event => {
@@ -123,14 +191,11 @@ const TrackTypeSelector = observer(({ model }: { model: AddTrackModel }) => {
123
191
  SelectDisplayProps: { 'data-testid': 'trackTypeSelect' },
124
192
  }}
125
193
  >
126
- {getEnv(session)
127
- .pluginManager.getElementTypesInGroup('track')
128
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
129
- .map(({ name }: any) => (
130
- <MenuItem key={name} value={name}>
131
- {name}
132
- </MenuItem>
133
- ))}
194
+ {trackTypes.map(({ name }) => (
195
+ <MenuItem key={name} value={name}>
196
+ {name}
197
+ </MenuItem>
198
+ ))}
134
199
  </TextField>
135
200
  )
136
201
  })
@@ -152,14 +217,13 @@ const TrackAssemblySelector = observer(
152
217
  SelectDisplayProps: { 'data-testid': 'assemblyNameSelect' },
153
218
  }}
154
219
  >
155
- {session.assemblies.map(conf => {
156
- const name = readConfObject(conf, 'name')
157
- return (
220
+ {session.assemblies
221
+ .map(conf => readConfObject(conf, 'name'))
222
+ .map(name => (
158
223
  <MenuItem key={name} value={name}>
159
224
  {name}
160
225
  </MenuItem>
161
- )
162
- })}
226
+ ))}
163
227
  </TextField>
164
228
  )
165
229
  },
@@ -167,7 +231,8 @@ const TrackAssemblySelector = observer(
167
231
 
168
232
  function ConfirmTrack({ model }: { model: AddTrackModel }) {
169
233
  const classes = useStyles()
170
- const { trackName, trackAdapter, trackType, warningMessage } = model
234
+ const { trackName, trackAdapter, trackType, warningMessage, adapterHint } =
235
+ model
171
236
 
172
237
  if (model.unsupported) {
173
238
  return (
@@ -198,6 +263,10 @@ function ConfirmTrack({ model }: { model: AddTrackModel }) {
198
263
  return <UnknownAdapterPrompt model={model} />
199
264
  }
200
265
 
266
+ if (adapterHint === '' && trackAdapter) {
267
+ model.setAdapterHint(trackAdapter.type)
268
+ }
269
+
201
270
  if (!trackAdapter?.type) {
202
271
  return <Typography>Could not recognize this data type.</Typography>
203
272
  }
@@ -1,7 +1,6 @@
1
- import { FileSelector } from '@jbrowse/core/ui'
2
- import Paper from '@material-ui/core/Paper'
3
- import { makeStyles } from '@material-ui/core/styles'
4
1
  import React from 'react'
2
+ import { FileSelector } from '@jbrowse/core/ui'
3
+ import { Paper, makeStyles } from '@material-ui/core'
5
4
  import { AddTrackModel } from '../model'
6
5
  import { getRoot } from 'mobx-state-tree'
7
6
  import { observer } from 'mobx-react'
@@ -4,32 +4,51 @@ import PluginManager from '@jbrowse/core/PluginManager'
4
4
  import Plugin from '@jbrowse/core/Plugin'
5
5
  import ViewType from '@jbrowse/core/pluggableElementTypes/ViewType'
6
6
  import stateModelFactory from './model'
7
+ import Alignments from '@jbrowse/plugin-alignments'
8
+ import SVG from '@jbrowse/plugin-svg'
9
+ import Variants from '@jbrowse/plugin-variants'
10
+ import Hic from '@jbrowse/plugin-hic'
7
11
 
8
12
  function standardInitializer() {
9
- const pluginManager = new PluginManager([new FakeViewPlugin()])
13
+ const pluginManager = new PluginManager([
14
+ new FakeViewPlugin(),
15
+ new Alignments(),
16
+ new SVG(),
17
+ new Variants(),
18
+ new Hic(),
19
+ ])
10
20
  pluginManager.createPluggableElements()
11
21
  pluginManager.configure()
12
22
 
13
- const SessionModel = types.model({
14
- view: FakeViewModel,
15
- widget: stateModelFactory(pluginManager),
16
- })
23
+ const SessionModel = types
24
+ .model({
25
+ view: FakeViewModel,
26
+ widget: stateModelFactory(pluginManager),
27
+ })
28
+ .volatile(() => ({
29
+ rpcManager: {},
30
+ configuration: {},
31
+ }))
17
32
 
18
33
  // assemblyNames is defined on the view, which is done in LGV for example
19
34
  // this is really just used for convenience to automatically fill in the
20
35
  // assembly field in the form
21
- return SessionModel.create({
22
- view: {
23
- id: 'testing',
24
- type: 'FakeView',
25
- assemblyNames: ['volvox'],
26
- },
27
- widget: {
28
- type: 'AddTrackWidget',
29
- view: 'testing',
36
+ return SessionModel.create(
37
+ {
38
+ view: {
39
+ id: 'testing',
40
+ type: 'FakeView',
41
+ assemblyNames: ['volvox'],
42
+ },
43
+ widget: {
44
+ type: 'AddTrackWidget',
45
+ view: 'testing',
46
+ },
30
47
  },
31
- })
48
+ { pluginManager },
49
+ )
32
50
  }
51
+
33
52
  const realLocation = window.location
34
53
 
35
54
  // https://stackoverflow.com/a/60110508/2129219
@@ -6,6 +6,7 @@ import {
6
6
  guessAdapter,
7
7
  guessTrackType,
8
8
  UNSUPPORTED,
9
+ getFileName,
9
10
  } from '@jbrowse/core/util/tracks'
10
11
 
11
12
  function isAbsoluteUrl(url = '') {
@@ -17,18 +18,6 @@ function isAbsoluteUrl(url = '') {
17
18
  }
18
19
  }
19
20
 
20
- function getFileName(track: FileLocation) {
21
- const uri = 'uri' in track ? track.uri : undefined
22
- const localPath = 'localPath' in track ? track.localPath : undefined
23
- const blob = 'blobId' in track ? track : undefined
24
- return (
25
- blob?.name ||
26
- uri?.slice(uri.lastIndexOf('/') + 1) ||
27
- localPath?.slice(localPath.lastIndexOf('/') + 1) ||
28
- ''
29
- )
30
- }
31
-
32
21
  export default function f(pluginManager: PluginManager) {
33
22
  return types
34
23
  .model('AddTrackModel', {
@@ -88,7 +77,7 @@ export default function f(pluginManager: PluginManager) {
88
77
  const { trackData, indexTrackData, adapterHint } = self
89
78
 
90
79
  return trackData
91
- ? guessAdapter(trackData, indexTrackData, getFileName, adapterHint)
80
+ ? guessAdapter(trackData, indexTrackData, adapterHint, self)
92
81
  : undefined
93
82
  },
94
83
 
@@ -148,7 +137,9 @@ export default function f(pluginManager: PluginManager) {
148
137
  get trackType() {
149
138
  return (
150
139
  self.altTrackType ||
151
- (this.trackAdapter ? guessTrackType(this.trackAdapter.type) : '')
140
+ (this.trackAdapter
141
+ ? guessTrackType(this.trackAdapter.type, self)
142
+ : '')
152
143
  )
153
144
  },
154
145
  }))
@@ -1,6 +1,7 @@
1
1
  import React, { useState } from 'react'
2
2
  import { observer } from 'mobx-react'
3
3
  import { makeStyles } from '@material-ui/core/styles'
4
+ import { AnyConfigurationModel } from '@jbrowse/core/configuration/configurationSchema'
4
5
  import {
5
6
  Button,
6
7
  Dialog,
@@ -52,7 +53,8 @@ const AssemblyManager = observer(
52
53
  const classes = useStyles()
53
54
  const [isFormOpen, setFormOpen] = useState(false)
54
55
  const [isAssemblyBeingEdited, setIsAssemblyBeingEdited] = useState(false)
55
- const [assemblyBeingEdited, setAssemblyBeingEdited] = useState()
56
+ const [assemblyBeingEdited, setAssemblyBeingEdited] =
57
+ useState<AnyConfigurationModel>()
56
58
 
57
59
  const showAssemblyTable = !isFormOpen && !isAssemblyBeingEdited
58
60
 
@@ -42,11 +42,14 @@ const AssemblyTable = observer(
42
42
  setIsAssemblyBeingEdited,
43
43
  setAssemblyBeingEdited,
44
44
  }: {
45
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
46
- rootModel: any
45
+ rootModel: {
46
+ jbrowse: {
47
+ removeAssemblyConf: (arg: string) => void
48
+ assemblies: AnyConfigurationModel[]
49
+ }
50
+ }
47
51
  setIsAssemblyBeingEdited(arg: boolean): void
48
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
49
- setAssemblyBeingEdited(arg: any): void
52
+ setAssemblyBeingEdited(arg: AnyConfigurationModel): void
50
53
  }) => {
51
54
  const classes = useStyles()
52
55
 
@@ -54,41 +57,39 @@ const AssemblyTable = observer(
54
57
  rootModel.jbrowse.removeAssemblyConf(name)
55
58
  }
56
59
 
57
- const rows = rootModel.jbrowse.assemblies.map(
58
- (assembly: AnyConfigurationModel) => {
59
- const name = readConfObject(assembly, 'name')
60
- const displayName = readConfObject(assembly, 'displayName')
61
- const aliases = readConfObject(assembly, 'aliases')
62
- return (
63
- <TableRow key={name}>
64
- <TableCell>{name}</TableCell>
65
- <TableCell>{displayName}</TableCell>
66
- <TableCell>{aliases ? aliases.toString() : ''}</TableCell>
67
- <TableCell className={classes.buttonCell}>
68
- <IconButton
69
- data-testid={`${name}-edit`}
70
- className={classes.button}
71
- onClick={() => {
72
- setIsAssemblyBeingEdited(true)
73
- setAssemblyBeingEdited(assembly)
74
- }}
75
- >
76
- <CreateIcon color="primary" />
77
- </IconButton>
78
- <IconButton
79
- data-testid={`${name}-delete`}
80
- className={classes.button}
81
- onClick={() => {
82
- removeAssembly(name)
83
- }}
84
- >
85
- <DeleteIcon color="error" />
86
- </IconButton>
87
- </TableCell>
88
- </TableRow>
89
- )
90
- },
91
- )
60
+ const rows = rootModel.jbrowse.assemblies.map(assembly => {
61
+ const name = readConfObject(assembly, 'name')
62
+ const displayName = readConfObject(assembly, 'displayName')
63
+ const aliases = readConfObject(assembly, 'aliases')
64
+ return (
65
+ <TableRow key={name}>
66
+ <TableCell>{name}</TableCell>
67
+ <TableCell>{displayName}</TableCell>
68
+ <TableCell>{aliases ? aliases.toString() : ''}</TableCell>
69
+ <TableCell className={classes.buttonCell}>
70
+ <IconButton
71
+ data-testid={`${name}-edit`}
72
+ className={classes.button}
73
+ onClick={() => {
74
+ setIsAssemblyBeingEdited(true)
75
+ setAssemblyBeingEdited(assembly)
76
+ }}
77
+ >
78
+ <CreateIcon color="primary" />
79
+ </IconButton>
80
+ <IconButton
81
+ data-testid={`${name}-delete`}
82
+ className={classes.button}
83
+ onClick={() => {
84
+ removeAssembly(name)
85
+ }}
86
+ >
87
+ <DeleteIcon color="error" />
88
+ </IconButton>
89
+ </TableCell>
90
+ </TableRow>
91
+ )
92
+ })
92
93
 
93
94
  return (
94
95
  <TableContainer component={Paper}>
@@ -9,9 +9,10 @@ const hasAnyOverlap = (a1 = [], a2 = []) =>
9
9
  function passesFilter(filter, config) {
10
10
  const name = getTrackName(config)
11
11
  const categories = readConfObject(config, 'category') || []
12
- const regexp = new RegExp(filter, 'i')
12
+ const filterLower = filter.toLowerCase()
13
13
  return (
14
- !!name.match(regexp) || categories.filter(cat => !!cat.match(regexp)).length
14
+ !!name.toLowerCase().includes(filterLower) ||
15
+ categories.filter(cat => !!cat.toLowerCase().includes(filterLower)).length
15
16
  )
16
17
  }
17
18